mirror of
https://github.com/fastapi-practices/fastapi_best_architecture.git
synced 2026-03-13 09:31:31 +08:00
Optimize the request params of the service layer (#903)
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user