// // APIProvider.swift // WoolniOriginalSwift // // Created by lyu dong on 2025/7/2. // Copyright © 2025 lydsnm. All rights reserved. // import Foundation import Moya import UIKit let myEndpointClosure = { (target: TargetType) -> Endpoint in let url = target.baseURL.absoluteString + target.path var task = target.task var endpoint = Endpoint( url: url, sampleResponseClosure: { .networkResponse(200, target.sampleData) }, method: target.method, task: task, httpHeaderFields: target.headers ) return endpoint } let myRequestClosure = { (endpoint: Endpoint, done: MoyaProvider.RequestResultClosure) in do { var request = try endpoint.urlRequest() request.timeoutInterval = 30 let token = UserCore.shared.token if !token.isEmpty { let body = request.httpBody ?? Data() let str = String(data:body, encoding: .utf8) if APIConfig.apiLogEnable { // dlog("⚠️ request加密前参数:\(str ?? "")") } /* // 加密结果 let key = (token + "AHkt5aUUtO6HZPid").md5().uppercased() let aes = try AES(key: key, iv: "HBB4UO5kEmM4169Z") let encrypted = try aes.encrypt(str!.bytes) let result = encrypted.toBase64() if APIConfig.apiLogEnable { // dlog("⚠️ request加密结果:\(result)") } let dic = ["key": result] let httpBody = try JSONSerialization.data(withJSONObject: dic, options: .prettyPrinted) request.httpBody = httpBody */ } done(.success(request)) } catch { done(.failure(MoyaError.underlying(error, nil))) } } let myNetworkPlugin = NetworkActivityPlugin.init { changeType, _ in switch changeType { case .began: dlog("开始请求网络") case .ended: dlog("结束请求网络") } } public enum ServiceErrorEnum: String, Codable { case unknow = "-1" case unknowEmpty = "" case internalError = "00000000" case common = "0000" case sign_usernotexist = "10050002" case sign_userLoginclientNotExist = "10050003" case sign_userNotAuthorizedClient = "10050004" case tokenExpired = "10050005" case tokenIllegal = "10050006" case accountIsFrozen = "10010002" /// 用户未登陆 case sign_usernotLoggedIn = "10050001" /// 用户账号已被冻结 case otherAccountFrozen = "10010005" /// 账号不存在 case newAccount = "10010001" /// 验证码不正确 case smscodeIncorrect = "10031001" /// 验证码过期 case smscodeExpired = "10031002" /// 用户不存在 case userExist = "10010004" case mustNotBeBlank = "00000001" case imageCheckFailed = "10019999" /// 生成图片超时 case imageGenTimeOut = "8006" /// 查看未解锁的AI照片 case imageBrowseButUnlock = "10010011" /// AI用户不存在 case aiRoleNotExist = "10010012" /// Coin 余额不足 case insufficentCoin = "INSUFFICIENT_BALANCE" public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let rawValue = try container.decode(String.self) self = ServiceErrorEnum(rawValue: rawValue) ?? .unknow } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .unknow: // 编码成一个默认值,例如 "-999",或直接跳过 try container.encode("unknown") default: try container.encode(self.rawValue) } } } // 假设 ResponseData 是一个泛型结构体,用于包装 API 响应 struct ResponseData: Codable { /// OK or ERROR let status: String? let errorCode: ServiceErrorEnum? let errorMsg: String? let content: T? let traceId : String? } class ResponseContentPageData: Codable{ var pn: Int? var ps: Int? var datas: [T]? var tc: Int? var sortType:String? var sortField: String? } class RequestPageData:Codable { var pn : Int = 1 var ps : Int = 20 } public enum ResponseError: Error { case serviceError(code: ServiceErrorEnum?, msg: String?) case deserializeError case networkError } //extension ResponseError { // enum Code: String, Codable { // case tokenExpired // case tokenIllegal // case accountIsFrozen // case sign_usernotexist // case sign_usernotLoggedIn // case sign_userLoginclientNotExist // case sign_userNotAuthorizedClient // case newAccount // case hcaptchaNeed // case common // } //} extension MoyaProvider { @discardableResult public func request(_ target: Target, progress _: ProgressBlock? = .none, modelType TTType: T.Type, autoShowErrMsg: Bool = true, completion: @escaping (Result) -> Void) -> Cancellable { if APIConfig.apiLogEnable { // 🗣\(target.method)\n var paramsLog = "" if case let .requestParameters(parameters, _) = target.task { if let jsonData = try? JSONSerialization.data(withJSONObject: parameters, options: [.prettyPrinted]), let jsonString = String(data: jsonData, encoding: .utf8) { paramsLog = "\n\(jsonString)" } else { paramsLog = "\(parameters)" } } else { paramsLog = "\(target.task)" } dlog("headers: \(target.headers ?? ["": ""])\n「🗣️Request」path: ⭐️\(target.path)⭐️\n🗣️⭐️params:\(paramsLog)") // } return request(target, completion: { result in switch result { case let .success(response): do { // 使用 JSONDecoder 解析 response.data let decoder = JSONDecoder() let data = try decoder.decode(ResponseData.self, from: response.data) // 调试日志:将 JSON 转换为字符串用于日志输出 if APIConfig.apiLogEnable { let jsonObject = try JSONSerialization.jsonObject(with: response.data) let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted) let jsonString = String(data: jsonData, encoding: .utf8) ?? String(data: response.data, encoding: .utf8) ?? "" dlog("👉⭐️\(target.path)⭐️ response:\n\(jsonString)") } let status = data.status let code = data.errorCode let msg = data.errorMsg if (status ?? "") == "OK" { let model = data.content completion(.success(model)) } else { var toastMsg = autoShowErrMsg if code == .tokenExpired || code == .tokenIllegal || code == .accountIsFrozen || code == .sign_usernotexist || code == .sign_usernotLoggedIn || code == .sign_userLoginclientNotExist || code == .sign_userNotAuthorizedClient { // ⚠️ 弹出登陆 UserCore.shared.logout() if let Vc = UIWindow.getTopViewController(), (Vc is CLLoginMainController || Vc is PersonalInformationFillController) { // do nothing. } else { // AppRouter.goBackRootController(jumpIndex: .home) { // NotificationCenter.post(name: .presentSignInVc, object: nil, userInfo: nil) // } NotificationCenter.post(name: .presentSignInVc, object: nil, userInfo: nil) } } else if code == .newAccount { // ⚠️ 不进行Toast的Msg类型 toastMsg = false } else if code == .insufficentCoin { // ⚠️钱包余额不足 // refresh wallet toastMsg = false var handled = false if let cow = target as? AICowAPI{ switch cow{ case .voiceCallOperate, .voiceAsr2, .voiceTts: IMAIViewModel.shared.showChatModelInsufficentCoinSheet() handled = true default: break } } if handled == false{ // 弹出充值Sheet CLPurchase.shared.showIAPBuyCoinSheet() } } if toastMsg, let msgUnpack = msg, msgUnpack.count > 0 { UIWindow.getTopDisplayWindow()?.makeToast(msgUnpack) } dlog("⛔️error: code = \(code ?? .common), msg = \(data.errorMsg ?? "业务状态失败")") completion(.failure(.serviceError(code: code, msg: msg))) } } catch { dlog("⛔️请求成功,但解析失败: \(error), Response:⛔️\(CodableHelper.jsonString(from: response.data) ?? "x")⛔️") completion(.failure(.deserializeError)) } case let .failure(error): dlog("⛔️ \(target.path) 网络连接失败\(error)") UIWindow.getTopDisplayWindow()?.makeToast("Internet connection failed") completion(.failure(.networkError)) } }) } }