"""依赖注入模块""" from typing import AsyncGenerator, Optional from sqlalchemy import select import redis.asyncio as redis from fastapi import Depends, HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from sqlalchemy.ext.asyncio import AsyncSession from app.core.database import AsyncSessionLocal from app.core.config import get_settings from app.models.user import User # JWT Bearer认证 security = HTTPBearer() async def get_db() -> AsyncGenerator[AsyncSession, None]: """ 获取数据库会话 """ async with AsyncSessionLocal() as session: try: yield session await session.commit() except Exception: await session.rollback() raise finally: await session.close() async def get_current_user( credentials: HTTPAuthorizationCredentials = Depends(security), db: AsyncSession = Depends(get_db), ) -> User: """ 获取当前用户(基于JWT) - 从 Authorization Bearer Token 中解析用户ID - 查询数据库返回完整的 User 对象 - 失败时抛出 401 未授权 """ from app.core.security import decode_token # 延迟导入避免循环依赖 if not credentials or not credentials.scheme or not credentials.credentials: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="未提供认证信息") if credentials.scheme.lower() != "bearer": raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="认证方式不支持") token = credentials.credentials try: payload = decode_token(token) user_id = int(payload.get("sub")) except Exception: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="无效的令牌") result = await db.execute( select(User).where(User.id == user_id, User.is_deleted == False) ) user = result.scalar_one_or_none() if not user or not user.is_active: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="用户不存在或已被禁用" ) return user async def require_admin(current_user: User = Depends(get_current_user)) -> User: """ 需要管理员权限 """ if getattr(current_user, "role", None) != "admin": raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="需要管理员权限") return current_user async def require_admin_or_manager(current_user: User = Depends(get_current_user)) -> User: """ 需要管理者或管理员权限 """ if getattr(current_user, "role", None) not in ("admin", "manager"): raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="需要管理者或管理员权限") return current_user async def get_optional_user( credentials: Optional[HTTPAuthorizationCredentials] = Depends(security), db: AsyncSession = Depends(get_db), ) -> Optional[User]: """ 获取可选的当前用户(不强制登录) """ if not credentials: return None try: return await get_current_user(credentials, db) except: return None async def get_current_active_user( current_user: User = Depends(get_current_user), ) -> User: """ 获取当前活跃用户 """ # TODO: 检查用户是否被禁用 return current_user async def verify_scrm_api_key( credentials: HTTPAuthorizationCredentials = Depends(security), ) -> bool: """ 验证 SCRM 系统 API Key 用于内部服务间调用认证,SCRM 系统通过固定 API Key 访问考陪练数据查询接口 请求头格式: Authorization: Bearer {SCRM_API_KEY} """ settings = get_settings() if not credentials or not credentials.credentials: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="未提供认证信息" ) if credentials.scheme.lower() != "bearer": raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="认证方式不支持,需要 Bearer Token" ) if credentials.credentials != settings.SCRM_API_KEY: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="无效的 API Key" ) return True # Redis 连接池 _redis_pool: Optional[redis.ConnectionPool] = None async def get_redis() -> AsyncGenerator[redis.Redis, None]: """ 获取 Redis 连接 """ global _redis_pool if _redis_pool is None: settings = get_settings() _redis_pool = redis.ConnectionPool.from_url( settings.REDIS_URL, encoding="utf-8", decode_responses=True ) client = redis.Redis(connection_pool=_redis_pool) try: yield client finally: await client.close()