import Foundation /// 推送点击后可到达的业务页面。 enum PushDestination: Sendable, Equatable { case paymentRecord case paymentDetails case messageCenter } /// 极光/APNs payload 解析结果,仅提取业务消息类型用于点击路由。 struct PushPayload: Sendable, Equatable { private let type: String /// 从系统通知 userInfo 提取顶层或推送包装层中的业务消息类型。 nonisolated init(userInfo: [AnyHashable: Any]) { type = Self.extractType(from: userInfo) } /// 从可发送的字符串字典创建 payload,仅保留业务消息类型。 nonisolated init(values: [String: String]) { type = Self.normalizedType(values["type"]) } /// 可发送的规范化字段快照。 nonisolated var normalizedValues: [String: String] { type.isEmpty ? [:] : ["type": type] } /// 仅根据后端业务消息类型解析目标页面。 nonisolated var destination: PushDestination { switch type { case "1": return .paymentRecord case "6": return .paymentDetails default: return .messageCenter } } private nonisolated static func extractType( from dictionary: [AnyHashable: Any], depth: Int = 0 ) -> String { guard depth <= 4 else { return "" } let directType = normalizedType(dictionary["type"]) guard directType.isEmpty else { return directType } for key in nestedTypeContainerKeys { guard let value = dictionary[key] else { continue } if let nested = value as? [AnyHashable: Any] { let nestedType = extractType(from: nested, depth: depth + 1) if !nestedType.isEmpty { return nestedType } continue } guard let text = value as? String, let data = text.data(using: .utf8), let object = try? JSONSerialization.jsonObject(with: data), let nested = object as? [String: Any] else { continue } let bridged = Dictionary(uniqueKeysWithValues: nested.map { (AnyHashable($0.key), $0.value) }) let nestedType = extractType(from: bridged, depth: depth + 1) if !nestedType.isEmpty { return nestedType } } return "" } private nonisolated static func normalizedType(_ value: Any?) -> String { switch value { case let string as String: return string.trimmingCharacters(in: .whitespacesAndNewlines) case let number as NSNumber: return number.stringValue default: return "" } } private nonisolated static let nestedTypeContainerKeys = [ "extras", "extra", "JMessageExtra", "n_extras", ] }