import Foundation /// 推送点击后可到达的业务页面。 enum PushDestination: Sendable, Equatable { case payment case orders case verificationOrders case task case queue case messageCenter /// 目标页面是否依赖当前景区上下文。 var requiresScenicContext: Bool { switch self { case .payment, .task, .queue: true case .orders, .verificationOrders, .messageCenter: false } } } /// 极光/APNs payload 解析结果,兼容顶层字段及常见 JSON 嵌套格式。 struct PushPayload: Sendable, Equatable { private let values: [String: String] /// 从系统通知 userInfo 解析路由字段。 nonisolated init(userInfo: [AnyHashable: Any]) { var flattened: [String: String] = [:] Self.mergeDictionary(userInfo, into: &flattened) values = flattened } /// 从可发送的字符串字典创建 payload,供跨 Actor 回调及测试使用。 nonisolated init(values: [String: String]) { self.values = values } /// 可发送的规范化字段快照。 nonisolated var normalizedValues: [String: String] { values } /// 根据后端约定和历史兼容字段解析目标页面。 nonisolated var destination: PushDestination { let type = value(for: "type") let route = value(for: "route") let uri = value(for: "uri") let action = value(for: "action") let merged = [route, uri, action].joined(separator: " ").lowercased() if type == "1" || type == "6" || merged.contains("payment") || merged.contains("payment_qr") || merged.contains("收款") { return .payment } if merged.contains("queue") || merged.contains("scenic-queue") || merged.contains("排队") || merged.contains("叫号") { return .queue } if merged.contains("verification") || merged.contains("writeoff") || merged.contains("write_off") || merged.contains("核销") { return .verificationOrders } if merged.contains("order") || merged.contains("订单") { return .orders } if merged.contains("task") || merged.contains("任务") { return .task } return .messageCenter } private nonisolated func value(for key: String) -> String { values[key]?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" } private nonisolated static func mergeDictionary( _ dictionary: [AnyHashable: Any], into result: inout [String: String] ) { for (rawKey, value) in dictionary { guard let key = rawKey as? String else { continue } merge(value, key: key, into: &result) } } private nonisolated static func merge( _ value: Any, key: String, into result: inout [String: String] ) { if let dictionary = value as? [String: Any] { result[key] = stringValue(value) mergeDictionary(dictionary, into: &result) return } if let dictionary = value as? [AnyHashable: Any] { result[key] = stringValue(value) mergeDictionary(dictionary, into: &result) return } let text = stringValue(value) result[key] = text guard Self.nestedJSONKeys.contains(key), let data = text.data(using: .utf8), let object = try? JSONSerialization.jsonObject(with: data) else { return } if let dictionary = object as? [String: Any] { mergeDictionary(dictionary, into: &result) } } private nonisolated static func stringValue(_ value: Any) -> String { switch value { case let string as String: string case let number as NSNumber: number.stringValue default: String(describing: value) } } private nonisolated static let nestedJSONKeys: Set = [ "extras", "extra", "data", "JMessageExtra", "n_extras", ] }