feat(audit): 扩大 GET 采集范围 + 修正动作文案
【扩大采集:核心业务数据的「查看详情」】
新增 _TRACKED_READ_PREFIXES(notifications / tasks / orders /
products / records)—— products 是补的:GET /products/scan/{sn}(扫码查询)
是整个车间最高频的读操作,不采它等于没采"活跃度"。
_should_audit 的 GET 分支改为三级判断:
敏感读(export/download/print) → 受跟踪前缀且非裸列表 → 否则不采
用 _is_bare_list 跳过「拉整个列表」:
· 列表接口被前端高频轮询(消息、任务列表尤其明显),逐条留痕会让
audit_logs 迅速膨胀,真正有价值的操作反而被淹没;
· 只有「查看详情」(/tasks/{id}) 才代表用户真的点开了某条业务数据。
判定用「去掉末尾斜杠后是否恰好等于某前缀」,天然排除查询串。
【修正文案】
- "read": "查询" → "查看详情"(前者易被误解成"随便搜了一下")
- 新增 "mark_read": "标为已读",并在 _SEGMENT_ACTION 补 "read" 映射 ——
否则 PUT /notifications/{id}/read 会回退到 _METHOD_ACTION(PUT→update),
把"点开一条通知"记成"修改了某样东西"
- "refresh": "刷新令牌" → "上线"(token 2 小时一换,业务上视作一次上线)
⚠️ 两点须知:
1. notifications / orders 目录下【只有列表路由】,而列表按规则不采集,
故这两个模块不会产生查看记录 —— 后端没有"查看单条消息"的接口。
且消息列表被前端轮询,采集它反而会造成日志爆炸,跳过是正确的。
2. products 被纳入后,工人每扫一次码就多一条记录(50 人 × 每天数百次
≈ 上万条/天)。若嫌吵,去掉该前缀一行即可。
注:读接口鉴权已在上一提交补齐,故这些"查看详情"记录能正确挂上操作人。
This commit is contained in:
@ -39,6 +39,22 @@ _MUTATING_METHODS = frozenset({"POST", "PUT", "PATCH", "DELETE"})
|
||||
# 读操作里需要留痕的(导出/下载/打印属于「读」,但把数据带出了系统)
|
||||
_SENSITIVE_READ_KEYWORDS = frozenset({"export", "download", "print"})
|
||||
|
||||
# 核心业务模块 —— 这些前缀下的「查看详情」GET 也采集,
|
||||
# 用于回答「谁在什么时候看过哪条业务数据」,而不只是「谁改过」。
|
||||
#
|
||||
# ⚠️ 只覆盖【核心业务实体】:
|
||||
# products —— 移动端扫码查询 GET /products/scan/{sn} 是车间最高频的读操作
|
||||
# tasks —— 查看任务详情 /tasks/{id}
|
||||
# records —— 任务记录
|
||||
# notifications / orders —— 见下方 _is_bare_list 的说明
|
||||
_TRACKED_READ_PREFIXES = (
|
||||
"/api/v1/notifications",
|
||||
"/api/v1/tasks",
|
||||
"/api/v1/orders",
|
||||
"/api/v1/products",
|
||||
"/api/v1/records",
|
||||
)
|
||||
|
||||
# 永久忽略的路径前缀
|
||||
_IGNORED_PREFIXES = ("/health", "/docs", "/redoc", "/openapi.json")
|
||||
|
||||
@ -81,6 +97,10 @@ _SEGMENT_ACTION: dict[str, str] = {
|
||||
"spawn": "spawn",
|
||||
"complete": "complete",
|
||||
"end": "end",
|
||||
# 消息已读:PUT /notifications/{id}/read。
|
||||
# 没有这一条时会回退到 _METHOD_ACTION(PUT → update → "修改"),
|
||||
# 把"点开一条通知"记成"修改了某样东西",语义完全走样。
|
||||
"read": "mark_read",
|
||||
}
|
||||
|
||||
_METHOD_ACTION: dict[str, str] = {
|
||||
@ -97,6 +117,20 @@ _NON_ID_SEGMENTS = frozenset(
|
||||
)
|
||||
|
||||
|
||||
def _is_bare_list(path: str) -> bool:
|
||||
"""判断是否只是「拉整个列表」(如 GET /api/v1/tasks/)。
|
||||
|
||||
这类请求【不采集】,理由:
|
||||
· 列表接口被前端高频轮询(消息、任务列表尤其明显),逐条留痕会让
|
||||
audit_logs 迅速膨胀,真正有价值的操作反而被淹没;
|
||||
· 「查看详情」(/tasks/{id}) 才代表用户真的点开了某条业务数据。
|
||||
|
||||
判定用「去掉末尾斜杠后是否恰好等于某个受跟踪前缀」,
|
||||
比正则更直观,也天然把查询串排除在外(request.url.path 不含 ?query)。
|
||||
"""
|
||||
return path.rstrip("/") in _TRACKED_READ_PREFIXES
|
||||
|
||||
|
||||
def _derive_module_and_action(path: str, method: str) -> tuple[str, str, str | None]:
|
||||
"""由请求路径与 HTTP 方法推导 (module, action, target_id)"""
|
||||
parts = [p for p in path.split("/") if p]
|
||||
@ -141,7 +175,12 @@ class AuditMiddleware(BaseHTTPMiddleware):
|
||||
return True
|
||||
if request.method == "GET":
|
||||
lowered = path.lower()
|
||||
return any(kw in lowered for kw in _SENSITIVE_READ_KEYWORDS)
|
||||
if any(kw in lowered for kw in _SENSITIVE_READ_KEYWORDS):
|
||||
return True
|
||||
# 核心业务数据的「查看详情」也留痕(证明用户在真的使用系统)
|
||||
if path.startswith(_TRACKED_READ_PREFIXES):
|
||||
return not _is_bare_list(path)
|
||||
return False
|
||||
return False
|
||||
|
||||
async def dispatch(
|
||||
|
||||
@ -62,11 +62,16 @@ ACTION_LABELS: dict[str, str] = {
|
||||
"create": "新增",
|
||||
"update": "修改",
|
||||
"delete": "删除",
|
||||
"read": "查询",
|
||||
# 只用于被采集的 GET(核心业务详情 / 敏感读)。
|
||||
# 叫「查看详情」而不是「查询」:前者说明用户确实点开了某条业务数据,
|
||||
# 后者容易被误解成"随便搜了一下"。
|
||||
"read": "查看详情",
|
||||
"export": "导出",
|
||||
"login": "登录",
|
||||
"logout": "登出",
|
||||
"refresh": "刷新令牌",
|
||||
# 刷新令牌 = 用户重新开始使用系统(token 2 小时一换,7 天免登录),
|
||||
# 业务上视作一次「上线」,比"刷新令牌"这种技术词更贴近车间口径
|
||||
"refresh": "上线",
|
||||
"print": "打印",
|
||||
"upload": "上传",
|
||||
"finalize": "收口",
|
||||
@ -77,6 +82,7 @@ ACTION_LABELS: dict[str, str] = {
|
||||
"spawn": "派发",
|
||||
"end": "结束分支",
|
||||
"complete": "完结",
|
||||
"mark_read": "标为已读",
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user