""" 用户注册 Use Case """ import secrets from datetime import datetime, timedelta, timezone from typing import Optional from uuid import uuid4 from packages.adapters.smtp import get_email_service from packages.domain.auth import password_hasher, password_validator from packages.domain.entities import User class RegisterUserRequest: """注册请求""" def __init__( self, email: str, password: str, username: str, display_name: str, ): self.email = email.strip().lower() self.password = password self.username = username.strip() self.display_name = display_name.strip() class RegisterUserResponse: """注册响应""" def __init__( self, user_id: str, email: str, username: str, display_name: str, email_verification_sent: bool, ): self.user_id = user_id self.email = email self.username = username self.display_name = display_name self.email_verification_sent = email_verification_sent class RegisterUserUseCase: """用户注册用例""" def __init__( self, user_repository, base_url: str = "http://localhost:3000", email_service=None, ): """ 初始化注册用例 Args: user_repository: 用户仓储 base_url: 应用基础 URL(用于生成验证链接) """ self.user_repository = user_repository self.base_url = base_url self.email_service = email_service or get_email_service() def execute(self, request: RegisterUserRequest) -> tuple[Optional[RegisterUserResponse], Optional[str]]: """ 执行注册 Args: request: 注册请求 Returns: (注册响应, 错误信息) """ try: # 1. 验证输入 if not request.email: return None, "Email is required" if not request.username: return None, "Username is required" if not request.display_name: return None, "Display name is required" # 2. 验证密码强度 valid, error = password_validator.validate(request.password) if not valid: return None, error # 3. 检查邮箱是否已存在 existing_user = self.user_repository.find_by_email(request.email) if existing_user: return None, "Email already registered" # 4. 检查用户名是否已存在 existing_username = self.user_repository.find_by_username(request.username) if existing_username: return None, "Username already taken" # 5. 哈希密码 hashed_password = password_hasher.hash_password(request.password) # 6. 生成邮箱验证令牌 verification_token = secrets.token_urlsafe(32) # 7. 创建用户 user = User( id=uuid4().hex, email=request.email, username=request.username, display_name=request.display_name, password_hash=hashed_password, email_verified=False, email_verification_token=verification_token, created_at=datetime.now(timezone.utc), ) # 8. 保存用户 self.user_repository.save(user) # 9. 发送验证邮件 verification_url = f"{self.base_url}/verify-email?token={verification_token}" email_sent = False try: success, error = self.email_service.send_verification_email( to_email=user.email, username=user.username, verification_url=verification_url, ) email_sent = success if not success: print(f"Failed to send verification email: {error}") except Exception as e: print(f"Email service error: {e}") # 10. 返回响应(即使邮件发送失败,用户也已创建) return ( RegisterUserResponse( user_id=user.id, email=user.email, username=user.username, display_name=user.display_name, email_verification_sent=email_sent, ), None, ) except Exception as e: return None, f"Registration failed: {str(e)}" class VerifyEmailRequest: """邮箱验证请求""" def __init__(self, token: str): self.token = token class VerifyEmailUseCase: """邮箱验证用例""" def __init__(self, user_repository): self.user_repository = user_repository def execute(self, request: VerifyEmailRequest) -> tuple[bool, Optional[str]]: """ 执行邮箱验证 Args: request: 验证请求 Returns: (是否成功, 错误信息) """ try: if not request.token: return False, "Verification token is required" # 查找用户 user = self.user_repository.find_by_verification_token(request.token) if not user: return False, "Invalid or expired verification token" # 检查是否已验证 if user.email_verified: return True, None # 已验证,返回成功 # 更新用户状态 user.email_verified = True user.email_verification_token = None # 清空令牌 self.user_repository.save(user) return True, None except Exception as e: return False, f"Email verification failed: {str(e)}"