financial_system/financial_system/api/routers/auth.py
2026-06-18 13:21:20 +08:00

267 lines
9.2 KiB
Python

from __future__ import annotations
from fastapi import APIRouter, Depends, File, HTTPException, Request, UploadFile
from ...core.logger import AppLogger
from ...core.permissions import PERMISSION_USER_CREATE, PERMISSION_USER_LIST
from ...database.orm import UserORM
from ...services import AuthService, OperationLogService
from ..dependencies import get_auth_service, get_current_user, get_operation_log_service, require_permission
from ..request_meta import operation_request_meta
from ..schemas.auth import (
ChangePasswordRequest,
ChangePasswordResponse,
CreateUserRequest,
LoginRequest,
LoginResponse,
UpdateProfileRequest,
UserProfileDTO,
UserResponse,
)
router = APIRouter(prefix="/api/auth", tags=["auth"])
logger = AppLogger.get_logger(__name__)
@router.post("/login", response_model=LoginResponse)
def login(
http_request: Request,
request: LoginRequest,
service: AuthService = Depends(get_auth_service),
operation_logs: OperationLogService = Depends(get_operation_log_service),
) -> LoginResponse:
"""登录成功后返回 token、可见菜单和可执行操作权限。"""
try:
session = service.login(request.username, request.password)
except ValueError as exc:
operation_logs.record(
actor=None,
module="auth",
action="login",
target_type="user",
target_id=request.username,
status="failed",
detail=str(exc),
**operation_request_meta(http_request),
)
logger.error("登录接口失败 username=%s reason=%s", request.username, exc)
raise HTTPException(status_code=401, detail=str(exc)) from exc
operation_logs.record(
actor=session.user,
module="auth",
action="login",
target_type="user",
target_id=str(session.user.id),
status="success",
detail="用户登录成功",
**operation_request_meta(http_request),
)
return LoginResponse.from_session(session)
@router.get("/me", response_model=UserProfileDTO)
def me(current_user: UserORM = Depends(get_current_user)) -> UserProfileDTO:
"""前端刷新页面时用它恢复当前用户权限上下文。"""
return UserProfileDTO.from_orm_model(current_user)
@router.put("/me", response_model=UserProfileDTO)
def update_me(
http_request: Request,
request: UpdateProfileRequest,
current_user: UserORM = Depends(get_current_user),
service: AuthService = Depends(get_auth_service),
operation_logs: OperationLogService = Depends(get_operation_log_service),
) -> UserProfileDTO:
"""当前登录用户修改个人基础信息。"""
try:
user = service.update_profile(
current_user.id,
display_name=request.display_name,
email=request.email,
phone=request.phone,
department=request.department,
position_title=request.position_title,
)
except ValueError as exc:
operation_logs.record(
actor=current_user,
module="auth",
action="profile.update",
target_type="user",
target_id=str(current_user.id),
status="failed",
detail=str(exc),
**operation_request_meta(http_request),
)
logger.error("修改个人资料接口失败 user_id=%s reason=%s", current_user.id, exc)
raise HTTPException(status_code=400, detail=str(exc)) from exc
operation_logs.record(
actor=user,
module="auth",
action="profile.update",
target_type="user",
target_id=str(user.id),
status="success",
detail="修改个人资料成功",
**operation_request_meta(http_request),
)
return UserProfileDTO.from_orm_model(user)
@router.put("/me/password", response_model=ChangePasswordResponse)
def change_my_password(
http_request: Request,
request: ChangePasswordRequest,
current_user: UserORM = Depends(get_current_user),
service: AuthService = Depends(get_auth_service),
operation_logs: OperationLogService = Depends(get_operation_log_service),
) -> ChangePasswordResponse:
"""当前登录用户修改自己的登录密码。"""
try:
service.change_password(
current_user.id,
old_password=request.old_password,
new_password=request.new_password,
confirm_password=request.confirm_password,
)
except ValueError as exc:
operation_logs.record(
actor=current_user,
module="auth",
action="password.change",
target_type="user",
target_id=str(current_user.id),
status="failed",
detail=str(exc),
**operation_request_meta(http_request),
)
logger.error("修改密码接口失败 user_id=%s reason=%s", current_user.id, exc)
raise HTTPException(status_code=400, detail=str(exc)) from exc
operation_logs.record(
actor=current_user,
module="auth",
action="password.change",
target_type="user",
target_id=str(current_user.id),
status="success",
detail="修改密码成功",
**operation_request_meta(http_request),
)
return ChangePasswordResponse(message="密码修改成功")
@router.post("/me/avatar", response_model=UserProfileDTO)
def update_my_avatar(
http_request: Request,
file: UploadFile = File(...),
current_user: UserORM = Depends(get_current_user),
service: AuthService = Depends(get_auth_service),
operation_logs: OperationLogService = Depends(get_operation_log_service),
) -> UserProfileDTO:
"""当前登录用户上传并修改头像。"""
try:
user = service.update_avatar(
current_user.id,
filename=file.filename or "avatar.png",
stream=file.file,
)
except ValueError as exc:
operation_logs.record(
actor=current_user,
module="auth",
action="avatar.update",
target_type="user",
target_id=str(current_user.id),
status="failed",
detail=str(exc),
**operation_request_meta(http_request),
)
logger.error("修改头像接口失败 user_id=%s filename=%s reason=%s", current_user.id, file.filename, exc)
raise HTTPException(status_code=400, detail=str(exc)) from exc
operation_logs.record(
actor=user,
module="auth",
action="avatar.update",
target_type="user",
target_id=str(user.id),
status="success",
detail=f"修改头像成功 filename={file.filename or 'avatar.png'}",
**operation_request_meta(http_request),
)
return UserProfileDTO.from_orm_model(user)
@router.post(
"/users",
response_model=UserResponse,
)
def create_user(
http_request: Request,
request: CreateUserRequest,
current_user: UserORM = Depends(require_permission(PERMISSION_USER_CREATE)),
service: AuthService = Depends(get_auth_service),
operation_logs: OperationLogService = Depends(get_operation_log_service),
) -> UserResponse:
"""只有超级用户可以创建用户,角色决定菜单和操作权限。"""
try:
user = service.create_user(
username=request.username,
password=request.password,
role=request.role,
display_name=request.display_name,
email=request.email,
phone=request.phone,
department=request.department,
position_title=request.position_title,
)
except ValueError as exc:
operation_logs.record(
actor=current_user,
module="system",
action="user.create",
target_type="user",
target_id=request.username,
status="failed",
detail=str(exc),
**operation_request_meta(http_request),
)
logger.error("创建用户接口失败 username=%s role=%s reason=%s", request.username, request.role, exc)
raise HTTPException(status_code=400, detail=str(exc)) from exc
operation_logs.record(
actor=current_user,
module="system",
action="user.create",
target_type="user",
target_id=str(user.id),
status="success",
detail=f"创建用户成功 username={user.username} role={user.role}",
**operation_request_meta(http_request),
)
return UserResponse.from_orm_model(user)
@router.get(
"/users",
response_model=list[UserResponse],
)
def list_users(
http_request: Request,
current_user: UserORM = Depends(require_permission(PERMISSION_USER_LIST)),
service: AuthService = Depends(get_auth_service),
operation_logs: OperationLogService = Depends(get_operation_log_service),
) -> list[UserResponse]:
"""用户管理菜单使用的用户列表。"""
users = service.list_users()
operation_logs.record(
actor=current_user,
module="system",
action="user.list",
target_type="user",
target_id="",
status="success",
detail=f"查询用户列表 count={len(users)}",
**operation_request_meta(http_request),
)
return [UserResponse.from_orm_model(user) for user in users]