Initial commit: suixinkan_ios UIKit rewrite project.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -0,0 +1,172 @@
|
||||
//
|
||||
// ScenicQueueSettingsStore.swift
|
||||
// suixinkan
|
||||
//
|
||||
// Created by Codex on 2026/6/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// 排队管理本地设置 key。
|
||||
enum ScenicQueueLocalSettings {
|
||||
static let selectedSpotIdKey = "scenic_queue_selected_spot_id"
|
||||
static let selectedSpotNameKey = "scenic_queue_selected_spot_name"
|
||||
static let customTtsTextKey = "scenic_queue_custom_tts_text"
|
||||
static let settingsSnapshotKey = "scenic_queue_settings_snapshot"
|
||||
static let photoEstimateSecondsKey = "scenic_queue_photo_estimate_seconds"
|
||||
static let broadcastIntervalSecondsKey = "scenic_queue_broadcast_interval_seconds"
|
||||
static let countdownThresholdSecondsKey = "scenic_queue_countdown_threshold_seconds"
|
||||
static let showStartShootingButtonKey = "scenic_queue_show_start_shooting_button"
|
||||
static let autoCallAheadCountKey = "scenic_queue_auto_call_ahead_count"
|
||||
static let quickCallButtonEnabledKey = "scenic_queue_quick_call_button_enabled"
|
||||
static let prepareCallButtonEnabledKey = "scenic_queue_prepare_call_button_enabled"
|
||||
static let presetVoicesKey = "scenic_queue_preset_voices"
|
||||
static let ttsEnabledKey = "scenic_queue_tts_enabled"
|
||||
static let backgroundPollEnabledKey = "scenic_queue_background_poll_enabled"
|
||||
}
|
||||
|
||||
/// 排队管理本地设置存储,按用户、景区、打卡点隔离并兼容旧 key。
|
||||
enum ScenicQueueSettingsStore {
|
||||
/// 生成景区作用域 key。
|
||||
static func scopedKey(base: String, userId: String?, scenicId: Int?) -> String {
|
||||
guard let scenicId else { return base }
|
||||
let user = normalizedUserId(userId)
|
||||
return "\(base)_user_\(user)_scenic_\(scenicId)"
|
||||
}
|
||||
|
||||
/// 生成打卡点作用域 key。
|
||||
static func scopedSpotKey(base: String, userId: String?, scenicId: Int?, spotId: Int?) -> String {
|
||||
guard let scenicId, let spotId else { return scopedKey(base: base, userId: userId, scenicId: scenicId) }
|
||||
let user = normalizedUserId(userId)
|
||||
return "\(base)_user_\(user)_scenic_\(scenicId)_spot_\(spotId)"
|
||||
}
|
||||
|
||||
/// 读取已选打卡点 ID。
|
||||
static func selectedSpotId(userId: String?, scenicId: Int?) -> Int? {
|
||||
guard let scenicId else { return legacyPositiveInt(forKey: ScenicQueueLocalSettings.selectedSpotIdKey) }
|
||||
let key = scopedKey(base: ScenicQueueLocalSettings.selectedSpotIdKey, userId: userId, scenicId: scenicId)
|
||||
if let value = positiveInt(forKey: key) { return value }
|
||||
if let legacy = legacyPositiveInt(forKey: ScenicQueueLocalSettings.selectedSpotIdKey) {
|
||||
UserDefaults.standard.set(legacy, forKey: key)
|
||||
return legacy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// 读取已选打卡点名称。
|
||||
static func selectedSpotName(userId: String?, scenicId: Int?) -> String {
|
||||
guard let scenicId else {
|
||||
return UserDefaults.standard.string(forKey: ScenicQueueLocalSettings.selectedSpotNameKey) ?? ""
|
||||
}
|
||||
let key = scopedKey(base: ScenicQueueLocalSettings.selectedSpotNameKey, userId: userId, scenicId: scenicId)
|
||||
if let value = UserDefaults.standard.string(forKey: key), !value.isEmpty { return value }
|
||||
let legacy = UserDefaults.standard.string(forKey: ScenicQueueLocalSettings.selectedSpotNameKey) ?? ""
|
||||
if !legacy.isEmpty {
|
||||
UserDefaults.standard.set(legacy, forKey: key)
|
||||
}
|
||||
return legacy
|
||||
}
|
||||
|
||||
/// 保存已选打卡点。
|
||||
static func saveSelectedSpot(id: Int, name: String, userId: String?, scenicId: Int) {
|
||||
let defaults = UserDefaults.standard
|
||||
defaults.set(id, forKey: ScenicQueueLocalSettings.selectedSpotIdKey)
|
||||
defaults.set(name, forKey: ScenicQueueLocalSettings.selectedSpotNameKey)
|
||||
defaults.set(id, forKey: scopedKey(base: ScenicQueueLocalSettings.selectedSpotIdKey, userId: userId, scenicId: scenicId))
|
||||
defaults.set(name, forKey: scopedKey(base: ScenicQueueLocalSettings.selectedSpotNameKey, userId: userId, scenicId: scenicId))
|
||||
}
|
||||
|
||||
/// 读取自定义语音文本。
|
||||
static func customTtsText(userId: String?, scenicId: Int?, spotId: Int?) -> String {
|
||||
guard let scenicId, let spotId else {
|
||||
return UserDefaults.standard.string(forKey: ScenicQueueLocalSettings.customTtsTextKey) ?? ""
|
||||
}
|
||||
let key = scopedSpotKey(base: ScenicQueueLocalSettings.customTtsTextKey, userId: userId, scenicId: scenicId, spotId: spotId)
|
||||
if let value = UserDefaults.standard.string(forKey: key) { return value }
|
||||
let legacy = UserDefaults.standard.string(forKey: ScenicQueueLocalSettings.customTtsTextKey) ?? ""
|
||||
if !legacy.isEmpty {
|
||||
UserDefaults.standard.set(legacy, forKey: key)
|
||||
}
|
||||
return legacy
|
||||
}
|
||||
|
||||
/// 保存自定义语音文本。
|
||||
static func saveCustomTtsText(_ text: String, userId: String?, scenicId: Int?, spotId: Int?) {
|
||||
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
UserDefaults.standard.set(trimmed, forKey: ScenicQueueLocalSettings.customTtsTextKey)
|
||||
if let scenicId, let spotId {
|
||||
UserDefaults.standard.set(
|
||||
trimmed,
|
||||
forKey: scopedSpotKey(base: ScenicQueueLocalSettings.customTtsTextKey, userId: userId, scenicId: scenicId, spotId: spotId)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// 读取设置快照。
|
||||
static func settingsSnapshot(userId: String?, scenicId: Int, spotId: Int) -> ScenicQueueSettingsSnapshot? {
|
||||
let keys = [
|
||||
scopedSpotKey(base: ScenicQueueLocalSettings.settingsSnapshotKey, userId: userId, scenicId: scenicId, spotId: spotId),
|
||||
"scenic_\(scenicId)_spot_\(spotId)_\(ScenicQueueLocalSettings.settingsSnapshotKey)",
|
||||
"scenic_\(scenicId)_\(ScenicQueueLocalSettings.settingsSnapshotKey)"
|
||||
]
|
||||
for key in keys {
|
||||
guard let data = UserDefaults.standard.data(forKey: key),
|
||||
let snapshot = try? JSONDecoder().decode(ScenicQueueSettingsSnapshot.self, from: data)
|
||||
else { continue }
|
||||
return snapshot
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// 保存设置快照。
|
||||
static func saveSettingsSnapshot(_ snapshot: ScenicQueueSettingsSnapshot, userId: String?, scenicId: Int, spotId: Int) {
|
||||
guard let data = try? JSONEncoder().encode(snapshot) else { return }
|
||||
UserDefaults.standard.set(
|
||||
data,
|
||||
forKey: scopedSpotKey(base: ScenicQueueLocalSettings.settingsSnapshotKey, userId: userId, scenicId: scenicId, spotId: spotId)
|
||||
)
|
||||
UserDefaults.standard.set(data, forKey: "scenic_\(scenicId)_spot_\(spotId)_\(ScenicQueueLocalSettings.settingsSnapshotKey)")
|
||||
}
|
||||
|
||||
/// 读取语音预设。
|
||||
static func presetVoices(userId: String?, scenicId: Int?, spotId: Int?) -> [String] {
|
||||
guard let scenicId, let spotId else { return [] }
|
||||
let keys = [
|
||||
scopedSpotKey(base: ScenicQueueLocalSettings.presetVoicesKey, userId: userId, scenicId: scenicId, spotId: spotId),
|
||||
"scenic_\(scenicId)_spot_\(spotId)_\(ScenicQueueLocalSettings.presetVoicesKey)"
|
||||
]
|
||||
for key in keys {
|
||||
guard let data = UserDefaults.standard.data(forKey: key),
|
||||
let voices = try? JSONDecoder().decode([String].self, from: data)
|
||||
else { continue }
|
||||
return voices
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
/// 保存语音预设。
|
||||
static func savePresetVoices(_ voices: [String], userId: String?, scenicId: Int, spotId: Int) {
|
||||
let normalized = Array(voices.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }.prefix(5))
|
||||
guard let data = try? JSONEncoder().encode(normalized) else { return }
|
||||
UserDefaults.standard.set(
|
||||
data,
|
||||
forKey: scopedSpotKey(base: ScenicQueueLocalSettings.presetVoicesKey, userId: userId, scenicId: scenicId, spotId: spotId)
|
||||
)
|
||||
UserDefaults.standard.set(data, forKey: "scenic_\(scenicId)_spot_\(spotId)_\(ScenicQueueLocalSettings.presetVoicesKey)")
|
||||
}
|
||||
|
||||
private static func normalizedUserId(_ userId: String?) -> String {
|
||||
let text = userId?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
return text.isEmpty ? "anonymous" : text
|
||||
}
|
||||
|
||||
private static func positiveInt(forKey key: String) -> Int? {
|
||||
guard UserDefaults.standard.object(forKey: key) != nil else { return nil }
|
||||
let value = UserDefaults.standard.integer(forKey: key)
|
||||
return value > 0 ? value : nil
|
||||
}
|
||||
|
||||
private static func legacyPositiveInt(forKey key: String) -> Int? {
|
||||
positiveInt(forKey: key)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user