From 550ebabc16835683b179d3e8f9e46f2eea490f4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9A=93=E6=9C=88=E5=BD=92=E5=B0=98?= Date: Sun, 23 Feb 2025 22:03:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BB=99=E7=94=A8=E6=88=B7=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E3=80=81=E9=83=A8=E9=97=A8=E7=AE=A1=E7=90=86=E3=80=81?= =?UTF-8?q?=E6=9D=83=E9=99=90=E7=AE=A1=E7=90=86=E3=80=81=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E3=80=81=E5=9B=BD=E9=99=85=E5=8C=96=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E3=80=81=E8=A7=92=E8=89=B2=E7=AE=A1=E7=90=86=E3=80=81?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E6=97=A5=E5=BF=97=E3=80=81=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E3=80=81=E7=BC=93=E5=AD=98=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E3=80=81=E7=BC=93=E5=AD=98=E7=9B=91=E6=8E=A7=E5=92=8C=E6=80=A7?= =?UTF-8?q?=E8=83=BD=E7=9B=91=E6=8E=A7=E6=B7=BB=E5=8A=A0=E6=8C=89=E9=92=AE?= =?UTF-8?q?=E7=BA=A7=E6=9D=83=E9=99=90=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- annotation/log.py | 2 +- api/cache.py | 8 ++ api/config.py | 33 ++++-- api/department.py | 266 +++++++++--------------------------------- api/i18n.py | 115 +++++++++++------- api/log.py | 163 ++++++++++++++++++-------- api/login.py | 23 +++- api/permission.py | 45 +++++-- api/role.py | 260 ++++++++++++++++++++++++----------------- api/server.py | 29 +++-- api/user.py | 167 +++++++++++++++++++------- config/get_redis.py | 2 +- controller/login.py | 13 ++- controller/query.py | 23 +++- models/__init__.py | 3 +- models/department.py | 35 ------ models/user.py | 2 - schemas/config.py | 7 -- schemas/department.py | 78 ------------- 19 files changed, 657 insertions(+), 617 deletions(-) diff --git a/annotation/log.py b/annotation/log.py index e237a8a..d13c65b 100644 --- a/annotation/log.py +++ b/annotation/log.py @@ -149,7 +149,7 @@ class Log: # else: 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")) + current_user = await User.get_or_none(username=payload.get("username"),del_flag=1) await LoginLog.create( user_id=current_user.id, login_ip=host, diff --git a/api/cache.py b/api/cache.py index f220b79..84736cc 100644 --- a/api/cache.py +++ b/api/cache.py @@ -9,6 +9,7 @@ from fastapi import APIRouter, Depends, Path, Request from fastapi.responses import JSONResponse +from annotation.auth import Auth from annotation.log import Log from config.constant import BusinessType, RedisKeyConfig from controller.login import LoginController @@ -26,6 +27,7 @@ cacheAPI = APIRouter( @cacheAPI.get("/monitor", response_class=JSONResponse, response_model=GetCacheMonitorResponse, summary="获取缓存监控信息") @Log(title="获取缓存监控信息", business_type=BusinessType.SELECT) +@Auth(permission_list=['cache:btn:infoList']) async def get_cache_info(request: Request): info = await request.app.state.redis.info() db_size = await request.app.state.redis.dbsize() @@ -45,6 +47,7 @@ async def get_cache_info(request: Request): @cacheAPI.get("/names", response_class=JSONResponse, response_model=GetCacheInfoResponse, summary="获取缓存名称列表") @Log(title="获取缓存名称列表", business_type=BusinessType.SELECT) +@Auth(permission_list=['cache:btn:list']) async def get_cache_names(request: Request): name_list = [] for key_config in RedisKeyConfig: @@ -62,6 +65,7 @@ async def get_cache_names(request: Request): @cacheAPI.get("/keys/{cacheName}", response_class=JSONResponse, response_model=GetCacheKeysListResponse, summary="获取缓存键名列表") @Log(title="获取缓存键名列表", business_type=BusinessType.SELECT) +@Auth(permission_list=['cache:btn:list']) async def get_cache_keys(request: Request, cacheName: str = Path(description="缓存名称")): cache_keys = await request.app.state.redis.keys(f'{cacheName}*') cache_key_list = [key.split(':', 1)[1] for key in cache_keys if key.startswith(f'{cacheName}:')] @@ -71,6 +75,7 @@ async def get_cache_keys(request: Request, cacheName: str = Path(description=" @cacheAPI.get("/info/{cacheName}/{cacheKey}", response_class=JSONResponse, response_model=GetCacheInfoResponse, summary="获取缓存信息") @Log(title="获取缓存信息", business_type=BusinessType.SELECT) +@Auth(permission_list=['cache:btn:info']) async def get_cache_info(request: Request, cacheName: str = Path(description="缓存名称"), cacheKey: str = Path(description="缓存键名")): cache_value = await request.app.state.redis.get(f'{cacheName}:{cacheKey}') @@ -88,6 +93,7 @@ async def get_cache_info(request: Request, cacheName: str = Path(description=" @cacheAPI.post("/cacheName/{name}", response_class=JSONResponse, response_model=BaseResponse, summary="通过键名删除缓存") @Log(title="通过键名删除缓存", business_type=BusinessType.DELETE) +@Auth(permission_list=['cache:btn:delete']) async def delete_cache(request: Request, name: str = Path(description="缓存名称")): cache_keys = await request.app.state.redis.keys(f'{name}*') if cache_keys: @@ -99,6 +105,7 @@ async def delete_cache(request: Request, name: str = Path(description="缓存名 summary="通过键值删除缓存") @cacheAPI.post("/cacheKey/{key}", response_class=JSONResponse, response_model=BaseResponse, summary="通过键值删除缓存") @Log(title="通过键值删除缓存", business_type=BusinessType.DELETE) +@Auth(permission_list=['cache:btn:delete']) async def delete_cache_key(request: Request, key: str = Path(description="缓存键名")): cache_keys = await request.app.state.redis.keys(f'*{key}') if cache_keys: @@ -109,6 +116,7 @@ async def delete_cache_key(request: Request, key: str = Path(description="缓存 @cacheAPI.delete("/clearAll", response_class=JSONResponse, response_model=BaseResponse, summary="删除所有缓存") @cacheAPI.post("/clearAll", response_class=JSONResponse, response_model=BaseResponse, summary="删除所有缓存") @Log(title="删除所有缓存", business_type=BusinessType.DELETE) +@Auth(permission_list=['cache:btn:delete']) async def delete_all_cache(request: Request): cache_keys = await request.app.state.redis.keys() if cache_keys: diff --git a/api/config.py b/api/config.py index 66f2f2f..4dcfb09 100644 --- a/api/config.py +++ b/api/config.py @@ -11,13 +11,14 @@ from typing import Optional from fastapi import APIRouter, Depends, Path, Request, Query from fastapi.responses import JSONResponse +from annotation.auth import Auth from annotation.log import Log from config.constant import BusinessType from config.get_redis import Redis from controller.login import LoginController from models import Config -from schemas.common import BaseResponse -from schemas.config import AddConfigParams, DeleteConfigListParams, GetConfigInfoResponse, GetConfigListResponse +from schemas.common import BaseResponse, DeleteListParams +from schemas.config import AddConfigParams, GetConfigInfoResponse, GetConfigListResponse from utils.response import Response configApi = APIRouter( @@ -28,8 +29,9 @@ configApi = APIRouter( @configApi.post("/add", response_class=JSONResponse, response_model=BaseResponse, summary="新增配置") @Log(title="新增配置", business_type=BusinessType.INSERT) +@Auth(permission_list=["config:btn:add"]) async def add_config(request: Request, params: AddConfigParams): - if await Config.get_or_none(name=params.name, key=params.key): + if await Config.get_or_none(name=params.name, key=params.key, del_flag=1): return Response.error(msg="配置已存在") config = await Config.create( name=params.name, @@ -48,9 +50,11 @@ async def add_config(request: Request, params: AddConfigParams): @configApi.delete("/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除配置") @configApi.post("/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除配置") @Log(title="删除配置", business_type=BusinessType.DELETE) +@Auth(permission_list=["config:btn:delete"]) async def delete_config(request: Request, id: str = Path(description="配置ID")): - if config := await Config.get_or_none(id=id): - await config.delete() + if config := await Config.get_or_none(id=id, del_flag=1): + config.del_flag = 0 + await config.save() await Redis.init_system_config(request.app) return Response.success(msg="删除成功") else: @@ -60,10 +64,12 @@ async def delete_config(request: Request, id: str = Path(description="配置ID") @configApi.delete("/deleteList", response_class=JSONResponse, response_model=BaseResponse, summary="批量删除配置") @configApi.post("/deleteList", response_class=JSONResponse, response_model=BaseResponse, summary="批量删除配置") @Log(title="批量删除配置", business_type=BusinessType.DELETE) -async def delete_config_list(request: Request, params: DeleteConfigListParams): +@Auth(permission_list=["config:btn:delete"]) +async def delete_config_list(request: Request, params: DeleteListParams): for id in set(params.ids): - if config := await Config.get_or_none(id=id): - await config.delete() + if config := await Config.get_or_none(id=id, del_flag=1): + config.del_flag = 0 + await config.save() await Redis.init_system_config(request.app) return Response.success(msg="删除成功") @@ -71,8 +77,9 @@ async def delete_config_list(request: Request, params: DeleteConfigListParams): @configApi.put("/update/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="修改配置") @configApi.post("/update/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="修改配置") @Log(title="修改配置", business_type=BusinessType.UPDATE) +@Auth(permission_list=["config:btn:update"]) async def update_config(request: Request, params: AddConfigParams, id: str = Path(description="配置ID")): - if config := await Config.get_or_none(id=id): + if config := await Config.get_or_none(id=id, del_flag=1): config.name = params.name config.key = params.key config.value = params.value @@ -87,8 +94,9 @@ async def update_config(request: Request, params: AddConfigParams, id: str = Pat @configApi.get("/info/{id}", response_class=JSONResponse, response_model=GetConfigInfoResponse, summary="获取配置信息") @Log(title="获取配置信息", business_type=BusinessType.SELECT) +@Auth(permission_list=["config:btn:info"]) async def get_config_info(request: Request, id: str = Path(description="配置ID")): - if config := await Config.get_or_none(id=id): + if config := await Config.get_or_none(id=id, del_flag=1): data = { "id": config.id, "name": config.name, @@ -108,6 +116,7 @@ async def get_config_info(request: Request, id: str = Path(description="配置ID @configApi.get("/list", response_class=JSONResponse, response_model=GetConfigListResponse, summary="获取配置列表") @Log(title="获取配置列表", business_type=BusinessType.SELECT) +@Auth(permission_list=["config:btn:list"]) async def get_config_list(request: Request, page: int = Query(default=1, description="当前页码"), pageSize: int = Query(default=10, description="每页数量"), @@ -122,8 +131,8 @@ async def get_config_list(request: Request, 'type': type, }.items() if v } - total = await Config.filter(**filterArgs).count() - data = await Config.filter(**filterArgs).offset((page - 1) * pageSize).limit(pageSize).values( + total = await Config.filter(**filterArgs, del_flag=1).count() + data = await Config.filter(**filterArgs, del_flag=1).offset((page - 1) * pageSize).limit(pageSize).values( id="id", name="name", key="key", diff --git a/api/department.py b/api/department.py index 7b7164e..fa46efd 100644 --- a/api/department.py +++ b/api/department.py @@ -10,20 +10,22 @@ from typing import Optional from fastapi import APIRouter, Depends, Query, Path, Request from fastapi.responses import JSONResponse +from annotation.auth import Auth from annotation.log import Log -from config.constant import BusinessType +from config.constant import BusinessType, RedisKeyConfig from controller.login import LoginController -from models import Department, Role, DepartmentRole -from schemas.common import BaseResponse +from models import Department, Role +from schemas.common import BaseResponse, DeleteListParams from schemas.department import AddDepartmentParams, GetDepartmentInfoResponse, \ - GetDepartmentListResponse, AddDepartmentRoleParams, GetDepartmentRoleInfoResponse, DeleteDepartmentListParams + GetDepartmentListResponse from utils.response import Response -departmentAPI = APIRouter(prefix="/department", dependencies=[Depends(LoginController.get_current_user)]) +departmentAPI = APIRouter(prefix="/department") @departmentAPI.post("/add", response_model=BaseResponse, response_class=JSONResponse, summary="新增部门") @Log(title="新增部门", business_type=BusinessType.INSERT) +@Auth(["department:btn:add"]) async def add_department(request: Request, params: AddDepartmentParams, current_user: dict = Depends(LoginController.get_current_user)): parent_id = current_user.get("department_id") @@ -40,6 +42,8 @@ async def add_department(request: Request, params: AddDepartmentParams, status=params.status ) if department: + if await request.app.state.redis.get(f'{RedisKeyConfig.USER_INFO.key}:*'): + await request.app.state.redis.delete(f'{RedisKeyConfig.USER_INFO.key}:*') return Response.success(msg="添加成功!") else: return Response.error(msg="添加失败!") @@ -48,9 +52,16 @@ async def add_department(request: Request, params: AddDepartmentParams, @departmentAPI.delete("/delete/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="删除部门") @departmentAPI.post("/delete/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="删除部门") @Log(title="删除部门", business_type=BusinessType.DELETE) -async def delete_department(request: Request, id: str = Path(description="部门ID")): +@Auth(["department:btn:delete"]) +async def delete_department(request: Request, id: str = Path(description="部门ID"), + current_user: dict = Depends(LoginController.get_current_user)): if department := await Department.get_or_none(id=id, del_flag=1): + sub_departments = current_user.get("sub_departments") + if department.id not in sub_departments: + return Response.error(msg="删除失败,无权限!") if await delete_department_recursive(department_id=department.id): + if await request.app.state.redis.get(f'{RedisKeyConfig.USER_INFO.key}:*'): + await request.app.state.redis.delete(f'{RedisKeyConfig.USER_INFO.key}:*') return Response.success(msg="删除成功!") return Response.error(msg="删除失败!") else: @@ -60,10 +71,16 @@ async def delete_department(request: Request, id: str = Path(description="部门 @departmentAPI.delete("/deleteList", response_model=BaseResponse, response_class=JSONResponse, summary="批量删除部门") @departmentAPI.post("/deleteList", response_model=BaseResponse, response_class=JSONResponse, summary="批量删除部门") @Log(title="批量删除部门", business_type=BusinessType.DELETE) -async def delete_department_list(request: Request, params: DeleteDepartmentListParams): +@Auth(["department:btn:delete"]) +async def delete_department_list(request: Request, params: DeleteListParams, + current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") for item in set(params.ids): if department := await Department.get_or_none(id=item, del_flag=1): - await delete_department_recursive(department_id=department.id) + if item in sub_departments: + await delete_department_recursive(department_id=department.id) + if await request.app.state.redis.get(f'{RedisKeyConfig.USER_INFO.key}:*'): + await request.app.state.redis.delete(f'{RedisKeyConfig.USER_INFO.key}:*') return Response.success(msg="删除成功!") @@ -73,8 +90,8 @@ async def delete_department_recursive(department_id: str): :param department_id: 部门ID :return: """ - await Department.filter(id=department_id).delete() - sub_departments = await Department.filter(parent_id=department_id).all() + await Department.filter(id=department_id, del_flag=1).update(del_flag=0) + sub_departments = await Department.filter(parent_id=department_id, del_flag=1).all() for sub_department in sub_departments: await delete_department_recursive(sub_department.id) return True @@ -83,8 +100,13 @@ async def delete_department_recursive(department_id: str): @departmentAPI.put("/update/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="修改部门") @departmentAPI.post("/update/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="修改部门") @Log(title="修改部门", business_type=BusinessType.UPDATE) -async def update_department(request: Request, params: AddDepartmentParams, id: str = Path(description="部门ID")): +@Auth(["department:btn:update"]) +async def update_department(request: Request, params: AddDepartmentParams, id: str = Path(description="部门ID"), + current_user: dict = Depends(LoginController.get_current_user)): if department := await Department.get_or_none(id=id, del_flag=1): + sub_departments = current_user.get("sub_departments") + if id not in sub_departments: + return Response.error(msg="修改失败,无权限!") department.name = params.name department.parent_id = params.parent_id department.principal = params.principal @@ -102,7 +124,9 @@ async def update_department(request: Request, params: AddDepartmentParams, id: s @departmentAPI.get("/info/{id}", response_model=GetDepartmentInfoResponse, response_class=JSONResponse, summary="查询部门详情") @Log(title="查询部门详情", business_type=BusinessType.SELECT) -async def get_department(request: Request, id: str = Path(description="部门ID")): +@Auth(["department:btn:info"]) +async def get_department(request: Request, id: str = Path(description="部门ID"), + current_user: dict = Depends(LoginController.get_current_user)): if department := await Department.get_or_none(id=id, del_flag=1).values( id="id", name="name", @@ -118,6 +142,9 @@ async def get_department(request: Request, id: str = Path(description="部门ID" create_by="create_by", update_by="update_by" ): + sub_departments = current_user.get("sub_departments") + if id not in sub_departments: + return Response.error(msg="查询失败,无权限!") return Response.success(data=department) else: return Response.error(msg="部门不存在!") @@ -126,6 +153,7 @@ async def get_department(request: Request, id: str = Path(description="部门ID" @departmentAPI.get("/list", response_model=GetDepartmentListResponse, response_class=JSONResponse, summary="查询部门列表") @Log(title="查询部门列表", business_type=BusinessType.SELECT) +@Auth(["department:btn:list"]) async def get_department_list( request: Request, page: int = Query(default=1, description="当前页码"), @@ -148,43 +176,11 @@ async def get_department_list( 'sort': sort }.items() if v } - department_id = current_user.get("department_id", "") - # 递归查询所有部门 - all_departments = await get_department_and_subdepartments(department_id, filterArgs) - - # 分页处理 - total = len(all_departments) - paginated_departments = all_departments[(page - 1) * pageSize: page * pageSize] - return Response.success(data={ - "result": paginated_departments, - "total": total, - "page": page - }) - - -async def get_department_and_subdepartments(department_id: str, filterArgs: dict, visited: set = None): - """ - 查询当前部门及其所有下属部门的数据,并根据 id 去重。 - - :param department_id: 当前部门 ID - :param filterArgs: 过滤条件 - :param visited: 已访问的部门 ID 集合,用于避免循环依赖 - :return: 去重后的部门列表 - """ - if visited is None: - visited = set() # 初始化已访问的部门 ID 集合 - - # 如果当前部门 ID 已经访问过,直接返回空列表,避免死循环 - if department_id in visited: - return [] - - visited.add(department_id) # 标记当前部门 ID 为已访问 - - # 查询当前部门 - current_department = await Department.filter( - id=department_id, - **filterArgs - ).values( + sub_departments = current_user.get("sub_departments") + total = await Department.filter(**filterArgs, del_flag=1, id__in=sub_departments).count() + data = await Department.filter(**filterArgs, del_flag=1, id__in=sub_departments).offset( + (page - 1) * pageSize).limit( + pageSize).values( id="id", name="name", parent_id="parent_id", @@ -199,177 +195,26 @@ async def get_department_and_subdepartments(department_id: str, filterArgs: dict create_by="create_by", update_by="update_by" ) - - # 查询直接子部门 - sub_departments = await Department.filter( - parent_id=department_id, # 只根据 parent_id 查询 - **filterArgs - ).values( - id="id", - name="name", - parent_id="parent_id", - principal="principal", - phone="phone", - email="email", - remark="remark", - sort="sort", - status="status", - create_time="create_time", - update_time="update_time", - create_by="create_by", - update_by="update_by" - ) - - # 递归查询子部门的子部门 - for department in sub_departments[:]: # 使用切片复制避免修改迭代中的列表 - sub_sub_departments = await get_department_and_subdepartments(department["id"], filterArgs, visited) - sub_departments.extend(sub_sub_departments) - - # 合并当前部门和所有下属部门的数据 - all_departments = current_department + sub_departments - - # 根据 id 去重 - unique_departments = [] - seen_ids = set() # 用于记录已经处理过的部门 ID - for department in all_departments: - if department["id"] not in seen_ids: - unique_departments.append(department) - seen_ids.add(department["id"]) - - return unique_departments - - -@departmentAPI.post("/addRole", response_model=BaseResponse, response_class=JSONResponse, summary="添加部门角色") -@Log(title="添加部门角色", business_type=BusinessType.INSERT) -async def add_department_role(request: Request, params: AddDepartmentRoleParams): - if await DepartmentRole.get_or_none(department_id=params.department_id, role_id=params.role_id, del_flag=1): - return Response.error(msg="该部门已存在该角色!") - if department := await Department.get_or_none(id=params.department_id, del_flag=1): - if role := await Role.get_or_none(id=params.role_id, del_flag=1): - departmentRole = await DepartmentRole.create(department_id=department.id, role_id=role.id) - if departmentRole: - return Response.success(msg="添加成功!") - else: - return Response.error(msg="添加失败!") - else: - return Response.error(msg="添加失败,角色不存在!") - else: - return Response.error(msg="添加失败,部门不存在!") - - -@departmentAPI.delete("/deleteRole/{id}", response_model=BaseResponse, response_class=JSONResponse, - summary="删除部门角色") -@departmentAPI.post("/deleteRole/{id}", response_model=BaseResponse, response_class=JSONResponse, - summary="删除部门角色") -@Log(title="删除部门角色", business_type=BusinessType.DELETE) -async def delete_department_role(request: Request, id: str = Path(description="部门角色ID")): - if departmentRole := await DepartmentRole.get_or_none(id=id, del_flag=1): - await departmentRole.delete() - return Response.success(msg="删除成功!") - else: - return Response.error(msg="删除失败,部门角色不存在!") - - -@departmentAPI.put("/updateRole/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="修改部门角色") -@departmentAPI.post("/updateRole/{id}", response_model=BaseResponse, response_class=JSONResponse, - summary="修改部门角色") -@Log(title="修改部门角色", business_type=BusinessType.UPDATE) -async def update_department_role(request: Request, params: AddDepartmentRoleParams, - id: str = Path(description="部门角色ID")): - if departmentRole := await DepartmentRole.get_or_none(id=id, del_flag=1): - if department := await Department.get_or_none(id=params.department_id, del_flag=1): - if role := await Role.get_or_none(id=params.role_id, del_flag=1): - departmentRole.department_id = department.id - departmentRole.role_id = role.id - await departmentRole.save() - return Response.success(msg="修改成功!") - else: - return Response.error(msg="修改失败,角色不存在!") - else: - return Response.error(msg="修改失败,部门不存在!") - else: - return Response.error(msg="修改失败,部门角色不存在!") - - -@departmentAPI.get("/roleInfo", response_model=GetDepartmentRoleInfoResponse, response_class=JSONResponse, - summary="获取部门角色信息") -@Log(title="获取部门角色信息", business_type=BusinessType.SELECT) -async def get_department_role_info(request: Request, id: str = Query(description="部门角色ID")): - if departmentRole := await DepartmentRole.get_or_none(id=id, del_flag=1): - data = await departmentRole.first().values( - id="id", - department_id="department__id", - department_name="department__name", - department_phone="department__phone", - department_principal="department__principal", - department_email="department__email", - role_name="role__name", - role_code="role__code", - role_id="role__id", - create_time="create_time", - update_time="update_time" - ) - return Response.success(data=data) - else: - return Response.error(msg="获取失败,部门角色不存在!") - - -@departmentAPI.get("/roleList", response_model=GetDepartmentListResponse, response_class=JSONResponse, - summary="获取部门角色列表") -@Log(title="获取部门角色列表", business_type=BusinessType.SELECT) -async def get_department_role_list( - request: Request, - page: int = Query(default=1, description="当前页码"), - pageSize: int = Query(default=10, description="每页条数"), - department_id: Optional[str] = Query(default=None, description="部门ID"), - department_name: Optional[str] = Query(default=None, description="部门名称"), - department_phone: Optional[str] = Query(default=None, description="部门电话"), - department_principal: Optional[str] = Query(default=None, description="部门负责人"), - department_email: Optional[str] = Query(default=None, description="部门邮箱"), - role_id: Optional[str] = Query(default=None, description="角色ID"), - role_name: Optional[str] = Query(default=None, description="角色名称"), - role_code: Optional[str] = Query(default=None, description="角色编码"), -): - filterArgs = { - f'{k}__contains': v for k, v in { - 'department__id': department_id, - 'department__name': department_name, - 'department__phone': department_phone, - 'department__principal': department_principal, - 'department__email': department_email, - 'role__id': role_id, - 'role__name': role_name, - 'role__code': role_code - }.items() if v - } - total = await DepartmentRole.filter(**filterArgs).count() - data = await DepartmentRole.filter(**filterArgs).offset((page - 1) * pageSize).limit(pageSize).values( - id="id", - department_id="department__id", - department_name="department__name", - department_phone="department__phone", - department_principal="department__principal", - department_email="department__email", - role_name="role__name", - role_code="role__code", - role_id="role__id", - create_time="create_time", - update_time="update_time" - ) return Response.success(data={ "result": data, "total": total, - "page": page + "page": page, + "pageSize": pageSize }) @departmentAPI.get("/roleList/{id}", response_model=GetDepartmentListResponse, response_class=JSONResponse, summary="用户获取部门角色列表") -@Log(title="获取部门角色列表", business_type=BusinessType.OTHER) +@Log(title="获取部门角色列表", business_type=BusinessType.SELECT) +@Auth(["department:btn:list"]) async def get_department_role_list( request: Request, - id: str = Path(..., description="部门ID") + id: str = Path(..., description="部门ID"), + current_user: dict = Depends(LoginController.get_current_user) ): + sub_departments = current_user.get("sub_departments") + if id not in sub_departments: + return Response.error(msg="查询失败,无权限!") data = await Role.filter(department__id=id).values( id="id", department_id="department__id", @@ -386,5 +231,6 @@ async def get_department_role_list( return Response.success(data={ "result": data, "total": len(data), - "page": 1 + "page": 1, + "pageSize": 9999 }) diff --git a/api/i18n.py b/api/i18n.py index bd2e2c1..c3edabf 100644 --- a/api/i18n.py +++ b/api/i18n.py @@ -12,24 +12,27 @@ from fastapi import APIRouter, Depends, Path, Request, Query from fastapi.encoders import jsonable_encoder from fastapi.responses import JSONResponse +from annotation.auth import Auth from annotation.log import Log from config.constant import BusinessType, RedisKeyConfig from controller.login import LoginController from models import I18n, Locale -from schemas.common import BaseResponse +from schemas.common import BaseResponse, DeleteListParams from schemas.i18n import AddLocaleParams, GetLocaleInfoResponse, AddI18nParams, GetI18nInfoResponse, \ GetI18nInfoListResponse, GetI18nListResponse from utils.response import Response i18nAPI = APIRouter( prefix="/i18n", + dependencies=[Depends(LoginController.get_current_user)] ) @i18nAPI.post("/addLocale", response_class=JSONResponse, response_model=BaseResponse, summary="添加国际化类型") @Log(title="添加国际化类型", business_type=BusinessType.INSERT) -async def add_locale(request: Request, params: AddLocaleParams, current_user=Depends(LoginController.get_current_user)): - if await Locale.get_or_none(code=params.code): +@Auth(["locale:btn:add"]) +async def add_locale(request: Request, params: AddLocaleParams): + if await Locale.get_or_none(code=params.code, del_flag=1): return Response.error(msg="该语言代码已存在!") locale = await Locale.create( code=params.code, @@ -46,23 +49,41 @@ async def add_locale(request: Request, params: AddLocaleParams, current_user=Dep @i18nAPI.post("/deleteLocale/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除国际化类型") @Log(title="删除国际化类型", business_type=BusinessType.DELETE) -async def delete_locale(request: Request, id: str = Path(description="国际化类型ID"), - current_user=Depends(LoginController.get_current_user)): - if locale := await Locale.get_or_none(id=id): +@Auth(["locale:btn:delete"]) +async def delete_locale(request: Request, id: str = Path(description="国际化类型ID")): + if locale := await Locale.get_or_none(id=id, del_flag=1): # 移除语言 - await I18n.filter(locale_id=locale.id).delete() - await locale.delete() + await I18n.filter(locale_id=locale.id, del_flag=1).update(del_flag=0) + locale.del_flag = 0 + await locale.save() return Response.success(msg="删除成功!") else: return Response.error(msg="该国际化类型不存在!") +@i18nAPI.delete("/deleteLocaleList", response_class=JSONResponse, response_model=BaseResponse, + summary="批量删除国际化类型") +@i18nAPI.post("/deleteLocaleList", response_class=JSONResponse, response_model=BaseResponse, + summary="批量删除国际化类型") +@Log(title="批量删除国际化类型", business_type=BusinessType.DELETE) +@Auth(["locale:btn:delete"]) +async def delete_locale_list(request: Request, params: DeleteListParams): + for id in set(params.ids): + if locale := await Locale.get_or_none(id=id, del_flag=1): + # 移除语言 + await I18n.filter(locale_id=locale.id, del_flag=1).update(del_flag=0) + locale.del_flag = 0 + await locale.save() + return Response.success(msg="删除成功!") + + @i18nAPI.put("/updateLocale/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="修改国际化类型") @i18nAPI.post("/updateLocale/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="修改国际化类型") @Log(title="修改国际化类型", business_type=BusinessType.UPDATE) +@Auth(["locale:btn:update"]) async def update_locale(request: Request, params: AddLocaleParams, id: str = Path(description="国际化类型ID")): - if locale := await Locale.get_or_none(id=id): - if await Locale.get_or_none(code=params.code, name=params.name, id=id): + if locale := await Locale.get_or_none(id=id, del_flag=1): + if await Locale.get_or_none(code=params.code, name=params.name, id=id, del_flag=1): return Response.error(msg="该国际化类型已存在!") locale.code = params.code locale.name = params.name @@ -75,8 +96,9 @@ async def update_locale(request: Request, params: AddLocaleParams, id: str = Pat @i18nAPI.get("/locale/info/{id}", response_class=JSONResponse, response_model=GetLocaleInfoResponse, summary="获取国际化类型信息") @Log(title="获取国际化类型信息", business_type=BusinessType.SELECT) +@Auth(["locale:btn:info"]) async def get_locale_info(request: Request, id: str = Path(description="国际化类型ID")): - if locale := await Locale.get_or_none(id=id): + if locale := await Locale.get_or_none(id=id, del_flag=1): locale = { "id": locale.id, "code": locale.code, @@ -92,7 +114,8 @@ async def get_locale_info(request: Request, id: str = Path(description="国际 @i18nAPI.get("/locale/list", response_class=JSONResponse, response_model=BaseResponse, summary="获取国际化类型列表") -# @Log(title="获取国际化类型列表", business_type=BusinessType.SELECT) +@Log(title="获取国际化类型列表", business_type=BusinessType.SELECT) +@Auth(["locale:btn:list"]) async def get_locale_list(request: Request, page: int = Query(default=1, description="当前页码"), pageSize: int = Query(default=50, description="每页条数"), @@ -105,8 +128,9 @@ async def get_locale_list(request: Request, 'code': code }.items() if v } - total = await Locale.filter(**filterArgs).count() - data = await Locale.filter(**filterArgs).offset((page - 1) * pageSize).limit(pageSize).distinct().values( + total = await Locale.filter(**filterArgs, del_flag=1).count() + data = await Locale.filter(**filterArgs, del_flag=1).offset((page - 1) * pageSize).limit( + pageSize).distinct().values( id="id", code="code", name="name", @@ -125,10 +149,11 @@ async def get_locale_list(request: Request, @i18nAPI.post("/addI18n", response_class=JSONResponse, response_model=BaseResponse, summary="添加国际化内容") @Log(title="添加国际化内容", business_type=BusinessType.INSERT) -async def add_i18n(request: Request, params: AddI18nParams, current_user=Depends(LoginController.get_current_user)): - if await I18n.get_or_none(key=params.key, locale_id=params.locale_id): +@Auth(["i18n:btn:add"]) +async def add_i18n(request: Request, params: AddI18nParams): + if await I18n.get_or_none(key=params.key, locale_id=params.locale_id, del_flag=1): return Response.error(msg="该国际化内容已存在!") - locale = await Locale.get_or_none(id=params.locale_id) + locale = await Locale.get_or_none(id=params.locale_id, del_flag=1) i18n = await I18n.create( key=params.key, translation=params.translation, @@ -145,22 +170,36 @@ async def add_i18n(request: Request, params: AddI18nParams, current_user=Depends @i18nAPI.post("/deleteI18n/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除国际化内容") @Log(title="删除国际化内容", business_type=BusinessType.DELETE) -async def delete_i18n(request: Request, id: str = Path(description="国际化内容ID"), - current_user=Depends(LoginController.get_current_user)): - if i18n := await I18n.get_or_none(id=id): - await i18n.delete() +@Auth(["i18n:btn:delete"]) +async def delete_i18n(request: Request, id: str = Path(description="国际化内容ID")): + if i18n := await I18n.get_or_none(id=id, del_flag=1): + i18n.del_flag = 0 + await i18n.save() return Response.success(msg="删除成功!") else: return Response.error(msg="该国际化内容不存在!") +@i18nAPI.delete("/deleteI18nList", response_class=JSONResponse, response_model=BaseResponse, + summary="批量删除国际化内容") +@i18nAPI.post("/deleteI18nList", response_class=JSONResponse, response_model=BaseResponse, summary="批量删除国际化内容") +@Log(title="批量删除国际化内容", business_type=BusinessType.DELETE) +@Auth(["i18n:btn:delete"]) +async def delete_i18n_list(request: Request, params: DeleteListParams): + for id in set(params.ids): + if i18n := await I18n.get_or_none(id=id, del_flag=1): + i18n.del_flag = 0 + await i18n.save() + return Response.success(msg="删除成功!") + + @i18nAPI.put("/updateI18n/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="修改国际化内容") @i18nAPI.post("/updateI18n/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="修改国际化内容") @Log(title="修改国际化内容", business_type=BusinessType.UPDATE) -async def update_i18n(request: Request, params: AddI18nParams, id: str = Path(description="国际化内容ID"), - current_user=Depends(LoginController.get_current_user)): - if i18n := await I18n.get_or_none(id=id): - locale = await Locale.get_or_none(id=params.locale_id) +@Auth(["i18n:btn:update"]) +async def update_i18n(request: Request, params: AddI18nParams, id: str = Path(description="国际化内容ID")): + if i18n := await I18n.get_or_none(id=id, del_flag=1): + locale = await Locale.get_or_none(id=params.locale_id, del_flag=1) i18n.key = params.key i18n.translation = params.translation i18n.locale = locale @@ -173,8 +212,9 @@ async def update_i18n(request: Request, params: AddI18nParams, id: str = Path(de @i18nAPI.get("/info/{id}", response_class=JSONResponse, response_model=GetI18nInfoResponse, summary="获取国际化内容信息") @Log(title="获取国际化内容信息", business_type=BusinessType.SELECT) +@Auth(["i18n:btn:info"]) async def get_i18n_info(request: Request, id: str = Path(description="国际化内容ID")): - if i18n := await I18n.get_or_none(id=id): + if i18n := await I18n.get_or_none(id=id, del_flag=1): i18n = { "id": i18n.id, "key": i18n.key, @@ -193,6 +233,7 @@ async def get_i18n_info(request: Request, id: str = Path(description="国际化 @i18nAPI.get("/list", response_class=JSONResponse, response_model=GetI18nListResponse, summary="获取国际化内容列表") @Log(title="获取国际化内容列表", business_type=BusinessType.SELECT) +@Auth(["i18n:btn:list"]) async def get_i18n_list(request: Request, page: int = Query(default=1, description="当前页码"), pageSize: int = Query(default=50, description="每页条数"), @@ -207,8 +248,8 @@ async def get_i18n_list(request: Request, 'translation': translation }.items() if v } - total = await I18n.filter(**filterArgs).count() - data = await I18n.filter(**filterArgs).offset((page - 1) * pageSize).limit(pageSize).values( + total = await I18n.filter(**filterArgs, del_flag=1).count() + data = await I18n.filter(**filterArgs, del_flag=1).offset((page - 1) * pageSize).limit(pageSize).values( id="id", key="key", translation="translation", @@ -229,14 +270,11 @@ async def get_i18n_list(request: Request, @i18nAPI.get("/infoList/{id}", response_class=JSONResponse, response_model=GetI18nInfoListResponse, summary="获取国际化列表") -# @Log(title="获取国际化列表", business_type=BusinessType.SELECT) +@Log(title="获取国际化列表", business_type=BusinessType.SELECT) +@Auth(["i18n:btn:infoList"]) async def get_i18n_info_list(request: Request, id: str = Path(description="国际化内容语言ID")): - if locale := await Locale.get_or_none(id=id): - result = await request.app.state.redis.get(f'{RedisKeyConfig.TRANSLATION_INFO.key}:{id}') - if result: - result = eval(result) - return Response.success(data=result) - data = await I18n.filter(locale_id=locale.id).values( + if locale := await Locale.get_or_none(id=id, del_flag=1): + data = await I18n.filter(locale_id=locale.id, del_flag=1).order_by("key").values( id="id", key="key", translation="translation", @@ -250,13 +288,6 @@ async def get_i18n_info_list(request: Request, id: str = Path(description="国 result = {} for i18n in data: result[f"{i18n['key']}"] = i18n["translation"] - await request.app.state.redis.set(f'{RedisKeyConfig.TRANSLATION_INFO.key}:{id}', - str(jsonable_encoder({ - "data": result, - "locale": locale.code, - "name": locale.name, - })), - ex=timedelta(minutes=60)) return Response.success(data={ "data": result, "locale": locale.code, diff --git a/api/log.py b/api/log.py index 1df6454..18026d4 100644 --- a/api/log.py +++ b/api/log.py @@ -5,6 +5,8 @@ # @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 @@ -21,21 +23,45 @@ from utils.response import Response logAPI = APIRouter( prefix="/log", - dependencies=[Depends(LoginController.get_current_user)] ) @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), ): - online_user_list = await LoginController.get_online_user(request) + sub_departments = current_user.get("sub_departments") + online_user_list = await LoginController.get_online_user(request, sub_departments) online_user_list = 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( + filter(lambda x: x["department_id"] in sub_departments, jsonable_encoder(online_user_list))) + 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 not department_id: + filterArgs['user__department__id__in'] = sub_departments + else: + 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", @@ -58,67 +84,113 @@ async def get_login_log(request: Request, if item["session_id"] == log["session_id"]: log["online"] = True return Response.success(data={ - "total": await LoginLog.filter(user_id=user_id).count(), + "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=["user:btn:logout"]) +@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)): - if await LoginLog.get_or_none(user_id=current_user.get("id"), session_id=id): - await request.app.state.redis.delete(f"{RedisKeyConfig.ACCESS_TOKEN.key}:{id}") - return Response.success(msg="强退成功!") + 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(id: str = Path(..., description="登录日志ID"), +async def delete_login_log(request: Request, id: str = Path(..., description="登录日志ID"), current_user: dict = Depends(LoginController.get_current_user)): - if log := await LoginLog.get_or_none(id=id): - if log.user == current_user.get("id"): - log.del_flag = 0 - await log.save() - return Response.success(msg="删除成功") - else: - return Response.failure(msg="无权限删除") + 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="用户删除登录日志") + summary="用户批量删除登录日志") @logAPI.post("/deleteList/login", response_model=BaseResponse, response_class=JSONResponse, - summary="用户删除登录日志") + summary="用户批量删除登录日志") @Log(title="用户批量删除登录日志", business_type=BusinessType.DELETE) @Auth(permission_list=["login:btn:delete"]) -async def delete_login_log(params: DeleteListParams, +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): - if log.user == current_user.get("id"): - log.del_flag = 0 - await log.save() + 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), ): - user_id = current_user.get("id") - result = await OperationLog.filter(operator_id=user_id, del_flag=1).offset((page - 1) * pageSize).limit( + sub_departments = current_user.get("sub_departments") + 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 not department_id: + filterArgs['department__id__in'] = sub_departments + else: + 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", @@ -142,9 +214,10 @@ async def get_operation_log(request: Request, cost_time="cost_time" ) return Response.success(data={ - "total": await OperationLog.filter(operator_id=user_id).count(), + "total": await OperationLog.filter(**filterArgs, del_flag=1, operator__del_flag=1).count(), "result": result, "page": page, + "pageSize": pageSize }) @@ -154,30 +227,28 @@ async def get_operation_log(request: Request, summary="用户删除操作日志") @Log(title="用户删除操作日志", business_type=BusinessType.DELETE) @Auth(permission_list=["operation:btn:delete"]) -async def delete_operation_log(id: str = Path(..., description="操作日志id"), +async def delete_operation_log(request: Request, id: str = Path(..., description="操作日志id"), current_user: dict = Depends(LoginController.get_current_user)): - if log := await OperationLog.get_or_none(id=id): - if log.operator == current_user.get("id"): - log.del_flag = 0 - await log.save() - return Response.success(msg="删除成功") - else: - return Response.failure(msg="无权限删除") + 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="用户删除操作日志") + summary="用户批量删除操作日志") @logAPI.post("/deleteList/operation", response_model=BaseResponse, response_class=JSONResponse, - summary="用户删除操作日志") + summary="用户批量删除操作日志") @Log(title="用户批量删除操作日志", business_type=BusinessType.DELETE) @Auth(permission_list=["operation:btn:delete"]) -async def delete_operation_log(params: DeleteListParams, +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): - if log.operator == current_user.get("id"): - log.del_flag = 0 - await log.save() + 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="删除成功") diff --git a/api/login.py b/api/login.py index 3c2c3ff..af91477 100644 --- a/api/login.py +++ b/api/login.py @@ -18,7 +18,7 @@ from config.constant import BusinessType from config.constant import RedisKeyConfig from controller.login import CustomOAuth2PasswordRequestForm, LoginController from controller.query import QueryController -from models import Department, User +from models import Department, User, Role, UserRole from schemas.common import BaseResponse from schemas.login import LoginParams, GetUserInfoResponse, LoginResponse, GetCaptchaResponse, GetEmailCodeParams, \ ResetPasswordParams @@ -94,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}:register_enabled') + if await request.app.state.redis.get(f'{RedisKeyConfig.SYSTEM_CONFIG.key}:account_register_enabled') == 'true' else False ) @@ -106,7 +106,14 @@ async def register(request: Request, params: RegisterUserParams): if await QueryController.register_user_before(username=params.username, phone=params.phone, email=params.email): return Response.error(msg="注册失败,用户已存在!") params.password = await Password.get_password_hash(input_password=params.password) + # 默认分配注册用户 + userRole = await Role.get_or_none(department__name="注册用户", code="user", del_flag=1).values( + department_id="department__id", id="id") + if not params.department_id: + params.department_id = userRole.get("department_id", "") department = await Department.get_or_none(id=params.department_id) + userRole = await Role.get_or_none(department__id=department.id, code="user", del_flag=1).values(id="id") + print(userRole) user = await User.create( username=params.username, password=params.password, @@ -118,6 +125,11 @@ async def register(request: Request, params: RegisterUserParams): status=params.status, ) if user: + # 默认分配普通用户角色 + await UserRole.create( + user_id=user.id, + role_id=userRole.get("id", ""), + ) userParams = LoginParams( username=params.username, password=params.password @@ -140,7 +152,7 @@ async def register(request: Request, params: RegisterUserParams): result.pop("session_id") result.pop("userInfo") return Response.success(msg="注册成功!", data=result) - return Response.error(msg="注册成功!") + return Response.success(msg="注册成功!") else: return Response.error(msg="注册失败!") @@ -221,12 +233,13 @@ async def info( @loginAPI.get("/getRoutes", response_class=JSONResponse, summary="获取路由信息") -# @Log(title="获取路由信息", business_type=BusinessType.SELECT) +@Log(title="获取路由信息", business_type=BusinessType.SELECT) async def get_routes(request: Request, current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") routes = await request.app.state.redis.get(f'{RedisKeyConfig.USER_ROUTES.key}:{current_user["id"]}') if routes: return Response.success(data=eval(routes)) - routes = await LoginController.get_user_routes(current_user["id"]) + routes = await LoginController.get_user_routes(current_user["id"], sub_departments=sub_departments) userRoutes = str(jsonable_encoder(routes)) await request.app.state.redis.set( f'{RedisKeyConfig.USER_ROUTES.key}:{current_user["id"]}', diff --git a/api/permission.py b/api/permission.py index b73d099..853fc2c 100644 --- a/api/permission.py +++ b/api/permission.py @@ -10,8 +10,9 @@ from typing import Optional from fastapi import APIRouter, Depends, Path, Query, Request from fastapi.responses import JSONResponse +from annotation.auth import Auth from annotation.log import Log -from config.constant import BusinessType +from config.constant import BusinessType, RedisKeyConfig from controller.login import LoginController from models import Permission from schemas.common import BaseResponse @@ -26,6 +27,7 @@ permissionAPI = APIRouter( @permissionAPI.post("/add", response_model=BaseResponse, response_class=JSONResponse, summary="新增权限") @Log(title="新增权限", business_type=BusinessType.INSERT) +@Auth(permission_list=["permission:btn:add"]) async def add_permission(request: Request, params: AddPermissionParams): permission = await Permission.create( name=params.name, @@ -51,6 +53,14 @@ async def add_permission(request: Request, params: AddPermissionParams): hidden_tag=params.hidden_tag, ) if permission: + # 更新用户信息缓存 + userInfos = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_INFO.key}*') + if userInfos: + await request.app.state.redis.delete(*userInfos) + # 更新用户路由缓存 + userRoutes = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_ROUTES.key}*') + if userRoutes: + await request.app.state.redis.delete(*userRoutes) return Response.success(msg="新增权限成功!") else: return Response.error(msg="新增权限失败!") @@ -59,9 +69,19 @@ async def add_permission(request: Request, params: AddPermissionParams): @permissionAPI.delete("/delete/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="删除权限") @permissionAPI.post("/delete/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="删除权限") @Log(title="删除权限", business_type=BusinessType.DELETE) +@Auth(permission_list=["permission:btn:delete"]) async def delete_permission(request: Request, id: str = Path(description="权限ID")): - if permission := await Permission.get_or_none(id=id): - await permission.delete() + if permission := await Permission.get_or_none(id=id, del_flag=1): + permission.del_flag = 0 + await permission.save() + # 更新用户信息缓存 + userInfos = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_INFO.key}*') + if userInfos: + await request.app.state.redis.delete(*userInfos) + # 更新用户路由缓存 + userRoutes = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_ROUTES.key}*') + if userRoutes: + await request.app.state.redis.delete(*userRoutes) return Response.success(msg="删除权限成功!") else: return Response.error(msg="删除权限失败,权限不存在!") @@ -70,8 +90,9 @@ async def delete_permission(request: Request, id: str = Path(description="权限 @permissionAPI.put("/update/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="更新权限") @permissionAPI.post("/update/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="更新权限") @Log(title="更新权限", business_type=BusinessType.UPDATE) +@Auth(permission_list=["permission:btn:update"]) async def update_permission(request: Request, params: AddPermissionParams, id: str = Path(description="权限ID"), ): - if permission := await Permission.get_or_none(id=id): + if permission := await Permission.get_or_none(id=id, del_flag=1): permission.name = params.name permission.parent_id = params.parent_id permission.path = params.path @@ -94,6 +115,14 @@ async def update_permission(request: Request, params: AddPermissionParams, id: s permission.fixed_tag = params.fixed_tag permission.hidden_tag = params.hidden_tag await permission.save() + # 更新用户信息缓存 + userInfos = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_INFO.key}*') + if userInfos: + await request.app.state.redis.delete(*userInfos) + # 更新用户路由缓存 + userRoutes = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_ROUTES.key}*') + if userRoutes: + await request.app.state.redis.delete(*userRoutes) return Response.success(msg="更新权限成功!") else: return Response.error(msg="更新权限失败,权限不存在!") @@ -102,8 +131,9 @@ async def update_permission(request: Request, params: AddPermissionParams, id: s @permissionAPI.get("/info/{id}", response_model=GetPermissionInfoResponse, response_class=JSONResponse, summary="查询权限详情") @Log(title="查询权限详情", business_type=BusinessType.SELECT) +@Auth(permission_list=["permission:btn:info"]) async def get_permission(request: Request, id: str = Path(description="权限ID")): - if permission := await Permission.get_or_none(permission_id=id): + if permission := await Permission.get_or_none(permission_id=id, del_flag=1): permission = await permission.first().values( id="id", create_by="create_by", @@ -140,6 +170,7 @@ async def get_permission(request: Request, id: str = Path(description="权限ID" @permissionAPI.get("/list", response_model=GetPermissionListResponse, response_class=JSONResponse, summary="查询权限列表") @Log(title="查询权限列表", business_type=BusinessType.SELECT) +@Auth(permission_list=["permission:btn:list"]) async def get_permission_list( request: Request, page: int = Query(default=1, description="当前页码"), @@ -191,8 +222,8 @@ async def get_permission_list( "hidden_tag": hiddenTag }.items() if v } - total = await Permission.filter(**filterArgs).count() - result = await Permission.filter(**filterArgs).offset((page - 1) * pageSize).limit(pageSize).order_by( + total = await Permission.filter(**filterArgs, del_flag=1).count() + result = await Permission.filter(**filterArgs, del_flag=1).offset((page - 1) * pageSize).limit(pageSize).order_by( 'rank').values( id="id", create_by="create_by", diff --git a/api/role.py b/api/role.py index a64a4f8..4e2f7c6 100644 --- a/api/role.py +++ b/api/role.py @@ -10,27 +10,32 @@ from typing import Optional from fastapi import APIRouter, Depends, Path, Query, Request from fastapi.responses import JSONResponse +from annotation.auth import Auth from annotation.log import Log -from config.constant import BusinessType +from config.constant import BusinessType, RedisKeyConfig from controller.login import LoginController from models import Role, Permission, RolePermission, Department -from schemas.common import BaseResponse +from schemas.common import BaseResponse, DeleteListParams from schemas.role import AddRoleParams, AddRolePermissionParams, GetRolePermissionInfoResponse, \ GetRolePermissionListResponse from utils.common import filterKeyValues from utils.response import Response roleAPI = APIRouter( - prefix="/role", - dependencies=[Depends(LoginController.get_current_user)] + prefix="/role" ) @roleAPI.post("/add", response_model=BaseResponse, response_class=JSONResponse, summary="新增角色") @Log(title="新增角色", business_type=BusinessType.INSERT) -async def add_role(request: Request, params: AddRoleParams): - if await Role.get_or_none(code=params.role_code, department_id=params.department_id, del_flag=1): +@Auth(permission_list=["role:btn:add"]) +async def add_role(request: Request, params: AddRoleParams, + current_user: dict = Depends(LoginController.get_current_user)): + if await Role.get_or_none(code=params.code, department_id=params.department_id, del_flag=1): return Response.error(msg="角色编码已存在!") + sub_departments = current_user.get("sub_departments") + if params.department_id not in sub_departments: + return Response.error(msg="新增失败,无权限!") department = await Department.get_or_none(id=params.department_id, del_flag=1) if department: role = await Role.create( @@ -42,13 +47,21 @@ async def add_role(request: Request, params: AddRoleParams): ) else: role = await Role.create( - code=params.role_code, - name=params.role_name, + code=params.code, + name=params.name, status=params.status, - description=params.role_description, + description=params.description, department_id=None, ) if role: + # 更新用户信息缓存 + userInfos = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_INFO.key}*') + if userInfos: + await request.app.state.redis.delete(*userInfos) + # 更新用户路由缓存 + userRoutes = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_ROUTES.key}*') + if userRoutes: + await request.app.state.redis.delete(*userRoutes) return Response.success(msg="新增角色成功!") return Response.error(msg="新增角色失败!") @@ -56,19 +69,60 @@ async def add_role(request: Request, params: AddRoleParams): @roleAPI.delete("/delete/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="删除角色") @roleAPI.post("/delete/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="删除角色") @Log(title="删除角色", business_type=BusinessType.DELETE) -async def delete_role(request: Request, id: int = Path(..., description="角色ID")): - if role := await Role.get_or_none(id=id, del_flag=1): +@Auth(permission_list=["role:btn:delete"]) +async def delete_role(request: Request, id: int = Path(..., description="角色ID"), + current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") + if role := await Role.get_or_none(id=id, department__id__in=sub_departments, del_flag=1): # 移除相应角色权限 - await RolePermission.filter(role_id=role.id).delete() - await role.delete() + await RolePermission.filter(role_id=role.id, del_flag=1).update(del_flag=0) + role.del_flag = 0 + await role.save() + # 更新用户信息缓存 + userInfos = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_INFO.key}*') + if userInfos: + await request.app.state.redis.delete(*userInfos) + # 更新用户路由缓存 + userRoutes = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_ROUTES.key}*') + if userRoutes: + await request.app.state.redis.delete(*userRoutes) return Response.success(msg="删除角色成功!") return Response.error(msg="删除角色失败!") +@roleAPI.delete("/deleteList", response_model=BaseResponse, response_class=JSONResponse, summary="批量删除角色") +@roleAPI.post("/deleteList", response_model=BaseResponse, response_class=JSONResponse, summary="批量删除角色") +@Log(title="批量删除角色", business_type=BusinessType.DELETE) +@Auth(permission_list=["role:btn:delete"]) +async def delete_role_list(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 role := await Role.get_or_none(id=id, del_flag=1, department__id__in=sub_departments): + # 移除相应角色权限 + await RolePermission.filter(role_id=role.id, del_flag=1).update(del_flag=0) + role.del_flag = 0 + await role.save() + # 更新用户信息缓存 + userInfos = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_INFO.key}*') + if userInfos: + await request.app.state.redis.delete(*userInfos) + # 更新用户路由缓存 + userRoutes = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_ROUTES.key}*') + if userRoutes: + await request.app.state.redis.delete(*userRoutes) + return Response.success(msg="批量删除角色成功!") + + @roleAPI.put("/update/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="修改角色") @roleAPI.post("/update/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="修改角色") @Log(title="修改角色", business_type=BusinessType.UPDATE) -async def update_role(request: Request, params: AddRoleParams, id: str = Path(..., description="角色ID")): +@Auth(permission_list=["role:btn:update"]) +async def update_role(request: Request, params: AddRoleParams, id: str = Path(..., description="角色ID"), + current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") + if params.department_id not in sub_departments: + return Response.error(msg="修改失败,无权限!") if role := await Role.get_or_none(id=id, del_flag=1): role.code = params.code role.name = params.name @@ -80,14 +134,25 @@ async def update_role(request: Request, params: AddRoleParams, id: str = Path(.. else: role.department_id = None await role.save() + # 更新用户信息缓存 + userInfos = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_INFO.key}*') + if userInfos: + await request.app.state.redis.delete(*userInfos) + # 更新用户路由缓存 + userRoutes = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_ROUTES.key}*') + if userRoutes: + await request.app.state.redis.delete(*userRoutes) return Response.success(msg="修改角色成功!") return Response.error(msg="修改角色失败!") @roleAPI.get("/info/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="查询角色详情") @Log(title="查询角色详情", business_type=BusinessType.SELECT) -async def get_role_info(request: Request, id: int = Path(..., description="角色ID")): - if role := await Role.get_or_none(id=id, del_flag=1).values( +@Auth(permission_list=["role:btn:info"]) +async def get_role_info(request: Request, id: int = Path(..., description="角色ID"), + current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") + if role := await Role.get_or_none(id=id, del_flag=1, department__id__in=sub_departments).values( id="id", create_by="create_by", create_time="create_time", @@ -109,6 +174,7 @@ async def get_role_info(request: Request, id: int = Path(..., description="角 @roleAPI.get("/list", response_model=BaseResponse, response_class=JSONResponse, summary="查询角色列表") @Log(title="查询角色列表", business_type=BusinessType.SELECT) +@Auth(permission_list=["role:btn:list"]) async def get_role_list( request: Request, page: int = Query(1, description="页码"), @@ -128,76 +194,12 @@ async def get_role_list( "status": status }.items() if v } - # 如果未提供 department_id,则使用当前用户的部门 ID if not department_id: - department_id = current_user.get("department_id") - - # 查询当前部门及其下属部门的角色 - all_roles = await get_role_and_subroles(department_id, filterArgs) - - # 分页处理 - total = len(all_roles) - paginated_roles = all_roles[(page - 1) * pageSize: page * pageSize] - - return Response.success(data={ - "result": paginated_roles, - "total": total, - "page": page - }) - - -async def get_department_and_subdepartments(department_id: str, visited: set = None): - """ - 递归查询当前部门及其所有下属部门的 ID。 - - :param department_id: 当前部门 ID - :param visited: 已访问的部门 ID 集合,用于避免循环依赖 - :return: 部门 ID 列表 - """ - if visited is None: - visited = set() # 初始化已访问的部门 ID 集合 - - # 如果当前部门 ID 已经访问过,直接返回空列表,避免死循环 - if department_id in visited: - return [] - - visited.add(department_id) # 标记当前部门 ID 为已访问 - - # 查询当前部门 - current_department = await Department.filter( - id=department_id - ).values_list("id", flat=True) - - # 查询直接子部门 - sub_departments = await Department.filter( - parent_id=department_id - ).values_list("id", flat=True) - - # 递归查询子部门的子部门 - for sub_department_id in sub_departments[:]: # 使用切片复制避免修改迭代中的列表 - sub_sub_departments = await get_department_and_subdepartments(sub_department_id, visited) - sub_departments.extend(sub_sub_departments) - - # 合并当前部门和所有下属部门的 ID - return current_department + sub_departments - - -async def get_role_and_subroles(department_id: str, filterArgs: dict): - """ - 查询当前部门及其下属部门的角色。 - - :param department_id: 当前部门 ID - :param filterArgs: 过滤条件 - :return: 角色列表 - """ - # 递归查询当前部门及其下属部门的 ID - department_ids = await get_department_and_subdepartments(department_id) - - # 查询这些部门的角色 - roles = await Role.filter( - department__id__in=department_ids, - **filterArgs - ).values( + filterArgs["department__id__in"] = current_user.get("sub_departments") + total = await Role.filter(**filterArgs, del_flag=1).count() + data = await Role.filter(**filterArgs, del_flag=1).offset( + (page - 1) * pageSize).limit( + pageSize).values( id="id", create_by="create_by", create_time="create_time", @@ -214,24 +216,24 @@ async def get_role_and_subroles(department_id: str, filterArgs: dict): department_email="department__email", ) - # 根据 id 去重 - unique_roles = [] - seen_ids = set() # 用于记录已经处理过的角色 ID - for role in roles: - if role["id"] not in seen_ids: - unique_roles.append(role) - seen_ids.add(role["id"]) - - return unique_roles + return Response.success(data={ + "result": data, + "total": total, + "page": page, + "pageSize": pageSize + }) @roleAPI.post("/addPermission", response_model=BaseResponse, response_class=JSONResponse, summary="新增角色权限") @Log(title="新增角色权限", business_type=BusinessType.INSERT) +@Auth(permission_list=["role:btn:addPermission"]) async def add_role_permission(request: Request, params: AddRolePermissionParams, - id: str = Path(..., description="角色ID")): - if role := await Role.get_or_none(id=id, del_flag=1): + id: str = Path(..., description="角色ID"), + current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") + if role := await Role.get_or_none(id=id, del_flag=1, department__id__in=sub_departments): # 已有角色权限 - rolePermissions = await RolePermission.filter(role_id=id).all().values("permission_id") + rolePermissions = await RolePermission.filter(role_id=id, del_flag=1).values("permission_id") rolePermissions = await filterKeyValues(rolePermissions, "permission_id") # 利用集合筛选出角色权限中不存在的权限 add_list = set(params.permission_ids).difference(set(rolePermissions)) @@ -243,6 +245,14 @@ async def add_role_permission(request: Request, params: AddRolePermissionParams, role_id=role.id, permission_id=permission.id ) + # 更新用户信息缓存 + userInfos = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_INFO.key}*') + if userInfos: + await request.app.state.redis.delete(*userInfos) + # 更新用户路由缓存 + userRoutes = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_ROUTES.key}*') + if userRoutes: + await request.app.state.redis.delete(*userRoutes) return Response.success(msg="新增角色权限成功!") return Response.error(msg="新增角色权限失败!") @@ -252,9 +262,21 @@ async def add_role_permission(request: Request, params: AddRolePermissionParams, @roleAPI.post("/deletePermission/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="删除角色权限") @Log(title="删除角色权限", business_type=BusinessType.DELETE) -async def delete_role_permission(request: Request, id: int = Path(..., description="角色权限ID")): - if rolePermission := await RolePermission.get_or_none(id=id, del_flag=1): - await rolePermission.delete() +@Auth(permission_list=["role:btn:deletePermission"]) +async def delete_role_permission(request: Request, id: int = Path(..., description="角色权限ID"), + current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") + if rolePermission := await RolePermission.get_or_none(id=id, del_flag=1, role__department__id__in=sub_departments): + rolePermission.del_flag = 0 + await rolePermission.save() + # 更新用户信息缓存 + userInfos = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_INFO.key}*') + if userInfos: + await request.app.state.redis.delete(*userInfos) + # 更新用户路由缓存 + userRoutes = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_ROUTES.key}*') + if userRoutes: + await request.app.state.redis.delete(*userRoutes) return Response.success(msg="删除角色权限成功!") return Response.error(msg="删除角色权限失败!") @@ -263,11 +285,14 @@ async def delete_role_permission(request: Request, id: int = Path(..., descripti @roleAPI.post("/updatePermission/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="修改角色权限") @Log(title="修改角色权限", business_type=BusinessType.UPDATE) +@Auth(permission_list=["role:btn:updatePermission"]) async def update_role_permission(request: Request, params: AddRolePermissionParams, - id: str = Path(..., description="角色ID")): - if role := await Role.get_or_none(id=id, del_flag=1): + id: str = Path(..., description="角色ID"), + current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") + if role := await Role.get_or_none(id=id, del_flag=1, department__id__in=sub_departments): # 已有角色权限 - rolePermissions = await RolePermission.filter(role_id=role.id).all().values("permission_id") + rolePermissions = await RolePermission.filter(role_id=role.id, del_flag=1).values("permission_id") rolePermissions = await filterKeyValues(rolePermissions, "permission_id") # 利用集合筛选出角色权限中不存在的权限 delete_list = set(rolePermissions).difference(set(params.permission_ids)) @@ -275,10 +300,18 @@ async def update_role_permission(request: Request, params: AddRolePermissionPara add_list = set(params.permission_ids).difference(set(rolePermissions)) # 循环删除角色权限 for item in delete_list: - await RolePermission.filter(role_id=id, permission_id=item).delete() + await RolePermission.filter(role_id=id, permission_id=item, del_flag=1).update(del_flag=0) # 循环添加角色权限 for item in add_list: await RolePermission.create(role_id=id, permission_id=item) + # 更新用户信息缓存 + userInfos = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_INFO.key}*') + if userInfos: + await request.app.state.redis.delete(*userInfos) + # 更新用户路由缓存 + userRoutes = await request.app.state.redis.keys(f'{RedisKeyConfig.USER_ROUTES.key}*') + if userRoutes: + await request.app.state.redis.delete(*userRoutes) return Response.success(msg="修改角色权限成功!") return Response.error(msg="修改角色权限失败!") @@ -286,8 +319,11 @@ async def update_role_permission(request: Request, params: AddRolePermissionPara @roleAPI.get("/permissionInfo/{id}", response_model=GetRolePermissionInfoResponse, response_class=JSONResponse, summary="获取角色权限信息") @Log(title="获取角色权限信息", business_type=BusinessType.SELECT) -async def get_role_permission_info(request: Request, id: int = Path(..., description="角色权限ID")): - if rolePermission := await RolePermission.get_or_none(id=id, del_flag=1): +@Auth(permission_list=["role:btn:permissionInfo"]) +async def get_role_permission_info(request: Request, id: int = Path(..., description="角色权限ID"), + current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") + if rolePermission := await RolePermission.get_or_none(id=id, del_flag=1, role__department__id__in=sub_departments): data = await rolePermission.first().values( id="id", create_by="create_by", @@ -309,9 +345,12 @@ async def get_role_permission_info(request: Request, id: int = Path(..., descrip @roleAPI.get("/permissionList/{id}", response_model=GetRolePermissionListResponse, response_class=JSONResponse, summary="获取角色权限列表") @Log(title="获取角色权限列表", business_type=BusinessType.SELECT) -async def get_role_permission_list(request: Request, id: str = Path(..., description="角色ID")): - total = await RolePermission.filter(role_id=id).count() - data = await RolePermission.filter(role_id=id).values( +@Auth(permission_list=["role:btn:permissionList"]) +async def get_role_permission_list(request: Request, id: str = Path(..., description="角色ID"), + current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") + total = await RolePermission.filter(role_id=id, role__department__id__in=sub_departments, del_flag=1).count() + data = await RolePermission.filter(role_id=id, role__department__id__in=sub_departments, del_flag=1).values( id="id", create_by="create_by", create_time="create_time", @@ -329,5 +368,6 @@ async def get_role_permission_list(request: Request, id: str = Path(..., descrip return Response.success(data={ "result": data, "total": total, - "page": 1 + "page": 1, + "pageSize": 9999 }) diff --git a/api/server.py b/api/server.py index d398ba5..d0ae2fd 100644 --- a/api/server.py +++ b/api/server.py @@ -14,12 +14,14 @@ import psutil from fastapi import APIRouter, Depends, Request from fastapi.responses import JSONResponse +from annotation.auth import Auth from annotation.log import Log from config.constant import BusinessType from controller.login import LoginController from schemas.server import GetServerInfoResponse, CpuInfo, MemoryInfo, SystemInfo, PythonInfo, SystemFiles, \ GetSystemInfoResult from utils.common import bytes2human +from utils.log import logger from utils.response import Response serverAPI = APIRouter( @@ -30,6 +32,7 @@ serverAPI = APIRouter( @serverAPI.get("", response_class=JSONResponse, response_model=GetServerInfoResponse, summary="获取服务器信息") @Log(title="获取服务器信息", business_type=BusinessType.SELECT) +@Auth(permission_list=["server:btn:info"]) async def get_server_info(request: Request): # CPU信息 # 获取CPU总核心数 @@ -96,17 +99,21 @@ async def get_server_info(request: Request): io = psutil.disk_partitions() sys_files = [] for i in io: - o = psutil.disk_usage(i.device) - disk_data = SystemFiles( - dirName=i.device, - sysTypeName=i.fstype, - typeName='本地固定磁盘(' + i.mountpoint.replace('\\', '') + ')', - total=bytes2human(o.total), - used=bytes2human(o.used), - free=bytes2human(o.free), - usage=f'{psutil.disk_usage(i.device).percent}%', - ) - sys_files.append(disk_data) + try: + o = psutil.disk_usage(i.device) + disk_data = SystemFiles( + dirName=i.device, + sysTypeName=i.fstype, + typeName='本地固定磁盘(' + i.mountpoint.replace('\\', '') + ')', + total=bytes2human(o.total), + used=bytes2human(o.used), + free=bytes2human(o.free), + usage=f'{psutil.disk_usage(i.device).percent}%', + ) + sys_files.append(disk_data) + except Exception as e: + logger.error(f"获取磁盘信息失败:{e}") + continue result = GetSystemInfoResult(cpu=cpu, memory=mem, system=sys, python=py, systemFiles=sys_files) return Response.success(data=result) diff --git a/api/user.py b/api/user.py index ef84923..cd15295 100644 --- a/api/user.py +++ b/api/user.py @@ -15,15 +15,16 @@ from fastapi.responses import JSONResponse from annotation.auth import Auth from annotation.log import Log -from config.constant import BusinessType +from config.constant import BusinessType, RedisKeyConfig from config.env import UploadConfig 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, QueryCode +from models import Role, Department, OperationLog, LoginLog +from models.code import QueryCode from models.user import User, UserRole -from schemas.common import BaseResponse +from schemas.common import BaseResponse, DeleteListParams from schemas.department import GetDepartmentListResponse from schemas.file import UploadFileResponse from schemas.user import AddUserParams, GetUserListResponse, GetUserInfoResponse, UpdateUserParams, \ @@ -33,19 +34,21 @@ from utils.common import filterKeyValues from utils.password import Password from utils.response import Response -userAPI = APIRouter(prefix="/user", dependencies=[Depends(LoginController.get_current_user)]) +userAPI = APIRouter(prefix="/user") @userAPI.post("/add", response_class=JSONResponse, response_model=BaseResponse, summary="新增用户") @Log(title="新增用户", business_type=BusinessType.INSERT) +@Auth(["user:btn:addUser"]) async def add_user( request: Request, - params: AddUserParams + params: AddUserParams, + current_user: dict = Depends(LoginController.get_current_user) ): if await QueryController.register_user_before(username=params.username, phone=params.phone, email=params.email): return Response.error(msg="添加失败,用户已存在!") params.password = await Password.get_password_hash(input_password=params.password) - department = await Department.get_or_none(id=params.department_id) + department = await Department.get_or_none(id=params.department_id, del_flag=1) user = await User.create( username=params.username, password=params.password, @@ -65,35 +68,75 @@ async def add_user( @userAPI.delete("/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除用户") @userAPI.post("/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除用户") @Log(title="删除用户", business_type=BusinessType.DELETE) +@Auth(["user:btn:deleteUser"]) async def delete_user( request: Request, - id: str = Path(..., description="用户ID")): - if user := await User.get_or_none(id=id): - await user.delete() + id: str = Path(..., description="用户ID"), + current_user: dict = Depends(LoginController.get_current_user) +): + sub_departments = current_user.get("sub_departments") + if user := await User.get_or_none(id=id, department__id__in=sub_departments, del_flag=1): + user.del_flag = 0 + await user.save() + # 移除用户角色 + await UserRole.filter(user_id=user.id, del_flag=1).update(del_flag=0) + # 移除用户登录日志 + await LoginLog.filter(user_id=user.id, del_flag=1).update(del_flag=0) + # 移除用户操作日志 + await OperationLog.filter(user_id=user.id, del_flag=1).update(del_flag=0) + # 更新用户信息缓存 + if await request.app.state.redis.get(f'{RedisKeyConfig.USER_INFO.key}:{id}'): + await request.app.state.redis.delete(f'{RedisKeyConfig.USER_INFO.key}:{id}') + # 更新用户路由缓存 + if await request.app.state.redis.get(f'{RedisKeyConfig.USER_ROUTES.key}:{id}'): + await request.app.state.redis.delete(f'{RedisKeyConfig.USER_ROUTES.key}:{id}') return Response.success(msg="删除成功!") else: return Response.error(msg="删除失败,用户不存在!") +@userAPI.delete("/deleteUserList", response_class=JSONResponse, response_model=BaseResponse, summary="批量删除用户") +@userAPI.post("/deleteUserList", response_class=JSONResponse, response_model=BaseResponse, summary="批量删除用户") +@Log(title="批量删除用户", business_type=BusinessType.DELETE) +@Auth(["user:btn:deleteUser"]) +async def delete_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 user := await User.get_or_none(id=id, department__id__in=sub_departments, del_flag=1): + user.del_flag = 0 + await user.save() + return Response.success(msg="删除成功!") + + @userAPI.put("/update/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="更新用户") @userAPI.post("/update/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="更新用户") @Log(title="更新用户", business_type=BusinessType.UPDATE) +@Auth(["user:btn:updateUser"]) async def update_user( request: Request, params: UpdateUserParams, - id: str = Path(..., description="用户ID")): - if user := await User.get_or_none(id=id): + id: str = Path(..., description="用户ID"), + current_user: dict = Depends(LoginController.get_current_user) +): + sub_departments = current_user.get("sub_departments") + if user := await User.get_or_none(id=id, department__id__in=sub_departments, del_flag=1): user.username = params.username user.nickname = params.nickname user.phone = params.phone user.email = params.email user.gender = params.gender user.status = params.status - if department := await Department.get_or_none(id=params.department_id): + if department := await Department.get_or_none(id=params.department_id, del_flag=1): user.department = department else: user.department = None await user.save() + if await request.app.state.redis.get(f'{RedisKeyConfig.USER_INFO.key}:{id}'): + await request.app.state.redis.delete(f'{RedisKeyConfig.USER_INFO.key}:{id}') return Response.success(msg="更新成功!") else: return Response.error(msg="更新失败,用户不存在!") @@ -101,8 +144,10 @@ async def update_user( @userAPI.get("/info/{id}", response_class=JSONResponse, response_model=GetUserInfoResponse, summary="获取用户信息") @Log(title="获取用户信息", business_type=BusinessType.SELECT) -async def get_user_info(request: Request, id: str = Path(..., description="用户ID")): - if user := await User.get_or_none(id=id): +@Auth(["user:btn:Userinfo"]) +async def get_user_info(request: Request, id: str = Path(..., description="用户ID"), + current_user: dict = Depends(LoginController.get_current_user)): + if user := await User.get_or_none(id=id, del_flag=1): user = await user.first().values( id="id", create_time="create_time", @@ -113,6 +158,7 @@ async def get_user_info(request: Request, id: str = Path(..., description="用 nickname="nickname", gender="gender", status="status", + avatar="avatar", department_id="department__id", ) return Response.success(data=user) @@ -134,7 +180,9 @@ async def get_user_list( gender: Optional[str] = Query(default=None, description="性别"), status: Optional[str] = Query(default=None, description="状态"), department_id: Optional[str] = Query(default=None, description="部门ID"), + current_user: dict = Depends(LoginController.get_current_user) ): + sub_departments = current_user.get("sub_departments") filterArgs = { f'{k}__contains': v for k, v in { 'username': username, @@ -146,8 +194,10 @@ async def get_user_list( 'department_id': department_id }.items() if v } - total = await User.filter(**filterArgs).count() - result = await User.filter(**filterArgs).offset((page - 1) * pageSize).limit(pageSize).values( + if not department_id: + filterArgs['department_id__in'] = sub_departments + total = await User.filter(**filterArgs, del_flag=1).count() + result = await User.filter(**filterArgs, del_flag=1).offset((page - 1) * pageSize).limit(pageSize).values( id="id", create_time="create_time", update_time="update_time", @@ -155,6 +205,7 @@ async def get_user_list( email="email", phone="phone", nickname="nickname", + avatar="avatar", gender="gender", status="status", department_id="department__id", @@ -162,19 +213,26 @@ async def get_user_list( return Response.success(data={ "result": result, "total": total, - "page": page + "page": page, + "pageSize": pageSize }) @userAPI.post("/addRole", response_model=BaseResponse, response_class=JSONResponse, summary="添加用户角色") @Log(title="添加用户角色", business_type=BusinessType.INSERT) -async def add_user_role(request: Request, params: AddUserRoleParams): - if await UserRole.get_or_none(user_id=params.user_id, role_id=params.role_id, del_flag=1): +@Auth(["user:btn:addRole"]) +async def add_user_role(request: Request, params: AddUserRoleParams, + current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") + if await UserRole.get_or_none(user_id=params.user_id, role_id=params.role_id, del_flag=1, + user__department__id__in=sub_departments): return Response.error(msg="该用户已存在该角色!") - if user := await User.get_or_none(id=params.user_id, del_flag=1): - if role := await Role.get_or_none(id=params.role_id, del_flag=1): + if user := await User.get_or_none(id=params.user_id, del_flag=1, department__id__in=sub_departments): + if role := await Role.get_or_none(id=params.role_id, del_flag=1, department__id__in=sub_departments): userRole = await UserRole.create(user_id=user.id, role_id=role.id) if userRole: + if await request.app.state.redis.get(f'{RedisKeyConfig.USER_INFO.key}:{user.id}'): + await request.app.state.redis.delete(f'{RedisKeyConfig.USER_INFO.key}:{user.id}') return Response.success(msg="添加成功!") else: return Response.error(msg="添加失败!") @@ -189,9 +247,15 @@ async def add_user_role(request: Request, params: AddUserRoleParams): @userAPI.post("/deleteRole/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="删除用户角色") @Log(title="删除用户角色", business_type=BusinessType.DELETE) -async def delete_user_role(request: Request, id: str = Path(description="用户角色ID")): - if userRole := await UserRole.get_or_none(id=id, del_flag=1): - await userRole.delete() +@Auth(["user:btn:deleteRole"]) +async def delete_user_role(request: Request, id: str = Path(description="用户角色ID"), + current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") + if userRole := await UserRole.get_or_none(id=id, del_flag=1, user__department__id__in=sub_departments): + userRole.del_flag = 0 + await userRole.save() + if await request.app.state.redis.get(f'{RedisKeyConfig.USER_INFO.key}:{current_user.get("id")}'): + await request.app.state.redis.delete(f'{RedisKeyConfig.USER_INFO.key}:{current_user.get("id")}') return Response.success(msg="删除成功!") else: return Response.error(msg="删除失败,用户角色不存在!") @@ -201,9 +265,13 @@ async def delete_user_role(request: Request, id: str = Path(description="用户 @userAPI.post("/updateRole", response_model=BaseResponse, response_class=JSONResponse, summary="修改用户角色") @Log(title="修改用户角色", business_type=BusinessType.UPDATE) -async def update_user_role(request: Request, params: UpdateUserRoleParams): +@Auth(["user:btn:updateRole"]) +async def update_user_role(request: Request, params: UpdateUserRoleParams, + current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") # 获取用户已有角色 - userRoles = await UserRole.filter(user_id=params.user_id, del_flag=1).values("role_id") + userRoles = await UserRole.filter(user_id=params.user_id, del_flag=1, + user__department__id__in=sub_departments).values("role_id") userRoles = await filterKeyValues(userRoles, "role_id") # 利用集合找到需要添加的角色 addRoles = set(params.role_ids).difference(set(userRoles)) @@ -211,20 +279,27 @@ async def update_user_role(request: Request, params: UpdateUserRoleParams): deleteRoles = set(userRoles).difference(set(params.role_ids)) # 添加角色 for role_id in addRoles: - if role := await Role.get_or_none(id=role_id, del_flag=1): + if role := await Role.get_or_none(id=role_id, del_flag=1, department__id__in=sub_departments): await UserRole.create(user_id=params.user_id, role_id=role.id) # 删除角色 for role_id in deleteRoles: - if userRole := await UserRole.get_or_none(user_id=params.user_id, role_id=role_id, del_flag=1): - await userRole.delete() + if userRole := await UserRole.get_or_none(user_id=params.user_id, role_id=role_id, del_flag=1, + user__department__id__in=sub_departments): + userRole.del_flag = 0 + await userRole.save() + if await request.app.state.redis.get(f'{RedisKeyConfig.USER_INFO.key}:{params.user_id}'): + await request.app.state.redis.delete(f'{RedisKeyConfig.USER_INFO.key}:{params.user_id}') return Response.success(msg="修改成功!") @userAPI.get("/roleInfo/{id}", response_model=GetUserRoleInfoResponse, response_class=JSONResponse, summary="获取用户角色信息") @Log(title="获取用户角色信息", business_type=BusinessType.SELECT) -async def get_user_role_info(request: Request, id: str = Path(description="用户角色ID")): - if userRole := await UserRole.get_or_none(id=id, del_flag=1): +@Auth(["user:btn:roleInfo"]) +async def get_user_role_info(request: Request, id: str = Path(description="用户角色ID"), + current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") + if userRole := await UserRole.get_or_none(id=id, del_flag=1, user__department__id__in=sub_departments): data = await userRole.first().values( id="id", user_id="user__id", @@ -243,11 +318,14 @@ async def get_user_role_info(request: Request, id: str = Path(description="用 @userAPI.get("/roleList/{id}", response_model=GetDepartmentListResponse, response_class=JSONResponse, summary="获取用户角色列表") @Log(title="获取用户角色列表", business_type=BusinessType.SELECT) +@Auth(["user:btn:roleList"]) async def get_user_role_list( request: Request, id: str = Path(description="用户ID"), + current_user: dict = Depends(LoginController.get_current_user) ): - result = await UserRole.filter(user_id=id).values( + sub_departments = current_user.get("sub_departments") + result = await UserRole.filter(user_id=id, del_flag=1, user__department__id__in=sub_departments).values( id="id", department_id="user__department__id", department_name="user__department__name", @@ -263,15 +341,19 @@ async def get_user_role_list( return Response.success(data={ "result": result, "total": len(result), - "page": 1 + "page": 1, + "pageSize": 10, }) @userAPI.get("/permissionList/{id}", response_class=JSONResponse, response_model=GetUserPermissionListResponse, summary="获取用户权限列表") @Log(title="获取用户权限列表", business_type=BusinessType.SELECT) -async def get_user_permission_list(request: Request, id: str = Path(description="用户ID")): - permissions = await QueryController.get_user_permissions(user_id=id) +@Auth(["user:btn:permissionList"]) +async def get_user_permission_list(request: Request, id: str = Path(description="用户ID"), + current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") + permissions = await QueryController.get_user_permissions(user_id=id, sub_departments=sub_departments) permissions = await filterKeyValues(permissions, "id") # 获取用户角色 return Response.success(data=list(set(permissions))) @@ -279,11 +361,13 @@ async def get_user_permission_list(request: Request, id: str = Path(description= @userAPI.post("/avatar/{id}", response_model=UploadFileResponse, response_class=JSONResponse, summary="上传用户头像") @Log(title="上传用户头像", business_type=BusinessType.UPDATE) +@Auth(["user:btn:uploadAvatar"]) async def upload_user_avatar( request: Request, id: str = Path(description="用户ID"), - file: UploadFile = File(...)): - if user := await User.get_or_none(id=id): + file: UploadFile = File(...), current_user: dict = Depends(LoginController.get_current_user)): + sub_departments = current_user.get("sub_departments") + if user := await User.get_or_none(id=id, del_flag=1, department__id__in=sub_departments): image_mimetypes = [ 'image/jpeg', 'image/png', @@ -335,6 +419,8 @@ async def upload_user_avatar( create_time="create_time", update_time="update_time", ) + if await request.app.state.redis.get(f'{RedisKeyConfig.USER_INFO.key}:{user.id}'): + await request.app.state.redis.delete(f'{RedisKeyConfig.USER_INFO.key}:{user.id}') return Response.success(data=result) return Response.failure(msg="用户不存在!") @@ -343,8 +429,9 @@ async def upload_user_avatar( @userAPI.post("/resetPassword/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="重置用户密码") @Log(title="重置用户密码", business_type=BusinessType.UPDATE) @Auth(permission_list=["user:btn:reset_password"]) -async def reset_user_password(request: Request, params: ResetPasswordParams, id: str = Path(description="用户ID")): - if user := await User.get_or_none(id=id): +async def reset_user_password(request: Request, params: ResetPasswordParams, id: str = Path(description="用户ID"), + current_user: dict = Depends(LoginController.get_current_user)): + if user := await User.get_or_none(id=id, del_flag=1, department__id__in=current_user.get("sub_departments")): user.password = await Password.get_password_hash(params.password) await user.save() return Response.success(msg="重置密码成功!") @@ -444,4 +531,4 @@ async def get_user_statistics(request: Request, current_user: dict = Depends(Log "last_month_count": last_month_count, "before_14day_count_success": before_14day_count_success, "before_14day_count_fail": before_14day_count_fail, - }) + }) \ No newline at end of file diff --git a/config/get_redis.py b/config/get_redis.py index 8d2a803..980c783 100644 --- a/config/get_redis.py +++ b/config/get_redis.py @@ -79,7 +79,7 @@ class Redis: # 删除匹配的键 if keys: await app.state.redis.delete(*keys) - config = await Config.all().values() + config = await Config.filter(del_flag=1).values() for item in config: await app.state.redis.set(f"{RedisKeyConfig.SYSTEM_CONFIG.key}:{item.get('key')}", item.get('value'), ) diff --git a/controller/login.py b/controller/login.py index f86983a..db9d7fc 100644 --- a/controller/login.py +++ b/controller/login.py @@ -130,7 +130,7 @@ class LoginController: userInfo = await QueryController.get_user_info(user_id=user_id) await request.app.state.redis.set(f'{RedisKeyConfig.USER_INFO.key}:{user_id}', str(jsonable_encoder(userInfo)), - ex=timedelta(minutes=5)) + ex=timedelta(minutes=2)) if not userInfo: logger.warning('用户token不合法') raise AuthException(data='', message='用户token不合法') @@ -160,11 +160,11 @@ class LoginController: return False @classmethod - async def get_user_routes(cls, user_id: str) -> Union[list, None]: + async def get_user_routes(cls, user_id: str, sub_departments: list) -> Union[list, None]: """ 获取用户路由 """ - permissions = await QueryController.get_user_permissions(user_id=user_id) + permissions = await QueryController.get_user_permissions(user_id=user_id, sub_departments=sub_departments) for permission in permissions: permission["id"] = str(permission["id"]) permission["parentId"] = str(permission["parentId"]) if permission.get("parentId") else "" @@ -239,7 +239,7 @@ class LoginController: return complete_data @classmethod - async def get_online_user(cls, request: Request) -> list: + async def get_online_user(cls, request: Request, sub_departments: list) -> list: """ 获取在线用户 """ @@ -251,7 +251,8 @@ class LoginController: for item in access_token_values_list: payload = jwt.decode(item, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm]) session_id = payload.get("session_id") - result = await LoginLog.get_or_none(session_id=session_id).values( + result = await LoginLog.get_or_none(session_id=session_id, user__department__id__in=sub_departments, + del_flag=1).values( id="id", user_id="user__id", username="user__username", @@ -268,5 +269,7 @@ class LoginController: create_time="create_time", update_time="update_time" ) + if not result: + continue online_info_list.append(result) return online_info_list diff --git a/controller/query.py b/controller/query.py index 10c7960..ece4217 100644 --- a/controller/query.py +++ b/controller/query.py @@ -9,7 +9,7 @@ from typing import Union from tortoise.expressions import Q -from models import User, UserRole, RolePermission +from models import User, UserRole, RolePermission, Department from utils.common import filterKeyValues @@ -65,6 +65,8 @@ class QueryController: userRole = await filterKeyValues(userRoles, "role_code") # 获取用户角色ID userRoleIds = await filterKeyValues(userRoles, "role_id") + # 获取用户下属部门 + subDepartments = await cls.get_sub_department_ids(department_id=userInfo['department_id']) # 根据用户角色ID获取用户权限 permissions = [] for item in userRoleIds: @@ -78,6 +80,7 @@ class QueryController: permissions = list(set(permissions)) userInfo["roles"] = userRole userInfo["permissions"] = permissions + userInfo["sub_departments"] = subDepartments return userInfo @classmethod @@ -92,12 +95,12 @@ class QueryController: return await User.get_or_none(Q(username=username) | Q(email=email) | Q(phone=phone), del_flag=1) @classmethod - async def get_user_permissions(cls, user_id: str) -> Union[list, None]: + async def get_user_permissions(cls, user_id: str, sub_departments: list = []) -> Union[list, None]: """ 获取用户权限 """ # 获取用户角色 - userRoles = await UserRole.filter(user_id=user_id, del_flag=1).values( + userRoles = await UserRole.filter(user_id=user_id, del_flag=1, user__department__id__in=sub_departments).values( role_id="role__id", role_name="role__name", role_code="role__code" @@ -133,3 +136,17 @@ class QueryController: ) permissions.extend(permission) return permissions + + @classmethod + async def get_sub_department_ids(cls, department_id: str) -> list: + # 递归获取指定部门及其所有下属部门的 ID + async def fetch_sub_deps(dep_id: str): + sub_deps = await Department.filter(parent_id=dep_id, del_flag=1).all() + sub_deps_list = [dep.id for dep in sub_deps] + for sub_dep in sub_deps: + sub_deps_list.extend(await fetch_sub_deps(sub_dep.id)) # 递归获取下属部门 + return sub_deps_list + + dataList = await fetch_sub_deps(department_id) + dataList.append(department_id) + return list(set(dataList)) diff --git a/models/__init__.py b/models/__init__.py index c4ab018..5683aac 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -8,7 +8,7 @@ from models.code import Code, QueryCode, QueryCodeLog from models.config import Config -from models.department import Department, DepartmentRole +from models.department import Department from models.file import File from models.i18n import I18n, Locale from models.log import LoginLog, OperationLog @@ -18,7 +18,6 @@ from models.user import User, UserRole __all__ = [ 'Department', - 'DepartmentRole', 'File', 'LoginLog', 'OperationLog', diff --git a/models/department.py b/models/department.py index 63995be..b5bd4bf 100644 --- a/models/department.py +++ b/models/department.py @@ -118,38 +118,3 @@ class Department(BaseModel): table = "department" # 数据库表名 table_description = "部门表" # 表描述 ordering = ["sort", "-create_time"] # 默认按排序权重和创建时间排序 - - -class DepartmentRole(BaseModel): - """ - 部门角色表模型。 - """ - - department = fields.ForeignKeyField( - "models.Department", - related_name="department_roles", - description="部门ID", - source_field="department_id" # 映射到数据库字段 department_id - ) - """ - 部门ID。 - - 外键关联到 Department 表。 - - 映射到数据库字段 department_id。 - """ - - role = fields.ForeignKeyField( - "models.Role", - related_name="department_roles", - description="角色ID", - source_field="role_id" # 映射到数据库字段 role_id - ) - """ - 角色ID。 - - 外键关联到 Role 表。 - - 映射到数据库字段 role_id。 - """ - - class Meta: - table = "department_role" # 数据库表名 - table_description = "部门角色表" # 表描述 - unique_together = (("department_id", "role_id"),) # 唯一约束,防止重复分配 diff --git a/models/user.py b/models/user.py index 4f9e829..9f22b02 100644 --- a/models/user.py +++ b/models/user.py @@ -17,13 +17,11 @@ class User(BaseModel): username = fields.CharField( max_length=255, - unique=True, description="用户名", source_field="username" # 映射到数据库字段 username ) """ 用户名。 - - 必须唯一。 - 最大长度为 255 个字符。 - 映射到数据库字段 username。 """ diff --git a/schemas/config.py b/schemas/config.py index 96698fa..67cc99b 100644 --- a/schemas/config.py +++ b/schemas/config.py @@ -42,13 +42,6 @@ class AddConfigParams(BaseModel): remark: Optional[str] = Field(default=None, max_length=255, description="备注信息") -class DeleteConfigListParams(BaseModel): - """ - 批量删除配置参数模型 - """ - ids: List[str] = Field(default=[], description="配置ID") - - class GetConfigInfoResponse(BaseResponse): """ 获取配置模型信息响应 diff --git a/schemas/department.py b/schemas/department.py index 5b21d49..fc9f260 100644 --- a/schemas/department.py +++ b/schemas/department.py @@ -86,13 +86,6 @@ class AddDepartmentParams(BaseModel): } -class DeleteDepartmentListParams(BaseModel): - """ - 删除部门参数模型。 - """ - ids: List[str] = Field(..., description="部门ID列表") - - class GetDepartmentListResult(ListQueryResult): """ 获取部门列表结果模型。 @@ -105,74 +98,3 @@ class GetDepartmentListResponse(BaseResponse): 获取部门列表响应模型。 """ data: GetDepartmentListResult = Field(default=None, description="响应数据") - - -class AddDepartmentRoleParams(BaseModel): - """ - 添加部门角色参数模型。 - """ - department_id: str = Field(..., max_length=36, description="部门ID") - role_id: str = Field(..., max_length=36, description="角色ID") - - class Config: - json_schema_extra = { - "example": { - "department_id": "550e8400-e29b-41d4-a716-446655440000", - "role_id": "550e8400-e29b-41d4-a716-446655440000" - } - } - - -class DepartmentRoleInfo(BaseModel): - """ - 部门角色信息模型。 - """ - id: str = Field(..., max_length=36, description="主键ID") - department_id: str = Field(..., max_length=36, description="部门ID") - department_name: str = Field(..., max_length=100, description="部门名称") - department_phone: str = Field(..., max_length=30, description="部门电话") - department_principal: str = Field(..., max_length=64, description="部门负责人") - department_email: str = Field(..., max_length=128, description="部门邮箱") - role_name: str = Field(..., max_length=100, description="角色名称") - role_code: str = Field(..., max_length=100, description="角色编码") - role_id: str = Field(..., max_length=36, description="角色ID") - create_time: datetime = Field(..., description="创建时间") - update_time: datetime = Field(..., description="更新时间") - - class Config: - json_schema_extra = { - "example": { - "id": "550e8400-e29b-41d4-a716-446655440000", - "department_id": "550e8400-e29b-41d4-a716-446655440000", - "department_name": "研发部", - "department_phone": "1234567890", - "department_principal": "张三", - "department_email": "dev@example.com", - "role_name": "管理员", - "role_code": "admin", - "role_id": "550e8400-e29b-41d4-a716-446655440000", - "create_time": "2023-10-01T12:00:00", - "update_time": "2023-10-01T12:00:00" - } - } - - -class GetDepartmentRoleInfoResponse(BaseResponse): - """ - 获取部门角色信息响应模型。 - """ - data: DepartmentRoleInfo = Field(default=None, description="响应数据") - - -class GetDepartmentRoleListResult(ListQueryResult): - """ - 获取部门角色列表结果模型。 - """ - result: List[DepartmentRoleInfo] = Field(default=[], description="部门角色列表") - - -class GetDepartmentRoleListResponse(BaseResponse): - """ - 获取部门角色列表响应模型。 - """ - data: GetDepartmentRoleListResult = Field(default=None, description="响应数据")