Files
e-party-iOS/yana/APIs/APIModels.swift
2025-07-07 14:19:07 +08:00

304 lines
9.4 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Foundation
import ComposableArchitecture
// MARK: - HTTP Method
/// HTTP
///
/// API HTTP
/// URLRequest
enum HTTPMethod: String, CaseIterable {
case GET = "GET"
case POST = "POST"
case PUT = "PUT"
case DELETE = "DELETE"
case PATCH = "PATCH"
}
// MARK: - API Error Types
/// API
///
/// API
/// 便
///
///
/// -
/// -
/// - HTTP
/// -
enum APIError: Error, Equatable {
case invalidURL
case noData
case decodingError(String)
case networkError(String)
case httpError(statusCode: Int, message: String?)
case timeout
case resourceTooLarge
case unknown(String)
var localizedDescription: String {
switch self {
case .invalidURL:
return "无效的 URL"
case .noData:
return "没有收到数据"
case .decodingError(let message):
return "数据解析失败: \(message)"
case .networkError(let message):
return "网络错误: \(message)"
case .httpError(let statusCode, let message):
return "HTTP 错误 \(statusCode): \(message ?? "未知错误")"
case .timeout:
return "请求超时"
case .resourceTooLarge:
return "响应数据过大"
case .unknown(let message):
return "未知错误: \(message)"
}
}
}
// MARK: - Base Request Parameters
///
///
/// API
/// API
///
///
/// -
/// -
/// -
///
/// 使
/// ```swift
/// var baseRequest = BaseRequest()
/// baseRequest.generateSignature(with: ["key": "value"])
/// ```
struct BaseRequest: Codable {
let acceptLanguage: String
let os: String = "iOS"
let osVersion: String
let netType: Int
let ispType: String
let channel: String
let model: String
let deviceId: String
let appVersion: String
let app: String
let lang: String
let mcc: String?
let spType: String?
var pubSign: String
enum CodingKeys: String, CodingKey {
case acceptLanguage = "Accept-Language"
case os, osVersion, netType, ispType, channel, model, deviceId
case appVersion, app, lang, mcc, spType
case pubSign = "pub_sign"
}
init() {
//
let preferredLanguage = Locale.current.languageCode ?? "en"
self.acceptLanguage = preferredLanguage
self.lang = preferredLanguage
//
self.osVersion = UIDevice.current.systemVersion
//
self.model = UIDevice.current.model
// ID
self.deviceId = UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString
//
self.appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.0"
//
self.app = Bundle.main.infoDictionary?["CFBundleName"] as? String ?? "yana"
// WiFi=2, =1
self.netType = NetworkTypeDetector.getCurrentNetworkType()
//
let carrierInfo = CarrierInfoManager.getCarrierInfo()
self.ispType = carrierInfo.ispType
self.mcc = carrierInfo.mcc == "65535" ? nil : carrierInfo.mcc
self.spType = self.mcc
//
#if DEBUG
self.channel = "TestFlight"
#else
self.channel = "appstore"
#endif
//
self.pubSign = "" //
}
/// API
///
///
/// 1.
/// 2.
/// 3. key
/// 4.
/// 5. MD5
///
/// - Parameter requestParams:
mutating func generateSignature(with requestParams: [String: Any] = [:]) {
// 1.
var allParams = requestParams
//
allParams["Accept-Language"] = self.acceptLanguage
allParams["os"] = self.os
allParams["osVersion"] = self.osVersion
allParams["netType"] = self.netType
allParams["ispType"] = self.ispType
allParams["channel"] = self.channel
allParams["model"] = self.model
allParams["deviceId"] = self.deviceId
allParams["appVersion"] = self.appVersion
allParams["app"] = self.app
allParams["lang"] = self.lang
if let mcc = self.mcc {
allParams["mcc"] = mcc
}
if let spType = self.spType {
allParams["spType"] = spType
}
// 2. API rule
let systemParams = [
"Accept-Language", "pub_uid", "appVersion", "appVersionCode",
"channel", "deviceId", "ispType", "netType", "os",
"osVersion", "app", "ticket", "client", "lang", "mcc"
]
var filteredParams = allParams
for param in systemParams {
filteredParams.removeValue(forKey: param)
}
// 3. key
let sortedKeys = filteredParams.keys.sorted()
let paramString = sortedKeys.map { key in
"\(key)=\(filteredParams[key] ?? "")"
}.joined(separator: "&")
// 4.
let keyString = "key=rpbs6us1m8r2j9g6u06ff2bo18orwaya"
let finalString = paramString.isEmpty ? keyString : "\(paramString)&\(keyString)"
// 5. MD5
self.pubSign = finalString.md5().uppercased()
}
}
// MARK: - Network Type Detector
struct NetworkTypeDetector {
static func getCurrentNetworkType() -> Int {
// WiFi = 2, = 1
//
return 1 //
}
}
// MARK: - Carrier Info Manager
struct CarrierInfoManager {
struct CarrierInfo {
let ispType: String
let mcc: String?
}
static func getCarrierInfo() -> CarrierInfo {
//
return CarrierInfo(ispType: "65535", mcc: nil)
}
}
// MARK: - User Info Manager (for Headers)
struct UserInfoManager {
static func getCurrentUserId() -> String? {
// ID
// AccountInfoStorage
return nil
}
static func getCurrentUserTicket() -> String? {
//
// AccountInfoStorage
return nil
}
}
// MARK: - API Request Protocol
/// API
///
/// API
/// API
///
///
/// - Response:
/// - endpoint: API
/// - method: HTTP
/// -
///
/// 使
/// ```swift
/// struct LoginRequest: APIRequestProtocol {
/// typealias Response = LoginResponse
/// let endpoint = "/auth/login"
/// let method: HTTPMethod = .POST
/// // ...
/// }
/// ```
protocol APIRequestProtocol {
associatedtype Response: Codable
var endpoint: String { get }
var method: HTTPMethod { get }
var queryParameters: [String: String]? { get }
var bodyParameters: [String: Any]? { get }
var headers: [String: String]? { get }
var timeout: TimeInterval { get }
var includeBaseParameters: Bool { get }
}
extension APIRequestProtocol {
var timeout: TimeInterval { 30.0 }
var includeBaseParameters: Bool { true }
var headers: [String: String]? { nil }
}
// MARK: - Generic API Response
struct APIResponse<T: Codable>: Codable {
let data: T?
let status: String?
let message: String?
let code: Int?
}
// MARK: - String MD5 Extension
extension String {
func md5() -> String {
let data = Data(self.utf8)
let hash = data.withUnsafeBytes { bytes -> [UInt8] in
var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
CC_MD5(bytes.baseAddress, CC_LONG(data.count), &hash)
return hash
}
return hash.map { String(format: "%02x", $0) }.joined()
}
}
// CommonCrypto
import CommonCrypto