import Foundation import ComposableArchitecture @Reducer struct IDLoginFeature { @ObservableState struct State: Equatable { var userID: String = "" var password: String = "" var isPasswordVisible = false var isLoading = false var errorMessage: String? // 新增: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 // 认证失败 } init() { self.userID = "" self.password = "" } } enum Action: Equatable { case userIDChanged(String) case passwordChanged(String) case togglePasswordVisibility case loginButtonTapped(userID: String, password: String) case forgotPasswordTapped case backButtonTapped case loginResponse(TaskResult) // 新增:Ticket 相关 actions case requestTicket(accessToken: String) case ticketResponse(TaskResult) case clearTicketError case resetLogin } @Dependency(\.apiService) var apiService var body: some ReducerOf { Reduce { state, action in switch action { case let .userIDChanged(userID): state.userID = userID return .none case let .passwordChanged(password): state.password = password return .none case .togglePasswordVisibility: state.isPasswordVisible.toggle() return .none case let .loginButtonTapped(userID, password): state.userID = userID state.password = password state.isLoading = true state.errorMessage = nil state.ticketError = nil state.loginStep = .authenticating // 真实登录 API 调用 Effect return .run { send in do { guard let loginRequest = await LoginHelper.createIDLoginRequest(userID: userID, 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 .forgotPasswordTapped: return .none case .backButtonTapped: return .none case let .loginResponse(.success(response)): state.isLoading = false if response.isSuccess { state.errorMessage = nil if let loginData = response.data, let accountModel = AccountModel.from(loginData: loginData) { state.accountModel = accountModel // 触发 Effect 保存 userInfo(如有) if let userInfo = loginData.userInfo { return .run { _ in await UserInfoManager.saveUserInfo(userInfo) } } // 自动获取 ticket return .send(.requestTicket(accessToken: accountModel.accessToken!)) } else { state.errorMessage = "登录数据格式错误" state.loginStep = .failed } } else { state.errorMessage = response.errorMessage state.loginStep = .failed } return .none case let .loginResponse(.failure(error)): state.isLoading = false state.errorMessage = error.localizedDescription state.loginStep = .failed return .none case let .requestTicket(accessToken): state.isTicketLoading = true state.ticketError = nil state.loginStep = .gettingTicket // 先拷贝所需字段,避免并发捕获 let uid: Int? = { if let am = state.accountModel, let uidStr = am.uid { return Int(uidStr) } else { return nil } }() return .run { send in do { let ticketRequest = TicketHelper.createTicketRequest(accessToken: accessToken, uid: uid) let response = try await apiService.request(ticketRequest) await send(.ticketResponse(.success(response))) } catch { debugErrorSync("❌ ID登录 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("✅ ID 登录完整流程成功") debugInfoSync("🎫 Ticket 获取成功: \(response.ticket ?? "nil")") if let ticket = response.ticket, let oldAccountModel = state.accountModel { let newAccountModel = oldAccountModel.withTicket(ticket) state.accountModel = newAccountModel return .run { _ in await UserInfoManager.saveAccountModel(newAccountModel) // 新增:登录成功后自动获取用户信息 debugInfoSync("🔄 登录成功,开始获取用户信息") if let _ = await UserInfoManager.fetchUserInfoFromServer( uid: newAccountModel.uid, apiService: apiService ) { debugInfoSync("✅ 用户信息获取成功") } else { debugErrorSync("❌ 用户信息获取失败,但不影响登录流程") } } } else if response.ticket == nil { state.ticketError = "Ticket 为空" state.loginStep = .failed } else { debugErrorSync("❌ AccountModel 不存在,无法保存 ticket") state.ticketError = "内部错误:账户信息丢失" 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("❌ ID 登录 Ticket 获取失败: \(error.localizedDescription)") return .none case .clearTicketError: state.ticketError = nil return .none case .resetLogin: state.isLoading = false state.isTicketLoading = false state.errorMessage = nil state.ticketError = nil state.accountModel = nil state.loginStep = .initial // Effect 清除认证信息 return .run { _ in await UserInfoManager.clearAllAuthenticationData() } } } } }