import Foundation /// RPC网络层 — 对应Bricks的bricks.tget()和fetch /// 处理API调用、数据获取、文件上传 @MainActor public final class BricksRPC: ObservableObject { public struct Response: Sendable { public let data: Data public let statusCode: Int public let headers: [String: String] public var json: Any? { try? JSONSerialization.jsonObject(with: data) } public var dictionary: [String: Any]? { json as? [String: Any] } public var array: [[String: Any]]? { if let dict = dictionary, let rows = dict["rows"] as? [[String: Any]] { return rows } return json as? [[String: Any]] } public var text: String { String(data: data, encoding: .utf8) ?? "" } public var isSuccess: Bool { (200...299).contains(statusCode) } } /// 基础URL(可选,用于相对路径解析) public var baseURL: String = "" /// 默认请求头 public var defaultHeaders: [String: String] = [ "Content-Type": "application/json" ] /// 认证token public var authToken: String? /// 请求拦截器 public var requestInterceptor: ((URLRequest) -> URLRequest)? /// 活跃请求数 @Published public var activeRequests: Int = 0 private let session: URLSession public init(session: URLSession = .shared) { self.session = session } // MARK: - GET public func get(_ url: String, params: [String: String]? = nil) async throws -> Response { var urlString = resolveURL(url) if let params, !params.isEmpty { let query = params.map { "\($0.key)=\($0.value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? $0.value)" } .joined(separator: "&") urlString += (urlString.contains("?") ? "&" : "?") + query } return try await request(urlString, method: "GET") } // MARK: - POST public func post(_ url: String, body: [String: Any]? = nil) async throws -> Response { var data: Data? if let body { data = try JSONSerialization.data(withJSONObject: body) } return try await request(resolveURL(url), method: "POST", body: data) } public func postForm(_ url: String, fields: [String: String]) async throws -> Response { let formString = fields.map { "\($0.key)=\($0.value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? $0.value)" } .joined(separator: "&") let data = formString.data(using: .utf8) return try await request(resolveURL(url), method: "POST", body: data, headers: ["Content-Type": "application/x-www-form-urlencoded"]) } // MARK: - 通用请求 public func request(_ url: String, method: String = "GET", body: Data? = nil, headers: [String: String]? = nil) async throws -> Response { activeRequests += 1 defer { activeRequests -= 1 } guard let urlObj = URL(string: url) else { throw BricksRPCError.invalidURL(url) } var request = URLRequest(url: urlObj) request.httpMethod = method request.httpBody = body request.timeoutInterval = 30 // 设置默认头 for (key, value) in defaultHeaders { request.setValue(value, forHTTPHeaderField: key) } // 自定义头 if let headers { for (key, value) in headers { request.setValue(value, forHTTPHeaderField: key) } } // 认证 if let token = authToken { request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") } // 拦截器 if let interceptor = requestInterceptor { request = interceptor(request) } do { let (data, response) = try await session.data(for: request) guard let httpResponse = response as? HTTPURLResponse else { throw BricksRPCError.invalidResponse } let responseHeaders = Dictionary( uniqueKeysWithValues: httpResponse.allHeaderFields.compactMap { key, value in guard let keyStr = key as? String, let valStr = value as? String else { return nil } return (keyStr, valStr) } ) return Response( data: data, statusCode: httpResponse.statusCode, headers: responseHeaders ) } catch { throw BricksRPCError.networkError(error) } } // MARK: - URL解析 private func resolveURL(_ url: String) -> String { if url.hasPrefix("http://") || url.hasPrefix("https://") { return url } if baseURL.isEmpty { return url } let base = baseURL.hasSuffix("/") ? String(baseURL.dropLast()) : baseURL let path = url.hasPrefix("/") ? url : "/\(url)" return base + path } } // MARK: - 错误类型 public enum BricksRPCError: Error, LocalizedError { case invalidURL(String) case invalidResponse case networkError(Error) case httpError(Int, String) public var errorDescription: String? { switch self { case .invalidURL(let url): return "无效URL: \(url)" case .invalidResponse: return "无效响应" case .networkError(let error): return "网络错误: \(error.localizedDescription)" case .httpError(let code, let msg): return "HTTP \(code): \(msg)" } } }