feat: 预测性成本智能MVP — KPI趋势预测引擎(线性回归/移动平均)+API+前端Tab+pytest覆盖
This commit is contained in:
@@ -714,3 +714,53 @@ def api_growth_quality(request: Request, data: dict):
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(400, f"增长质量诊断失败: {str(e)}")
|
||||
|
||||
|
||||
# ── KPI趋势预测(预测性成本智能 MVP) ────────────────────────────
|
||||
from app.utils.kpi_forecast_engine import ( # noqa: E402
|
||||
MODELS, forecast_kpi, forecast_finance_kpis,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/kpi-forecast")
|
||||
def api_kpi_forecast(
|
||||
kpi_code: str,
|
||||
periods: int = 3,
|
||||
model: str = "linear",
|
||||
entity_id: int = Depends(get_entity_id),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""单个财务KPI预测 — 线性回归/移动平均,多租户隔离(entity_id 权限校验)"""
|
||||
if periods < 0 or periods > 24:
|
||||
raise HTTPException(400, "periods 必须在 0~24 之间")
|
||||
if model not in MODELS:
|
||||
raise HTTPException(400, f"不支持的模型: {model},可选: {'/'.join(MODELS)}")
|
||||
result = forecast_kpi(entity_id, kpi_code, db, periods=periods, model=model)
|
||||
if result is None:
|
||||
raise HTTPException(
|
||||
404,
|
||||
f"KPI {kpi_code} 在企业 entity_id={entity_id} 下不存在,或历史数据不足(至少2条)",
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@router.get("/kpi-forecast/finance")
|
||||
def api_kpi_forecast_finance(
|
||||
periods: int = 3,
|
||||
model: str = "linear",
|
||||
entity_id: int = Depends(get_entity_id),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""批量预测该企业全部财务维度KPI(历史≥3条),按可预测性排序"""
|
||||
if periods < 0 or periods > 24:
|
||||
raise HTTPException(400, "periods 必须在 0~24 之间")
|
||||
if model not in MODELS:
|
||||
raise HTTPException(400, f"不支持的模型: {model},可选: {'/'.join(MODELS)}")
|
||||
results = forecast_finance_kpis(entity_id, db, periods=periods, model=model)
|
||||
return {
|
||||
"entity_id": entity_id,
|
||||
"model": model,
|
||||
"periods": periods,
|
||||
"total": len(results),
|
||||
"data": results,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user