模块化 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

@ -61,9 +61,9 @@ final class ScenicQueueSettingsViewModel {
init(appStore: AppStore = .shared, ttsManager: ScenicQueueTTSManaging = AliyunNLSTTSManager.shared) {
self.appStore = appStore
self.ttsManager = ttsManager
let snapshot = appStore.scenicQueueSettingsSnapshot() ?? ScenicQueueSettingsSnapshot()
selectedPunchSpotId = appStore.scenicQueuePunchSpotId.nonEmptyTrimmed
selectedPunchSpotLabel = appStore.scenicQueuePunchSpotName
let snapshot = appStore.scenicQueue.settingsSnapshot() ?? ScenicQueueSettingsSnapshot()
selectedPunchSpotId = appStore.scenicQueue.punchSpotId.nonEmptyTrimmed
selectedPunchSpotLabel = appStore.scenicQueue.punchSpotName
shootMinute = snapshot.shootMinute.clamped(to: Limits.shootMinute)
shootSecond = snapshot.shootSecond.clamped(to: Limits.shootSecond)
firstAheadCount = snapshot.firstAheadCount.clamped(to: Limits.ahead)
@ -84,8 +84,8 @@ final class ScenicQueueSettingsViewModel {
businessOpen = snapshot.businessOpen
businessStartTime = Self.normalizedTime(snapshot.businessStartTime, fallback: "10:00")
businessEndTime = Self.normalizedTime(snapshot.businessEndTime, fallback: "20:00")
customTTSText = appStore.scenicQueueRemark
presetVoices = appStore.scenicQueuePresetVoices()
customTTSText = appStore.scenicQueue.remark
presetVoices = appStore.scenicQueue.presetVoices()
}
var selectedPunchSpotDisplayText: String {
@ -105,7 +105,7 @@ final class ScenicQueueSettingsViewModel {
/// 使线
var useOfflineTTS: Bool {
appStore.scenicQueueUseOfflineTTS
appStore.scenicQueue.useOfflineTTS
}
func openSettingChangeLog() {
@ -114,12 +114,12 @@ final class ScenicQueueSettingsViewModel {
///
func loadPunchSpotOptions(api: ScenicQueueAPIProtocol) async {
guard appStore.currentScenicId > 0 else {
guard appStore.session.currentScenicId > 0 else {
onShowMessage?("请先选择景区")
return
}
do {
let response = try await api.scenicSpotListAll(scenicId: String(appStore.currentScenicId))
let response = try await api.scenicSpotListAll(scenicId: String(appStore.session.currentScenicId))
punchSpotOptions = response.list.map {
ScenicQueuePunchSpotOption(id: String($0.id), label: $0.name)
}
@ -178,17 +178,17 @@ final class ScenicQueueSettingsViewModel {
/// 线
func setUseOfflineTTS(_ enabled: Bool) {
guard enabled else {
appStore.scenicQueueUseOfflineTTS = false
appStore.scenicQueue.useOfflineTTS = false
notifyStateChange()
return
}
guard ttsManager.supportsOfflineTTS else {
appStore.scenicQueueUseOfflineTTS = false
appStore.scenicQueue.useOfflineTTS = false
onShowMessage?("离线语音资源未安装")
notifyStateChange()
return
}
appStore.scenicQueueUseOfflineTTS = true
appStore.scenicQueue.useOfflineTTS = true
notifyStateChange()
}
@ -234,7 +234,7 @@ final class ScenicQueueSettingsViewModel {
///
func saveCurrentTextAsPresetVoiceLocal() {
guard appStore.currentScenicId > 0, selectedPunchSpotId?.nonEmptyTrimmed != nil else {
guard appStore.session.currentScenicId > 0, selectedPunchSpotId?.nonEmptyTrimmed != nil else {
onShowMessage?("请先选择打卡点")
return
}
@ -252,7 +252,7 @@ final class ScenicQueueSettingsViewModel {
return
}
presetVoices.append(text)
appStore.saveScenicQueuePresetVoices(presetVoices)
appStore.scenicQueue.savePresetVoices(presetVoices)
onShowMessage?("已保存到本地")
notifyStateChange()
}
@ -260,7 +260,7 @@ final class ScenicQueueSettingsViewModel {
func deletePresetVoice(at index: Int) {
guard presetVoices.indices.contains(index) else { return }
let removed = presetVoices.remove(at: index)
appStore.saveScenicQueuePresetVoices(presetVoices)
appStore.scenicQueue.savePresetVoices(presetVoices)
if ttsLoopAnchorText == removed {
ttsManager.stopCustomTextLoop()
ttsLoopAnchorText = nil
@ -332,11 +332,11 @@ final class ScenicQueueSettingsViewModel {
)
do {
try await api.saveSetting(request)
appStore.scenicQueuePunchSpotId = String(ids.scenicSpotId)
appStore.scenicQueuePunchSpotName = selectedPunchSpotLabel
appStore.scenicQueueRemark = remark
appStore.saveScenicQueuePresetVoices(presetVoices)
appStore.saveScenicQueueSettingsSnapshot(currentSnapshot())
appStore.scenicQueue.punchSpotId = String(ids.scenicSpotId)
appStore.scenicQueue.punchSpotName = selectedPunchSpotLabel
appStore.scenicQueue.remark = remark
appStore.scenicQueue.savePresetVoices(presetVoices)
appStore.scenicQueue.saveSettingsSnapshot(currentSnapshot())
_ = try? await api.stats(scenicId: ids.scenicId, scenicSpotId: ids.scenicSpotId)
onShowMessage?("保存成功")
onNavigateBack?()
@ -346,7 +346,7 @@ final class ScenicQueueSettingsViewModel {
}
private func fetchQueueSettingAfterPunchSpotSelected(_ spotId: String, api: ScenicQueueAPIProtocol) async {
guard let scenicId = Int64("\(appStore.currentScenicId)"),
guard let scenicId = Int64("\(appStore.session.currentScenicId)"),
let scenicSpotId = Int64(spotId),
scenicId > 0,
scenicSpotId > 0 else { return }
@ -480,7 +480,7 @@ final class ScenicQueueSettingsViewModel {
}
private func currentScenicAndSpotIdsForSelected() -> (scenicId: Int64, scenicSpotId: Int64)? {
guard let scenicId = Int64("\(appStore.currentScenicId)"), scenicId > 0,
guard let scenicId = Int64("\(appStore.session.currentScenicId)"), scenicId > 0,
let spotIdText = selectedPunchSpotId?.trimmingCharacters(in: .whitespacesAndNewlines),
let scenicSpotId = Int64(spotIdText), scenicSpotId > 0 else { return nil }
return (scenicId, scenicSpotId)