import Foundation import ComposableArchitecture @Reducer struct EMailLoginFeature { @ObservableState struct State: Equatable { var email: String = "" var verificationCode: String = "" var isLoading: Bool = false var isCodeLoading: Bool = false var errorMessage: String? = nil var isCodeSent: Bool = false #if DEBUG init() { self.email = "exzero@126.com" self.verificationCode = "" } #endif } enum Action { case emailChanged(String) case verificationCodeChanged(String) case getVerificationCodeTapped case getCodeResponse(Result) case loginButtonTapped(email: String, verificationCode: String) case loginResponse(Result) case forgotPasswordTapped case resetState } @Dependency(\.apiService) var apiService var body: some ReducerOf { Reduce { state, action in switch action { case .emailChanged(let email): state.email = email state.errorMessage = nil return .none case .verificationCodeChanged(let code): state.verificationCode = code state.errorMessage = nil return .none case .getVerificationCodeTapped: guard !state.email.isEmpty else { state.errorMessage = "email_login.email_required".localized return .none } guard ValidationHelper.isValidEmail(state.email) else { state.errorMessage = "email_login.invalid_email".localized return .none } state.isCodeLoading = true state.isCodeSent = false // 重置状态确保触发变化 state.errorMessage = nil return .run { [email = state.email] send in do { guard let request = LoginHelper.createEmailGetCodeRequest(email: email) else { await send(.getCodeResponse(.failure(APIError.encryptionFailed))) return } let response = try await apiService.request(request) await send(.getCodeResponse(.success(response))) } catch { await send(.getCodeResponse(.failure(error))) } } case .getCodeResponse(.success(let response)): state.isCodeLoading = false if response.isSuccess { state.isCodeSent = true return .none } else { state.errorMessage = response.errorMessage return .none } case .getCodeResponse(.failure(let error)): state.isCodeLoading = false if let apiError = error as? APIError { state.errorMessage = apiError.localizedDescription } else { state.errorMessage = "验证码发送失败,请检查网络连接" } return .none case .loginButtonTapped(let email, let verificationCode): guard !email.isEmpty && !verificationCode.isEmpty else { state.errorMessage = "email_login.fields_required".localized return .none } guard ValidationHelper.isValidEmail(email) else { state.errorMessage = "email_login.invalid_email".localized return .none } state.isLoading = true state.errorMessage = nil return .run { send in do { guard let request = LoginHelper.createEmailLoginRequest(email: email, code: verificationCode) else { await send(.loginResponse(.failure(APIError.encryptionFailed))) return } let response = try await apiService.request(request) if response.isSuccess, let loginData = response.data { guard let accountModel = AccountModel.from(loginData: loginData) else { await send(.loginResponse(.failure(APIError.invalidResponse))) return } // 第二阶段:获取Ticket let ticketRequest = TicketHelper.createTicketRequest( accessToken: accountModel.accessToken ?? "", uid: accountModel.uid.flatMap { Int($0) } ) let ticketResponse = try await apiService.request(ticketRequest) if ticketResponse.isSuccess, let ticket = ticketResponse.ticket { let completeAccount = accountModel.withTicket(ticket) await send(.loginResponse(.success(completeAccount))) } else { await send(.loginResponse(.failure(APIError.ticketFailed))) } } else { await send(.loginResponse(.failure(APIError.custom(response.errorMessage)))) } } catch { await send(.loginResponse(.failure(error))) } } case .loginResponse(.success(let accountModel)): state.isLoading = false // 保存AccountModel到本地存储 UserInfoManager.saveAccountModel(accountModel) // 发送成功通知,触发导航到主界面 NotificationCenter.default.post(name: .ticketSuccess, object: nil) return .none case .loginResponse(.failure(let error)): state.isLoading = false if let apiError = error as? APIError { state.errorMessage = apiError.localizedDescription } else { state.errorMessage = "登录失败,请重试" } return .none case .forgotPasswordTapped: return .none case .resetState: state.email = "" state.verificationCode = "" state.isLoading = false state.isCodeLoading = false state.errorMessage = nil state.isCodeSent = false return .none } } } }