From bfc5b75b1ac60048f864c384417c6dbd398fb280 Mon Sep 17 00:00:00 2001 From: zhang1106 <849185023@qq.com> Date: Thu, 5 Feb 2026 11:00:42 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E7=94=9F=E4=BA=A7?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E5=89=8D=E7=AB=AF=E7=AB=AF=E5=8F=A3=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 检测是否为生产环境(通过检查dist目录) - 生产环境显示Nginx配置提示,不自动重启 - 开发环境保持自动重启功能 - 区分显示生产环境和开发环境状态 --- DEPLOYMENT.md | 2 +- backend/scripts/add-isSystem-mysql.js | 73 ++++++++++++ backend/scripts/add-isSystem-sqlite.js | 49 ++++++++ frontend/.frontend-pid | 1 + frontend/.frontend-port | 1 + frontend/src/pages/SystemSettings.jsx | 152 +++++++++++++++---------- scripts/frontend-manager.js | 16 ++- 7 files changed, 231 insertions(+), 63 deletions(-) create mode 100644 backend/scripts/add-isSystem-mysql.js create mode 100644 backend/scripts/add-isSystem-sqlite.js create mode 100644 frontend/.frontend-pid create mode 100644 frontend/.frontend-port diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 2ad5ff0..cfb8192 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -135,7 +135,7 @@ MYSQL_DATABASE=idc_management #### 2. 安装生产依赖 ```bash -npm install --only=production +npm install ``` #### 3. 配置数据库 diff --git a/backend/scripts/add-isSystem-mysql.js b/backend/scripts/add-isSystem-mysql.js new file mode 100644 index 0000000..6902ccf --- /dev/null +++ b/backend/scripts/add-isSystem-mysql.js @@ -0,0 +1,73 @@ +require('dotenv').config(); +const { sequelize } = require('../db'); + +async function migrate() { + try { + console.log('开始 MySQL 数据库迁移...'); + console.log('数据库类型:', process.env.DB_TYPE); + + // 检查列是否存在(MySQL 方式) + const [columns] = await sequelize.query(` + SELECT COLUMN_NAME + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = 'deviceFields' + AND COLUMN_NAME = 'isSystem' + AND TABLE_SCHEMA = '${process.env.MYSQL_DATABASE || 'it_assest'}' + `); + + if (columns.length === 0) { + // 添加 isSystem 列 + await sequelize.query(` + ALTER TABLE deviceFields + ADD COLUMN isSystem BOOLEAN DEFAULT 0 + COMMENT '是否为系统字段,系统字段不可删除' + `); + console.log('✓ isSystem 列添加成功'); + } else { + console.log('✓ isSystem 列已存在'); + } + + // 更新系统字段标记 + const systemFields = [ + 'deviceId', 'name', 'type', 'model', 'serialNumber', + 'rackId', 'position', 'height', 'powerConsumption', + 'status', 'purchaseDate', 'warrantyExpiry' + ]; + + for (const fieldName of systemFields) { + await sequelize.query(` + UPDATE deviceFields SET isSystem = 1 WHERE fieldName = '${fieldName}' + `); + console.log(`✓ 标记系统字段: ${fieldName}`); + } + + // 验证结果 + const [results] = await sequelize.query(` + SELECT fieldName, displayName, isSystem + FROM deviceFields + ORDER BY isSystem DESC, fieldName + `); + + console.log('\n========== 迁移结果 =========='); + console.log('字段总数:', results.length); + console.log('系统字段数:', results.filter(r => r.isSystem).length); + console.log('\n系统字段列表:'); + results.filter(r => r.isSystem).forEach(r => { + console.log(` 🔒 ${r.displayName} (${r.fieldName})`); + }); + console.log('\n可选字段列表:'); + results.filter(r => !r.isSystem).forEach(r => { + console.log(` ✏️ ${r.displayName} (${r.fieldName})`); + }); + console.log('==============================\n'); + + console.log('✅ 迁移完成!'); + process.exit(0); + } catch (error) { + console.error('❌ 迁移失败:', error.message); + console.error(error); + process.exit(1); + } +} + +migrate(); diff --git a/backend/scripts/add-isSystem-sqlite.js b/backend/scripts/add-isSystem-sqlite.js new file mode 100644 index 0000000..085395c --- /dev/null +++ b/backend/scripts/add-isSystem-sqlite.js @@ -0,0 +1,49 @@ +const { sequelize } = require('../db'); + +async function migrate() { + try { + console.log('开始 SQLite 数据库迁移...'); + + // 检查列是否存在(SQLite 使用 PRAGMA) + const tableInfo = await sequelize.query( + "PRAGMA table_info(deviceFields)", + { type: sequelize.QueryTypes.SELECT } + ); + + const hasIsSystemColumn = tableInfo.some(col => col.name === 'isSystem'); + + if (!hasIsSystemColumn) { + // 添加 isSystem 列 + await sequelize.query( + "ALTER TABLE deviceFields ADD COLUMN isSystem BOOLEAN DEFAULT 0", + { type: sequelize.QueryTypes.RAW } + ); + console.log('isSystem 列添加成功'); + } else { + console.log('isSystem 列已存在'); + } + + // 更新系统字段标记 + const systemFields = [ + 'deviceId', 'name', 'type', 'model', 'serialNumber', + 'rackId', 'position', 'height', 'powerConsumption', + 'status', 'purchaseDate', 'warrantyExpiry' + ]; + + for (const fieldName of systemFields) { + await sequelize.query( + `UPDATE deviceFields SET isSystem = 1 WHERE fieldName = '${fieldName}'`, + { type: sequelize.QueryTypes.RAW } + ); + console.log(`标记系统字段: ${fieldName}`); + } + + console.log('迁移完成!'); + process.exit(0); + } catch (error) { + console.error('迁移失败:', error); + process.exit(1); + } +} + +migrate(); diff --git a/frontend/.frontend-pid b/frontend/.frontend-pid new file mode 100644 index 0000000..de43e88 --- /dev/null +++ b/frontend/.frontend-pid @@ -0,0 +1 @@ +15988 \ No newline at end of file diff --git a/frontend/.frontend-port b/frontend/.frontend-port new file mode 100644 index 0000000..8da85a2 --- /dev/null +++ b/frontend/.frontend-port @@ -0,0 +1 @@ +3000 \ No newline at end of file diff --git a/frontend/src/pages/SystemSettings.jsx b/frontend/src/pages/SystemSettings.jsx index 9966819..ea0b989 100644 --- a/frontend/src/pages/SystemSettings.jsx +++ b/frontend/src/pages/SystemSettings.jsx @@ -71,71 +71,102 @@ const SystemSettings = () => { await axios.put('/api/system-settings', { settings: updates }); - // 如果修改了前端端口,同步配置并自动重启 + // 如果修改了前端端口,同步配置并自动重启(仅开发环境) if ('frontend_port' in updates) { try { // 1. 同步端口到配置文件 await axios.post('/api/system-settings/frontend/port/sync'); - // 2. 显示确认对话框 - Modal.confirm({ - title: '前端端口已修改', - icon: , - content: ( -
-

前端端口已从 {settings.frontend_port?.value} 更改为 {updates.frontend_port}

-

是否立即重启前端服务以应用新端口?

- -
- ), - okText: '立即重启', - cancelText: '稍后手动重启', - onOk: async () => { - const newPort = updates.frontend_port; - const newUrl = `http://localhost:${newPort}`; + // 2. 检查是否为生产环境 + const statusRes = await axios.get('/api/system-settings/frontend/status'); + const isProduction = statusRes.data.isProduction; - // 先显示跳转提示,再调用重启API - // 因为重启API会导致当前服务中断 - Modal.success({ - title: '正在重启前端服务', - content: ( -
-

前端服务正在重启,新端口:{newPort}

-

页面将在3秒后自动跳转到新地址...

-

如果跳转失败,请手动访问:{newUrl}

-
- ), - okText: '立即跳转', - closable: false, - maskClosable: false, - onOk: () => { + if (isProduction) { + // 生产环境:只显示提示,不自动重启 + Modal.info({ + title: '前端端口已修改(生产环境)', + content: ( +
+

前端端口已从 {settings.frontend_port?.value} 更改为 {updates.frontend_port}

+ +

生产环境由 Nginx 或其他服务器托管,请手动更新服务器配置文件:

+

1. 更新 Nginx 配置中的监听端口

+

2. 重启 Nginx 服务

+

3. 更新 vite.config.js 中的端口配置(用于下次构建)

+
+ } + type="info" + showIcon + style={{ marginTop: 16 }} + /> + + ), + okText: '知道了' + }); + } else { + // 开发环境:显示确认对话框并自动重启 + Modal.confirm({ + title: '前端端口已修改', + icon: , + content: ( +
+

前端端口已从 {settings.frontend_port?.value} 更改为 {updates.frontend_port}

+

是否立即重启前端服务以应用新端口?

+ +
+ ), + okText: '立即重启', + cancelText: '稍后手动重启', + onOk: async () => { + const newPort = updates.frontend_port; + const newUrl = `http://localhost:${newPort}`; + + // 先显示跳转提示,再调用重启API + // 因为重启API会导致当前服务中断 + Modal.success({ + title: '正在重启前端服务', + content: ( +
+

前端服务正在重启,新端口:{newPort}

+

页面将在3秒后自动跳转到新地址...

+

如果跳转失败,请手动访问:{newUrl}

+
+ ), + okText: '立即跳转', + closable: false, + maskClosable: false, + onOk: () => { + window.location.href = newUrl; + } + }); + + // 延迟调用重启API,让用户看到提示 + setTimeout(async () => { + try { + // 调用重启API(这个请求可能会因为服务重启而失败) + await axios.post('/api/system-settings/frontend/restart', {}, { timeout: 5000 }); + } catch (error) { + // 忽略错误,因为服务重启会导致连接中断 + console.log('重启请求已发送,服务正在重启...'); + } + }, 1000); + + // 延迟3秒后跳转 + setTimeout(() => { window.location.href = newUrl; - } - }); - - // 延迟调用重启API,让用户看到提示 - setTimeout(async () => { - try { - // 调用重启API(这个请求可能会因为服务重启而失败) - await axios.post('/api/system-settings/frontend/restart', {}, { timeout: 5000 }); - } catch (error) { - // 忽略错误,因为服务重启会导致连接中断 - console.log('重启请求已发送,服务正在重启...'); - } - }, 1000); - - // 延迟3秒后跳转 - setTimeout(() => { - window.location.href = newUrl; - }, 3000); - } - }); + }, 3000); + } + }); + } } catch (syncError) { console.warn('同步前端端口配置失败:', syncError); message.warning('端口配置已保存,但同步到配置文件失败'); @@ -314,12 +345,15 @@ const SystemSettings = () => { {frontendStatus && (

当前端口:{frontendStatus.port}

运行状态:{frontendStatus.isRunning ? 运行中 : 未运行}

{frontendStatus.portInUse &&

警告:端口被其他程序占用

} + {frontendStatus.isProduction && ( +

提示:生产环境由 Nginx 或其他服务器托管,如需修改端口请更新服务器配置

+ )}

访问地址:{frontendStatus.url}

} diff --git a/scripts/frontend-manager.js b/scripts/frontend-manager.js index e51bbe9..a0addbc 100644 --- a/scripts/frontend-manager.js +++ b/scripts/frontend-manager.js @@ -169,12 +169,22 @@ const getStatus = async () => { // 检查端口实际占用情况 const portAvailable = await checkPort(port); + // 检测是否为生产环境(通过检查是否存在 dist 目录) + const frontendDir = path.join(__dirname, '../frontend'); + const distDir = path.join(frontendDir, 'dist'); + const isProduction = fs.existsSync(distDir); + + // 生产环境:只要端口被占用就认为服务在运行(Nginx/其他服务器) + // 开发环境:通过 PID 判断 + const isServiceRunning = isProduction ? !portAvailable : isRunning; + return { pid, port, - isRunning, - portInUse: !portAvailable && !isRunning, // 端口被占用但服务未运行 - url: `http://localhost:${port}` + isRunning: isServiceRunning, + portInUse: !portAvailable && !isServiceRunning, // 端口被占用但服务未运行 + url: `http://localhost:${port}`, + isProduction // 是否为生产环境 }; };