- 将全部路由文件(12个)的 jsonify 响应迁移至 success_response/error_response 统一格式 - 修复 sync_routes.py error_response 参数错误(P0) - 新增异步任务系统:task_registry + task_routes - 新增状态码:TASK_NOT_FOUND(4014)、TASK_CONFLICT(4015)、REINDEX_ERROR(5040) - 修正 task_routes/exam_pkg 中语义不匹配的状态码 - 更新 curl 测试手册、后端对接规范文档 - 添加缓存性能报告和 Redis 迁移计划
137 lines
4.2 KiB
Python
137 lines
4.2 KiB
Python
"""
|
||
审计日志 API
|
||
|
||
路由:
|
||
- GET /audit/logs - 查询审计日志(管理员)
|
||
"""
|
||
|
||
from flask import Blueprint, request, jsonify
|
||
import logging
|
||
|
||
logger = logging.getLogger(__name__)
|
||
from auth.gateway import require_gateway_auth
|
||
from data.db import get_connection
|
||
from core.status_codes import SUCCESS, INTERNAL_ERROR
|
||
from api.response_utils import success_response, error_response
|
||
|
||
audit_bp = Blueprint('audit', __name__)
|
||
|
||
|
||
@audit_bp.route('/audit/logs', methods=['GET'])
|
||
@require_gateway_auth
|
||
def get_audit_logs():
|
||
"""
|
||
查询审计日志
|
||
|
||
参数:
|
||
- limit: 返回条数(默认50)
|
||
- days: 查询天数(默认7)
|
||
- action: 操作类型过滤(可选)
|
||
|
||
返回:
|
||
{
|
||
"logs": [
|
||
{
|
||
"id": 1,
|
||
"user_id": "admin001",
|
||
"username": "admin",
|
||
"action": "rag_query",
|
||
"query": "xxx",
|
||
"result_summary": "...",
|
||
"role": "admin",
|
||
"department": "管理部",
|
||
"ip_address": "127.0.0.1",
|
||
"duration_ms": 1234,
|
||
"timestamp": "2025-01-01 12:00:00"
|
||
}
|
||
],
|
||
"total": 100
|
||
}
|
||
"""
|
||
limit = request.args.get('limit', 50, type=int)
|
||
days = request.args.get('days', 7, type=int)
|
||
action_filter = request.args.get('action', '')
|
||
|
||
try:
|
||
with get_connection("session") as conn:
|
||
# 构建查询
|
||
where_clauses = ["created_at >= datetime('now', ?)"]
|
||
params = [f'-{days} days']
|
||
|
||
if action_filter:
|
||
where_clauses.append("action = ?")
|
||
params.append(action_filter)
|
||
|
||
where_sql = " AND ".join(where_clauses)
|
||
|
||
# 查询总数
|
||
count_sql = f"SELECT COUNT(*) FROM audit_logs WHERE {where_sql}"
|
||
total = conn.execute(count_sql, params).fetchone()[0]
|
||
|
||
# 查询日志
|
||
query_sql = f"""
|
||
SELECT id, user_id, username, action, query, result_summary,
|
||
role, department, ip_address, duration_ms, created_at
|
||
FROM audit_logs
|
||
WHERE {where_sql}
|
||
ORDER BY created_at DESC
|
||
LIMIT ?
|
||
"""
|
||
params.append(limit)
|
||
rows = conn.execute(query_sql, params).fetchall()
|
||
|
||
logs = []
|
||
for row in rows:
|
||
logs.append({
|
||
"id": row[0],
|
||
"user_id": row[1],
|
||
"username": row[2],
|
||
"action": row[3],
|
||
"query": row[4],
|
||
"result_summary": row[5],
|
||
"role": row[6],
|
||
"department": row[7],
|
||
"ip_address": row[8],
|
||
"duration_ms": row[9],
|
||
"timestamp": row[10]
|
||
})
|
||
|
||
return success_response(data={"logs": logs, "total": total})
|
||
|
||
except Exception as e:
|
||
logger.error(f"审计查询异常: {e}")
|
||
return error_response("QUERY_FAILED", INTERNAL_ERROR, "查询失败", http_status=500)
|
||
|
||
|
||
def log_audit_event(user_id: str, username: str, action: str,
|
||
query: str = None, result_summary: str = None,
|
||
role: str = None, department: str = None,
|
||
ip_address: str = None, duration_ms: int = None):
|
||
"""
|
||
记录审计日志(供其他模块调用)
|
||
|
||
Args:
|
||
user_id: 用户ID
|
||
username: 用户名
|
||
action: 操作类型(rag_query, chat, feedback, sync 等)
|
||
query: 查询内容
|
||
result_summary: 结果摘要
|
||
role: 用户角色
|
||
department: 部门
|
||
ip_address: IP地址
|
||
duration_ms: 耗时(毫秒)
|
||
"""
|
||
try:
|
||
with get_connection("session") as conn:
|
||
conn.execute("""
|
||
INSERT INTO audit_logs
|
||
(user_id, username, action, query, result_summary,
|
||
role, department, ip_address, duration_ms)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||
""", (user_id, username, action, query, result_summary,
|
||
role, department, ip_address, duration_ms))
|
||
except Exception as e:
|
||
# 审计日志写入失败不应影响主流程
|
||
logger.debug(f"审计日志写入失败: {e}")
|
||
pass
|