import express from 'express'; import { promises as fs } from 'fs'; import path from 'path'; const app = express(); const PORT = process.env.PORT || 3001; const SCORES_FILE = path.join(__dirname, 'scores.json'); // 分数数据类型 interface ScoreEntry { name: string; time: number; region: string; store: string; timestamp: number; } // 中间件 app.use(express.json()); app.use(express.static('dist')); // 服务静态文件 // 初始化分数文件 async function initScoresFile() { try { await fs.access(SCORES_FILE); } catch { // 文件不存在,创建空数组 await fs.writeFile(SCORES_FILE, JSON.stringify([])); } } // 读取分数数据 async function readScores(): Promise { try { const data = await fs.readFile(SCORES_FILE, 'utf-8'); return JSON.parse(data); } catch { return []; } } // 写入分数数据 async function writeScores(scores: ScoreEntry[]): Promise { await fs.writeFile(SCORES_FILE, JSON.stringify(scores, null, 2)); } // POST /api/score - 提交分数 app.post('/api/score', async (req, res) => { try { const { region, store, name, time } = req.body; // 验证必要字段 if (!region || !store || !name || typeof time !== 'number') { return res.status(400).json({ error: '缺少必要字段: region, store, name, time' }); } // 验证时间值 if (time <= 0) { return res.status(400).json({ error: '时间必须大于0' }); } const scores = await readScores(); // 创建新的分数记录 const newScore: ScoreEntry = { name: String(name).trim(), time: Number(time), region: String(region).trim(), store: String(store).trim(), timestamp: Date.now() }; // 检查是否已有相同用户的记录 const existingIndex = scores.findIndex( score => score.name === newScore.name && score.region === newScore.region && score.store === newScore.store ); if (existingIndex !== -1) { // 如果新时间更好(更短),则更新记录 if (newScore.time < scores[existingIndex].time) { scores[existingIndex] = newScore; } } else { // 添加新记录 scores.push(newScore); } await writeScores(scores); res.json({ success: true, message: '分数提交成功', score: newScore }); } catch (error) { console.error('提交分数时出错:', error); res.status(500).json({ error: '服务器内部错误' }); } }); // GET /api/score - 获取排行榜(前8名) app.get('/api/score', async (req, res) => { try { const scores = await readScores(); // 按时间排序(时间短的在前)并取前8名 const leaderBoard = scores .sort((a, b) => a.time - b.time) .slice(0, 8) .map(score => ({ name: score.name, time: score.time, region: score.region, store: score.store })); res.json(leaderBoard); } catch (error) { console.error('获取排行榜时出错:', error); res.status(500).json({ error: '服务器内部错误' }); } }); // GET /api/score/all - 获取所有分数(可选,用于管理) app.get('/api/score/all', async (req, res) => { try { const scores = await readScores(); res.json(scores.sort((a, b) => a.time - b.time)); } catch (error) { console.error('获取所有分数时出错:', error); res.status(500).json({ error: '服务器内部错误' }); } }); // 健康检查端点 app.get('/api/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString() }); }); // 处理 SPA 路由 - 将所有未匹配的路由返回 index.html app.get('/*', (req, res) => { try { res.sendFile(path.join(__dirname, 'dist', 'index.html')); } catch (error) { res.status(404).send('Page not found'); } }); // 启动服务器 async function startServer() { await initScoresFile(); app.listen(PORT, () => { console.log(`🚀 服务器运行在 http://localhost:${PORT}`); console.log(`📊 API 端点:`); console.log(` POST /api/score - 提交分数`); console.log(` GET /api/score - 获取排行榜`); console.log(` GET /api/health - 健康检查`); }); } startServer().catch(console.error);