""" FastAPI 依赖项 """ from typing import Optional from fastapi import Depends, HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials import asyncpg from core.database import get_db_pool from core.security import decode_access_token from models.user import User from services.user_service import UserService from logger.logging import get_logger logger = get_logger(__name__) # HTTP Bearer 认证方案 security = HTTPBearer() async def get_db() -> asyncpg.Connection: """获取数据库连接(依赖注入)""" pool = await get_db_pool() async with pool.acquire() as connection: yield connection async def get_current_user( credentials: HTTPAuthorizationCredentials = Depends(security), conn: asyncpg.Connection = Depends(get_db) ) -> User: """ 获取当前登录用户(必须登录) Args: credentials: HTTP Bearer 认证凭证 conn: 数据库连接 Returns: User: 当前登录的用户 Raises: HTTPException: 如果 token 无效或用户不存在 """ token = credentials.credentials # 解码 token payload = decode_access_token(token) if payload is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="无效的认证凭证", headers={"WWW-Authenticate": "Bearer"}, ) # 从 payload 中获取用户 ID user_id_str = payload.get("sub") if user_id_str is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="无效的认证凭证", headers={"WWW-Authenticate": "Bearer"}, ) try: user_id = int(user_id_str) except ValueError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="无效的认证凭证", headers={"WWW-Authenticate": "Bearer"}, ) # 从数据库获取用户 user = await UserService.get_user_by_id(conn, user_id) if user is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="用户不存在", headers={"WWW-Authenticate": "Bearer"}, ) # 检查用户是否激活 if not user.is_active: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="用户已被禁用", ) return user async def get_current_admin_user( current_user: User = Depends(get_current_user), ) -> User: """仅企业管理员(role=admin)可访问后台管理接口。""" if getattr(current_user, "role", None) != "admin": raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="需要企业管理员权限", ) return current_user async def get_current_user_optional( credentials: Optional[HTTPAuthorizationCredentials] = Depends(HTTPBearer(auto_error=False)), conn: asyncpg.Connection = Depends(get_db) ) -> Optional[User]: """ 获取当前登录用户(可选,不强制登录) Args: credentials: HTTP Bearer 认证凭证(可选) conn: 数据库连接 Returns: Optional[User]: 当前登录的用户,如果未登录则返回 None """ if credentials is None: return None try: token = credentials.credentials # 解码 token payload = decode_access_token(token) if payload is None: return None # 从 payload 中获取用户 ID user_id_str = payload.get("sub") if user_id_str is None: return None try: user_id = int(user_id_str) except ValueError: return None # 从数据库获取用户 user = await UserService.get_user_by_id(conn, user_id) if user is None or not user.is_active: return None return user except Exception as e: logger.warning(f"获取当前用户时发生错误: {e}") return None # 审核服务单例实例 _moderation_service: Optional["ModerationService"] = None async def get_moderation_service(): """ 获取或创建审核服务实例(依赖注入) 实现单例模式,复用 ModerationService 实例以提高性能。 行为: - 如果 MODERATION_ENABLED 为 False,返回 NoOpModerationService(空操作实现) - 如果 MODERATION_ENABLED 为 True,验证凭证并返回 ModerationService 实例 - 使用全局变量缓存服务实例,避免重复创建 Returns: ModerationService 或 NoOpModerationService: 审核服务实例 Raises: RuntimeError: 如果审核已启用但凭证配置缺失 Example: >>> @router.post("/chat/completion") >>> async def chat_completion( >>> moderation_service = Depends(get_moderation_service) >>> ): >>> result = await moderation_service.moderate_text(text) """ global _moderation_service # 导入配置和服务(延迟导入避免循环依赖) from core.config import get_settings from services.moderation_service import ModerationService, NoOpModerationService settings = get_settings() # 如果审核功能被禁用,返回空操作服务 if not settings.moderation_enabled: logger.info("审核功能已禁用 - 返回 NoOpModerationService") return NoOpModerationService() # 如果服务实例尚未创建,创建新实例 if _moderation_service is None: # 验证必需的凭证配置 if not settings.aliyun_access_key_id: error_msg = ( "审核服务配置错误: ALIYUN_ACCESS_KEY_ID 未设置。" "请在 .env 文件中配置 ALIYUN_ACCESS_KEY_ID," "或设置 MODERATION_ENABLED=false 禁用审核功能。" ) logger.error(error_msg) raise RuntimeError(error_msg) if not settings.aliyun_access_key_secret: error_msg = ( "审核服务配置错误: ALIYUN_ACCESS_KEY_SECRET 未设置。" "请在 .env 文件中配置 ALIYUN_ACCESS_KEY_SECRET," "或设置 MODERATION_ENABLED=false 禁用审核功能。" ) logger.error(error_msg) raise RuntimeError(error_msg) # 创建审核服务实例 _moderation_service = ModerationService( access_key_id=settings.aliyun_access_key_id, access_key_secret=settings.aliyun_access_key_secret, region=settings.aliyun_moderation_region, timeout=settings.moderation_timeout_seconds, service_type=settings.moderation_service_type, image_service_type=settings.image_moderation_service_type ) logger.info( f"审核服务实例已创建(增强版)- 区域: {settings.aliyun_moderation_region}, " f"文本服务类型: {settings.moderation_service_type}, " f"图片服务类型: {settings.image_moderation_service_type}, " f"超时: {settings.moderation_timeout_seconds}秒" ) return _moderation_service