Files

143 lines
6.3 KiB
Python

"""
方案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✅ 重置完成")