import Foundation /// 门店身份注销依赖;网络服务在主线程协调,ViewModel 自身不绑定 MainActor。 @MainActor protocol StoreAccountDeregistrationStatusServing { /// 只读查询当前注销记录,供进入普通业务前核验。 func status() async throws -> StoreAccountDeregistrationStatus } /// 完整注销服务,在状态查询之外提供条件、确认和申请操作。 @MainActor protocol StoreAccountDeregistrationServing: StoreAccountDeregistrationStatusServing { /// 获取资产与全部注销条件。 func eligibility() async throws -> StoreAccountDeregistrationEligibility /// 分别确认现金余额。 func waiveWallet() async throws /// 分别确认积分。 func waivePoints() async throws /// 向当前身份的绑定手机号发送短信。 func sendSMS() async throws /// 提交用户输入的验证码与注销原因。 func apply(smsCode: String, reason: String) async throws /// 服务端判定是否允许撤销,不按本机截止时间判断。 func cancel() async throws } /// 旧门店身份注销接口请求层;仅使用提供的 account-deregister 路径和请求字段。 @MainActor final class StoreAccountDeregistrationAPI: StoreAccountDeregistrationServing { private let client: APIClient private let identity: StoreAccountDeregistrationIdentity private let isCurrentIdentity: @MainActor () -> Bool private let basePath = "/api/yf-handset-app/account-deregister" /// 注入网络客户端、冻结的门店身份和当前会话校验,便于隔离真实与测试请求。 init( client: APIClient, identity: StoreAccountDeregistrationIdentity, isCurrentIdentity: @escaping @MainActor () -> Bool ) { self.client = client self.identity = identity self.isCurrentIdentity = isCurrentIdentity } /// 查询当前门店身份的资产、风险期和全部阻断项。 func eligibility() async throws -> StoreAccountDeregistrationEligibility { let value: StoreAccountDeregistrationEligibility = try await send(APIRequest(method: .get, path: basePath + "/eligibility")) guard String(value.storeUserID) == identity.userID else { throw StoreAccountDeregistrationError.sessionChanged } return value } /// 调用旧现金确认接口;与积分接口的顺序协调由ViewModel负责。 func waiveWallet() async throws { let _: EmptyPayload = try await send(APIRequest( method: .post, path: basePath + "/waivers/wallet", body: StoreAccountDeregistrationWaiverRequest() )) } /// 调用旧积分确认接口;与现金接口的顺序协调由ViewModel负责。 func waivePoints() async throws { let _: EmptyPayload = try await send(APIRequest( method: .post, path: basePath + "/waivers/points", body: StoreAccountDeregistrationWaiverRequest() )) } /// 向当前门店身份绑定手机号发送短信;收件人由后端从 Token 确定。 func sendSMS() async throws { let _: EmptyPayload = try await send(APIRequest(method: .post, path: basePath + "/send-sms")) } /// 使用用户输入的验证码和原因提交申请;成功后需查询服务端状态。 func apply(smsCode: String, reason: String) async throws { let _: EmptyPayload = try await send(APIRequest( method: .post, path: basePath + "/apply", body: StoreAccountDeregistrationApplyRequest(smsCode: smsCode, reason: reason) )) } /// 查询当前身份的草稿、冷静期、撤销、阻断或完成状态,不触发重新登录。 func status() async throws -> StoreAccountDeregistrationStatus { try await send(APIRequest(method: .get, path: basePath + "/status")) } /// 正式完成前由用户主动撤销;是否可撤销始终由服务端判定。 func cancel() async throws { let _: EmptyPayload = try await send(APIRequest(method: .post, path: basePath + "/cancel")) } private func send(_ request: APIRequest) async throws -> Response { guard isCurrentIdentity() else { throw StoreAccountDeregistrationError.sessionChanged } var sensitiveRequest = request sensitiveRequest.logsPayload = false let response = try await client.send(sensitiveRequest, tokenOverride: identity.token) guard isCurrentIdentity() else { throw StoreAccountDeregistrationError.sessionChanged } return response } }