import Foundation import CoreFoundation /// 本机提交意图记录,不代表后端已受理或任何注销业务状态。 protocol StoreAccountDeregistrationSubmissionTracking { /// 是否存在本机尚无法确定结果的提交意图。 var hasUnresolvedSubmission: Bool { get } /// 在发出 POST 前记录意图;无法记录时不允许发出申请。 func recordSubmissionIntent(previousStatus: StoreAccountDeregistrationStatus?) throws /// 仅在服务端明确拒绝本次申请时清除此意图。 func clearRejectedSubmission() /// 用新撤销记录核销提交意图;与重新申请前相同的旧撤销响应不能放行。 func clearCancelledSubmission(matching status: StoreAccountDeregistrationStatus) -> Bool } /// 按环境和门店用户 ID 隔离提交意图;不按手机号保存,不存储凭证、验证码或资产。 final class StoreAccountDeregistrationSubmissionStore: StoreAccountDeregistrationSubmissionTracking { private let defaults: UserDefaults private let key: String /// 注入持久化容器便于测试;生产环境使用当前服务域名隔离测试/正式账号。 init(storeUserID: String, environment: APIEnvironment = .current, defaults: UserDefaults = .standard) { self.defaults = defaults key = "store_deregister_submission_intent_v1_\(environment.baseURL.host ?? "unknown")_\(storeUserID)" } /// 任何残留记录都按待核实处理,不因不认识的本地值而允许重新提交。 var hasUnresolvedSubmission: Bool { defaults.object(forKey: key) != nil } /// UserDefaults 记录提交意图;不把本地写入成功等同于后端收到申请。 func recordSubmissionIntent(previousStatus: StoreAccountDeregistrationStatus? = nil) throws { defaults.set(["previous_cancellation": previousStatus?.cancellationFingerprint ?? ""], forKey: key) guard hasUnresolvedSubmission else { throw SubmissionPersistenceError.unavailable } } /// 只删除当前环境、当前门店用户对应的意图,不影响其他身份。 func clearRejectedSubmission() { defaults.removeObject(forKey: key) } /// 旧版本只会从无记录/草稿提交,其布尔意图可由完整撤销记录核销。 func clearCancelledSubmission(matching status: StoreAccountDeregistrationStatus) -> Bool { guard let fingerprint = status.cancellationFingerprint else { return false } if let stored = defaults.object(forKey: key) { if let record = stored as? [String: String], let previous = record["previous_cancellation"] { guard previous != fingerprint else { return false } } else { // 仅兼容旧版本写入的 true;损坏或未知结构继续等待核验。 guard let value = stored as? NSNumber, CFGetTypeID(value) == CFBooleanGetTypeID(), value.boolValue else { return false } } } defaults.removeObject(forKey: key) return true } } /// 无法保存提交意图时阻止申请,避免在重启后失去重复提交保护。 private enum SubmissionPersistenceError: LocalizedError { case unavailable var errorDescription: String? { "无法保存提交核验记录,请稍后重试" } }