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 import bcrypt 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"]) 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 bcrypt.checkpw(req.password.encode('utf-8'), user.password_hash.encode('utf-8')): 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 bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')