fix(cma): 工作台跨实体污染治本 — 全局键拆账套 + 缺列收口 + 预警归属兜底
任务 dama-workbench-entity-isolation-fullstack-20260922(DAMA 诊断 / 项目Bot batch5 §B / 任总 2026-09-22「都批」) B1 现金基线拆账套(零 DDL) - cash_forecast_engine: 键 cash.current_balance → cash.current_balance.<entity_id>; 读只读本账套键、不做旧键回退;set_* 签名 entity_id 必传(漏传 TypeError) - cash.py: POST /balance 挂 get_entity_id;网银流水联动写回带账套 B3(a) alignment_config 拆账套(零 DDL) - alignment.py: GET/POST /config、GET /tree 只读 alignment_config.<entity_id>;未配置=configured:false B3(b) 预警 KPI 归属兜底 - bot_bridge_v2: 去掉 kpi_id or 1 兜底;本账套无 KPI 定义 → 跳过建预警 + OperationLog 留痕 (kpi_alerts.kpi_id NOT NULL ⇒ 方案A 置 NULL 不可行,走方案B 跳过+审计) B2 缺列五表分表处置 - notification_logs: 经 alert_id → kpi_alerts.entity_id join 归属(无父键行不返回) - notification_channels / okr_templates: 集团共享口径写入 docstring(有意不过滤) - system_configs: 由 B1/B3(a) 拆键覆盖;kpi_data_quality_logs 沿用 batch5 的 join 收口 P0 语义收敛 - tenant.py: current_tenant 收敛为项目Bot A2A 状态记录,GET 仅 it/ceo;POST 补 operation_logs 审计 证据:新增/改写用例 22 条;全量 1370 passed(基线 1348)
This commit is contained in:
@@ -21,7 +21,7 @@ from app.database import get_db
|
||||
from app.models import (
|
||||
MpmResult, BotBridgeConfig,
|
||||
KPIDefinition, KPIValue, KPIAlert,
|
||||
ActionPlan, Entity,
|
||||
ActionPlan, Entity, OperationLog,
|
||||
)
|
||||
from app.risk_levels import risk_level
|
||||
# 裁定 R1/R5(2026-09-19 财务口径):demo 账套任何写通道不产生业务预警;期间非规整月不入库(单一事实源)
|
||||
@@ -364,6 +364,7 @@ def receive_mpm_result(
|
||||
|
||||
# ── 3. 触发预警 ──
|
||||
alerts = []
|
||||
alerts_skipped_no_kpi = [] # B3(b): KPI 定义缺失而跳过的预警(留痕用)
|
||||
for field, config in ALERT_THRESHOLDS.items():
|
||||
field_value = results.get(field)
|
||||
if field_value is None:
|
||||
@@ -383,6 +384,12 @@ def receive_mpm_result(
|
||||
logger.info("R5 期间守卫:跳过不入库(不可归期,非规整月 YYYY-MM)period=%r", period)
|
||||
continue
|
||||
# 查找关联KPI
|
||||
# B3(b)(2026-09-22 dama-workbench-entity-isolation-fullstack-20260922):
|
||||
# 修复前 `kpi_id=kpi_id or 1` —— KPI 定义缺失时把预警错挂到「全库第 1 个 KPI」
|
||||
# (该 KPI 可能属他账套:预警 entity_id 正确、KPI 维度却跨了账套)。
|
||||
# 现改为**显式校验**:拿不到本账套的 KPI 定义 → 不建预警(跳过)+ 落 OperationLog 留痕。
|
||||
# 为什么不是 `kpi_id=None`:`kpi_alerts.kpi_id` 为 NOT NULL(models/__init__.py:145),
|
||||
# batch5 §B3(b) 方案 A(保留可见性)在本 schema 下不可行;方案 B(跳过 + 审计)落地。
|
||||
kpi_id = None
|
||||
if kpi_code:
|
||||
kpi_def = db.query(KPIDefinition).filter(
|
||||
@@ -392,11 +399,38 @@ def receive_mpm_result(
|
||||
if kpi_def:
|
||||
kpi_id = kpi_def.id
|
||||
|
||||
# 创建预警
|
||||
alert_msg = f"⚠️ {kpi_name}异常: {field_value:.1f}% (阈值: {threshold}%) [entity={entity.short_name or entity.name}, period={period}]"
|
||||
|
||||
if kpi_id is None:
|
||||
# 宁缺勿错:不入库,但必须留痕(防「预警静默丢失」)
|
||||
alerts_skipped_no_kpi.append(f"{kpi_name}(kpi_code={kpi_code!r})")
|
||||
db.add(OperationLog(
|
||||
user_id=None,
|
||||
action="skip_alert",
|
||||
target_type="alert",
|
||||
target_id=None,
|
||||
detail={
|
||||
"reason": "kpi_definition_not_found_for_entity",
|
||||
"kpi_code": kpi_code,
|
||||
"kpi_name": kpi_name,
|
||||
"entity_id": entity_id,
|
||||
"period": period,
|
||||
"field": field,
|
||||
"field_value": field_value,
|
||||
"threshold": threshold,
|
||||
"source": "bot_bridge_v2.mpm-result",
|
||||
},
|
||||
))
|
||||
logger.warning(
|
||||
"B3(b) 跳过建预警(KPI定义缺失,不再回落 kpi_id=1): kpi_code=%r entity_id=%s period=%s",
|
||||
kpi_code, entity_id, period,
|
||||
)
|
||||
continue
|
||||
|
||||
# 创建预警
|
||||
alert = KPIAlert(
|
||||
entity_id=entity_id, # 2026-09-13 F1: 显式写归属
|
||||
kpi_id=kpi_id or 1, # fallback to first KPI if not found
|
||||
kpi_id=kpi_id, # B3(b): 显式校验后的本账套 KPI(禁止 or 1 兜底)
|
||||
alert_level="red",
|
||||
alert_message=alert_msg,
|
||||
alert_type="actual",
|
||||
@@ -413,10 +447,13 @@ def receive_mpm_result(
|
||||
"success": True,
|
||||
"kpi_updated": len(kpi_updates),
|
||||
"alerts_triggered": len(alerts),
|
||||
# B3(b): 因本账套无对应 KPI 定义而跳过的预警(已落 OperationLog,非静默丢失)
|
||||
"alerts_skipped_missing_kpi": len(alerts_skipped_no_kpi),
|
||||
"mpm_record_id": record.id,
|
||||
"details": {
|
||||
"kpi_codes": kpi_updates,
|
||||
"alerts": alerts,
|
||||
"alerts_skipped_missing_kpi": alerts_skipped_no_kpi,
|
||||
},
|
||||
"skipped_non_month_period": (not period_attributable),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user