migrate_shop_items_bilingual_name.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. """
  2. 迁移脚本:为 shop_items 增加 name_zh / name_en 并回填
  3. 执行:
  4. python migrations/migrate_shop_items_bilingual_name.py
  5. 或:
  6. python migrations/migrate_shop_items_bilingual_name.py --force
  7. """
  8. import asyncio
  9. import sys
  10. from pathlib import Path
  11. from typing import Any
  12. import aiomysql
  13. backend_dir = Path(__file__).resolve().parents[1]
  14. if str(backend_dir) not in sys.path:
  15. sys.path.insert(0, str(backend_dir))
  16. from config import settings
  17. async def _column_exists(conn: aiomysql.Connection, table_name: str, column_name: str) -> bool:
  18. async with conn.cursor() as cursor:
  19. await cursor.execute(
  20. """
  21. SELECT COUNT(*)
  22. FROM information_schema.columns
  23. WHERE table_schema = %s
  24. AND table_name = %s
  25. AND column_name = %s
  26. """,
  27. (settings.MYSQL_DATABASE, table_name, column_name),
  28. )
  29. row: Any = await cursor.fetchone()
  30. return bool(row and row[0] > 0)
  31. async def migrate() -> bool:
  32. conn: aiomysql.Connection | None = None
  33. try:
  34. conn = await aiomysql.connect(
  35. host=settings.MYSQL_HOST,
  36. port=settings.MYSQL_PORT,
  37. user=settings.MYSQL_USER,
  38. password=settings.MYSQL_PASSWORD,
  39. db=settings.MYSQL_DATABASE,
  40. charset="utf8mb4",
  41. autocommit=False,
  42. )
  43. async with conn.cursor() as cursor:
  44. has_name_zh = await _column_exists(conn, "shop_items", "name_zh")
  45. if not has_name_zh:
  46. await cursor.execute(
  47. """
  48. ALTER TABLE `shop_items`
  49. ADD COLUMN `name_zh` VARCHAR(100) NOT NULL DEFAULT ''
  50. COMMENT '商品中文名称'
  51. AFTER `name`
  52. """
  53. )
  54. print("✅ 已新增列 shop_items.name_zh")
  55. else:
  56. print("ℹ️ 列 shop_items.name_zh 已存在,跳过新增")
  57. has_name_en = await _column_exists(conn, "shop_items", "name_en")
  58. if not has_name_en:
  59. await cursor.execute(
  60. """
  61. ALTER TABLE `shop_items`
  62. ADD COLUMN `name_en` VARCHAR(100) NOT NULL DEFAULT ''
  63. COMMENT '商品英文名称'
  64. AFTER `name_zh`
  65. """
  66. )
  67. print("✅ 已新增列 shop_items.name_en")
  68. else:
  69. print("ℹ️ 列 shop_items.name_en 已存在,跳过新增")
  70. await cursor.execute(
  71. """
  72. UPDATE `shop_items`
  73. SET `name_zh` = `name`
  74. WHERE (`name_zh` IS NULL OR `name_zh` = '')
  75. """
  76. )
  77. print(f"✅ 已回填 name_zh:{cursor.rowcount or 0} 行")
  78. await cursor.execute(
  79. """
  80. UPDATE `shop_items`
  81. SET `name_en` = CONCAT(
  82. UPPER(LEFT(REPLACE(`item_code`, '_', ' '), 1)),
  83. LOWER(SUBSTRING(REPLACE(`item_code`, '_', ' '), 2))
  84. )
  85. WHERE (`name_en` IS NULL OR `name_en` = '')
  86. """
  87. )
  88. print(f"✅ 已回填 name_en:{cursor.rowcount or 0} 行")
  89. await conn.commit()
  90. print("🎉 双语名称迁移完成")
  91. return True
  92. except Exception as exc: # noqa: BLE001
  93. if conn is not None:
  94. try:
  95. await conn.rollback()
  96. except Exception:
  97. pass
  98. print(f"❌ 迁移失败: {exc}")
  99. return False
  100. finally:
  101. if conn is not None:
  102. conn.close()
  103. if __name__ == "__main__":
  104. if len(sys.argv) > 1 and sys.argv[1] == "--force":
  105. confirm = "y"
  106. else:
  107. confirm = input("\n是否继续执行双语名称迁移? (y/n): ").strip().lower()
  108. if confirm != "y":
  109. sys.exit(0)
  110. ok = asyncio.run(migrate())
  111. sys.exit(0 if ok else 1)