Files
cma-management/docs/audit-and-notification-caliber-20260922.md
T
fullstack 30fa745135 fix(cma): 工作台跨实体治本 · DAMA 回查收口 — 审计可归属 + 通知日志口径
DAMA 独立回查 dama-recheck-workbench-entity-isolation-20260922 的 §4b/§5 两处待办:

1. POST /api/cma/tenant/switch 审计行原来不可归属:
   - user_id=None → 改为注入会话用户(user=Depends(require_auth_401))
   - target_type/target_id 从 system_config/None 对齐为 entity/<entity_id>,
     与 /auth/switch-entity 的 switch_entity 共用目标维度 ⇒ 按账套可过滤,零 DDL
   - action 字面 switch_tenant_state 保留不合并(与会话级 switch_entity 语义不同),
     口径登记 docs/audit-and-notification-caliber-20260922.md

2. /api/cma/notifications/logs 增加 policy/note 口径字段,前端发送记录表
   empty-text 提示同一口径 —— 避免「B2 上线后 e1/e2 均 0 条」被当成页面故障
   (现库 8 行历史记录全为 alert_id IS NULL 的 OKR 复盘提醒,无账套归属)

回归:tests/test_workbench_entity_isolation_20260922.py 22/22;全量 1370 passed;前端 vite build 通过
2026-09-22 11:21:24 +08:00

68 lines
4.6 KiB
Markdown
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.
# 口径登记:审计留痕(`operation_logs`)与通知发送记录(`notification_logs`)
- **立档**: wecom-fullstack,2026-09-22
- **来源**: DAMA 独立回查 `dama-recheck-workbench-entity-isolation-20260922`(2026-09-22 11:07–11:20)
§4b(审计字面与库内既有口径不一致 / 无 `entity_id` 列 / `user_id=None`)与
§5(B2 上线后 `/notifications/logs` 在 e1/e2 均为 0 条 = 口径副作用)两处待办
- **性质**: 口径登记 + 代码收口说明。**不含**任何数据订正(数据订正另立单 + 审批)
---
## 一、`operation_logs` 审计字面字典(换账套类)
| action 字面 | 写入端点 | 语义 | target_type | target_id |
|---|---|---|---|---|
| `switch_entity` | `post /api/cma/auth/switch-entity`(`app/api/auth.py`) | 改**用户自己的会话账套**(重签 token、旧 token 下线) | `entity` | 目标 `entity_id` |
| `switch_tenant_state` | `post /api/cma/tenant/switch`(`app/api/tenant.py`) | 改**平台全局状态记录** `system_configs.current_tenant`(项目Bot 分发 A2A 任务用;**非数据维度**) | `entity` | 目标 `entity_id` |
**为什么两个字面不合并**:两者作用域不同(个人会话 vs 全局单值键),合并后审计上不可分辨
「谁把自己切走了」与「谁改了全平台状态」。DAMA 复核提出「统一为 `switch_entity` 或登记字典」——
本档取**后者(登记不合并)**,原因是前者会丢语义可分辨性;若后续裁定要求合并,改动面为
`app/api/tenant.py` 一行字面 + 本表一行(可逆)。
**可归属与可过滤(2026-09-22 收口,随本档同一提交落地)**:
- `operation_logs` **无** `entity_id` 列(仅 `user_id / action / target_type / target_id / detail / created_at`)。
故「按账套查换账套记录」不靠新列,而靠**目标维度**:
```sql
SELECT * FROM operation_logs WHERE target_type='entity' AND target_id=? ORDER BY id DESC;
```
- `/tenant/switch` 原写 `user_id=None`(端点无当前用户注入)⇒ 事后无法追操作人。
已改为注入会话用户(`user=Depends(require_auth_401)` → `user_id=user.id`),
并把 `target_type='entity'` / `target_id=entity_id` 与 `switch_entity` 对齐。
- `detail` JSON 仍保留 `entity_id / entity_name / source / previous`(人读明细)。
**现网实况(只读核对,2026-09-22 11:15,`cma_reader` 16/16 PASS)**:
`switch_entity` = 49 行、`switch_tenant_state` = 0 行(本单尚未部署 ⇒ 字面是新引入的,
上线前库里无该字面属正常)。ops 止血那次 `POST /tenant/switch`(10:36:01,部署码)**没有**审计行——
部署码该端点无审计,正是本单补齐项。
---
## 二、`/api/cma/notifications/logs` 口径(发送记录页)
- **归属口径**:`notification_logs` 无 `entity_id` 列 ⇒ 经**父键** join 归属:
`notification_logs.alert_id → kpi_alerts.id → kpi_alerts.entity_id`;按会话账套过滤,
与 batch5 对 `kpi_data_quality_log` 的处置同口径(**宁少勿错**)。
- **`alert_id IS NULL` 的行**:无可归属父键 ⇒ **不返回给任何账套**(不跨账套暴露)。
现库 8 行历史记录(2026-09-11/12「OKR 季度复盘提醒」)**100% 属此类**,
写入方 = `backend/scripts/okr_review_reminder.py`(复用本表留存审计,`alert_id` 留空)。
- **上线后现象(须预期,不是故障)**:`/notifications/logs` 在 e1/e2 均返回 `total=0`,
前端发送记录页为空。现役预警推送写入器 `app/utils/notifier.py` **必带** `alert_id`,
故此后由预警产生的新记录会正常显示;只有「无父键历史行」永不出现在该页。
- **页面与接口的提示(随本档同一提交落地)**:
- 接口返回 `policy="alert-linked-only"` + `note`(说明「仅显示与预警关联的推送」);
- 前端 `NotificationManage.vue` 发送记录表 `empty-text` 写明同一口径。
- **未做(留待 P2 裁定)**:为无父键通知单列只读视图(需先给 `notification_logs` 补账套维度,
属 DDL/迁移,超出「零迁移 B2」范围)。
---
## 三、本档未覆盖(另立单,见对应任务文件)
1. `kpi_alerts` id 589/590 跨账套错挂(`entity_id=1` 挂 e2 的 `BH_ADM_EXPENSE`):数据订正须
另立单 + 任总放行(生产写)。
2. `kpi_id=1` 兜底残留清点(9 行、其中 1 行 `pending`)与归属裁定。
3. 同源全局键继续拆账套:`kpi_alert_higher_better`、`kpi_visibility`(DAMA §7 反向挑错新增漏项)、
`budget_mode` / `budget_driver_mode`(batch5 §七 已记录未施工)。