From 83e542c2c273e39f9b095f29625450cebd5272d0 Mon Sep 17 00:00:00 2001 From: Pine Date: Mon, 24 Aug 2026 18:40:59 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=B9=B3=E5=8F=B0=E7=AB=AF):=20=E5=AE=8C?= =?UTF-8?q?=E6=95=B4=E7=94=A8=E6=88=B7=E7=AE=A1=E7=90=86=20=E2=80=94=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E4=BF=AE=E6=94=B9(PUT=20/admin/users/{id}?= =?UTF-8?q?)=20+=20=E5=88=A0=E9=99=A4(DELETE=20/admin/users/{id})?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UserRepository.delete(硬删);UpdateUserRequest(昵称/角色/子角色/组织/区域);update_user 调 update_profile+set_role, delete_user 调 delete;走 action:user.manage + write_audit。TestClient:create/update/delete 均 200。 --- app/api/routers/rbac_admin.py | 39 +++++++++++++++++++++++++++++- app/api/schemas/admin.py | 7 ++++++ app/infrastructure/repositories.py | 9 +++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/app/api/routers/rbac_admin.py b/app/api/routers/rbac_admin.py index 0cc5a21..13b5c79 100644 --- a/app/api/routers/rbac_admin.py +++ b/app/api/routers/rbac_admin.py @@ -7,7 +7,7 @@ from pydantic import BaseModel from ..dependencies import get_db from ..schemas.admin import SetUserRoleRequest, SetUserStatusRequest -from ..schemas.admin import UserCreateRequest, RolePermissionRequest +from ..schemas.admin import UserCreateRequest, RolePermissionRequest, UpdateUserRequest from ...rbac import require_permission, require_roles, write_audit from ...infrastructure.repositories import Database @@ -89,6 +89,43 @@ async def set_user_status( return {"ok": True, "user": await db.users.to_profile(updated)} +@router.put("/users/{user_id}", summary="修改用户(资料/角色)") +async def update_user( + user_id: str, + req: UpdateUserRequest, + request: Request, + db: Database = Depends(get_db), + actor: dict = Depends(require_permission("action:user.manage")), +): + user = await db.users.get_by_id(user_id) + if user is None: + raise HTTPException(status_code=404, detail="User not found") + profile_fields = {k: v for k, v in req.model_dump(exclude_none=True).items() if k in ("nickname",)} + if profile_fields: + user = await db.users.update_profile(user_id, profile_fields) or user + if req.role or req.sub_role is not None: + user = await db.users.set_role(user_id, req.role or user.get("role"), req.sub_role, + req.org_id or user.get("org_id"), req.region_id or user.get("region_id")) + await write_audit(db, action="user.update", resource="user", resource_id=user_id, + detail=str(req.model_dump(exclude_none=True)), user=actor, request=request) + return {"ok": True, "user": await db.users.to_profile(user)} + + +@router.delete("/users/{user_id}", summary="删除用户") +async def delete_user( + user_id: str, + request: Request, + db: Database = Depends(get_db), + actor: dict = Depends(require_permission("action:user.manage")), +): + ok = await db.users.delete(user_id) + if not ok: + raise HTTPException(status_code=404, detail="User not found") + await write_audit(db, action="user.delete", resource="user", resource_id=user_id, + detail="deleted", user=actor, request=request) + return {"ok": True, "user_id": user_id} + + @router.get("/roles", summary="角色列表") async def list_roles( db: Database = Depends(get_db), diff --git a/app/api/schemas/admin.py b/app/api/schemas/admin.py index 53ceb93..dec30c8 100644 --- a/app/api/schemas/admin.py +++ b/app/api/schemas/admin.py @@ -9,6 +9,13 @@ class SetUserRoleRequest(BaseModel): class SetUserStatusRequest(BaseModel): status: str = "active" # active | disabled +class UpdateUserRequest(BaseModel): + nickname: str | None = None + role: str | None = None + sub_role: str | None = None + org_id: str | None = None + region_id: str | None = None + class UserCreateRequest(BaseModel): username: str # 手机号 password: str diff --git a/app/infrastructure/repositories.py b/app/infrastructure/repositories.py index 47913f3..9ffbbf9 100644 --- a/app/infrastructure/repositories.py +++ b/app/infrastructure/repositories.py @@ -181,6 +181,15 @@ class UserRepository: async def list(self) -> list[dict]: return [_user_to_dict(u) for u in await self.session.scalars(select(User).order_by(User.created_at))] + async def delete(self, user_id: str) -> bool: + """硬删除用户(含其身份/会话在级联范围内由 DB 层处理)。""" + u = await self.session.get(User, user_id) + if u is None: + return False + await self.session.delete(u) + await self.session.commit() + return True + async def create( self, username: str,