// // ScenicQueueModels.swift // suixinkan // import Foundation /// 当前排队列表中的叫号状态。 enum ScenicQueueCallStatus: Hashable { case waiting case called } /// 当前排队卡片在指定拍摄会话与设置下的操作展示状态。 struct ScenicQueueTicketActionState: Equatable { let skipEnabled: Bool let primaryVisible: Bool let primaryEnabled: Bool let primaryTitle: String let recallVisible: Bool let completeVisible: Bool /// 按 Android 排队卡片规则生成操作状态。 static func make( ticket: ScenicQueueTicket, shootingQueueNo: String?, remainSeconds: Int, showStartShootingButton: Bool ) -> ScenicQueueTicketActionState { let isShooting = shootingQueueNo == ticket.queueNo if isShooting { return ScenicQueueTicketActionState( skipEnabled: false, primaryVisible: true, primaryEnabled: false, primaryTitle: "剩余 \(max(0, remainSeconds)) 秒", recallVisible: false, completeVisible: ticket.showCompleteAction ) } let primaryVisible = ticket.status == .waiting || showStartShootingButton return ScenicQueueTicketActionState( skipEnabled: true, primaryVisible: primaryVisible, primaryEnabled: true, primaryTitle: ticket.status == .waiting ? "叫号" : "开始拍摄", recallVisible: ticket.status == .called, completeVisible: ticket.showCompleteAction ) } } /// 排队首页 Diffable Data Source 的稳定列表项。 enum ScenicQueueListItem: Hashable { case current(ScenicQueueTicket) case skipped(ScenicQueueSkippedTicket) } /// 当前排队列表展示实体。 struct ScenicQueueTicket: Hashable { let recordId: Int64 let queueNo: String let phoneMasked: String let dialPhoneDigits: String let status: ScenicQueueCallStatus let statusText: String let waitMinutes: Int let peopleAhead: Int let showCompleteAction: Bool let queueBanLabel: String let identityTag: String let queueTimeDisplay: String let queueCountToday: Int let uid: Int64 let markAsPhotog: Int let markAsFreelancePhotog: Int let isMissRequeue: Bool let missRequeueText: String /// 是否展示身份角标,普通用户不展示。 var shouldShowIdentityTag: Bool { let text = identityTag.trimmingCharacters(in: .whitespacesAndNewlines) return !text.isEmpty && text != "普通用户" } /// 返回叫号后本地更新后的副本。 func markedCalled(statusText newStatusText: String) -> ScenicQueueTicket { ScenicQueueTicket( recordId: recordId, queueNo: queueNo, phoneMasked: phoneMasked, dialPhoneDigits: dialPhoneDigits, status: .called, statusText: newStatusText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? statusText : newStatusText, waitMinutes: waitMinutes, peopleAhead: peopleAhead, showCompleteAction: true, queueBanLabel: queueBanLabel, identityTag: identityTag, queueTimeDisplay: queueTimeDisplay, queueCountToday: queueCountToday, uid: uid, markAsPhotog: markAsPhotog, markAsFreelancePhotog: markAsFreelancePhotog, isMissRequeue: isMissRequeue, missRequeueText: missRequeueText ) } } /// 过号列表展示实体。 struct ScenicQueueSkippedTicket: Hashable { let recordId: Int64 let queueNo: String let phoneMasked: String let dialPhoneDigits: String let statusText: String let skippedTimeDisplay: String let queueBanLabel: String let isMissRequeue: Bool let missRequeueText: String } /// 排队页顶部统计。 struct ScenicQueueSummary: Equatable { let peopleInQueue: Int let avgWaitMinutes: Int static let empty = ScenicQueueSummary(peopleInQueue: 0, avgWaitMinutes: 0) } /// 打卡点下拉选项。 struct ScenicQueuePunchSpotOption: Hashable { let id: String let label: String } /// 排队设置本地快照,用于快速恢复 UI 与叫号配置。 struct ScenicQueueSettingsSnapshot: Codable, Equatable { var shootMinute: Int = 0 var shootSecond: Int = 0 var firstAheadCount: Int = 0 var firstSms: Bool = false var firstPhone: Bool = false var secondAheadCount: Int = 0 var secondSms: Bool = false var secondPhone: Bool = false var broadcastIntervalSec: Int = 50 var countdownThresholdSec: Int = 15 var queueDistanceMeter: Int = 0 var queueTakeLimit: Int = 0 var missCallRequeueOffset: Int = 1 var showStartShootingButton: Bool = true var autoCallAheadCount: Int = 0 var quickCallButtonEnabled: Bool = false var prepareCallButtonEnabled: Bool = false var businessOpen: Bool = true var businessStartTime: String = "10:00" var businessEndTime: String = "20:00" } /// 待确认的过号操作。 struct ScenicQueuePendingSkip: Equatable { let recordId: Int64 let queueNo: String } /// 待确认的重新排队操作。 struct ScenicQueuePendingRequeue: Equatable { let recordId: Int64 let queueNo: String } /// 待确认的完成操作。 struct ScenicQueuePendingFinish: Equatable { let recordId: Int64 let queueNo: String } /// 待确认的叫号操作。 struct ScenicQueuePendingCall: Equatable { let recordId: Int64 let queueNo: String let isRecall: Bool } /// 待确认的用户标记操作。 struct ScenicQueuePendingMark: Equatable { let uid: Int64 let queueNo: String let phoneMasked: String let markAsFreelancePhotog: Int } /// 排队模块数字字段键,用于校验定位。 enum ScenicQueueNumericFieldKey: String { case queueDistanceMeter case queueTakeLimit case missCallRequeueOffset case autoCallAheadCount case shootMinute case shootSecond case firstNoticeAhead case secondNoticeAhead case broadcastIntervalSec case countdownReadableThresholdSec } /// 排队设置页 Tab。 enum ScenicQueueSettingsTab: Int, CaseIterable { case basic case notify case voice var title: String { switch self { case .basic: "基础配置" case .notify: "通知配置" case .voice: "语音播报" } } }