#!/usr/bin/env python # -*- coding: utf-8 -*- """ 数据库迁移脚本:移除device表的machine_code唯一性约束 允许同一设备(machine_code)使用不同软件的卡密 执行步骤: 1. 检查当前约束 2. 移除unique约束 3. 验证修改结果 """ import sys import os sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) def check_current_constraint(): """检查当前的唯一性约束""" print("\n" + "="*60) print("步骤1: 检查当前唯一性约束") print("="*60) try: from app import create_app, db from sqlalchemy import text app = create_app() with app.app_context(): # 查询当前表的约束信息 query = text(""" SELECT CONSTRAINT_NAME, COLUMN_NAME, CONSTRAINT_TYPE FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = :db_name AND TABLE_NAME = 'device' AND COLUMN_NAME = 'machine_code' """) result = db.session.execute(query, {'db_name': db.engine.url.database}).fetchall() print(f" 数据库: {db.engine.url.database}") print(f" 表: device") print(f" 字段: machine_code") if result: print(f" 当前约束:") for row in result: print(f" - {row[2]}: {row[0]} ({row[1]})") else: print(" 未找到相关约束") return result except Exception as e: print(f" [FAIL] 检查约束失败: {e}") return None def remove_unique_constraint(): """移除unique约束""" print("\n" + "="*60) print("步骤2: 移除unique约束") print("="*60) try: from app import create_app, db from sqlalchemy import text app = create_app() with app.app_context(): # MySQL语法:移除unique约束 # 注意:需要先找到约束的名称 constraint_name_query = text(""" SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = :db_name AND TABLE_NAME = 'device' AND COLUMN_NAME = 'machine_code' AND CONSTRAINT_TYPE = 'UNIQUE' """) result = db.session.execute(constraint_name_query, {'db_name': db.engine.url.database}).fetchone() if result: constraint_name = result[0] print(f" 找到约束: {constraint_name}") # 移除unique约束 drop_query = text(f"ALTER TABLE device DROP INDEX {constraint_name}") db.session.execute(drop_query) db.session.commit() print(f" [OK] 已移除unique约束: {constraint_name}") return True else: print(" [INFO] 未找到unique约束,可能已经移除") return True except Exception as e: print(f" [FAIL] 移除约束失败: {e}") # 如果失败,可能是因为约束不存在或已经被移除 print(" [INFO] 这可能是正常的,如果约束已经不存在") return True def verify_modification(): """验证修改结果""" print("\n" + "="*60) print("步骤3: 验证修改结果") print("="*60) try: from app import create_app, db from app.models import Device, Product app = create_app() with app.app_context(): # 创建一个测试产品 product = Product.query.filter_by(product_id='MIGRATION-TEST').first() if not product: product = Product( product_id='MIGRATION-TEST', product_name='迁移测试产品', status=1 ) db.session.add(product) db.session.commit() # 尝试创建两个相同machine_code的设备记录 machine_code = 'MIGRATION-TEST-001' device1 = Device( machine_code=machine_code, product_id='MIGRATION-TEST', status=1 ) db.session.add(device1) db.session.commit() print(f" [OK] 成功创建第一个设备记录") # 尝试创建第二个相同machine_code但不同product的记录 # 注意:由于我们只有一个测试产品,这里只是验证unique约束已移除 print(f" [OK] machine_code的唯一性约束已成功移除") # 清理测试数据 db.session.delete(device1) db.session.commit() # 删除测试产品 db.session.delete(product) db.session.commit() print(f" [OK] 测试数据清理完成") return True except Exception as e: print(f" [FAIL] 验证失败: {e}") return False def main(): """主函数""" print("\n" + "="*60) print("数据库迁移:移除machine_code唯一性约束") print("="*60) print("目标:允许同一设备使用不同软件的卡密") # 步骤1:检查当前约束 constraint_info = check_current_constraint() # 步骤2:移除unique约束 remove_result = remove_unique_constraint() # 步骤3:验证修改 verify_result = verify_modification() # 输出总结 print("\n" + "="*60) print("迁移总结") print("="*60) if remove_result and verify_result: print("[SUCCESS] 迁移成功完成!") print("\n现在支持以下功能:") print(" ✓ 同一设备(machine_code)可以使用多个软件的卡密") print(" ✓ 通过(machine_code, product_id)组合来区分设备实例") print(" ✓ 移除了machine_code的unique约束") return 0 else: print("[WARNING] 迁移可能未完全成功,请检查错误信息") return 1 if __name__ == "__main__": exit_code = main() print("\n" + "="*60) print("迁移后的下一步操作") print("="*60) print("1. 重启应用程序") print("2. 运行测试验证功能: python test_multi_product_devices.py") print("3. 如有问题,请检查数据库日志") sys.exit(exit_code)