diff --git a/backend/scripts/fix-mysql-migration.js b/backend/scripts/fix-mysql-migration.js new file mode 100644 index 0000000..cd45a02 --- /dev/null +++ b/backend/scripts/fix-mysql-migration.js @@ -0,0 +1,126 @@ +/** + * MySQL 数据库字段修复脚本 + * 添加缺失的 isConsumableDeleted 字段和 consumableSnapshot 字段 + */ + +const { sequelize, DB_TYPE } = require('../db'); + +async function fix() { + try { + console.log('开始修复 MySQL 数据库字段...'); + + if (DB_TYPE !== 'mysql') { + console.log('当前不是 MySQL 数据库,跳过修复'); + process.exit(0); + } + + // 检查字段是否存在 + const [columns] = await sequelize.query( + `SHOW COLUMNS FROM consumable_logs LIKE 'isConsumableDeleted'` + ); + + if (columns.length === 0) { + console.log('添加 isConsumableDeleted 字段...'); + await sequelize.query(` + ALTER TABLE consumable_logs + ADD COLUMN isConsumableDeleted BOOLEAN DEFAULT FALSE COMMENT '关联耗材是否已被删除' + `); + console.log('✓ 添加成功'); + } else { + console.log('✓ isConsumableDeleted 字段已存在'); + } + + // 检查 consumableSnapshot 字段 + const [snapshotColumns] = await sequelize.query( + `SHOW COLUMNS FROM consumable_logs LIKE 'consumableSnapshot'` + ); + + if (snapshotColumns.length === 0) { + console.log('添加 consumableSnapshot 字段...'); + await sequelize.query(` + ALTER TABLE consumable_logs + ADD COLUMN consumableSnapshot JSON DEFAULT NULL COMMENT '耗材快照信息' + `); + console.log('✓ 添加成功'); + } else { + console.log('✓ consumableSnapshot 字段已存在'); + } + + // 检查 relatedId 字段 + const [relatedColumns] = await sequelize.query( + `SHOW COLUMNS FROM consumable_logs LIKE 'relatedId'` + ); + + if (relatedColumns.length === 0) { + console.log('添加 relatedId 字段...'); + await sequelize.query(` + ALTER TABLE consumable_logs + ADD COLUMN relatedId VARCHAR(255) DEFAULT NULL COMMENT '关联归档ID' + `); + console.log('✓ 添加成功'); + } else { + console.log('✓ relatedId 字段已存在'); + } + + // 检查索引是否存在 + const [indexes] = await sequelize.query( + `SHOW INDEX FROM consumable_logs WHERE Key_name = 'consumable_logs_is_consumable_deleted'` + ); + + if (indexes.length === 0) { + console.log('添加索引...'); + await sequelize.query(` + ALTER TABLE consumable_logs + ADD INDEX consumable_logs_is_consumable_deleted (isConsumableDeleted) + `); + console.log('✓ 索引添加成功'); + } else { + console.log('✓ 索引已存在'); + } + + // 创建归档表 + console.log('\n检查归档表...'); + const [archiveTables] = await sequelize.query( + `SHOW TABLES LIKE 'consumable_log_archives'` + ); + + if (archiveTables.length === 0) { + console.log('创建归档表...'); + await sequelize.query(` + CREATE TABLE consumable_log_archives ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + archiveId VARCHAR(255) NOT NULL UNIQUE COMMENT '归档记录唯一标识', + consumableId VARCHAR(255) NOT NULL COMMENT '被删除的耗材ID', + consumableName VARCHAR(255) NOT NULL COMMENT '耗材名称', + consumableSnapshot JSON DEFAULT NULL COMMENT '耗材快照信息', + totalOperations INTEGER DEFAULT 0 COMMENT '操作记录总数', + firstOperationAt DATETIME COMMENT '首次操作时间', + lastOperationAt DATETIME COMMENT '最后操作时间', + totalInQuantity INTEGER DEFAULT 0 COMMENT '总入库数量', + totalOutQuantity INTEGER DEFAULT 0 COMMENT '总出库数量', + finalStock INTEGER DEFAULT 0 COMMENT '删除时库存', + deletedBy VARCHAR(255) COMMENT '删除人', + deletedAt DATETIME COMMENT '删除时间', + deleteReason VARCHAR(255) COMMENT '删除原因', + createdAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + INDEX idx_consumable_id (consumableId), + INDEX idx_archive_id (archiveId), + INDEX idx_deleted_at (deletedAt), + INDEX idx_consumable_deleted (consumableId, deletedAt) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='耗材操作日志归档表' + `); + console.log('✓ 归档表创建成功'); + } else { + console.log('✓ 归档表已存在'); + } + + console.log('\n数据库修复完成!'); + process.exit(0); + } catch (error) { + console.error('修复失败:', error); + process.exit(1); + } +} + +fix();