// // AccountDeletionModels.swift // suixinkan // import Foundation /// 注销核验资产,使用中性文案展示当前账号拥有的数据与权益。 struct AccountDeletionAssetSummary: Codable, Hashable, Sendable { /// 注销核验覆盖的资产类型。 enum Kind: String, Codable, CaseIterable, Sendable { case wallet case works case projects case cloudFiles } let kind: Kind let title: String let valueText: String } /// 注销前置核验结果,包含资产快照和注销影响说明。 struct AccountDeletionPrecheck: Equatable, Sendable { let assets: [AccountDeletionAssetSummary] let consequences: [String] } /// 注销申请当前状态。 enum AccountDeletionRequestStatus: String, Codable, Sendable { case pending case canceled case completed } /// 注销申请记录,保存7天冷静期及用户确认的资产范围。 struct AccountDeletionRequest: Codable, Equatable, Sendable { let id: UUID let clientRequestID: UUID let username: String let submittedAt: Date let scheduledDeletionAt: Date var status: AccountDeletionRequestStatus let acknowledgedAssetKinds: [AccountDeletionAssetSummary.Kind] } /// 登录时需要处理的注销状态。 enum AccountDeletionLoginState: Equatable, Sendable { case none case pending(AccountDeletionRequest) case completed(AccountDeletionRequest) } /// 注销短信验证页所需的稳定上下文。 struct AccountDeletionVerificationContext: Equatable, Sendable { let username: String let maskedPhone: String let assets: [AccountDeletionAssetSummary] } /// 注销业务错误,统一提供可直接展示的中文提示。 enum AccountDeletionError: LocalizedError, Equatable { case missingIdentity case invalidVerificationCode case incompleteAcknowledgement case noPendingRequest case cancellationExpired case deletionCompleted case persistenceFailed var errorDescription: String? { switch self { case .missingIdentity: "当前登录账号缺少手机号,暂时无法提交注销" case .invalidVerificationCode: "验证码错误,请输入演示验证码 123456" case .incompleteAcknowledgement: "请先确认已了解全部资产和注销后果" case .noPendingRequest: "当前账号没有待取消的注销申请" case .cancellationExpired: "7天取消期限已过,无法恢复账号" case .deletionCompleted: "账号已完成注销,无法继续登录" case .persistenceFailed: "注销状态保存失败,请稍后重试" } } } /// 注销账号身份工具,统一手机号规范化、掩码和当前登录身份解析。 enum AccountDeletionIdentity { /// 从当前会话读取登录手机号,优先使用登录页保存的原始账号。 static func currentUsername(session: AppSessionStore = AppStore.shared.session) -> String? { let candidates = [session.lastLoginUsername, session.phone] return candidates .compactMap { $0 } .map(normalizedUsername) .first { !$0.isEmpty } } /// 将手机号账号规范化为纯数字,并兼容 +86 前缀。 static func normalizedUsername(_ value: String) -> String { var digits = value.filter(\.isNumber) if digits.hasPrefix("86"), digits.count == 13 { digits.removeFirst(2) } return digits } /// 将手机号转换为用于页面展示的脱敏格式。 static func maskedPhone(_ value: String) -> String { let username = normalizedUsername(value) guard username.count == 11 else { return username } let start = username.prefix(3) let end = username.suffix(4) return "\(start)****\(end)" } } /// 注销时间格式化工具,统一成功页和登录恢复弹窗的中文时间。 enum AccountDeletionDateFormatter { /// 将服务端计划删除时间格式化为中文日期时间。 static func displayText(_ date: Date) -> String { let formatter = DateFormatter() formatter.locale = Locale(identifier: "zh_CN") formatter.timeZone = TimeZone(identifier: "Asia/Shanghai") formatter.dateFormat = "yyyy年M月d日 HH:mm" return formatter.string(from: date) } }