import uuid from datetime import datetime, timedelta import jwt from fastapi import APIRouter, Depends, HTTPException, Request from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from passlib.context import CryptContext from database import get_db from models import User, UserRole, Role, RolePermission, Permission from schemas import LoginRequest, TokenResponse, UserOut, RoleOut from config import settings router = APIRouter(prefix="/api/auth", tags=["auth"]) pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") async def get_permission_codes(db: AsyncSession, role_ids: list[uuid.UUID]) -> list[str]: result = await db.execute( select(Permission.code) .join(RolePermission) .where(RolePermission.role_id.in_(role_ids)) ) return list(set(result.scalars().all())) async def get_user_roles(db: AsyncSession, user_id: uuid.UUID) -> list[RoleOut]: result = await db.execute( select(Role).join(UserRole).where(UserRole.user_id == user_id) ) roles = result.scalars().all() out = [] for role in roles: rp_result = await db.execute( select(Permission.code) .join(RolePermission) .where(RolePermission.role_id == role.id) ) perms = list(rp_result.scalars().all()) out.append(RoleOut( id=role.id, name=role.name, code=role.code, description=role.description, is_system=role.is_system, data_scope=role.data_scope, permissions=perms, )) return out @router.post("/login", response_model=TokenResponse) async def login(req: LoginRequest, db: AsyncSession = Depends(get_db)): result = await db.execute(select(User).where(User.username == req.username)) user = result.scalar_one_or_none() if not user or not pwd_context.verify(req.password, user.password_hash): raise HTTPException(401, "用户名或密码错误") if user.status != "active": raise HTTPException(403, "账户已被禁用") roles = await get_user_roles(db, user.id) expire = datetime.utcnow() + timedelta(minutes=settings.JWT_EXPIRE_MINUTES) token = jwt.encode( {"sub": str(user.id), "username": user.username, "exp": expire}, settings.JWT_SECRET, algorithm=settings.JWT_ALGORITHM, ) return TokenResponse( access_token=token, user=UserOut( id=user.id, username=user.username, display_name=user.display_name, email=user.email, phone=user.phone, wecom_user_id=user.wecom_user_id, department_id=user.department_id, position=user.position, manager_id=user.manager_id, status=user.status, roles=roles, created_at=user.created_at, ), ) @router.get("/me", response_model=UserOut) async def get_me(request: Request, db: AsyncSession = Depends(get_db)): user_ctx = request.state.user result = await db.execute(select(User).where(User.id == user_ctx["id"])) user = result.scalar_one_or_none() if not user: raise HTTPException(404, "用户不存在") roles = await get_user_roles(db, user.id) return UserOut( id=user.id, username=user.username, display_name=user.display_name, email=user.email, phone=user.phone, wecom_user_id=user.wecom_user_id, department_id=user.department_id, position=user.position, manager_id=user.manager_id, status=user.status, roles=roles, created_at=user.created_at, ) def hash_password(password: str) -> str: return pwd_context.hash(password)