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:
@@ -15,7 +15,8 @@ from app.deps import get_entity_id
|
||||
from app.auth_middleware import require_auth, require_role
|
||||
from app.models import KPIDefinition, KPIValue, KPIAlert, OperationLog
|
||||
# 裁定 R1(2026-09-19 财务口径):demo 账套不参与预警生成/遍历(单一事实源)
|
||||
from app.utils.alert_entity_scope import is_excluded_entity
|
||||
# 裁定 R5(2026-09-19 财务口径):期间非规整月 YYYY-MM → 该 kpi_alerts 行不入库(生成侧守卫)
|
||||
from app.utils.alert_entity_scope import guard_alert_period, is_excluded_entity
|
||||
|
||||
logger = logging.getLogger("alert_rules")
|
||||
|
||||
@@ -273,6 +274,9 @@ def run_all_alert_checks(db: Session = Depends(get_db), entity_id: int = Depends
|
||||
value_cache = {}
|
||||
|
||||
alerts_generated = 0
|
||||
# R5(裁定 2026-09-19):期间非规整月(含伪期间 2026-H1/H2)→ 该行不入库,跳过并记数
|
||||
skipped_non_month_period = 0
|
||||
skipped_non_month_period_kpis = []
|
||||
|
||||
for rule in rules:
|
||||
try:
|
||||
@@ -341,6 +345,14 @@ def run_all_alert_checks(db: Session = Depends(get_db), entity_id: int = Depends
|
||||
alert_level, alert_message = _check_trend(kpi.id, value, "down", params, db)
|
||||
|
||||
if alert_level and alert_level != "green":
|
||||
# R5 生成侧守卫(裁定 2026-09-19):期间非规整月 YYYY-MM → 该 kpi_alerts 行不入库。
|
||||
# 注:`latest_value` 按 period 字符串倒序取,`2026-H1` 会排在 `2026-09` 之前 →
|
||||
# 伪期间确实可能被取为「最新值」,此处是伪期间入库唯一的拦截点。
|
||||
if not guard_alert_period(period, source="alert_rules.run_all_alert_checks",
|
||||
detail="kpi=%s rule_id=%s" % (kpi.kpi_code, rule.id)):
|
||||
skipped_non_month_period += 1
|
||||
skipped_non_month_period_kpis.append("%s[%s]" % (kpi.kpi_code, period))
|
||||
continue
|
||||
# 检查是否已有相同预警
|
||||
existing_alert = db.query(KPIAlert).filter(
|
||||
KPIAlert.kpi_id == rule.kpi_id,
|
||||
@@ -366,7 +378,12 @@ def run_all_alert_checks(db: Session = Depends(get_db), entity_id: int = Depends
|
||||
continue
|
||||
|
||||
db.commit()
|
||||
return {"message": f"预警检查完成: 生成{alerts_generated}条", "generated": alerts_generated}
|
||||
if skipped_non_month_period:
|
||||
logger.info("R5 期间守卫:本次跳过 %s 条不可归期预警(%s)",
|
||||
skipped_non_month_period, skipped_non_month_period_kpis)
|
||||
return {"message": f"预警检查完成: 生成{alerts_generated}条", "generated": alerts_generated,
|
||||
"skipped_non_month_period": skipped_non_month_period,
|
||||
"skipped_non_month_period_kpis": skipped_non_month_period_kpis}
|
||||
|
||||
|
||||
@router.get("/dynamic-thresholds")
|
||||
|
||||
Reference in New Issue
Block a user