import Foundation /// Issue 事件数据模型 public struct IssueEvent: Codable, Sendable { public let level: String public let platform: String public let fingerprint: String public let appKey: String public let exception: ExceptionInfo? public let breadcrumbs: [Breadcrumb] public let release: String public let environment: String public let userId: String? public let user: UserInfo? public let device: DeviceInfo? public let tags: [String: AnyCodable]? public let sdk: SdkInfo? public init( level: String, platform: String, fingerprint: String, appKey: String, exception: ExceptionInfo? = nil, breadcrumbs: [Breadcrumb] = [], release: String, environment: String, userId: String? = nil, user: UserInfo? = nil, device: DeviceInfo? = nil, tags: [String: Any?]? = nil, sdk: SdkInfo? = nil ) { self.level = level self.platform = platform self.fingerprint = fingerprint self.appKey = appKey self.exception = exception self.breadcrumbs = breadcrumbs self.release = release self.environment = environment self.userId = userId self.user = user self.device = device self.tags = tags?.mapValues { AnyCodable($0) } self.sdk = sdk } public struct ExceptionInfo: Codable, Sendable { public let type: String public let value: String public let stacktrace: String? public init(type: String, value: String, stacktrace: String? = nil) { self.type = type self.value = value self.stacktrace = stacktrace } } public struct UserInfo: Codable, Sendable { public let id: String public init(id: String) { self.id = id } } public struct DeviceInfo: Codable, Sendable { public let model: String public let manufacturer: String public let osName: String public let osVersion: String public let locale: String public let timezone: String public let isEmulator: Bool public let freeMemoryMb: Int public let buildType: String public init( model: String, manufacturer: String, osName: String, osVersion: String, locale: String, timezone: String, isEmulator: Bool, freeMemoryMb: Int, buildType: String ) { self.model = model self.manufacturer = manufacturer self.osName = osName self.osVersion = osVersion self.locale = locale self.timezone = timezone self.isEmulator = isEmulator self.freeMemoryMb = freeMemoryMb self.buildType = buildType } } public struct SdkInfo: Codable, Sendable { public let name: String public let version: String public init(name: String, version: String) { self.name = name self.version = version } } } /// 面包屑 public struct Breadcrumb: Codable, Sendable { public let timestamp: Int public let category: String public let message: String public let level: String public let data: [String: AnyCodable]? public init( timestamp: Int, category: String, message: String, level: String, data: [String: Any?]? = nil ) { self.timestamp = timestamp self.category = category self.message = message self.level = level self.data = data?.mapValues { AnyCodable($0) } } } /// 日志事件 public struct LogEvent: Codable, Sendable { public let name: String public let properties: [String: AnyCodable]? public let appKey: String public let userId: String public let platform: String public let release: String public let environment: String public let sdk: IssueEvent.SdkInfo public init( name: String, properties: [String: Any?]? = nil, appKey: String, userId: String, platform: String, release: String, environment: String, sdk: IssueEvent.SdkInfo ) { self.name = name self.properties = properties?.mapValues { AnyCodable($0) } self.appKey = appKey self.userId = userId self.platform = platform self.release = release self.environment = environment self.sdk = sdk } } /// 日志级别 public enum LogLevel: Int, Sendable { case debug = 0 case info = 1 case warn = 2 case error = 3 case none = 4 } /// AnyCodable 包装器 public struct AnyCodable: Codable, Sendable { public let value: Any public init(_ value: Any?) { self.value = value ?? () } public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if container.decodeNil() { self.value = () } else if let bool = try? container.decode(Bool.self) { self.value = bool } else if let int = try? container.decode(Int.self) { self.value = int } else if let double = try? container.decode(Double.self) { self.value = double } else if let string = try? container.decode(String.self) { self.value = string } else if let array = try? container.decode([AnyCodable].self) { self.value = array.map { $0.value } } else if let dict = try? container.decode([String: AnyCodable].self) { self.value = dict.mapValues { $0.value } } else { self.value = () } } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch value { case is Void: try container.encodeNil() case let bool as Bool: try container.encode(bool) case let int as Int: try container.encode(int) case let double as Double: try container.encode(double) case let string as String: try container.encode(string) case let array as [Any]: try container.encode(array.map { AnyCodable($0) }) case let dict as [String: Any]: try container.encode(dict.mapValues { AnyCodable($0) }) default: try container.encodeNil() } } }