145 lines
4.6 KiB
Python
145 lines
4.6 KiB
Python
"""管理员用户管理。"""
|
|
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.orm import Session
|
|
|
|
from database import get_db
|
|
from dependencies import require_permission
|
|
from models import Role, User, UserRole
|
|
from routers.auth import user_to_out
|
|
from schemas import (
|
|
ResetPasswordIn,
|
|
RoleOut,
|
|
UserCreate,
|
|
UserOut,
|
|
UserUpdate,
|
|
)
|
|
from seed.rbac import ROLES
|
|
from services.learning_profile import (
|
|
ensure_profile,
|
|
remove_unused_admin_profile,
|
|
reset_all_learning,
|
|
)
|
|
from services.security import hash_password
|
|
|
|
|
|
router = APIRouter(prefix="/users", tags=["users"], dependencies=[])
|
|
|
|
|
|
@router.post("/reset-learning")
|
|
def reset_learning(
|
|
db: Session = Depends(get_db),
|
|
_: User = Depends(require_permission("users:manage")),
|
|
):
|
|
"""清空所有用户的学习过程数据(保留用户/教材/题库/知识点图谱)。"""
|
|
reset_all_learning(db)
|
|
return {"ok": True, "message": "所有学习过程数据已清空,可从新的章节任务重新开始"}
|
|
|
|
|
|
def _role(db: Session, code: str) -> Role:
|
|
role = db.query(Role).filter(Role.code == code).first()
|
|
if role is None:
|
|
raise HTTPException(status_code=400, detail="角色不存在")
|
|
return role
|
|
|
|
|
|
@router.get("", response_model=list[UserOut])
|
|
def list_users(
|
|
role_code: str = "",
|
|
db: Session = Depends(get_db),
|
|
_: User = Depends(require_permission("users:manage")),
|
|
):
|
|
query = db.query(User)
|
|
if role_code:
|
|
query = (
|
|
query.join(UserRole, UserRole.user_id == User.id)
|
|
.join(Role, Role.id == UserRole.role_id)
|
|
.filter(Role.code == role_code)
|
|
)
|
|
users = query.order_by(User.id.asc()).all()
|
|
return [user_to_out(db, user) for user in users]
|
|
|
|
|
|
@router.get("/roles", response_model=list[RoleOut])
|
|
def list_roles(
|
|
db: Session = Depends(get_db),
|
|
_: User = Depends(require_permission("users:manage")),
|
|
):
|
|
return [RoleOut.model_validate(role) for role in db.query(Role).order_by(Role.id).all()]
|
|
|
|
|
|
@router.post("", response_model=UserOut)
|
|
def create_user(
|
|
payload: UserCreate,
|
|
db: Session = Depends(get_db),
|
|
_: User = Depends(require_permission("users:manage")),
|
|
):
|
|
if payload.role_code not in ROLES:
|
|
raise HTTPException(status_code=400, detail="不支持的角色")
|
|
if db.query(User).filter(User.username == payload.username).first():
|
|
raise HTTPException(status_code=400, detail="用户名已存在")
|
|
role = _role(db, payload.role_code)
|
|
user = User(
|
|
username=payload.username,
|
|
display_name=payload.display_name or payload.username,
|
|
password_hash=hash_password(payload.password),
|
|
active=True,
|
|
)
|
|
db.add(user)
|
|
db.flush()
|
|
db.add(UserRole(user_id=user.id, role_id=role.id))
|
|
db.commit()
|
|
if role.code != "admin":
|
|
ensure_profile(db, user.id)
|
|
else:
|
|
remove_unused_admin_profile(db, user.id)
|
|
db.refresh(user)
|
|
return user_to_out(db, user)
|
|
|
|
|
|
@router.put("/{user_id}", response_model=UserOut)
|
|
def update_user(
|
|
user_id: int,
|
|
payload: UserUpdate,
|
|
db: Session = Depends(get_db),
|
|
operator: User = Depends(require_permission("users:manage")),
|
|
):
|
|
user = db.get(User, user_id)
|
|
if user is None:
|
|
raise HTTPException(status_code=404, detail="用户不存在")
|
|
if user.id == operator.id and payload.active is False:
|
|
raise HTTPException(status_code=400, detail="不能停用自己的账号")
|
|
|
|
if payload.display_name is not None:
|
|
user.display_name = payload.display_name
|
|
if payload.active is not None:
|
|
user.active = payload.active
|
|
if payload.role_code is not None:
|
|
role = _role(db, payload.role_code)
|
|
if user.id == operator.id and role.code != "admin":
|
|
raise HTTPException(status_code=400, detail="不能移除自己的管理员角色")
|
|
db.query(UserRole).filter(UserRole.user_id == user.id).delete()
|
|
db.add(UserRole(user_id=user.id, role_id=role.id))
|
|
if role.code == "admin":
|
|
remove_unused_admin_profile(db, user.id)
|
|
else:
|
|
ensure_profile(db, user.id)
|
|
db.commit()
|
|
return user_to_out(db, user)
|
|
|
|
|
|
@router.post("/{user_id}/reset-password")
|
|
def reset_password(
|
|
user_id: int,
|
|
payload: ResetPasswordIn,
|
|
db: Session = Depends(get_db),
|
|
_: User = Depends(require_permission("users:manage")),
|
|
):
|
|
user = db.get(User, user_id)
|
|
if user is None:
|
|
raise HTTPException(status_code=404, detail="用户不存在")
|
|
user.password_hash = hash_password(payload.password)
|
|
db.commit()
|
|
return {"ok": True, "message": f"已重置 {user.username} 的密码。"}
|