PayrollManagementSystem/backend/app/services/log_service.py
liuwenxin 5ece9f6dab 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>
2026-10-22 19:43:20 +08:00

79 lines
2.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""日志查询编排。
操作日志与登录日志分别提供分页查询(两表字段与筛选项不同),操作日志另有
单条详情(列表为性能排除的 `request_data` 走详情按需加载)。
纯只读操作,不写审计 —— 读操作记日志只会淹没审计表。
"""
from sqlalchemy.orm import Session
from app.core.exceptions import NotFoundError
from app.core.response import PageResult
from app.repositories import login_log_repository, operation_log_repository
from app.repositories.query_utils import LogFilters
from app.schemas.log import (
LoginLogOut,
LoginLogQuery,
OperationLogDetailOut,
OperationLogOut,
OperationLogQuery,
)
def list_operation_logs(session: Session, query: OperationLogQuery) -> PageResult[OperationLogOut]:
"""分页查询操作日志(列表不含 request_data,详情走 `get_operation_log`)。"""
repo_query = operation_log_repository.OperationLogListQuery(
filters=_filters_from(query),
offset=query.offset,
limit=query.page_size,
module=query.module,
action=query.action,
)
logs = operation_log_repository.list_logs(session, repo_query)
total = operation_log_repository.count_logs(session, repo_query)
return PageResult[OperationLogOut](
records=[OperationLogOut.model_validate(log) for log in logs],
total=total,
page_num=query.page_num,
page_size=query.page_size,
)
def get_operation_log(session: Session, log_id: int) -> OperationLogDetailOut:
"""查询单条操作日志详情(含已脱敏的 request_data)。
:raises NotFoundError: 日志不存在
"""
log = operation_log_repository.get_by_id(session, log_id)
if log is None:
raise NotFoundError("日志不存在")
return OperationLogDetailOut.model_validate(log)
def list_login_logs(session: Session, query: LoginLogQuery) -> PageResult[LoginLogOut]:
"""分页查询登录日志。"""
repo_query = login_log_repository.LoginLogListQuery(
filters=_filters_from(query),
offset=query.offset,
limit=query.page_size,
ip=query.ip,
)
logs = login_log_repository.list_logs(session, repo_query)
total = login_log_repository.count_logs(session, repo_query)
return PageResult[LoginLogOut](
records=[LoginLogOut.model_validate(log) for log in logs],
total=total,
page_num=query.page_num,
page_size=query.page_size,
)
def _filters_from(query: OperationLogQuery | LoginLogQuery) -> LogFilters:
"""两类 Query 的公共筛选字段 → LogFilters(口径与 repository 共用一个实现)。"""
return LogFilters(
username=query.username,
success=query.success,
start_time=query.start_time,
end_time=query.end_time,
)