fix(alerts): enforce caliber ruling R5

- alert_entity_scope: add is_attributable_period / guard_alert_period (R5) and entity_alert_guard (R1, any write path)
- alert_rules.run_all_alert_checks: skip non-YYYY-MM period alert rows (R5)
- bot_bridge / bot_bridge_v2: R1 demo-entity guard + R5 period guard on write paths
- tests: new R5 suite (10 cases)

Not pushed / not deployed (release gate held by 任总).
This commit is contained in:
Hermes CI Fix
2026-09-19 10:42:09 +08:00
parent 3beec14d96
commit 5a99ed87e0
5 changed files with 347 additions and 6 deletions
+21 -1
View File
@@ -24,6 +24,8 @@ from app.models import (
ActionPlan, Entity,
)
from app.risk_levels import risk_level
# 裁定 R1/R5(2026-09-19 财务口径):demo 账套任何写通道不产生业务预警;期间非规整月不入库(单一事实源)
from app.utils.alert_entity_scope import entity_alert_guard, guard_alert_period
logger = logging.getLogger("cma.bot_bridge_v2")
@@ -307,6 +309,12 @@ def receive_mpm_result(
if not entity:
raise HTTPException(404, f"实体entity_id={entity_id}不存在")
# R1/R5(裁定 2026-09-19):「R1 与入口无关」——Bot 通道按显式 entity 写入同样不得产出业务预警;
# 期间非规整月(含 2026-H1/H2 伪期间)的 kpi_alerts 行一律不入库(跳过 + 记日志)。
alerts_allowed = entity_alert_guard(db, entity_id, source="bot_bridge_v2.receive_mpm_result")
period_attributable = guard_alert_period(period, source="bot_bridge_v2.receive_mpm_result",
detail="entity_id=%s" % entity_id)
# ── 1. 写入MPM结果 ──
record = MpmResult(
entity_id=entity_id,
@@ -367,6 +375,13 @@ def receive_mpm_result(
if (operator == ">" and field_value > threshold) or \
(operator == ">=" and field_value >= threshold):
# R1/R5 生成侧守卫(裁定 2026-09-19):demo 账套 / 不可归期期间 → 该行不入库
if not alerts_allowed:
logger.info("R1 账套守卫:跳过不入库(demo 演示账套)entity_id=%s", entity_id)
continue
if not period_attributable:
logger.info("R5 期间守卫:跳过不入库(不可归期,非规整月 YYYY-MM)period=%r", period)
continue
# 查找关联KPI
kpi_id = None
if kpi_code:
@@ -394,7 +409,7 @@ def receive_mpm_result(
db.commit()
db.refresh(record)
return {
resp = {
"success": True,
"kpi_updated": len(kpi_updates),
"alerts_triggered": len(alerts),
@@ -403,7 +418,12 @@ def receive_mpm_result(
"kpi_codes": kpi_updates,
"alerts": alerts,
},
"skipped_non_month_period": (not period_attributable),
}
if not alerts_allowed:
# R1(裁定 2026-09-19):demo 账套在 Bot 通道同样不产生业务预警
resp.update({"skipped": True, "reason": "R1_demo_entity_excluded"})
return resp
@router.post("/kpi-result")