# -*- coding: utf-8 -*- """幂等迁移:为 ``escrows`` 表补充微信服务商分账扩展列(非运行态)。 用法:uv run python scripts/db/migrate_escrow_columns.py - 通过 SQLAlchemy Inspector 检查缺失列,对缺失列执行 ``ALTER TABLE ... ADD COLUMN``; - 幂等:已存在列自动跳过,可重复执行;兼容 SQLite(默认)与 MySQL(生产)。 - 新增表 ``payment_bindings`` / ``settlement_logs`` 由应用启动时 ``create_all`` 幂等创建, 无需在此处理(本脚本只负责对既有表加列)。 """ from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent.parent sys.path.insert(0, str(ROOT)) from sqlalchemy import create_engine, inspect # noqa: E402 from app.infrastructure.db import sync_database_url # noqa: E402 # escrows 需补充的扩展列(列名, 方言通用 DDL) ESCROW_COLUMNS = [ ("channel", "VARCHAR(32) NOT NULL DEFAULT 'wx_split'"), ("transaction_id", "VARCHAR(64) NOT NULL DEFAULT ''"), ("share_order_no", "VARCHAR(64) NOT NULL DEFAULT ''"), ("share_status", "VARCHAR(32) NOT NULL DEFAULT 'pending'"), ("receiver_binding_id", "VARCHAR(64) NOT NULL DEFAULT ''"), ] def main() -> None: url = sync_database_url() engine = create_engine(url) insp = inspect(engine) if "escrows" not in insp.get_table_names(): print("escrows 表不存在(未建库),无需迁移") return existing = {c["name"] for c in insp.get_columns("escrows")} added, skipped = [], [] for name, ddl in ESCROW_COLUMNS: if name in existing: skipped.append(name) continue with engine.begin() as conn: conn.exec_driver_sql(f"ALTER TABLE escrows ADD COLUMN {name} {ddl}") added.append(name) print(f"新增列: {added or '无'}") print(f"已存在跳过: {skipped or '无'}") engine.dispose() if added: print("迁移完成。") else: print("无新增列,escrows 已是最新。") print("注:新表 payment_bindings / settlement_logs 由应用启动时 create_all 幂等创建。") if __name__ == "__main__": main()