Files
draw/server/index.js
2025-09-18 23:34:55 +08:00

1062 lines
30 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import express from 'express';
import cors from 'cors';
import sqlite3 from 'sqlite3';
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
import { createServer } from 'http';
import { Server as SocketIOServer } from 'socket.io';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const app = express();
const PORT = process.env.PORT || 3001;
const server = createServer(app);
const io = new SocketIOServer(server, {
cors: {
origin: function(origin, callback) {
callback(null, true);
},
credentials: true,
methods: ['GET', 'POST']
}
});
// 数据库文件路径
const DB_PATH = path.join(__dirname, 'lottery.db');
// 中间件
app.use(cors({
origin: function(origin, callback) {
// 允许所有来源包括localhost和IP地址访问
callback(null, true);
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
// JSON解析中间件添加错误处理
app.use(express.json({ limit: '10mb' }));
app.use((err, req, res, next) => {
if (err instanceof SyntaxError && err.status === 400 && 'body' in err) {
console.error('JSON解析错误:', err.message);
return res.status(400).json({
success: false,
error: 'Invalid JSON format',
message: '请求数据格式错误'
});
}
next();
});
app.use(express.static(path.join(__dirname, 'dist')));
// 添加预检请求处理
app.options('*', cors());
// 请求日志中间件
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} ${req.method} ${req.path}`);
next();
});
// 数据库连接
let db;
// 初始化数据库
function initDatabase() {
return new Promise((resolve, reject) => {
db = new sqlite3.Database(DB_PATH, (err) => {
if (err) {
// 数据库连接失败
reject(err);
return;
}
// SQLite数据库连接成功
// 创建表结构
createTables()
.then(() => {
// 数据库表创建完成
return initDefaultData();
})
.then(() => {
// 默认数据初始化完成
resolve();
})
.catch(reject);
});
});
}
// 创建表结构
function createTables() {
return new Promise((resolve, reject) => {
const tables = [
`CREATE TABLE IF NOT EXISTS system_config (
id TEXT PRIMARY KEY,
admin_password TEXT NOT NULL,
login_password TEXT NOT NULL,
max_draw_times INTEGER NOT NULL,
background_config TEXT,
hide_positions TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS prizes (
id TEXT PRIMARY KEY,
prize_name TEXT NOT NULL,
prize_level INTEGER NOT NULL,
total_quantity INTEGER NOT NULL,
remaining_quantity INTEGER NOT NULL,
probability REAL NOT NULL,
is_active BOOLEAN NOT NULL,
created_at TEXT NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS students (
student_id TEXT PRIMARY KEY,
draw_count INTEGER NOT NULL DEFAULT 0,
first_draw_at TEXT,
last_draw_at TEXT
)`,
`CREATE TABLE IF NOT EXISTS records (
id TEXT PRIMARY KEY,
student_id TEXT NOT NULL,
prize_id TEXT NOT NULL,
prize_name TEXT NOT NULL,
draw_time TEXT NOT NULL,
FOREIGN KEY (student_id) REFERENCES students (student_id),
FOREIGN KEY (prize_id) REFERENCES prizes (id)
)`
];
let completed = 0;
tables.forEach((sql, index) => {
db.run(sql, (err) => {
if (err) {
// 创建表失败
reject(err);
return;
}
completed++;
if (completed === tables.length) {
resolve();
}
});
});
});
}
// 初始化默认数据
function initDefaultData() {
return new Promise((resolve, reject) => {
// 检查是否已有系统配置
db.get('SELECT * FROM system_config LIMIT 1', (err, row) => {
if (err) {
reject(err);
return;
}
if (!row) {
// 插入默认系统配置
const defaultConfig = {
id: generateId(),
admin_password: '123456',
login_password: '123456',
max_draw_times: 1,
background_config: '{}',
hide_positions: '3,4,5,6',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString()
};
db.run(
`INSERT INTO system_config (id, admin_password, login_password, max_draw_times, background_config, hide_positions, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[defaultConfig.id, defaultConfig.admin_password, defaultConfig.login_password,
defaultConfig.max_draw_times, defaultConfig.background_config, defaultConfig.hide_positions,
defaultConfig.created_at, defaultConfig.updated_at],
(err) => {
if (err) {
reject(err);
return;
}
initDefaultPrizes().then(resolve).catch(reject);
}
);
} else {
initDefaultPrizes().then(resolve).catch(reject);
}
});
});
}
// 初始化默认奖项
function initDefaultPrizes() {
return new Promise((resolve, reject) => {
db.get('SELECT COUNT(*) as count FROM prizes', (err, row) => {
if (err) {
reject(err);
return;
}
if (row.count === 0) {
const defaultPrizes = [
{
id: generateId(),
prize_name: '一等奖-iPad',
prize_level: 1,
total_quantity: 1,
remaining_quantity: 1,
probability: 0.0010,
is_active: true,
created_at: new Date().toISOString()
},
{
id: generateId(),
prize_name: '二等奖-蓝牙耳机',
prize_level: 2,
total_quantity: 5,
remaining_quantity: 5,
probability: 0.0050,
is_active: true,
created_at: new Date().toISOString()
},
{
id: generateId(),
prize_name: '三等奖-保温杯',
prize_level: 3,
total_quantity: 20,
remaining_quantity: 20,
probability: 0.0200,
is_active: true,
created_at: new Date().toISOString()
},
{
id: generateId(),
prize_name: '谢谢参与',
prize_level: 4,
total_quantity: 9974,
remaining_quantity: 9974,
probability: 0.9740,
is_active: true,
created_at: new Date().toISOString()
}
];
let completed = 0;
defaultPrizes.forEach(prize => {
db.run(
`INSERT INTO prizes (id, prize_name, prize_level, total_quantity, remaining_quantity, probability, is_active, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[prize.id, prize.prize_name, prize.prize_level, prize.total_quantity,
prize.remaining_quantity, prize.probability, prize.is_active, prize.created_at],
(err) => {
if (err) {
reject(err);
return;
}
completed++;
if (completed === defaultPrizes.length) {
resolve();
}
}
);
});
} else {
resolve();
}
});
});
}
// 生成唯一ID
function generateId() {
return Math.random().toString(36).substr(2, 9) + Date.now().toString(36);
}
// API路由
// 获取系统配置
app.get('/api/system-config', (req, res) => {
db.get('SELECT * FROM system_config LIMIT 1', (err, row) => {
if (err) {
console.error('获取系统配置失败:', err);
res.status(500).json({
success: false,
error: 'Database Error',
message: '获取系统配置失败'
});
return;
}
res.json({
success: true,
data: row || null
});
});
});
// 更新系统配置
app.put('/api/system-config', (req, res) => {
const { admin_password, login_password, max_draw_times, background_config, hide_positions } = req.body;
const updated_at = new Date().toISOString();
db.run(
`UPDATE system_config SET
admin_password = COALESCE(?, admin_password),
login_password = COALESCE(?, login_password),
max_draw_times = COALESCE(?, max_draw_times),
background_config = COALESCE(?, background_config),
hide_positions = COALESCE(?, hide_positions),
updated_at = ?
WHERE id = (SELECT id FROM system_config LIMIT 1)`,
[admin_password, login_password, max_draw_times, background_config, hide_positions, updated_at],
function(err) {
if (err) {
res.status(500).json({ error: err.message });
return;
}
res.json({ success: true, changes: this.changes });
// 广播系统配置更新
if (this.changes > 0) {
emitSystemConfigUpdate();
}
}
);
});
// 获取所有奖项
app.get('/api/prizes', (req, res) => {
db.all('SELECT * FROM prizes ORDER BY prize_level', (err, rows) => {
if (err) {
console.error('获取奖项列表失败:', err);
res.status(500).json({
success: false,
error: 'Database Error',
message: '获取奖项列表失败'
});
return;
}
res.json({
success: true,
data: rows || []
});
});
});
// 添加奖项
app.post('/api/prizes', (req, res) => {
const { prize_name, prize_level, total_quantity, remaining_quantity, probability, is_active } = req.body;
const id = generateId();
const created_at = new Date().toISOString();
db.run(
`INSERT INTO prizes (id, prize_name, prize_level, total_quantity, remaining_quantity, probability, is_active, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[id, prize_name, prize_level, total_quantity, remaining_quantity, probability, is_active, created_at],
function(err) {
if (err) {
res.status(500).json({ error: err.message });
return;
}
res.json({ success: true, id, lastID: this.lastID });
// 广播数据更新
emitDataUpdate();
}
);
});
// 更新奖项
app.put('/api/prizes/:id', (req, res) => {
const { id } = req.params;
const { prize_name, prize_level, total_quantity, remaining_quantity, probability, is_active } = req.body;
db.run(
`UPDATE prizes SET
prize_name = COALESCE(?, prize_name),
prize_level = COALESCE(?, prize_level),
total_quantity = COALESCE(?, total_quantity),
remaining_quantity = COALESCE(?, remaining_quantity),
probability = COALESCE(?, probability),
is_active = COALESCE(?, is_active)
WHERE id = ?`,
[prize_name, prize_level, total_quantity, remaining_quantity, probability, is_active, id],
function(err) {
if (err) {
res.status(500).json({ error: err.message });
return;
}
res.json({ success: true, changes: this.changes });
// 广播数据更新
emitDataUpdate();
}
);
});
// 删除奖项
app.delete('/api/prizes/:id', (req, res) => {
const { id } = req.params;
db.run('DELETE FROM prizes WHERE id = ?', [id], function(err) {
if (err) {
res.status(500).json({ error: err.message });
return;
}
res.json({ success: true, changes: this.changes });
// 广播数据更新
emitDataUpdate();
});
});
// 获取学生信息
app.get('/api/students/:studentId', (req, res) => {
const { studentId } = req.params;
db.get('SELECT * FROM students WHERE student_id = ?', [studentId], (err, row) => {
if (err) {
res.status(500).json({ error: err.message });
return;
}
res.json(row || null);
});
});
// 更新学生抽奖次数
app.put('/api/students/:studentId', (req, res) => {
const { studentId } = req.params;
const { draw_count } = req.body;
const now = new Date().toISOString();
// 先检查学生是否存在
db.get('SELECT * FROM students WHERE student_id = ?', [studentId], (err, row) => {
if (err) {
res.status(500).json({ error: err.message });
return;
}
if (row) {
// 更新现有学生
db.run(
'UPDATE students SET draw_count = ?, last_draw_at = ? WHERE student_id = ?',
[draw_count, now, studentId],
function(err) {
if (err) {
res.status(500).json({ error: err.message });
return;
}
res.json({ success: true, changes: this.changes });
// 广播学生数据更新
if (this.changes > 0) {
emitStudentUpdate(studentId);
}
}
);
} else {
// 创建新学生记录
db.run(
'INSERT INTO students (student_id, draw_count, first_draw_at, last_draw_at) VALUES (?, ?, ?, ?)',
[studentId, draw_count, now, now],
function(err) {
if (err) {
res.status(500).json({ error: err.message });
return;
}
res.json({ success: true, lastID: this.lastID });
// 广播新学生创建
emitStudentUpdate(studentId);
}
);
}
});
});
// 获取抽奖记录
app.get('/api/records', (req, res) => {
db.all('SELECT * FROM records ORDER BY draw_time DESC', (err, rows) => {
if (err) {
console.error('获取抽奖记录失败:', err);
res.status(500).json({
success: false,
error: 'Database Error',
message: '获取抽奖记录失败'
});
return;
}
res.json({
success: true,
data: rows || []
});
});
});
// 添加抽奖记录
app.post('/api/records', (req, res) => {
const { student_id, prize_id, prize_name } = req.body;
const id = generateId();
const draw_time = new Date().toISOString();
db.run(
'INSERT INTO records (id, student_id, prize_id, prize_name, draw_time) VALUES (?, ?, ?, ?, ?)',
[id, student_id, prize_id, prize_name, draw_time],
function(err) {
if (err) {
res.status(500).json({ error: err.message });
return;
}
res.json({ success: true, id, lastID: this.lastID });
// 使用优化的增量广播
emitNewRecord(id);
// 同时更新相关奖项数据
if (prize_id) {
emitPrizeUpdate(prize_id);
}
}
);
});
// 清空抽奖记录
app.delete('/api/records', (req, res) => {
db.run('DELETE FROM records', function(err) {
if (err) {
res.status(500).json({ error: err.message });
return;
}
// 同时重置学生抽奖次数
db.run('DELETE FROM students', function(err) {
if (err) {
res.status(500).json({ error: err.message });
return;
}
res.json({ success: true, changes: this.changes });
// 广播数据清空更新
emitDataClear();
});
});
});
// 重置系统数据
app.post('/api/reset', (req, res) => {
// 清空所有数据表
const tables = ['records', 'students'];
let completed = 0;
tables.forEach(table => {
db.run(`DELETE FROM ${table}`, function(err) {
if (err) {
res.status(500).json({ error: err.message });
return;
}
completed++;
if (completed === tables.length) {
// 重置奖项剩余数量
db.run(
'UPDATE prizes SET remaining_quantity = total_quantity',
function(err) {
if (err) {
res.status(500).json({ error: err.message });
return;
}
res.json({ success: true, message: '系统数据重置成功' });
// 广播系统重置
emitSystemReset();
}
);
}
});
});
});
// 强制重置数据库
app.post('/api/force-reset', (req, res) => {
// 重置所有表数据并恢复默认配置
const tables = ['records', 'students', 'prizes', 'system_config'];
let completed = 0;
tables.forEach(table => {
db.run(`DELETE FROM ${table}`, function(err) {
if (err) {
res.status(500).json({ error: err.message });
return;
}
completed++;
if (completed === tables.length) {
// 重新初始化默认数据
initDefaultData()
.then(() => {
res.json({ success: true, message: '数据库强制重置成功' });
// 广播强制重置
emitForceReset();
})
.catch(err => {
res.status(500).json({ error: err.message });
});
}
});
});
});
// 抽奖API - 核心功能
app.post('/api/lottery/draw', (req, res) => {
const { studentId } = req.body;
if (!studentId) {
res.status(400).json({
success: false,
message: '学号不能为空'
});
return;
}
console.log(`🎯 收到抽奖请求,学号: ${studentId}`);
// 获取系统配置
db.get('SELECT * FROM system_config LIMIT 1', (err, config) => {
if (err) {
// 获取系统配置失败
res.status(500).json({
success: false,
message: '系统配置获取失败'
});
return;
}
if (!config) {
res.status(500).json({
success: false,
message: '系统配置未找到'
});
return;
}
// 检查学生抽奖次数
db.get('SELECT * FROM students WHERE student_id = ?', [studentId], (err, student) => {
if (err) {
console.error('获取学生信息失败:', err);
res.status(500).json({
success: false,
message: '学生信息获取失败'
});
return;
}
const currentDrawCount = student ? student.draw_count : 0;
if (currentDrawCount >= config.max_draw_times) {
res.status(400).json({
success: false,
message: `已达到最大抽奖次数(${config.max_draw_times}次)`
});
return;
}
// 获取可用奖项
db.all('SELECT * FROM prizes WHERE is_active = 1 AND remaining_quantity > 0', (err, prizes) => {
if (err) {
console.error('获取奖项失败:', err);
res.status(500).json({
success: false,
message: '奖项获取失败'
});
return;
}
if (prizes.length === 0) {
res.status(400).json({
success: false,
message: '暂无可用奖项'
});
return;
}
// 加权随机抽奖
const totalWeight = prizes.reduce((sum, prize) => sum + prize.probability, 0);
const random = Math.random() * totalWeight;
let cumulativeWeight = 0;
let selectedPrize = null;
for (const prize of prizes) {
cumulativeWeight += prize.probability;
if (random <= cumulativeWeight) {
selectedPrize = prize;
break;
}
}
if (!selectedPrize) {
res.status(500).json({
success: false,
message: '抽奖算法异常'
});
return;
}
console.log(`🎉 抽中奖品: ${selectedPrize.prize_name}`);
// 更新奖项剩余数量
const newRemainingQuantity = selectedPrize.remaining_quantity - 1;
db.run(
'UPDATE prizes SET remaining_quantity = ? WHERE id = ?',
[newRemainingQuantity, selectedPrize.id],
function(err) {
if (err) {
console.error('更新奖项数量失败:', err);
res.status(500).json({
success: false,
message: '奖项更新失败'
});
return;
}
// 添加抽奖记录
const recordId = generateId();
const drawTime = new Date().toISOString();
db.run(
'INSERT INTO records (id, student_id, prize_id, prize_name, draw_time) VALUES (?, ?, ?, ?, ?)',
[recordId, studentId, selectedPrize.id, selectedPrize.prize_name, drawTime],
function(err) {
if (err) {
console.error('添加抽奖记录失败:', err);
// 回滚奖项数量
db.run('UPDATE prizes SET remaining_quantity = ? WHERE id = ?',
[selectedPrize.remaining_quantity, selectedPrize.id]);
res.status(500).json({
success: false,
message: '记录添加失败'
});
return;
}
// 更新学生抽奖次数
const newDrawCount = currentDrawCount + 1;
const now = new Date().toISOString();
if (student) {
// 更新现有学生
db.run(
'UPDATE students SET draw_count = ?, last_draw_at = ? WHERE student_id = ?',
[newDrawCount, now, studentId],
function(err) {
if (err) {
console.error('更新学生信息失败:', err);
}
// 返回抽奖结果
const result = {
success: true,
message: '抽奖成功',
data: {
prize: {
id: selectedPrize.id,
name: selectedPrize.prize_name,
level: selectedPrize.prize_level
},
remaining: newRemainingQuantity,
drawCount: newDrawCount,
maxDrawTimes: config.max_draw_times
}
};
console.log('✅ 抽奖完成,返回结果:', result);
res.json(result);
// 广播数据更新
emitNewRecord(recordId);
emitPrizeUpdate(selectedPrize.id);
emitStudentUpdate(studentId);
}
);
} else {
// 创建新学生记录
db.run(
'INSERT INTO students (student_id, draw_count, first_draw_at, last_draw_at) VALUES (?, ?, ?, ?)',
[studentId, newDrawCount, now, now],
function(err) {
if (err) {
console.error('创建学生记录失败:', err);
}
// 返回抽奖结果
const result = {
success: true,
message: '抽奖成功',
data: {
prize: {
id: selectedPrize.id,
name: selectedPrize.prize_name,
level: selectedPrize.prize_level
},
remaining: newRemainingQuantity,
drawCount: newDrawCount,
maxDrawTimes: config.max_draw_times
}
};
console.log('✅ 抽奖完成,返回结果:', result);
res.json(result);
// 广播数据更新
emitNewRecord(recordId);
emitPrizeUpdate(selectedPrize.id);
emitStudentUpdate(studentId);
}
);
}
}
);
}
);
});
});
});
});
// 模拟抽奖
app.post('/api/simulate', (req, res) => {
const { times = 500 } = req.body;
// 获取奖项列表进行模拟
db.all('SELECT * FROM prizes WHERE is_active = 1', (err, prizes) => {
if (err) {
res.status(500).json({ error: err.message });
return;
}
const results = {};
const totalProbability = prizes.reduce((sum, prize) => sum + prize.probability, 0);
// 模拟抽奖
for (let i = 0; i < times; i++) {
const random = Math.random() * totalProbability;
let cumulativeProbability = 0;
for (const prize of prizes) {
cumulativeProbability += prize.probability;
if (random <= cumulativeProbability) {
results[prize.prize_name] = (results[prize.prize_name] || 0) + 1;
break;
}
}
}
// 计算概率
const simulationResult = Object.keys(results).map(prizeName => ({
prizeName,
count: results[prizeName],
percentage: ((results[prizeName] / times) * 100).toFixed(2)
}));
res.json({
totalSimulations: times,
results: simulationResult,
summary: `模拟${times}次抽奖完成`
});
});
});
// 全局错误处理中间件
app.use((err, req, res, next) => {
console.error('服务器错误:', err);
// 数据库错误
if (err.code === 'SQLITE_ERROR' || err.code === 'SQLITE_BUSY') {
return res.status(500).json({
success: false,
error: 'Database Error',
message: '数据库操作失败,请稍后重试'
});
}
// 默认服务器错误
res.status(500).json({
success: false,
error: 'Internal Server Error',
message: '服务器内部错误'
});
});
// 404处理中间件
app.use('/api/*', (req, res) => {
res.status(404).json({
success: false,
error: 'API Not Found',
message: `API接口 ${req.path} 不存在`
});
});
// 处理前端路由
app.get('*', (req, res) => {
if (!req.path.startsWith('/api')) {
res.sendFile(path.join(__dirname, 'dist', 'index.html'));
}
});
// WebSocket连接处理
io.on('connection', (socket) => {
console.log('客户端连接:', socket.id);
// 客户端加入房间
socket.join('lottery_room');
// 处理断开连接
socket.on('disconnect', () => {
console.log('客户端断开连接:', socket.id);
});
// 处理客户端请求最新数据
socket.on('request_data_update', () => {
emitDataUpdate();
});
});
// 广播数据更新(全量)
function emitDataUpdate() {
// 开始广播数据更新
// 获取最新的奖项数据
db.all('SELECT * FROM prizes ORDER BY prize_level', (err, prizes) => {
if (!err && prizes) {
// 发送包含updatedPrizes字段的对象确保前端能正确解析
io.to('lottery_room').emit('prizes_updated', { updatedPrizes: prizes });
// 广播奖项更新
} else {
// 获取奖项数据失败
}
});
// 获取最新的抽奖记录
db.all('SELECT * FROM records ORDER BY draw_time DESC LIMIT 10', (err, records) => {
if (!err && records) {
io.to('lottery_room').emit('records_updated', records);
// 广播记录更新
} else {
// 获取记录数据失败
}
});
}
// 广播单个奖项更新(优化版)
function emitPrizeUpdate(prizeId) {
if (prizeId) {
db.get('SELECT * FROM prizes WHERE id = ?', [prizeId], (err, prize) => {
if (!err && prize) {
io.to('lottery_room').emit('prize_updated', { prize, action: 'update' });
// 广播单个奖项更新
}
});
} else {
// 如果没有指定ID发送全量数据
emitDataUpdate();
}
}
// 广播新增抽奖记录(优化版)
function emitNewRecord(recordId) {
if (recordId) {
db.get('SELECT * FROM records WHERE id = ?', [recordId], (err, record) => {
if (!err && record) {
io.to('lottery_room').emit('new_record', { record, action: 'add' });
// 广播新增抽奖记录
}
});
}
}
// 广播系统配置更新
function emitSystemConfigUpdate() {
db.get('SELECT * FROM system_config LIMIT 1', (err, config) => {
if (!err && config) {
io.to('lottery_room').emit('system_config_updated', config);
// 广播系统配置更新
}
});
}
// 广播学生数据更新
function emitStudentUpdate(studentId) {
db.get('SELECT * FROM students WHERE student_id = ?', [studentId], (err, student) => {
if (!err) {
io.to('lottery_room').emit('student_updated', { studentId, student });
// 广播学生数据更新
}
});
}
// 广播数据清空
function emitDataClear() {
io.to('lottery_room').emit('data_cleared', {
type: 'records_and_students',
timestamp: new Date().toISOString()
});
// 广播数据清空事件
// 同时发送最新的空数据
emitDataUpdate();
}
// 广播系统重置
function emitSystemReset() {
io.to('lottery_room').emit('system_reset', {
type: 'partial_reset',
timestamp: new Date().toISOString()
});
console.log('广播系统重置事件');
// 发送最新数据
emitDataUpdate();
}
// 广播强制重置
function emitForceReset() {
io.to('lottery_room').emit('force_reset', {
type: 'full_reset',
timestamp: new Date().toISOString()
});
// 广播强制重置事件
// 发送最新数据
emitDataUpdate();
emitSystemConfigUpdate();
}
// 启动服务器
initDatabase()
.then(() => {
server.listen(PORT, () => {
console.log(`服务器运行在端口 ${PORT}`);
console.log(`数据库文件位置: ${DB_PATH}`);
console.log('WebSocket服务已启动');
});
})
.catch(err => {
console.error('服务器启动失败:', err);
process.exit(1);
});
// 优雅关闭
process.on('SIGINT', () => {
console.log('\n正在关闭服务器...');
if (db) {
db.close((err) => {
if (err) {
console.error('关闭数据库连接失败:', err.message);
} else {
console.log('数据库连接已关闭');
}
process.exit(0);
});
} else {
process.exit(0);
}
});