import Foundation import XuqmCoreSDK /// 日志上传器 final class LogUploader { private let appKey: String private let encoder: JSONEncoder = { let e = JSONEncoder() e.dateEncodingStrategy = .iso8601 return e }() init(appKey: String) { self.appKey = appKey } /// 上传 Issue 事件 func uploadIssues(_ issues: [IssueEvent], completion: ((Bool) -> Void)? = nil) { let baseURL = SDKEndpoints.bugCollectApiURL ?? SDKEndpoints.apiBaseURL guard let url = URL(string: "\(baseURL)/bugcollect/v1/issues/batch") else { completion?(false) return } let body: [String: Any] = [ "sentAt": ISO8601DateFormatter().string(from: Date()), "sdk": ["name": "bugcollect.ios", "version": "1.0.0"], "events": issues.map { issue in var event: [String: Any] = [ "type": "issue", "level": issue.level, "platform": issue.platform, "fingerprint": issue.fingerprint, "appKey": issue.appKey, "release": issue.release, "environment": issue.environment ] if let exception = issue.exception { event["exception"] = [ "type": exception.type, "value": exception.value, "stacktrace": exception.stacktrace ?? "" ] } if let userId = issue.userId { event["userId"] = userId } if let user = issue.user { event["user"] = ["id": user.id] } if let device = issue.device { event["device"] = [ "model": device.model, "manufacturer": device.manufacturer, "osName": device.osName, "osVersion": device.osVersion, "locale": device.locale, "timezone": device.timezone, "isEmulator": device.isEmulator, "freeMemoryMb": device.freeMemoryMb, "buildType": device.buildType ] } return event } ] upload(url: url, body: body, completion: completion) } /// 上传日志事件 func uploadEvents(_ events: [LogEvent], completion: ((Bool) -> Void)? = nil) { let baseURL = SDKEndpoints.bugCollectApiURL ?? SDKEndpoints.apiBaseURL guard let url = URL(string: "\(baseURL)/bugcollect/v1/events/batch") else { completion?(false) return } let body: [String: Any] = [ "sentAt": ISO8601DateFormatter().string(from: Date()), "sdk": ["name": "bugcollect.ios", "version": "1.0.0"], "events": events.map { event in var dict: [String: Any] = [ "type": "event", "name": event.name, "appKey": event.appKey, "userId": event.userId, "platform": event.platform, "release": event.release, "environment": event.environment ] if let props = event.properties { dict["properties"] = props.mapValues { $0.value } } return dict } ] upload(url: url, body: body, completion: completion) } private func upload(url: URL, body: [String: Any], completion: ((Bool) -> Void)?) { var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") // 添加认证 token if let token = XuqmSDK.shared.token { request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") } do { request.httpBody = try JSONSerialization.data(withJSONObject: body) } catch { completion?(false) return } URLSession.shared.dataTask(with: request) { _, response, error in let success = error == nil && (response as? HTTPURLResponse)?.statusCode ?? 0 < 400 completion?(success) }.resume() } }