From d785a22efd87392f087d7b97f141f075549876e0 Mon Sep 17 00:00:00 2001 From: Hermes CI Fix Date: Sat, 19 Sep 2026 08:35:25 +0800 Subject: [PATCH] fix(alerts): scope check-timeout batch upgrade by KPIAlert.entity_id GET /api/cma/alerts/check-timeout picked candidates via the join on KPIDefinition.entity_id, so a session could batch-upgrade (red + processing) alert rows whose own entity_id belongs to another account. Switch the filter to the alert row's own entity_id, drop the now-unneeded join and the unused KPIDefinition import. Write path only; no data change, candidate ids on consistent rows are identical before/after. Derived from dama-20260919-cma-alert-filter-and-cash-baseline-fix (C1/C2 residual in the write endpoint). --- backend/app/api/alerts.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/backend/app/api/alerts.py b/backend/app/api/alerts.py index 0ac16e31..5937a3b6 100644 --- a/backend/app/api/alerts.py +++ b/backend/app/api/alerts.py @@ -5,7 +5,7 @@ from sqlalchemy.orm import Session from app.database import get_db from app.deps import get_entity_id, assert_entity_slug_scope, resolve_session_slug from app.auth_middleware import require_auth, require_role -from app.models import KPIAlert, OperationLog, ActionPlan, KPIDefinition +from app.models import KPIAlert, OperationLog, ActionPlan import logging logger = logging.getLogger("cma.alerts") @@ -94,11 +94,16 @@ def escalate_alert(alert_id: int, data: dict, db: Session = Depends(get_db), ent @router.get("/check-timeout") def check_alert_timeout(db: Session = Depends(get_db), entity_id: int = Depends(get_entity_id)): """超时预警检测 — 超过24小时未处理的pending预警自动升级为红色 - (账套隔离: join kpi_definitions 按企业过滤, 2026-09-18 — 原为全账套批处理)""" + + 账套隔离(2026-09-19 写路径跨账套修复):按**预警行自身** KPIAlert.entity_id 过滤; + 修复前 join kpi_definitions 按 KPI 归属企业过滤 → KPI 属他账套而预警行属本账套的行 + 会被本账套调用**批量改写**(升级 red + 置 processing),比只读泄漏更严重 + (2026-08 之前为全账套批处理;2026-09-18 加的 join 口径见 dama-20260919 派生单)。 + """ from datetime import datetime, timedelta cutoff = datetime.now() - timedelta(hours=24) - timeout_alerts = db.query(KPIAlert).join(KPIDefinition, KPIDefinition.id == KPIAlert.kpi_id).filter( - KPIDefinition.entity_id == entity_id, + timeout_alerts = db.query(KPIAlert).filter( + KPIAlert.entity_id == entity_id, KPIAlert.status == "pending", KPIAlert.created_at < cutoff, KPIAlert.alert_level != "red",