style: normalize python formatting gates

This commit is contained in:
Xiaoxia AI
2026-06-21 06:52:19 +08:00
parent 0809a079c5
commit bfbaddbd9a
129 changed files with 3024 additions and 2485 deletions
+14 -13
View File
@@ -1,25 +1,26 @@
"""认证相关 Use Cases"""
from packages.application.auth.register_user_use_case import (
RegisterUserUseCase,
RegisterUserRequest,
RegisterUserResponse,
VerifyEmailUseCase,
VerifyEmailRequest,
)
from packages.application.auth.login_use_case import (
LoginUseCase,
LoginRequest,
LoginResponse,
RefreshTokenUseCase,
RefreshTokenRequest,
LogoutUseCase,
LoginUseCase,
LogoutRequest,
LogoutUseCase,
RefreshTokenRequest,
RefreshTokenUseCase,
)
from packages.application.auth.password_reset_use_case import (
RequestPasswordResetUseCase,
RequestPasswordResetRequest,
ResetPasswordUseCase,
RequestPasswordResetUseCase,
ResetPasswordRequest,
ResetPasswordUseCase,
)
from packages.application.auth.register_user_use_case import (
RegisterUserRequest,
RegisterUserResponse,
RegisterUserUseCase,
VerifyEmailRequest,
VerifyEmailUseCase,
)
__all__ = [
+48 -46
View File
@@ -1,20 +1,18 @@
"""
用户登录 Use Case
"""
import secrets
from datetime import datetime, timezone, timedelta
from datetime import datetime, timedelta, timezone
from typing import Optional
from packages.adapters.redis import get_session_store
from packages.domain.auth import (
password_hasher,
jwt_service,
)
from packages.domain.auth import jwt_service, password_hasher
class LoginRequest:
"""登录请求"""
def __init__(
self,
email: str,
@@ -30,7 +28,7 @@ class LoginRequest:
class LoginResponse:
"""登录响应"""
def __init__(
self,
access_token: str,
@@ -52,18 +50,18 @@ class LoginResponse:
class LoginUseCase:
"""用户登录用例"""
def __init__(self, user_repository, session_store=None):
self.user_repository = user_repository
self.session_store = session_store or get_session_store()
def execute(self, request: LoginRequest) -> tuple[Optional[LoginResponse], Optional[str]]:
"""
执行登录
Args:
request: 登录请求
Returns:
(登录响应, 错误信息)
"""
@@ -71,31 +69,32 @@ class LoginUseCase:
# 1. 验证输入
if not request.email:
return None, "Email is required"
if not request.password:
return None, "Password is required"
# 2. 查找用户
user = self.user_repository.find_by_email(request.email)
if not user:
return None, "Invalid email or password"
# 3. 验证密码
if not password_hasher.verify_password(request.password, user.password_hash):
return None, "Invalid email or password"
# 4. 检查邮箱是否已验证(可选,根据需求决定是否强制)
# if not user.email_verified:
# return None, "Please verify your email first"
# 5. 创建 session 并生成 refresh_token
session_id = secrets.token_urlsafe(16)
refresh_token = secrets.token_urlsafe(32)
# 6. 生成基础 JWT token(包含 session_id,不包含 workspace)
# 这里使用一个特殊的 "user_token",不包含 workspace 和 role
# 用户选择工作空间后,会换取包含 workspace 的 access_token
import jwt as pyjwt
now = datetime.now(timezone.utc)
access_token_payload = {
"sub": user.id,
@@ -107,7 +106,7 @@ class LoginUseCase:
access_token = pyjwt.encode(
access_token_payload,
jwt_service.config.SECRET_KEY,
algorithm=jwt_service.config.ALGORITHM
algorithm=jwt_service.config.ALGORITHM,
)
self.session_store.save_session(
session_id=session_id,
@@ -117,74 +116,77 @@ class LoginUseCase:
ip_address=request.ip_address,
expires_in_seconds=30 * 24 * 3600, # 30 天
)
# 8. 更新最后登录信息
user.last_login_at = datetime.now(timezone.utc)
user.last_login_ip = request.ip_address
self.user_repository.save(user)
# 9. 返回响应
return LoginResponse(
access_token=access_token,
refresh_token=refresh_token,
user_id=user.id,
email=user.email,
username=user.username,
display_name=user.display_name,
expires_in=jwt_service.config.ACCESS_TOKEN_EXPIRE_MINUTES * 60,
), None
return (
LoginResponse(
access_token=access_token,
refresh_token=refresh_token,
user_id=user.id,
email=user.email,
username=user.username,
display_name=user.display_name,
expires_in=jwt_service.config.ACCESS_TOKEN_EXPIRE_MINUTES * 60,
),
None,
)
except Exception as e:
return None, f"Login failed: {str(e)}"
class RefreshTokenRequest:
"""刷新令牌请求"""
def __init__(self, refresh_token: str):
self.refresh_token = refresh_token
class RefreshTokenUseCase:
"""刷新令牌用例"""
def __init__(self, user_repository):
self.user_repository = user_repository
def execute(self, request: RefreshTokenRequest) -> tuple[Optional[LoginResponse], Optional[str]]:
"""
执行令牌刷新
Args:
request: 刷新请求
Returns:
(登录响应, 错误信息)
"""
try:
if not request.refresh_token:
return None, "Refresh token is required"
# 1. 查找 session(通过遍历所有 session)
# 注意:这里为了简化,先用遍历实现,生产环境应该用 refresh_token -> session_id 的索引
session = None
session_id = None
# 这是一个简化实现,实际应该在 SessionStore 中添加 find_by_refresh_token 方法
# 这里我们假设 refresh_token 就是 session_id(简化处理)
# 生产环境需要更复杂的映射
# 临时方案:从 Redis 获取(需要在 session_store 中添加方法)
# 现在先返回错误,提示需要实现
return None, "Refresh token implementation pending (需要完善 session_store)"
except Exception as e:
return None, f"Token refresh failed: {str(e)}"
class LogoutRequest:
"""登出请求"""
def __init__(
self,
user_id: str,
@@ -198,17 +200,17 @@ class LogoutRequest:
class LogoutUseCase:
"""用户登出用例"""
def __init__(self, session_store=None):
self.session_store = session_store or get_session_store()
def execute(self, request: LogoutRequest) -> tuple[bool, Optional[str]]:
"""
执行登出
Args:
request: 登出请求
Returns:
(是否成功, 错误信息)
"""
@@ -221,12 +223,12 @@ class LogoutUseCase:
# 删除当前 session
if not request.session_id:
return False, "Session ID is required"
success = self.session_store.delete_session(request.session_id)
if success:
return True, None
else:
return False, "Session not found"
except Exception as e:
return False, f"Logout failed: {str(e)}"
@@ -1,6 +1,7 @@
"""
密码重置 Use Case
"""
import secrets
from datetime import datetime, timedelta, timezone
from typing import Optional
@@ -11,14 +12,14 @@ from packages.domain.auth import password_hasher, password_validator
class RequestPasswordResetRequest:
"""请求密码重置"""
def __init__(self, email: str):
self.email = email.strip().lower()
class RequestPasswordResetUseCase:
"""请求密码重置用例"""
def __init__(
self,
user_repository,
@@ -30,41 +31,39 @@ class RequestPasswordResetUseCase:
self.base_url = base_url
self.token_expire_hours = token_expire_hours
self.email_service = email_service or get_email_service()
def execute(self, request: RequestPasswordResetRequest) -> tuple[bool, Optional[str]]:
"""
执行密码重置请求
Args:
request: 重置请求
Returns:
(是否成功, 错误信息)
"""
try:
if not request.email:
return False, "Email is required"
# 查找用户
user = self.user_repository.find_by_email(request.email)
# 安全考虑:即使用户不存在,也返回成功(避免暴露用户存在性)
if not user:
return True, None
# 生成重置令牌
reset_token = secrets.token_urlsafe(32)
reset_url = f"{self.base_url}/reset-password?token={reset_token}"
# 设置令牌和过期时间
user.password_reset_token = reset_token
user.password_reset_expires_at = datetime.now(timezone.utc) + timedelta(
hours=self.token_expire_hours
)
user.password_reset_expires_at = datetime.now(timezone.utc) + timedelta(hours=self.token_expire_hours)
# 保存用户
self.user_repository.save(user)
# 发送重置邮件
try:
success, error = self.email_service.send_password_reset_email(
@@ -72,22 +71,22 @@ class RequestPasswordResetUseCase:
username=user.username or user.display_name,
reset_url=reset_url,
)
if not success:
print(f"Failed to send password reset email: {error}")
# 不返回错误,避免暴露用户存在性
except Exception as e:
print(f"Email service error: {e}")
return True, None
except Exception as e:
return False, f"Password reset request failed: {str(e)}"
class ResetPasswordRequest:
"""重置密码请求"""
def __init__(self, token: str, new_password: str):
self.token = token
self.new_password = new_password
@@ -95,54 +94,54 @@ class ResetPasswordRequest:
class ResetPasswordUseCase:
"""重置密码用例"""
def __init__(self, user_repository):
self.user_repository = user_repository
def execute(self, request: ResetPasswordRequest) -> tuple[bool, Optional[str]]:
"""
执行密码重置
Args:
request: 重置请求
Returns:
(是否成功, 错误信息)
"""
try:
if not request.token:
return False, "Reset token is required"
if not request.new_password:
return False, "New password is required"
# 验证新密码强度
valid, error = password_validator.validate(request.new_password)
if not valid:
return False, error
# 查找用户
user = self.user_repository.find_by_password_reset_token(request.token)
if not user:
return False, "Invalid or expired reset token"
# 检查令牌是否过期
if user.password_reset_expires_at:
if datetime.now(timezone.utc) > user.password_reset_expires_at:
return False, "Reset token has expired"
# 哈希新密码
hashed_password = password_hasher.hash_password(request.new_password)
# 更新用户密码
user.password_hash = hashed_password
user.password_reset_token = None
user.password_reset_expires_at = None
# 保存用户
self.user_repository.save(user)
return True, None
except Exception as e:
return False, f"Password reset failed: {str(e)}"
@@ -1,19 +1,20 @@
"""
用户注册 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.entities import User
from packages.domain.auth import password_hasher, password_validator
from packages.domain.entities import User
class RegisterUserRequest:
"""注册请求"""
def __init__(
self,
email: str,
@@ -29,7 +30,7 @@ class RegisterUserRequest:
class RegisterUserResponse:
"""注册响应"""
def __init__(
self,
user_id: str,
@@ -47,7 +48,7 @@ class RegisterUserResponse:
class RegisterUserUseCase:
"""用户注册用例"""
def __init__(
self,
user_repository,
@@ -56,7 +57,7 @@ class RegisterUserUseCase:
):
"""
初始化注册用例
Args:
user_repository: 用户仓储
base_url: 应用基础 URL(用于生成验证链接)
@@ -64,14 +65,14 @@ class RegisterUserUseCase:
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:
(注册响应, 错误信息)
"""
@@ -79,34 +80,34 @@ class RegisterUserUseCase:
# 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,
@@ -118,14 +119,14 @@ class RegisterUserUseCase:
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,
@@ -133,68 +134,71 @@ class RegisterUserUseCase:
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
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)}"