import Foundation import ComposableArchitecture @Reducer struct RecoverPasswordFeature { @ObservableState struct State: Equatable { var email: String = "" var verificationCode: String = "" var newPassword: String = "" var isCodeLoading: Bool = false var isResetLoading: Bool = false var isResetSuccess: Bool = false var errorMessage: String? = nil var isCodeSent: Bool = false #if DEBUG init() { self.email = "exzero@126.com" self.verificationCode = "" self.newPassword = "" } #endif } enum Action { case emailChanged(String) case verificationCodeChanged(String) case newPasswordChanged(String) case getVerificationCodeTapped case getCodeResponse(Result) case resetPasswordTapped case resetPasswordResponse(Result) case resetSuccess 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 .newPasswordChanged(let password): state.newPassword = password state.errorMessage = nil return .none case .getVerificationCodeTapped: guard !state.email.isEmpty else { state.errorMessage = LocalizedStringSync("recover_password.email_required", comment: "") return .none } guard ValidationHelper.isValidEmail(state.email) else { state.errorMessage = LocalizedStringSync("recover_password.invalid_email", comment: "") return .none } state.isCodeLoading = true state.isCodeSent = false state.errorMessage = nil return .run { [email = state.email] send in do { guard let request = RecoverPasswordHelper.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 = LocalizedStringSync("recover_password.code_send_failed", comment: "") } return .none case .resetPasswordTapped: guard !state.email.isEmpty && !state.verificationCode.isEmpty && !state.newPassword.isEmpty else { state.errorMessage = LocalizedStringSync("recover_password.fields_required", comment: "") return .none } guard ValidationHelper.isValidEmail(state.email) else { state.errorMessage = LocalizedStringSync("recover_password.invalid_email", comment: "") return .none } guard ValidationHelper.isValidPassword(state.newPassword) else { state.errorMessage = LocalizedStringSync("recover_password.invalid_password", comment: "") return .none } state.isResetLoading = true state.errorMessage = nil return .run { [email = state.email, code = state.verificationCode, password = state.newPassword] send in do { guard let request = RecoverPasswordHelper.createResetPasswordRequest( email: email, code: code, newPassword: password ) else { await send(.resetPasswordResponse(.failure(APIError.encryptionFailed))) return } let response = try await apiService.request(request) await send(.resetPasswordResponse(.success(response))) } catch { await send(.resetPasswordResponse(.failure(error))) } } case .resetPasswordResponse(.success(let response)): state.isResetLoading = false if response.isSuccess { state.isResetSuccess = true state.errorMessage = nil return .send(.resetSuccess) } else { state.errorMessage = response.errorMessage return .none } case .resetPasswordResponse(.failure(let error)): state.isResetLoading = false if let apiError = error as? APIError { state.errorMessage = apiError.localizedDescription } else { state.errorMessage = LocalizedStringSync("recover_password.reset_failed", comment: "") } return .none case .resetSuccess: // 密码重置成功,准备返回上一页 return .none case .resetState: state.email = "" state.verificationCode = "" state.newPassword = "" state.isCodeLoading = false state.isResetLoading = false state.isResetSuccess = false state.errorMessage = nil state.isCodeSent = false return .none } } } } // MARK: - Password Reset API Models /// 密码重置响应模型 struct ResetPasswordResponse: 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 ?? LocalizedStringSync("recover_password.reset_failed", comment: "") } } /// 密码重置请求 - 新API端点 struct ResetPasswordRequest: APIRequestProtocol { typealias Response = ResetPasswordResponse let endpoint = "/acc/pwd/resetByEmail" // 新的API端点 let method: HTTPMethod = .POST let includeBaseParameters = true let queryParameters: [String: String]? var bodyParameters: [String: Any]? { nil } let timeout: TimeInterval = 30.0 /// 初始化密码重置请求 /// - Parameters: /// - email: DES加密后的邮箱地址 /// - code: 验证码 /// - newPwd: DES加密后的新密码 init(email: String, code: String, newPwd: String) { self.queryParameters = [ "email": email, "newPwd": newPwd, // 参数名改为newPwd "code": code ] } } // MARK: - Recover Password Helper struct RecoverPasswordHelper { /// 创建邮箱验证码获取请求(复用邮箱登录的实现) /// - Parameter email: 原始邮箱地址 /// - Returns: 配置好的API请求,如果加密失败返回nil 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)") // 使用type=3表示密码重置验证码 return EmailGetCodeRequest(emailAddress: email, type: 3) } /// 创建密码重置请求 /// - Parameters: /// - email: 原始邮箱地址 /// - code: 验证码 /// - newPassword: 新密码 /// - Returns: 配置好的API请求,如果加密失败返回nil static func createResetPasswordRequest(email: String, code: String, newPassword: String) -> ResetPasswordRequest? { let encryptionKey = "1ea53d260ecf11e7b56e00163e046a26" guard let encryptedEmail = DESEncrypt.encryptUseDES(email, key: encryptionKey), let encryptedPassword = DESEncrypt.encryptUseDES(newPassword, key: encryptionKey) else { debugErrorSync("❌ 密码重置DES加密失败") return nil } debugInfoSync("🔐 密码重置DES加密成功") debugInfoSync(" 原始邮箱: \(email)") debugInfoSync(" 加密邮箱: \(encryptedEmail)") debugInfoSync(" 验证码: \(code)") debugInfoSync(" 原始新密码: \(newPassword)") debugInfoSync(" 加密新密码: \(encryptedPassword)") return ResetPasswordRequest( email: email, code: code, newPwd: encryptedPassword // 参数名改为newPwd ) } }