将会话、权限、位置、收款与排队存储拆分为独立模块,同步更新各 ViewModel 与单元测试,并补充素材管理 UI 与个人空间设置交互。 Co-authored-by: Cursor <cursoragent@cursor.com>
216 lines
7.3 KiB
Swift
216 lines
7.3 KiB
Swift
//
|
|
// CooperationAcquirerViewModel.swift
|
|
// suixinkan
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// 合作获客员 ViewModel,对齐 Android `CooperationAcquirerViewModel`。
|
|
final class CooperationAcquirerViewModel {
|
|
|
|
private(set) var acquirers: [CooperativeSalerEntity] = []
|
|
private(set) var isRefreshing = false
|
|
private(set) var remarkEditingAcquirer: CooperativeSalerEntity?
|
|
private(set) var remarkDraft = ""
|
|
private(set) var commissionEditingAcquirer: CooperativeSalerEntity?
|
|
private(set) var commissionRateDraft = ""
|
|
private(set) var commissionSmsCodeDraft = ""
|
|
private(set) var commissionSmsCountdown = 0
|
|
|
|
var onStateChange: (() -> Void)?
|
|
var onShowMessage: ((String) -> Void)?
|
|
var onNavigateBindAcquirer: ((Int) -> Void)?
|
|
|
|
private let appStore: AppStore
|
|
private var commissionSmsTimerTask: Task<Void, Never>?
|
|
|
|
init(appStore: AppStore = .shared) {
|
|
self.appStore = appStore
|
|
}
|
|
|
|
deinit {
|
|
commissionSmsTimerTask?.cancel()
|
|
}
|
|
|
|
func loadAcquirers(api: OrderAPI, initial: Bool = false) async {
|
|
let storeId = appStore.session.currentStoreId
|
|
let storeIdParam = storeId > 0 ? String(storeId) : nil
|
|
do {
|
|
let response = try await api.cooperativeSalers(storeId: storeIdParam, pageSize: "20")
|
|
acquirers = response.list
|
|
} catch {
|
|
onShowMessage?(error.localizedDescription)
|
|
}
|
|
isRefreshing = false
|
|
notifyStateChange()
|
|
}
|
|
|
|
func refresh(api: OrderAPI) async {
|
|
isRefreshing = true
|
|
notifyStateChange()
|
|
await loadAcquirers(api: api, initial: false)
|
|
}
|
|
|
|
func startEditRemark(_ acquirer: CooperativeSalerEntity) {
|
|
remarkEditingAcquirer = acquirer
|
|
remarkDraft = acquirer.photogRemarkName
|
|
notifyStateChange()
|
|
}
|
|
|
|
func updateRemarkDraft(_ text: String) {
|
|
remarkDraft = String(text.prefix(CooperationOrderFeature.remarkNameMaxLength))
|
|
notifyStateChange()
|
|
}
|
|
|
|
func dismissRemarkDialog() {
|
|
remarkEditingAcquirer = nil
|
|
remarkDraft = ""
|
|
notifyStateChange()
|
|
}
|
|
|
|
func saveRemark(api: OrderAPI) async {
|
|
guard let acquirer = remarkEditingAcquirer else { return }
|
|
let saleUserId = acquirer.saleUserId
|
|
guard saleUserId > 0 else { return }
|
|
let trimmedRemark = remarkDraft.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
if let message = CooperationOrderFeature.remarkNameValidationMessage(remarkDraft) {
|
|
onShowMessage?(message)
|
|
return
|
|
}
|
|
dismissRemarkDialog()
|
|
do {
|
|
try await api.saleUserUpdateRemark(saleUserId: saleUserId, photogRemarkName: trimmedRemark)
|
|
acquirers = acquirers.map { item in
|
|
item.saleUserId == saleUserId ? item.withRemarkName(trimmedRemark) : item
|
|
}
|
|
onShowMessage?("备注已保存")
|
|
notifyStateChange()
|
|
} catch {
|
|
onShowMessage?(error.localizedDescription)
|
|
}
|
|
}
|
|
|
|
func startEditCommissionRate(_ acquirer: CooperativeSalerEntity) {
|
|
stopCommissionSmsTimer()
|
|
commissionEditingAcquirer = acquirer
|
|
commissionRateDraft = String(acquirer.commissionRate)
|
|
commissionSmsCodeDraft = ""
|
|
notifyStateChange()
|
|
}
|
|
|
|
func updateCommissionRateDraft(_ text: String) {
|
|
commissionRateDraft = String(text.filter(\.isNumber).prefix(3))
|
|
notifyStateChange()
|
|
}
|
|
|
|
func updateCommissionSmsCodeDraft(_ text: String) {
|
|
commissionSmsCodeDraft = String(text.filter(\.isNumber).prefix(6))
|
|
notifyStateChange()
|
|
}
|
|
|
|
func dismissCommissionRateDialog() {
|
|
commissionEditingAcquirer = nil
|
|
commissionRateDraft = ""
|
|
commissionSmsCodeDraft = ""
|
|
stopCommissionSmsTimer()
|
|
notifyStateChange()
|
|
}
|
|
|
|
func requestCommissionSmsCode(api: OrderAPI) async {
|
|
guard let acquirer = commissionEditingAcquirer else { return }
|
|
if commissionSmsCountdown > 0 {
|
|
onShowMessage?("请在倒计时结束后重新获取")
|
|
return
|
|
}
|
|
let phone = acquirer.salerPhone.isEmpty ? acquirer.displayPhone : acquirer.salerPhone
|
|
let trimmedPhone = phone.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
guard !trimmedPhone.isEmpty else {
|
|
onShowMessage?("获客员手机号为空")
|
|
return
|
|
}
|
|
do {
|
|
try await api.saleUserCommissionRateSendSmsCode(phone: trimmedPhone)
|
|
startCommissionSmsTimer()
|
|
onShowMessage?("验证码已发送")
|
|
} catch {
|
|
onShowMessage?(error.localizedDescription)
|
|
}
|
|
}
|
|
|
|
func saveCommissionRate(api: OrderAPI) async {
|
|
guard let acquirer = commissionEditingAcquirer else { return }
|
|
let saleUserId = acquirer.saleUserId
|
|
guard saleUserId > 0 else { return }
|
|
guard let commissionRate = Int(commissionRateDraft.trimmingCharacters(in: .whitespacesAndNewlines)) else {
|
|
onShowMessage?("请输入分成比例")
|
|
return
|
|
}
|
|
guard (0...100).contains(commissionRate) else {
|
|
onShowMessage?("分成比例需在0到100之间")
|
|
return
|
|
}
|
|
let smsCode = commissionSmsCodeDraft.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
guard !smsCode.isEmpty else {
|
|
onShowMessage?("请输入验证码")
|
|
return
|
|
}
|
|
do {
|
|
let updated = try await api.saleUserUpdateCommissionRate(
|
|
saleUserId: saleUserId,
|
|
commissionRate: commissionRate,
|
|
smsCode: smsCode
|
|
)
|
|
dismissCommissionRateDialog()
|
|
if updated.saleUserId > 0 {
|
|
acquirers = acquirers.map { item in
|
|
item.saleUserId == updated.saleUserId ? updated : item
|
|
}
|
|
} else {
|
|
await loadAcquirers(api: api, initial: false)
|
|
}
|
|
onShowMessage?("分成比例已修改")
|
|
notifyStateChange()
|
|
} catch {
|
|
onShowMessage?(error.localizedDescription)
|
|
}
|
|
}
|
|
|
|
func processScanResult(_ scanResult: String) {
|
|
guard let saleUserId = SaleUserQrParser.parseSaleUserId(scanResult), saleUserId > 0 else {
|
|
onShowMessage?("请扫描正确的获客员二维码")
|
|
return
|
|
}
|
|
onNavigateBindAcquirer?(saleUserId)
|
|
}
|
|
|
|
private func startCommissionSmsTimer() {
|
|
commissionSmsTimerTask?.cancel()
|
|
commissionSmsCountdown = 60
|
|
notifyStateChange()
|
|
commissionSmsTimerTask = Task { [weak self] in
|
|
guard let self else { return }
|
|
try? await Task.sleep(nanoseconds: 1_000_000_000)
|
|
for second in stride(from: 59, through: 1, by: -1) {
|
|
if Task.isCancelled { return }
|
|
commissionSmsCountdown = second
|
|
notifyStateChange()
|
|
try? await Task.sleep(nanoseconds: 1_000_000_000)
|
|
}
|
|
if !Task.isCancelled {
|
|
commissionSmsCountdown = 0
|
|
notifyStateChange()
|
|
}
|
|
}
|
|
}
|
|
|
|
private func stopCommissionSmsTimer() {
|
|
commissionSmsTimerTask?.cancel()
|
|
commissionSmsTimerTask = nil
|
|
commissionSmsCountdown = 0
|
|
}
|
|
|
|
private func notifyStateChange() {
|
|
onStateChange?()
|
|
}
|
|
}
|