feat(设备导入): 改进机柜字段处理为机房+机柜名称组合
将设备导入模板中的机柜ID字段拆分为机房名称和机柜名称两列,以便更直观地定位设备位置 在导入处理时,根据机房名称和机柜名称自动查找或创建对应机柜 移除默认机房创建逻辑,改为严格验证机房是否存在
This commit is contained in:
+89
-44
@@ -159,9 +159,24 @@ router.get('/import-template', async (req, res) => {
|
|||||||
const dateFields = ['购买日期', '保修到期'];
|
const dateFields = ['购买日期', '保修到期'];
|
||||||
|
|
||||||
// 根据字段配置动态生成CSV标题(排除设备ID,由系统自动生成)
|
// 根据字段配置动态生成CSV标题(排除设备ID,由系统自动生成)
|
||||||
const headers = deviceFields
|
// 将rackId字段替换为机房名称+机柜名称,以便唯一定位
|
||||||
|
const headers = [];
|
||||||
|
deviceFields
|
||||||
.filter(field => field.visible && field.fieldName !== 'deviceId')
|
.filter(field => field.visible && field.fieldName !== 'deviceId')
|
||||||
.map(field => {
|
.forEach(field => {
|
||||||
|
// 如果是机柜字段,拆分为机房名称和机柜名称两列
|
||||||
|
if (field.fieldName === 'rackId') {
|
||||||
|
headers.push({
|
||||||
|
id: '所在机房名称',
|
||||||
|
title: '所在机房名称(必填)'
|
||||||
|
});
|
||||||
|
headers.push({
|
||||||
|
id: '所在机柜名称',
|
||||||
|
title: '所在机柜名称(必填)'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let title = field.displayName;
|
let title = field.displayName;
|
||||||
|
|
||||||
// 添加字段类型提示
|
// 添加字段类型提示
|
||||||
@@ -178,7 +193,7 @@ router.get('/import-template', async (req, res) => {
|
|||||||
title = `${field.displayName}(${field.required ? '必填' : '可选'})`;
|
title = `${field.displayName}(${field.required ? '必填' : '可选'})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { id: field.displayName, title };
|
headers.push({ id: field.displayName, title });
|
||||||
});
|
});
|
||||||
|
|
||||||
// 准备示例数据(根据字段配置生成,排除设备ID)
|
// 准备示例数据(根据字段配置生成,排除设备ID)
|
||||||
@@ -186,6 +201,13 @@ router.get('/import-template', async (req, res) => {
|
|||||||
deviceFields
|
deviceFields
|
||||||
.filter(field => field.visible && field.fieldName !== 'deviceId')
|
.filter(field => field.visible && field.fieldName !== 'deviceId')
|
||||||
.forEach(field => {
|
.forEach(field => {
|
||||||
|
// 如果是机柜字段,拆分为机房名称和机柜名称
|
||||||
|
if (field.fieldName === 'rackId') {
|
||||||
|
exampleData['所在机房名称'] = '测试机房';
|
||||||
|
exampleData['所在机柜名称'] = 'A01';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (field.fieldName) {
|
switch (field.fieldName) {
|
||||||
case 'name':
|
case 'name':
|
||||||
exampleData[field.displayName] = '测试服务器001';
|
exampleData[field.displayName] = '测试服务器001';
|
||||||
@@ -199,9 +221,6 @@ router.get('/import-template', async (req, res) => {
|
|||||||
case 'serialNumber':
|
case 'serialNumber':
|
||||||
exampleData[field.displayName] = 'SN1234567890';
|
exampleData[field.displayName] = 'SN1234567890';
|
||||||
break;
|
break;
|
||||||
case 'rackId':
|
|
||||||
exampleData[field.displayName] = 'A01';
|
|
||||||
break;
|
|
||||||
case 'position':
|
case 'position':
|
||||||
exampleData[field.displayName] = '1';
|
exampleData[field.displayName] = '1';
|
||||||
break;
|
break;
|
||||||
@@ -455,28 +474,19 @@ router.post('/import', async (req, res) => {
|
|||||||
.on('error', reject);
|
.on('error', reject);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 获取所有机柜信息用于验证
|
// 获取所有机房和机柜信息用于验证
|
||||||
const racks = await Rack.findAll();
|
const rooms = await Room.findAll();
|
||||||
const rackIds = racks.map(rack => rack.rackId);
|
const roomNameToIdMap = new Map(rooms.map(room => [room.name, room.roomId]));
|
||||||
|
const racks = await Rack.findAll({
|
||||||
|
include: [{ model: Room }]
|
||||||
|
});
|
||||||
|
|
||||||
// 获取所有机房信息,用于自动创建机柜时的默认机房
|
// 创建机柜查找映射:机房名称+机柜名称 -> rackId
|
||||||
let rooms = await Room.findAll();
|
const rackLocationMap = new Map();
|
||||||
let defaultRoomId;
|
racks.forEach(rack => {
|
||||||
|
const key = `${rack.Room?.name || ''}_${rack.name}`;
|
||||||
// 如果没有机房,创建一个默认机房
|
rackLocationMap.set(key, rack.rackId);
|
||||||
if (rooms.length === 0) {
|
|
||||||
const defaultRoom = await Room.create({
|
|
||||||
roomId: 'ROOM001',
|
|
||||||
name: '默认机房',
|
|
||||||
location: '默认位置',
|
|
||||||
area: 100,
|
|
||||||
capacity: 100
|
|
||||||
});
|
});
|
||||||
defaultRoomId = defaultRoom.roomId;
|
|
||||||
} else {
|
|
||||||
// 使用第一个机房作为默认机房
|
|
||||||
defaultRoomId = rooms[0].roomId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取设备字段配置
|
// 获取设备字段配置
|
||||||
const deviceFields = await DeviceField.findAll();
|
const deviceFields = await DeviceField.findAll();
|
||||||
@@ -590,28 +600,63 @@ router.post('/import', async (req, res) => {
|
|||||||
throw new Error(`序列号已存在:${serialNumber}`);
|
throw new Error(`序列号已存在:${serialNumber}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证机柜ID是否为空
|
// 获取机房名称和机柜名称
|
||||||
const rackId = getFieldValue('rackId');
|
const roomName = fieldValueMap['所在机房名称'];
|
||||||
if (!rackId || rackId.trim() === '') {
|
const rackName = fieldValueMap['所在机柜名称'];
|
||||||
throw new Error('所在机柜ID不能为空');
|
|
||||||
|
if (!roomName || roomName.trim() === '') {
|
||||||
|
throw new Error('所在机房名称不能为空');
|
||||||
|
}
|
||||||
|
if (!rackName || rackName.trim() === '') {
|
||||||
|
throw new Error('所在机柜名称不能为空');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证机柜是否存在,如果不存在则自动创建
|
// 验证机房是否存在
|
||||||
if (!rackIds.includes(rackId)) {
|
const roomId = roomNameToIdMap.get(roomName.trim());
|
||||||
// 自动创建机柜
|
if (!roomId) {
|
||||||
const newRack = await Rack.create({
|
throw new Error(`机房不存在:${roomName},请先创建机房`);
|
||||||
rackId: rackId,
|
}
|
||||||
name: rackId, // 使用机柜ID作为默认名称
|
|
||||||
height: 42, // 默认42U高度
|
// 根据机房名称+机柜名称查找机柜
|
||||||
maxPower: 10000, // 默认最大功耗10000W
|
const locationKey = `${roomName.trim()}_${rackName.trim()}`;
|
||||||
currentPower: 0,
|
let rackId = rackLocationMap.get(locationKey);
|
||||||
status: 'active',
|
|
||||||
roomId: defaultRoomId
|
// 如果机柜不存在,自动创建
|
||||||
|
if (!rackId) {
|
||||||
|
// 生成新的机柜ID
|
||||||
|
const existingRacks = await Rack.findAll({
|
||||||
|
where: {
|
||||||
|
rackId: {
|
||||||
|
[require('sequelize').Op.like]: 'RACK%'
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 更新机柜列表和ID列表
|
let maxNumber = 0;
|
||||||
racks.push(newRack);
|
existingRacks.forEach(rack => {
|
||||||
rackIds.push(newRack.rackId);
|
const match = rack.rackId.match(/^RACK(\d+)$/);
|
||||||
|
if (match) {
|
||||||
|
const num = parseInt(match[1], 10);
|
||||||
|
if (num > maxNumber) {
|
||||||
|
maxNumber = num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const newRackId = `RACK${String(maxNumber + 1).padStart(3, '0')}`;
|
||||||
|
|
||||||
|
const newRack = await Rack.create({
|
||||||
|
rackId: newRackId,
|
||||||
|
name: rackName.trim(),
|
||||||
|
height: 42,
|
||||||
|
maxPower: 10000,
|
||||||
|
currentPower: 0,
|
||||||
|
status: 'active',
|
||||||
|
roomId: roomId
|
||||||
|
});
|
||||||
|
|
||||||
|
rackId = newRack.rackId;
|
||||||
|
rackLocationMap.set(locationKey, rackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证状态值
|
// 验证状态值
|
||||||
|
|||||||
Reference in New Issue
Block a user