Initial commit: suixinkan_ios UIKit rewrite project.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
150
suixinkan_ios/App/State/AccountContext.swift
Normal file
150
suixinkan_ios/App/State/AccountContext.swift
Normal file
@ -0,0 +1,150 @@
|
||||
//
|
||||
// AccountContext.swift
|
||||
// suixinkan
|
||||
//
|
||||
// Created by Codex on 2026/6/18.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// 当前账号的基础资料实体,用于跨页面展示昵称、手机号和头像。
|
||||
struct AccountProfile: Codable, Equatable {
|
||||
var userId: String
|
||||
var displayName: String
|
||||
var phone: String?
|
||||
var avatarURL: String?
|
||||
}
|
||||
|
||||
/// 当前业务作用域实体,表示景区或门店维度的上下文。
|
||||
struct BusinessScope: Codable, Equatable, Identifiable {
|
||||
/// 业务作用域类型,用于区分景区和门店。
|
||||
enum Kind: Codable, Equatable {
|
||||
case scenic
|
||||
case store
|
||||
}
|
||||
|
||||
var id: Int
|
||||
var name: String
|
||||
var kind: Kind
|
||||
var parentScenicId: Int?
|
||||
var status: Int?
|
||||
var address: String?
|
||||
var latitude: Double?
|
||||
var longitude: Double?
|
||||
var coverURLString: String?
|
||||
|
||||
/// 创建业务作用域,门店可通过 parentScenicId 标记所属景区。
|
||||
init(
|
||||
id: Int,
|
||||
name: String,
|
||||
kind: Kind,
|
||||
parentScenicId: Int? = nil,
|
||||
status: Int? = nil,
|
||||
address: String? = nil,
|
||||
latitude: Double? = nil,
|
||||
longitude: Double? = nil,
|
||||
coverURLString: String? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.kind = kind
|
||||
self.parentScenicId = parentScenicId
|
||||
self.status = status
|
||||
self.address = address
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.coverURLString = coverURLString
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
/// 账号上下文状态中心,保存当前账号资料和景区/门店作用域。
|
||||
final class AccountContext {
|
||||
|
||||
var onChange: (() -> Void)?
|
||||
private(set) var profile: AccountProfile? { didSet { onChange?() } }
|
||||
private(set) var scenicScopes: [BusinessScope] = [] { didSet { onChange?() } }
|
||||
private(set) var storeScopes: [BusinessScope] = [] { didSet { onChange?() } }
|
||||
var currentScenic: BusinessScope? { didSet { onChange?() } }
|
||||
var currentStore: BusinessScope? { didSet { onChange?() } }
|
||||
|
||||
/// 应用登录成功后写入账号资料。
|
||||
func applyLogin(profile: AccountProfile? = nil) {
|
||||
self.profile = profile
|
||||
}
|
||||
|
||||
/// 替换当前账号资料,通常用于用户信息刷新后同步全局展示。
|
||||
func replaceProfile(_ profile: AccountProfile?) {
|
||||
self.profile = profile
|
||||
}
|
||||
|
||||
/// 替换景区和门店作用域,并尽量保持当前选择不变。
|
||||
func replaceScopes(
|
||||
scenic: [BusinessScope],
|
||||
stores: [BusinessScope],
|
||||
currentScenicId: Int? = nil,
|
||||
currentStoreId: Int? = nil
|
||||
) {
|
||||
scenicScopes = scenic
|
||||
storeScopes = stores
|
||||
currentScenic = currentScenicId.flatMap { id in
|
||||
scenic.first { $0.id == id }
|
||||
} ?? currentScenic.flatMap { current in
|
||||
scenic.first { $0.id == current.id }
|
||||
} ?? scenic.first
|
||||
currentStore = resolvedStore(
|
||||
in: stores,
|
||||
currentScenic: currentScenic,
|
||||
preferredStoreId: currentStoreId ?? currentStore?.id
|
||||
)
|
||||
}
|
||||
|
||||
/// 切换当前景区,并按景区重新校准当前门店。
|
||||
func selectScenic(id scenicId: Int) {
|
||||
guard let scenic = scenicScopes.first(where: { $0.id == scenicId }) else { return }
|
||||
currentScenic = scenic
|
||||
currentStore = resolvedStore(
|
||||
in: storeScopes,
|
||||
currentScenic: scenic,
|
||||
preferredStoreId: currentStore?.id
|
||||
)
|
||||
}
|
||||
|
||||
/// 切换当前门店,并在门店有关联景区时同步当前景区。
|
||||
func selectStore(id storeId: Int) {
|
||||
guard let store = storeScopes.first(where: { $0.id == storeId }) else { return }
|
||||
currentStore = store
|
||||
if let scenicId = store.parentScenicId,
|
||||
currentScenic?.id != scenicId,
|
||||
let scenic = scenicScopes.first(where: { $0.id == scenicId }) {
|
||||
currentScenic = scenic
|
||||
}
|
||||
}
|
||||
|
||||
/// 清空账号上下文,通常在退出登录时调用。
|
||||
func reset() {
|
||||
profile = nil
|
||||
scenicScopes = []
|
||||
storeScopes = []
|
||||
currentScenic = nil
|
||||
currentStore = nil
|
||||
}
|
||||
|
||||
/// 按当前景区和期望门店 ID 选择有效门店。
|
||||
private func resolvedStore(
|
||||
in stores: [BusinessScope],
|
||||
currentScenic: BusinessScope?,
|
||||
preferredStoreId: Int?
|
||||
) -> BusinessScope? {
|
||||
let scenicId = currentScenic?.id
|
||||
let storesForScenic = stores.filter { store in
|
||||
guard let scenicId else { return true }
|
||||
return store.parentScenicId == nil || store.parentScenicId == scenicId
|
||||
}
|
||||
if let preferredStoreId,
|
||||
let store = storesForScenic.first(where: { $0.id == preferredStoreId }) {
|
||||
return store
|
||||
}
|
||||
return storesForScenic.first
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user