模块化 AppStore 并完善素材管理与个人空间设置。
将会话、权限、位置、收款与排队存储拆分为独立模块,同步更新各 ViewModel 与单元测试,并补充素材管理 UI 与个人空间设置交互。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
233
suixinkan/DataStore/AppSessionStore.swift
Normal file
233
suixinkan/DataStore/AppSessionStore.swift
Normal file
@ -0,0 +1,233 @@
|
||||
//
|
||||
// AppSessionStore.swift
|
||||
// suixinkan
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// 应用账号类型,保留后端返回的未知类型以兼容后续扩展。
|
||||
enum AppAccountType: Equatable, Sendable {
|
||||
case scenicUser
|
||||
case storeUser
|
||||
case unknown(String)
|
||||
|
||||
/// 从持久化或后端原始值创建账号类型。
|
||||
init(rawValue: String) {
|
||||
switch rawValue {
|
||||
case "scenic_user": self = .scenicUser
|
||||
case "store_user": self = .storeUser
|
||||
default: self = .unknown(rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
/// 用于接口与持久化的原始字符串。
|
||||
var rawValue: String {
|
||||
switch self {
|
||||
case .scenicUser: "scenic_user"
|
||||
case .storeUser: "store_user"
|
||||
case let .unknown(value): value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 管理登录凭证、用户资料、账号角色及当前业务上下文。
|
||||
final class AppSessionStore {
|
||||
|
||||
private enum Key: String {
|
||||
case token = "key_in_token"
|
||||
case lastLoginUsername = "key_last_login_username"
|
||||
case privacyAgreementAccepted = "key_privacy_agreement_accepted"
|
||||
case userId = "key_in_user_id"
|
||||
case userName = "key_in_user_name"
|
||||
case realName = "key_in_real_name"
|
||||
case avatar = "key_in_avatar"
|
||||
case phone = "key_in_phone"
|
||||
case accountType = "key_in_account_type"
|
||||
case accountDisplayName = "key_in_account_display_name"
|
||||
case roleCode = "key_in_role_code"
|
||||
case roleName = "key_in_role_name"
|
||||
case currentScenicId = "key_in_current_scenic_id"
|
||||
case currentScenicName = "key_in_current_scenic_name"
|
||||
case currentStoreId = "key_in_current_store_id"
|
||||
}
|
||||
|
||||
private let defaults: UserDefaults
|
||||
|
||||
/// 使用指定 UserDefaults 创建会话存储。
|
||||
init(defaults: UserDefaults) {
|
||||
self.defaults = defaults
|
||||
}
|
||||
|
||||
/// 登录 Token。
|
||||
var token: String {
|
||||
get { string(for: .token) }
|
||||
set { defaults.set(newValue, forKey: Key.token.rawValue) }
|
||||
}
|
||||
|
||||
/// 上次登录手机号,用于登录页恢复。
|
||||
var lastLoginUsername: String? {
|
||||
get { defaults.string(forKey: Key.lastLoginUsername.rawValue) }
|
||||
set {
|
||||
if let newValue {
|
||||
defaults.set(newValue, forKey: Key.lastLoginUsername.rawValue)
|
||||
} else {
|
||||
defaults.removeObject(forKey: Key.lastLoginUsername.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 是否已同意隐私协议。
|
||||
var privacyAgreementAccepted: Bool {
|
||||
get { defaults.bool(forKey: Key.privacyAgreementAccepted.rawValue) }
|
||||
set { defaults.set(newValue, forKey: Key.privacyAgreementAccepted.rawValue) }
|
||||
}
|
||||
|
||||
/// 当前业务用户 ID。
|
||||
var userId: String {
|
||||
get { string(for: .userId) }
|
||||
set { defaults.set(newValue, forKey: Key.userId.rawValue) }
|
||||
}
|
||||
|
||||
/// 当前用户展示名称。
|
||||
var userName: String {
|
||||
get { string(for: .userName) }
|
||||
set { defaults.set(newValue, forKey: Key.userName.rawValue) }
|
||||
}
|
||||
|
||||
/// 当前用户真实姓名。
|
||||
var realName: String {
|
||||
get { string(for: .realName) }
|
||||
set { defaults.set(newValue, forKey: Key.realName.rawValue) }
|
||||
}
|
||||
|
||||
/// 当前用户头像地址。
|
||||
var avatar: String {
|
||||
get { string(for: .avatar) }
|
||||
set { defaults.set(newValue, forKey: Key.avatar.rawValue) }
|
||||
}
|
||||
|
||||
/// 当前用户手机号。
|
||||
var phone: String {
|
||||
get { string(for: .phone) }
|
||||
set { defaults.set(newValue, forKey: Key.phone.rawValue) }
|
||||
}
|
||||
|
||||
/// 当前账号业务类型。
|
||||
var accountType: AppAccountType {
|
||||
get { AppAccountType(rawValue: string(for: .accountType)) }
|
||||
set { defaults.set(newValue.rawValue, forKey: Key.accountType.rawValue) }
|
||||
}
|
||||
|
||||
/// 当前账号展示名称。
|
||||
var accountDisplayName: String {
|
||||
get { string(for: .accountDisplayName) }
|
||||
set { defaults.set(newValue, forKey: Key.accountDisplayName.rawValue) }
|
||||
}
|
||||
|
||||
/// 后端返回的原始角色编码。
|
||||
var roleCode: String {
|
||||
get { string(for: .roleCode) }
|
||||
set { defaults.set(newValue, forKey: Key.roleCode.rawValue) }
|
||||
}
|
||||
|
||||
/// 当前角色名称。
|
||||
var roleName: String {
|
||||
get { string(for: .roleName) }
|
||||
set { defaults.set(newValue, forKey: Key.roleName.rawValue) }
|
||||
}
|
||||
|
||||
/// 当前景区 ID,0 表示未选择。
|
||||
var currentScenicId: Int {
|
||||
get { Int(string(for: .currentScenicId)) ?? 0 }
|
||||
set { defaults.set(newValue > 0 ? String(newValue) : "", forKey: Key.currentScenicId.rawValue) }
|
||||
}
|
||||
|
||||
/// 当前景区名称。
|
||||
var currentScenicName: String {
|
||||
get { string(for: .currentScenicName) }
|
||||
set { defaults.set(newValue, forKey: Key.currentScenicName.rawValue) }
|
||||
}
|
||||
|
||||
/// 当前店铺 ID,0 表示未选择。
|
||||
var currentStoreId: Int {
|
||||
get { defaults.integer(forKey: Key.currentStoreId.rawValue) }
|
||||
set { defaults.set(newValue, forKey: Key.currentStoreId.rawValue) }
|
||||
}
|
||||
|
||||
/// 当前账号缓存前缀,对齐 Android `getAccountCachePrefix`。
|
||||
var accountCachePrefix: String {
|
||||
let uid = userId.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let type = accountType.rawValue.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if uid.isEmpty { return "guest" }
|
||||
if type.isEmpty { return uid }
|
||||
return "\(uid)_\(type)"
|
||||
}
|
||||
|
||||
/// 当前解析后的业务角色。
|
||||
var currentAppRole: AppRoleCode? {
|
||||
AppRoleCode.fromCode(roleCode) ?? AppRoleCode.fromRoleName(roleName)
|
||||
}
|
||||
|
||||
/// 当前是否为摄影师角色。
|
||||
var isPhotographerRole: Bool {
|
||||
if let role = currentAppRole {
|
||||
return role.isPhotographer
|
||||
}
|
||||
return roleName.trimmingCharacters(in: .whitespacesAndNewlines) == "摄影师"
|
||||
}
|
||||
|
||||
/// Token 非空时表示已登录。
|
||||
var isLoggedIn: Bool {
|
||||
!token.isEmpty
|
||||
}
|
||||
|
||||
/// 保存登录 Token。
|
||||
func saveToken(_ token: String) {
|
||||
self.token = token
|
||||
}
|
||||
|
||||
/// 从账号切换实体写入会话快照。
|
||||
func applyAccount(_ account: AccountSwitchAccount) {
|
||||
userId = String(account.businessUserId)
|
||||
accountType = AppAccountType(rawValue: account.accountType)
|
||||
accountDisplayName = account.title
|
||||
userName = account.subtitle
|
||||
phone = account.phone
|
||||
if !account.avatar.isEmpty {
|
||||
avatar = account.avatar
|
||||
}
|
||||
currentScenicName = account.scenicName
|
||||
currentScenicId = account.scenicId ?? 0
|
||||
currentStoreId = account.storeId ?? 0
|
||||
}
|
||||
|
||||
/// 从用户资料接口回写展示字段。
|
||||
func applyUserInfo(_ info: UserInfoResponse) {
|
||||
if !info.nickname.isEmpty { userName = info.nickname }
|
||||
if !info.realName.isEmpty { realName = info.realName }
|
||||
if !info.avatar.isEmpty { avatar = info.avatar }
|
||||
if !info.phone.isEmpty { phone = info.phone }
|
||||
if !info.roleName.isEmpty, roleName.isEmpty { roleName = info.roleName }
|
||||
}
|
||||
|
||||
/// 清除登录态及账号快照,保留隐私授权和上次登录用户名。
|
||||
func clearAuthenticatedSession() {
|
||||
token = ""
|
||||
userId = ""
|
||||
userName = ""
|
||||
realName = ""
|
||||
avatar = ""
|
||||
phone = ""
|
||||
accountType = .unknown("")
|
||||
accountDisplayName = ""
|
||||
roleCode = ""
|
||||
roleName = ""
|
||||
currentScenicId = 0
|
||||
currentScenicName = ""
|
||||
currentStoreId = 0
|
||||
}
|
||||
|
||||
private func string(for key: Key) -> String {
|
||||
defaults.string(forKey: key.rawValue) ?? ""
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user