
- 注释掉Podfile中的Alamofire依赖,更新Podfile.lock以反映更改。 - 在yana/APIs/API-README.md中新增自动认证Header机制的详细文档,描述其工作原理、实现细节及最佳实践。 - 在yana/yanaApp.swift中将print语句替换为debugInfo以增强调试信息的输出。 - 在API相关文件中实现用户认证状态检查和相关header的自动添加逻辑,提升API请求的安全性和用户体验。 - 更新多个文件中的日志输出,确保在DEBUG模式下提供详细的调试信息。
223 lines
9.0 KiB
Swift
223 lines
9.0 KiB
Swift
import Foundation
|
||
import ComposableArchitecture
|
||
|
||
@Reducer
|
||
struct LoginFeature {
|
||
@ObservableState
|
||
struct State: Equatable {
|
||
var account: String = ""
|
||
var password: String = ""
|
||
var isLoading = false
|
||
var error: String?
|
||
var idLoginState = IDLoginFeature.State()
|
||
var emailLoginState = EMailLoginFeature.State() // 新增:邮箱登录状态
|
||
|
||
// 新增:Account Model 和 Ticket 相关状态
|
||
var accountModel: AccountModel?
|
||
var isTicketLoading = false
|
||
var ticketError: String?
|
||
var loginStep: LoginStep = .initial
|
||
|
||
enum LoginStep: Equatable {
|
||
case initial // 初始状态
|
||
case authenticating // 正在进行 OAuth 认证
|
||
case gettingTicket // 正在获取 Ticket
|
||
case completed // 认证完成
|
||
case failed // 认证失败
|
||
}
|
||
|
||
#if DEBUG
|
||
init() {
|
||
// 移除测试用的硬编码凭据
|
||
self.account = ""
|
||
self.password = ""
|
||
}
|
||
#endif
|
||
}
|
||
|
||
enum Action {
|
||
case updateAccount(String)
|
||
case updatePassword(String)
|
||
case login
|
||
case loginResponse(TaskResult<IDLoginResponse>)
|
||
case idLogin(IDLoginFeature.Action)
|
||
case emailLogin(EMailLoginFeature.Action) // 新增:邮箱登录action
|
||
|
||
// 新增:Ticket 相关 actions
|
||
case requestTicket(accessToken: String)
|
||
case ticketResponse(TaskResult<TicketResponse>)
|
||
case clearTicketError
|
||
case resetLogin
|
||
}
|
||
|
||
@Dependency(\.apiService) var apiService
|
||
|
||
var body: some ReducerOf<Self> {
|
||
Scope(state: \.idLoginState, action: \.idLogin) {
|
||
IDLoginFeature()
|
||
}
|
||
|
||
Scope(state: \.emailLoginState, action: \.emailLogin) {
|
||
EMailLoginFeature()
|
||
}
|
||
|
||
Reduce { state, action in
|
||
switch action {
|
||
case let .updateAccount(account):
|
||
state.account = account
|
||
return .none
|
||
|
||
case let .updatePassword(password):
|
||
state.password = password
|
||
return .none
|
||
|
||
case .login:
|
||
state.isLoading = true
|
||
state.error = nil
|
||
state.ticketError = nil
|
||
state.loginStep = .authenticating
|
||
|
||
// 实现登录逻辑(使用account和password)
|
||
return .run { [account = state.account, password = state.password] send in
|
||
do {
|
||
// 使用LoginHelper创建加密的登录请求
|
||
guard let loginRequest = LoginHelper.createIDLoginRequest(userID: account, password: password) else {
|
||
await send(.loginResponse(.failure(APIError.decodingError("加密失败"))))
|
||
return
|
||
}
|
||
|
||
// 发起登录请求
|
||
let response = try await apiService.request(loginRequest)
|
||
await send(.loginResponse(.success(response)))
|
||
} catch {
|
||
if let apiError = error as? APIError {
|
||
await send(.loginResponse(.failure(apiError)))
|
||
} else {
|
||
await send(.loginResponse(.failure(APIError.unknown(error.localizedDescription))))
|
||
}
|
||
}
|
||
}
|
||
|
||
case let .loginResponse(.success(response)):
|
||
state.isLoading = false
|
||
if response.isSuccess {
|
||
// OAuth 认证成功,清除错误信息
|
||
state.error = nil
|
||
|
||
// 从响应数据创建 AccountModel
|
||
if let loginData = response.data,
|
||
let accountModel = AccountModel.from(loginData: loginData) {
|
||
state.accountModel = accountModel
|
||
|
||
debugInfo("✅ OAuth 认证成功")
|
||
debugInfo("🔑 Access Token: \(accountModel.accessToken ?? "nil")")
|
||
debugInfo("🆔 用户 UID: \(accountModel.uid ?? "nil")")
|
||
|
||
// 自动获取 ticket
|
||
return .send(.requestTicket(accessToken: accountModel.accessToken!))
|
||
} else {
|
||
state.error = "登录数据格式错误"
|
||
state.loginStep = .failed
|
||
}
|
||
} else {
|
||
state.error = response.errorMessage
|
||
state.loginStep = .failed
|
||
}
|
||
return .none
|
||
|
||
case let .loginResponse(.failure(error)):
|
||
state.isLoading = false
|
||
state.error = error.localizedDescription
|
||
state.loginStep = .failed
|
||
return .none
|
||
|
||
case let .requestTicket(accessToken):
|
||
state.isTicketLoading = true
|
||
state.ticketError = nil
|
||
state.loginStep = .gettingTicket
|
||
|
||
return .run { [accountModel = state.accountModel] send in
|
||
do {
|
||
// 从 AccountModel 获取 uid,转换为 Int 类型
|
||
let uid = accountModel?.uid != nil ? Int(accountModel!.uid!) : nil
|
||
let ticketRequest = TicketHelper.createTicketRequest(accessToken: accessToken, uid: uid)
|
||
let response = try await apiService.request(ticketRequest)
|
||
await send(.ticketResponse(.success(response)))
|
||
} catch {
|
||
debugError("❌ Ticket 获取失败: \(error)")
|
||
await send(.ticketResponse(.failure(APIError.networkError(error.localizedDescription))))
|
||
}
|
||
}
|
||
|
||
case let .ticketResponse(.success(response)):
|
||
state.isTicketLoading = false
|
||
if response.isSuccess {
|
||
state.ticketError = nil
|
||
state.loginStep = .completed
|
||
|
||
debugInfo("✅ 完整登录流程成功")
|
||
debugInfo("🎫 Ticket 获取成功: \(response.ticket ?? "nil")")
|
||
|
||
// 更新 AccountModel 中的 ticket 并保存
|
||
if let ticket = response.ticket {
|
||
if var accountModel = state.accountModel {
|
||
accountModel.ticket = ticket
|
||
state.accountModel = accountModel
|
||
|
||
// 保存完整的 AccountModel
|
||
UserInfoManager.saveAccountModel(accountModel)
|
||
|
||
// 发送 Ticket 获取成功通知,触发导航到主页面
|
||
NotificationCenter.default.post(name: .ticketSuccess, object: nil)
|
||
} else {
|
||
debugError("❌ AccountModel 不存在,无法保存 ticket")
|
||
state.ticketError = "内部错误:账户信息丢失"
|
||
state.loginStep = .failed
|
||
}
|
||
} else {
|
||
state.ticketError = "Ticket 为空"
|
||
state.loginStep = .failed
|
||
}
|
||
|
||
} else {
|
||
state.ticketError = response.errorMessage
|
||
state.loginStep = .failed
|
||
}
|
||
return .none
|
||
|
||
case let .ticketResponse(.failure(error)):
|
||
state.isTicketLoading = false
|
||
state.ticketError = error.localizedDescription
|
||
state.loginStep = .failed
|
||
debugError("❌ Ticket 获取失败: \(error.localizedDescription)")
|
||
return .none
|
||
|
||
case .clearTicketError:
|
||
state.ticketError = nil
|
||
return .none
|
||
|
||
case .resetLogin:
|
||
state.isLoading = false
|
||
state.isTicketLoading = false
|
||
state.error = nil
|
||
state.ticketError = nil
|
||
state.accountModel = nil // 清除 AccountModel
|
||
state.loginStep = .initial
|
||
|
||
// 清除本地存储的认证信息
|
||
UserInfoManager.clearAllAuthenticationData()
|
||
|
||
return .none
|
||
|
||
case .idLogin:
|
||
// IDLogin动作由子feature处理
|
||
return .none
|
||
|
||
case .emailLogin:
|
||
// EmailLogin动作由子feature处理
|
||
return .none
|
||
}
|
||
}
|
||
}
|
||
}
|