// // LNHttpManager.swift // Lanu // // Created by OneeChan on 2025/11/6. // import Foundation /// HTTP请求方法枚举 enum HTTPMethod: String { case get = "GET" case post = "POST" case put = "PUT" case delete = "DELETE" } /// HTTP请求错误枚举 enum LNHttpError: Error, LocalizedError { case invalidURL case invalidResponse case networkError(Error) case parsingError(Error) case statusCode(Int) case serverError(Int, String) var errorDesc: String { switch self { case .invalidURL: "无效的URL" case .invalidResponse: "无效的响应" case .networkError(let error): error.localizedDescription case .parsingError(let error): error.localizedDescription case .statusCode(let code): .init(key: "B00125", code) case .serverError(_, let error): error } } } enum LNHttpCommonErrorCode: Int { case beKicked = 100004 } /// HTTP请求管理器 class LNHttpManager { static let shared = LNHttpManager() private let session: URLSession private init() { let configuration = URLSessionConfiguration.default configuration.timeoutIntervalForRequest = 30 configuration.timeoutIntervalForResource = 60 session = URLSession(configuration: configuration) } /// 通用HTTP请求方法 /// - Parameters: /// - urlString: 请求URL字符串 /// - method: HTTP方法 /// - parameters: 请求参数 /// - headers: 请求头 /// - completion: 完成回调 func request( path: String, method: HTTPMethod = .get, parameters: [String: Any]? = nil, headers: [String: String]? = nil, completion: @escaping (T?, LNHttpError?) -> Void ) { var sign = "" var commonHeader: [String: String] = [:] let id = "\(Int(curTimeInNano))" sign += id commonHeader["id"] = id let udid = curDeviceId sign += udid commonHeader["udid"] = udid let app = curAppBundleIdentifier sign += app commonHeader["app"] = app let device = curDeviceModelName sign += device commonHeader["device"] = device let platform = "2" sign += platform commonHeader["platform"] = platform let channel = "appStore" sign += channel commonHeader["channel"] = channel let api = "1" sign += api commonHeader["api"] = api let version = curBuildVersion sign += version commonHeader["version"] = version let network = LNNetworkMonitor.curNetworkType.desc sign += network commonHeader["network"] = network let time = "\(Int(curTimeInMicro))" sign += time commonHeader["time"] = time let token = LNAccountManager.shared.token if !token.isEmpty { sign += token commonHeader["token"] = token } let secret = "abc|abc|edg|9527|1234" sign += secret let mergedHeader = commonHeader.merging(headers ?? [:]) { $1 } // 检查URL是否有效 guard let url = buildURL(from: path, method: method, parameters: parameters) else { completion(nil, .invalidURL) return } // 创建请求 var request = URLRequest(url: url) request.httpMethod = method.rawValue // 设置请求头 mergedHeader.forEach { key, value in request.addValue(value, forHTTPHeaderField: key) } request.addValue(LNAppConfig.shared.curLang.bundleName, forHTTPHeaderField: "Accept-Language") // 设置请求体(POST, PUT等方法) if method != .get, let parameters = parameters { let body = try? JSONSerialization .data(withJSONObject: parameters) request.httpBody = body if let body, let bodyStr = String(data: body, encoding: .utf8) { sign += bodyStr } } request.addValue(sign.md5, forHTTPHeaderField: "sign") request.addValue("application/json", forHTTPHeaderField: "Content-Type") // 执行请求 let task = session.dataTask(with: request) { [weak self] data, response, error in guard let self else { return } // 处理网络错误 if let error = error { Log.d("receive \(request.url?.absoluteString ?? "") error: \(error.localizedDescription)") completion(nil, .networkError(error)) return } // 检查响应是否有效 guard let httpResponse = response as? HTTPURLResponse else { Log.d("receive \(request.url?.absoluteString ?? "") response error") completion(nil, .invalidResponse) return } if let data { Log.d("receive \(request.url?.absoluteString ?? "") code:\(httpResponse.statusCode) data \(String(data: data, encoding: .utf8) ?? "")") } else { Log.d("receive \(request.url?.absoluteString ?? "") code:\(httpResponse.statusCode)") } // 检查状态码 guard 200...299 ~= httpResponse.statusCode else { completion(nil, .statusCode(httpResponse.statusCode)) return } // 处理响应数据 guard let data = data else { completion(nil, .invalidResponse) return } // 解析JSON数据 self.parseJSON(data: data, completion: completion) } if let body = request.httpBody { Log.d("send \(request.httpMethod ?? "") - \(request.url?.absoluteString ?? "") \(String(data: body, encoding: .utf8) ?? "")") } else { Log.d("send \(request.httpMethod ?? "") - \(request.url?.absoluteString ?? "")") } task.resume() } /// 构建请求URL(处理GET参数) private func buildURL(from path: String, method: HTTPMethod, parameters: [String: Any]?) -> URL? { guard var urlComponents = URLComponents(string: LNNetworkConfig.host + (path.starts(with: "/") ? path : "/\(path)")) else { return nil } // GET方法的参数拼接到URL上 if method == .get, let parameters = parameters { urlComponents.queryItems = parameters.map { key, value in URLQueryItem(name: key, value: "\(value)") } } return urlComponents.url } /// 解析JSON数据 private func parseJSON( data: Data, completion: @escaping (T?, LNHttpError?) -> Void ) { do { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .useDefaultKeys // 处理蛇形命名 let result = try decoder.decode(LNHttpResponse.self, from: data) if result.code != 0 { if result.code == LNHttpCommonErrorCode.beKicked.rawValue { LNAccountManager.shared.clean() } completion(nil, .serverError(result.code, result.msg)) } else { completion(result.data, nil) } } catch { completion(nil, .parsingError(error)) } } } // MARK: - 便捷请求方法扩展 extension LNHttpManager { /// 发送GET请求 func get( path: String, params: [String: Any]? = nil, completion: @escaping (T?, LNHttpError?) -> Void ) { request( path: path, method: .get, parameters: params, completion: completion ) } /// 发送POST请求 func post( path: String, params: [String: Any]? = nil, completion: @escaping (T?, LNHttpError?) -> Void ) { request( path: path, method: .post, parameters: params, completion: completion ) } /// 发送PUT请求 func put( path: String, params: [String: Any]? = nil, completion: @escaping (T?, LNHttpError?) -> Void ) { request( path: path, method: .put, parameters: params, completion: completion ) } /// 发送DELETE请求 func delete( path: String, params: [String: Any]? = nil, completion: @escaping (T?, LNHttpError?) -> Void ) { request( path: path, method: .delete, parameters: params, completion: completion ) } } extension LNHttpManager { func request( path: String, method: HTTPMethod = .get, parameters: [String: Any]? = nil, headers: [String: String]? = nil, completion: @escaping (LNHttpError?) -> Void ) { let handler: ( LNHttpEmptyResponse?, LNHttpError? ) -> Void = { _, err in completion(err) } request( path: path, method: method, parameters: parameters, headers: headers, completion: handler ) } /// 发送GET请求 func get( path: String, params: [String: Any]? = nil, completion: @escaping (LNHttpError?) -> Void ) { request( path: path, method: .get, parameters: params, completion: completion ) } /// 发送POST请求 func post( path: String, params: [String: Any]? = nil, completion: @escaping (LNHttpError?) -> Void ) { request( path: path, method: .post, parameters: params, completion: completion ) } /// 发送PUT请求 func put( path: String, params: [String: Any]? = nil, completion: @escaping (LNHttpError?) -> Void ) { request( path: path, method: .put, parameters: params, completion: completion ) } /// 发送DELETE请求 func delete( path: String, params: [String: Any]? = nil, completion: @escaping (LNHttpError?) -> Void ) { request( path: path, method: .delete, parameters: params, completion: completion ) } }