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:
@@ -8,6 +8,7 @@ from app.deps import get_entity_id
|
||||
from app.auth_middleware import require_auth, require_role
|
||||
from app.models import KPIDefinition, KPIValue, KPIAlert, StrategicMap, User, ActionPlan, BudgetPlan, AISuggestion
|
||||
from app.utils.cache import get as cache_get, set as cache_set
|
||||
from app.utils.alert_entity_scope import month_period_clause
|
||||
import json, hashlib, httpx, os, urllib.request, urllib.parse
|
||||
from datetime import datetime, date
|
||||
router = APIRouter(prefix="/api/cma/ai", tags=["AI分析"],
|
||||
@@ -102,6 +103,7 @@ def generate_rule_suggestions(db: Session, entity_id: int,
|
||||
KPIValue.entity_id == entity_id,
|
||||
KPIValue.entity_id.is_(None),
|
||||
),
|
||||
month_period_clause(KPIValue.period),
|
||||
).order_by(KPIValue.period.desc()).first()
|
||||
if not latest or latest.actual_value is None:
|
||||
continue
|
||||
@@ -286,7 +288,9 @@ async def dashboard_analysis(role: str = Query("ceo"), db: Session = Depends(get
|
||||
KPIDefinition.status == "active").all()
|
||||
kpi_summary = []
|
||||
for k in kpis:
|
||||
latest = db.query(KPIValue).filter(KPIValue.kpi_id == k.id).order_by(KPIValue.period.desc()).first()
|
||||
latest = db.query(KPIValue).filter(
|
||||
KPIValue.kpi_id == k.id, month_period_clause(KPIValue.period)
|
||||
).order_by(KPIValue.period.desc()).first()
|
||||
kpi_summary.append({
|
||||
"name": k.kpi_name,
|
||||
"code": k.kpi_code,
|
||||
@@ -455,7 +459,9 @@ async def dashboard_analysis_stream(role: str = Query("ceo"), db: Session = Depe
|
||||
).all()
|
||||
kpi_summary = []
|
||||
for k in kpis:
|
||||
latest = db.query(KPIValue).filter(KPIValue.kpi_id == k.id).order_by(KPIValue.period.desc()).first()
|
||||
latest = db.query(KPIValue).filter(
|
||||
KPIValue.kpi_id == k.id, month_period_clause(KPIValue.period)
|
||||
).order_by(KPIValue.period.desc()).first()
|
||||
kpi_summary.append({
|
||||
"name": k.kpi_name, "code": k.kpi_code, "dimension": k.dimension,
|
||||
"target": k.target_value, "actual": latest.actual_value if latest else None,
|
||||
@@ -505,7 +511,9 @@ async def ask_question(
|
||||
).all()
|
||||
kpi_context = []
|
||||
for k in kpis:
|
||||
latest = db.query(KPIValue).filter(KPIValue.kpi_id == k.id).order_by(KPIValue.period.desc()).first()
|
||||
latest = db.query(KPIValue).filter(
|
||||
KPIValue.kpi_id == k.id, month_period_clause(KPIValue.period)
|
||||
).order_by(KPIValue.period.desc()).first()
|
||||
alert = db.query(KPIAlert).filter(KPIAlert.kpi_id == k.id, KPIAlert.status == "pending").first()
|
||||
kpi_context.append(
|
||||
f"{k.kpi_name}({k.kpi_code}): 当前值={latest.actual_value if latest else '无'}"
|
||||
@@ -573,7 +581,9 @@ async def review_plans(
|
||||
for p in plans:
|
||||
kpi = db.query(KPIDefinition).filter(KPIDefinition.id == p.kpi_id).first()
|
||||
kpi_name = kpi.kpi_name if kpi else "未知"
|
||||
latest = db.query(KPIValue).filter(KPIValue.kpi_id == p.kpi_id).order_by(KPIValue.period.desc()).first()
|
||||
latest = db.query(KPIValue).filter(
|
||||
KPIValue.kpi_id == p.kpi_id, month_period_clause(KPIValue.period)
|
||||
).order_by(KPIValue.period.desc()).first()
|
||||
plan_text.append(
|
||||
f"- {p.title}\n"
|
||||
f" 关联KPI: {kpi_name}(当前值: {latest.actual_value if latest else '无'})\n"
|
||||
|
||||
Reference in New Issue
Block a user