"""预测模拟API — 管理会计OS""" import logging from fastapi import APIRouter, HTTPException from app.utils.predict_engine import ( cvp_analysis, npv, irr, sensitivity_analysis, scenario_analysis, ) logger = logging.getLogger("cma.predict") router = APIRouter(prefix="/api/cma/predict", tags=["预测模拟"]) @router.post("/cvp") def api_cvp_analysis(data: dict): """CVP本量利分析""" try: result = cvp_analysis( unit_price=float(data.get("unit_price", 0)), unit_variable_cost=float(data.get("unit_variable_cost", 0)), fixed_cost=float(data.get("fixed_cost", 0)), target_profit=float(data["target_profit"]) if data.get("target_profit") else None, actual_volume=float(data["actual_volume"]) if data.get("actual_volume") else None, ) return result except Exception as e: raise HTTPException(400, f"CVP计算失败: {str(e)}") @router.post("/investment") def api_investment_analysis(data: dict): """投资决策分析(NPV/IRR/回收期)""" try: initial = float(data.get("initial_investment", 0)) rate = float(data.get("discount_rate", 10)) cash_flows = [float(cf) for cf in data.get("cash_flows", [])] if not cash_flows: raise HTTPException(400, "现金流列表不能为空") npv_result = npv(initial, cash_flows, rate) irr_result = irr(initial, cash_flows) return { "npv_analysis": npv_result, "irr_analysis": irr_result, } except HTTPException: raise except Exception as e: raise HTTPException(400, f"投资决策计算失败: {str(e)}") @router.post("/sensitivity") def api_sensitivity_analysis(data: dict): """敏感性分析""" try: result = sensitivity_analysis( base_revenue=float(data.get("base_revenue", 0)), base_cost=float(data.get("base_cost", 0)), base_profit=float(data["base_profit"]) if data.get("base_profit") else None, step=int(data.get("step", 5)), max_step=int(data.get("max_step", 20)), ) return result except Exception as e: raise HTTPException(400, f"敏感性分析失败: {str(e)}") @router.post("/scenario") def api_scenario_analysis(data: dict): """情景模拟""" try: optimistic = data.get("optimistic", {}) pessimistic = data.get("pessimistic", {}) base = data.get("base", {}) if not all([optimistic, pessimistic, base]): raise HTTPException(400, "需要提供乐观/中性/悲观三个情景的参数") result = scenario_analysis( optimistic={ "revenue": float(optimistic.get("revenue", 0)), "cost": float(optimistic.get("cost", 0)), }, pessimistic={ "revenue": float(pessimistic.get("revenue", 0)), "cost": float(pessimistic.get("cost", 0)), }, base={ "revenue": float(base.get("revenue", 0)), "cost": float(base.get("cost", 0)), }, ) return result except HTTPException: raise except Exception as e: raise HTTPException(400, f"情景模拟失败: {str(e)}") @router.post("/cvp-detailed") def api_cvp_detailed(data: dict): """CVP本量利详细分析 — 含改善方案推演和保本图数据 (CMA P2)""" try: fixed_cost = float(data.get("fixed_cost", 617)) variable_cost_rate = float(data.get("variable_cost_rate", 0.4862)) unit_price = float(data.get("unit_price", 228)) current_volume = float(data.get("current_volume", 5300)) contribution_margin_rate = 1 - variable_cost_rate breakeven_revenue = round(fixed_cost / contribution_margin_rate, 2) breakeven_units = round(breakeven_revenue * 10000 / unit_price, 0) current_revenue = round(current_volume * unit_price / 10000, 2) current_profit = round(current_revenue * (1 - variable_cost_rate) - fixed_cost, 2) safety_margin = round((current_revenue - breakeven_revenue) / current_revenue * 100, 2) if current_revenue > 0 else 0 scenarios = [ {"name": "降固定费用至300万", "fixed_cost": 300, "variable_cost_rate": variable_cost_rate, "breakeven_revenue": round(300 / contribution_margin_rate, 2), "breakeven_units": round(300 / contribution_margin_rate * 10000 / unit_price, 0)}, {"name": "降变动成本率至30%", "fixed_cost": fixed_cost, "variable_cost_rate": 0.3, "breakeven_revenue": round(fixed_cost / 0.7, 2), "breakeven_units": round(fixed_cost / 0.7 * 10000 / unit_price, 0)}, {"name": "两者同时改善", "fixed_cost": 300, "variable_cost_rate": 0.3, "breakeven_revenue": round(300 / 0.7, 2), "breakeven_units": round(300 / 0.7 * 10000 / unit_price, 0)}, ] # 保本图数据点 chart_data = [] max_volume = int(max(breakeven_units * 2, current_volume * 3)) step = max(1, int(max_volume / 20)) for vol in range(0, int(max_volume) + step, step): rev = round(vol * unit_price / 10000, 2) tc = round(fixed_cost + rev * variable_cost_rate, 2) chart_data.append({"volume": vol, "revenue": rev, "total_cost": tc, "profit": round(rev - tc, 2)}) return { "fixed_cost": fixed_cost, "variable_cost_rate": round(variable_cost_rate * 100, 2), "unit_price": unit_price, "contribution_margin_rate": round(contribution_margin_rate * 100, 2), "breakeven_revenue": breakeven_revenue, "breakeven_units": int(breakeven_units), "current_revenue": current_revenue, "current_profit": current_profit, "current_volume": int(current_volume), "safety_margin": safety_margin, "scenarios": scenarios, "chart_data": chart_data, } except Exception as e: raise HTTPException(400, f"CVP详细分析失败: {str(e)}") # ── 实物期权计算器 ───────────────────────────────────────────── import math def _norm_cdf(x: float) -> float: """标准正态分布CDF — Abramowitz & Stegun 近似 (max error ≈ 1.5×10⁻⁷)""" a1, a2, a3, a4, a5 = 0.254829592, -0.284496736, 1.421413741, -1.453152027, 1.061405429 p = 0.3275911 sign = 1.0 if x < 0: sign = -1.0 x_abs = abs(x) / math.sqrt(2.0) t = 1.0 / (1.0 + p * x_abs) y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * math.exp(-x_abs * x_abs) return 0.5 * (1.0 + sign * y) def _black_scholes_call(S0: float, X: float, t: float, r: float, sigma: float) -> dict: """BSM看涨期权定价(扩张期权/延迟期权)""" sqrt_t = math.sqrt(t) d1 = (math.log(S0 / X) + (r + 0.5 * sigma ** 2) * t) / (sigma * sqrt_t) d2 = d1 - sigma * sqrt_t nd1 = _norm_cdf(d1) nd2 = _norm_cdf(d2) call_value = max(S0 * nd1 - X * math.exp(-r * t) * nd2, 0.0) return {"value": round(call_value, 4), "d1": round(d1, 4), "d2": round(d2, 4), "Nd1": round(nd1, 4), "Nd2": round(nd2, 4)} def _black_scholes_put(S0: float, X: float, t: float, r: float, sigma: float) -> dict: """BSM看跌期权定价(放弃期权/收缩期权)""" sqrt_t = math.sqrt(t) d1 = (math.log(S0 / X) + (r + 0.5 * sigma ** 2) * t) / (sigma * sqrt_t) d2 = d1 - sigma * sqrt_t nd1 = _norm_cdf(-d1) nd2 = _norm_cdf(-d2) put_value = max(X * math.exp(-r * t) * nd2 - S0 * nd1, 0.0) return {"value": round(put_value, 4), "d1": round(d1, 4), "d2": round(d2, 4), "N(-d1)": round(nd1, 4), "N(-d2)": round(nd2, 4)} def _binomial_tree_call(S0: float, X: float, t: float, r: float, sigma: float, n: int = 100) -> float: """二叉树欧式看涨期权定价(延迟期权)""" dt = t / n u = math.exp(sigma * math.sqrt(dt)) d = 1.0 / u p = (math.exp(r * dt) - d) / (u - d) discount = math.exp(-r * dt) prices = [S0 * (u ** (n - j)) * (d ** j) for j in range(n + 1)] values = [max(p - X, 0.0) for p in prices] for i in range(n - 1, -1, -1): for j in range(i + 1): values[j] = discount * (p * values[j] + (1 - p) * values[j + 1]) return max(values[0], 0.0) def _binomial_tree_american_put(S0: float, X: float, t: float, r: float, sigma: float, n: int = 100) -> float: """二叉树美式看跌期权定价(可随时放弃的放弃期权)""" dt = t / n u = math.exp(sigma * math.sqrt(dt)) d = 1.0 / u p = (math.exp(r * dt) - d) / (u - d) discount = math.exp(-r * dt) prices = [S0 * (u ** (n - j)) * (d ** j) for j in range(n + 1)] values = [max(X - p, 0.0) for p in prices] for i in range(n - 1, -1, -1): for j in range(i + 1): hold = discount * (p * values[j] + (1 - p) * values[j + 1]) exercise = X - (S0 * (u ** (i - j)) * (d ** j)) values[j] = max(hold, exercise) return max(values[0], 0.0) @router.post("/real-option") def api_real_option(data: dict): """实物期权计算器""" try: opt_type = data.get("opt_type", "expansion") # expansion|abandon|delay|shrink model = data.get("model", "bs") # bs|binomial S0 = float(data.get("S0", 100.0)) X = float(data.get("X", 80.0)) t = float(data.get("t", 3.0)) r = float(data.get("r", 0.0174)) sigma = float(data.get("sigma", 0.30)) expansion_factor = float(data.get("expansion_factor", 1.5)) salvage_value = float(data.get("salvage_value", S0 * 0.3)) n_steps = int(data.get("n_steps", 100)) # 输入校验 if S0 <= 0 or X <= 0 or t <= 0 or sigma <= 0: raise HTTPException(400, "参数必须为正数") if sigma > 2.0: raise HTTPException(400, "波动率σ不能超过200%") result = {"option_type": opt_type, "model": model, "S0": S0, "X": X, "t": t, "r": r, "sigma": sigma} # 计算期权价值 if opt_type in ("expansion", "delay") and model == "bs": bs = _black_scholes_call(S0, X, t, r, sigma) result["option_value"] = bs["value"] result["intermediate"] = {k: v for k, v in bs.items() if k != "value"} elif opt_type == "expansion" and model == "binomial": adj_X = X / expansion_factor bt_val = _binomial_tree_call(S0, adj_X, t, r, sigma, n_steps) option_value = max(bt_val * expansion_factor, 0.0) result["option_value"] = round(option_value, 4) result["intermediate"] = {"expansion_factor": expansion_factor, "adjusted_X": round(adj_X, 4), "tree_value": round(bt_val, 4)} elif opt_type == "delay" and model == "binomial": option_value = _binomial_tree_call(S0, X, t, r, sigma, n_steps) result["option_value"] = round(option_value, 4) # Also compute BS for reference bs = _black_scholes_call(S0, X, t, r, sigma) result["intermediate"] = {"n_steps": n_steps, "bs_reference": round(bs["value"], 4)} elif opt_type in ("abandon", "shrink") and model == "bs": effective_X = salvage_value if opt_type == "abandon" else X bs = _black_scholes_put(S0, effective_X, t, r, sigma) result["option_value"] = bs["value"] result["intermediate"] = {k: v for k, v in bs.items() if k != "value"} if opt_type == "abandon": result["intermediate"]["salvage_value"] = effective_X elif opt_type == "abandon" and model == "binomial": bt_val = _binomial_tree_american_put(S0, salvage_value, t, r, sigma, n_steps) result["option_value"] = round(bt_val, 4) result["intermediate"] = {"n_steps": n_steps, "salvage_value": salvage_value} else: raise HTTPException(400, f"不支持的组合: {opt_type} + {model}") # 决策建议 val = result["option_value"] if val > 0: result["suggestion"] = "期权价值 > 0,管理弹性有价值,建议保留决策弹性,在有利时机行权" result["suggestion_type"] = "positive" else: result["suggestion"] = "期权价值 ≈ 0,弹性无明显价值,建议按传统NPV决策,无需等待" result["suggestion_type"] = "neutral" # 扩展NPV(假设传统NPV = S0 - X) npv_without = S0 - X expanded_npv = npv_without + val result["npv_without_flexibility"] = round(npv_without, 4) result["expanded_npv"] = round(expanded_npv, 4) if expanded_npv > 0: result["decision"] = "✅ 扩展NPV > 0,含弹性后项目整体值得投资" else: result["decision"] = "❌ 扩展NPV ≤ 0,含弹性后项目仍不值得投资" # 敏感性分析数据(σ从10%~90%变化) sensitivity = [] for s_pct in range(5, 96, 5): s = s_pct / 100.0 if opt_type in ("expansion", "delay"): if model == "bs": v = _black_scholes_call(S0, X, t, r, s)["value"] else: bt = _binomial_tree_call(S0, X, t, r, s, n_steps) v = bt * expansion_factor if opt_type == "expansion" else bt else: eff_X = salvage_value if opt_type == "abandon" else X if model == "bs": v = _black_scholes_put(S0, eff_X, t, r, s)["value"] else: v = _binomial_tree_american_put(S0, eff_X, t, r, s, n_steps) sensitivity.append({"sigma": s_pct, "option_value": round(v, 4)}) result["sensitivity"] = sensitivity # 警告提示 warnings = [] if t * sigma * sigma * 0.5 > r: warnings.append("高波动+长时间,延迟价值显著") if S0 < X: warnings.append("价外期权,期权价值较低") if S0 > X * 1.5: warnings.append("深度价内,几乎确定行权") if sigma < 0.10: warnings.append("波动率过低,期权价值趋近于0") if t > 10: warnings.append("长期期权,贴现因子影响大") result["warnings"] = warnings return result except HTTPException: raise except Exception as e: raise HTTPException(400, f"实物期权计算失败: {str(e)}") # ── 增长质量诊断 ───────────────────────────────────────────── def _score_revenue_structure(entity: dict) -> int: """营收结构评分:渠补率越低越好""" rebate_rate = float(entity.get("rebateRate", entity.get("rebate_rate", 0))) if rebate_rate > 80: return 1 if rebate_rate > 60: return 2 if rebate_rate > 40: return 3 if rebate_rate > 20: return 4 return 5 def _score_profit_structure(entity: dict) -> int: """利润结构评分:真实毛利率越高越好""" gross_margin = float(entity.get("trueGrossMargin", entity.get("true_gross_margin", 0))) if gross_margin < 0: return 1 if gross_margin < 10: return 2 if gross_margin < 20: return 3 if gross_margin < 30: return 4 return 5 def _score_cash_assets(entity: dict) -> int: """现金资产评分:现金比率越高越好""" cash_ratio = float(entity.get("cashRatio", entity.get("cash_ratio", 0))) if cash_ratio < 10: return 1 if cash_ratio < 30: return 2 if cash_ratio < 50: return 3 if cash_ratio < 100: return 4 return 5 def _score_growth_driver(entity: dict) -> int: """增长驱动评分:费用增速相对收入增速越低越好""" expense_growth = float(entity.get("expenseGrowthRate", entity.get("expense_growth_rate", 0))) revenue_growth = float(entity.get("revenueGrowthRate", entity.get("revenue_growth_rate", 1))) if revenue_growth <= 0: revenue_growth = 1 # prevent div by zero ratio = expense_growth / revenue_growth if ratio > 1.5: return 1 if ratio > 1.2: return 2 if ratio > 1.0: return 3 if ratio > 0.8: return 4 return 5 def _score_org_efficiency(entity: dict) -> int: """组织效率评分:管理费/净收入越低越好""" mgmt_ratio = float(entity.get("mgmtRatio", entity.get("mgmt_ratio", 0))) if mgmt_ratio > 300: return 1 if mgmt_ratio > 200: return 2 if mgmt_ratio > 100: return 3 if mgmt_ratio > 50: return 4 return 5 def _diagnosis_text(overall: float, dimensions: dict, entity_name: str) -> str: """根据评分生成诊断结论""" lines = [] low_dims = {k: v for k, v in dimensions.items() if v["score"] <= 2} mid_dims = {k: v for k, v in dimensions.items() if 2 < v["score"] < 4} dim_labels = { "revenueStructure": "营收结构", "profitStructure": "利润结构", "cashAssets": "现金资产", "growthDriver": "增长驱动", "orgEfficiency": "组织效率", } if overall < 2: lines.append(f"{entity_name}的增长质量评分仅{overall}分,属于「越增长越重」类型。") lines.append("增长主要依赖资源投入而非核心能力积累,可持续性堪忧。") elif overall < 3: lines.append(f"{entity_name}的增长质量评分{overall}分,需重点关注。") lines.append("部分维度存在风险,增长质量有待改善。") elif overall < 4: lines.append(f"{entity_name}的增长质量评分{overall}分,处于中等水平。") lines.append("多数维度表现尚可,仍有优化空间。") else: lines.append(f"{entity_name}的增长质量评分{overall}分,「越增长越轻」。") lines.append("增长模式健康,具备持续增长能力。") if low_dims: low_names = [dim_labels.get(k, k) for k in low_dims] lines.append(f"⚠️ 需重点关注:{'、'.join(low_names)}评分偏低(≤2分)。") if mid_dims: mid_names = [dim_labels.get(k, k) for k in mid_dims] lines.append(f"💡 可优化:{'、'.join(mid_names)}有提升空间。") # 具体建议(硬编码的关键诊断) if dimensions.get("revenueStructure", {}).get("score", 5) <= 2: lines.append("• 营收依赖渠道返利,建议降低渠补率、拓展直销渠道。") if dimensions.get("orgEfficiency", {}).get("score", 5) <= 2: lines.append("• 管理费率高企,建议精简费用结构、优化运营效率。") if dimensions.get("cashAssets", {}).get("score", 5) <= 2: lines.append("• 现金比率极低,存在断流风险,建议加强现金流管理。") if dimensions.get("growthDriver", {}).get("score", 5) <= 2: lines.append("• 费用增速远超收入增速,增长不可持续,需控制费用膨胀。") return "\n".join(lines) def _generate_improvement_suggestions(dimension: str, score: int, entity: dict) -> list: """为指定维度生成改善建议""" suggestions = [] if dimension == "revenueStructure": rebate = float(entity.get("rebateRate", entity.get("rebate_rate", 0))) if score <= 2: target_rebate = max(rebate - 10, 0) savings = f"释放现金{round(rebate - target_rebate, 1)}%/月" suggestions.append(f"渠补谈判:{rebate}%→{target_rebate}%({savings})") suggestions.append("客户分散:拓展直销渠道,降低渠道依赖") suggestions.append("渠补制度:分级管理,差异化返利") else: suggestions.append("维持现有渠补政策") elif dimension == "profitStructure": gm = float(entity.get("trueGrossMargin", entity.get("true_gross_margin", 0))) if score <= 2: suggestions.append(f"成本优化:毛利率仅{gm}%,需分析成本构成") suggestions.append("产品结构:提高高毛利产品占比") suggestions.append("定价策略:评估提价空间") else: suggestions.append("维持毛利率水平") elif dimension == "cashAssets": cr = float(entity.get("cashRatio", entity.get("cash_ratio", 0))) if score <= 2: suggestions.append(f"现金管理:现金比率仅{cr}%,存在断流风险") suggestions.append("应收账款:加快回款周期") suggestions.append("融资安排:准备短期授信额度") else: suggestions.append("维持现金流健康") elif dimension == "growthDriver": eg = float(entity.get("expenseGrowthRate", entity.get("expense_growth_rate", 0))) if score <= 2: suggestions.append(f"费用管控:费用增速{eg}倍于收入,需严控费用") suggestions.append("预算管理:建立费用增长红线机制") suggestions.append("投资回报:评估每项投入的ROI") else: suggestions.append("维持费用增长与收入增长匹配") elif dimension == "orgEfficiency": mr = float(entity.get("mgmtRatio", entity.get("mgmt_ratio", 0))) if score <= 2: suggestions.append(f"管理效率:管理费/净收入{mr}%,急需降本增效") suggestions.append("组织精简:评估管理层级压缩空间") suggestions.append("流程优化:推进数字化降本") else: suggestions.append("维持管理效率水平") return suggestions def _get_dim_detail_indicators(dimension: str, entity: dict) -> list: """获取维度的明细诊断指标""" indicators = [] if dimension == "revenueStructure": rebate = float(entity.get("rebateRate", entity.get("rebate_rate", 0))) net_ratio = round(100 - rebate, 1) indicators.append({"label": "渠补率", "value": f"{rebate}%", "verdict": "收入依赖渠道返利" if rebate > 50 else "渠道依赖程度中等", "status": "danger" if rebate > 50 else "warning" if rebate > 20 else "success"}) indicators.append({"label": "净收入占比", "value": f"{net_ratio}%", "verdict": f"仅{net_ratio}%归公司" if net_ratio < 30 else "净收入占比合理", "status": "danger" if net_ratio < 30 else "success"}) elif dimension == "profitStructure": gm = float(entity.get("trueGrossMargin", entity.get("true_gross_margin", 0))) indicators.append({"label": "真实毛利率", "value": f"{gm}%", "verdict": "毛利偏低" if gm < 15 else "毛利正常", "status": "danger" if gm < 10 else "warning" if gm < 20 else "success"}) elif dimension == "cashAssets": cr = float(entity.get("cashRatio", entity.get("cash_ratio", 0))) indicators.append({"label": "现金比率", "value": f"{cr}%", "verdict": "断流风险" if cr < 5 else "现金紧张" if cr < 30 else "现金充足", "status": "danger" if cr < 5 else "warning" if cr < 30 else "success"}) elif dimension == "growthDriver": eg = float(entity.get("expenseGrowthRate", entity.get("expense_growth_rate", 0))) rg = float(entity.get("revenueGrowthRate", entity.get("revenue_growth_rate", 1))) ratio = eg / rg if rg > 0 else 99 indicators.append({"label": "费用增速/收入增速", "value": f"{ratio:.1f}倍", "verdict": "费用增速过快" if ratio > 1.5 else "费用可控" if ratio > 1 else "增长健康", "status": "danger" if ratio > 1.5 else "warning" if ratio > 1 else "success"}) elif dimension == "orgEfficiency": mr = float(entity.get("mgmtRatio", entity.get("mgmt_ratio", 0))) indicators.append({"label": "管理费/净收入", "value": f"{mr}%", "verdict": "管理费极高" if mr > 200 else "管理费偏高" if mr > 100 else "管理费正常", "status": "danger" if mr > 200 else "warning" if mr > 100 else "success"}) return indicators @router.post("/growth-quality") def api_growth_quality(data: dict): """增长质量诊断 — 五维度评分+综合评分+诊断结论""" try: entity_id = data.get("entity_id") ENTITY_DATA = { 1: {"entity":"陕西酣客文化传媒","rebateRate":82.8,"trueGrossMargin":18.6,"cashRatio":0.6,"expenseGrowthRate":2.2,"revenueGrowthRate":1.0,"mgmtRatio":447}, 2: {"entity":"陕西博海网络科技","rebateRate":0,"trueGrossMargin":13.1,"cashRatio":6.7,"expenseGrowthRate":0.8,"revenueGrowthRate":1.0,"mgmtRatio":1.4}, } entity = ENTITY_DATA.get(entity_id, data.get("entity", data)) entity_name = entity.get("entity", entity.get("name", "该企业")) period = entity.get("period", data.get("period", "当前")) # 五维度评分 dim_scores = { "revenueStructure": _score_revenue_structure(entity), "profitStructure": _score_profit_structure(entity), "cashAssets": _score_cash_assets(entity), "growthDriver": _score_growth_driver(entity), "orgEfficiency": _score_org_efficiency(entity), } overall = round(sum(dim_scores.values()) / 5, 1) # 综合等级 if overall >= 4: level = "🟢 越增长越轻" level_type = "excellent" elif overall >= 3: level = "🟡 增长质量中等" level_type = "medium" elif overall >= 2: level = "🟠 需关注" level_type = "warning" else: level = "🔴 越增长越重" level_type = "danger" # 诊断结论 dimensions_payload = {} detail_payload = {} for dim, score in dim_scores.items(): dimensions_payload[dim] = {"score": score, "weight": 20} detail_payload[dim] = { "score": score, "indicators": _get_dim_detail_indicators(dim, entity), "suggestions": _generate_improvement_suggestions(dim, score, entity), } diagnosis = _diagnosis_text(overall, dimensions_payload, entity_name) # 对比数据(如果请求中包含多个实体) compare = data.get("compare", None) compare_result = None if compare: compare_entity = compare compare_name = compare_entity.get("entity", compare_entity.get("name", "对比企业")) cdims = { "revenueStructure": _score_revenue_structure(compare_entity), "profitStructure": _score_profit_structure(compare_entity), "cashAssets": _score_cash_assets(compare_entity), "growthDriver": _score_growth_driver(compare_entity), "orgEfficiency": _score_org_efficiency(compare_entity), } compare_overall = round(sum(cdims.values()) / 5, 1) compare_result = { "entity_name": compare_name, "overall": compare_overall, "dimensions": {k: {"score": v, "weight": 20} for k, v in cdims.items()}, "level": ("🟢 越增长越轻" if compare_overall >= 4 else "🟡 增长质量中等" if compare_overall >= 3 else "🟠 需关注" if compare_overall >= 2 else "🔴 越增长越重"), } return { "entity_name": entity_name, "period": period, "overall": overall, "level": level, "level_type": level_type, "dimensions": dimensions_payload, "detail": detail_payload, "diagnosis": diagnosis, "compare": compare_result, } except Exception as e: raise HTTPException(400, f"增长质量诊断失败: {str(e)}")