模块化 AppStore 并完善素材管理与个人空间设置。

将会话、权限、位置、收款与排队存储拆分为独立模块,同步更新各 ViewModel 与单元测试,并补充素材管理 UI 与个人空间设置交互。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-13 11:01:08 +08:00
parent ceca780ab3
commit 005349f8e6
128 changed files with 2953 additions and 1123 deletions

View File

@ -97,8 +97,8 @@ final class ScenicQueueViewModel {
/// WebSocket
func startQueueStatsPolling(api: ScenicQueueAPIProtocol) {
guard queueGatePassed,
let scenicId = Int64("\(appStore.currentScenicId)"),
let scenicSpotId = Int64(appStore.scenicQueuePunchSpotId),
let scenicId = Int64("\(appStore.session.currentScenicId)"),
let scenicSpotId = Int64(appStore.scenicQueue.punchSpotId),
scenicId > 0,
scenicSpotId > 0 else { return }
Task {
@ -186,16 +186,16 @@ final class ScenicQueueViewModel {
///
func confirmUserMark(api: ScenicQueueAPIProtocol, markAsFreelancePhotog: Bool, queueBanDays: Int) async {
guard let pendingMark else { return }
guard appStore.currentScenicId > 0 else {
guard appStore.session.currentScenicId > 0 else {
onShowMessage?("请先选择景区")
return
}
let request = ScenicQueueUserMarkRequest(
uid: pendingMark.uid,
scenicId: Int64(appStore.currentScenicId),
scenicId: Int64(appStore.session.currentScenicId),
markAsFreelancePhotog: markAsFreelancePhotog ? 1 : 0,
queueBanDays: markAsFreelancePhotog ? min(max(queueBanDays, 0), 999) : nil,
operatorId: Int(appStore.userId)
operatorId: Int(appStore.session.userId)
)
do {
try await api.userMark(request)
@ -270,7 +270,7 @@ final class ScenicQueueViewModel {
func confirmRequeueTicket(api: ScenicQueueAPIProtocol) async {
guard let pending = pendingRequeue else { return }
dismissRequeueDialog()
guard let operatorId = Int(appStore.userId), operatorId > 0 else {
guard let operatorId = Int(appStore.session.userId), operatorId > 0 else {
onShowMessage?("请先登录")
return
}
@ -395,18 +395,18 @@ final class ScenicQueueViewModel {
}
private func refreshSelectedPunchSpotLine() {
selectedPunchSpotLine = appStore.hasScenicQueuePunchSpotSaved()
? appStore.scenicQueuePunchSpotName.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty ?? "--"
selectedPunchSpotLine = appStore.scenicQueue.hasPunchSpotSaved()
? appStore.scenicQueue.punchSpotName.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty ?? "--"
: "--"
refreshShootingCallConfig()
}
private func refreshQueueGateLocally() {
queueGatePassed = appStore.hasScenicQueuePunchSpotSaved()
queueGatePassed = appStore.scenicQueue.hasPunchSpotSaved()
}
private func refreshShootingCallConfig() {
let snapshot = appStore.scenicQueueSettingsSnapshot() ?? ScenicQueueSettingsSnapshot()
let snapshot = appStore.scenicQueue.settingsSnapshot() ?? ScenicQueueSettingsSnapshot()
showStartShootingButton = snapshot.showStartShootingButton
quickCallButtonEnabled = snapshot.quickCallButtonEnabled
prepareCallButtonEnabled = snapshot.prepareCallButtonEnabled
@ -419,18 +419,18 @@ final class ScenicQueueViewModel {
guard data.exists, let setting = data.setting,
setting.scenicId == nil || setting.scenicId == ids.scenicId,
setting.scenicSpotId == nil || setting.scenicSpotId == ids.scenicSpotId else { return }
let local = appStore.scenicQueueSettingsSnapshot() ?? ScenicQueueSettingsSnapshot()
appStore.saveScenicQueueSettingsSnapshot(setting.toSnapshot(fallback: local))
appStore.scenicQueueRemark = setting.remark ?? ""
let local = appStore.scenicQueue.settingsSnapshot() ?? ScenicQueueSettingsSnapshot()
appStore.scenicQueue.saveSettingsSnapshot(setting.toSnapshot(fallback: local))
appStore.scenicQueue.remark = setting.remark ?? ""
if let voices = setting.voiceBroadcasts {
appStore.saveScenicQueuePresetVoices(
appStore.scenicQueue.savePresetVoices(
voices
.sorted { ($0.sortOrder ?? Int.max) < ($1.sortOrder ?? Int.max) }
.compactMap { $0.content?.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty }
)
}
if let name = setting.scenicSpotName?.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty {
appStore.scenicQueuePunchSpotName = name
appStore.scenicQueue.punchSpotName = name
selectedPunchSpotLine = name
}
refreshShootingCallConfig()
@ -501,7 +501,7 @@ final class ScenicQueueViewModel {
private func handleSocketMessage(_ message: ScenicQueueSocketMessage) {
guard let params = message.data?.params,
let changedSpotId = params.scenicSpotId,
let currentSpotId = Int64(appStore.scenicQueuePunchSpotId),
let currentSpotId = Int64(appStore.scenicQueue.punchSpotId),
changedSpotId == currentSpotId else { return }
switch message.data?.action {
case ScenicQueueSocketAction.queueUpdated.rawValue:
@ -516,7 +516,7 @@ final class ScenicQueueViewModel {
private func handleRemoteTicketCalled(_ params: ScenicQueueSocketParams) {
guard let recordId = params.recordId, recordId > 0 else { return }
if let myUid = Int64(appStore.userId), let operatorUid = params.operatorUid, myUid == operatorUid {
if let myUid = Int64(appStore.session.userId), let operatorUid = params.operatorUid, myUid == operatorUid {
return
}
let eventId = params.eventId?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
@ -614,7 +614,7 @@ final class ScenicQueueViewModel {
}
private func resolveShootingTiming() -> (totalSeconds: Int, intervalSeconds: Int, readableThresholdSeconds: Int) {
let snapshot = appStore.scenicQueueSettingsSnapshot() ?? ScenicQueueSettingsSnapshot()
let snapshot = appStore.scenicQueue.settingsSnapshot() ?? ScenicQueueSettingsSnapshot()
let total = max(0, snapshot.shootMinute * 60 + snapshot.shootSecond)
let interval = (40...60).contains(snapshot.broadcastIntervalSec) ? snapshot.broadcastIntervalSec : 50
let readable = (10...30).contains(snapshot.countdownThresholdSec) ? snapshot.countdownThresholdSec : 15
@ -627,7 +627,7 @@ final class ScenicQueueViewModel {
}
private func speakFollowingTickets(after queueNo: String) async {
let count = (appStore.scenicQueueSettingsSnapshot() ?? ScenicQueueSettingsSnapshot()).autoCallAheadCount.clamped(to: 0...5)
let count = (appStore.scenicQueue.settingsSnapshot() ?? ScenicQueueSettingsSnapshot()).autoCallAheadCount.clamped(to: 0...5)
guard count > 0, let index = currentQueue.firstIndex(where: { $0.queueNo == queueNo }) else { return }
let nextNos = currentQueue.dropFirst(index + 1).prefix(count).map(\.queueNo).filter { !$0.isEmpty }
guard !nextNos.isEmpty else { return }
@ -639,9 +639,9 @@ final class ScenicQueueViewModel {
}
private func currentScenicAndSpotIds() -> (scenicId: Int64, scenicSpotId: Int64)? {
let scenicId = Int64(appStore.currentScenicId)
let scenicId = Int64(appStore.session.currentScenicId)
guard scenicId > 0,
let scenicSpotId = Int64(appStore.scenicQueuePunchSpotId),
let scenicSpotId = Int64(appStore.scenicQueue.punchSpotId),
scenicSpotId > 0 else { return nil }
return (scenicId, scenicSpotId)
}