699 lines
19 KiB
JavaScript
699 lines
19 KiB
JavaScript
const express = require('express');
|
|
const cors = require('cors');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const app = express();
|
|
|
|
// 初始连接映射数据
|
|
const initialConnectionMap = {
|
|
pass: {
|
|
//---Copper连接检查---
|
|
"main-permanent": {
|
|
type: "copper",
|
|
connectedTo: "remote-channel",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "pass"
|
|
},
|
|
"remote-channel": {
|
|
type: "copper",
|
|
connectedTo: "main-channel",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "pass"
|
|
},
|
|
"main-channel": {
|
|
type: "copper",
|
|
connectedTo: "remote-permanent",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "pass"
|
|
},
|
|
//---CFP连接检查---
|
|
"main-cfp-sm-out": {
|
|
type: "fiber",
|
|
connectedTo: "remote-cfp-in",
|
|
fiberstatus: "ref-pass"
|
|
},
|
|
"main-cfp-mm-out": {
|
|
type: "fiber",
|
|
connectedTo: "remote-cfp-in",
|
|
fiberstatus: "ref-pass"
|
|
},
|
|
|
|
"remote-cfp-sm-out": {
|
|
type: "fiber",
|
|
connectedTo: "main-cfp-in",
|
|
fiberstatus: "ref-pass"
|
|
},
|
|
"remote-cfp-mm-out": {
|
|
type: "fiber",
|
|
connectedTo: "main-cfp-in",
|
|
fiberstatus: "ref-pass"
|
|
},
|
|
|
|
},
|
|
Office: {
|
|
"Room1-TO-1": {
|
|
type: "copper",
|
|
apitype:"t568b",
|
|
connectedTo: "1A-1",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "pass"
|
|
},
|
|
"Room1-TO-2": {
|
|
type: "copper",
|
|
apitype:"t568b",
|
|
connectedTo: "1A-2",
|
|
wiremapstatus: "open",
|
|
performancestatus: "pass"
|
|
},
|
|
"Room2-TO-1": {
|
|
type: "copper",
|
|
apitype:"t568b",
|
|
connectedTo: "1A-3",
|
|
wiremapstatus: "short",
|
|
performancestatus: "pass"
|
|
},
|
|
"Room2-TO-2": {
|
|
type: "copper",
|
|
apitype:"t568b",
|
|
connectedTo: "1A-4",
|
|
wiremapstatus: "cross",
|
|
performancestatus: "pass"
|
|
},
|
|
"Room3-TO-1": {
|
|
type: "copper",
|
|
apitype:"t568b",
|
|
connectedTo: "1A-5",
|
|
wiremapstatus: "reversed",
|
|
performancestatus: "pass"
|
|
},
|
|
"Room3-TO-2": {
|
|
type: "copper",
|
|
apitype:"t568b",
|
|
connectedTo: "1A-6",
|
|
wiremapstatus: "miswire",
|
|
performancestatus: "pass"
|
|
},
|
|
"Room4-CAM": {
|
|
type: "copper",
|
|
apitype:"t568b",
|
|
connectedTo: "1A-7",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "return-loss-fail"
|
|
},
|
|
"1B-1": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "1B-5",
|
|
fiberstatus: "sm-pass"
|
|
},
|
|
"1B-2": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "1B-6",
|
|
fiberstatus: "connector-fail-start"
|
|
},
|
|
"1B-3": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "1B-7",
|
|
fiberstatus: "splice-fail"
|
|
},
|
|
"1B-4": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "1B-8",
|
|
fiberstatus: "bend"
|
|
}
|
|
},
|
|
Industry: {
|
|
"Device1-1": {
|
|
type: "copper",
|
|
apitype:"workshop-m12",
|
|
connectedTo: "Cabinet-A1",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "pass"
|
|
},
|
|
"Device1-2": {
|
|
type: "copper",
|
|
apitype:"workshop-m12",
|
|
connectedTo: "Cabinet-A2",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "pass"
|
|
},
|
|
"Device2-1": {
|
|
type: "copper",
|
|
apitype:"workshop-m12",
|
|
connectedTo: "Cabinet-A3",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "pass"
|
|
},
|
|
"Device2-2": {
|
|
type: "copper",
|
|
apitype:"workshop-m12",
|
|
connectedTo: "Cabinet-A4",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "pass"
|
|
},
|
|
"Device3-1": {
|
|
type: "copper",
|
|
apitype:"workshop-2p",
|
|
connectedTo: "Cabinet-R1",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "pass"
|
|
},
|
|
"Device3-2": {
|
|
type: "copper",
|
|
apitype:"workshop-2p",
|
|
connectedTo: "Cabinet-R2",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "pass"
|
|
},
|
|
"Device4-1": {
|
|
type: "copper",
|
|
apitype:"workshop-2p",
|
|
connectedTo: "Cabinet-R3",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "pass"
|
|
},
|
|
"Device4-2": {
|
|
type: "copper",
|
|
apitype:"workshop-2p",
|
|
connectedTo: "Cabinet-R4",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "pass"
|
|
}
|
|
},
|
|
DataCenter: {
|
|
"CA-1A-1-port1": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "CB-1A-1-port1",
|
|
fiberstatus: "mm-pass"
|
|
},
|
|
"CA-1A-1-port2": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "CB-1A-1-port2",
|
|
fiberstatus: "mm-pass"
|
|
},
|
|
"CA-1A-1-port3": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "CB-1A-1-port3",
|
|
fiberstatus: "mm-pass"
|
|
},
|
|
"CA-1A-1-port4": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "CB-1A-1-port4",
|
|
fiberstatus: "mm-pass"
|
|
},
|
|
"CA-1A-1-port5": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "CB-1A-1-port6",
|
|
fiberstatus: "mm-pass"
|
|
},
|
|
"CA-1A-1-port6": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "CB-1A-1-port6",
|
|
fiberstatus: "mm-pass"
|
|
},
|
|
},
|
|
SkillCompetition: {
|
|
"Room1-1": {
|
|
type: "copper",
|
|
connectedTo: "1A-1",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "pass"
|
|
},
|
|
"Room1-2": {
|
|
type: "copper",
|
|
connectedTo: "1A-2",
|
|
wiremapstatus: "open",
|
|
performancestatus: "pass"
|
|
},
|
|
"Room2-1": {
|
|
type: "copper",
|
|
connectedTo: "1A-11",
|
|
wiremapstatus: "short",
|
|
performancestatus: "pass"
|
|
},
|
|
"Room2-2": {
|
|
type: "copper",
|
|
connectedTo: "1A-12",
|
|
wiremapstatus: "cross",
|
|
performancestatus: "pass"
|
|
},
|
|
"Room3-1": {
|
|
type: "copper",
|
|
connectedTo: "1B-5",
|
|
wiremapstatus: "reversed",
|
|
performancestatus: "pass"
|
|
},
|
|
"Room3-2": {
|
|
type: "copper",
|
|
connectedTo: "1B-6",
|
|
wiremapstatus: "miswire",
|
|
performancestatus: "pass"
|
|
},
|
|
},
|
|
WorldSkill: {
|
|
"Room1-TO-1": {
|
|
type: "copper",
|
|
apitype:"t568b",
|
|
connectedTo: "1F-RackA-1A-1",
|
|
wiremapstatus: "short2",
|
|
performancestatus: "pass"
|
|
},
|
|
"Room1-TO-2": {
|
|
type: "copper",
|
|
apitype:"t568b",
|
|
connectedTo: "1F-RackA-1A-2",
|
|
wiremapstatus: "short",
|
|
performancestatus: "pass"
|
|
},
|
|
"Room3-TO-1": {
|
|
type: "copper",
|
|
apitype:"t568b",
|
|
connectedTo: "1F-RackA-1A-5",
|
|
wiremapstatus: "cross",
|
|
performancestatus: "next-fail"
|
|
},
|
|
"Room3-TO-2": {
|
|
type: "copper",
|
|
apitype:"t568b",
|
|
connectedTo: "1F-RackA-1A-6",
|
|
wiremapstatus: "reversed",
|
|
performancestatus: "pass"
|
|
},
|
|
"Room4-CAM": {
|
|
type: "copper",
|
|
apitype:"t568b",
|
|
connectedTo: "1F-RackA-1A-12",
|
|
wiremapstatus: "pass",
|
|
performancestatus: "mptl-nextfail"
|
|
},
|
|
// OLTS
|
|
"2F-RackA-1A-1": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "1F-RackA-1B-1",
|
|
fiberstatus: "sm-fail"
|
|
},
|
|
"2F-RackA-1A-2": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "1F-RackA-1B-2",
|
|
fiberstatus: "sm-pass"
|
|
},
|
|
"2F-RackA-1A-3": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "1F-RackA-1B-3",
|
|
fiberstatus: "sm-pass"
|
|
},
|
|
"2F-RackA-1A-4": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "1F-RackA-1B-4",
|
|
fiberstatus: "sm-pass"
|
|
},
|
|
"2F-RackA-1A-5": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "1F-RackA-1B-6",
|
|
fiberstatus: "sm-pass"
|
|
},
|
|
"2F-RackA-1A-6": {
|
|
type: "fiber",
|
|
apitype:"olts",
|
|
connectedTo: "1F-RackA-1B-5",
|
|
fiberstatus: "sm-pass"
|
|
},
|
|
// OTDR
|
|
"1F-RackA-1C-1": {
|
|
type: "fiber",
|
|
apitype:"otdr",
|
|
connectedTo: "x",
|
|
fiberstatus: "sm-pass"
|
|
},
|
|
"1F-RackA-1C-2": {
|
|
type: "fiber",
|
|
apitype:"otdr",
|
|
connectedTo: "x",
|
|
fiberstatus: "connector-fail-start"
|
|
},
|
|
"1F-RackA-1C-3": {
|
|
type: "fiber",
|
|
apitype:"otdr",
|
|
connectedTo: "x",
|
|
fiberstatus: "splice-fail"
|
|
},
|
|
"1F-RackA-1C-4": {
|
|
type: "fiber",
|
|
apitype:"otdr",
|
|
connectedTo: "x",
|
|
fiberstatus: "bend"
|
|
},
|
|
// 车间
|
|
"RMA-1": {
|
|
type: "copper",
|
|
apitype:"workshop-m12",
|
|
connectedTo: "PLC-Rack-1A-1",
|
|
wiremapstatus: "pass-2pair",
|
|
performancestatus: "workshop-m12-pass-30m"
|
|
},
|
|
"RMA-2": {
|
|
type: "copper",
|
|
apitype:"workshop-m12",
|
|
connectedTo: "PLC-Rack-1A-2",
|
|
wiremapstatus: "sopen",
|
|
performancestatus: "workshop-m12-pass-30m"
|
|
},
|
|
"PLC-Rack-1B-1": {
|
|
type: "copper",
|
|
apitype:"workshop-2p",
|
|
connectedTo: "1F-RackA-1A-1",
|
|
wiremapstatus: "pass-2pair",
|
|
performancestatus: "workshop-2p-pass-80m"
|
|
},
|
|
"PLC-Rack-1B-2": {
|
|
type: "copper",
|
|
apitype:"workshop-2p",
|
|
connectedTo: "1F-RackA-1A-2",
|
|
wiremapstatus: "pass-2pair",
|
|
performancestatus: "workshop-2p-pass-80m"
|
|
},
|
|
},
|
|
CopperAnalyzer:{
|
|
|
|
},
|
|
};
|
|
|
|
// 初始化连接映射数据
|
|
let connectionMap;
|
|
const connectionMapPath = path.join(__dirname, 'connection_maps', 'connectionMap.json');
|
|
|
|
try {
|
|
if (fs.existsSync(connectionMapPath)) {
|
|
const data = fs.readFileSync(connectionMapPath, 'utf8');
|
|
connectionMap = JSON.parse(data);
|
|
//console.log('从connectionMap.json加载连接映射数据');
|
|
} else {
|
|
connectionMap = JSON.parse(JSON.stringify(initialConnectionMap));
|
|
//console.log('使用默认初始连接映射数据');
|
|
}
|
|
} catch (err) {
|
|
console.error('加载连接映射数据时出错:', err);
|
|
connectionMap = JSON.parse(JSON.stringify(initialConnectionMap));
|
|
}
|
|
|
|
// 中间件
|
|
app.use(cors());
|
|
app.use(express.json());
|
|
|
|
// 获取连接映射的API
|
|
app.get('/api/connectionMap', (req, res) => {
|
|
const scene = req.query.scene;
|
|
|
|
if (!scene) {
|
|
//console.log('[ERROR] 获取连接映射失败: 缺少scene参数');
|
|
return res.status(400).json({ error: '缺少scene参数' });
|
|
}
|
|
|
|
if (!connectionMap[scene]) {
|
|
//console.log(`[ERROR] 获取连接映射失败: 无效的场景 (${scene})`);
|
|
return res.status(404).json({ error: '无效的场景' });
|
|
}
|
|
|
|
// 合并pass场景和当前场景的数据
|
|
const mergedConnectionMap = Object.assign({}, connectionMap['pass'], connectionMap[scene]);
|
|
//console.log(`[GET] 获取连接映射 - 场景: ${scene}`);
|
|
res.json(mergedConnectionMap);
|
|
});
|
|
|
|
|
|
|
|
// 更新整个连接映射的API
|
|
app.post('/api/connectionMap', (req, res) => {
|
|
const scene = Object.keys(req.body)[0];
|
|
let newConnectionMap = req.body[scene];
|
|
|
|
// 验证数据格式
|
|
if (!scene || !newConnectionMap || typeof newConnectionMap !== 'object') {
|
|
//console.log('[ERROR] 更新连接映射失败: 无效的数据格式');
|
|
return res.status(400).json({ error: '无效的数据格式' });
|
|
}
|
|
|
|
// 禁止修改pass场景
|
|
if (scene === 'pass') {
|
|
//console.log('[ERROR] 更新连接映射失败: 不允许修改pass场景');
|
|
return res.status(403).json({ error: '不允许修改pass场景' });
|
|
}
|
|
|
|
// 移除与pass场景重复的数据
|
|
const passKeys = Object.keys(connectionMap.pass);
|
|
newConnectionMap = Object.fromEntries(
|
|
Object.entries(newConnectionMap).filter(([key]) => !passKeys.includes(key))
|
|
);
|
|
|
|
// 更新连接映射
|
|
const oldConnectionMap = JSON.stringify(connectionMap[scene]);
|
|
connectionMap[scene] = newConnectionMap;
|
|
//console.log(`[UPDATE] 连接映射已更新 - 场景: ${scene}`);
|
|
//console.log(`[DIFF] 旧数据: ${oldConnectionMap}`);
|
|
//console.log(`[DIFF] 新数据: ${JSON.stringify(connectionMap[scene])}`);
|
|
|
|
res.json({ success: true, message: '连接映射已更新' });
|
|
// 持久化存储连接映射
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const dirPath = path.join(__dirname, 'connection_maps');
|
|
if (!fs.existsSync(dirPath)) {
|
|
fs.mkdirSync(dirPath);
|
|
}
|
|
fs.writeFileSync(path.join(dirPath, `connectionMap.json`), JSON.stringify(connectionMap, null, 2));
|
|
//console.log(`[SAVE] 连接映射已持久化存储`);
|
|
});
|
|
|
|
// 初始化连接映射的API
|
|
app.post('/api/initConnectionMap', (req, res) => {
|
|
connectionMap = JSON.parse(JSON.stringify(initialConnectionMap));
|
|
|
|
res.json({ success: true, message: '连接映射已恢复默认状态' });
|
|
// 持久化存储连接映射
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const dirPath = path.join(__dirname, 'connection_maps');
|
|
if (!fs.existsSync(dirPath)) {
|
|
fs.mkdirSync(dirPath);
|
|
}
|
|
fs.writeFileSync(path.join(dirPath, `connectionMap.json`), JSON.stringify(initialConnectionMap, null, 2));
|
|
//console.log(`[SAVE] 连接映射已初始化并`);
|
|
|
|
});
|
|
// -----------------**************--------连接映射-----------***************--------------
|
|
|
|
|
|
// 比赛状态存储
|
|
let competitionStatus = {
|
|
UUID: null,
|
|
isRunning: false,
|
|
startTime: null,
|
|
statisticsData: {}
|
|
};
|
|
|
|
// 比赛开始API
|
|
app.post('/api/competition/start', (req, res) => {
|
|
if (competitionStatus.isRunning) {
|
|
return res.status(400).json({ error: '比赛已在进行中' });
|
|
}
|
|
|
|
competitionStatus = {
|
|
UUID: require('crypto').randomUUID(),
|
|
isRunning: true,
|
|
startTime: new Date(new Date().getTime() + (8 * 60 * 60 * 1000)).toISOString(),
|
|
statisticsData: {}
|
|
};
|
|
|
|
// 创建比赛临时数据目录
|
|
const tmpDirPath = path.join(__dirname, 'competition_tmp', competitionStatus.UUID);
|
|
if (!fs.existsSync(path.join(__dirname, 'competition_tmp'))) {
|
|
fs.mkdirSync(path.join(__dirname, 'competition_tmp'));
|
|
}
|
|
if (!fs.existsSync(tmpDirPath)) {
|
|
fs.mkdirSync(tmpDirPath);
|
|
}
|
|
|
|
// 启动定时备份
|
|
const backupInterval = setInterval(() => {
|
|
if (!competitionStatus.isRunning) {
|
|
clearInterval(backupInterval);
|
|
return;
|
|
}
|
|
|
|
const timestamp = new Date(new Date().getTime() + (8 * 60 * 60 * 1000)).toISOString().replace(/[:.]/g, '-');
|
|
const backupData = {
|
|
connectionMap,
|
|
competitionStatus
|
|
};
|
|
|
|
// 编码数据
|
|
const dataStr = JSON.stringify(backupData, null, 2);
|
|
const encodedData = encodeURIComponent(dataStr).split('').reverse().join('');
|
|
const finalData = `EST_ENCODED_DATA:${encodedData}`;
|
|
|
|
fs.writeFileSync(
|
|
path.join(tmpDirPath, `backup_${timestamp}.est`),
|
|
finalData
|
|
);
|
|
}, 60000); // 每分钟备份一次
|
|
|
|
//console.log(`[COMPETITION] 比赛开始 - UUID: ${competitionStatus.UUID}`);
|
|
res.json({ success: true, UUID: competitionStatus.UUID });
|
|
});
|
|
|
|
// 比赛结束API
|
|
app.post('/api/competition/end', (req, res) => {
|
|
if (!competitionStatus.isRunning) {
|
|
return res.status(400).json({ error: '没有进行中的比赛' });
|
|
}
|
|
|
|
// 持久化存储比赛数据
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const dateStr = new Date().toISOString().split('T')[0];
|
|
const fileName = `${dateStr}_${competitionStatus.UUID}.est`;
|
|
const dirPath = path.join(__dirname, 'competition_data');
|
|
const saveData = {
|
|
connectionMap,
|
|
competitionStatus,
|
|
};
|
|
if (!fs.existsSync(dirPath)) {
|
|
fs.mkdirSync(dirPath);
|
|
}
|
|
|
|
// 使用编码和简单的字符替换来编码数据
|
|
const dataStr = JSON.stringify(saveData, null, 2);
|
|
const encodedData = encodeURIComponent(dataStr).split('').reverse().join('');
|
|
// 添加自定义头部标识,表明这是编码后的文件
|
|
const finalData = `EST_ENCODED_DATA:${encodedData}`;
|
|
|
|
fs.writeFileSync(
|
|
path.join(dirPath, fileName),
|
|
finalData
|
|
);
|
|
|
|
competitionStatus.isRunning = false;
|
|
//console.log(`[COMPETITION] 比赛结束 - 数据已保存到 ${fileName}`);
|
|
res.json({
|
|
uuid: competitionStatus.UUID,
|
|
success: true,
|
|
data: saveData,
|
|
});
|
|
competitionStatus.statisticsData = {};
|
|
});
|
|
|
|
// 比赛状态查询API
|
|
app.get('/api/competition/status', (req, res) => {
|
|
const response = {
|
|
isRunning: competitionStatus.isRunning
|
|
|
|
};
|
|
|
|
if (competitionStatus.isRunning) {
|
|
response.UUID = competitionStatus.UUID;
|
|
response.startTime = competitionStatus.startTime;
|
|
|
|
}
|
|
|
|
res.json(response);
|
|
});
|
|
|
|
// 比赛数据上传API
|
|
app.post('/api/competition/data', (req, res) => {
|
|
const { UUID, fingerprint, timestamp, data } = req.body;
|
|
|
|
// 验证比赛状态
|
|
if (!competitionStatus.isRunning) {
|
|
return res.status(400).json({ error: '没有进行中的比赛' });
|
|
}
|
|
|
|
// 验证UUID
|
|
if (!UUID || UUID !== competitionStatus.UUID) {
|
|
return res.status(400).json({ error: '无效的UUID或UUID不匹配当前比赛' });
|
|
}
|
|
|
|
// 验证数据完整性
|
|
if (!data || !fingerprint) {
|
|
return res.status(400).json({ error: '缺少必要数据' });
|
|
}
|
|
|
|
try {
|
|
// 使用指纹作为键,存储最新的数据
|
|
competitionStatus.statisticsData[fingerprint] = {
|
|
...data,
|
|
lastUpdate: timestamp
|
|
};
|
|
|
|
// 比赛数据查询API (根据UUID和fingerprint)
|
|
app.get('/api/competition/data', (req, res) => {
|
|
const { UUID, fingerprint } = req.query;
|
|
|
|
// 验证参数
|
|
if (!UUID || !fingerprint) {
|
|
//console.log('[ERROR] 查询比赛数据失败: 缺少UUID或fingerprint参数');
|
|
return res.status(400).json({ error: '缺少UUID或fingerprint参数' });
|
|
}
|
|
|
|
// 检查比赛状态
|
|
if (!competitionStatus.isRunning || competitionStatus.UUID !== UUID) {
|
|
//console.log('[ERROR] 查询比赛数据失败: 无效的比赛UUID或比赛未进行');
|
|
return res.status(404).json({ error: '无效的比赛UUID或比赛未进行' });
|
|
}
|
|
|
|
// 获取指定指纹的数据
|
|
const data = competitionStatus.statisticsData[fingerprint];
|
|
if (!data) {
|
|
//console.log(`[WARN] 未找到指纹 ${fingerprint} 的比赛数据`);
|
|
return res.status(404).json({ error: '未找到指定指纹的比赛数据' });
|
|
}
|
|
|
|
//console.log(`[GET] 获取比赛数据 - UUID: ${UUID}, 指纹: ${fingerprint}`);
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
projects: data.projects || []
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
res.json({ success: true });
|
|
} catch (error) {
|
|
console.error('保存比赛数据失败:', error);
|
|
res.status(500).json({ error: '服务器内部错误' });
|
|
}
|
|
});
|
|
|
|
// 比赛数据查询API
|
|
app.get('/api/competition/data', (req, res) => {
|
|
res.json(competitionStatus.statisticsData);
|
|
});
|
|
|
|
// 启动服务器
|
|
if (require.main === module) {
|
|
app.listen(PORT, () => {
|
|
console.log(`[SERVER] 服务器运行在 http://localhost:${PORT}`);
|
|
console.log('\x1b[31m%s\x1b[0m', '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
|
|
console.log('\x1b[31m\x1b[1m%s\x1b[0m', '!! 比赛期间请勿关闭此窗口 !!');
|
|
console.log('\x1b[31m\x1b[1m%s\x1b[0m', '!! 比赛期间避免键盘操作 如Ctrl+C等 !!');
|
|
console.log('\x1b[31m\x1b[1m%s\x1b[0m', '!! 关闭后比赛数据将会丢失 !!');
|
|
console.log('\x1b[31m%s\x1b[0m', '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
|
|
});
|
|
}
|
|
|
|
module.exports = app; |