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() // 新增:邮箱登录状态 // 新增:HomeFeature 状态 var homeState = HomeFeature.State() // 新增:Account Model 和 Ticket 相关状态 var accountModel: AccountModel? var isTicketLoading = false var ticketError: String? var loginStep: LoginStep = .initial // 新增:初始化状态管理 - 防止重复执行 var isInitialized = false // 新增:任一登录方式完成时为 true var isAnyLoginCompleted: Bool { idLoginState.loginStep == .completed || emailLoginState.loginStep == .completed } 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 onAppear case updateAccount(String) case updatePassword(String) case login case loginResponse(TaskResult) case idLogin(IDLoginFeature.Action) case emailLogin(EMailLoginFeature.Action) // 新增:邮箱登录action // 新增:HomeFeature action case home(HomeFeature.Action) // 新增:Ticket 相关 actions case requestTicket(accessToken: String) case ticketResponse(TaskResult) case clearTicketError case resetLogin } @Dependency(\.apiService) var apiService var body: some ReducerOf { Scope(state: \.idLoginState, action: \.idLogin) { IDLoginFeature() } Scope(state: \.emailLoginState, action: \.emailLogin) { EMailLoginFeature() } // 新增:HomeFeature 作用域 Scope(state: \.homeState, action: \.home) { HomeFeature() } Reduce { state, action in switch action { case .onAppear: // 防止重复初始化 guard !state.isInitialized else { debugInfoSync("🚀 LoginFeature: 已初始化,跳过重复执行") return .none } state.isInitialized = true debugInfoSync("🚀 LoginFeature: 首次初始化") // 登录页面出现时的初始化逻辑 return .none 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 = await 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 debugInfoSync("✅ OAuth 认证成功") debugInfoSync("🔑 Access Token: \(accountModel.accessToken ?? "nil")") debugInfoSync("🆔 用户 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 { debugErrorSync("❌ 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 debugInfoSync("✅ 完整登录流程成功") debugInfoSync("🎫 Ticket 获取成功: \(response.ticket ?? "nil")") // 更新 AccountModel 中的 ticket 并保存 if let ticket = response.ticket { if let oldAccountModel = state.accountModel { let newAccountModel = oldAccountModel.withTicket(ticket) state.accountModel = newAccountModel // Effect 保存完整的 AccountModel return .run { _ in await UserInfoManager.saveAccountModel(newAccountModel) } } else { debugErrorSync("❌ 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 debugErrorSync("❌ 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 // Effect 清除本地存储的认证信息 return .run { _ in await UserInfoManager.clearAllAuthenticationData() } case .idLogin: // IDLogin动作由子feature处理 return .none case .emailLogin: // EmailLogin动作由子feature处理 return .none case .home(_): return .none } } } } // 移除:未使用的通知名称定义 // extension Notification.Name { // static let ticketSuccess = Notification.Name("ticketSuccess") // }