85 lines
3.4 KiB
Python
85 lines
3.4 KiB
Python
"""verify-engine-fix-20260830 DB 迁移脚本
|
||
|
||
1. kpi_values.entity_id 回填:kpi_id → kpi_definitions.entity_id(现有 67 条 NULL)
|
||
2. action_plans.status 'done' → 'completed'(枚举修正;当前 0 条,防御性执行)
|
||
|
||
执行前先 SELECT 预览影响行数,再执行 UPDATE,最后回查验证。
|
||
用法: venv/bin/python3 scripts/migrate_verify_fix_20260830.py
|
||
"""
|
||
import sys
|
||
import os
|
||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||
|
||
from sqlalchemy import text
|
||
from app.database import get_engine
|
||
|
||
|
||
def main():
|
||
engine = get_engine()
|
||
with engine.connect() as conn:
|
||
# ── 1. 预览 ──
|
||
preview_null = conn.execute(text(
|
||
"SELECT COUNT(*) AS cnt FROM kpi_values kv "
|
||
"LEFT JOIN kpi_definitions kd ON kv.kpi_id = kd.id "
|
||
"WHERE kv.entity_id IS NULL AND kd.entity_id IS NOT NULL"
|
||
)).fetchone()
|
||
preview_orphan = conn.execute(text(
|
||
"SELECT COUNT(*) AS cnt FROM kpi_values kv "
|
||
"LEFT JOIN kpi_definitions kd ON kv.kpi_id = kd.id "
|
||
"WHERE kv.entity_id IS NULL AND kd.id IS NULL"
|
||
)).fetchone()
|
||
preview_done = conn.execute(text(
|
||
"SELECT COUNT(*) AS cnt FROM action_plans WHERE status = 'done'"
|
||
)).fetchone()
|
||
|
||
print(f"[预览] 可回填(entity_id NULL 且 kpi 存在): {preview_null.cnt}")
|
||
print(f"[预览] 无法回填(kpi 不存在): {preview_orphan.cnt}")
|
||
print(f"[预览] action_plans status='done': {preview_done.cnt}")
|
||
|
||
# ── 2. 执行回填 ──
|
||
r = conn.execute(text(
|
||
"UPDATE kpi_values kv "
|
||
"JOIN kpi_definitions kd ON kv.kpi_id = kd.id "
|
||
"SET kv.entity_id = kd.entity_id "
|
||
"WHERE kv.entity_id IS NULL AND kd.entity_id IS NOT NULL"
|
||
))
|
||
print(f"[执行] kpi_values entity_id 回填 {r.rowcount} 条")
|
||
|
||
# 无法关联的孤儿行(kpi 不存在)→ 置默认 entity_id=1 并留 remark
|
||
r2 = conn.execute(text(
|
||
"UPDATE kpi_values kv "
|
||
"LEFT JOIN kpi_definitions kd ON kv.kpi_id = kd.id "
|
||
"SET kv.entity_id = 1, kv.remark = CONCAT(COALESCE(kv.remark, ''), '; verify-fix-20260830 孤儿行默认entity_id=1') "
|
||
"WHERE kv.entity_id IS NULL AND kd.id IS NULL"
|
||
))
|
||
if r2.rowcount:
|
||
print(f"[执行] 孤儿行置默认 entity_id=1: {r2.rowcount} 条")
|
||
else:
|
||
print("[执行] 无孤儿行需处理")
|
||
|
||
# ── 3. status done → completed ──
|
||
r3 = conn.execute(text(
|
||
"UPDATE action_plans SET status = 'completed' WHERE status = 'done'"
|
||
))
|
||
print(f"[执行] action_plans status done→completed: {r3.rowcount} 条")
|
||
|
||
conn.commit()
|
||
|
||
# ── 4. 验证 ──
|
||
after_null = conn.execute(text(
|
||
"SELECT COUNT(*) AS cnt FROM kpi_values WHERE entity_id IS NULL"
|
||
)).fetchone()
|
||
after_done = conn.execute(text(
|
||
"SELECT COUNT(*) AS cnt FROM action_plans WHERE status = 'done'"
|
||
)).fetchone()
|
||
print(f"[验证] kpi_values entity_id IS NULL 残留: {after_null.cnt}(目标 0)")
|
||
print(f"[验证] action_plans status='done' 残留: {after_done.cnt}(目标 0)")
|
||
if after_null.cnt != 0:
|
||
print("[结果] ❌ 回填不彻底,请人工检查")
|
||
sys.exit(1)
|
||
print("[结果] ✅ 迁移完成")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|