Visual_Novel_iOS/crush/Crush/Src/API/Network/APIProvider.swift

283 lines
10 KiB
Swift
Raw Normal View History

2025-10-09 10:29:35 +00:00
//
// 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<T: Codable>: Codable {
/// OK or ERROR
let status: String?
let errorCode: ServiceErrorEnum?
let errorMsg: String?
let content: T?
let traceId : String?
}
class ResponseContentPageData<T: Codable>: 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<T: Codable>(_ target: Target,
progress _: ProgressBlock? = .none,
modelType TTType: T.Type,
autoShowErrMsg: Bool = true,
completion: @escaping (Result<T?, ResponseError>) -> 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<T>.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 {
// ToastMsg
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))
}
})
}
}