Files
e-party-iOS/yana/APIs/LoginModels.swift
edwinQQQ 99a53d7274 feat: 新增我的动态信息结构和相关API请求逻辑
- 在DynamicsModels.swift中新增MyMomentInfo结构,专门用于处理/dynamic/getMyDynamic接口的响应数据。
- 更新MyMomentsResponse结构以使用MyMomentInfo,确保数据类型一致性。
- 在LoginModels.swift中重构IDLoginAPIRequest和EmailLoginRequest,优化queryParameters的实现方式,提升代码可读性。
- 在RecoverPasswordFeature中重构ResetPasswordRequest,优化queryParameters的实现方式,确保一致性。
- 在多个视图中添加调试信息,增强调试能力和用户体验。
- 更新Localizable.strings文件,新增动态列表为空时的提示信息,提升用户交互体验。
2025-08-04 19:12:31 +08:00

700 lines
21 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
// 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
var bodyParameters: [String: Any]? { nil }
let timeout: TimeInterval = 30.0
// MARK: - Private Properties
private let phone: String
private let password: String
private let clientSecret: String
private let version: String
private let clientId: String
private let grantType: String
// MARK: - Computed Properties
var queryParameters: [String: String]? {
return [
"phone": phone,
"password": password,
"client_secret": clientSecret,
"version": version,
"client_id": clientId,
"grant_type": grantType
]
}
/// ID
/// - Parameters:
/// - phone: DESID/
/// - 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.phone = phone
self.password = password
self.clientSecret = clientSecret
self.version = version
self.clientId = clientId
self.grantType = 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 uid: Int?
let userId: String? //
let nick: String?
let nickname: String? //
let avatar: String?
let region: String?
let regionDesc: String?
let gender: Int?
let birth: Int64?
let userDesc: String?
let userLevelVo: UserLevelVo?
let userVipInfoVO: UserVipInfoVO?
let medalsPic: [MedalsPic]?
let userHeadwear: UserHeadwear?
let privatePhoto: [PrivatePhoto]?
let createTime: Int64?
let phoneAreaCode: String?
let erbanNo: Int?
let isCertified: Bool?
let isBindPhone: Bool?
let isBindApple: Bool?
let isBindPasswd: Bool?
let isBindPaymentPwd: Bool?
let banAccount: Bool?
let visitNum: Int?
let fansNum: Int?
let followNum: Int?
let visitHide: Bool?
let visitListView: Bool?
let newUser: Bool?
let defUser: Int?
let platformRole: Int?
let bindType: Int?
let showLimitCharge: Bool?
let uploadGifAvatarPrice: Int?
let hasRegPacket: Bool?
let hasPrettyErbanNo: Bool?
let hasSuperRole: Bool?
let isRechargeUser: Bool?
let isFirstCharge: Bool?
let fromSayHelloChannel: Bool?
let partitionId: Int?
let useStatus: Int?
let micNickColor: String?
let micCircle: String?
let audioCard: AudioCard?
let userInfoSkillVo: UserInfoSkillVo?
let userInfoCardPic: String?
let iosBubbleUrl: String?
let androidBubbleUrl: String?
let status: String? //
let username: String? //
let email: String? //
let phone: String? //
let updateTime: String? //
enum CodingKeys: String, CodingKey {
case uid
case userId = "user_id"
case nick
case nickname
case avatar
case region
case regionDesc
case gender
case birth
case userDesc
case userLevelVo
case userVipInfoVO
case medalsPic
case userHeadwear
case privatePhoto
case createTime
case phoneAreaCode
case erbanNo
case isCertified
case isBindPhone
case isBindApple
case isBindPasswd
case isBindPaymentPwd
case banAccount
case visitNum
case fansNum
case followNum
case visitHide
case visitListView
case newUser
case defUser
case platformRole
case bindType
case showLimitCharge
case uploadGifAvatarPrice
case hasRegPacket
case hasPrettyErbanNo
case hasSuperRole
case isRechargeUser
case isFirstCharge
case fromSayHelloChannel
case partitionId
case useStatus
case micNickColor
case micCircle
case audioCard
case userInfoSkillVo
case userInfoCardPic
case iosBubbleUrl
case androidBubbleUrl
case status
case username
case email
case phone
case updateTime
}
}
// MARK: -
struct UserLevelVo: Codable, Equatable {
let experUrl: String?
let charmLevelSeq: Int?
let experLevelName: String?
let charmLevelName: String?
let charmAmount: Int?
let experLevelGrp: String?
let charmUrl: String?
let experLevelSeq: Int?
let experAmount: Int?
let charmLevelGrp: String?
}
struct UserVipInfoVO: Codable, Equatable {
let vipIcon: String?
let nameplateId: Int?
let vipLogo: String?
let userCardBG: String?
let preventKick: Bool?
let preventTrace: Bool?
let preventFollow: Bool?
let micNickColour: String?
let micCircle: String?
let enterRoomEffects: String?
let medalSeat: Int?
let friendNickColour: String?
let visitHide: Bool?
let visitListView: Bool?
let privateChatLimit: Bool?
let nameplateUrl: String?
let roomPicScreen: Bool?
let uploadGifAvatar: Bool?
let expireTime: Int64?
let enterHide: Bool?
let vipLevel: Int?
let vipName: String?
}
struct MedalsPic: Codable, Equatable {
let picUrl: String?
let mp4Url: String?
}
struct UserHeadwear: Codable, Equatable {
let expireTime: Int64?
let renewPrice: Int?
let uid: Int?
let comeFrom: Int?
let labelType: Int?
let limitDesc: String?
let redirectLink: String?
let headwearId: Int?
let buyTime: Int64?
let pic: String?
let used: Bool?
let price: Int?
let originalPrice: Int?
let type: Int?
let days: Int?
let headwearName: String?
let effect: String?
let expireDays: Int?
let status: Int?
}
struct PrivatePhoto: Codable, Equatable {
let seqNo: Int?
let photoUrl: String?
let createTime: Int64?
let review: Bool?
let pid: Int?
}
struct AudioCard: Codable, Equatable {
let uid: Int?
let status: Int?
}
struct UserInfoSkillVo: Codable, Equatable {
let liveTag: Bool?
let liveSkillVoList: [LiveSkillVo]?
}
struct LiveSkillVo: Codable, Equatable {
// API
}
// MARK: - Login Helper
struct LoginHelper {
/// ID
/// DES
/// - Parameters:
/// - userID: ID
/// - password:
/// - Returns: APInil
static func createIDLoginRequest(userID: String, password: String) async -> IDLoginAPIRequest? {
// 使DESID
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
var bodyParameters: [String: Any]? { nil }
let timeout: TimeInterval = 30.0
// MARK: - Private Properties
private let email: String
private let code: String
private let clientSecret: String
private let version: String
private let clientId: String
private let grantType: String
// MARK: - Computed Properties
var queryParameters: [String: String]? {
return [
"email": email,
"code": code,
"client_secret": clientSecret,
"version": version,
"client_id": clientId,
"grant_type": grantType
]
}
///
/// - 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.email = email
self.code = code
self.clientSecret = clientSecret
self.version = version
self.clientId = clientId
self.grantType = grantType
}
}
// MARK: - Email Login Helper
extension LoginHelper {
///
/// - Parameter email:
/// - Returns: APInil
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: APInil
static func createEmailLoginRequest(email: String, code: String) async -> EmailLoginRequest? {
let encryptionKey = "1ea53d260ecf11e7b56e00163e046a26"
guard let encryptedEmail = DESEncrypt.encryptUseDES(email, key: encryptionKey) else {
debugErrorSync("❌ 邮箱DES加密失败")
return nil
}
debugInfoSync("🔐 邮箱验证码登录DES加密成功")
debugInfoSync(" 原始邮箱: \(email)")
debugInfoSync(" 加密邮箱: \(encryptedEmail)")
debugInfoSync(" 验证码: \(code)")
return EmailLoginRequest(email: encryptedEmail, code: code)
}
}
// MARK: - User Info API Models
///
struct GetUserInfoRequest: APIRequestProtocol {
typealias Response = GetUserInfoResponse
let endpoint = APIEndpoint.getUserInfo.path
let method: HTTPMethod = .GET
let includeBaseParameters = true
var bodyParameters: [String: Any]? { nil }
let timeout: TimeInterval = 30.0
let shouldShowLoading: Bool = false // loading
let shouldShowError: Bool = false //
// MARK: - Private Properties
private let uid: String
// MARK: - Computed Properties
var queryParameters: [String: String]? {
return [
"uid": uid
]
}
///
/// - Parameter uid: ID
init(uid: String) {
self.uid = uid
}
}
///
struct GetUserInfoResponse: Codable, Equatable {
let code: Int?
let message: String?
let timestamp: Int64?
let data: UserInfo?
///
var isSuccess: Bool {
return code == 200
}
///
var errorMessage: String {
return message ?? "获取用户信息失败,请重试"
}
}
// MARK: - User Info Helper
struct UserInfoHelper {
///
/// - Parameter uid: ID
/// - Returns: API
static func createGetUserInfoRequest(uid: String) -> GetUserInfoRequest {
return GetUserInfoRequest(uid: uid)
}
///
/// - Parameter uid: ID
static func debugGetUserInfoRequest(uid: String) {
debugInfoSync("👤 获取用户信息请求调试")
debugInfoSync(" UID: \(uid)")
debugInfoSync(" Endpoint: /user/get")
debugInfoSync(" Method: GET")
debugInfoSync(" Parameters: uid=\(uid)")
}
}