fix: 修复后端12个+前端5个高优先级问题

后端:
- devices.js: 修复 newRackId 重复声明、批量移动加事务、ID生成改MAX聚合
- tickets.js: 添加工单状态机校验、req.body白名单过滤
- roles.js: init-roles 添加 authMiddleware
- backup.js: 备份列表只读4KB头部,压缩文件超1MB跳过
- consumables.js: SN查询改用数据库LIKE替代全表扫描
- consumableRecords.js: 出入库加 SELECT FOR UPDATE 行级锁
- inventory.js: not_found 状态判断从 abnormal if 内移到 else if
- idleDevices.js: 删除重复的 batch-restore 路由定义(182行)
- cables.js: 接线冲突检测增加反向端口检查

前端:
- AuthContext.jsx: 初始化时调用 fetchProfile 验证token、Error提取message
- api/index.js: backupAPI.download 返回Promise、checkAdmin改POST
- Login.jsx: checkIsFirstUser 静默处理错误
- DeviceManagement.jsx: 导出 currentPage scope 使用 currentPageDevices
This commit is contained in:
zhang96110
2026-04-01 07:27:19 +00:00
parent 1d7a83bbe8
commit de7da77c11
13 changed files with 122 additions and 223 deletions
-182
View File
@@ -779,188 +779,6 @@ router.put('/:deviceId/restore', async (req, res) => {
}
});
router.put('/batch-restore', async (req, res) => {
const t = await require('../db').sequelize.transaction();
try {
const { devices } = req.body;
console.log('========== batch-restore 开始 ==========');
console.log('原始请求 body:', JSON.stringify(req.body));
console.log('devices 参数:', devices);
if (!devices || !Array.isArray(devices) || devices.length === 0) {
await t.rollback();
console.log('错误: devices 参数无效');
return res.status(400).json({ error: '请提供有效的设备列表' });
}
const deviceIds = devices.map(d => d.deviceId).filter(Boolean);
console.log('提取的 deviceIds:', deviceIds);
console.log('deviceIds 类型:', typeof deviceIds, Array.isArray(deviceIds));
if (deviceIds.length === 0) {
await t.rollback();
console.log('错误: deviceIds 为空');
return res.status(400).json({ error: '设备ID不能为空' });
}
console.log('开始查询设备,条件:', { deviceId: { [Op.in]: deviceIds }, isIdle: true });
const idleDevices = await Device.findAll({
where: { deviceId: { [Op.in]: deviceIds }, isIdle: true },
transaction: t,
});
console.log('查询到的空闲设备数量:', idleDevices.length);
if (idleDevices.length > 0) {
console.log(
'查询到的设备ID:',
idleDevices.map(d => d.deviceId)
);
}
if (idleDevices.length === 0) {
console.log('没有找到空闲设备,检查设备是否存在:');
const allDevices = await Device.findAll({
where: { deviceId: { [Op.in]: deviceIds } },
transaction: t,
});
console.log('设备表中存在的设备数量:', allDevices.length);
if (allDevices.length > 0) {
console.log(
'存在的设备及其 isIdle 状态:',
allDevices.map(d => ({ deviceId: d.deviceId, isIdle: d.isIdle }))
);
}
await t.rollback();
return res.status(404).json({ error: '没有找到空闲设备' });
}
let restoredCount = 0;
const results = [];
for (const device of idleDevices) {
const deviceConfig = devices.find(d => d.deviceId === device.deviceId);
if (!deviceConfig) {
continue;
}
const targetRackId = deviceConfig.targetRackId;
const targetPosition = deviceConfig.targetPosition;
if (!targetRackId) {
results.push({
deviceId: device.deviceId,
name: device.name,
status: 'skipped',
reason: '未指定目标机柜',
});
continue;
}
const targetRack = await Rack.findByPk(targetRackId, { transaction: t });
if (!targetRack) {
results.push({
deviceId: device.deviceId,
name: device.name,
status: 'failed',
reason: '目标机柜不存在',
});
continue;
}
const height = device.height || 1;
const position = targetPosition || 1;
const checkResult = await checkPositionAvailable(targetRackId, position, height, null, t);
if (!checkResult.available) {
results.push({
deviceId: device.deviceId,
name: device.name,
status: 'failed',
reason: `U位${position}已被占用`,
});
continue;
}
await device.update(
{
isIdle: false,
idleDate: null,
idleReason: null,
rackId: targetRackId,
position: position,
warehouseId: null,
sourceType: 'rack',
status: 'offline',
},
{ transaction: t }
);
await targetRack.update(
{
currentPower: targetRack.currentPower + (device.powerConsumption || 0),
},
{ transaction: t }
);
restoredCount++;
results.push({
deviceId: device.deviceId,
name: device.name,
status: 'success',
targetRack: targetRack.name,
targetPosition: position,
});
}
await t.commit();
const successCount = results.filter(r => r.status === 'success').length;
const failedCount = results.filter(r => r.status === 'failed').length;
const skippedCount = results.filter(r => r.status === 'skipped').length;
const successDevices = idleDevices.filter(d =>
results.some(r => r.deviceId === d.deviceId && r.status === 'success')
);
const deviceSummary = successDevices
.map(
d =>
`${d.name}(编号:${d.deviceId}${d.type ? `,类型:${d.type}` : ''}${d.ipAddress ? `,IP:${d.ipAddress}` : ''})`
)
.join('、');
await logDeviceOperation(
'batch_restore',
`批量上架 ${successCount} 台空闲设备:${deviceSummary}`,
{
targetId: deviceIds.join(','),
targetName: `${successCount}台设备`,
req,
metadata: {
results,
type: 'batch_idle_device_restore',
devices: successDevices.map(d => d.toJSON()),
},
}
);
res.json({
message: `成功上架 ${successCount} 台设备`,
total: idleDevices.length,
restored: successCount,
failed: failedCount,
skipped: skippedCount,
details: results,
});
} catch (error) {
await t.rollback();
console.error('批量上架设备失败:', error);
res.status(500).json({ error: error.message });
}
});
router.delete('/:deviceId', async (req, res) => {
const t = await require('../db').sequelize.transaction();
try {