
- 在APIEndpoints.swift中新增getUserInfo端点以支持获取用户信息。 - 在APIModels.swift中实现获取用户信息请求和响应模型,处理用户信息的请求与解析。 - 在UserInfoManager中新增方法以从服务器获取用户信息,并在登录成功后自动获取用户信息。 - 在SettingFeature中新增用户信息刷新状态管理,支持用户信息的刷新操作。 - 在SettingView中集成用户信息刷新按钮,提升用户体验。 - 在SplashFeature中实现自动获取用户信息的逻辑,优化用户登录流程。 - 在yanaAPITests中添加用户信息相关的单元测试,确保功能的正确性。
209 lines
8.3 KiB
Swift
209 lines
8.3 KiB
Swift
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
|
||
// 新增:登录流程状态
|
||
var loginStep: LoginStep = .initial
|
||
enum LoginStep: Equatable {
|
||
case initial
|
||
case authenticating
|
||
case completed
|
||
case failed
|
||
}
|
||
|
||
#if DEBUG
|
||
init() {
|
||
self.email = "exzero@126.com"
|
||
self.verificationCode = ""
|
||
self.loginStep = .initial
|
||
}
|
||
#endif
|
||
}
|
||
|
||
enum Action {
|
||
case emailChanged(String)
|
||
case verificationCodeChanged(String)
|
||
case getVerificationCodeTapped
|
||
case getCodeResponse(Result<EmailGetCodeResponse, Error>)
|
||
case loginButtonTapped(email: String, verificationCode: String)
|
||
case loginResponse(Result<AccountModel, Error>)
|
||
case forgotPasswordTapped
|
||
case resetState
|
||
}
|
||
|
||
@Dependency(\.apiService) var apiService
|
||
|
||
var body: some ReducerOf<Self> {
|
||
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 = NSLocalizedString("email_login.email_required", comment: "")
|
||
return .none
|
||
}
|
||
|
||
guard ValidationHelper.isValidEmail(state.email) else {
|
||
state.errorMessage = NSLocalizedString("email_login.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 = 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 = NSLocalizedString("email_login.fields_required", comment: "")
|
||
return .none
|
||
}
|
||
|
||
guard ValidationHelper.isValidEmail(email) else {
|
||
state.errorMessage = NSLocalizedString("email_login.invalid_email", comment: "")
|
||
return .none
|
||
}
|
||
|
||
state.isLoading = true
|
||
state.errorMessage = nil
|
||
state.loginStep = .authenticating
|
||
|
||
return .run { send in
|
||
do {
|
||
guard let request = await 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
|
||
state.loginStep = .completed
|
||
// Effect 保存AccountModel并获取用户信息
|
||
return .run { _ in
|
||
await UserInfoManager.saveAccountModel(accountModel)
|
||
|
||
// 新增:登录成功后自动获取用户信息
|
||
debugInfoSync("🔄 邮箱登录成功,开始获取用户信息")
|
||
if let _ = await UserInfoManager.fetchUserInfoFromServer(
|
||
uid: accountModel.uid,
|
||
apiService: apiService
|
||
) {
|
||
debugInfoSync("✅ 用户信息获取成功")
|
||
} else {
|
||
debugErrorSync("❌ 用户信息获取失败,但不影响登录流程")
|
||
}
|
||
}
|
||
|
||
case .loginResponse(.failure(let error)):
|
||
state.isLoading = false
|
||
state.loginStep = .failed
|
||
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
|
||
state.loginStep = .initial
|
||
return .none
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|