// // AccountDeletionViewModels.swift // suixinkan // import Foundation /// 注销资产核验页 ViewModel,管理资产快照、确认状态与验证页上下文。 final class AccountDeletionViewModel { private let service: any AccountDeletionServing let username: String private(set) var assets: [AccountDeletionAssetSummary] = [] private(set) var consequences: [String] = [] private(set) var isAcknowledged = false private(set) var isLoading = false private(set) var errorMessage: String? var onStateChange: (() -> Void)? /// 创建注销核验 ViewModel,默认读取当前登录账号并使用共享 Mock 服务。 init( username: String? = AccountDeletionIdentity.currentUsername(), service: any AccountDeletionServing = AccountDeletionMockService.shared ) { self.username = AccountDeletionIdentity.normalizedUsername(username ?? "") self.service = service } /// 勾选后才允许进入短信验证。 var isContinueEnabled: Bool { !assets.isEmpty && isAcknowledged && !isLoading } /// 资产加载完成后主按钮保持可点击,未勾选时由业务校验给出明确提示。 var isContinueButtonEnabled: Bool { !assets.isEmpty && !isLoading } /// 拉取注销前置核验结果。 func load() async { guard !isLoading else { return } isLoading = true errorMessage = nil notifyStateChange() defer { isLoading = false notifyStateChange() } do { let precheck = try await service.loadPrecheck(username: username) assets = precheck.assets consequences = precheck.consequences } catch { errorMessage = error.localizedDescription } } /// 切换用户对注销影响的确认状态。 func toggleAcknowledgement() { isAcknowledged.toggle() notifyStateChange() } /// 生成短信验证页上下文。 func makeVerificationContext() throws -> AccountDeletionVerificationContext { guard isContinueEnabled else { throw AccountDeletionError.incompleteAcknowledgement } guard !username.isEmpty else { throw AccountDeletionError.missingIdentity } return AccountDeletionVerificationContext( username: username, maskedPhone: AccountDeletionIdentity.maskedPhone(username), assets: assets ) } /// 使用当前服务创建短信验证 ViewModel,保持同一套 Mock 状态。 func makeVerificationViewModel() throws -> AccountDeletionVerificationViewModel { try AccountDeletionVerificationViewModel( context: makeVerificationContext(), service: service ) } private func notifyStateChange() { onStateChange?() } } /// 注销短信验证页 ViewModel,负责验证码发送、校验和提交申请。 final class AccountDeletionVerificationViewModel { private let service: any AccountDeletionServing private let clientRequestID = UUID() let context: AccountDeletionVerificationContext private(set) var verificationCode = "" private(set) var isSendingCode = false private(set) var isSubmitting = false private(set) var hasSentCode = false private(set) var errorMessage: String? var onStateChange: (() -> Void)? /// 创建短信验证 ViewModel。 init( context: AccountDeletionVerificationContext, service: any AccountDeletionServing = AccountDeletionMockService.shared ) throws { guard !context.username.isEmpty else { throw AccountDeletionError.missingIdentity } self.context = context self.service = service } /// 验证码满足6位数字且当前未提交时允许继续。 var isSubmitEnabled: Bool { verificationCode.count == 6 && verificationCode.allSatisfy(\.isNumber) && !isSubmitting } /// 更新用户输入的验证码,只保留前6位数字。 func updateVerificationCode(_ value: String) { verificationCode = String(value.filter(\.isNumber).prefix(6)) errorMessage = nil notifyStateChange() } /// 发送 Mock 短信验证码。 func sendVerificationCode() async { guard !isSendingCode else { return } isSendingCode = true errorMessage = nil notifyStateChange() defer { isSendingCode = false notifyStateChange() } do { try await service.sendVerificationCode(to: context.username) hasSentCode = true } catch { errorMessage = error.localizedDescription } } /// 提交注销申请并返回计划删除时间。 func submit() async throws -> AccountDeletionRequest { guard isSubmitEnabled else { throw AccountDeletionError.invalidVerificationCode } guard !isSubmitting else { throw CancellationError() } isSubmitting = true errorMessage = nil notifyStateChange() defer { isSubmitting = false notifyStateChange() } do { return try await service.submitDeletion( username: context.username, verificationCode: verificationCode, acknowledgedAssetKinds: Set(context.assets.map(\.kind)), clientRequestID: clientRequestID ) } catch { errorMessage = error.localizedDescription throw error } } private func notifyStateChange() { onStateChange?() } }