""" 方案C:重置KPI字典 — 使用原生SQL以绕过ORM外键约束 """ import sys import os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from dotenv import load_dotenv load_dotenv() from app.database import get_engine from sqlalchemy import text engine = get_engine() with engine.connect() as conn: print("=" * 60) print("方案C:重置KPI字典") print("=" * 60) # 步骤1:先禁用外键检查,干净删除 print("\n[1/4] 删除旧数据...") conn.execute(text("SET FOREIGN_KEY_CHECKS = 0")) # 清理顺序:依赖链最深的先删 for tbl in [ "notification_logs", "kpi_alerts", "action_plans", "kpi_values", "operation_logs", "kpi_definitions", ]: r = conn.execute(text(f"DELETE FROM {tbl}")) print(f" 已清空 {tbl}: {r.rowcount} 条") conn.execute(text("SET FOREIGN_KEY_CHECKS = 1")) conn.commit() # 步骤2:从模板实例化标准KPI print("\n[2/4] 从模板实例化标准KPI...") rows = conn.execute(text( "SELECT id, kpi_code, kpi_name, dimension, category, formula, formula_desc, " "unit, target_value, description FROM kpi_templates WHERE is_system=1 ORDER BY kpi_code" )).fetchall() print(f" 共 {len(rows)} 个系统模板") created = 0 for r in rows: conn.execute(text( "INSERT INTO kpi_definitions (template_id, is_system, kpi_code, kpi_name, " "dimension, category, formula, formula_desc, unit, target_value, " "data_source_type, frequency, status) " "VALUES (:tid, 1, :code, :name, :dim, :cat, :formula, :fdesc, :unit, :target, 'manual', 'monthly', 'active')" ), { "tid": r[0], "code": r[1], "name": r[2], "dim": r[3], "cat": r[4], "formula": r[5], "fdesc": r[6], "unit": r[7] or "%", "target": r[8] }) # 更新usage_count conn.execute(text("UPDATE kpi_templates SET usage_count = IFNULL(usage_count,0)+1 WHERE id=:id"), {"id": r[0]}) created += 1 # 步骤3:补充额外KPI print("\n[3/4] 补充额外KPI...") extra_kpis = [ ("F_REVENUE_GROWTH", "收入增长率", "finance", "revenue_growth", "(本期收入-上期收入)/上期收入*100", "%", 15.0), ("F_ROE", "净资产收益率(ROE)", "finance", "profitability", "净利润/净资产*100", "%", 12.0), ("F_AR_TURNOVER", "应收账款周转率", "finance", "asset_efficiency", "营业收入/平均应收账款", "次", 6.0), ("F_DEBT_RATIO", "资产负债率", "finance", "cash_risk", "总负债/总资产*100", "%", 50.0), ("C_MARKET_SHARE", "市场份额", "customer", "customer_scale", "本公司销售额/行业总销售额*100", "%", None), ("C_CAC", "新客户获取成本(CAC)", "customer", "customer_scale", "销售费用/新客户数", "元", None), ("C_CLV", "客户生命周期价值(CLV)", "customer", "customer_scale", "平均客单价*复购次数*毛利率", "元", None), ("C_RETENTION", "客户留存率", "customer", "customer_scale", "期末客户数/期初客户数*100", "%", 85.0), ("C_NPS", "净推荐值(NPS)", "customer", "customer_satisfaction", "推荐者占比-贬损者占比", "分", 50.0), ("P_CAPACITY", "产能利用率", "process", "supply_chain", "实际产量/设计产能*100", "%", 85.0), ("P_OEE", "设备综合效率(OEE)", "process", "supply_chain", "可用率*表现率*质量率*100", "%", 75.0), ("P_INV_TURNOVER", "存货周转率", "process", "supply_chain", "营业成本/平均存货", "次", 8.0), ("P_FIRST_PASS", "产品一次合格率", "process", "delivery_quality", "一次合格数/总检验数*100", "%", 98.0), ("L_PER_CAPITA", "人均产值", "learning", "employee_engagement", "营业收入/员工总数", "万元", None), ("L_HR_SATISFACTION", "员工满意度指数", "learning", "employee_engagement", "满意度调查得分", "分", 85.0), ("L_SYSTEM_COVERAGE", "信息系统覆盖率", "learning", "innovation", "已系统化业务流程数/总业务流程数*100", "%", 70.0), ] ek_created = 0 for code, name, dim, cat, formula, unit, target in extra_kpis: existing = conn.execute(text("SELECT COUNT(*) FROM kpi_definitions WHERE kpi_code=:code"), {"code": code}).scalar() if existing > 0: continue conn.execute(text( "INSERT INTO kpi_definitions (is_system, kpi_code, kpi_name, dimension, category, " "formula, unit, target_value, data_source_type, frequency, status) " "VALUES (1, :code, :name, :dim, :cat, :formula, :unit, :target, 'manual', 'monthly', 'active')" ), {"code": code, "name": name, "dim": dim, "cat": cat, "formula": formula, "unit": unit, "target": target}) ek_created += 1 print(f" 补充了 {ek_created} 条额外KPI") # 步骤4:标记ERP数据源 print("\n[4/4] 标记ERP数据源...") erp_codes = [ "F_REVENUE", "C_CUSTOMER_COUNT", "F_PROFIT_RATE", "F_NET_PROFIT_RATE", "C_CUSTOMER_CONCENTRATION", "F_CASH_FLOW", "C_CAC", "F_REVENUE_GROWTH", "F_AR_TURNOVER", "P_INV_TURNOVER", "F_ROE", ] for code in erp_codes: r = conn.execute(text( "UPDATE kpi_definitions SET data_source_type='erp' WHERE kpi_code=:code" ), {"code": code}) if r.rowcount > 0: print(f" {code}: ERP") conn.commit() # 最终统计 total = conn.execute(text("SELECT COUNT(*) FROM kpi_definitions WHERE status='active'")).scalar() by_dim = conn.execute(text( "SELECT dimension, COUNT(*) FROM kpi_definitions WHERE status='active' GROUP BY dimension ORDER BY dimension" )).fetchall() print(f"\n{'=' * 60}") print(f"完成!KPI字典共 {total} 条") for d, c in by_dim: print(f" {d}: {c} 条") # 打印所有KPI print(f"\n{'=' * 60}") print("新KPI字典清单:") print(f"{'=' * 60}") all_kpis = conn.execute(text( "SELECT id, kpi_code, kpi_name, dimension, data_source_type FROM kpi_definitions WHERE status='active' ORDER BY kpi_code" )).fetchall() for r in all_kpis: src = "ERP" if r[4] == "erp" else "手动" print(f" [{r[0]:2d}] {r[1]:30s} {r[2]:20s} {r[3]:12s} [{src}]") print("\n✅ 重置完成")