119 lines
4.4 KiB
Python
119 lines
4.4 KiB
Python
"""审计日志服务模块
|
||
|
||
负责审计日志的查询和写入。
|
||
所有业务模块的写操作(增删改、状态变更等)均通过此服务记录审计日志,
|
||
支持操作前后值对比,便于问题追溯和合规审计。
|
||
|
||
被调用方: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()
|