"""审计日志服务模块 负责审计日志的查询和写入。 所有业务模块的写操作(增删改、状态变更等)均通过此服务记录审计日志, 支持操作前后值对比,便于问题追溯和合规审计。 被调用方:audit 路由(日志查询接口);被 reminder、report、system、config、ai 等多个服务调用。 """ import json import logging logger = logging.getLogger(__name__) from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session from backend.app.core.error_codes import ErrorCode from backend.app.core.exceptions import AppException from backend.app.repositories.audit_repository import AuditRepository class AuditService: """审计日志业务服务。 依赖: - AuditRepository:审计日志数据的读写操作。 """ def __init__(self) -> None: self.repository = AuditRepository() def list_audit_logs(self, session: Session | None = None, filters: dict | None = None) -> dict: """分页查询审计日志列表。 参数: session: 数据库会话,不可为 None。 filters: 可选的筛选条件,支持 page_no、page_size 等分页参数。 返回: 包含 total、page_no、page_size、list 的分页结果字典。 被调用方:audit 路由(日志查询接口)。 """ if session is None: raise AppException(code=ErrorCode.PARAM_ERROR, message="数据库会话未初始化", status_code=500) filters = filters or {} try: logs = self.repository.list_logs(session, filters) page_no = filters.get("page_no", 1) page_size = filters.get("page_size", 20) start = max(page_no - 1, 0) * page_size page_logs = logs[start : start + page_size] return { "total": len(logs), "page_no": page_no, "page_size": page_size, "list": [ { "log_id": item.id, "operator_name": item.operator_name or "", "operate_type": item.operate_type, "biz_type": item.biz_type, "biz_id": item.biz_id, "result": item.result, "operate_time": item.operate_time.strftime("%Y-%m-%d %H:%M:%S") if item.operate_time else "", "remark": item.remark, } for item in page_logs ], } except SQLAlchemyError as exc: raise AppException(code=ErrorCode.SYSTEM_ERROR, message="数据库操作失败", status_code=500) from exc def write_log(self, session: Session, payload: dict) -> None: """写入审计日志记录。 参数: session: 数据库会话(调用方负责 commit/rollback)。 payload: 日志数据字典,必含 operate_type、biz_type、biz_id, 可选 operator_id、operator_name、before_value、after_value、result、remark。 被调用方:被 reminder、report、system、config、ai、order 等多个服务的写操作调用。 """ before_value = payload.get("before_value") after_value = payload.get("after_value") self.repository.create_log( session, { "operator_id": payload.get("operator_id"), "operator_name": payload.get("operator_name") or "系统", "operate_type": payload["operate_type"], "biz_type": payload["biz_type"], "biz_id": payload["biz_id"], "before_value": self._serialize_value(before_value), "after_value": self._serialize_value(after_value), "result": payload.get("result", "success"), "remark": payload.get("remark"), }, ) def _serialize_value(self, value) -> str | None: """将任意值序列化为 JSON 字符串,用于存储审计日志的 before/after 值。 参数: value: 原始值,支持 None、str、dict、list 等类型。 返回: JSON 字符串,None 值原样返回。 """ if value is None: return None if isinstance(value, str): return value return json.dumps(value, ensure_ascii=False, default=str) audit_service = AuditService()