fix(fetch-caliber): 取数层排除半年/预测伪期间 + subjects 响应补 entity_id

R3 期间口径(仅 YYYY-MM)延伸到取数层:新增 alert_entity_scope.month_period_clause()(跨库可移植 SQL 等价式),应用到 15 个文件 26 处「按 period 倒序取最新值/最近N期」位点;dashboard 原 MySQL-only REGEXP 正则收口到同单一事实源。

GET /api/cma/subjects 响应增补 entity_id(NULL=全局标准准则科目 / N=账套私有明细),既有 10 字段语义与名称不变。

证据:pending-tasks/_reports/yanxue-20260924-cma-fetch-caliber-fixes-fullstack.md(改前/改后 A/B:F_NET_PROFIT 2026-H2/-55.0 → 2026-08/-10.46)。新增 7 例回归测试;全量 1 failed(预存在) 1473 passed,基线同项。
This commit is contained in:
Hermes CI Fix
2026-09-26 02:42:04 +08:00
parent 52b693ddbb
commit 242f3cf5ec
17 changed files with 282 additions and 18 deletions
+6 -1
View File
@@ -11,6 +11,7 @@ from app.deps import get_entity_id
from app.auth_middleware import require_auth, require_role, filter_kpis_by_role, kpi_visible_dims
from app.models import StrategicMap, MapObjective, KPIDefinition, KPIValue, KPIAlert, OperationLog, Entity, KPICausality, KPIHierarchy, User
from app.utils.oplog import log_op
from app.utils.alert_entity_scope import month_period_clause
from app.api.kpi_governance import validate_kpi_payload, kpi_issues_message
from app.utils.threshold_parser import (
calc_level as _tp_calc_level,
@@ -200,6 +201,7 @@ def get_kpi_score(
)
if period:
val_query = val_query.filter(KPIValue.period == period)
val_query = val_query.filter(month_period_clause(KPIValue.period))
latest_val = val_query.order_by(KPIValue.period.desc()).first()
current_val = latest_val.actual_value if latest_val else None
@@ -291,6 +293,7 @@ def get_kpi_glossary(
latest_val = db.query(KPIValue).filter(
KPIValue.kpi_id == k.id,
KPIValue.actual_value.isnot(None),
month_period_clause(KPIValue.period),
).order_by(KPIValue.period.desc()).first()
current_value = latest_val.actual_value if latest_val else None
@@ -568,7 +571,9 @@ def list_kpi_values(
raise HTTPException(404, "KPI不存在")
if kpi.entity_id != entity_id:
raise HTTPException(404, "KPI不存在")
vals = db.query(KPIValue).filter(KPIValue.kpi_id == kpi_id).order_by(KPIValue.period.desc()).all()
vals = db.query(KPIValue).filter(
KPIValue.kpi_id == kpi_id, month_period_clause(KPIValue.period)
).order_by(KPIValue.period.desc()).all()
return {
"data": [{
"id": v.id,