// // AccountSwitchViewModel.swift // suixinkan // // Created by Codex on 2026/6/22. // import Foundation @MainActor /// 账号切换 ViewModel,管理可切换账号列表、选择状态和提交状态。 final class AccountSwitchViewModel { var onChange: (() -> Void)? private(set) var accounts: [AccountSwitchAccount] = [] { didSet { onChange?() } } var selectedAccountId: String? { didSet { onChange?() } } private(set) var loading = false { didSet { onChange?() } } private(set) var switching = false { didSet { onChange?() } } private var didLoad = false /// 当前选中的账号。 var selectedAccount: AccountSwitchAccount? { accounts.first { $0.id == selectedAccountId } } /// 拉取可切换账号列表,默认只加载一次。 func load(api: ProfileAPI, force: Bool = false, currentAccountId: String? = nil) async throws { guard force || !didLoad else { return } didLoad = true loading = true defer { loading = false } let response = try await api.switchableAccounts() accounts = response.accounts.filter { $0.businessUserId > 0 } selectedAccountId = accounts.first(where: { $0.isCurrent || isCurrent($0, currentAccountId: currentAccountId) })?.id ?? accounts.first?.id } /// 选择指定账号。 func select(_ account: AccountSwitchAccount) { selectedAccountId = account.id } /// 提交账号切换,调用 set-user 换取正式 token。 func switchAccount(_ account: AccountSwitchAccount, api: AuthAPI) async throws -> V9AuthResponse { guard !switching else { throw AccountSwitchError.submitting } guard account.businessUserId > 0 else { throw AccountSwitchError.invalidAccount } switching = true defer { switching = false } return try await api.setUser(account.toSetUserRequest()) } /// 判断账号是否与当前账号标识一致。 func isCurrent(_ account: AccountSwitchAccount, currentAccountId: String?) -> Bool { guard let currentAccountId else { return false } return account.id == currentAccountId || String(account.businessUserId) == currentAccountId } } /// 账号切换错误实体,表示重复提交或账号数据异常。 enum AccountSwitchError: LocalizedError, Equatable { case submitting case invalidAccount var errorDescription: String? { switch self { case .submitting: "账号正在切换,请稍候" case .invalidAccount: "账号信息异常,请重新选择账号" } } }