refactor(backend): 仓储层散参数收敛为查询 DTO
仓储层的 list_*/count_* 此前以 keyword/status/offset/limit 四五个散参数 传参,调用点重复铺开、易漏传且不可扩展;按项目「参数 >4 封装 DTO」的 约定统一收敛为各模块的 Query 对象(如 RoleQuery、BusinessQuery), 服务层与测试调用同步调整,行为不变。 - repositories 层查询方法签名改为 (session, query),分页与筛选条件 由 Query 承载,_apply_filters 同步收口 - schemas 层补齐各模块 Query 模型(approval/business_role/report 等) - services 层调用点同步改为传 Query - tests 随之调整;新增用例:待办角色流转(财务通过后领导出现待办)、 参数预览顺序稳定 验证:全量 pytest 900 passed(11:08);单元 526 passed;ruff check 绿。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
1 parent
c48d24ab6d
commit
5ece9f6dab
45 files changed
+1045
-1028
No files matched your search
@@ -10,6 +10,7 @@ from sqlalchemy.orm import Session, selectinload
|
||||
|
||||
from app.models import Role, User, UserRole
|
||||
from app.repositories.query_utils import fuzzy
|
||||
from app.schemas.user import UserQuery
|
||||
|
||||
|
||||
def _base_query() -> Select[tuple[User]]:
|
||||
@@ -48,16 +49,7 @@ def touch_last_login(session: Session, user: User, moment: datetime) -> None:
|
||||
# ==================== 列表查询 ====================
|
||||
|
||||
|
||||
def list_users(
|
||||
session: Session,
|
||||
*,
|
||||
offset: int,
|
||||
limit: int,
|
||||
username: str | None = None,
|
||||
real_name: str | None = None,
|
||||
status: int | None = None,
|
||||
role_code: str | None = None,
|
||||
) -> list[User]:
|
||||
def list_users(session: Session, query: UserQuery) -> list[User]:
|
||||
"""分页查询用户列表。
|
||||
|
||||
**按 id 倒序而非 create_time**:create_time 是秒级精度,批量导入的用户
|
||||
@@ -65,48 +57,35 @@ def list_users(
|
||||
id 是自增主键,天然唯一且与创建顺序一致。
|
||||
"""
|
||||
stmt = _base_query()
|
||||
stmt = _apply_filters(stmt, username=username, real_name=real_name, status=status)
|
||||
if role_code:
|
||||
stmt = _apply_filters(stmt, query)
|
||||
if query.role_code:
|
||||
# 按角色筛选需 join 关联表;角色是字典数据,数量极少,join 代价可忽略
|
||||
stmt = stmt.join(UserRole).join(Role).where(Role.role_code == role_code)
|
||||
stmt = stmt.order_by(User.id.desc()).offset(offset).limit(limit)
|
||||
stmt = stmt.join(UserRole).join(Role).where(Role.role_code == query.role_code)
|
||||
stmt = stmt.order_by(User.id.desc()).offset(query.offset).limit(query.page_size)
|
||||
return list(session.execute(stmt).scalars().unique())
|
||||
|
||||
|
||||
def count_users(
|
||||
session: Session,
|
||||
*,
|
||||
username: str | None = None,
|
||||
real_name: str | None = None,
|
||||
status: int | None = None,
|
||||
role_code: str | None = None,
|
||||
) -> int:
|
||||
def count_users(session: Session, query: UserQuery) -> int:
|
||||
"""统计符合条件的用户总数,供分页使用。"""
|
||||
stmt = select(func.count(User.id))
|
||||
stmt = _apply_filters(stmt, username=username, real_name=real_name, status=status)
|
||||
if role_code:
|
||||
stmt = stmt.join(UserRole).join(Role).where(Role.role_code == role_code)
|
||||
stmt = _apply_filters(stmt, query)
|
||||
if query.role_code:
|
||||
stmt = stmt.join(UserRole).join(Role).where(Role.role_code == query.role_code)
|
||||
return int(session.execute(stmt).scalar_one())
|
||||
|
||||
|
||||
def _apply_filters(
|
||||
stmt: Select,
|
||||
*,
|
||||
username: str | None,
|
||||
real_name: str | None,
|
||||
status: int | None,
|
||||
) -> Select:
|
||||
def _apply_filters(stmt: Select, query: UserQuery) -> Select:
|
||||
"""应用通用筛选条件。列表与计数共用,避免两处口径不一致。
|
||||
|
||||
模糊匹配统一走 `query_utils.fuzzy`(转义 LIKE 通配符)—— 用户名普遍
|
||||
含下划线,直接拼接会让 `_` 当单字符通配执行、误配无关账号。
|
||||
"""
|
||||
if username:
|
||||
stmt = fuzzy(stmt, User.username, username)
|
||||
if real_name:
|
||||
stmt = fuzzy(stmt, User.real_name, real_name)
|
||||
if status is not None:
|
||||
stmt = stmt.where(User.status == status)
|
||||
if query.username:
|
||||
stmt = fuzzy(stmt, User.username, query.username)
|
||||
if query.real_name:
|
||||
stmt = fuzzy(stmt, User.real_name, query.real_name)
|
||||
if query.status is not None:
|
||||
stmt = stmt.where(User.status == query.status)
|
||||
return stmt
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user