import Foundation // MARK: - Account Model /// 账户认证信息模型 /// 用于承接 oauth/token 和 oauth/ticket 接口的认证数据 /// 参照 OC 版本的 AccountModel 设计 struct AccountModel: Codable, Equatable { let uid: String? // 用户唯一标识 let jti: String? // JWT ID let tokenType: String? // Token 类型 (bearer) let refreshToken: String? // 刷新令牌 let netEaseToken: String? // 网易云信令牌 let accessToken: String? // OAuth 访问令牌 let expiresIn: Int? // 过期时间(秒) let scope: String? // 权限范围 var ticket: String? // 业务会话票据(来自 oauth/ticket) enum CodingKeys: String, CodingKey { case uid case jti case tokenType = "token_type" case refreshToken = "refresh_token" case netEaseToken case accessToken = "access_token" case expiresIn = "expires_in" case scope case ticket } /// 检查是否有有效的认证信息 var hasValidAuthentication: Bool { return accessToken != nil && !accessToken!.isEmpty } /// 检查是否有有效的业务会话 var hasValidSession: Bool { return hasValidAuthentication && ticket != nil && !ticket!.isEmpty } /// 从 IDLoginData 创建 AccountModel /// - Parameter loginData: 登录响应数据 /// - Returns: AccountModel 实例,如果数据无效则返回 nil static func from(loginData: IDLoginData) -> AccountModel? { // 确保至少有 accessToken 和 uid guard let accessToken = loginData.accessToken, let uid = loginData.uid else { return nil } return AccountModel( uid: String(uid), jti: loginData.jti, tokenType: loginData.tokenType, refreshToken: loginData.refreshToken, netEaseToken: loginData.netEaseToken, accessToken: accessToken, expiresIn: loginData.expiresIn, scope: loginData.scope, ticket: nil // 初始为空,后续通过 oauth/ticket 填充 ) } /// 更新 ticket 信息 /// - Parameter ticket: 从 oauth/ticket 获取的票据 /// - Returns: 更新后的 AccountModel func withTicket(_ ticket: String) -> AccountModel { var updatedModel = self updatedModel.ticket = ticket return updatedModel } } // MARK: - ID Login Request Model struct IDLoginAPIRequest: APIRequestProtocol { typealias Response = IDLoginResponse let endpoint = APIEndpoint.login.path // 使用枚举定义的登录端点 let method: HTTPMethod = .POST let includeBaseParameters = true let queryParameters: [String: String]? var bodyParameters: [String: Any]? { nil } let timeout: TimeInterval = 30.0 /// 初始化ID登录请求 /// - Parameters: /// - phone: DES加密后的用户ID/手机号 /// - password: DES加密后的密码 /// - clientSecret: 客户端密钥,固定为"uyzjdhds" /// - version: 版本号,固定为"1" /// - clientId: 客户端ID,固定为"erban-client" /// - grantType: 授权类型,固定为"password" init(phone: String, password: String, clientSecret: String = "uyzjdhds", version: String = "1", clientId: String = "erban-client", grantType: String = "password") { self.queryParameters = [ "phone": phone, "password": password, "client_secret": clientSecret, "version": version, "client_id": clientId, "grant_type": grantType ]; } } // MARK: - ID Login Response Model struct IDLoginResponse: Codable, Equatable { let status: String? let message: String? let code: Int? let data: IDLoginData? /// 是否登录成功 var isSuccess: Bool { return code == 200 || status?.lowercased() == "success" } /// 错误消息(如果有) var errorMessage: String { return message ?? "登录失败,请重试" } } // MARK: - ID Login Data Model struct IDLoginData: Codable, Equatable { let accessToken: String? let refreshToken: String? let tokenType: String? let expiresIn: Int? let scope: String? let userInfo: UserInfo? let uid: Int? // 修改:从String?改为Int?以匹配API返回 let netEaseToken: String? // 新增:网易云token let jti: String? // 新增:JWT token identifier enum CodingKeys: String, CodingKey { case accessToken = "access_token" case refreshToken = "refresh_token" case tokenType = "token_type" case expiresIn = "expires_in" case scope case userInfo = "user_info" case uid case netEaseToken case jti } } // MARK: - User Info Model struct UserInfo: Codable, Equatable { let userId: String? let username: String? let nickname: String? let avatar: String? let email: String? let phone: String? let status: String? let createTime: String? let updateTime: String? enum CodingKeys: String, CodingKey { case userId = "user_id" case username case nickname case avatar case email case phone case status case createTime = "create_time" case updateTime = "update_time" } } // MARK: - Login Helper struct LoginHelper { /// 创建ID登录请求 /// 这个方法会自动处理DES加密 /// - Parameters: /// - userID: 原始用户ID /// - password: 原始密码 /// - Returns: 配置好的API请求,如果加密失败返回nil static func createIDLoginRequest(userID: String, password: String) async -> IDLoginAPIRequest? { // 使用DES加密ID和密码 let encryptionKey = "1ea53d260ecf11e7b56e00163e046a26" guard let encryptedID = DESEncrypt.encryptUseDES(userID, key: encryptionKey), let encryptedPassword = DESEncrypt.encryptUseDES(password, key: encryptionKey) else { debugErrorSync("❌ DES加密失败") return nil } debugInfoSync("🔐 DES加密成功") debugInfoSync(" 原始ID: \(userID)") debugInfoSync(" 加密后ID: \(encryptedID)") debugInfoSync(" 原始密码: \(password)") debugInfoSync(" 加密后密码: \(encryptedPassword)") return IDLoginAPIRequest( phone: userID, password: encryptedPassword ) } } // MARK: - Ticket API Models /// Ticket 请求结构体 struct TicketAPIRequest: APIRequestProtocol { typealias Response = TicketResponse let endpoint = "/oauth/ticket" let method: HTTPMethod = .POST let includeBaseParameters = true let queryParameters: [String: String]? var bodyParameters: [String: Any]? { nil } let timeout: TimeInterval = 30.0 let customHeaders: [String: String]? /// 初始化 Ticket 请求 /// - Parameters: /// - accessToken: OAuth 访问令牌 /// - issueType: 签发类型,固定为"multi" /// - uid: 用户唯一标识,用于添加到请求头 init(accessToken: String, issueType: String = "multi", uid: Int? = nil) { self.queryParameters = [ "access_token": accessToken, "issue_type": issueType ] // 设置自定义请求头 var headers: [String: String] = [:] if let uid = uid { headers["pub_uid"] = "\(uid)" // 转换为字符串 } self.customHeaders = headers.isEmpty ? nil : headers } } /// Ticket 响应结构体 struct TicketResponse: Codable, Equatable { let code: Int? let message: String? let data: TicketData? /// 是否获取成功 var isSuccess: Bool { return code == 200 } /// 错误消息(如果有) var errorMessage: String { return message ?? "Ticket 获取失败,请重试" } /// 获取 Ticket 字符串 var ticket: String? { return data?.tickets?.first?.ticket } } /// Ticket 数据结构体 struct TicketData: Codable, Equatable { let tickets: [TicketInfo]? } /// Ticket 信息结构体 struct TicketInfo: Codable, Equatable { let ticket: String? } // MARK: - Ticket Helper struct TicketHelper { /// 创建 Ticket 请求 /// - Parameters: /// - accessToken: OAuth 访问令牌 /// - uid: 用户唯一标识 /// - Returns: 配置好的 Ticket API 请求 static func createTicketRequest(accessToken: String, uid: Int?) -> TicketAPIRequest { return TicketAPIRequest(accessToken: accessToken, uid: uid) } /// 调试打印 Ticket 请求信息 /// - Parameters: /// - accessToken: OAuth 访问令牌 /// - uid: 用户唯一标识 static func debugTicketRequest(accessToken: String, uid: Int?) { debugInfoSync("🎫 Ticket 请求调试信息") debugInfoSync(" AccessToken: \(accessToken)") debugInfoSync(" UID: \(uid?.description ?? "nil")") debugInfoSync(" Endpoint: /oauth/ticket") debugInfoSync(" Method: POST") debugInfoSync(" Headers: pub_uid = \(uid?.description ?? "nil")") debugInfoSync(" Parameters: access_token=\(accessToken), issue_type=multi") } } // MARK: - 兼容旧的LoginResponse(如果需要) typealias LoginResponse = IDLoginResponse // MARK: - Email Verification Code Models /// 邮箱验证码获取请求 struct EmailGetCodeRequest: APIRequestProtocol { typealias Response = EmailGetCodeResponse let endpoint = APIEndpoint.emailGetCode.path let method: HTTPMethod = .POST let includeBaseParameters = true let queryParameters: [String: String]? var bodyParameters: [String: Any]? { nil } let timeout: TimeInterval = 30.0 /// 初始化邮箱验证码获取请求 /// - Parameters: /// - emailAddress: DES加密后的邮箱地址 /// - type: 验证码类型(1=注册/登录) init(emailAddress: String, type: Int = 1) { self.queryParameters = [ "emailAddress": emailAddress, "type": String(type) ] } } /// 邮箱验证码获取响应 struct EmailGetCodeResponse: Codable, Equatable { let status: String? let message: String? let code: Int? let data: String? // 通常为空,成功时只需要检查状态码 /// 是否发送成功 var isSuccess: Bool { return code == 200 || status?.lowercased() == "success" } /// 错误消息(如果有) var errorMessage: String { return message ?? "验证码发送失败,请重试" } } /// 邮箱验证码登录请求 struct EmailLoginRequest: APIRequestProtocol { typealias Response = IDLoginResponse // 复用ID登录的响应模型 let endpoint = APIEndpoint.login.path let method: HTTPMethod = .POST let includeBaseParameters = true let queryParameters: [String: String]? var bodyParameters: [String: Any]? { nil } let timeout: TimeInterval = 30.0 /// 初始化邮箱验证码登录请求 /// - Parameters: /// - email: DES加密后的邮箱地址 /// - code: 验证码 /// - clientSecret: 客户端密钥,固定为"uyzjdhds" /// - version: 版本号,固定为"1" /// - clientId: 客户端ID,固定为"erban-client" /// - grantType: 授权类型,固定为"email" init(email: String, code: String, clientSecret: String = "uyzjdhds", version: String = "1", clientId: String = "erban-client", grantType: String = "email") { self.queryParameters = [ "email": email, "code": code, "client_secret": clientSecret, "version": version, "client_id": clientId, "grant_type": grantType ] } } // MARK: - Email Login Helper extension LoginHelper { /// 创建邮箱验证码获取请求 /// - Parameter email: 原始邮箱地址 /// - Returns: 配置好的API请求,如果加密失败返回nil static func createEmailGetCodeRequest(email: String) -> EmailGetCodeRequest? { let encryptionKey = "1ea53d260ecf11e7b56e00163e046a26" guard let encryptedEmail = DESEncrypt.encryptUseDES(email, key: encryptionKey) else { debugErrorSync("❌ 邮箱DES加密失败") return nil } debugInfoSync("🔐 邮箱DES加密成功") debugInfoSync(" 原始邮箱: \(email)") debugInfoSync(" 加密邮箱: \(encryptedEmail)") return EmailGetCodeRequest(emailAddress: email, type: 1) } /// 创建邮箱验证码登录请求 /// - Parameters: /// - email: 原始邮箱地址 /// - code: 验证码 /// - Returns: 配置好的API请求,如果加密失败返回nil static func createEmailLoginRequest(email: String, code: String) async -> EmailLoginRequest? { let encryptionKey = "1ea53d260ecf11e7b56e00163e046a26" guard let encryptedEmail = DESEncrypt.encryptUseDES(email, key: encryptionKey) else { await debugErrorSync("❌ 邮箱DES加密失败") return nil } debugInfoSync("🔐 邮箱验证码登录DES加密成功") debugInfoSync(" 原始邮箱: \(email)") debugInfoSync(" 加密邮箱: \(encryptedEmail)") debugInfoSync(" 验证码: \(code)") return EmailLoginRequest(email: encryptedEmail, code: code) } }