From 0173db80859aa5db13e0cd865d5f2367bee8f662 Mon Sep 17 00:00:00 2001 From: jinchao <383321154@qq.com> Date: Thu, 15 May 2025 16:59:12 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=EF=BC=8C=E8=BF=98=E6=9C=AA=E4=BF=AE=E6=94=B9=E5=89=8D?= =?UTF-8?q?=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 - Release/database/XNSim.db | Bin 200704 -> 278528 bytes XNSimHtml/log/log_20250514_201953.log | 12 - XNSimHtml/log/log_20250514_202037.log | 47 - XNSimHtml/log/log_20250514_203924.log | 47 - XNSimHtml/log/log_20250514_204602.log | 47 - XNSimHtml/log/log_20250515_084119.log | 12 - XNSimHtml/log/log_20250515_084202.log | 47 - XNSimHtml/log/log_20250515_090739.log | 47 - XNSimHtml/log/log_20250515_090823.log | 47 - XNSimHtml/log/log_20250515_091632.log | 47 - XNSimHtml/log/log_20250515_101556.log | 95 -- XNSimHtml/log/log_20250515_104912.log | 62 - XNSimHtml/routes/interface-config.js | 118 +- XNSimHtml/routes/model-dev.js | 22 +- XNSimHtml/routes/qa.js | 2 +- XNSimHtml/routes/run-simulation.js | 2 +- XNSimHtml/routes/service-dev.js | 2 +- XNSimHtml/routes/system-log.js | 2 +- XNSimHtml/routes/todos.js | 2 +- XNSimHtml/utils/configuration-utils.js | 187 +++ XNSimHtml/utils/data-interface-utils.js | 280 ++++ XNSimHtml/utils/db-utils.js | 1729 +-------------------- XNSimHtml/utils/file-utils.js | 42 +- XNSimHtml/utils/model-utils.js | 271 ++++ XNSimHtml/utils/qa-utils.js | 146 ++ XNSimHtml/utils/service-utils.js | 243 +++ XNSimHtml/utils/system-log-utils.js | 71 + XNSimHtml/utils/todo-utils.js | 177 +++ XNSimHtml/utils/xnengine-process-utils.js | 130 ++ 30 files changed, 1595 insertions(+), 2342 deletions(-) delete mode 100644 XNSimHtml/log/log_20250514_201953.log delete mode 100644 XNSimHtml/log/log_20250514_202037.log delete mode 100644 XNSimHtml/log/log_20250514_203924.log delete mode 100644 XNSimHtml/log/log_20250514_204602.log delete mode 100644 XNSimHtml/log/log_20250515_084119.log delete mode 100644 XNSimHtml/log/log_20250515_084202.log delete mode 100644 XNSimHtml/log/log_20250515_090739.log delete mode 100644 XNSimHtml/log/log_20250515_090823.log delete mode 100644 XNSimHtml/log/log_20250515_091632.log delete mode 100644 XNSimHtml/log/log_20250515_101556.log delete mode 100644 XNSimHtml/log/log_20250515_104912.log create mode 100644 XNSimHtml/utils/configuration-utils.js create mode 100644 XNSimHtml/utils/data-interface-utils.js create mode 100644 XNSimHtml/utils/model-utils.js create mode 100644 XNSimHtml/utils/qa-utils.js create mode 100644 XNSimHtml/utils/service-utils.js create mode 100644 XNSimHtml/utils/system-log-utils.js create mode 100644 XNSimHtml/utils/todo-utils.js create mode 100644 XNSimHtml/utils/xnengine-process-utils.js diff --git a/.gitignore b/.gitignore index 0faad47..aae295b 100644 --- a/.gitignore +++ b/.gitignore @@ -36,5 +36,4 @@ build/ #log -log/ logs/ diff --git a/Release/database/XNSim.db b/Release/database/XNSim.db index 0cc271a0fb198a13b0268d575051c7e6f853fd42..0316178704409c2ac628725a621d34318a298bf7 100644 GIT binary patch delta 16275 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 { try { - const { systemName, productName } = req.query; - const interfaces = await getDataInterfaces(systemName, productName); + const { systemName, confID } = req.query; + if (!confID) { + return res.status(400).json({ error: 'ConfID 是必填字段' }); + } + const interfaces = await getDataInterfaces(systemName, confID); res.json(interfaces); } catch (error) { console.error('获取接口列表失败:', error); @@ -87,8 +67,11 @@ router.put('/update', async (req, res) => { // 删除接口 router.delete('/delete', async (req, res) => { try { - const { systemName, productName, ataName, modelStructName, interfaceName } = req.query; - const result = await deleteDataInterface(systemName, productName, ataName, modelStructName, interfaceName); + const { interfaceName, confID } = req.query; + if (!interfaceName || !confID) { + return res.status(400).json({ error: 'InterfaceName 和 ConfID 是必填字段' }); + } + const result = await deleteDataInterface(interfaceName, confID); res.json(result); } catch (error) { console.error('删除接口失败:', error); @@ -99,8 +82,11 @@ router.delete('/delete', async (req, res) => { // 获取接口结构体列表 router.get('/struct/list', async (req, res) => { try { - const { systemName, productName, ataName } = req.query; - const structs = await getDataInterfaceStructs(systemName, productName, ataName); + const { systemName, planeName, ataName, confID } = req.query; + if (!confID) { + return res.status(400).json({ error: 'ConfID 是必填字段' }); + } + const structs = await getDataInterfaceStructs(systemName, planeName, ataName, confID); res.json(structs); } catch (error) { console.error('获取接口结构体列表失败:', error); @@ -108,72 +94,6 @@ router.get('/struct/list', async (req, res) => { } }); -// 添加接口结构体 -router.post('/struct/add', async (req, res) => { - try { - const result = await addDataInterfaceStruct(req.body); - res.json(result); - } catch (error) { - console.error('添加接口结构体失败:', error); - res.status(500).json({ error: error.message || '添加接口结构体失败' }); - } -}); - -// 更新接口结构体 -router.put('/struct/update', async (req, res) => { - try { - const result = await updateDataInterfaceStruct(req.body); - res.json(result); - } catch (error) { - console.error('更新接口结构体失败:', error); - res.status(500).json({ error: error.message || '更新接口结构体失败' }); - } -}); - -// 删除接口结构体 -router.delete('/struct/delete', async (req, res) => { - try { - const { systemName, productName, ataName, modelStructName } = req.query; - const result = await deleteDataInterfaceStruct(systemName, productName, ataName, modelStructName); - res.json(result); - } catch (error) { - console.error('删除接口结构体失败:', error); - res.status(500).json({ error: error.message || '删除接口结构体失败' }); - } -}); - -// 下载模板 -router.get('/template', async (req, res) => { - try { - const workbook = xlsx.utils.book_new(); - const worksheet = xlsx.utils.json_to_sheet([ - { - '系统名称': 'XNSim', - '产品名称': 'C909', - 'ATA章节': '', - '模型结构名': '', - '接口名': '', - '接口类型': '', - '接口选项': '', - '是否为数组': '', - '数组大小1': '', - '数组大小2': '', - '备注': '' - } - ]); - xlsx.utils.book_append_sheet(workbook, worksheet, '接口变量模板'); - - res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); - res.setHeader('Content-Disposition', 'attachment; filename=接口变量模板.xlsx'); - - const buffer = xlsx.write(workbook, { type: 'buffer', bookType: 'xlsx' }); - res.send(buffer); - } catch (error) { - console.error('下载模板失败:', error); - res.status(500).json({ error: '下载模板失败' }); - } -}); - // 导入数据 router.post('/import', async (req, res) => { try { @@ -188,7 +108,7 @@ router.post('/import', async (req, res) => { // 验证所有必填字段 const requiredFields = [ 'SystemName', - 'ProductName', + 'PlaneName', 'ATAName', 'ModelStructName', 'InterfaceName', @@ -197,7 +117,7 @@ router.post('/import', async (req, res) => { 'InterfaceIsArray', 'InterfaceArraySize_1', 'InterfaceArraySize_2', - 'InterfaceNotes' + 'ConfID' ]; // 检查所有必填字段是否存在 diff --git a/XNSimHtml/routes/model-dev.js b/XNSimHtml/routes/model-dev.js index 1c198d6..f9a810f 100644 --- a/XNSimHtml/routes/model-dev.js +++ b/XNSimHtml/routes/model-dev.js @@ -5,7 +5,7 @@ const { getModelsByChapterId, getModelVersionsByClassName, saveModelVersion -} = require('../utils/db-utils'); +} = require('../utils/model-utils'); // 获取所有ATA章节 router.get('/ata-chapters', (req, res) => { @@ -22,13 +22,13 @@ router.get('/ata-chapters', (req, res) => { router.get('/chapter-models/:chapterId', (req, res) => { try { const { chapterId } = req.params; - const { productName } = req.query; + const { planeName } = req.query; if (!chapterId) { return res.status(400).json({ error: '缺少章节ID参数' }); } - const models = getModelsByChapterId(chapterId, productName); + const models = getModelsByChapterId(chapterId, planeName); res.json(models); } catch (error) { console.error('处理章节模型请求时出错:', error); @@ -40,13 +40,13 @@ router.get('/chapter-models/:chapterId', (req, res) => { router.get('/model-versions/:className', (req, res) => { try { const className = req.params.className; - const { productName } = req.query; + const { planeName } = req.query; if (!className) { return res.status(400).json({ error: '模型类名不能为空' }); } - const versions = getModelVersionsByClassName(className, productName); + const versions = getModelVersionsByClassName(className, planeName); res.json(versions); } catch (error) { console.error(`获取模型版本失败: ${error.message}`); @@ -58,17 +58,17 @@ router.get('/model-versions/:className', (req, res) => { router.post('/model-versions', (req, res) => { try { const versionData = req.body; - const { productName } = req.query; if (!versionData || !versionData.ClassName) { return res.status(400).json({ error: '缺少必要的模型版本数据' }); } - // 如果请求中没有productName,则使用versionData中的ProductName - if (!productName && versionData.ProductName) { - versionData.ProductName = versionData.ProductName; - } else if (productName) { - versionData.ProductName = productName; + // 验证必填字段 + const requiredFields = ['ClassName', 'Name', 'Version', 'Author', 'PlaneName', 'ConfID']; + for (const field of requiredFields) { + if (!versionData[field]) { + return res.status(400).json({ error: `${field} 是必填字段` }); + } } const result = saveModelVersion(versionData); diff --git a/XNSimHtml/routes/qa.js b/XNSimHtml/routes/qa.js index 5b27af1..e7a9c68 100644 --- a/XNSimHtml/routes/qa.js +++ b/XNSimHtml/routes/qa.js @@ -6,7 +6,7 @@ const { addAnswer, deleteQuestion, deleteAnswer -} = require('../utils/db-utils'); +} = require('../utils/qa-utils'); // 获取所有问题 router.get('/questions', (req, res) => { diff --git a/XNSimHtml/routes/run-simulation.js b/XNSimHtml/routes/run-simulation.js index c176b77..32de16f 100644 --- a/XNSimHtml/routes/run-simulation.js +++ b/XNSimHtml/routes/run-simulation.js @@ -11,7 +11,7 @@ const { updateXNEngineProcessStatus, deleteXNEngineProcess, getLatestRunningXNEngineProcess -} = require('../utils/db-utils'); +} = require('../utils/xnengine-process-utils'); const { getXNCorePath } = require('../utils/file-utils'); // 存储正在运行的仿真进程 diff --git a/XNSimHtml/routes/service-dev.js b/XNSimHtml/routes/service-dev.js index afb880f..2a8b187 100644 --- a/XNSimHtml/routes/service-dev.js +++ b/XNSimHtml/routes/service-dev.js @@ -5,7 +5,7 @@ const { getServiceVersionsByClassName, saveServiceVersion, createService -} = require('../utils/db-utils'); +} = require('../utils/service-utils'); // 获取所有服务列表 router.get('/services', (req, res) => { diff --git a/XNSimHtml/routes/system-log.js b/XNSimHtml/routes/system-log.js index baef9f1..9b33dd9 100644 --- a/XNSimHtml/routes/system-log.js +++ b/XNSimHtml/routes/system-log.js @@ -1,6 +1,6 @@ const express = require('express'); const router = express.Router(); -const { getSystemLogs, addSystemLog } = require('../utils/db-utils'); +const { getSystemLogs, addSystemLog } = require('../utils/system-log-utils'); // 获取所有系统日志 router.get('/logs', async (req, res) => { diff --git a/XNSimHtml/routes/todos.js b/XNSimHtml/routes/todos.js index 71147a6..e98daf5 100644 --- a/XNSimHtml/routes/todos.js +++ b/XNSimHtml/routes/todos.js @@ -1,6 +1,6 @@ const express = require('express'); const router = express.Router(); -const { getTodos, addTodo, updateTodoStatus, deleteTodo } = require('../utils/db-utils'); +const { getTodos, addTodo, updateTodoStatus, deleteTodo } = require('../utils/todo-utils'); // 获取所有待办事项 router.get('/', async (req, res) => { diff --git a/XNSimHtml/utils/configuration-utils.js b/XNSimHtml/utils/configuration-utils.js new file mode 100644 index 0000000..7cfb571 --- /dev/null +++ b/XNSimHtml/utils/configuration-utils.js @@ -0,0 +1,187 @@ +const { getDBConnection } = require('./file-utils'); + +// 获取所有配置 +function getConfigurations() { + try { + const db = getDBConnection(true); + + const configs = db.prepare(` + SELECT * FROM Configuration + ORDER BY ConfID ASC + `).all(); + + return configs; + } catch (error) { + console.error('获取配置列表失败:', error); + throw error; + } +} + +// 根据ID获取配置 +function getConfigurationById(confId) { + try { + const db = getDBConnection(true); + + const config = db.prepare(` + SELECT * FROM Configuration + WHERE ConfID = ? + `).get(confId); + + return config; + } catch (error) { + console.error(`获取配置ID ${confId} 失败:`, error); + throw error; + } +} + +// 创建新配置 +function createConfiguration(configData) { + try { + // 验证必填字段 + const requiredFields = ['ConfName', 'WorkPath', 'DomainID']; + for (const field of requiredFields) { + if (!configData[field]) { + throw new Error(`${field} 是必填字段`); + } + } + + const db = getDBConnection(); + + const result = db.prepare(` + INSERT INTO Configuration ( + PlaneName, ConfName, OSName, OSVersion, RTXVersion, + CPUAffinity, WorkPath, ModelsPath, ServicesPath, + DomainID, ConsoleDebug, ConsoleInfo, ConsoleWarning, + ConsoleError, LogDebug, LogInfo, LogWarning, LogError + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `).run( + configData.PlaneName || 'C909', + configData.ConfName, + configData.OSName || 'Debian 11', + configData.OSVersion || '5.10.0-32-rt-amd64', + configData.RTXVersion || 'preempt-rt', + configData.CPUAffinity || '0,1', + configData.WorkPath, + configData.ModelsPath || 'Models/', + configData.ServicesPath || 'Services/', + configData.DomainID, + configData.ConsoleDebug !== undefined ? configData.ConsoleDebug : 1, + configData.ConsoleInfo !== undefined ? configData.ConsoleInfo : 1, + configData.ConsoleWarning !== undefined ? configData.ConsoleWarning : 1, + configData.ConsoleError !== undefined ? configData.ConsoleError : 1, + configData.LogDebug !== undefined ? configData.LogDebug : 0, + configData.LogInfo !== undefined ? configData.LogInfo : 1, + configData.LogWarning !== undefined ? configData.LogWarning : 1, + configData.LogError !== undefined ? configData.LogError : 1 + ); + + return { + success: true, + confId: result.lastInsertRowid, + message: '配置创建成功' + }; + } catch (error) { + console.error('创建配置失败:', error); + throw error; + } +} + +// 更新配置 +function updateConfiguration(configData) { + try { + // 验证必填字段 + if (!configData.ConfID) { + throw new Error('ConfID 是必填字段'); + } + + const db = getDBConnection(); + + // 检查配置是否存在 + const existingConfig = db.prepare(` + SELECT COUNT(*) as count FROM Configuration + WHERE ConfID = ? + `).get(configData.ConfID); + + if (existingConfig.count === 0) { + throw new Error('要更新的配置不存在'); + } + + const result = db.prepare(` + UPDATE Configuration + SET PlaneName = ?, + ConfName = ?, + OSName = ?, + OSVersion = ?, + RTXVersion = ?, + CPUAffinity = ?, + WorkPath = ?, + ModelsPath = ?, + ServicesPath = ?, + DomainID = ?, + ConsoleDebug = ?, + ConsoleInfo = ?, + ConsoleWarning = ?, + ConsoleError = ?, + LogDebug = ?, + LogInfo = ?, + LogWarning = ?, + LogError = ? + WHERE ConfID = ? + `).run( + configData.PlaneName || 'C909', + configData.ConfName, + configData.OSName || 'Debian 11', + configData.OSVersion || '5.10.0-32-rt-amd64', + configData.RTXVersion || 'preempt-rt', + configData.CPUAffinity || '0,1', + configData.WorkPath, + configData.ModelsPath || 'Models/', + configData.ServicesPath || 'Services/', + configData.DomainID, + configData.ConsoleDebug !== undefined ? configData.ConsoleDebug : 1, + configData.ConsoleInfo !== undefined ? configData.ConsoleInfo : 1, + configData.ConsoleWarning !== undefined ? configData.ConsoleWarning : 1, + configData.ConsoleError !== undefined ? configData.ConsoleError : 1, + configData.LogDebug !== undefined ? configData.LogDebug : 0, + configData.LogInfo !== undefined ? configData.LogInfo : 1, + configData.LogWarning !== undefined ? configData.LogWarning : 1, + configData.LogError !== undefined ? configData.LogError : 1, + configData.ConfID + ); + + return { + success: true, + changes: result.changes, + message: '配置更新成功' + }; + } catch (error) { + console.error('更新配置失败:', error); + throw error; + } +} + +// 删除配置 +function deleteConfiguration(confId) { + try { + const db = getDBConnection(); + + const result = db.prepare('DELETE FROM Configuration WHERE ConfID = ?').run(confId); + + return { + success: true, + changes: result.changes, + message: result.changes > 0 ? '配置删除成功' : '配置不存在或已被删除' + }; + } catch (error) { + console.error('删除配置失败:', error); + throw error; + } +} + +module.exports = { + getConfigurations, + getConfigurationById, + createConfiguration, + updateConfiguration, + deleteConfiguration +}; \ No newline at end of file diff --git a/XNSimHtml/utils/data-interface-utils.js b/XNSimHtml/utils/data-interface-utils.js new file mode 100644 index 0000000..b0eacd0 --- /dev/null +++ b/XNSimHtml/utils/data-interface-utils.js @@ -0,0 +1,280 @@ +const { getDBConnection } = require('./file-utils'); + +// 获取接口列表 +function getDataInterfaces(systemName = 'XNSim', confID) { + try { + if (!confID) { + throw new Error('ConfID 是必填字段'); + } + + const db = getDBConnection(true); + + const tableName = `DataInterface_${confID}`; + + // 查询所有接口 + const query = ` + SELECT SystemName, PlaneName, ATAName, ModelStructName, InterfaceName, + InterfaceType, InterfaceOption, InterfaceIsArray, + InterfaceArraySize_1, InterfaceArraySize_2, InterfaceNotes + FROM '${tableName}' + WHERE SystemName = ? + ORDER BY PlaneName, ATAName, ModelStructName, InterfaceName + `; + + const interfaces = db.prepare(query).all(systemName); + + return interfaces; + } catch (error) { + console.error('获取接口列表数据失败:', error.message); + throw error; + } +} + +// 添加接口 +function addDataInterface(interfaceData) { + try { + // 验证必填字段 + const requiredFields = [ + 'SystemName', + 'PlaneName', + 'ATAName', + 'ModelStructName', + 'InterfaceName', + 'InterfaceType', + 'InterfaceOption', + 'InterfaceIsArray', + 'InterfaceArraySize_1', + 'InterfaceArraySize_2', + 'ConfID' + ]; + + for (const field of requiredFields) { + if (interfaceData[field] === undefined || interfaceData[field] === null || interfaceData[field] === '') { + throw new Error(`${field} 是必填字段`); + } + } + + const db = getDBConnection(); + + const tableName = `DataInterface_${interfaceData.ConfID}`; + + // 检查接口是否已存在 + const existingInterface = db.prepare(` + SELECT COUNT(*) as count FROM '${tableName}' + WHERE InterfaceName = ? + `).get(interfaceData.InterfaceName); + + if (existingInterface.count > 0) { + throw new Error('接口名称已存在'); + } + + // 插入新接口 + const insertResult = db.prepare(` + INSERT INTO '${tableName}' ( + SystemName, PlaneName, ATAName, ModelStructName, InterfaceName, + InterfaceType, InterfaceOption, InterfaceIsArray, + InterfaceArraySize_1, InterfaceArraySize_2, InterfaceNotes + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `).run( + interfaceData.SystemName, + interfaceData.PlaneName, + interfaceData.ATAName, + interfaceData.ModelStructName, + interfaceData.InterfaceName, + interfaceData.InterfaceType, + interfaceData.InterfaceOption, + interfaceData.InterfaceIsArray, + interfaceData.InterfaceArraySize_1, + interfaceData.InterfaceArraySize_2, + interfaceData.InterfaceNotes || null + ); + + return { + success: true, + id: insertResult.lastInsertRowid, + message: '接口添加成功' + }; + } catch (error) { + console.error('添加接口失败:', error); + throw error; + } +} + +// 更新接口 +function updateDataInterface(interfaceData) { + try { + // 验证必填字段 + const requiredFields = [ + 'SystemName', + 'PlaneName', + 'ATAName', + 'ModelStructName', + 'InterfaceName', + 'InterfaceType', + 'InterfaceOption', + 'InterfaceIsArray', + 'InterfaceArraySize_1', + 'InterfaceArraySize_2', + 'ConfID' + ]; + + // 检查数据结构 + if (!interfaceData.currentData || !interfaceData.originalData) { + throw new Error('数据格式错误:缺少 currentData 或 originalData'); + } + + // 验证 currentData 中的必填字段 + for (const field of requiredFields) { + if (interfaceData.currentData[field] === undefined || + interfaceData.currentData[field] === null || + interfaceData.currentData[field] === '') { + throw new Error(`${field} 是必填字段`); + } + } + + const db = getDBConnection(); + + const tableName = `DataInterface_${interfaceData.currentData.ConfID}`; + + // 首先检查记录是否存在 + const existingRecord = db.prepare(` + SELECT COUNT(*) as count FROM '${tableName}' + WHERE InterfaceName = ? + `).get(interfaceData.originalData.InterfaceName); + + if (existingRecord.count === 0) { + throw new Error('要更新的记录不存在'); + } + + // 更新接口,包括所有字段 + const updateResult = db.prepare(` + UPDATE '${tableName}' + SET SystemName = ?, + PlaneName = ?, + ATAName = ?, + ModelStructName = ?, + InterfaceName = ?, + InterfaceType = ?, + InterfaceOption = ?, + InterfaceIsArray = ?, + InterfaceArraySize_1 = ?, + InterfaceArraySize_2 = ?, + InterfaceNotes = ? + WHERE InterfaceName = ? + `).run( + interfaceData.currentData.SystemName, + interfaceData.currentData.PlaneName, + interfaceData.currentData.ATAName, + interfaceData.currentData.ModelStructName, + interfaceData.currentData.InterfaceName, + interfaceData.currentData.InterfaceType, + interfaceData.currentData.InterfaceOption, + interfaceData.currentData.InterfaceIsArray, + interfaceData.currentData.InterfaceArraySize_1, + interfaceData.currentData.InterfaceArraySize_2, + interfaceData.currentData.InterfaceNotes || null, + interfaceData.originalData.InterfaceName + ); + + if (updateResult.changes === 0) { + throw new Error('更新失败:没有记录被修改'); + } + + return { + success: true, + changes: updateResult.changes, + message: '接口更新成功' + }; + } catch (error) { + console.error('更新接口失败:', error); + throw error; + } +} + +// 删除接口 +function deleteDataInterface(interfaceName, confID) { + try { + if (!confID) { + throw new Error('ConfID 是必填字段'); + } + + if (!interfaceName) { + throw new Error('InterfaceName 是必填字段'); + } + + const db = getDBConnection(); + + const tableName = `DataInterface_${confID}`; + + // 删除接口 + const deleteResult = db.prepare(` + DELETE FROM '${tableName}' + WHERE InterfaceName = ? + `).run(interfaceName); + + return { + success: true, + changes: deleteResult.changes, + message: '接口删除成功' + }; + } catch (error) { + console.error('删除接口失败:', error); + throw error; + } +} + +// 获取接口结构体列表 +function getDataInterfaceStructs(systemName = 'XNSim', planeName, ataName, confID) { + try { + if (!confID) { + throw new Error('ConfID 是必填字段'); + } + + const db = getDBConnection(true); + + // 根据是否传入 planeName 构建不同的查询 + let query; + let params; + const tableName = `DataInterface_${confID}`; + + if (!planeName || planeName === '') { + query = ` + SELECT SystemName, PlaneName, ATAName, ModelStructName + FROM '${tableName}' + WHERE SystemName = ? + GROUP BY ModelStructName + ORDER BY PlaneName, ATAName, ModelStructName + `; + params = [systemName]; + } else { + query = ` + SELECT SystemName, PlaneName, ATAName, ModelStructName + FROM '${tableName}' + WHERE SystemName = ? AND PlaneName = ? + GROUP BY ModelStructName + ORDER BY ATAName, ModelStructName + `; + params = [systemName, planeName]; + } + + if (ataName) { + query = query.replace('GROUP BY', 'AND ATAName = ? GROUP BY'); + params.push(ataName); + } + + const structs = db.prepare(query).all(...params); + + return structs; + } catch (error) { + console.error('获取接口结构体列表数据失败:', error.message); + throw error; + } +} + +module.exports = { + getDataInterfaces, + addDataInterface, + updateDataInterface, + deleteDataInterface, + getDataInterfaceStructs +}; \ No newline at end of file diff --git a/XNSimHtml/utils/db-utils.js b/XNSimHtml/utils/db-utils.js index 9c413ac..b171fcd 100644 --- a/XNSimHtml/utils/db-utils.js +++ b/XNSimHtml/utils/db-utils.js @@ -1,1404 +1,20 @@ -const Database = require('better-sqlite3'); -const { getXNCorePath } = require('./file-utils'); +const { getDBConnection } = require('./file-utils'); -// 查询ATAChapters表 -function getATAChapters() { +// 查询Plane表中的所有飞机 +function getPlanes() { try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } + const db = getDBConnection(true); - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath, { readonly: true }); - - // 直接使用ATAChapters表名查询 - const chapters = db.prepare("SELECT ID, Name, Name_CN FROM 'ATAChapters' ORDER BY ID").all(); - - db.close(); - - return chapters; - } catch (error) { - console.error('获取ATA章节数据失败:', error.message); - throw error; - } -} - -// 根据章节ID查询XNModels表中的模型 -function getModelsByChapterId(chapterId, productName) { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath, { readonly: true }); - - // 根据productName是否为空构建不同的查询 - let query; - let params; - - if (!productName || productName === '' || productName === 'undefined') { - query = ` - SELECT ProductName, Chapters_ID, ModelName, ModelName_CN, Description, ClassName - FROM 'XNModels' - WHERE Chapters_ID = ? - ORDER BY ModelName - `; - params = [chapterId]; - } else { - query = ` - SELECT ProductName, Chapters_ID, ModelName, ModelName_CN, Description, ClassName - FROM 'XNModels' - WHERE Chapters_ID = ? AND ProductName = ? - ORDER BY ModelName - `; - params = [chapterId, productName]; - } - - const models = db.prepare(query).all(...params); - - db.close(); - - return models; - } catch (error) { - console.error(`获取章节${chapterId}的模型数据失败:`, error.message); - throw error; - } -} - -// 根据ClassName查询XNModelsVersion表中的模型版本 -function getModelVersionsByClassName(className, productName) { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath, { readonly: true }); - - // 根据productName是否为空构建不同的查询 - let query; - let params; - - if (!productName || productName === '') { - query = ` - SELECT ProductName, ClassName, Name, Version, CodePath, Author, Description, - CreatTime, ChangeTime, RunFreqGroup, RunNode, Priority, - DataPackagePath, DataPackageHeaderPath, DataPackageEntryPoint, DataPackageInterfaceName - FROM 'XNModelsVersion' - WHERE ClassName = ? - ORDER BY Version DESC - `; - params = [className]; - } else { - query = ` - SELECT ProductName, ClassName, Name, Version, CodePath, Author, Description, - CreatTime, ChangeTime, RunFreqGroup, RunNode, Priority, - DataPackagePath, DataPackageHeaderPath, DataPackageEntryPoint, DataPackageInterfaceName - FROM 'XNModelsVersion' - WHERE ClassName = ? AND ProductName = ? - ORDER BY Version DESC - `; - params = [className, productName]; - } - - const versions = db.prepare(query).all(...params); - - db.close(); - - return versions; - } catch (error) { - console.error(`获取模型${className}的版本数据失败:`, error.message); - throw error; - } -} - -// 保存或更新模型版本信息 -function saveModelVersion(versionData) { - try { - // 验证必填字段 - const requiredFields = ['ClassName', 'Name', 'Version', 'Author', 'ProductName']; - for (const field of requiredFields) { - if (!versionData[field]) { - throw new Error(`${field} 是必填字段`); - } - } - - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - // 检查是否为更新模式 - if (versionData.isUpdate) { - // 查询是否存在要更新的版本 - const existingVersion = db.prepare(` - SELECT COUNT(*) as count FROM 'XNModelsVersion' - WHERE ClassName = ? AND Version = ? AND ProductName = ? - `).get(versionData.ClassName, versionData.originalVersion || versionData.Version, versionData.ProductName); - - if (existingVersion.count === 0) { - // 不存在要更新的版本,创建新版本 - return saveNewVersion(db, versionData); - } - - // 使用前端传来的修改时间,如果没有则生成当前时间 - let changeTime = versionData.ChangeTime; - if (!changeTime) { - // 生成当前时间,格式为YYYY-MM-DD HH:MM:SS - const now = new Date(); - const year = now.getFullYear(); - const month = String(now.getMonth() + 1).padStart(2, '0'); - const day = String(now.getDate()).padStart(2, '0'); - const hours = String(now.getHours()).padStart(2, '0'); - const minutes = String(now.getMinutes()).padStart(2, '0'); - const seconds = String(now.getSeconds()).padStart(2, '0'); - changeTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; - } - - // 更新现有版本 - const updateResult = db.prepare(` - UPDATE 'XNModelsVersion' - SET - Name = ?, - Version = ?, - Author = ?, - Description = ?, - CodePath = ?, - ChangeTime = ?, - RunFreqGroup = ?, - RunNode = ?, - Priority = ?, - DataPackagePath = ?, - DataPackageHeaderPath = ?, - DataPackageEntryPoint = ?, - DataPackageInterfaceName = ?, - ProductName = ? - WHERE ClassName = ? AND Version = ? AND ProductName = ? - `).run( - versionData.Name, - versionData.Version, - versionData.Author, - versionData.Description || '', - versionData.CodePath || '', - changeTime, - versionData.RunFreqGroup || '', - versionData.RunNode || '', - versionData.Priority || '0', - versionData.DataPackagePath || '', - versionData.DataPackageHeaderPath || '', - versionData.DataPackageEntryPoint || '', - versionData.DataPackageInterfaceName || '', - versionData.ProductName, - versionData.ClassName, - versionData.originalVersion || versionData.Version, - versionData.ProductName - ); - - db.close(); - - return { - success: true, - isNew: false, - changes: updateResult.changes, - message: '模型版本更新成功' - }; - } else { - // 创建新版本 - return saveNewVersion(db, versionData); - } - } catch (error) { - console.error('保存模型版本时出错:', error); - throw error; - } -} - -// 内部函数:保存新版本 -function saveNewVersion(db, versionData) { - try { - // 检查版本是否已存在 - const existingVersion = db.prepare(` - SELECT COUNT(*) as count FROM 'XNModelsVersion' - WHERE ClassName = ? AND Version = ? AND ProductName = ? - `).get(versionData.ClassName, versionData.Version, versionData.ProductName); - - if (existingVersion.count > 0) { - db.close(); - throw new Error(`版本 ${versionData.Version} 已存在,请使用其他版本号`); - } - - // 生成当前时间,格式为YYYY-MM-DD HH:MM:SS - const now = new Date(); - const year = now.getFullYear(); - const month = String(now.getMonth() + 1).padStart(2, '0'); - const day = String(now.getDate()).padStart(2, '0'); - const hours = String(now.getHours()).padStart(2, '0'); - const minutes = String(now.getMinutes()).padStart(2, '0'); - const seconds = String(now.getSeconds()).padStart(2, '0'); - const formattedDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; - - // 使用前端传来的时间或生成的时间 - const createTime = versionData.CreatTime || formattedDateTime; - const changeTime = versionData.ChangeTime || formattedDateTime; - - // 插入新版本 - const insertResult = db.prepare(` - INSERT INTO 'XNModelsVersion' ( - ProductName, ClassName, Name, Version, CodePath, Author, Description, - CreatTime, ChangeTime, RunFreqGroup, RunNode, Priority, - DataPackagePath, DataPackageHeaderPath, DataPackageEntryPoint, DataPackageInterfaceName - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `).run( - versionData.ProductName, - versionData.ClassName, - versionData.Name, - versionData.Version, - versionData.CodePath || '', - versionData.Author, - versionData.Description || '', - createTime, - changeTime, - versionData.RunFreqGroup || '', - versionData.RunNode || '', - versionData.Priority || '0', - versionData.DataPackagePath || '', - versionData.DataPackageHeaderPath || '', - versionData.DataPackageEntryPoint || '', - versionData.DataPackageInterfaceName || '' - ); - - db.close(); - - return { - success: true, - isNew: true, - id: insertResult.lastInsertRowid, - message: '模型版本创建成功' - }; - } catch (error) { - if (db) db.close(); - console.error('创建模型版本失败:', error); - throw error; - } -} - -// 查询XNServices表中的所有服务 -function getServices() { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath, { readonly: true }); - - // 查询所有服务 - const services = db.prepare(` - SELECT ServiceName, ServiceName_CN, Description, ClassName - FROM 'XNServices' - ORDER BY ServiceName + // 查询所有飞机 + const planes = db.prepare(` + SELECT PlaneName, Description + FROM 'Plane' + ORDER BY PlaneName `).all(); - db.close(); - - return services; + return planes; } catch (error) { - console.error('获取服务列表数据失败:', error.message); - throw error; - } -} - -// 根据ClassName查询XNServiceVersion表中的服务版本 -function getServiceVersionsByClassName(className) { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath, { readonly: true }); - - // 查询该类名下的所有版本 - const versions = db.prepare(` - SELECT ClassName, Name, Version, CodePath, Author, Description, - CreatTime, ChangeTime - FROM 'XNServiceVersion' - WHERE ClassName = ? - ORDER BY Version DESC - `).all(className); - - db.close(); - - return versions; - } catch (error) { - console.error(`获取服务${className}的版本数据失败:`, error.message); - throw error; - } -} - -// 保存或更新服务版本信息 -function saveServiceVersion(versionData) { - try { - // 验证必填字段 - const requiredFields = ['ClassName', 'Name', 'Version', 'Author']; - for (const field of requiredFields) { - if (!versionData[field]) { - throw new Error(`${field} 是必填字段`); - } - } - - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - // 检查是否为更新模式 - if (versionData.isUpdate) { - // 查询是否存在要更新的版本 - const existingVersion = db.prepare(` - SELECT COUNT(*) as count FROM 'XNServiceVersion' - WHERE ClassName = ? AND Version = ? - `).get(versionData.ClassName, versionData.originalVersion || versionData.Version); - - if (existingVersion.count === 0) { - // 不存在要更新的版本,创建新版本 - return saveNewServiceVersion(db, versionData); - } - - // 使用前端传来的修改时间,如果没有则生成当前时间 - let changeTime = versionData.ChangeTime; - if (!changeTime) { - // 生成当前时间,格式为YYYY-MM-DD HH:MM:SS - const now = new Date(); - const year = now.getFullYear(); - const month = String(now.getMonth() + 1).padStart(2, '0'); - const day = String(now.getDate()).padStart(2, '0'); - const hours = String(now.getHours()).padStart(2, '0'); - const minutes = String(now.getMinutes()).padStart(2, '0'); - const seconds = String(now.getSeconds()).padStart(2, '0'); - changeTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; - } - - // 更新现有版本 - const updateResult = db.prepare(` - UPDATE 'XNServiceVersion' - SET - Name = ?, - Version = ?, - Author = ?, - Description = ?, - CodePath = ?, - ChangeTime = ? - WHERE ClassName = ? AND Version = ? - `).run( - versionData.Name, - versionData.Version, - versionData.Author, - versionData.Description || '', - versionData.CodePath || '', - changeTime, // 使用前端传来的时间或生成的当前时间 - versionData.ClassName, - versionData.originalVersion || versionData.Version - ); - - db.close(); - - return { - success: true, - isNew: false, - changes: updateResult.changes, - message: '服务版本更新成功' - }; - } else { - // 创建新版本 - return saveNewServiceVersion(db, versionData); - } - } catch (error) { - console.error('保存服务版本时出错:', error); - throw error; - } -} - -// 内部函数:保存新的服务版本 -function saveNewServiceVersion(db, versionData) { - try { - // 检查版本是否已存在 - const existingVersion = db.prepare(` - SELECT COUNT(*) as count FROM 'XNServiceVersion' - WHERE ClassName = ? AND Version = ? - `).get(versionData.ClassName, versionData.Version); - - if (existingVersion.count > 0) { - db.close(); - throw new Error(`版本 ${versionData.Version} 已存在,请使用其他版本号`); - } - - // 生成当前时间,格式为YYYY-MM-DD HH:MM:SS - const now = new Date(); - const year = now.getFullYear(); - const month = String(now.getMonth() + 1).padStart(2, '0'); - const day = String(now.getDate()).padStart(2, '0'); - const hours = String(now.getHours()).padStart(2, '0'); - const minutes = String(now.getMinutes()).padStart(2, '0'); - const seconds = String(now.getSeconds()).padStart(2, '0'); - const formattedDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; - - // 使用前端传来的时间或生成的时间 - const createTime = versionData.CreatTime || formattedDateTime; - const changeTime = versionData.ChangeTime || formattedDateTime; - - // 插入新版本 - const insertResult = db.prepare(` - INSERT INTO 'XNServiceVersion' ( - ClassName, Name, Version, CodePath, Author, Description, - CreatTime, ChangeTime - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) - `).run( - versionData.ClassName, - versionData.Name, - versionData.Version, - versionData.CodePath || '', - versionData.Author, - versionData.Description || '', - createTime, // 使用前端传来的创建时间或生成的当前时间 - changeTime // 使用前端传来的修改时间或生成的当前时间 - ); - - db.close(); - - return { - success: true, - isNew: true, - id: insertResult.lastInsertRowid, - message: '服务版本创建成功' - }; - } catch (error) { - if (db) db.close(); - console.error('创建服务版本失败:', error); - throw error; - } -} - -// 创建新服务 -function createService(serviceData) { - try { - // 验证必填字段 - const requiredFields = ['ClassName', 'ServiceName', 'ServiceName_CN']; - for (const field of requiredFields) { - if (!serviceData[field]) { - throw new Error(`${field} 是必填字段`); - } - } - - // 验证类名是否以XN开头 - if (!serviceData.ClassName.startsWith('XN')) { - throw new Error('服务类名必须以XN开头'); - } - - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - // 检查服务类名是否已存在 - const existingService = db.prepare(` - SELECT COUNT(*) as count FROM 'XNServices' - WHERE ClassName = ? - `).get(serviceData.ClassName); - - if (existingService.count > 0) { - db.close(); - throw new Error(`服务类名 ${serviceData.ClassName} 已存在,请使用其他类名`); - } - - // 检查服务名称是否已存在 - const existingServiceName = db.prepare(` - SELECT COUNT(*) as count FROM 'XNServices' - WHERE ServiceName = ? - `).get(serviceData.ServiceName); - - if (existingServiceName.count > 0) { - db.close(); - throw new Error(`服务名称 ${serviceData.ServiceName} 已存在,请使用其他名称`); - } - - // 插入新服务 - const insertResult = db.prepare(` - INSERT INTO 'XNServices' ( - ClassName, ServiceName, ServiceName_CN, Description - ) VALUES (?, ?, ?, ?) - `).run( - serviceData.ClassName, - serviceData.ServiceName, - serviceData.ServiceName_CN, - serviceData.Description || '' - ); - - db.close(); - - return { - success: true, - id: insertResult.lastInsertRowid, - message: '服务创建成功' - }; - } catch (error) { - console.error('创建服务失败:', error); - throw error; - } -} - -// 查询Products表中的所有产品 -function getProducts() { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath, { readonly: true }); - - // 查询所有产品 - const products = db.prepare(` - SELECT ProductName, Description - FROM 'Products' - ORDER BY ProductName - `).all(); - - db.close(); - - return products; - } catch (error) { - console.error('获取产品列表数据失败:', error.message); - throw error; - } -} - -// 获取接口列表 -function getDataInterfaces(systemName = 'XNSim', productName) { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath, { readonly: true }); - - // 根据是否传入 productName 构建不同的查询 - let query; - let params; - - if (!productName || productName === '') { - query = ` - SELECT SystemName, ProductName, ATAName, ModelStructName, InterfaceName, - InterfaceType, InterfaceOption, InterfaceIsArray, - InterfaceArraySize_1, InterfaceArraySize_2, InterfaceNotes - FROM 'DataInterface' - WHERE SystemName = ? - ORDER BY ProductName, ATAName, ModelStructName, InterfaceName - `; - params = [systemName]; - } else { - query = ` - SELECT SystemName, ProductName, ATAName, ModelStructName, InterfaceName, - InterfaceType, InterfaceOption, InterfaceIsArray, - InterfaceArraySize_1, InterfaceArraySize_2, InterfaceNotes - FROM 'DataInterface' - WHERE SystemName = ? AND ProductName = ? - ORDER BY ATAName, ModelStructName, InterfaceName - `; - params = [systemName, productName]; - } - - const interfaces = db.prepare(query).all(...params); - - db.close(); - - return interfaces; - } catch (error) { - console.error('获取接口列表数据失败:', error.message); - throw error; - } -} - -// 添加接口 -function addDataInterface(interfaceData) { - try { - // 验证必填字段 - const requiredFields = [ - 'SystemName', - 'ProductName', - 'ATAName', - 'ModelStructName', - 'InterfaceName', - 'InterfaceType', - 'InterfaceOption', - 'InterfaceIsArray', - 'InterfaceArraySize_1', - 'InterfaceArraySize_2' - ]; - - for (const field of requiredFields) { - if (interfaceData[field] === undefined || interfaceData[field] === null) { - throw new Error(`${field} 是必填字段`); - } - } - - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - // 检查接口是否已存在 - const existingInterface = db.prepare(` - SELECT COUNT(*) as count FROM 'DataInterface' - WHERE SystemName = ? AND ProductName = ? AND ATAName = ? - AND ModelStructName = ? AND InterfaceName = ? - `).get( - interfaceData.SystemName, - interfaceData.ProductName, - interfaceData.ATAName, - interfaceData.ModelStructName, - interfaceData.InterfaceName - ); - - if (existingInterface.count > 0) { - db.close(); - throw new Error('接口已存在'); - } - - // 插入新接口 - const insertResult = db.prepare(` - INSERT INTO 'DataInterface' ( - SystemName, ProductName, ATAName, ModelStructName, InterfaceName, - InterfaceType, InterfaceOption, InterfaceIsArray, - InterfaceArraySize_1, InterfaceArraySize_2, InterfaceNotes - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `).run( - interfaceData.SystemName, - interfaceData.ProductName, - interfaceData.ATAName, - interfaceData.ModelStructName, - interfaceData.InterfaceName, - interfaceData.InterfaceType, - interfaceData.InterfaceOption, - interfaceData.InterfaceIsArray, - interfaceData.InterfaceArraySize_1, - interfaceData.InterfaceArraySize_2, - interfaceData.InterfaceNotes || null - ); - - db.close(); - - return { - success: true, - id: insertResult.lastInsertRowid, - message: '接口添加成功' - }; - } catch (error) { - console.error('添加接口失败:', error); - throw error; - } -} - -// 更新接口 -function updateDataInterface(interfaceData) { - try { - // 验证必填字段 - const requiredFields = [ - 'SystemName', - 'ProductName', - 'ATAName', - 'ModelStructName', - 'InterfaceName', - 'InterfaceType', - 'InterfaceOption', - 'InterfaceIsArray', - 'InterfaceArraySize_1', - 'InterfaceArraySize_2' - ]; - - // 检查数据结构 - if (!interfaceData.currentData || !interfaceData.originalData) { - throw new Error('数据格式错误:缺少 currentData 或 originalData'); - } - - // 验证 currentData 中的必填字段 - for (const field of requiredFields) { - if (interfaceData.currentData[field] === undefined || interfaceData.currentData[field] === null) { - throw new Error(`${field} 是必填字段`); - } - } - - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - // 首先检查记录是否存在 - const existingRecord = db.prepare(` - SELECT COUNT(*) as count FROM 'DataInterface' - WHERE SystemName = ? AND ProductName = ? AND ATAName = ? - AND ModelStructName = ? AND InterfaceName = ? - `).get( - interfaceData.originalData.SystemName, - interfaceData.originalData.ProductName, - interfaceData.originalData.ATAName, - interfaceData.originalData.ModelStructName, - interfaceData.originalData.InterfaceName - ); - - if (existingRecord.count === 0) { - db.close(); - throw new Error('要更新的记录不存在'); - } - - // 更新接口,包括所有字段 - const updateResult = db.prepare(` - UPDATE 'DataInterface' - SET SystemName = ?, - ProductName = ?, - ATAName = ?, - ModelStructName = ?, - InterfaceName = ?, - InterfaceType = ?, - InterfaceOption = ?, - InterfaceIsArray = ?, - InterfaceArraySize_1 = ?, - InterfaceArraySize_2 = ?, - InterfaceNotes = ? - WHERE SystemName = ? AND ProductName = ? AND ATAName = ? - AND ModelStructName = ? AND InterfaceName = ? - `).run( - interfaceData.currentData.SystemName, - interfaceData.currentData.ProductName, - interfaceData.currentData.ATAName, - interfaceData.currentData.ModelStructName, - interfaceData.currentData.InterfaceName, - interfaceData.currentData.InterfaceType, - interfaceData.currentData.InterfaceOption, - interfaceData.currentData.InterfaceIsArray, - interfaceData.currentData.InterfaceArraySize_1, - interfaceData.currentData.InterfaceArraySize_2, - interfaceData.currentData.InterfaceNotes || null, - interfaceData.originalData.SystemName, - interfaceData.originalData.ProductName, - interfaceData.originalData.ATAName, - interfaceData.originalData.ModelStructName, - interfaceData.originalData.InterfaceName - ); - - db.close(); - - if (updateResult.changes === 0) { - throw new Error('更新失败:没有记录被修改'); - } - - return { - success: true, - changes: updateResult.changes, - message: '接口更新成功' - }; - } catch (error) { - console.error('更新接口失败:', error); - throw error; - } -} - -// 删除接口 -function deleteDataInterface(systemName, productName, ataName, modelStructName, interfaceName) { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - // 删除接口 - const deleteResult = db.prepare(` - DELETE FROM 'DataInterface' - WHERE SystemName = ? AND ProductName = ? AND ATAName = ? - AND ModelStructName = ? AND InterfaceName = ? - `).run( - systemName || 'XNSim', - productName || 'C909', - ataName, - modelStructName, - interfaceName - ); - - db.close(); - - return { - success: true, - changes: deleteResult.changes, - message: '接口删除成功' - }; - } catch (error) { - console.error('删除接口失败:', error); - throw error; - } -} - -// 获取接口结构体列表 -function getDataInterfaceStructs(systemName = 'XNSim', productName, ataName) { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath, { readonly: true }); - - // 根据是否传入 productName 构建不同的查询 - let query; - let params; - - if (!productName || productName === '') { - query = ` - SELECT SystemName, ProductName, ATAName, ModelStructName - FROM 'DataInterface' - WHERE SystemName = ? - GROUP BY ModelStructName - ORDER BY ProductName, ATAName, ModelStructName - `; - params = [systemName]; - } else { - query = ` - SELECT SystemName, ProductName, ATAName, ModelStructName - FROM 'DataInterface' - WHERE SystemName = ? AND ProductName = ? - GROUP BY ModelStructName - ORDER BY ATAName, ModelStructName - `; - params = [systemName, productName]; - } - - if (ataName) { - query = query.replace('GROUP BY', 'AND ATAName = ? GROUP BY'); - params.push(ataName); - } - - const structs = db.prepare(query).all(...params); - - db.close(); - - return structs; - } catch (error) { - console.error('获取接口结构体列表数据失败:', error.message); - throw error; - } -} - -// 获取所有问题 -function getQuestions() { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath, { readonly: true }); - - const questions = db.prepare(` - SELECT q.*, - COUNT(a.id) as answer_count, - MAX(a.created_at) as last_answer_time - FROM questions q - LEFT JOIN answers a ON q.id = a.question_id - GROUP BY q.id - ORDER BY q.created_at DESC - `).all(); - - // 获取每个问题的回答 - questions.forEach(question => { - question.answers = db.prepare(` - SELECT * FROM answers - WHERE question_id = ? - ORDER BY created_at ASC - `).all(question.id); - }); - - db.close(); - return questions; - } catch (error) { - console.error('获取问题列表失败:', error); - throw error; - } -} - -// 创建新问题 -function createQuestion(title, content, author) { - try { - if (!title || !content) { - throw new Error('标题和内容不能为空'); - } - - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - const result = db.prepare(` - INSERT INTO questions (title, content, author) - VALUES (?, ?, ?) - `).run(title, content, author || '匿名用户'); - - db.close(); - - if (result.changes > 0) { - return { - success: true, - questionId: result.lastInsertRowid, - message: '问题创建成功' - }; - } else { - throw new Error('问题创建失败'); - } - } catch (error) { - console.error('创建问题失败:', error); - throw error; - } -} - -// 添加回答 -function addAnswer(questionId, content, author) { - try { - if (!content) { - throw new Error('回答内容不能为空'); - } - - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - // 检查问题是否存在 - const question = db.prepare('SELECT id FROM questions WHERE id = ?').get(questionId); - if (!question) { - db.close(); - throw new Error('问题不存在'); - } - - const result = db.prepare(` - INSERT INTO answers (question_id, content, author) - VALUES (?, ?, ?) - `).run(questionId, content, author || '匿名用户'); - - db.close(); - - if (result.changes > 0) { - return { - success: true, - answerId: result.lastInsertRowid, - message: '回答添加成功' - }; - } else { - throw new Error('回答添加失败'); - } - } catch (error) { - console.error('添加回答失败:', error); - throw error; - } -} - -// 删除问题 -function deleteQuestion(questionId) { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - const result = db.prepare('DELETE FROM questions WHERE id = ?').run(questionId); - db.close(); - - if (result.changes > 0) { - return { - success: true, - message: '问题删除成功' - }; - } else { - throw new Error('问题不存在'); - } - } catch (error) { - console.error('删除问题失败:', error); - throw error; - } -} - -// 删除回答 -function deleteAnswer(answerId) { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - const result = db.prepare('DELETE FROM answers WHERE id = ?').run(answerId); - db.close(); - - if (result.changes > 0) { - return { - success: true, - message: '回答删除成功' - }; - } else { - throw new Error('回答不存在'); - } - } catch (error) { - console.error('删除回答失败:', error); - throw error; - } -} - -// 获取所有待办事项 -function getTodos() { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath, { readonly: true }); - - // 创建todos表(如果不存在) - db.prepare(` - CREATE TABLE IF NOT EXISTS todos ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - project TEXT NOT NULL DEFAULT '其它', - subproject TEXT NOT NULL DEFAULT '其它', - title TEXT NOT NULL, - text TEXT, - adduser TEXT NOT NULL, - exeuser TEXT, - completed BOOLEAN DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - sche_time DATETIME DEFAULT CURRENT_TIMESTAMP, - complete_time DATETIME DEFAULT CURRENT_TIMESTAMP - ) - `).run(); - - const todos = db.prepare('SELECT * FROM todos ORDER BY created_at DESC').all(); - db.close(); - - return todos; - } catch (error) { - console.error('获取待办事项失败:', error); - throw error; - } -} - -// 添加待办事项 -function addTodo(todoData) { - try { - if (!todoData.title) { - throw new Error('待办事项标题不能为空'); - } - - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - // 获取当前本地时间 - const now = new Date(); - const year = now.getFullYear(); - const month = String(now.getMonth() + 1).padStart(2, '0'); - const day = String(now.getDate()).padStart(2, '0'); - const hours = String(now.getHours()).padStart(2, '0'); - const minutes = String(now.getMinutes()).padStart(2, '0'); - const seconds = String(now.getSeconds()).padStart(2, '0'); - const localDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; - - // 处理计划时间 - let scheTime = todoData.sche_time; - if (scheTime) { - // 如果sche_time是datetime-local格式(YYYY-MM-DDTHH:mm),转换为数据库格式 - if (scheTime.includes('T')) { - const [datePart, timePart] = scheTime.split('T'); - scheTime = `${datePart} ${timePart}:00`; - } - } else { - scheTime = localDateTime; - } - - const result = db.prepare(` - INSERT INTO todos ( - project, subproject, title, text, adduser, - exeuser, completed, created_at, sche_time - ) VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?) - `).run( - todoData.project || '其它', - todoData.subproject || '其它', - todoData.title, - todoData.text || '', - todoData.adduser || '系统', - todoData.exeuser || null, - localDateTime, - scheTime - ); - - db.close(); - - if (result.changes > 0) { - return { - success: true, - id: result.lastInsertRowid, - message: '待办事项添加成功' - }; - } else { - throw new Error('待办事项添加失败'); - } - } catch (error) { - console.error('添加待办事项失败:', error); - throw error; - } -} - -// 更新待办事项状态 -function updateTodoStatus(id, completed, exeuser, title, text, sche_time) { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - // 获取当前本地时间 - const now = new Date(); - const year = now.getFullYear(); - const month = String(now.getMonth() + 1).padStart(2, '0'); - const day = String(now.getDate()).padStart(2, '0'); - const hours = String(now.getHours()).padStart(2, '0'); - const minutes = String(now.getMinutes()).padStart(2, '0'); - const seconds = String(now.getSeconds()).padStart(2, '0'); - const localDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; - - // 处理计划时间 - let scheTime = sche_time; - if (scheTime) { - // 如果sche_time是datetime-local格式(YYYY-MM-DDTHH:mm),转换为数据库格式 - if (scheTime.includes('T')) { - const [datePart, timePart] = scheTime.split('T'); - scheTime = `${datePart} ${timePart}`; - } - } - - const result = db.prepare(` - UPDATE todos - SET completed = ?, - exeuser = ?, - title = ?, - text = ?, - sche_time = ?, - complete_time = CASE WHEN ? = 1 THEN ? ELSE complete_time END - WHERE id = ? - `).run( - completed ? 1 : 0, - exeuser || null, - title, - text || '', - scheTime || null, - completed ? 1 : 0, - completed ? localDateTime : null, - id - ); - - db.close(); - - if (result.changes > 0) { - return { - success: true, - message: '待办事项更新成功' - }; - } else { - throw new Error('待办事项不存在'); - } - } catch (error) { - console.error('更新待办事项失败:', error); - throw error; - } -} - -// 删除待办事项 -function deleteTodo(id) { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - const result = db.prepare('DELETE FROM todos WHERE id = ?').run(id); - db.close(); - - return { - success: true, - message: result.changes > 0 ? '待办事项删除成功' : '待办事项不存在或已被删除' - }; - } catch (error) { - console.error('删除待办事项失败:', error); + console.error('获取飞机列表数据失败:', error.message); throw error; } } @@ -1406,18 +22,7 @@ function deleteTodo(id) { // 获取所有用户信息 function getUsers() { try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath, { readonly: true }); + const db = getDBConnection(true); const users = db.prepare(` SELECT @@ -1434,8 +39,6 @@ function getUsers() { ORDER BY id ASC `).all(); - db.close(); - return users; } catch (error) { console.error('获取用户信息失败:', error); @@ -1443,311 +46,7 @@ function getUsers() { } } -// 获取所有系统日志 -function getSystemLogs() { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath, { readonly: true }); - - const logs = db.prepare(` - SELECT id, time, level, user, source, log - FROM SystemLog - ORDER BY time DESC - `).all(); - - db.close(); - - return logs; - } catch (error) { - console.error('获取系统日志失败:', error); - throw error; - } -} - -// 添加系统日志 -function addSystemLog(logData) { - try { - // 验证必填字段 - if (!logData.level || !logData.source) { - throw new Error('日志级别和来源是必填字段'); - } - - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - // 获取当前本地时间 - const now = new Date(); - const year = now.getFullYear(); - const month = String(now.getMonth() + 1).padStart(2, '0'); - const day = String(now.getDate()).padStart(2, '0'); - const hours = String(now.getHours()).padStart(2, '0'); - const minutes = String(now.getMinutes()).padStart(2, '0'); - const seconds = String(now.getSeconds()).padStart(2, '0'); - const localDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; - - // 在插入日志时添加时间 - const result = db.prepare(` - INSERT INTO SystemLog (level, user, source, log, time) - VALUES (?, ?, ?, ?, ?) - `).run( - logData.level, - logData.user || null, - logData.source, - logData.log || null, - localDateTime - ); - - db.close(); - - if (result.changes > 0) { - return { - success: true, - id: result.lastInsertRowid, - message: '系统日志添加成功' - }; - } else { - throw new Error('系统日志添加失败'); - } - } catch (error) { - console.error('添加系统日志失败:', error); - throw error; - } -} - -// 获取运行中的XNEngine进程 -function getRunningXNEngineProcess(pid) { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath, { readonly: true }); - - // 创建进程表(如果不存在) - db.prepare(` - CREATE TABLE IF NOT EXISTS xnengine_processes ( - pid INTEGER PRIMARY KEY, - log_file TEXT, - start_time TEXT, - cmd TEXT, - status TEXT DEFAULT 'running', - scenario_file TEXT - ) - `).run(); - - const process = db.prepare('SELECT * FROM xnengine_processes WHERE pid = ?').get(pid); - db.close(); - - return process; - } catch (error) { - console.error('获取XNEngine进程信息失败:', error); - throw error; - } -} - -// 保存XNEngine进程信息 -function saveXNEngineProcess(processData) { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - // 创建进程表(如果不存在) - db.prepare(` - CREATE TABLE IF NOT EXISTS xnengine_processes ( - pid INTEGER PRIMARY KEY, - log_file TEXT, - start_time TEXT, - cmd TEXT, - status TEXT DEFAULT 'running', - scenario_file TEXT - ) - `).run(); - - const result = db.prepare(` - INSERT INTO xnengine_processes (pid, log_file, start_time, cmd, scenario_file) - VALUES (?, ?, ?, ?, ?) - `).run( - processData.pid, - processData.log_file, - processData.start_time, - processData.cmd, - processData.scenario_file || null - ); - - db.close(); - - return { - success: true, - message: 'XNEngine进程信息保存成功' - }; - } catch (error) { - console.error('保存XNEngine进程信息失败:', error); - throw error; - } -} - -// 更新XNEngine进程状态 -function updateXNEngineProcessStatus(pid, status) { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - const result = db.prepare(` - UPDATE xnengine_processes - SET status = ? - WHERE pid = ? - `).run(status, pid); - - db.close(); - - return { - success: true, - message: 'XNEngine进程状态更新成功' - }; - } catch (error) { - console.error('更新XNEngine进程状态失败:', error); - throw error; - } -} - -// 删除XNEngine进程信息 -function deleteXNEngineProcess(pid) { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath); - - const result = db.prepare('DELETE FROM xnengine_processes WHERE pid = ?').run(pid); - db.close(); - - return { - success: true, - message: 'XNEngine进程信息删除成功' - }; - } catch (error) { - console.error('删除XNEngine进程信息失败:', error); - throw error; - } -} - -// 获取最新的运行中XNEngine进程 -function getLatestRunningXNEngineProcess() { - try { - const xnCorePath = getXNCorePath(); - if (!xnCorePath) { - throw new Error('XNCore环境变量未设置,无法获取数据库路径'); - } - - const dbPath = xnCorePath + '/database/XNSim.db'; - if (!dbPath) { - throw new Error('无法找到数据库文件'); - } - - // 打开数据库连接 - const db = new Database(dbPath, { readonly: true }); - - const process = db.prepare(` - SELECT * FROM xnengine_processes - WHERE status = ? - ORDER BY start_time DESC - LIMIT 1 - `).get('running'); - - db.close(); - - return process; - } catch (error) { - console.error('获取最新XNEngine进程信息失败:', error); - throw error; - } -} - module.exports = { - getATAChapters, - getModelsByChapterId, - getModelVersionsByClassName, - saveModelVersion, - getServices, - getServiceVersionsByClassName, - saveServiceVersion, - createService, - getProducts, - getDataInterfaces, - addDataInterface, - updateDataInterface, - deleteDataInterface, - getDataInterfaceStructs, - getQuestions, - createQuestion, - addAnswer, - deleteQuestion, - deleteAnswer, - getTodos, - addTodo, - updateTodoStatus, - deleteTodo, - getUsers, - getSystemLogs, - addSystemLog, - getRunningXNEngineProcess, - saveXNEngineProcess, - updateXNEngineProcessStatus, - deleteXNEngineProcess, - getLatestRunningXNEngineProcess + getPlanes, + getUsers }; \ No newline at end of file diff --git a/XNSimHtml/utils/file-utils.js b/XNSimHtml/utils/file-utils.js index 3489873..006b56a 100644 --- a/XNSimHtml/utils/file-utils.js +++ b/XNSimHtml/utils/file-utils.js @@ -1,5 +1,43 @@ const path = require('path'); const fs = require('fs').promises; +const Database = require('better-sqlite3'); + +// 数据库连接管理 +let dbConnection = null; + +// 获取数据库连接 +function getDBConnection(readonly = false) { + try { + if (dbConnection) { + return dbConnection; + } + + const xnCorePath = getXNCorePath(); + if (!xnCorePath) { + throw new Error('XNCore环境变量未设置,无法获取数据库路径'); + } + + const dbPath = path.join(xnCorePath, 'database', 'XNSim.db'); + if (!dbPath) { + throw new Error('无法找到数据库文件'); + } + + // 打开数据库连接 + dbConnection = new Database(dbPath, { readonly }); + return dbConnection; + } catch (error) { + console.error('数据库连接失败:', error); + throw error; + } +} + +// 关闭数据库连接 +function closeDBConnection() { + if (dbConnection) { + dbConnection.close(); + dbConnection = null; + } +} // 获取XNCore路径 function getXNCorePath() { @@ -112,5 +150,7 @@ module.exports = { getActualLogPath, isPathSafe, ensureDirectoryExists, - validateXml + validateXml, + getDBConnection, + closeDBConnection }; \ No newline at end of file diff --git a/XNSimHtml/utils/model-utils.js b/XNSimHtml/utils/model-utils.js new file mode 100644 index 0000000..266bbbc --- /dev/null +++ b/XNSimHtml/utils/model-utils.js @@ -0,0 +1,271 @@ +const { getDBConnection } = require('./file-utils'); + +// 查询ATAChapters表 +function getATAChapters() { + try { + const db = getDBConnection(true); + + // 直接使用ATAChapters表名查询 + const chapters = db.prepare("SELECT ID, Name, Name_CN FROM 'ATAChapters' ORDER BY ID").all(); + + return chapters; + } catch (error) { + console.error('获取ATA章节数据失败:', error.message); + throw error; + } +} + +// 根据章节ID查询XNModels表中的模型 +function getModelsByChapterId(chapterId, planeName) { + try { + const db = getDBConnection(true); + + // 根据planeName是否为空构建不同的查询 + let query; + let params; + + if (!planeName || planeName === '' || planeName === 'undefined') { + query = ` + SELECT PlaneName, Chapters_ID, ModelName, ModelName_CN, Description, ClassName + FROM 'XNModels' + WHERE Chapters_ID = ? + ORDER BY ModelName + `; + params = [chapterId]; + } else { + query = ` + SELECT PlaneName, Chapters_ID, ModelName, ModelName_CN, Description, ClassName + FROM 'XNModels' + WHERE Chapters_ID = ? AND PlaneName = ? + ORDER BY ModelName + `; + params = [chapterId, planeName]; + } + + const models = db.prepare(query).all(...params); + + return models; + } catch (error) { + console.error(`获取章节${chapterId}的模型数据失败:`, error.message); + throw error; + } +} + +// 根据ClassName查询XNModelsVersion表中的模型版本 +function getModelVersionsByClassName(className, planeName) { + try { + const db = getDBConnection(true); + + // 根据planeName是否为空构建不同的查询 + let query; + let params; + + if (!planeName || planeName === '') { + query = ` + SELECT + PlaneName, ClassName, Name, Version, CodePath, Author, Description, + CreatTime, ChangeTime, RunFreqGroup, RunNode, Priority, + DataPackagePath, DataPackageHeaderPath, DataPackageEntryPoint, DataPackageInterfaceName, + ConfID + FROM 'XNModelsVersion' + WHERE ClassName = ? + ORDER BY Version DESC + `; + params = [className]; + } else { + query = ` + SELECT + PlaneName, ClassName, Name, Version, CodePath, Author, Description, + CreatTime, ChangeTime, RunFreqGroup, RunNode, Priority, + DataPackagePath, DataPackageHeaderPath, DataPackageEntryPoint, DataPackageInterfaceName, + ConfID + FROM 'XNModelsVersion' + WHERE ClassName = ? AND PlaneName = ? + ORDER BY Version DESC + `; + params = [className, planeName]; + } + + const versions = db.prepare(query).all(...params); + + return versions; + } catch (error) { + console.error(`获取模型${className}的版本数据失败:`, error.message); + throw error; + } +} + +// 保存或更新模型版本信息 +function saveModelVersion(versionData) { + try { + // 验证必填字段 + const requiredFields = ['ClassName', 'Name', 'Version', 'Author', 'PlaneName', 'ConfID']; + for (const field of requiredFields) { + if (!versionData[field]) { + throw new Error(`${field} 是必填字段`); + } + } + + const db = getDBConnection(); + + // 检查是否为更新模式 + if (versionData.isUpdate) { + // 查询是否存在要更新的版本 + const existingVersion = db.prepare(` + SELECT COUNT(*) as count FROM 'XNModelsVersion' + WHERE ClassName = ? AND Version = ? AND PlaneName = ? + `).get(versionData.ClassName, versionData.originalVersion || versionData.Version, versionData.PlaneName); + + if (existingVersion.count === 0) { + // 不存在要更新的版本,创建新版本 + return saveNewVersion(db, versionData); + } + + // 使用前端传来的修改时间,如果没有则生成当前时间 + let changeTime = versionData.ChangeTime; + if (!changeTime) { + // 生成当前时间,格式为YYYY-MM-DD HH:MM:SS + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + const day = String(now.getDate()).padStart(2, '0'); + const hours = String(now.getHours()).padStart(2, '0'); + const minutes = String(now.getMinutes()).padStart(2, '0'); + const seconds = String(now.getSeconds()).padStart(2, '0'); + changeTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + } + + // 更新现有版本 + const updateResult = db.prepare(` + UPDATE 'XNModelsVersion' + SET + PlaneName = ?, + ClassName = ?, + Name = ?, + Version = ?, + CodePath = ?, + Author = ?, + Description = ?, + CreatTime = ?, + ChangeTime = ?, + RunFreqGroup = ?, + RunNode = ?, + Priority = ?, + DataPackagePath = ?, + DataPackageHeaderPath = ?, + DataPackageEntryPoint = ?, + DataPackageInterfaceName = ?, + ConfID = ? + WHERE ClassName = ? AND Version = ? AND PlaneName = ? + `).run( + versionData.PlaneName, + versionData.ClassName, + versionData.Name, + versionData.Version, + versionData.CodePath || '', + versionData.Author, + versionData.Description || '', + versionData.CreatTime || changeTime, + changeTime, + versionData.RunFreqGroup || '', + versionData.RunNode || '', + versionData.Priority || '0', + versionData.DataPackagePath || '', + versionData.DataPackageHeaderPath || '', + versionData.DataPackageEntryPoint || '', + versionData.DataPackageInterfaceName || '', + versionData.ConfID, + versionData.ClassName, + versionData.originalVersion || versionData.Version, + versionData.PlaneName + ); + + return { + success: true, + isNew: false, + changes: updateResult.changes, + message: '模型版本更新成功' + }; + } else { + // 创建新版本 + return saveNewVersion(db, versionData); + } + } catch (error) { + console.error('保存模型版本时出错:', error); + throw error; + } +} + +// 内部函数:保存新版本 +function saveNewVersion(db, versionData) { + try { + // 检查版本是否已存在 + const existingVersion = db.prepare(` + SELECT COUNT(*) as count FROM 'XNModelsVersion' + WHERE ClassName = ? AND Version = ? AND PlaneName = ? + `).get(versionData.ClassName, versionData.Version, versionData.PlaneName); + + if (existingVersion.count > 0) { + throw new Error(`版本 ${versionData.Version} 已存在,请使用其他版本号`); + } + + // 生成当前时间,格式为YYYY-MM-DD HH:MM:SS + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + const day = String(now.getDate()).padStart(2, '0'); + const hours = String(now.getHours()).padStart(2, '0'); + const minutes = String(now.getMinutes()).padStart(2, '0'); + const seconds = String(now.getSeconds()).padStart(2, '0'); + const formattedDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + + // 使用前端传来的时间或生成的时间 + const createTime = versionData.CreatTime || formattedDateTime; + const changeTime = versionData.ChangeTime || formattedDateTime; + + // 插入新版本 + const insertResult = db.prepare(` + INSERT INTO 'XNModelsVersion' ( + PlaneName, ClassName, Name, Version, CodePath, Author, Description, + CreatTime, ChangeTime, RunFreqGroup, RunNode, Priority, + DataPackagePath, DataPackageHeaderPath, DataPackageEntryPoint, DataPackageInterfaceName, + ConfID + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `).run( + versionData.PlaneName, + versionData.ClassName, + versionData.Name, + versionData.Version, + versionData.CodePath || '', + versionData.Author, + versionData.Description || '', + createTime, + changeTime, + versionData.RunFreqGroup || '', + versionData.RunNode || '', + versionData.Priority || '0', + versionData.DataPackagePath || '', + versionData.DataPackageHeaderPath || '', + versionData.DataPackageEntryPoint || '', + versionData.DataPackageInterfaceName || '', + versionData.ConfID + ); + + return { + success: true, + isNew: true, + id: insertResult.lastInsertRowid, + message: '模型版本创建成功' + }; + } catch (error) { + console.error('创建模型版本失败:', error); + throw error; + } +} + +module.exports = { + getATAChapters, + getModelsByChapterId, + getModelVersionsByClassName, + saveModelVersion +}; \ No newline at end of file diff --git a/XNSimHtml/utils/qa-utils.js b/XNSimHtml/utils/qa-utils.js new file mode 100644 index 0000000..b43b678 --- /dev/null +++ b/XNSimHtml/utils/qa-utils.js @@ -0,0 +1,146 @@ +const { getDBConnection } = require('./file-utils'); + +// 获取所有问题 +function getQuestions() { + try { + const db = getDBConnection(true); + + const questions = db.prepare(` + SELECT q.*, + COUNT(a.id) as answer_count, + MAX(a.created_at) as last_answer_time + FROM questions q + LEFT JOIN answers a ON q.id = a.question_id + GROUP BY q.id + ORDER BY q.created_at DESC + `).all(); + + // 获取每个问题的回答 + questions.forEach(question => { + question.answers = db.prepare(` + SELECT * FROM answers + WHERE question_id = ? + ORDER BY created_at ASC + `).all(question.id); + }); + + return questions; + } catch (error) { + console.error('获取问题列表失败:', error); + throw error; + } +} + +// 创建新问题 +function createQuestion(title, content, author) { + try { + if (!title || !content) { + throw new Error('标题和内容不能为空'); + } + + const db = getDBConnection(); + + const result = db.prepare(` + INSERT INTO questions (title, content, author) + VALUES (?, ?, ?) + `).run(title, content, author || '匿名用户'); + + if (result.changes > 0) { + return { + success: true, + questionId: result.lastInsertRowid, + message: '问题创建成功' + }; + } else { + throw new Error('问题创建失败'); + } + } catch (error) { + console.error('创建问题失败:', error); + throw error; + } +} + +// 添加回答 +function addAnswer(questionId, content, author) { + try { + if (!content) { + throw new Error('回答内容不能为空'); + } + + const db = getDBConnection(); + + // 检查问题是否存在 + const question = db.prepare('SELECT id FROM questions WHERE id = ?').get(questionId); + if (!question) { + throw new Error('问题不存在'); + } + + const result = db.prepare(` + INSERT INTO answers (question_id, content, author) + VALUES (?, ?, ?) + `).run(questionId, content, author || '匿名用户'); + + if (result.changes > 0) { + return { + success: true, + answerId: result.lastInsertRowid, + message: '回答添加成功' + }; + } else { + throw new Error('回答添加失败'); + } + } catch (error) { + console.error('添加回答失败:', error); + throw error; + } +} + +// 删除问题 +function deleteQuestion(questionId) { + try { + const db = getDBConnection(); + + const result = db.prepare('DELETE FROM questions WHERE id = ?').run(questionId); + + if (result.changes > 0) { + return { + success: true, + message: '问题删除成功' + }; + } else { + throw new Error('问题不存在'); + } + } catch (error) { + console.error('删除问题失败:', error); + throw error; + } +} + +// 删除回答 +function deleteAnswer(answerId) { + try { + const db = getDBConnection(); + + const result = db.prepare('DELETE FROM answers WHERE id = ?').run(answerId); + + if (result.changes > 0) { + return { + success: true, + message: '回答删除成功' + }; + } else { + throw new Error('回答不存在'); + } + } catch (error) { + console.error('删除回答失败:', error); + throw error; + } +} + +module.exports = { + getQuestions, + createQuestion, + addAnswer, + deleteQuestion, + deleteAnswer +}; \ No newline at end of file diff --git a/XNSimHtml/utils/service-utils.js b/XNSimHtml/utils/service-utils.js new file mode 100644 index 0000000..111f310 --- /dev/null +++ b/XNSimHtml/utils/service-utils.js @@ -0,0 +1,243 @@ +const { getDBConnection } = require('./file-utils'); + +// 查询XNServices表中的所有服务 +function getServices() { + try { + const db = getDBConnection(true); + + // 查询所有服务 + const services = db.prepare(` + SELECT ServiceName, ServiceName_CN, Description, ClassName + FROM 'XNServices' + ORDER BY ServiceName + `).all(); + + return services; + } catch (error) { + console.error('获取服务列表数据失败:', error.message); + throw error; + } +} + +// 根据ClassName查询XNServiceVersion表中的服务版本 +function getServiceVersionsByClassName(className) { + try { + const db = getDBConnection(true); + + // 查询该类名下的所有版本 + const versions = db.prepare(` + SELECT ClassName, Name, Version, CodePath, Author, Description, + CreatTime, ChangeTime + FROM 'XNServiceVersion' + WHERE ClassName = ? + ORDER BY Version DESC + `).all(className); + + return versions; + } catch (error) { + console.error(`获取服务${className}的版本数据失败:`, error.message); + throw error; + } +} + +// 保存或更新服务版本信息 +function saveServiceVersion(versionData) { + try { + // 验证必填字段 + const requiredFields = ['ClassName', 'Name', 'Version', 'Author']; + for (const field of requiredFields) { + if (!versionData[field]) { + throw new Error(`${field} 是必填字段`); + } + } + + const db = getDBConnection(); + + // 检查是否为更新模式 + if (versionData.isUpdate) { + // 查询是否存在要更新的版本 + const existingVersion = db.prepare(` + SELECT COUNT(*) as count FROM 'XNServiceVersion' + WHERE ClassName = ? AND Version = ? + `).get(versionData.ClassName, versionData.originalVersion || versionData.Version); + + if (existingVersion.count === 0) { + // 不存在要更新的版本,创建新版本 + return saveNewServiceVersion(db, versionData); + } + + // 使用前端传来的修改时间,如果没有则生成当前时间 + let changeTime = versionData.ChangeTime; + if (!changeTime) { + // 生成当前时间,格式为YYYY-MM-DD HH:MM:SS + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + const day = String(now.getDate()).padStart(2, '0'); + const hours = String(now.getHours()).padStart(2, '0'); + const minutes = String(now.getMinutes()).padStart(2, '0'); + const seconds = String(now.getSeconds()).padStart(2, '0'); + changeTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + } + + // 更新现有版本 + const updateResult = db.prepare(` + UPDATE 'XNServiceVersion' + SET + Name = ?, + Version = ?, + Author = ?, + Description = ?, + CodePath = ?, + ChangeTime = ? + WHERE ClassName = ? AND Version = ? + `).run( + versionData.Name, + versionData.Version, + versionData.Author, + versionData.Description || '', + versionData.CodePath || '', + changeTime, // 使用前端传来的时间或生成的当前时间 + versionData.ClassName, + versionData.originalVersion || versionData.Version + ); + + return { + success: true, + isNew: false, + changes: updateResult.changes, + message: '服务版本更新成功' + }; + } else { + // 创建新版本 + return saveNewServiceVersion(db, versionData); + } + } catch (error) { + console.error('保存服务版本时出错:', error); + throw error; + } +} + +// 内部函数:保存新的服务版本 +function saveNewServiceVersion(db, versionData) { + try { + // 检查版本是否已存在 + const existingVersion = db.prepare(` + SELECT COUNT(*) as count FROM 'XNServiceVersion' + WHERE ClassName = ? AND Version = ? + `).get(versionData.ClassName, versionData.Version); + + if (existingVersion.count > 0) { + throw new Error(`版本 ${versionData.Version} 已存在,请使用其他版本号`); + } + + // 生成当前时间,格式为YYYY-MM-DD HH:MM:SS + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + const day = String(now.getDate()).padStart(2, '0'); + const hours = String(now.getHours()).padStart(2, '0'); + const minutes = String(now.getMinutes()).padStart(2, '0'); + const seconds = String(now.getSeconds()).padStart(2, '0'); + const formattedDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + + // 使用前端传来的时间或生成的时间 + const createTime = versionData.CreatTime || formattedDateTime; + const changeTime = versionData.ChangeTime || formattedDateTime; + + // 插入新版本 + const insertResult = db.prepare(` + INSERT INTO 'XNServiceVersion' ( + ClassName, Name, Version, CodePath, Author, Description, + CreatTime, ChangeTime + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) + `).run( + versionData.ClassName, + versionData.Name, + versionData.Version, + versionData.CodePath || '', + versionData.Author, + versionData.Description || '', + createTime, // 使用前端传来的创建时间或生成的当前时间 + changeTime // 使用前端传来的修改时间或生成的当前时间 + ); + + return { + success: true, + isNew: true, + id: insertResult.lastInsertRowid, + message: '服务版本创建成功' + }; + } catch (error) { + console.error('创建服务版本失败:', error); + throw error; + } +} + +// 创建新服务 +function createService(serviceData) { + try { + // 验证必填字段 + const requiredFields = ['ClassName', 'ServiceName', 'ServiceName_CN']; + for (const field of requiredFields) { + if (!serviceData[field]) { + throw new Error(`${field} 是必填字段`); + } + } + + // 验证类名是否以XN开头 + if (!serviceData.ClassName.startsWith('XN')) { + throw new Error('服务类名必须以XN开头'); + } + + const db = getDBConnection(); + + // 检查服务类名是否已存在 + const existingService = db.prepare(` + SELECT COUNT(*) as count FROM 'XNServices' + WHERE ClassName = ? + `).get(serviceData.ClassName); + + if (existingService.count > 0) { + throw new Error(`服务类名 ${serviceData.ClassName} 已存在,请使用其他类名`); + } + + // 检查服务名称是否已存在 + const existingServiceName = db.prepare(` + SELECT COUNT(*) as count FROM 'XNServices' + WHERE ServiceName = ? + `).get(serviceData.ServiceName); + + if (existingServiceName.count > 0) { + throw new Error(`服务名称 ${serviceData.ServiceName} 已存在,请使用其他名称`); + } + + // 插入新服务 + const insertResult = db.prepare(` + INSERT INTO 'XNServices' ( + ClassName, ServiceName, ServiceName_CN, Description + ) VALUES (?, ?, ?, ?) + `).run( + serviceData.ClassName, + serviceData.ServiceName, + serviceData.ServiceName_CN, + serviceData.Description || '' + ); + + return { + success: true, + id: insertResult.lastInsertRowid, + message: '服务创建成功' + }; + } catch (error) { + console.error('创建服务失败:', error); + throw error; + } +} + +module.exports = { + getServices, + getServiceVersionsByClassName, + saveServiceVersion, + createService +}; \ No newline at end of file diff --git a/XNSimHtml/utils/system-log-utils.js b/XNSimHtml/utils/system-log-utils.js new file mode 100644 index 0000000..ac98fe4 --- /dev/null +++ b/XNSimHtml/utils/system-log-utils.js @@ -0,0 +1,71 @@ +const { getDBConnection } = require('./file-utils'); + +// 获取所有系统日志 +function getSystemLogs() { + try { + const db = getDBConnection(true); + + const logs = db.prepare(` + SELECT id, time, level, user, source, log + FROM SystemLog + ORDER BY time DESC + `).all(); + + return logs; + } catch (error) { + console.error('获取系统日志失败:', error); + throw error; + } +} + +// 添加系统日志 +function addSystemLog(logData) { + try { + // 验证必填字段 + if (!logData.level || !logData.source) { + throw new Error('日志级别和来源是必填字段'); + } + + const db = getDBConnection(); + + // 获取当前本地时间 + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + const day = String(now.getDate()).padStart(2, '0'); + const hours = String(now.getHours()).padStart(2, '0'); + const minutes = String(now.getMinutes()).padStart(2, '0'); + const seconds = String(now.getSeconds()).padStart(2, '0'); + const localDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + + // 在插入日志时添加时间 + const result = db.prepare(` + INSERT INTO SystemLog (level, user, source, log, time) + VALUES (?, ?, ?, ?, ?) + `).run( + logData.level, + logData.user || null, + logData.source, + logData.log || null, + localDateTime + ); + + if (result.changes > 0) { + return { + success: true, + id: result.lastInsertRowid, + message: '系统日志添加成功' + }; + } else { + throw new Error('系统日志添加失败'); + } + } catch (error) { + console.error('添加系统日志失败:', error); + throw error; + } +} + +module.exports = { + getSystemLogs, + addSystemLog +}; \ No newline at end of file diff --git a/XNSimHtml/utils/todo-utils.js b/XNSimHtml/utils/todo-utils.js new file mode 100644 index 0000000..16b4ff6 --- /dev/null +++ b/XNSimHtml/utils/todo-utils.js @@ -0,0 +1,177 @@ +const { getDBConnection } = require('./file-utils'); + +// 获取所有待办事项 +function getTodos() { + try { + const db = getDBConnection(true); + + // 创建todos表(如果不存在) + db.prepare(` + CREATE TABLE IF NOT EXISTS todos ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + project TEXT NOT NULL DEFAULT '其它', + subproject TEXT NOT NULL DEFAULT '其它', + title TEXT NOT NULL, + text TEXT, + adduser TEXT NOT NULL, + exeuser TEXT, + completed BOOLEAN DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + sche_time DATETIME DEFAULT CURRENT_TIMESTAMP, + complete_time DATETIME DEFAULT CURRENT_TIMESTAMP + ) + `).run(); + + const todos = db.prepare('SELECT * FROM todos ORDER BY created_at DESC').all(); + + return todos; + } catch (error) { + console.error('获取待办事项失败:', error); + throw error; + } +} + +// 添加待办事项 +function addTodo(todoData) { + try { + if (!todoData.title) { + throw new Error('待办事项标题不能为空'); + } + + const db = getDBConnection(); + + // 获取当前本地时间 + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + const day = String(now.getDate()).padStart(2, '0'); + const hours = String(now.getHours()).padStart(2, '0'); + const minutes = String(now.getMinutes()).padStart(2, '0'); + const seconds = String(now.getSeconds()).padStart(2, '0'); + const localDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + + // 处理计划时间 + let scheTime = todoData.sche_time; + if (scheTime) { + // 如果sche_time是datetime-local格式(YYYY-MM-DDTHH:mm),转换为数据库格式 + if (scheTime.includes('T')) { + const [datePart, timePart] = scheTime.split('T'); + scheTime = `${datePart} ${timePart}:00`; + } + } else { + scheTime = localDateTime; + } + + const result = db.prepare(` + INSERT INTO todos ( + project, subproject, title, text, adduser, + exeuser, completed, created_at, sche_time + ) VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?) + `).run( + todoData.project || '其它', + todoData.subproject || '其它', + todoData.title, + todoData.text || '', + todoData.adduser || '系统', + todoData.exeuser || null, + localDateTime, + scheTime + ); + + if (result.changes > 0) { + return { + success: true, + id: result.lastInsertRowid, + message: '待办事项添加成功' + }; + } else { + throw new Error('待办事项添加失败'); + } + } catch (error) { + console.error('添加待办事项失败:', error); + throw error; + } +} + +// 更新待办事项状态 +function updateTodoStatus(id, completed, exeuser, title, text, sche_time) { + try { + const db = getDBConnection(); + + // 获取当前本地时间 + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + const day = String(now.getDate()).padStart(2, '0'); + const hours = String(now.getHours()).padStart(2, '0'); + const minutes = String(now.getMinutes()).padStart(2, '0'); + const seconds = String(now.getSeconds()).padStart(2, '0'); + const localDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + + // 处理计划时间 + let scheTime = sche_time; + if (scheTime) { + // 如果sche_time是datetime-local格式(YYYY-MM-DDTHH:mm),转换为数据库格式 + if (scheTime.includes('T')) { + const [datePart, timePart] = scheTime.split('T'); + scheTime = `${datePart} ${timePart}`; + } + } + + const result = db.prepare(` + UPDATE todos + SET completed = ?, + exeuser = ?, + title = ?, + text = ?, + sche_time = ?, + complete_time = CASE WHEN ? = 1 THEN ? ELSE complete_time END + WHERE id = ? + `).run( + completed ? 1 : 0, + exeuser || null, + title, + text || '', + scheTime || null, + completed ? 1 : 0, + completed ? localDateTime : null, + id + ); + + if (result.changes > 0) { + return { + success: true, + message: '待办事项更新成功' + }; + } else { + throw new Error('待办事项不存在'); + } + } catch (error) { + console.error('更新待办事项失败:', error); + throw error; + } +} + +// 删除待办事项 +function deleteTodo(id) { + try { + const db = getDBConnection(); + + const result = db.prepare('DELETE FROM todos WHERE id = ?').run(id); + + return { + success: true, + message: result.changes > 0 ? '待办事项删除成功' : '待办事项不存在或已被删除' + }; + } catch (error) { + console.error('删除待办事项失败:', error); + throw error; + } +} + +module.exports = { + getTodos, + addTodo, + updateTodoStatus, + deleteTodo +}; \ No newline at end of file diff --git a/XNSimHtml/utils/xnengine-process-utils.js b/XNSimHtml/utils/xnengine-process-utils.js new file mode 100644 index 0000000..731110b --- /dev/null +++ b/XNSimHtml/utils/xnengine-process-utils.js @@ -0,0 +1,130 @@ +const { getDBConnection } = require('./file-utils'); + +// 获取运行中的XNEngine进程 +function getRunningXNEngineProcess(pid) { + try { + const db = getDBConnection(true); + + // 创建进程表(如果不存在) + db.prepare(` + CREATE TABLE IF NOT EXISTS xnengine_processes ( + pid INTEGER PRIMARY KEY, + log_file TEXT, + start_time TEXT, + cmd TEXT, + status TEXT DEFAULT 'running', + scenario_file TEXT + ) + `).run(); + + const process = db.prepare('SELECT * FROM xnengine_processes WHERE pid = ?').get(pid); + + return process; + } catch (error) { + console.error('获取XNEngine进程信息失败:', error); + throw error; + } +} + +// 保存XNEngine进程信息 +function saveXNEngineProcess(processData) { + try { + const db = getDBConnection(); + + // 创建进程表(如果不存在) + db.prepare(` + CREATE TABLE IF NOT EXISTS xnengine_processes ( + pid INTEGER PRIMARY KEY, + log_file TEXT, + start_time TEXT, + cmd TEXT, + status TEXT DEFAULT 'running', + scenario_file TEXT + ) + `).run(); + + const result = db.prepare(` + INSERT INTO xnengine_processes (pid, log_file, start_time, cmd, scenario_file) + VALUES (?, ?, ?, ?, ?) + `).run( + processData.pid, + processData.log_file, + processData.start_time, + processData.cmd, + processData.scenario_file || null + ); + + return { + success: true, + message: 'XNEngine进程信息保存成功' + }; + } catch (error) { + console.error('保存XNEngine进程信息失败:', error); + throw error; + } +} + +// 更新XNEngine进程状态 +function updateXNEngineProcessStatus(pid, status) { + try { + const db = getDBConnection(); + + const result = db.prepare(` + UPDATE xnengine_processes + SET status = ? + WHERE pid = ? + `).run(status, pid); + + return { + success: true, + message: 'XNEngine进程状态更新成功' + }; + } catch (error) { + console.error('更新XNEngine进程状态失败:', error); + throw error; + } +} + +// 删除XNEngine进程信息 +function deleteXNEngineProcess(pid) { + try { + const db = getDBConnection(); + + const result = db.prepare('DELETE FROM xnengine_processes WHERE pid = ?').run(pid); + + return { + success: true, + message: 'XNEngine进程信息删除成功' + }; + } catch (error) { + console.error('删除XNEngine进程信息失败:', error); + throw error; + } +} + +// 获取最新的运行中XNEngine进程 +function getLatestRunningXNEngineProcess() { + try { + const db = getDBConnection(true); + + const process = db.prepare(` + SELECT * FROM xnengine_processes + WHERE status = ? + ORDER BY start_time DESC + LIMIT 1 + `).get('running'); + + return process; + } catch (error) { + console.error('获取最新XNEngine进程信息失败:', error); + throw error; + } +} + +module.exports = { + getRunningXNEngineProcess, + saveXNEngineProcess, + updateXNEngineProcessStatus, + deleteXNEngineProcess, + getLatestRunningXNEngineProcess +}; \ No newline at end of file