feat: 优化账号注销流程
This commit is contained in:
+51
-5
@@ -23,6 +23,8 @@ final class StoreAccountDeregistrationViewModel {
|
||||
private(set) var submissionAttempted = false
|
||||
/// 成功响应仅代表申请已提交,不代表账号注销完成。
|
||||
private(set) var submissionAccepted = false
|
||||
/// 申请明确受理后,以当前凭证只读查询到的冷静期截止时间;查询失败不改变受理结果。
|
||||
private(set) var submittedCoolingUntil: String?
|
||||
private let storeUserID: Int
|
||||
private let submissionStore: (any StoreAccountDeregistrationSubmissionTracking)?
|
||||
private var previousCancellationFingerprint: String?
|
||||
@@ -179,20 +181,42 @@ final class StoreAccountDeregistrationViewModel {
|
||||
} catch { invalidate(error); return false }
|
||||
}
|
||||
|
||||
/// 校验输入后申请;服务端明确接受即交由页面退出,不再查询状态或推断正式完成。
|
||||
/// 同步校验验证码与必填原因;失败时不启动查询、持久化或申请请求。
|
||||
@discardableResult
|
||||
func validateSubmissionInput(smsCode: String, reason: String) -> Bool {
|
||||
let code = smsCode.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let reason = reason.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if code.isEmpty {
|
||||
errorMessage = "请输入收到的短信验证码"
|
||||
return false
|
||||
}
|
||||
if reason.isEmpty {
|
||||
errorMessage = "请输入注销原因"
|
||||
return false
|
||||
}
|
||||
if reason.count > 50 {
|
||||
errorMessage = "注销原因不能超过 50 个字"
|
||||
return false
|
||||
}
|
||||
errorMessage = nil
|
||||
return true
|
||||
}
|
||||
|
||||
/// 校验输入后申请;服务端明确接受后只读查询一次冷静期截止时间,再交由页面退出。
|
||||
func submit(smsCode: String, reason: String, api: any StoreAccountDeregistrationServing) async {
|
||||
guard canContinue, step == .verification else { return }
|
||||
let code = smsCode.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let reason = reason.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !code.isEmpty, !reason.isEmpty else {
|
||||
errorMessage = "请输入收到的短信验证码和注销原因"
|
||||
return
|
||||
}
|
||||
guard validateSubmissionInput(smsCode: code, reason: reason) else { return }
|
||||
var verifiedEligibility: StoreAccountDeregistrationEligibility?
|
||||
var verifiedStatus: StoreAccountDeregistrationStatus?
|
||||
isBusy = true
|
||||
errorMessage = nil
|
||||
defer { isBusy = false }
|
||||
do {
|
||||
try await checkReady(api: api)
|
||||
verifiedEligibility = eligibility
|
||||
verifiedStatus = status
|
||||
previousCancellationFingerprint = status?.cancellationFingerprint
|
||||
try submissionStore?.recordSubmissionIntent(previousStatus: status)
|
||||
submissionAttempted = true
|
||||
@@ -200,7 +224,21 @@ final class StoreAccountDeregistrationViewModel {
|
||||
eligibility = nil
|
||||
try await api.apply(smsCode: code, reason: reason)
|
||||
submissionAccepted = true
|
||||
// 申请成功已经成立;状态查询只用于结果页展示,失败或尚未进入冷静期都不能推翻成功响应。
|
||||
if let acceptedStatus = try? await api.status(), acceptedStatus.isCooling {
|
||||
status = acceptedStatus
|
||||
submittedCoolingUntil = acceptedStatus.coolingUntil
|
||||
}
|
||||
} catch {
|
||||
if !submissionAccepted, isVerificationCodeRejection(error) {
|
||||
submissionStore?.clearRejectedSubmission()
|
||||
submissionAttempted = false
|
||||
eligibility = verifiedEligibility
|
||||
status = verifiedStatus
|
||||
step = .verification
|
||||
errorMessage = error.localizedDescription
|
||||
return
|
||||
}
|
||||
// 业务码明确拒绝申请时可重新核验;网络/解码错误不能证明服务端没有收到申请。
|
||||
if !submissionAccepted, case APIError.serverCode = error {
|
||||
submissionStore?.clearRejectedSubmission()
|
||||
@@ -211,6 +249,14 @@ final class StoreAccountDeregistrationViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
/// 旧接口没有独立错误码,验证码拒绝只能按服务端业务提示识别。
|
||||
private func isVerificationCodeRejection(_ error: Error) -> Bool {
|
||||
guard case let APIError.serverCode(_, message) = error else { return false }
|
||||
return message.localizedCaseInsensitiveContains("验证码")
|
||||
|| message.localizedCaseInsensitiveContains("verification code")
|
||||
|| message.localizedCaseInsensitiveContains("sms code")
|
||||
}
|
||||
|
||||
private func checkReady(api: any StoreAccountDeregistrationServing) async throws {
|
||||
try await reload(api: api)
|
||||
guard status?.permitsPreparation == true, eligibility?.permitsApplication == true else {
|
||||
|
||||
Reference in New Issue
Block a user