From 0763aebc1043f8f170f5eaf2b7608801c000c90e Mon Sep 17 00:00:00 2001 From: zhang1106 <849185023@qq.com> Date: Fri, 6 Feb 2026 16:27:53 +0800 Subject: [PATCH] =?UTF-8?q?fix(racks):=20=E4=BF=AE=E5=A4=8D=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E8=B7=AF=E7=94=B1=E4=BD=8D=E7=BD=AE=E4=BB=A5=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E8=A2=AB=E5=BD=93=E4=BD=9CrackId=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将导出租机柜数据的路由移动到/:rackId路由之前,防止路由冲突 --- backend/routes/racks.js | 194 ++++++++++++++++++++-------------------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/backend/routes/racks.js b/backend/routes/racks.js index 589b97a..eadbaf8 100644 --- a/backend/routes/racks.js +++ b/backend/routes/racks.js @@ -98,6 +98,103 @@ router.get('/import-template', async (req, res) => { } }); +// 导出租机柜数据 - 必须放在 /:rackId 路由之前,避免被当作 rackId 参数 +router.get('/export', async (req, res) => { + try { + // 获取所有机柜数据(包含机房信息) + const racks = await Rack.findAll({ + include: [ + { model: Room, attributes: ['name'] }, + { model: Device, attributes: ['deviceId', 'name', 'powerConsumption'] } + ], + order: [['rackId', 'ASC']] + }); + + // 准备导出数据 + const exportData = racks.map(rack => { + const deviceCount = rack.Devices ? rack.Devices.length : 0; + const totalPower = rack.Devices ? rack.Devices.reduce((sum, d) => sum + (d.powerConsumption || 0), 0) : 0; + + return { + '机柜ID': rack.rackId, + '机柜名称': rack.name, + '所属机房': rack.Room ? rack.Room.name : '', + '机柜高度(U)': rack.height, + '最大功耗(W)': rack.maxPower, + '当前功耗(W)': rack.currentPower || 0, + '设备数量': deviceCount, + '设备总功耗(W)': totalPower, + '状态': rack.status === 'active' ? '启用' : '停用', + '创建时间': rack.createdAt ? new Date(rack.createdAt).toLocaleString() : '' + }; + }); + + // 创建工作簿 + const wb = XLSX.utils.book_new(); + const ws = XLSX.utils.json_to_sheet(exportData); + + // 设置列宽 + ws['!cols'] = [ + { wch: 15 }, // 机柜ID + { wch: 20 }, // 机柜名称 + { wch: 20 }, // 所属机房 + { wch: 12 }, // 机柜高度 + { wch: 15 }, // 最大功耗 + { wch: 15 }, // 当前功耗 + { wch: 12 }, // 设备数量 + { wch: 15 }, // 设备总功耗 + { wch: 10 }, // 状态 + { wch: 20 } // 创建时间 + ]; + + XLSX.utils.book_append_sheet(wb, ws, '机柜列表'); + + // 生成文件名 + const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); + const fileName = `机柜导出_${timestamp}.xlsx`; + + // 确保temp目录存在 + const tempDir = path.join(__dirname, '../temp'); + if (!fs.existsSync(tempDir)) { + fs.mkdirSync(tempDir, { recursive: true }); + } + + const filePath = path.join(tempDir, fileName); + + // 写入文件 + XLSX.writeFile(wb, filePath); + + // 发送文件 + res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + res.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(fileName)}`); + + const fileStream = fs.createReadStream(filePath); + fileStream.pipe(res); + + // 发送完成后删除临时文件 + fileStream.on('close', () => { + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); + } + }); + + fileStream.on('error', (err) => { + console.error('文件流错误:', err); + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); + } + }); + + } catch (error) { + console.error('导出租机柜数据失败:', error); + res.status(500).json({ + success: false, + message: '导出失败', + error: error.message + }); + } +}); + // 获取单个机柜 router.get('/:rackId', async (req, res) => { try { @@ -452,101 +549,4 @@ router.post('/import', async (req, res) => { } }); -// 导出租机柜数据 -router.get('/export', async (req, res) => { - try { - // 获取所有机柜数据(包含机房信息) - const racks = await Rack.findAll({ - include: [ - { model: Room, attributes: ['name'] }, - { model: Device, attributes: ['deviceId', 'name', 'powerConsumption'] } - ], - order: [['rackId', 'ASC']] - }); - - // 准备导出数据 - const exportData = racks.map(rack => { - const deviceCount = rack.Devices ? rack.Devices.length : 0; - const totalPower = rack.Devices ? rack.Devices.reduce((sum, d) => sum + (d.powerConsumption || 0), 0) : 0; - - return { - '机柜ID': rack.rackId, - '机柜名称': rack.name, - '所属机房': rack.Room ? rack.Room.name : '', - '机柜高度(U)': rack.height, - '最大功耗(W)': rack.maxPower, - '当前功耗(W)': rack.currentPower || 0, - '设备数量': deviceCount, - '设备总功耗(W)': totalPower, - '状态': rack.status === 'active' ? '启用' : '停用', - '创建时间': rack.createdAt ? new Date(rack.createdAt).toLocaleString() : '' - }; - }); - - // 创建工作簿 - const wb = XLSX.utils.book_new(); - const ws = XLSX.utils.json_to_sheet(exportData); - - // 设置列宽 - ws['!cols'] = [ - { wch: 15 }, // 机柜ID - { wch: 20 }, // 机柜名称 - { wch: 20 }, // 所属机房 - { wch: 12 }, // 机柜高度 - { wch: 15 }, // 最大功耗 - { wch: 15 }, // 当前功耗 - { wch: 12 }, // 设备数量 - { wch: 15 }, // 设备总功耗 - { wch: 10 }, // 状态 - { wch: 20 } // 创建时间 - ]; - - XLSX.utils.book_append_sheet(wb, ws, '机柜列表'); - - // 生成文件名 - const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); - const fileName = `机柜导出_${timestamp}.xlsx`; - - // 确保temp目录存在 - const tempDir = path.join(__dirname, '../temp'); - if (!fs.existsSync(tempDir)) { - fs.mkdirSync(tempDir, { recursive: true }); - } - - const filePath = path.join(tempDir, fileName); - - // 写入文件 - XLSX.writeFile(wb, filePath); - - // 发送文件 - res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); - res.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(fileName)}`); - - const fileStream = fs.createReadStream(filePath); - fileStream.pipe(res); - - // 发送完成后删除临时文件 - fileStream.on('close', () => { - if (fs.existsSync(filePath)) { - fs.unlinkSync(filePath); - } - }); - - fileStream.on('error', (err) => { - console.error('文件流错误:', err); - if (fs.existsSync(filePath)) { - fs.unlinkSync(filePath); - } - }); - - } catch (error) { - console.error('导出租机柜数据失败:', error); - res.status(500).json({ - success: false, - message: '导出失败', - error: error.message - }); - } -}); - module.exports = router; \ No newline at end of file