const http = require('http'); const fs = require('fs'); const path = require('path'); const PORT = process.env.PORT || 3001; const DATA_DIR = path.join(__dirname, '../data'); const DATA_FILE = path.join(DATA_DIR, 'quiz_stats.json'); const QUESTIONS_FILE = path.join(__dirname, '../questions.json'); // 保证数据目录存在 if (!fs.existsSync(DATA_DIR)) { fs.mkdirSync(DATA_DIR, { recursive: true }); } // 内存中的状态缓存 let state = { weights: {}, history: [], totalAnswered: 0 }; // 初始化状态 function initState() { if (fs.existsSync(DATA_FILE)) { try { const content = fs.readFileSync(DATA_FILE, 'utf-8'); state = JSON.parse(content); console.log(`[Init] 已从文件载入全局数据,累计答题数: ${state.totalAnswered}`); return; } catch (e) { console.error('[Init] 读取已有数据文件失败,将重新初始化:', e); } } // 初始化默认权重 let defaultWeights = {}; if (fs.existsSync(QUESTIONS_FILE)) { try { const qData = JSON.parse(fs.readFileSync(QUESTIONS_FILE, 'utf-8')); qData.forEach(q => { defaultWeights[q.id] = 1; }); } catch (e) { console.error('[Init] 读取 questions.json 失败:', e); } } state = { weights: defaultWeights, history: [], totalAnswered: 0 }; saveState(); } // 异步持久化保存 function saveState() { const tmpFile = `${DATA_FILE}.tmp`; fs.writeFile(tmpFile, JSON.stringify(state, null, 2), 'utf-8', (err) => { if (err) { console.error('[Save] 保存临时数据文件失败:', err); return; } fs.rename(tmpFile, DATA_FILE, (renameErr) => { if (renameErr) { console.error('[Save] 原子重命名数据文件失败:', renameErr); } }); }); } initState(); const server = http.createServer((req, res) => { // 设置 CORS 标头 res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; } const url = new URL(req.url, `http://${req.headers.host}`); const pathname = url.pathname.replace(/^\/api/, ''); // 统一兼容 /api 前缀与无前缀 if (req.method === 'GET' && (pathname === '/stats' || pathname === '/stats/')) { res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' }); res.end(JSON.stringify(state)); return; } if (req.method === 'POST' && (pathname === '/answer' || pathname === '/answer/')) { let body = ''; req.on('data', chunk => { body += chunk; }); req.on('end', () => { try { const payload = JSON.parse(body || '{}'); const questionId = Number(payload.questionId); const isCorrect = Boolean(payload.isCorrect); if (!questionId) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Missing questionId' })); return; } // 更新历史记录(保留最近 100 条) state.history.push(isCorrect); if (state.history.length > 100) { state.history = state.history.slice(-100); } // 更新全局总数 state.totalAnswered = (state.totalAnswered || 0) + 1; // 更新题目权重 const currentWeight = state.weights[questionId] || 1; if (isCorrect) { state.weights[questionId] = Math.max(1, currentWeight - 1); } else { state.weights[questionId] = currentWeight + 3; // 答错大幅增加权重 } saveState(); res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' }); res.end(JSON.stringify({ success: true, weights: state.weights, history: state.history, totalAnswered: state.totalAnswered })); } catch (err) { console.error('[Error] 处理 /api/answer 异常:', err); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Internal Server Error' })); } }); return; } if (req.method === 'POST' && (pathname === '/reset' || pathname === '/reset/')) { initState(); res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' }); res.end(JSON.stringify({ success: true, message: 'Stats reset' })); return; } res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Not Found' })); }); server.listen(PORT, '0.0.0.0', () => { console.log(`[Server] 全局刷题服务已启动,监听端口: ${PORT}`); });