// // InviteViewModels.swift // suixinkan // import Foundation import UIKit /// 邀请页 ViewModel,负责加载邀请信息、生成二维码和暴露复制内容。 final class PhotographerInviteViewModel { private(set) var isLoading = false private(set) var inviteCode = "" private(set) var inviteUrl = "" private(set) var rules: [String] = [] private(set) var qrImage: UIImage? private(set) var errorMessage: String? var onStateChange: (() -> Void)? /// 重新加载邀请信息。 func reload(api: any InviteServing) async { isLoading = true errorMessage = nil notifyStateChange() do { let response = try await api.inviteInfo() inviteCode = response.inviteCode inviteUrl = response.inviteUrl rules = response.description qrImage = QRCodeGenerator.generateStyledQRCode( content: response.inviteUrl, pixelSize: 560, backgroundColor: .white, cornerRadius: 0 ) } catch { clear() errorMessage = error.localizedDescription } isLoading = false notifyStateChange() } /// 返回待复制的邀请码。 func inviteCodeForCopy() -> String { inviteCode } /// 返回待复制的邀请链接。 func inviteUrlForCopy() -> String { inviteUrl } private func clear() { inviteCode = "" inviteUrl = "" rules = [] qrImage = nil } private func notifyStateChange() { onStateChange?() } } /// 邀请记录 ViewModel,负责邀请用户、奖励流水和钱包汇总分页。 final class InviteRecordViewModel { private(set) var selectedTab: InviteRecordTab = .invite private(set) var totalRewardText = "0.00" private(set) var withdrawableText = "0.00" private(set) var inviteRows: [InviteDisplayRow] = [] private(set) var rewardRows: [WalletTransactionItem] = [] private(set) var isInviteRefreshing = false private(set) var isRewardRefreshing = false private(set) var canLoadMoreInvite = false private(set) var canLoadMoreReward = false private(set) var errorMessage: String? var onStateChange: (() -> Void)? private var invitePage = 1 private let invitePageSize = 20 private var rewardPage = 1 private let rewardPageSize = 10 private var isLoadingInvite = false private var isLoadingReward = false /// 初次加载邀请记录和钱包汇总。 func loadInitial(inviteAPI: any InviteServing, walletAPI: any WalletServing) async { await refreshInvite(inviteAPI: inviteAPI, walletAPI: walletAPI) } /// 切换分段并在需要时加载奖励记录。 func selectTab(_ tab: InviteRecordTab, inviteAPI: any InviteServing, walletAPI: any WalletServing) async { guard tab != selectedTab else { return } selectedTab = tab notifyStateChange() if tab == .reward, rewardRows.isEmpty { await refreshReward(walletAPI: walletAPI) } } /// 刷新邀请记录和钱包汇总。 func refreshInvite(inviteAPI: any InviteServing, walletAPI: any WalletServing) async { await loadSummary(walletAPI: walletAPI) await loadInviteRows(inviteAPI: inviteAPI, refresh: true) } /// 加载下一页邀请记录。 func loadMoreInvite(inviteAPI: any InviteServing) async { guard canLoadMoreInvite, !isInviteRefreshing else { return } await loadInviteRows(inviteAPI: inviteAPI, refresh: false) } /// 刷新奖励记录和钱包汇总。 func refreshReward(walletAPI: any WalletServing) async { await loadSummary(walletAPI: walletAPI) await loadRewardRows(walletAPI: walletAPI, refresh: true) } /// 加载下一页奖励记录。 func loadMoreReward(walletAPI: any WalletServing) async { guard canLoadMoreReward, !isRewardRefreshing else { return } await loadRewardRows(walletAPI: walletAPI, refresh: false) } private func loadSummary(walletAPI: any WalletServing) async { do { let summary = try await walletAPI.walletSummary(type: 2) totalRewardText = summary.amountTotal.isEmpty ? "0.00" : summary.amountTotal withdrawableText = summary.amountWithdrawable.isEmpty ? "0.00" : summary.amountWithdrawable } catch { errorMessage = error.localizedDescription } notifyStateChange() } private func loadInviteRows(inviteAPI: any InviteServing, refresh: Bool) async { if isLoadingInvite && !refresh { return } if refresh { invitePage = 1 } isLoadingInvite = true isInviteRefreshing = refresh errorMessage = nil notifyStateChange() do { let users = try await inviteAPI.inviteUserList(page: invitePage, pageSize: invitePageSize) let mapped = users.map { user in InviteDisplayRow( id: "invite_\(user.id)", title: user.realName.isEmpty ? "**\(user.phone.suffix(4))" : user.realName, phone: user.phone, avatar: user.avatar, time: user.createdAt, inviteLevel: user.inviteLevel ) } inviteRows = refresh ? mapped : inviteRows + mapped invitePage += 1 canLoadMoreInvite = users.count >= invitePageSize } catch { if refresh { inviteRows = [] canLoadMoreInvite = false } errorMessage = error.localizedDescription } isLoadingInvite = false isInviteRefreshing = false notifyStateChange() } private func loadRewardRows(walletAPI: any WalletServing, refresh: Bool) async { if isLoadingReward && !refresh { return } if refresh { rewardPage = 1 } isLoadingReward = true isRewardRefreshing = refresh errorMessage = nil notifyStateChange() do { let response = try await walletAPI.walletTransactionList( startDate: "2025-01-01", endDate: Self.dayFormatter.string(from: Date()), page: rewardPage, pageSize: rewardPageSize, type: 2 ) rewardRows = refresh ? response.list : rewardRows + response.list rewardPage += 1 canLoadMoreReward = response.list.count >= rewardPageSize && rewardRows.count < response.total } catch { if refresh { rewardRows = [] canLoadMoreReward = false } errorMessage = error.localizedDescription } isLoadingReward = false isRewardRefreshing = false notifyStateChange() } private func notifyStateChange() { onStateChange?() } private static let dayFormatter: DateFormatter = { let formatter = DateFormatter() formatter.locale = Locale(identifier: "zh_CN") formatter.dateFormat = "yyyy-MM-dd" return formatter }() }