Files
cma-management/backend/app/main.py
T
Hermes CI Fix 94aeb14e95 feat: 预算系统6项技术改进(告警归因/实际值自动归集/真零基/派生规则/告警路径统一/现金流分类)
P1-③ 告警归因: budget_deviation_alerts+alert_type/attribution/scenario_id, 归因引擎alert_attribution.py(子KPI/科目/量价差/趋势), deviation-check统一写归因+场景, GET /deviation-alerts/{id}/attribution详情(旧告警现场组装)
P1-④ 实际值自动归集: kpi_value_sources/kpi_value_collect_logs表+CRUD+试跑+覆盖率, 采集器kpi_value_collector.py(voucher_details/进销存/cash_plans按entity+period汇总, 幂等upsert不覆盖人工), crontab每日06:30
P2-① 真零基: budget_zero_based_items逐项论证表+generate, method-comparison有论证项逐项求和is_demo=false否则fallback
P2-② 派生规则: budget_derivation_rules配置表, apply-method优先读规则rule_source=configured
P2-⑤ 告警双路径合并: deviation_engine.build_deviation_alert统一函数, 方向列表配置化kpi_alert_higher_better+alert-direction接口
P2-⑥ 现金流分类: cash_plan_classify_rules规则表+cash_plan_unclassified待分类队列, sync-cash-plans未命中进队列不静默跳过
新增: GET /kpis/{kpi_id}/values + 前端kpiApi.values(归集标签页数据源), scenario_suggestions幂等seed(init_db)
测试: test_budget_tech_improve.py 15用例, 预算相关96 passed, 全量646 passed
2026-08-28 18:03:47 +08:00

170 lines
6.6 KiB
Python

"""管理会计OS — 主入口"""
import logging
from fastapi import FastAPI, Request, Depends
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from dotenv import load_dotenv
from app.database import init_db
from app.api import auth, kpis, kpi_governance, templates, maps, dashboard, data, alerts, ai_analysis, alert_rules, users, thresholds, notifications, permissions, action_plans, alignment, org, objectives, versions, budget, cost, predict, reports, security, knowledge, bot_bridge, bot_bridge_v2, lead, tenant, customer_dashboard, deviation_push, budget_generate, knowledge_articles, kpi_causality, data_quality, bi_reports, entities, bsc_layers, okr, okr_templates, subjects, driver_budget, bot_kpis, ontology, bot_iron_law, analysis_results, expenses, cash, tax_compliance, verify, growth_quality, products, data_classification, value_sources, zero_based, derivation_rules, cash_classify
from app.utils.cache import clear_all as clear_cache, delete as delete_cache
from scripts.erp_sync import run_sync as run_erp_sync
from app.auth_middleware import require_auth
from app.api.audit_log import bot_audit_middleware
load_dotenv()
# 日志配置
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger("cma")
app = FastAPI(title="管理会计OS API", version="1.0.0", docs_url="/docs")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Bot API 操作审计(L1-L4分级标注 + JSON行审计日志,不阻塞业务)
app.middleware("http")(bot_audit_middleware)
app.include_router(auth.router)
app.include_router(kpis.router)
app.include_router(kpi_governance.router)
app.include_router(templates.router)
app.include_router(maps.router)
app.include_router(dashboard.router)
app.include_router(data.router)
app.include_router(alerts.router)
app.include_router(ai_analysis.router)
app.include_router(alert_rules.router)
app.include_router(users.router)
app.include_router(thresholds.router)
app.include_router(notifications.router)
app.include_router(permissions.router)
app.include_router(action_plans.router)
app.include_router(alignment.router)
app.include_router(org.router)
app.include_router(objectives.router)
app.include_router(versions.router)
app.include_router(budget.router)
app.include_router(value_sources.router)
app.include_router(zero_based.router)
app.include_router(derivation_rules.router)
app.include_router(cash_classify.router)
app.include_router(cost.router)
app.include_router(predict.router)
app.include_router(growth_quality.router)
app.include_router(products.router)
app.include_router(reports.router)
app.include_router(security.router)
app.include_router(knowledge.router)
app.include_router(bot_bridge.router)
app.include_router(bot_bridge_v2.router)
app.include_router(lead.router)
app.include_router(tenant.router)
app.include_router(customer_dashboard.router)
app.include_router(deviation_push.router)
app.include_router(budget_generate.router)
app.include_router(knowledge_articles.router)
app.include_router(kpi_causality.router)
app.include_router(data_quality.router)
app.include_router(bi_reports.router)
app.include_router(entities.router)
app.include_router(bsc_layers.router)
app.include_router(okr.router)
app.include_router(okr_templates.router)
app.include_router(subjects.router)
app.include_router(driver_budget.router)
app.include_router(bot_kpis.router)
app.include_router(ontology.router)
app.include_router(bot_iron_law.router)
app.include_router(analysis_results.router)
app.include_router(expenses.router)
app.include_router(cash.router)
app.include_router(data_classification.router)
app.include_router(tax_compliance.router)
app.include_router(verify.router)
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
logger.error(f"未捕获异常: {exc}", exc_info=True)
return JSONResponse(status_code=500, content={"detail": "服务器内部错误"})
@app.on_event("startup")
def startup():
init_db()
logger.info("管理会计OS后端启动完成")
@app.post("/api/cma/admin/erp-sync")
def admin_erp_sync(user=Depends(require_auth), kpi_codes: str = None):
"""手动触发ERP数据同步"""
kpi_list = kpi_codes.split(",") if kpi_codes else None
try:
run_erp_sync(dry_run=False, kpi_codes=kpi_list, use_api=True)
return {"message": "ERP同步完成", "kpis": kpi_list}
except Exception as e:
return JSONResponse(status_code=500, content={"detail": f"同步失败: {str(e)}"})
@app.get("/api/cma/admin/erp-sync/dry-run")
def admin_erp_sync_dry_run(user=Depends(require_auth), kpi_codes: str = None):
"试运行,不写入数据库"""
kpi_list = kpi_codes.split(",") if kpi_codes else None
try:
run_erp_sync(dry_run=True, kpi_codes=kpi_list, use_api=True)
return {"message": "试运行完成"}
except Exception as e:
return JSONResponse(status_code=500, content={"detail": f"试运行失败: {str(e)}"})
@app.post("/api/cma/admin/alerts/check")
def admin_check_alerts():
"""手动触发预警检查(含资金管理预警:缺口前3天 + 到期未收款)"""
from app.database import get_session_local
from scripts.alert_generator import generate_and_push
from app.utils.cash_forecast_engine import check_cash_alerts
from app.models import Entity
db = get_session_local()()
try:
result = generate_and_push(db)
# 资金管理预警 — 对每个激活企业执行
cash_result = None
try:
entities = db.query(Entity).filter(Entity.status == "active").all()
cash_list = []
for e in entities:
cash_list.append(check_cash_alerts(db, entity_id=e.id))
cash_result = {"entities": len(entities), "details": cash_list}
except Exception as e:
logger.warning(f"资金预警检查失败: {e}")
return {"message": "预警检查完成", "result": result, "cash": cash_result}
except Exception as e:
return JSONResponse(status_code=500, content={"detail": f"检查失败: {str(e)}"})
finally:
db.close()
@app.post("/api/cma/admin/cache/clear")
def admin_clear_cache(module: str = None):
"""清空缓存,指定module则只清该模块"""
if module:
delete_cache(module)
return {"message": f"缓存已清空: {module}"}
else:
clear_cache()
return {"message": "全部缓存已清空"}
@app.get("/health")
def health():
return {"status": "ok", "version": "1.0.0"}