185 lines
5.6 KiB
Swift
185 lines
5.6 KiB
Swift
//
|
|
// 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?()
|
|
}
|
|
}
|