feat(线缆管理): 新增向导式接线创建功能

- 新增四步向导流程,简化接线创建过程
- 添加线缆标签、颜色、安装信息等新字段
- 实现端口可视化面板和冲突检测功能
- 新增耗材日志设备关联字段
- 添加耗材导入后台任务管理
- 更新线缆管理文档和使用指南
This commit is contained in:
zhang1106
2026-03-31 14:34:34 +08:00
parent 5d972799f2
commit 06e3b56469
16 changed files with 3914 additions and 105 deletions
+63 -27
View File
@@ -100,6 +100,11 @@ const migrations = [
description: '为 devices 表添加复合索引,优化位置冲突检测和悲观锁性能',
migrate: migrateDevicePositionIndexes,
},
{
name: '耗材日志设备关联',
description: '为 consumable_logs 表添加 deviceId、deviceName、rackId、rackName、roomId、roomName 字段',
migrate: migrateConsumableLogDeviceAssociation,
},
];
async function runMigrations() {
@@ -738,37 +743,68 @@ async function migrateDevicePositionIndexes() {
return;
}
console.log(` → 为 ${tableName} 表添加复合索引 rackId_position...`);
try {
if (dialect === 'sqlite') {
await sequelize.query(`CREATE INDEX IF NOT EXISTS devices_rackId_position ON ${tableName}(rackId, position)`);
} else {
await sequelize.query(`CREATE INDEX IF NOT EXISTS \`devices_rackId_position\` ON \`${tableName}\`(\`rackId\`, \`position\`)`);
}
console.log(' ✓ 索引创建成功');
} catch (error) {
if (error.message.includes('already exists') || error.message.includes('Duplicate key name')) {
console.log(' → 索引已存在,跳过');
} else {
throw error;
const indexesToCreate = [
{ name: 'devices_rackId_position', fields: ['rackId', 'position'] },
{ name: 'devices_rackId_position_isIdle', fields: ['rackId', 'position', 'isIdle'] },
];
for (const idx of indexesToCreate) {
console.log(` → 为 ${tableName} 表添加复合索引 ${idx.name}...`);
try {
const existingIndexes = await sequelize.query(`SHOW INDEX FROM ${tableName}`, {
type: sequelize.QueryTypes.SELECT,
});
const indexExists = existingIndexes.some(
existing => existing.Key_name === idx.name
);
if (indexExists) {
console.log(' → 索引已存在,跳过');
} else {
if (dialect === 'sqlite') {
await sequelize.query(
`CREATE INDEX IF NOT EXISTS ${idx.name} ON ${tableName}(${idx.fields.join(', ')})`
);
} else {
await sequelize.query(
`CREATE INDEX \`${idx.name}\` ON \`${tableName}\`(\`${idx.fields.join('`, `')}\`)`
);
}
console.log(' ✓ 索引创建成功');
}
} catch (error) {
if (error.message.includes('already exists') || error.message.includes('Duplicate key name')) {
console.log(' → 索引已存在,跳过');
} else {
throw error;
}
}
}
}
async function migrateConsumableLogDeviceAssociation() {
const tableName = 'consumable_logs';
if (!(await tableExists(tableName))) {
console.log(` ${tableName} 表不存在,跳过`);
return;
}
console.log(` → 为 ${tableName} 表添加复合索引 rackId_position_isIdle...`);
try {
if (dialect === 'sqlite') {
await sequelize.query(`CREATE INDEX IF NOT EXISTS devices_rackId_position_isIdle ON ${tableName}(rackId, position, isIdle)`);
} else {
await sequelize.query(`CREATE INDEX IF NOT EXISTS \`devices_rackId_position_isIdle\` ON \`${tableName}\`(\`rackId\`, \`position\`, \`isIdle\`)`);
}
console.log(' ✓ 索引创建成功');
} catch (error) {
if (error.message.includes('already exists') || error.message.includes('Duplicate key name')) {
console.log(' → 索引已存在,跳过');
} else {
throw error;
}
const columns = await getTableColumns(tableName);
const newColumns = [
{ name: 'deviceId', def: 'VARCHAR(255)' },
{ name: 'deviceName', def: 'VARCHAR(255)' },
{ name: 'rackId', def: 'VARCHAR(255)' },
{ name: 'rackName', def: 'VARCHAR(255)' },
{ name: 'roomId', def: 'VARCHAR(255)' },
{ name: 'roomName', def: 'VARCHAR(255)' },
];
for (const col of newColumns) {
await addColumnIfNotExists(tableName, col.name, col.def);
}
console.log(' 耗材日志设备关联迁移完成');
}
// 执行迁移
+42
View File
@@ -0,0 +1,42 @@
const { sequelize } = require('../db');
const Cable = require('../models/Cable');
async function migrateCableFields() {
try {
console.log('开始迁移 Cable 模型字段...');
// 检查字段是否已存在
const [results] = await sequelize.query('PRAGMA table_info(cables)');
const existingColumns = results.map(row => row.name);
const newColumns = [
{ name: 'cableLabel', type: 'VARCHAR(255)' },
{ name: 'cableColor', type: 'VARCHAR(50)' },
{ name: 'installedBy', type: 'VARCHAR(100)' },
{ name: 'installedAt', type: 'DATETIME' },
{ name: 'lastTestedAt', type: 'DATETIME' },
];
for (const column of newColumns) {
if (!existingColumns.includes(column.name)) {
console.log(`添加字段: ${column.name}`);
await sequelize.query(`ALTER TABLE cables ADD COLUMN ${column.name} ${column.type}`);
} else {
console.log(`字段已存在: ${column.name}`);
}
}
console.log('迁移完成!');
console.log('\n新增字段:');
console.log(' - cableLabel: 线缆标签/编号');
console.log(' - cableColor: 线缆颜色(便于识别)');
console.log(' - installedBy: 安装人');
console.log(' - installedAt: 安装时间');
console.log(' - lastTestedAt: 上次测试时间');
} catch (error) {
console.error('迁移失败:', error);
process.exit(1);
}
}
migrateCableFields();