migrate_shop_items_special_scene_trigger.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. """
  2. 迁移脚本:为 shop_items 增加 has_special_scene_trigger 并回填
  3. 执行:
  4. python migrations/migrate_shop_items_special_scene_trigger.py
  5. 或:
  6. python migrations/migrate_shop_items_special_scene_trigger.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_column = await _column_exists(conn, "shop_items", "has_special_scene_trigger")
  45. if not has_column:
  46. await cursor.execute(
  47. """
  48. ALTER TABLE `shop_items`
  49. ADD COLUMN `has_special_scene_trigger` TINYINT(1)
  50. NOT NULL DEFAULT 0
  51. COMMENT '是否有特殊触发场景(1=有,0=无)'
  52. AFTER `scene_anchor_type`
  53. """
  54. )
  55. print("✅ 已新增列 shop_items.has_special_scene_trigger")
  56. else:
  57. print("ℹ️ 列 shop_items.has_special_scene_trigger 已存在,跳过新增")
  58. await cursor.execute(
  59. """
  60. UPDATE `shop_items`
  61. SET `has_special_scene_trigger` = 1
  62. WHERE `item_code` IN ('coffee_mug', 'books', 'snacks')
  63. """
  64. )
  65. print(f"✅ 已回填特殊触发场景商品:{cursor.rowcount or 0} 行")
  66. await cursor.execute(
  67. """
  68. UPDATE `shop_items`
  69. SET `has_special_scene_trigger` = 0
  70. WHERE `item_code` NOT IN ('coffee_mug', 'books', 'snacks')
  71. """
  72. )
  73. print(f"✅ 已回填非特殊触发场景商品:{cursor.rowcount or 0} 行")
  74. await conn.commit()
  75. print("🎉 has_special_scene_trigger 迁移完成")
  76. return True
  77. except Exception as exc: # noqa: BLE001
  78. if conn is not None:
  79. try:
  80. await conn.rollback()
  81. except Exception:
  82. pass
  83. print(f"❌ 迁移失败: {exc}")
  84. return False
  85. finally:
  86. if conn is not None:
  87. conn.close()
  88. if __name__ == "__main__":
  89. if len(sys.argv) > 1 and sys.argv[1] == "--force":
  90. confirm = "y"
  91. else:
  92. confirm = input("\n是否继续执行特殊触发场景字段迁移? (y/n): ").strip().lower()
  93. if confirm != "y":
  94. sys.exit(0)
  95. ok = asyncio.run(migrate())
  96. sys.exit(0 if ok else 1)