fix: 自动创建KPI时补全所有NOT NULL字段(formula/data_source/data_owner/unit/entity_id)
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
"""置信度评分系统 — 财务Bot分析结论管理"""
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func
|
||||
from app.database import get_db
|
||||
from app.auth_middleware import require_auth
|
||||
from app.models import AnalysisResult, KPIValue, KPIDefinition
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
|
||||
router = APIRouter(prefix="/api/cma/analysis", tags=["置信度评分"],
|
||||
dependencies=[Depends(require_auth)],
|
||||
)
|
||||
|
||||
|
||||
def _calc_confidence(has_actual: bool, has_target: bool, has_trend: bool, has_review: bool) -> int:
|
||||
"""基于数据完整度自动计算置信度"""
|
||||
if has_review:
|
||||
return 95
|
||||
if has_actual and has_target and has_trend:
|
||||
return 85
|
||||
if has_actual and has_target:
|
||||
return 70
|
||||
if has_actual:
|
||||
return 50
|
||||
return 30 # 无实际值,基于推测
|
||||
|
||||
|
||||
@router.get("/result")
|
||||
async def get_analysis_results(
|
||||
period: Optional[str] = Query(None, description="期间 YYYY-MM"),
|
||||
kpi_code: Optional[str] = Query(None, description="KPI编码"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""查询分析结论(按期间和/或KPI编码过滤)"""
|
||||
query = db.query(AnalysisResult).order_by(AnalysisResult.created_at.desc())
|
||||
|
||||
if period:
|
||||
query = query.filter(AnalysisResult.period == period)
|
||||
if kpi_code:
|
||||
query = query.filter(AnalysisResult.kpi_code == kpi_code)
|
||||
|
||||
results = query.all()
|
||||
|
||||
return {
|
||||
"total": len(results),
|
||||
"period": period,
|
||||
"results": [
|
||||
{
|
||||
"id": r.id,
|
||||
"period": r.period,
|
||||
"结论": r.conclusion,
|
||||
"置信度": f"{r.confidence}%",
|
||||
"数据来源": r.data_source,
|
||||
"计算逻辑": r.calculation_logic,
|
||||
"可比基准": r.comparable_benchmark,
|
||||
"局限": r.limitations,
|
||||
"kpi_code": r.kpi_code,
|
||||
"kpi_name": r.kpi_name,
|
||||
"has_actual": bool(r.has_actual),
|
||||
"has_target": bool(r.has_target),
|
||||
"has_trend": bool(r.has_trend),
|
||||
"has_review": bool(r.has_review),
|
||||
"created_at": r.created_at.strftime("%Y-%m-%d %H:%M:%S") if r.created_at else None,
|
||||
}
|
||||
for r in results
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@router.post("/result")
|
||||
async def create_analysis_result(
|
||||
period: str = Query(..., description="期间 YYYY-MM"),
|
||||
conclusion: str = Query(..., description="分析结论"),
|
||||
data_source: Optional[str] = Query(None, description="数据来源"),
|
||||
calculation_logic: Optional[str] = Query(None, description="计算逻辑"),
|
||||
comparable_benchmark: Optional[str] = Query(None, description="可比基准"),
|
||||
limitations: Optional[str] = Query(None, description="局限说明"),
|
||||
kpi_code: Optional[str] = Query(None, description="关联KPI编码"),
|
||||
kpi_name: Optional[str] = Query(None, description="关联KPI名称"),
|
||||
has_actual: bool = Query(False, description="有实际值"),
|
||||
has_target: bool = Query(False, description="有目标值"),
|
||||
has_trend: bool = Query(False, description="有历史趋势"),
|
||||
has_review: bool = Query(False, description="有人工复核"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""提交分析结果(自动计算置信度)"""
|
||||
confidence = _calc_confidence(has_actual, has_target, has_trend, has_review)
|
||||
|
||||
result = AnalysisResult(
|
||||
period=period,
|
||||
conclusion=conclusion,
|
||||
confidence=confidence,
|
||||
data_source=data_source,
|
||||
calculation_logic=calculation_logic,
|
||||
comparable_benchmark=comparable_benchmark,
|
||||
limitations=limitations,
|
||||
kpi_code=kpi_code,
|
||||
kpi_name=kpi_name,
|
||||
has_actual=1 if has_actual else 0,
|
||||
has_target=1 if has_target else 0,
|
||||
has_trend=1 if has_trend else 0,
|
||||
has_review=1 if has_review else 0,
|
||||
)
|
||||
db.add(result)
|
||||
db.commit()
|
||||
db.refresh(result)
|
||||
|
||||
return {
|
||||
"id": result.id,
|
||||
"period": result.period,
|
||||
"结论": result.conclusion,
|
||||
"置信度": f"{result.confidence}%",
|
||||
"数据来源": result.data_source,
|
||||
"计算逻辑": result.calculation_logic,
|
||||
"可比基准": result.comparable_benchmark,
|
||||
"局限": result.limitations,
|
||||
"confidence_score": result.confidence,
|
||||
"message": "分析结论已保存",
|
||||
}
|
||||
|
||||
|
||||
@router.delete("/result/{result_id}")
|
||||
async def delete_analysis_result(
|
||||
result_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""删除分析结论"""
|
||||
result = db.query(AnalysisResult).filter(AnalysisResult.id == result_id).first()
|
||||
if not result:
|
||||
raise HTTPException(404, "分析结论不存在")
|
||||
db.delete(result)
|
||||
db.commit()
|
||||
return {"message": "已删除"}
|
||||
|
||||
|
||||
@router.post("/auto-calculate")
|
||||
async def auto_calculate_confidence(
|
||||
period: str = Query(..., description="期间 YYYY-MM"),
|
||||
kpi_code: str = Query(..., description="KPI编码"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""根据KPI数据完整性自动生成置信度评分"""
|
||||
# 查找KPI定义
|
||||
kpi = db.query(KPIDefinition).filter(KPIDefinition.kpi_code == kpi_code).first()
|
||||
if not kpi:
|
||||
raise HTTPException(404, f"KPI编码 {kpi_code} 不存在")
|
||||
|
||||
# 查找该期间的实际值
|
||||
value = db.query(KPIValue).filter(
|
||||
KPIValue.kpi_id == kpi.id,
|
||||
KPIValue.period == period,
|
||||
).first()
|
||||
|
||||
# 查找历史数据(趋势)
|
||||
trend_values = db.query(KPIValue).filter(
|
||||
KPIValue.kpi_id == kpi.id,
|
||||
).order_by(KPIValue.period.desc()).limit(6).all()
|
||||
|
||||
has_actual = value is not None and value.actual_value is not None
|
||||
has_target = kpi.target_value is not None
|
||||
has_trend = len(trend_values) >= 2
|
||||
has_review = False
|
||||
|
||||
confidence = _calc_confidence(has_actual, has_target, has_trend, has_review)
|
||||
|
||||
return {
|
||||
"kpi_code": kpi_code,
|
||||
"kpi_name": kpi.kpi_name,
|
||||
"period": period,
|
||||
"has_actual": has_actual,
|
||||
"has_target": has_target,
|
||||
"has_trend": has_trend,
|
||||
"has_review": has_review,
|
||||
"confidence": confidence,
|
||||
"confidence_label": f"{confidence}%",
|
||||
"数据完备度": {
|
||||
"10%": "无数据",
|
||||
"50%": "有实际值",
|
||||
"70%": "有实际值+目标值",
|
||||
"85%": "有实际值+目标值+历史趋势",
|
||||
"95%": "有全部数据+人工复核",
|
||||
}.get(str(confidence), "基于推测"),
|
||||
}
|
||||
@@ -244,11 +244,17 @@ async def import_excel_smart(file: UploadFile = File(...), db: Session = Depends
|
||||
if not kpi_code:
|
||||
new_code = f"{stype_prefix}{len(kpis) + created_kpis + 1:03d}"
|
||||
new_kpi = KPIDefinition(
|
||||
entity_id=1,
|
||||
kpi_code=new_code,
|
||||
kpi_name=clean_name,
|
||||
dimension="finance",
|
||||
category="financial_report",
|
||||
formula="-",
|
||||
data_source_type="excel",
|
||||
data_source="Excel导入",
|
||||
data_owner="财务部",
|
||||
frequency="monthly",
|
||||
unit="元",
|
||||
status="active",
|
||||
)
|
||||
db.add(new_kpi)
|
||||
|
||||
Reference in New Issue
Block a user