forked from moyin/whale-town-front
- 创建AuthManager.gd:负责所有认证业务逻辑 - 用户登录/注册逻辑 - 表单验证逻辑 - 验证码管理逻辑 - 网络请求管理 - 创建ToastManager.gd:负责Toast消息管理 - Toast创建和显示 - 动画和生命周期管理 - 支持成功/失败样式 - 重构AuthScene.gd:纯视图层实现 - 只负责UI交互和显示 - 通过信号与业务层通信 - 移除所有业务逻辑代码 - 修复GDScript警告: - 未使用参数添加下划线前缀 - 修复变量名与基类方法冲突 - 修复EventSystem中的try语法错误 - 修复AuthManager中的方法名不匹配错误 符合docs中的架构要求,实现完全解耦
589 lines
18 KiB
GDScript
589 lines
18 KiB
GDScript
class_name AuthManager
|
||
|
||
# ============================================================================
|
||
# AuthManager.gd - 认证管理器
|
||
# ============================================================================
|
||
# 认证系统的业务逻辑管理器,负责处理所有认证相关的业务逻辑
|
||
#
|
||
# 核心职责:
|
||
# - 用户登录业务逻辑(密码登录 + 验证码登录)
|
||
# - 用户注册业务逻辑
|
||
# - 表单验证逻辑
|
||
# - 验证码管理逻辑
|
||
# - 网络请求管理
|
||
# - 响应处理和状态管理
|
||
#
|
||
# 使用方式:
|
||
# var auth_manager = AuthManager.new()
|
||
# auth_manager.login_success.connect(_on_login_success)
|
||
# auth_manager.execute_password_login(username, password)
|
||
#
|
||
# 注意事项:
|
||
# - 这是业务逻辑层,不包含任何UI相关代码
|
||
# - 通过信号与UI层通信
|
||
# - 所有验证逻辑都在这里实现
|
||
# ============================================================================
|
||
|
||
extends RefCounted
|
||
|
||
# ============ 信号定义 ============
|
||
|
||
# 登录成功信号
|
||
signal login_success(username: String)
|
||
|
||
# 登录失败信号
|
||
signal login_failed(message: String)
|
||
|
||
# 注册成功信号
|
||
signal register_success(message: String)
|
||
|
||
# 注册失败信号
|
||
signal register_failed(message: String)
|
||
|
||
# 验证码发送成功信号
|
||
signal verification_code_sent(message: String)
|
||
|
||
# 验证码发送失败信号
|
||
signal verification_code_failed(message: String)
|
||
|
||
# 表单验证失败信号
|
||
signal form_validation_failed(field: String, message: String)
|
||
|
||
# 网络状态变化信号
|
||
signal network_status_changed(is_connected: bool, message: String)
|
||
|
||
# 按钮状态变化信号
|
||
signal button_state_changed(button_name: String, is_loading: bool, text: String)
|
||
|
||
# Toast消息信号
|
||
signal show_toast_message(message: String, is_success: bool)
|
||
|
||
# ============ 枚举定义 ============
|
||
|
||
# 登录模式枚举
|
||
enum LoginMode {
|
||
PASSWORD, # 密码登录模式
|
||
VERIFICATION # 验证码登录模式
|
||
}
|
||
|
||
# ============ 成员变量 ============
|
||
|
||
# 登录状态
|
||
var current_login_mode: LoginMode = LoginMode.PASSWORD
|
||
var is_processing: bool = false
|
||
|
||
# 验证码管理
|
||
var verification_codes_sent: Dictionary = {}
|
||
var code_cooldown: float = 60.0
|
||
var current_email: String = ""
|
||
|
||
# 网络请求管理
|
||
var active_request_ids: Array = []
|
||
|
||
# ============ 生命周期方法 ============
|
||
|
||
# 初始化管理器
|
||
func _init():
|
||
print("AuthManager 初始化完成")
|
||
|
||
# 清理资源
|
||
func cleanup():
|
||
# 取消所有活动的网络请求
|
||
for request_id in active_request_ids:
|
||
NetworkManager.cancel_request(request_id)
|
||
active_request_ids.clear()
|
||
|
||
# ============ 登录相关方法 ============
|
||
|
||
# 执行密码登录
|
||
#
|
||
# 参数:
|
||
# username: String - 用户名/邮箱
|
||
# password: String - 密码
|
||
#
|
||
# 功能:
|
||
# - 验证输入参数
|
||
# - 发送登录请求
|
||
# - 处理响应结果
|
||
func execute_password_login(username: String, password: String):
|
||
if is_processing:
|
||
show_toast_message.emit("请等待当前操作完成", false)
|
||
return
|
||
|
||
# 验证输入
|
||
var validation_result = validate_login_inputs(username, password)
|
||
if not validation_result.valid:
|
||
form_validation_failed.emit(validation_result.field, validation_result.message)
|
||
return
|
||
|
||
# 设置处理状态
|
||
is_processing = true
|
||
button_state_changed.emit("main_btn", true, "登录中...")
|
||
show_toast_message.emit("正在验证登录信息...", true)
|
||
|
||
# 发送网络请求
|
||
var request_id = NetworkManager.login(username, password, _on_login_response)
|
||
if request_id != "":
|
||
active_request_ids.append(request_id)
|
||
else:
|
||
_reset_login_state()
|
||
show_toast_message.emit("网络请求失败", false)
|
||
|
||
# 执行验证码登录
|
||
#
|
||
# 参数:
|
||
# identifier: String - 用户标识符
|
||
# verification_code: String - 验证码
|
||
func execute_verification_login(identifier: String, verification_code: String):
|
||
if is_processing:
|
||
show_toast_message.emit("请等待当前操作完成", false)
|
||
return
|
||
|
||
# 验证输入
|
||
if identifier.is_empty():
|
||
form_validation_failed.emit("username", "请输入用户名/手机/邮箱")
|
||
return
|
||
|
||
if verification_code.is_empty():
|
||
form_validation_failed.emit("verification", "请输入验证码")
|
||
return
|
||
|
||
# 设置处理状态
|
||
is_processing = true
|
||
button_state_changed.emit("main_btn", true, "登录中...")
|
||
show_toast_message.emit("正在验证验证码...", true)
|
||
|
||
# 发送网络请求
|
||
var request_id = NetworkManager.verification_code_login(identifier, verification_code, _on_verification_login_response)
|
||
if request_id != "":
|
||
active_request_ids.append(request_id)
|
||
else:
|
||
_reset_login_state()
|
||
show_toast_message.emit("网络请求失败", false)
|
||
|
||
# 切换登录模式
|
||
func toggle_login_mode():
|
||
if current_login_mode == LoginMode.PASSWORD:
|
||
current_login_mode = LoginMode.VERIFICATION
|
||
else:
|
||
current_login_mode = LoginMode.PASSWORD
|
||
|
||
# 获取当前登录模式
|
||
func get_current_login_mode() -> LoginMode:
|
||
return current_login_mode
|
||
|
||
# ============ 注册相关方法 ============
|
||
|
||
# 执行用户注册
|
||
#
|
||
# 参数:
|
||
# username: String - 用户名
|
||
# email: String - 邮箱
|
||
# password: String - 密码
|
||
# confirm_password: String - 确认密码
|
||
# verification_code: String - 邮箱验证码
|
||
func execute_register(username: String, email: String, password: String, confirm_password: String, verification_code: String):
|
||
if is_processing:
|
||
show_toast_message.emit("请等待当前操作完成", false)
|
||
return
|
||
|
||
# 验证注册表单
|
||
var validation_result = validate_register_form(username, email, password, confirm_password, verification_code)
|
||
if not validation_result.valid:
|
||
form_validation_failed.emit(validation_result.field, validation_result.message)
|
||
return
|
||
|
||
# 设置处理状态
|
||
is_processing = true
|
||
button_state_changed.emit("register_btn", true, "注册中...")
|
||
show_toast_message.emit("正在创建账户...", true)
|
||
|
||
# 发送注册请求
|
||
var request_id = NetworkManager.register(username, password, username, email, verification_code, _on_register_response)
|
||
if request_id != "":
|
||
active_request_ids.append(request_id)
|
||
else:
|
||
_reset_register_state()
|
||
show_toast_message.emit("网络请求失败", false)
|
||
|
||
# ============ 验证码相关方法 ============
|
||
|
||
# 发送邮箱验证码
|
||
#
|
||
# 参数:
|
||
# email: String - 邮箱地址
|
||
func send_email_verification_code(email: String):
|
||
# 验证邮箱格式
|
||
var email_validation = validate_email(email)
|
||
if not email_validation.valid:
|
||
form_validation_failed.emit("email", email_validation.message)
|
||
return
|
||
|
||
# 检查冷却时间
|
||
if not _can_send_verification_code(email):
|
||
var remaining = get_remaining_cooldown_time(email)
|
||
show_toast_message.emit("该邮箱请等待 %d 秒后再次发送" % remaining, false)
|
||
return
|
||
|
||
# 记录发送状态
|
||
_record_verification_code_sent(email)
|
||
|
||
# 发送请求
|
||
var request_id = NetworkManager.send_email_verification(email, _on_send_code_response)
|
||
if request_id != "":
|
||
active_request_ids.append(request_id)
|
||
else:
|
||
_reset_verification_code_state(email)
|
||
show_toast_message.emit("网络请求失败", false)
|
||
|
||
# 发送登录验证码
|
||
#
|
||
# 参数:
|
||
# identifier: String - 用户标识符
|
||
func send_login_verification_code(identifier: String):
|
||
if identifier.is_empty():
|
||
form_validation_failed.emit("username", "请先输入用户名/手机/邮箱")
|
||
return
|
||
|
||
button_state_changed.emit("get_code_btn", true, "发送中...")
|
||
show_toast_message.emit("正在发送登录验证码...", true)
|
||
|
||
var request_id = NetworkManager.send_login_verification_code(identifier, _on_send_login_code_response)
|
||
if request_id != "":
|
||
active_request_ids.append(request_id)
|
||
else:
|
||
button_state_changed.emit("get_code_btn", false, "获取验证码")
|
||
show_toast_message.emit("网络请求失败", false)
|
||
|
||
# 发送密码重置验证码
|
||
#
|
||
# 参数:
|
||
# identifier: String - 用户标识符
|
||
func send_password_reset_code(identifier: String):
|
||
if identifier.is_empty():
|
||
show_toast_message.emit("请先输入邮箱或手机号", false)
|
||
return
|
||
|
||
if not _is_valid_identifier(identifier):
|
||
show_toast_message.emit("请输入有效的邮箱或手机号", false)
|
||
return
|
||
|
||
button_state_changed.emit("forgot_password_btn", true, "发送中...")
|
||
show_toast_message.emit("正在发送密码重置验证码...", true)
|
||
|
||
var request_id = NetworkManager.forgot_password(identifier, _on_forgot_password_response)
|
||
if request_id != "":
|
||
active_request_ids.append(request_id)
|
||
else:
|
||
button_state_changed.emit("forgot_password_btn", false, "忘记密码")
|
||
show_toast_message.emit("网络请求失败", false)
|
||
|
||
# ============ 验证方法 ============
|
||
|
||
# 验证登录输入
|
||
func validate_login_inputs(username: String, password: String) -> Dictionary:
|
||
var result = {"valid": false, "field": "", "message": ""}
|
||
|
||
if username.is_empty():
|
||
result.field = "username"
|
||
result.message = "用户名不能为空"
|
||
return result
|
||
|
||
if password.is_empty():
|
||
result.field = "password"
|
||
result.message = "密码不能为空"
|
||
return result
|
||
|
||
result.valid = true
|
||
return result
|
||
|
||
# 验证注册表单
|
||
func validate_register_form(username: String, email: String, password: String, confirm_password: String, verification_code: String) -> Dictionary:
|
||
var result = {"valid": false, "field": "", "message": ""}
|
||
|
||
# 验证用户名
|
||
var username_validation = validate_username(username)
|
||
if not username_validation.valid:
|
||
result.field = "username"
|
||
result.message = username_validation.message
|
||
return result
|
||
|
||
# 验证邮箱
|
||
var email_validation = validate_email(email)
|
||
if not email_validation.valid:
|
||
result.field = "email"
|
||
result.message = email_validation.message
|
||
return result
|
||
|
||
# 验证密码
|
||
var password_validation = validate_password(password)
|
||
if not password_validation.valid:
|
||
result.field = "password"
|
||
result.message = password_validation.message
|
||
return result
|
||
|
||
# 验证确认密码
|
||
var confirm_validation = validate_confirm_password(password, confirm_password)
|
||
if not confirm_validation.valid:
|
||
result.field = "confirm"
|
||
result.message = confirm_validation.message
|
||
return result
|
||
|
||
# 验证验证码
|
||
var code_validation = validate_verification_code(verification_code)
|
||
if not code_validation.valid:
|
||
result.field = "verification"
|
||
result.message = code_validation.message
|
||
return result
|
||
|
||
# 检查是否已发送验证码
|
||
if not _has_sent_verification_code(email):
|
||
result.field = "verification"
|
||
result.message = "请先获取邮箱验证码"
|
||
return result
|
||
|
||
result.valid = true
|
||
return result
|
||
|
||
# 验证用户名
|
||
func validate_username(username: String) -> Dictionary:
|
||
var result = {"valid": false, "message": ""}
|
||
|
||
if username.is_empty():
|
||
result.message = "用户名不能为空"
|
||
return result
|
||
|
||
if not StringUtils.is_valid_username(username):
|
||
if username.length() > 50:
|
||
result.message = "用户名长度不能超过50字符"
|
||
else:
|
||
result.message = "用户名只能包含字母、数字和下划线"
|
||
return result
|
||
|
||
result.valid = true
|
||
return result
|
||
|
||
# 验证邮箱
|
||
func validate_email(email: String) -> Dictionary:
|
||
var result = {"valid": false, "message": ""}
|
||
|
||
if email.is_empty():
|
||
result.message = "邮箱不能为空"
|
||
return result
|
||
|
||
if not StringUtils.is_valid_email(email):
|
||
result.message = "请输入有效的邮箱地址"
|
||
return result
|
||
|
||
result.valid = true
|
||
return result
|
||
|
||
# 验证密码
|
||
func validate_password(password: String) -> Dictionary:
|
||
return StringUtils.validate_password_strength(password)
|
||
|
||
# 验证确认密码
|
||
func validate_confirm_password(password: String, confirm: String) -> Dictionary:
|
||
var result = {"valid": false, "message": ""}
|
||
|
||
if confirm.is_empty():
|
||
result.message = "确认密码不能为空"
|
||
return result
|
||
|
||
if password != confirm:
|
||
result.message = "两次输入的密码不一致"
|
||
return result
|
||
|
||
result.valid = true
|
||
return result
|
||
|
||
# 验证验证码
|
||
func validate_verification_code(code: String) -> Dictionary:
|
||
var result = {"valid": false, "message": ""}
|
||
|
||
if code.is_empty():
|
||
result.message = "验证码不能为空"
|
||
return result
|
||
|
||
if code.length() != 6:
|
||
result.message = "验证码必须是6位数字"
|
||
return result
|
||
|
||
for i in range(code.length()):
|
||
var character = code[i]
|
||
if not (character >= '0' and character <= '9'):
|
||
result.message = "验证码必须是6位数字"
|
||
return result
|
||
|
||
result.valid = true
|
||
return result
|
||
|
||
# ============ 网络响应处理 ============
|
||
|
||
# 处理登录响应
|
||
func _on_login_response(success: bool, data: Dictionary, error_info: Dictionary):
|
||
_reset_login_state()
|
||
|
||
var result = ResponseHandler.handle_login_response(success, data, error_info)
|
||
|
||
if result.should_show_toast:
|
||
show_toast_message.emit(result.message, result.success)
|
||
|
||
if result.success:
|
||
var username = ""
|
||
if data.has("data") and data.data.has("user") and data.data.user.has("username"):
|
||
username = data.data.user.username
|
||
|
||
# 延迟发送登录成功信号
|
||
await Engine.get_main_loop().create_timer(1.0).timeout
|
||
login_success.emit(username)
|
||
else:
|
||
login_failed.emit(result.message)
|
||
|
||
# 处理验证码登录响应
|
||
func _on_verification_login_response(success: bool, data: Dictionary, error_info: Dictionary):
|
||
_reset_login_state()
|
||
|
||
var result = ResponseHandler.handle_verification_code_login_response(success, data, error_info)
|
||
|
||
if result.should_show_toast:
|
||
show_toast_message.emit(result.message, result.success)
|
||
|
||
if result.success:
|
||
var username = ""
|
||
if data.has("data") and data.data.has("user") and data.data.user.has("username"):
|
||
username = data.data.user.username
|
||
|
||
await Engine.get_main_loop().create_timer(1.0).timeout
|
||
login_success.emit(username)
|
||
else:
|
||
login_failed.emit(result.message)
|
||
|
||
# 处理注册响应
|
||
func _on_register_response(success: bool, data: Dictionary, error_info: Dictionary):
|
||
_reset_register_state()
|
||
|
||
var result = ResponseHandler.handle_register_response(success, data, error_info)
|
||
|
||
if result.should_show_toast:
|
||
show_toast_message.emit(result.message, result.success)
|
||
|
||
if result.success:
|
||
register_success.emit(result.message)
|
||
else:
|
||
register_failed.emit(result.message)
|
||
|
||
# 处理发送验证码响应
|
||
func _on_send_code_response(success: bool, data: Dictionary, error_info: Dictionary):
|
||
var result = ResponseHandler.handle_send_verification_code_response(success, data, error_info)
|
||
|
||
if result.should_show_toast:
|
||
show_toast_message.emit(result.message, result.success)
|
||
|
||
if result.success:
|
||
verification_code_sent.emit(result.message)
|
||
else:
|
||
verification_code_failed.emit(result.message)
|
||
_reset_verification_code_state(current_email)
|
||
|
||
# 处理发送登录验证码响应
|
||
func _on_send_login_code_response(success: bool, data: Dictionary, error_info: Dictionary):
|
||
button_state_changed.emit("get_code_btn", false, "获取验证码")
|
||
|
||
var result = ResponseHandler.handle_send_login_code_response(success, data, error_info)
|
||
|
||
if result.should_show_toast:
|
||
show_toast_message.emit(result.message, result.success)
|
||
|
||
# 处理忘记密码响应
|
||
func _on_forgot_password_response(success: bool, data: Dictionary, error_info: Dictionary):
|
||
button_state_changed.emit("forgot_password_btn", false, "忘记密码")
|
||
|
||
var result = ResponseHandler.handle_send_login_code_response(success, data, error_info)
|
||
|
||
if result.should_show_toast:
|
||
show_toast_message.emit(result.message, result.success)
|
||
|
||
# ============ 网络测试 ============
|
||
|
||
# 测试网络连接
|
||
func test_network_connection():
|
||
var request_id = NetworkManager.get_app_status(_on_network_test_response)
|
||
if request_id != "":
|
||
active_request_ids.append(request_id)
|
||
|
||
# 处理网络测试响应
|
||
func _on_network_test_response(success: bool, data: Dictionary, error_info: Dictionary):
|
||
var result = ResponseHandler.handle_network_test_response(success, data, error_info)
|
||
network_status_changed.emit(result.success, result.message)
|
||
|
||
# ============ 私有辅助方法 ============
|
||
|
||
# 重置登录状态
|
||
func _reset_login_state():
|
||
is_processing = false
|
||
button_state_changed.emit("main_btn", false, "进入小镇")
|
||
|
||
# 重置注册状态
|
||
func _reset_register_state():
|
||
is_processing = false
|
||
button_state_changed.emit("register_btn", false, "注册")
|
||
|
||
# 检查是否可以发送验证码
|
||
func _can_send_verification_code(email: String) -> bool:
|
||
if not verification_codes_sent.has(email):
|
||
return true
|
||
|
||
var email_data = verification_codes_sent[email]
|
||
if not email_data.sent:
|
||
return true
|
||
|
||
var current_time = Time.get_time_dict_from_system()
|
||
var current_timestamp = current_time.hour * 3600 + current_time.minute * 60 + current_time.second
|
||
|
||
return (current_timestamp - email_data.time) >= code_cooldown
|
||
|
||
# 获取剩余冷却时间
|
||
func get_remaining_cooldown_time(email: String) -> int:
|
||
if not verification_codes_sent.has(email):
|
||
return 0
|
||
|
||
var email_data = verification_codes_sent[email]
|
||
var current_time = Time.get_time_dict_from_system()
|
||
var current_timestamp = current_time.hour * 3600 + current_time.minute * 60 + current_time.second
|
||
|
||
return int(code_cooldown - (current_timestamp - email_data.time))
|
||
|
||
# 记录验证码发送状态
|
||
func _record_verification_code_sent(email: String):
|
||
var current_time = Time.get_time_dict_from_system()
|
||
var current_timestamp = current_time.hour * 3600 + current_time.minute * 60 + current_time.second
|
||
|
||
if not verification_codes_sent.has(email):
|
||
verification_codes_sent[email] = {}
|
||
|
||
verification_codes_sent[email].sent = true
|
||
verification_codes_sent[email].time = current_timestamp
|
||
current_email = email
|
||
|
||
# 重置验证码状态
|
||
func _reset_verification_code_state(email: String):
|
||
if verification_codes_sent.has(email):
|
||
verification_codes_sent[email].sent = false
|
||
|
||
# 检查是否已发送验证码
|
||
func _has_sent_verification_code(email: String) -> bool:
|
||
if not verification_codes_sent.has(email):
|
||
return false
|
||
|
||
return verification_codes_sent[email].get("sent", false)
|
||
|
||
# 验证标识符格式
|
||
func _is_valid_identifier(identifier: String) -> bool:
|
||
return StringUtils.is_valid_email(identifier) or _is_valid_phone(identifier)
|
||
|
||
# 验证手机号格式
|
||
func _is_valid_phone(phone: String) -> bool:
|
||
var regex = RegEx.new()
|
||
regex.compile("^\\+?[1-9]\\d{1,14}$")
|
||
return regex.search(phone) != null |