fix: harden user management operations

This commit is contained in:
yml2213
2026-08-13 15:07:33 +08:00
parent b07c5fdbf6
commit 090698e6b2
9 changed files with 286 additions and 33 deletions
+84 -8
View File
@@ -1,14 +1,18 @@
"""用户管理路由(超管)"""
from datetime import datetime, timezone
from uuid import uuid4
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
from ..database import get_db
from ..models import User, AuditLog
from ..models import Account, AuditLog, HuyaAccount, User
from ..security import hash_password
from ..permissions import ROLE_LABELS, PERMISSIONS, get_user_permissions
from ..schemas import UserCreate, UserUpdate, UserInfo
from ..deps import require_permission, get_current_user
from ..permissions import PERMISSIONS, get_user_permissions
from ..schemas import UserCreate, UserRename, UserUpdate, UserInfo
from ..deps import require_permission
router = APIRouter(prefix="/api/users", tags=["用户管理"])
@@ -18,7 +22,7 @@ def list_users(
db: Session = Depends(get_db),
_: User = Depends(require_permission("user:view")),
):
users = db.query(User).order_by(User.id).all()
users = db.query(User).filter(User.deleted_at.is_(None)).order_by(User.id).all()
result = []
for u in users:
result.append(UserInfo(
@@ -76,6 +80,8 @@ def update_user(
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
if user.deleted_at is not None:
raise HTTPException(status_code=404, detail="用户不存在")
if req.password:
user.password_hash = hash_password(req.password)
@@ -103,6 +109,51 @@ def update_user(
)
@router.patch("/{user_id}/username", response_model=UserInfo)
def rename_user(
user_id: int,
req: UserRename,
db: Session = Depends(get_db),
current: User = Depends(require_permission("user:rename")),
):
"""仅变更用户名,保留用户 ID 和全部关联业务数据。"""
user = db.query(User).filter(User.id == user_id, User.deleted_at.is_(None)).first()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
username = req.username.strip()
if len(username) < 2:
raise HTTPException(status_code=400, detail="用户名至少 2 个字符")
if username == user.username:
return UserInfo(
id=user.id, username=user.username, role=user.role,
is_active=user.is_active, remark=user.remark or "",
permissions=get_user_permissions(user), custom_permissions=user.custom_permissions,
)
if db.query(User.id).filter(User.username == username).first():
raise HTTPException(status_code=400, detail="用户名已存在")
old_username = user.username
user.username = username
db.add(AuditLog(
user_id=current.id,
username=current.username,
action="user:rename",
target=f"{old_username} -> {username}",
))
try:
db.commit()
except IntegrityError as exc:
db.rollback()
raise HTTPException(status_code=400, detail="用户名已存在") from exc
db.refresh(user)
return UserInfo(
id=user.id, username=user.username, role=user.role,
is_active=user.is_active, remark=user.remark or "",
permissions=get_user_permissions(user), custom_permissions=user.custom_permissions,
)
@router.delete("/{user_id}")
def delete_user(
user_id: int,
@@ -112,14 +163,39 @@ def delete_user(
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
if user.deleted_at is not None:
raise HTTPException(status_code=404, detail="用户不存在")
if user.role == "super_admin":
raise HTTPException(status_code=400, detail="不能删除超级管理员")
if user.id == current.id:
raise HTTPException(status_code=400, detail="不能删除当前登录用户")
# 保留任务与审计记录的创建者 ID,避免删除用户时破坏历史数据。
released_accounts = (
db.query(Account)
.filter(Account.assigned_to == user.id)
.update({Account.assigned_to: None}, synchronize_session=False)
)
released_huya_accounts = (
db.query(HuyaAccount)
.filter(HuyaAccount.assigned_to == user.id)
.update({HuyaAccount.assigned_to: None}, synchronize_session=False)
)
deleted_username = user.username
db.add(AuditLog(user_id=current.id, username=current.username,
action="user:delete", target=user.username))
db.delete(user)
action="user:delete", target=deleted_username))
user.is_active = False
user.deleted_at = datetime.now(timezone.utc)
user.deleted_username = deleted_username
# 释放用户名供新用户使用,同时不改变历史任务通过 user.id 保存的归属。
user.username = f"deleted-{user.id}-{uuid4().hex[:12]}"
db.commit()
return {"message": "已删除", "success": True}
return {
"message": "已删除",
"success": True,
"released_accounts": released_accounts,
"released_huya_accounts": released_huya_accounts,
}
@router.get("/permissions/list")