feat: 增强邮箱登录功能和密码恢复流程
- 更新邮箱登录相关功能,新增邮箱验证码获取和登录API端点。 - 添加AccountModel以管理用户认证信息,支持会话票据的存储和更新。 - 实现密码恢复功能,支持通过邮箱获取验证码和重置密码。 - 增加本地化支持,更新相关字符串以适应新功能。 - 引入ValidationHelper以验证邮箱和密码格式,确保用户输入的有效性。 - 更新视图以支持邮箱登录和密码恢复的用户交互。
This commit is contained in:
@@ -10,14 +10,13 @@ struct LoginFeature {
|
||||
var isLoading = false
|
||||
var error: String?
|
||||
var idLoginState = IDLoginFeature.State()
|
||||
var emailLoginState = EMailLoginFeature.State() // 新增:邮箱登录状态
|
||||
|
||||
// 新增:Ticket 相关状态
|
||||
var accessToken: String?
|
||||
var ticket: String?
|
||||
// 新增:Account Model 和 Ticket 相关状态
|
||||
var accountModel: AccountModel?
|
||||
var isTicketLoading = false
|
||||
var ticketError: String?
|
||||
var loginStep: LoginStep = .initial
|
||||
var uid: Int? // 修改:保存用户 uid,类型改为Int
|
||||
|
||||
enum LoginStep: Equatable {
|
||||
case initial // 初始状态
|
||||
@@ -36,12 +35,13 @@ struct LoginFeature {
|
||||
#endif
|
||||
}
|
||||
|
||||
enum Action: Equatable {
|
||||
enum Action {
|
||||
case updateAccount(String)
|
||||
case updatePassword(String)
|
||||
case login
|
||||
case loginResponse(TaskResult<IDLoginResponse>)
|
||||
case idLogin(IDLoginFeature.Action)
|
||||
case emailLogin(EMailLoginFeature.Action) // 新增:邮箱登录action
|
||||
|
||||
// 新增:Ticket 相关 actions
|
||||
case requestTicket(accessToken: String)
|
||||
@@ -57,6 +57,10 @@ struct LoginFeature {
|
||||
IDLoginFeature()
|
||||
}
|
||||
|
||||
Scope(state: \.emailLoginState, action: \.emailLogin) {
|
||||
EMailLoginFeature()
|
||||
}
|
||||
|
||||
Reduce { state, action in
|
||||
switch action {
|
||||
case let .updateAccount(account):
|
||||
@@ -99,20 +103,21 @@ struct LoginFeature {
|
||||
if response.isSuccess {
|
||||
// OAuth 认证成功,清除错误信息
|
||||
state.error = nil
|
||||
state.accessToken = response.data?.accessToken
|
||||
state.uid = response.data?.uid // 保存 uid
|
||||
|
||||
print("✅ OAuth 认证成功")
|
||||
if let accessToken = response.data?.accessToken {
|
||||
print("🔑 Access Token: \(accessToken)")
|
||||
// 自动获取 ticket,传递 uid
|
||||
return .send(.requestTicket(accessToken: accessToken))
|
||||
}
|
||||
if let userInfo = response.data?.userInfo {
|
||||
print("👤 用户信息: \(userInfo)")
|
||||
}
|
||||
if let uid = response.data?.uid {
|
||||
print("🆔 用户 UID: \(uid)")
|
||||
// 从响应数据创建 AccountModel
|
||||
if let loginData = response.data,
|
||||
let accountModel = AccountModel.from(loginData: loginData) {
|
||||
state.accountModel = accountModel
|
||||
|
||||
print("✅ OAuth 认证成功")
|
||||
print("🔑 Access Token: \(accountModel.accessToken ?? "nil")")
|
||||
print("🆔 用户 UID: \(accountModel.uid ?? "nil")")
|
||||
|
||||
// 自动获取 ticket
|
||||
return .send(.requestTicket(accessToken: accountModel.accessToken!))
|
||||
} else {
|
||||
state.error = "登录数据格式错误"
|
||||
state.loginStep = .failed
|
||||
}
|
||||
} else {
|
||||
state.error = response.errorMessage
|
||||
@@ -131,9 +136,10 @@ struct LoginFeature {
|
||||
state.ticketError = nil
|
||||
state.loginStep = .gettingTicket
|
||||
|
||||
return .run { [uid = state.uid] send in
|
||||
return .run { [accountModel = state.accountModel] send in
|
||||
do {
|
||||
// 使用 TicketHelper 创建请求,传递 uid
|
||||
// 从 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)))
|
||||
@@ -147,25 +153,32 @@ struct LoginFeature {
|
||||
state.isTicketLoading = false
|
||||
if response.isSuccess {
|
||||
state.ticketError = nil
|
||||
state.ticket = response.ticket
|
||||
state.loginStep = .completed
|
||||
|
||||
print("✅ 完整登录流程成功")
|
||||
print("🎫 Ticket 获取成功: \(response.ticket ?? "nil")")
|
||||
|
||||
// 保存认证信息到本地存储
|
||||
if let accessToken = state.accessToken,
|
||||
let ticket = response.ticket {
|
||||
UserInfoManager.saveCompleteAuthenticationData(
|
||||
accessToken: accessToken,
|
||||
ticket: ticket,
|
||||
uid: state.uid,
|
||||
userInfo: nil // LoginFeature 中没有用户信息,由具体的登录页面传递
|
||||
)
|
||||
// 更新 AccountModel 中的 ticket 并保存
|
||||
if let ticket = response.ticket {
|
||||
if var accountModel = state.accountModel {
|
||||
accountModel.ticket = ticket
|
||||
state.accountModel = accountModel
|
||||
|
||||
// 保存完整的 AccountModel
|
||||
UserInfoManager.saveAccountModel(accountModel)
|
||||
|
||||
// 发送 Ticket 获取成功通知,触发导航到主页面
|
||||
NotificationCenter.default.post(name: .ticketSuccess, object: nil)
|
||||
} else {
|
||||
print("❌ AccountModel 不存在,无法保存 ticket")
|
||||
state.ticketError = "内部错误:账户信息丢失"
|
||||
state.loginStep = .failed
|
||||
}
|
||||
} else {
|
||||
state.ticketError = "Ticket 为空"
|
||||
state.loginStep = .failed
|
||||
}
|
||||
|
||||
// TODO: 触发导航到主界面
|
||||
|
||||
} else {
|
||||
state.ticketError = response.errorMessage
|
||||
state.loginStep = .failed
|
||||
@@ -188,9 +201,7 @@ struct LoginFeature {
|
||||
state.isTicketLoading = false
|
||||
state.error = nil
|
||||
state.ticketError = nil
|
||||
state.accessToken = nil
|
||||
state.ticket = nil
|
||||
state.uid = nil // 清除 uid
|
||||
state.accountModel = nil // 清除 AccountModel
|
||||
state.loginStep = .initial
|
||||
|
||||
// 清除本地存储的认证信息
|
||||
@@ -201,6 +212,10 @@ struct LoginFeature {
|
||||
case .idLogin:
|
||||
// IDLogin动作由子feature处理
|
||||
return .none
|
||||
|
||||
case .emailLogin:
|
||||
// EmailLogin动作由子feature处理
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user