PayrollManagementSystem/backend/app/api/v1/system/users.py

147 lines
5.1 KiB
Python
Raw Normal View History

"""用户管理接口(需求 2.5.1:用户账号管理)。
全部要求管理员权限 —— 按需求「系统管理员(全部权限)、普通用户(业务操作)」,
用户管理属管理员专属。
删除、禁用、改角色三处都有**自我保护**:不允许对当前登录账号执行,
否则管理员误操作会立刻失去全部管理权限且无法自行恢复。
全部写操作经 `RequestMetaDep` 带入请求元信息后由 service 层写入操作日志
(成功与失败都记),操作人为当前登录管理员。
"""
from typing import Annotated
from fastapi import APIRouter, Path, Query
from app.api.deps import AdminUser, DbSession, RequestMetaDep
from app.core.response import ApiResponse, PageResult, ok
from app.schemas.role import RoleOut
from app.schemas.user import (
PasswordResetIn,
UserCreateIn,
UserOut,
UserQuery,
UserStatusIn,
UserUpdateIn,
)
from app.services import user_service
router = APIRouter(prefix="/users", tags=["系统管理 · 用户"])
@router.get("", response_model=ApiResponse[PageResult[UserOut]], summary="用户列表")
def list_users(
session: DbSession,
_: AdminUser,
query: Annotated[UserQuery, Query()],
) -> ApiResponse[PageResult[UserOut]]:
"""分页查询用户,支持按用户名/姓名模糊、状态与角色筛选。"""
return ok(user_service.list_users(session, query))
@router.get("/{user_id}", response_model=ApiResponse[UserOut], summary="用户详情")
def get_user(
session: DbSession,
_: AdminUser,
user_id: Annotated[int, Path(ge=1)],
) -> ApiResponse[UserOut]:
"""查询用户详情。"""
return ok(user_service.get_user(session, user_id))
@router.post("", response_model=ApiResponse[UserOut], summary="新增用户")
def create_user(
session: DbSession,
current_user: AdminUser,
payload: UserCreateIn,
meta: RequestMetaDep,
) -> ApiResponse[UserOut]:
"""新增用户。用户名须唯一,至少指定一个角色。"""
return ok(
user_service.create_user(session, payload, operator=current_user, meta=meta),
message="用户已创建",
)
@router.put("/{user_id}", response_model=ApiResponse[UserOut], summary="编辑用户")
def update_user(
session: DbSession,
current_user: AdminUser,
user_id: Annotated[int, Path(ge=1)],
payload: UserUpdateIn,
meta: RequestMetaDep,
) -> ApiResponse[UserOut]:
"""编辑用户的基本信息与角色。
**不含用户名与密码**:用户名是审计追溯的锚点;密码走专门的重置接口。
"""
return ok(
user_service.update_user(session, user_id, payload, operator=current_user, meta=meta),
message="已保存",
)
@router.delete("/{user_id}", response_model=ApiResponse[None], summary="删除用户")
def delete_user(
session: DbSession,
current_user: AdminUser,
user_id: Annotated[int, Path(ge=1)],
meta: RequestMetaDep,
) -> ApiResponse[None]:
"""删除用户。
物理删除。历史操作日志通过 `user_id` 置空 + 冗余用户名保留可追溯性。
业务上更推荐用「禁用」代替删除 —— 禁用保留完整审计线索。
"""
user_service.delete_user(session, user_id, operator=current_user, meta=meta)
return ok(message="用户已删除")
@router.put("/{user_id}/password", response_model=ApiResponse[None], summary="重置密码")
def reset_password(
session: DbSession,
current_user: AdminUser,
user_id: Annotated[int, Path(ge=1)],
payload: PasswordResetIn,
meta: RequestMetaDep,
) -> ApiResponse[None]:
"""重置用户密码。
会同时提升该用户的令牌版本,使其**已签发的令牌立即失效** ——
否则改密后旧令牌在有效期内仍可用。
"""
user_service.reset_password(
session, user_id, payload.new_password, operator=current_user, meta=meta
)
return ok(message="密码已重置,该用户需重新登录")
@router.put("/{user_id}/status", response_model=ApiResponse[UserOut], summary="启用/禁用用户")
def set_status(
session: DbSession,
current_user: AdminUser,
user_id: Annotated[int, Path(ge=1)],
payload: UserStatusIn,
meta: RequestMetaDep,
) -> ApiResponse[UserOut]:
"""启用或禁用用户。
禁用会提升令牌版本使已签发令牌立即失效,否则被禁用者仍可凭旧令牌
继续访问直到过期(最长 2 小时)。
"""
result = user_service.set_status(
session, user_id, payload.status, operator=current_user, meta=meta
)
return ok(result, message="已启用" if payload.status == 1 else "已禁用")
@router.get("/meta/roles", response_model=ApiResponse[list[RoleOut]], summary="角色下拉选项")
def list_roles(session: DbSession, _: AdminUser) -> ApiResponse[list[RoleOut]]:
"""取全部启用角色,供用户表单的角色选择使用。
路径放在 `/users/meta/roles` 而非 `/roles`,是为了避免与
`/{user_id}` 的路由匹配冲突(`roles` 会被当成 user_id 解析而报 422)。
"""
return ok(user_service.list_roles(session))