Files
tianpu-ems/backend/app/api/v1/users.py
Du Wenbo f53a610a19 Initial commit: Tianpu Zero-Carbon EMS Platform
Full-stack energy management system for Tianpu Daxing campus.
- Frontend: React 19 + TypeScript + Ant Design + ECharts
- Backend: FastAPI + SQLAlchemy + PostgreSQL/TimescaleDB
- Features: PV monitoring, heat pump management, carbon tracking, alarms, reports

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 13:36:06 +08:00

80 lines
2.9 KiB
Python

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
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()
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="用户不存在")
for k, v in data.model_dump(exclude_unset=True).items():
setattr(target, k, v)
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()]