# _*_ coding : UTF-8 _*_ # @Time : 2025/01/27 21:40 # @UpdateTime : 2025/01/27 21:40 # @Author : sonder # @File : log.py # @Software : PyCharm # @Comment : 本程序 from datetime import datetime from typing import Optional from fastapi import APIRouter, Depends, Path, Query, Request from fastapi.encoders import jsonable_encoder from fastapi.responses import JSONResponse from annotation.auth import Auth, hasAuth from annotation.log import Log from config.constant import BusinessType, RedisKeyConfig from controller.login import LoginController from models import LoginLog, OperationLog from schemas.common import BaseResponse, DeleteListParams from schemas.log import GetLoginLogResponse, GetOperationLogResponse from utils.response import Response logAPI = APIRouter( prefix="/log", ) @logAPI.get("/login", response_class=JSONResponse, response_model=GetLoginLogResponse, summary="用户获取登录日志") @Log(title="用户获取登录日志", business_type=BusinessType.SELECT) @Auth(permission_list=["login:btn:list"]) async def get_login_log(request: Request, page: int = Query(default=1, description="页码"), pageSize: int = Query(default=10, description="每页数量"), username: Optional[str] = Query(default=None, description="用户账号"), nickname: Optional[str] = Query(default=None, description="用户昵称"), department_id: Optional[str] = Query(default=None, description="部门ID"), startTime: Optional[str] = Query(default=None, description="开始时间"), endTime: Optional[str] = Query(default=None, description="结束时间"), status: Optional[str] = Query(default=None, description="登录状态"), current_user: dict = Depends(LoginController.get_current_user), ): sub_departments = current_user.get("sub_departments") user_id = current_user.get("id") online_user_list = await LoginController.get_online_user(request, sub_departments) filterArgs = { f'{k}__contains': v for k, v in { 'username': username, 'nickname': nickname, }.items() if v } if status is not None: filterArgs['status'] = status if startTime and endTime: startTime = datetime.fromtimestamp(float(startTime) / 1000) endTime = datetime.fromtimestamp(float(endTime) / 1000) filterArgs['login_time__range'] = [startTime, endTime] if await hasAuth(request, "login:btn:admin"): online_user_list = list( filter(lambda x: x["department_id"] in sub_departments, jsonable_encoder(online_user_list))) if not department_id: filterArgs['user__department__id__in'] = sub_departments else: filterArgs['user__department__id'] = department_id else: online_user_list = list( filter(lambda x: x["user_id"] == user_id, jsonable_encoder(online_user_list))) if department_id: filterArgs['user__department__id'] = department_id result = await LoginLog.filter(**filterArgs, user__del_flag=1, del_flag=1).offset( (page - 1) * pageSize).limit(pageSize).values( id="id", user_id="user__id", username="user__username", user_nickname="user__nickname", department_id="user__department__id", department_name="user__department__name", login_ip="login_ip", login_location="login_location", browser="browser", os="os", status="status", login_time="login_time", session_id="session_id", create_time="create_time", update_time="update_time" ) for log in result: log["online"] = False for item in online_user_list: if item["session_id"] == log["session_id"]: log["online"] = True return Response.success(data={ "total": await LoginLog.filter(**filterArgs, del_flag=1, user__del_flag=1, ).count(), "result": result, "page": page, }) @logAPI.delete("/logout/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="用户强制退出") @logAPI.post("/logout/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="用户强制退出") @Log(title="用户强制退出", business_type=BusinessType.DELETE) @Auth(permission_list=["login:btn:logout"]) async def logout_user(request: Request, id: str = Path(description="会话ID"), current_user: dict = Depends(LoginController.get_current_user)): sub_departments = current_user.get("sub_departments") if await LoginLog.get_or_none(user__department__id__in=sub_departments, session_id=id, del_flag=1): if await request.app.state.redis.get(f"{RedisKeyConfig.ACCESS_TOKEN.key}:{id}"): await request.app.state.redis.delete(f"{RedisKeyConfig.ACCESS_TOKEN.key}:{id}") return Response.success(msg="强退成功!") return Response.failure(msg="会话不存在!") @logAPI.delete("/logoutList", response_class=JSONResponse, response_model=BaseResponse, summary="用户批量强制退出") @logAPI.post("/logoutList", response_class=JSONResponse, response_model=BaseResponse, summary="用户批量强制退出") @Log(title="用户批量强制退出", business_type=BusinessType.DELETE) @Auth(permission_list=["login:btn:logout"]) async def logout_user_list(request: Request, params: DeleteListParams, current_user: dict = Depends(LoginController.get_current_user)): sub_departments = current_user.get("sub_departments") for id in params.ids: if await LoginLog.get_or_none(user__department__id__in=sub_departments, session_id=id, del_flag=1): if await request.app.state.redis.get(f"{RedisKeyConfig.ACCESS_TOKEN.key}:{id}"): await request.app.state.redis.delete(f"{RedisKeyConfig.ACCESS_TOKEN.key}:{id}") return Response.success(msg="批量强退成功!") @logAPI.delete("/delete/login/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="用户删除登录日志") @logAPI.post("/delete/login/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="用户删除登录日志") @Log(title="用户删除登录日志", business_type=BusinessType.DELETE) @Auth(permission_list=["login:btn:delete"]) async def delete_login_log(request: Request, id: str = Path(..., description="登录日志ID"), current_user: dict = Depends(LoginController.get_current_user)): sub_departments = current_user.get("sub_departments") if log := await LoginLog.get_or_none(id=id, del_flag=1, user__department__id__in=sub_departments): log.del_flag = 0 await log.save() if await request.app.state.redis.get(f"{RedisKeyConfig.ACCESS_TOKEN.key}:{log.session_id}"): await request.app.state.redis.delete(f"{RedisKeyConfig.ACCESS_TOKEN.key}:{log.session_id}") return Response.success(msg="删除成功") else: return Response.failure(msg="删除失败,登录日志不存在!") @logAPI.delete("/deleteList/login", response_model=BaseResponse, response_class=JSONResponse, summary="用户批量删除登录日志") @logAPI.post("/deleteList/login", response_model=BaseResponse, response_class=JSONResponse, summary="用户批量删除登录日志") @Log(title="用户批量删除登录日志", business_type=BusinessType.DELETE) @Auth(permission_list=["login:btn:delete"]) async def delete_login_log(request: Request, params: DeleteListParams, current_user: dict = Depends(LoginController.get_current_user)): sub_departments = current_user.get("sub_departments") for id in set(params.ids): if log := await LoginLog.get_or_none(id=id, del_flag=1, user__department__id__in=sub_departments): log.del_flag = 0 await log.save() if await request.app.state.redis.get(f"{RedisKeyConfig.ACCESS_TOKEN.key}:{log.session_id}"): await request.app.state.redis.delete(f"{RedisKeyConfig.ACCESS_TOKEN.key}:{log.session_id}") return Response.success(msg="删除成功") @logAPI.get("/operation", response_class=JSONResponse, response_model=GetOperationLogResponse, summary="用户获取操作日志") @Auth(permission_list=["operation:btn:list"]) async def get_operation_log(request: Request, page: int = Query(default=1, description="页码"), name: Optional[str] = Query(default=None, description="操作名称"), type: Optional[str] = Query(default=None, description="操作类型"), pageSize: int = Query(default=10, description="每页数量"), username: Optional[str] = Query(default=None, description="用户账号"), nickname: Optional[str] = Query(default=None, description="用户昵称"), department_id: Optional[str] = Query(default=None, description="部门ID"), startTime: Optional[str] = Query(default=None, description="开始时间"), endTime: Optional[str] = Query(default=None, description="结束时间"), status: Optional[str] = Query(default=None, description="登录状态"), current_user: dict = Depends(LoginController.get_current_user), ): sub_departments = current_user.get("sub_departments") user_id = current_user.get("id") filterArgs = { f'{k}__contains': v for k, v in { 'operation_name': name, 'operation_type': type, 'operator__username': username, 'operator__nickname': nickname, }.items() if v } if status is not None: filterArgs['status'] = status if startTime and endTime: startTime = datetime.fromtimestamp(float(startTime) / 1000) endTime = datetime.fromtimestamp(float(endTime) / 1000) filterArgs['operation_time__range'] = [startTime, endTime] if await hasAuth(request, "operation:btn:admin"): if not department_id: filterArgs['department__id__in'] = sub_departments else: filterArgs['department__id'] = department_id else: filterArgs['operator__id'] = user_id if department_id: filterArgs['department__id'] = department_id result = await OperationLog.filter(**filterArgs, operator__del_flag=1, del_flag=1).offset( (page - 1) * pageSize).limit( pageSize).values( id="id", operation_name="operation_name", operation_type="operation_type", request_path="request_path", request_method="request_method", request_params="request_params", response_result="response_result", host="host", location="location", browser="browser", os="os", user_agent="user_agent", operator_id="operator__id", operator_name="operator__username", operator_nickname="operator__nickname", department_id="department__id", department_name="department__name", status="status", operation_time="operation_time", cost_time="cost_time" ) return Response.success(data={ "total": await OperationLog.filter(**filterArgs, del_flag=1, operator__del_flag=1).count(), "result": result, "page": page, "pageSize": pageSize }) @logAPI.delete("/delete/operation/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="用户删除操作日志") @logAPI.post("/delete/operation/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="用户删除操作日志") @Log(title="用户删除操作日志", business_type=BusinessType.DELETE) @Auth(permission_list=["operation:btn:delete"]) async def delete_operation_log(request: Request, id: str = Path(..., description="操作日志id"), current_user: dict = Depends(LoginController.get_current_user)): sub_departments = current_user.get("sub_departments") if log := await OperationLog.get_or_none(id=id, department__id__in=sub_departments, del_flag=1): log.del_flag = 0 await log.save() return Response.success(msg="删除成功") else: return Response.failure(msg="删除失败,操作日志不存在!") @logAPI.delete("/deleteList/operation", response_model=BaseResponse, response_class=JSONResponse, summary="用户批量删除操作日志") @logAPI.post("/deleteList/operation", response_model=BaseResponse, response_class=JSONResponse, summary="用户批量删除操作日志") @Log(title="用户批量删除操作日志", business_type=BusinessType.DELETE) @Auth(permission_list=["operation:btn:delete"]) async def delete_operation_log(request: Request, params: DeleteListParams, current_user: dict = Depends(LoginController.get_current_user)): sub_departments = current_user.get("sub_departments") for id in set(params.ids): if log := await OperationLog.get_or_none(id=id, department__id__in=sub_departments, del_flag=1): log.del_flag = 0 await log.save() return Response.success(msg="删除成功")