From 292f5d7e48ebbaab5b43d374320b6f80db757a34 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Sat, 8 Nov 2025 16:24:19 +0800 Subject: [PATCH] Optimize the request params of the service layer (#903) --- backend/app/admin/api/v1/sys/dept.py | 4 +- backend/app/admin/api/v1/sys/user.py | 10 ++-- backend/app/admin/crud/crud_dept.py | 8 +-- backend/app/admin/service/dept_service.py | 8 +-- backend/app/admin/service/user_service.py | 59 ++++++++--------------- backend/common/security/permission.py | 11 +++-- 6 files changed, 43 insertions(+), 57 deletions(-) diff --git a/backend/app/admin/api/v1/sys/dept.py b/backend/app/admin/api/v1/sys/dept.py index 86ef0efb..32a41ee5 100644 --- a/backend/app/admin/api/v1/sys/dept.py +++ b/backend/app/admin/api/v1/sys/dept.py @@ -30,7 +30,9 @@ async def get_dept_tree( phone: Annotated[str | None, Query(description='联系电话')] = None, status: Annotated[int | None, Query(description='状态')] = None, ) -> ResponseSchemaModel[list[GetDeptTree]]: - dept = await dept_service.get_tree(db=db, request=request, name=name, leader=leader, phone=phone, status=status) + dept = await dept_service.get_tree( + db=db, request_user=request.user, name=name, leader=leader, phone=phone, status=status + ) return response_base.success(data=dept) diff --git a/backend/app/admin/api/v1/sys/user.py b/backend/app/admin/api/v1/sys/user.py index 6912f2b6..7487cb3c 100644 --- a/backend/app/admin/api/v1/sys/user.py +++ b/backend/app/admin/api/v1/sys/user.py @@ -102,7 +102,9 @@ async def update_user_permission( async def update_user_password( db: CurrentSessionTransaction, request: Request, obj: ResetPasswordParam ) -> ResponseModel: - count = await user_service.update_password(db=db, request=request, obj=obj) + count = await user_service.update_password( + db=db, user_id=request.user.id, hash_password=request.user.password, obj=obj + ) if count > 0: return response_base.success() return response_base.fail() @@ -126,7 +128,7 @@ async def update_user_nickname( request: Request, nickname: Annotated[str, Body(embed=True, description='用户昵称')], ) -> ResponseModel: - count = await user_service.update_nickname(db=db, request=request, nickname=nickname) + count = await user_service.update_nickname(db=db, user_id=request.user.id, nickname=nickname) if count > 0: return response_base.success() return response_base.fail() @@ -138,7 +140,7 @@ async def update_user_avatar( request: Request, avatar: Annotated[str, Body(embed=True, description='用户头像地址')], ) -> ResponseModel: - count = await user_service.update_avatar(db=db, request=request, avatar=avatar) + count = await user_service.update_avatar(db=db, user_id=request.user.id, avatar=avatar) if count > 0: return response_base.success() return response_base.fail() @@ -151,7 +153,7 @@ async def update_user_email( captcha: Annotated[str, Body(embed=True, description='邮箱验证码')], email: Annotated[str, Body(embed=True, description='用户邮箱')], ) -> ResponseModel: - count = await user_service.update_email(db=db, request=request, captcha=captcha, email=email) + count = await user_service.update_email(db=db, user_id=request.user.id, captcha=captcha, email=email) if count > 0: return response_base.success() return response_base.fail() diff --git a/backend/app/admin/crud/crud_dept.py b/backend/app/admin/crud/crud_dept.py index 54caded7..6bd48de2 100644 --- a/backend/app/admin/crud/crud_dept.py +++ b/backend/app/admin/crud/crud_dept.py @@ -1,11 +1,11 @@ from collections.abc import Sequence -from fastapi import Request from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy_crud_plus import CRUDPlus from backend.app.admin.model import Dept from backend.app.admin.schema.dept import CreateDeptParam, UpdateDeptParam +from backend.app.admin.schema.user import GetUserInfoWithRelationDetail from backend.common.security.permission import filter_data_permission @@ -34,8 +34,8 @@ class CRUDDept(CRUDPlus[Dept]): async def get_all( self, - request: Request, db: AsyncSession, + request_user: GetUserInfoWithRelationDetail, name: str | None, leader: str | None, phone: str | None, @@ -44,8 +44,8 @@ class CRUDDept(CRUDPlus[Dept]): """ 获取所有部门 - :param request: FastAPI 请求对象 :param db: 数据库会话 + :param request_user: 请求用户 :param name: 部门名称 :param leader: 负责人 :param phone: 联系电话 @@ -63,7 +63,7 @@ class CRUDDept(CRUDPlus[Dept]): if status is not None: filters['status'] = status - data_filtered = await filter_data_permission(db, request) + data_filtered = await filter_data_permission(db, request_user) return await self.select_models_order(db, 'sort', 'desc', data_filtered, **filters) async def create(self, db: AsyncSession, obj: CreateDeptParam) -> None: diff --git a/backend/app/admin/service/dept_service.py b/backend/app/admin/service/dept_service.py index d75cd687..a4f27af2 100644 --- a/backend/app/admin/service/dept_service.py +++ b/backend/app/admin/service/dept_service.py @@ -1,11 +1,11 @@ from typing import Any -from fastapi import Request from sqlalchemy.ext.asyncio import AsyncSession from backend.app.admin.crud.crud_dept import dept_dao from backend.app.admin.model import Dept from backend.app.admin.schema.dept import CreateDeptParam, UpdateDeptParam +from backend.app.admin.schema.user import GetUserInfoWithRelationDetail from backend.common.exception import errors from backend.core.conf import settings from backend.database.redis import redis_client @@ -34,7 +34,7 @@ class DeptService: async def get_tree( *, db: AsyncSession, - request: Request, + request_user: GetUserInfoWithRelationDetail, name: str | None, leader: str | None, phone: str | None, @@ -44,7 +44,7 @@ class DeptService: 获取部门树形结构 :param db: 数据库会话 - :param request: FastAPI 请求对象 + :param request_user: 请求用户 :param name: 部门名称 :param leader: 部门负责人 :param phone: 联系电话 @@ -52,7 +52,7 @@ class DeptService: :return: """ - dept_select = await dept_dao.get_all(request, db, name, leader, phone, status) + dept_select = await dept_dao.get_all(db, request_user, name, leader, phone, status) tree_data = get_tree_data(dept_select) return tree_data diff --git a/backend/app/admin/service/user_service.py b/backend/app/admin/service/user_service.py index f214220e..d6ecf486 100644 --- a/backend/app/admin/service/user_service.py +++ b/backend/app/admin/service/user_service.py @@ -200,93 +200,74 @@ class UserService: return count @staticmethod - async def update_nickname(*, db: AsyncSession, request: Request, nickname: str) -> int: + async def update_nickname(*, db: AsyncSession, user_id: int, nickname: str) -> int: """ 更新当前用户昵称 :param db: 数据库会话 - :param request: FastAPI 请求对象 + :param user_id: 用户 ID :param nickname: 用户昵称 :return: """ - token = get_token(request) - token_payload = jwt_decode(token) - user = await user_dao.get(db, token_payload.id) - if not user: - raise errors.NotFoundError(msg='用户不存在') - count = await user_dao.update_nickname(db, token_payload.id, nickname) - await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}') + count = await user_dao.update_nickname(db, user_id, nickname) + await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user_id}') return count @staticmethod - async def update_avatar(*, db: AsyncSession, request: Request, avatar: str) -> int: + async def update_avatar(*, db: AsyncSession, user_id: int, avatar: str) -> int: """ 更新当前用户头像 :param db: 数据库会话 - :param request: FastAPI 请求对象 + :param user_id: 用户 ID :param avatar: 头像地址 :return: """ - token = get_token(request) - token_payload = jwt_decode(token) - user = await user_dao.get(db, token_payload.id) - if not user: - raise errors.NotFoundError(msg='用户不存在') - count = await user_dao.update_avatar(db, token_payload.id, avatar) - await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}') + count = await user_dao.update_avatar(db, user_id, avatar) + await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user_id}') return count @staticmethod - async def update_email(*, db: AsyncSession, request: Request, captcha: str, email: str) -> int: + async def update_email(*, db: AsyncSession, user_id: int, captcha: str, email: str) -> int: """ 更新当前用户邮箱 :param db: 数据库会话 - :param request: FastAPI 请求对象 + :param user_id: 用户 ID :param captcha: 邮箱验证码 :param email: 邮箱 :return: """ - token = get_token(request) - token_payload = jwt_decode(token) - user = await user_dao.get(db, token_payload.id) - if not user: - raise errors.NotFoundError(msg='用户不存在') captcha_code = await redis_client.get(f'{settings.EMAIL_CAPTCHA_REDIS_PREFIX}:{ctx.ip}') if not captcha_code: raise errors.RequestError(msg='验证码已失效,请重新获取') if captcha != captcha_code: raise errors.CustomError(error=CustomErrorCode.CAPTCHA_ERROR) await redis_client.delete(f'{settings.EMAIL_CAPTCHA_REDIS_PREFIX}:{ctx.ip}') - count = await user_dao.update_email(db, token_payload.id, email) - await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}') + count = await user_dao.update_email(db, user_id, email) + await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user_id}') return count @staticmethod - async def update_password(*, db: AsyncSession, request: Request, obj: ResetPasswordParam) -> int: + async def update_password(*, db: AsyncSession, user_id: int, hash_password: str, obj: ResetPasswordParam) -> int: """ 更新当前用户密码 :param db: 数据库会话 - :param request: FastAPI 请求对象 + :param user_id: 用户 ID + :param hash_password: 哈希密码 :param obj: 密码重置参数 :return: """ - token = get_token(request) - token_payload = jwt_decode(token) - user = await user_dao.get(db, token_payload.id) - if not user: - raise errors.NotFoundError(msg='用户不存在') - if not password_verify(obj.old_password, user.password): + if not password_verify(obj.old_password, hash_password): raise errors.RequestError(msg='原密码错误') if obj.new_password != obj.confirm_password: raise errors.RequestError(msg='密码输入不一致') - count = await user_dao.reset_password(db, user.id, obj.new_password) + count = await user_dao.reset_password(db, user_id, obj.new_password) key_prefix = [ - f'{settings.TOKEN_REDIS_PREFIX}:{user.id}', - f'{settings.TOKEN_REFRESH_REDIS_PREFIX}:{user.id}', - f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}', + f'{settings.TOKEN_REDIS_PREFIX}:{user_id}', + f'{settings.TOKEN_REFRESH_REDIS_PREFIX}:{user_id}', + f'{settings.JWT_USER_REDIS_PREFIX}:{user_id}', ] for prefix in key_prefix: await redis_client.delete_prefix(prefix) diff --git a/backend/common/security/permission.py b/backend/common/security/permission.py index b5739881..d266a49b 100644 --- a/backend/common/security/permission.py +++ b/backend/common/security/permission.py @@ -3,6 +3,7 @@ from sqlalchemy import ColumnElement, and_, or_ from sqlalchemy.ext.asyncio import AsyncSession from backend.app.admin.crud.crud_data_scope import data_scope_dao +from backend.app.admin.schema.user import GetUserInfoWithRelationDetail from backend.common.context import ctx from backend.common.enums import RoleDataRuleExpressionType, RoleDataRuleOperatorType from backend.common.exception import errors @@ -42,7 +43,7 @@ class RequestPermission: ctx.permission = self.value -async def filter_data_permission(db: AsyncSession, request: Request) -> ColumnElement[bool]: # noqa: C901 +async def filter_data_permission(db: AsyncSession, request_user: GetUserInfoWithRelationDetail) -> ColumnElement[bool]: # noqa: C901 """ 过滤数据权限,控制用户可见数据范围 @@ -50,20 +51,20 @@ async def filter_data_permission(db: AsyncSession, request: Request) -> ColumnEl - 控制用户能看到哪些数据 :param db: 数据库会话 - :param request: FastAPI 请求对象 + :param request_user: 请求用户 :return: """ # 是否过滤数据权限 - if request.user.is_superuser: + if request_user.is_superuser: return or_(1 == 1) - for role in request.user.roles: + for role in request_user.roles: if not role.is_filter_scopes: return or_(1 == 1) # 获取数据范围 data_scope_ids = set() - for role in request.user.roles: + for role in request_user.roles: for scope in role.scopes: if scope.status: data_scope_ids.add(scope.id)