from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, func from pydantic import BaseModel from app.core.database import get_db from app.core.deps import get_current_user, require_roles from app.core.security import hash_password from app.models.user import User, Role from app.services.audit import log_audit router = APIRouter(prefix="/users", tags=["用户管理"]) class UserCreate(BaseModel): username: str password: str full_name: str | None = None email: str | None = None phone: str | None = None role: str = "visitor" class UserUpdate(BaseModel): full_name: str | None = None email: str | None = None phone: str | None = None role: str | None = None is_active: bool | None = None @router.get("") async def list_users( page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), db: AsyncSession = Depends(get_db), user: User = Depends(require_roles("admin", "energy_manager")), ): count_q = select(func.count(User.id)) total = (await db.execute(count_q)).scalar() result = await db.execute(select(User).offset((page - 1) * page_size).limit(page_size).order_by(User.id)) return { "total": total, "items": [{ "id": u.id, "username": u.username, "full_name": u.full_name, "email": u.email, "phone": u.phone, "role": u.role, "is_active": u.is_active, "last_login": str(u.last_login) if u.last_login else None, } for u in result.scalars().all()] } @router.post("") async def create_user(data: UserCreate, db: AsyncSession = Depends(get_db), user: User = Depends(require_roles("admin"))): existing = await db.execute(select(User).where(User.username == data.username)) if existing.scalar_one_or_none(): raise HTTPException(status_code=400, detail="用户名已存在") new_user = User( username=data.username, hashed_password=hash_password(data.password), full_name=data.full_name, email=data.email, phone=data.phone, role=data.role, ) db.add(new_user) await db.flush() await log_audit(db, user.id, "create", "user", detail=f"创建用户 {data.username}") return {"id": new_user.id, "username": new_user.username} @router.put("/{user_id}") async def update_user(user_id: int, data: UserUpdate, db: AsyncSession = Depends(get_db), admin: User = Depends(require_roles("admin"))): result = await db.execute(select(User).where(User.id == user_id)) target = result.scalar_one_or_none() if not target: raise HTTPException(status_code=404, detail="用户不存在") updates = data.model_dump(exclude_unset=True) for k, v in updates.items(): setattr(target, k, v) await log_audit(db, admin.id, "update", "user", detail=f"更新用户 {target.username}: {', '.join(updates.keys())}") return {"message": "已更新"} @router.get("/roles") async def list_roles(db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)): result = await db.execute(select(Role).order_by(Role.id)) return [{"id": r.id, "name": r.name, "display_name": r.display_name, "description": r.description} for r in result.scalars().all()]