first commit for e-party
This commit is contained in:
@@ -2,6 +2,11 @@ import Foundation
|
||||
import ComposableArchitecture
|
||||
|
||||
// MARK: - HTTP Method
|
||||
|
||||
/// HTTP 请求方法枚举
|
||||
///
|
||||
/// 定义了 API 请求支持的 HTTP 方法类型
|
||||
/// 每个方法都有对应的字符串值,用于构建 URLRequest
|
||||
enum HTTPMethod: String, CaseIterable {
|
||||
case GET = "GET"
|
||||
case POST = "POST"
|
||||
@@ -11,6 +16,17 @@ enum HTTPMethod: String, CaseIterable {
|
||||
}
|
||||
|
||||
// MARK: - API Error Types
|
||||
|
||||
/// API 错误类型枚举
|
||||
///
|
||||
/// 定义了 API 请求过程中可能出现的各种错误类型,
|
||||
/// 每种错误都包含详细的描述信息,便于调试和用户提示
|
||||
///
|
||||
/// 错误类型包括:
|
||||
/// - 网络相关错误(超时、连接失败等)
|
||||
/// - 数据相关错误(解析失败、数据过大等)
|
||||
/// - HTTP 状态码错误
|
||||
/// - 其他未知错误
|
||||
enum APIError: Error, Equatable {
|
||||
case invalidURL
|
||||
case noData
|
||||
@@ -44,31 +60,50 @@ enum APIError: Error, Equatable {
|
||||
}
|
||||
|
||||
// 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 = "molistar_enterprise"
|
||||
let channel: String
|
||||
let model: String
|
||||
let deviceId: String
|
||||
let appVersion: String
|
||||
let app: String = "youmi"
|
||||
let app: String
|
||||
let lang: String
|
||||
let mcc: String?
|
||||
let spType: String?
|
||||
let pubSign: String
|
||||
var pubSign: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case acceptLanguage = "Accept-Language"
|
||||
case appVersion = "appVersion"
|
||||
case os, osVersion, ispType, channel, model, deviceId
|
||||
case app, mcc, spType
|
||||
case os, osVersion, netType, ispType, channel, model, deviceId
|
||||
case appVersion, app, lang, mcc, spType
|
||||
case pubSign = "pub_sign"
|
||||
}
|
||||
|
||||
init() {
|
||||
// 获取系统首选语言
|
||||
self.acceptLanguage = Locale.current.languageCode ?? "en"
|
||||
let preferredLanguage = Locale.current.languageCode ?? "en"
|
||||
self.acceptLanguage = preferredLanguage
|
||||
self.lang = preferredLanguage
|
||||
|
||||
// 获取系统版本
|
||||
self.osVersion = UIDevice.current.systemVersion
|
||||
@@ -76,24 +111,154 @@ struct BaseRequest: Codable {
|
||||
// 获取设备型号
|
||||
self.model = UIDevice.current.model
|
||||
|
||||
// 生成设备ID (这里使用 identifierForVendor,实际项目中可能需要更稳定的方案)
|
||||
// 生成设备ID
|
||||
self.deviceId = UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString
|
||||
|
||||
// 获取应用版本
|
||||
self.appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.0"
|
||||
|
||||
// 运营商相关信息(简化处理)
|
||||
self.ispType = "65535"
|
||||
self.mcc = nil
|
||||
self.spType = nil
|
||||
// 应用名称
|
||||
self.app = Bundle.main.infoDictionary?["CFBundleName"] as? String ?? "yana"
|
||||
|
||||
// 生成签名(这里使用时间戳的 MD5,实际项目中需要根据具体签名规则)
|
||||
let timestamp = String(Int(Date().timeIntervalSince1970))
|
||||
self.pubSign = timestamp.md5()
|
||||
// 网络类型检测(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
|
||||
|
||||
@@ -135,3 +300,4 @@ extension String {
|
||||
|
||||
// 需要导入 CommonCrypto
|
||||
import CommonCrypto
|
||||
|
||||
|
Reference in New Issue
Block a user