From 003e7b87425b8290b0a85b6fd34cd69949baf755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9A=93=E6=9C=88=E5=BD=92=E5=B0=98?= Date: Fri, 21 Feb 2025 02:38:30 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E5=90=8C=E6=AD=A5=E8=BF=91=E6=9C=9F?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- annotation/log.py | 24 +++++------ api/department.py | 2 +- api/log.py | 2 +- api/login.py | 8 +++- api/user.py | 104 ++++++++++++++++++++++++++++++++++++++++++++-- models/config.py | 2 +- schemas/login.py | 4 +- schemas/user.py | 39 +++++++++++++++++ 8 files changed, 162 insertions(+), 23 deletions(-) diff --git a/annotation/log.py b/annotation/log.py index f69cebb..e237a8a 100644 --- a/annotation/log.py +++ b/annotation/log.py @@ -147,18 +147,18 @@ class Log: # if request_from_swagger or request_from_redoc: # pass # else: - if status == 1: - session_id = request.app.state.session_id - current_user = await User.get_or_none(username=payload.get("username")) - await LoginLog.create( - user_id=current_user.id, - login_ip=host, - login_location=location, - browser=browser, - os=system_os, - status=status, - session_id=session_id - ) + session_id = request.app.state.session_id + status = 1 if request.app.state.login_status else 0 + current_user = await User.get_or_none(username=payload.get("username")) + await LoginLog.create( + user_id=current_user.id, + login_ip=host, + login_location=location, + browser=browser, + os=system_os, + status=status, + session_id=session_id + ) else: if "image" in request.headers.get("Accept", ""): pass diff --git a/api/department.py b/api/department.py index 58f1607..7b7164e 100644 --- a/api/department.py +++ b/api/department.py @@ -74,7 +74,7 @@ async def delete_department_recursive(department_id: str): :return: """ await Department.filter(id=department_id).delete() - sub_departments = await Department.filter(parentId=department_id).all() + sub_departments = await Department.filter(parent_id=department_id).all() for sub_department in sub_departments: await delete_department_recursive(sub_department.id) return True diff --git a/api/log.py b/api/log.py index 24343ce..1df6454 100644 --- a/api/log.py +++ b/api/log.py @@ -33,7 +33,7 @@ async def get_login_log(request: Request, ): online_user_list = await LoginController.get_online_user(request) online_user_list = list( - filter(lambda x: x["user_id"] == current_user.get("id"), jsonable_encoder(online_user_list, ))) + filter(lambda x: x["user_id"] == current_user.get("id"), jsonable_encoder(online_user_list))) user_id = current_user.get("id") result = await LoginLog.filter(user_id=user_id, del_flag=1).offset((page - 1) * pageSize).limit(pageSize).values( id="id", diff --git a/api/login.py b/api/login.py index 5b1f9ba..3c2c3ff 100644 --- a/api/login.py +++ b/api/login.py @@ -38,6 +38,8 @@ async def login( request: Request, params: CustomOAuth2PasswordRequestForm = Depends() ): + request.app.state.session_id = None + request.app.state.login_status = False user = LoginParams( username=params.username, password=params.password, @@ -47,7 +49,7 @@ async def login( ) captcha_enabled = ( True - if await request.app.state.redis.get(f'{RedisKeyConfig.SYSTEM_CONFIG.key}:account.captcha_enabled') + if await request.app.state.redis.get(f'{RedisKeyConfig.SYSTEM_CONFIG.key}:account_captcha_enabled') == 'true' else False ) @@ -75,6 +77,7 @@ async def login( ex=timedelta(minutes=5), ) request.app.state.session_id = result["session_id"] + request.app.state.login_status = True if request_from_swagger or request_from_redoc: return {'access_token': result["accessToken"], 'token_type': 'Bearer', "expires_in": result["expiresIn"] * 60} @@ -83,6 +86,7 @@ async def login( result.pop("session_id") result.pop("userInfo") return Response.success(data=result) + request.app.state.login_status = False return Response.failure(msg="登录失败,账号或密码错误!") @@ -90,7 +94,7 @@ async def login( async def register(request: Request, params: RegisterUserParams): register_enabled = ( True - if await request.app.state.redis.get(f'{RedisKeyConfig.SYSTEM_CONFIG.key}:account_register_enabled') + if await request.app.state.redis.get(f'{RedisKeyConfig.SYSTEM_CONFIG.key}:register_enabled') == 'true' else False ) diff --git a/api/user.py b/api/user.py index 37f60b0..ef84923 100644 --- a/api/user.py +++ b/api/user.py @@ -5,8 +5,9 @@ # @File : user.py # @Software : PyCharm # @Comment : 本程序 +import calendar import os -from datetime import datetime +from datetime import datetime, timedelta from typing import Optional from fastapi import APIRouter, Depends, Path, Query, UploadFile, File, Request @@ -20,13 +21,14 @@ from controller.login import LoginController from controller.query import QueryController from exceptions.exception import ModelValidatorException from models import File as FileModel -from models import Role, Department +from models import Role, Department, QueryCode from models.user import User, UserRole from schemas.common import BaseResponse from schemas.department import GetDepartmentListResponse from schemas.file import UploadFileResponse from schemas.user import AddUserParams, GetUserListResponse, GetUserInfoResponse, UpdateUserParams, \ - AddUserRoleParams, GetUserRoleInfoResponse, UpdateUserRoleParams, GetUserPermissionListResponse, ResetPasswordParams + AddUserRoleParams, GetUserRoleInfoResponse, UpdateUserRoleParams, GetUserPermissionListResponse, \ + ResetPasswordParams, GetUserStatisticsResponse from utils.common import filterKeyValues from utils.password import Password from utils.response import Response @@ -347,3 +349,99 @@ async def reset_user_password(request: Request, params: ResetPasswordParams, id: await user.save() return Response.success(msg="重置密码成功!") return Response.failure(msg="用户不存在!") + + +@userAPI.get("/statistics", response_model=GetUserStatisticsResponse, response_class=JSONResponse, + summary="获取用户查询统计") +@Log(title="获取用户查询统计", business_type=BusinessType.SELECT) +async def get_user_statistics(request: Request, current_user: dict = Depends(LoginController.get_current_user)): + user_id = current_user.get("id") + # 获取当前时间 + now = datetime.now() + + # 今日开始时间 + today_start_time = now.replace(hour=0, minute=0, second=0, microsecond=0) + + # 今日结束时间 + today_end_time = now.replace(hour=23, minute=59, second=59, microsecond=999999) + + # 当月开始时间 + this_month_start_time = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0) + + # 计算当月的最后一天 + this_month_last_day = calendar.monthrange(now.year, now.month)[1] + + # 当月结束时间 + this_month_end_time = now.replace( + day=this_month_last_day, hour=23, minute=59, second=59, microsecond=999999 + ) + + # 计算上个月的年和月 + if now.month == 1: # 处理1月(上月是去年的12月) + last_month_year = now.year - 1 + last_month = 12 + else: + last_month_year = now.year + last_month = now.month - 1 + + # 上月开始时间 + last_month_start_time = datetime(last_month_year, last_month, 1, 0, 0, 0, 0) + + # 计算上个月最后一天 + last_month_last_day = calendar.monthrange(last_month_year, last_month)[1] + + # 上月结束时间 + last_month_end_time = datetime(last_month_year, last_month, last_month_last_day, 23, 59, 59, 999999) + + # 计算今日查询数量 + today_count = await QueryCode.filter(create_time__gte=today_start_time, create_time__lte=today_end_time, + session__operator__id=user_id).count() + # 计算当月查询数量 + this_month_count = await QueryCode.filter(create_time__gte=this_month_start_time, + create_time__lte=this_month_end_time, + session__operator__id=user_id).count() + # 计算上月查询数量 + last_month_count = await QueryCode.filter(create_time__gte=last_month_start_time, + create_time__lte=last_month_end_time, + session__operator__id=user_id).count() + + async def get_last_14_days_count(status: int = 1): + today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + + # 保存结果的列表 + result = [] + + # 遍历最近7天(包括今天) + for i in range(14): + day = today - timedelta(days=i) + + # 当天的开始时间和结束时间 + day_start = day # 00:00:00 + day_end = (day + timedelta(days=1)) # 23:59:59 + + # 统计当天查询数量 + count = await QueryCode.filter( + create_time__gte=day_start, + create_time__lte=day_end, + session__operator__id=user_id, + status=status + ).count() + + # 添加到结果列表(格式化日期为 YYYY-MM-DD) + result.append({"date": day.strftime("%Y-%m-%d"), "count": count}) + + # 结果按日期升序排列(可选,确保从过去到现在排序) + result.sort(key=lambda x: x["date"]) + return result + + # 过去14天内的查询数量 + before_14day_count_success = await get_last_14_days_count(1) + before_14day_count_fail = await get_last_14_days_count(0) + + return Response.success(data={ + "today_count": today_count, + "this_month_count": this_month_count, + "last_month_count": last_month_count, + "before_14day_count_success": before_14day_count_success, + "before_14day_count_fail": before_14day_count_fail, + }) diff --git a/models/config.py b/models/config.py index d3f8d12..26f52e9 100644 --- a/models/config.py +++ b/models/config.py @@ -66,5 +66,5 @@ class Config(BaseModel): """ class Meta: - table = "sys_config" + table = "config" table_description = "系统配置表" diff --git a/schemas/login.py b/schemas/login.py index 1cfab7a..ab7be55 100644 --- a/schemas/login.py +++ b/schemas/login.py @@ -117,15 +117,13 @@ class GetCaptchaResult(BaseModel): uuid: Optional[str] = Field(default=None, description="验证码UUID") captcha: Optional[str] = Field(default=None, description="验证码图片") captcha_enabled: Optional[bool] = Field(default=False, description="是否开启验证码") - register_enabled: Optional[bool] = Field(default=False, description="是否开启注册") class Config: json_schema_extra = { "example": { "uuid": "1234567890", "captcha": "base64编码的图片", - "captcha_enabled": True, - "register_enabled": True + "captcha_enabled": True } } diff --git a/schemas/user.py b/schemas/user.py index abc3377..564c32a 100644 --- a/schemas/user.py +++ b/schemas/user.py @@ -348,3 +348,42 @@ class ResetPasswordParams(BaseModel): "password": "123456" } } + + +class GetUserStatisticsResult(BaseModel): + """ + 用户统计信息模型。 + """ + today_count: int = Field(default=0, description="今日查询次数") + this_month_count: int = Field(default=0, description="本月查询次数") + last_month_count: int = Field(default=0, description="上月查询次数") + before_14day_count_success: List[dict] = Field(default=[], description="最近14天成功查询次数") + before_14day_count_fail: List[dict] = Field(default=[], description="最近14天失败查询次数") + + class Config: + json_schema_extra = { + "example": { + "today_count": 0, + "this_month_count": 0, + "last_month_count": 0, + "before_14day_count_success": [ + { + "date": "2023-10-01", + "count": 0 + } + ], + "before_14day_count_fail": [ + { + "date": "2023-10-01", + "count": 0 + } + ] + } + } + + +class GetUserStatisticsResponse(BaseResponse): + """ + 获取用户统计信息响应模型。 + """ + data: GetUserStatisticsResult = Field(default=None, description="响应数据")