| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- //
- // 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<T: Decodable>(
- 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<T: Decodable>(
- data: Data,
- completion: @escaping (T?, LNHttpError?) -> Void
- ) {
- do {
- let decoder = JSONDecoder()
- decoder.keyDecodingStrategy = .useDefaultKeys // 处理蛇形命名
- let result = try decoder.decode(LNHttpResponse<T>.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<T: Decodable>(
- path: String,
- params: [String: Any]? = nil,
- completion: @escaping (T?, LNHttpError?) -> Void
- ) {
- request(
- path: path,
- method: .get,
- parameters: params,
- completion: completion
- )
- }
-
- /// 发送POST请求
- func post<T: Decodable>(
- path: String,
- params: [String: Any]? = nil,
- completion: @escaping (T?, LNHttpError?) -> Void
- ) {
- request(
- path: path,
- method: .post,
- parameters: params,
- completion: completion
- )
- }
-
- /// 发送PUT请求
- func put<T: Decodable>(
- path: String,
- params: [String: Any]? = nil,
- completion: @escaping (T?, LNHttpError?) -> Void
- ) {
- request(
- path: path,
- method: .put,
- parameters: params,
- completion: completion
- )
- }
-
- /// 发送DELETE请求
- func delete<T: Decodable>(
- 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
- )
- }
- }
|