diff --git a/backend/captcha.js b/backend/captcha.js deleted file mode 100644 index 98a65a1..0000000 --- a/backend/captcha.js +++ /dev/null @@ -1,40 +0,0 @@ -const crypto = require('crypto'); -const { getDb } = require('./db'); - -function generate() { - const db = getDb(); - db.prepare("DELETE FROM captchas WHERE datetime(created_at) < datetime('now','localtime','-5 minutes')").run(); - - const id = crypto.randomUUID(); - const ops = ['+', '-', '\u00d7']; - const op = ops[Math.floor(Math.random() * ops.length)]; - let a, b, answer; - - if (op === '\u00d7') { - a = Math.floor(Math.random() * 9) + 1; - b = Math.floor(Math.random() * 9) + 1; - } else { - a = Math.floor(Math.random() * 20) + 1; - b = Math.floor(Math.random() * 10) + 1; - if (op === '-' && a < b) [a, b] = [b, a]; - } - - if (op === '+') answer = a + b; - else if (op === '-') answer = a - b; - else answer = a * b; - - const question = `${a} ${op} ${b} = ?`; - db.prepare('INSERT INTO captchas(id, question, answer) VALUES (?,?,?)').run(id, question, String(answer)); - - return { id, question }; -} - -function verify(id, answer) { - const db = getDb(); - const row = db.prepare("SELECT * FROM captchas WHERE id = ? AND datetime(created_at) > datetime('now','localtime','-5 minutes')").get(id); - if (!row) return false; - db.prepare('DELETE FROM captchas WHERE id = ?').run(id); - return String(answer).trim() === row.answer; -} - -module.exports = { generate, verify }; diff --git a/backend/routes/complaints.js b/backend/routes/complaints.js deleted file mode 100644 index 4fc3350..0000000 --- a/backend/routes/complaints.js +++ /dev/null @@ -1,208 +0,0 @@ -const express = require('express'); -const { getDb } = require('../db'); -const { authenticate, requireRole, optionalAuth } = require('../middleware/auth'); - -const router = express.Router(); - -router.get('/', optionalAuth, (req, res) => { - const db = getDb(); - let query = `SELECT c.*, - p.display_name as planner_name, i.display_name as implementer_name, r.display_name as reviewer_name - FROM complaints c - LEFT JOIN users p ON c.assigned_planner_id = p.id - LEFT JOIN users i ON c.assigned_implementer_id = i.id - LEFT JOIN users r ON c.assigned_reviewer_id = r.id`; - let conditions = []; - let params = []; - - const { status, priority, category, search } = req.query; - - if (!req.user) { - conditions.push("c.status != 'pending'"); - } - - if (status) { - conditions.push('c.status = ?'); - params.push(status); - } - if (priority) { - conditions.push('c.priority = ?'); - params.push(priority); - } - if (category) { - conditions.push('c.category = ?'); - params.push(category); - } - if (search) { - conditions.push("(c.title LIKE ? OR c.description LIKE ? OR c.player_name LIKE ?)"); - const s = `%${search}%`; - params.push(s, s, s); - } - - if (req.user && req.user.role === 'planner') { - conditions.push('(c.assigned_planner_id = ? OR c.assigned_planner_id IS NULL)'); - params.push(req.user.id); - } - if (req.user && req.user.role === 'implementer') { - conditions.push('c.assigned_implementer_id = ?'); - params.push(req.user.id); - } - if (req.user && req.user.role === 'reviewer') { - conditions.push('c.assigned_reviewer_id = ?'); - params.push(req.user.id); - } - - if (conditions.length > 0) { - query += ' WHERE ' + conditions.join(' AND '); - } - query += ' ORDER BY c.updated_at DESC'; - - const complaints = db.prepare(query).all(...params); - res.json(complaints); -}); - -router.get('/stats', authenticate, (req, res) => { - const db = getDb(); - const total = db.prepare('SELECT COUNT(*) as count FROM complaints').get(); - const byStatus = db.prepare('SELECT status, COUNT(*) as count FROM complaints GROUP BY status').all(); - const byPriority = db.prepare('SELECT priority, COUNT(*) as count FROM complaints GROUP BY priority').all(); - const today = db.prepare("SELECT COUNT(*) as count FROM complaints WHERE date(created_at) = date('now','localtime')").get(); - - let myStats = {}; - if (['planner', 'implementer', 'reviewer'].includes(req.user.role)) { - const roleMap = { planner: 'assigned_planner_id', implementer: 'assigned_implementer_id', reviewer: 'assigned_reviewer_id' }; - const col = roleMap[req.user.role]; - myStats.total = db.prepare(`SELECT COUNT(*) as count FROM complaints WHERE ${col} = ?`).get(req.user.id).count; - myStats.pending = db.prepare(`SELECT COUNT(*) as count FROM complaints WHERE ${col} = ? AND status NOT IN ('resolved','closed','rejected')`).get(req.user.id).count; - myStats.completed = db.prepare(`SELECT COUNT(*) as count FROM complaints WHERE ${col} = ? AND status IN ('resolved','closed')`).get(req.user.id).count; - } - - res.json({ total: total.count, byStatus, byPriority, today: today.count, myStats }); -}); - -router.get('/:id', optionalAuth, (req, res) => { - const db = getDb(); - const complaint = db.prepare(`SELECT c.*, - p.display_name as planner_name, i.display_name as implementer_name, r.display_name as reviewer_name - FROM complaints c - LEFT JOIN users p ON c.assigned_planner_id = p.id - LEFT JOIN users i ON c.assigned_implementer_id = i.id - LEFT JOIN users r ON c.assigned_reviewer_id = r.id - WHERE c.id = ?`).get(req.params.id); - if (!complaint) return res.status(404).json({ error: '投诉不存在' }); - - const responses = db.prepare(`SELECT r.*, u.display_name as user_name, u.role as user_role - FROM responses r LEFT JOIN users u ON r.user_id = u.id - WHERE r.complaint_id = ? ORDER BY r.created_at ASC`).all(req.params.id); - complaint.responses = responses; - - if (complaint.plan_id) { - const plan = db.prepare(`SELECT p.*, u.display_name as planner_name FROM plans p - LEFT JOIN users u ON p.planner_id = u.id WHERE p.id = ?`).get(complaint.plan_id); - complaint.plan = plan; - } - if (complaint.implementation_id) { - const impl = db.prepare(`SELECT i.*, u.display_name as implementer_name FROM implementations i - LEFT JOIN users u ON i.implementer_id = u.id WHERE i.id = ?`).get(complaint.implementation_id); - complaint.implementation = impl; - } - if (complaint.review_id) { - const review = db.prepare(`SELECT r.*, u.display_name as reviewer_name FROM reviews r - LEFT JOIN users u ON r.reviewer_id = u.id WHERE r.id = ?`).get(complaint.review_id); - complaint.review = review; - } - - res.json(complaint); -}); - -router.post('/', optionalAuth, (req, res) => { - const { title, description, category, priority, player_name, player_email, player_contact } = req.body; - if (!title || !description || !player_name) { - return res.status(400).json({ error: '标题、描述和玩家名称为必填项' }); - } - const db = getDb(); - const result = db.prepare(`INSERT INTO complaints(title, description, category, priority, player_name, player_email, player_contact, status) - VALUES (?,?,?,?,?,?,?,'pending')`).run( - title, description, category || 'other', priority || 'medium', player_name, player_email || '', player_contact || '' - ); - const complaintId = result.lastInsertRowid; - - db.prepare(`INSERT INTO responses(complaint_id, user_id, response_type, content) VALUES (?,?,?,?)`).run( - complaintId, req.user?.id || null, 'system', '投诉已提交,等待处理' - ); - db.prepare(`INSERT INTO audit_logs(user_id, username, action, entity_type, entity_id, details) VALUES (?,?,?,?,?,?)`).run( - req.user?.id || null, req.user?.username || player_name, 'create', 'complaint', complaintId, - `投诉已创建: ${title}` - ); - res.status(201).json({ id: complaintId, message: '投诉提交成功' }); -}); - -router.put('/:id', authenticate, (req, res) => { - const db = getDb(); - const complaint = db.prepare('SELECT * FROM complaints WHERE id = ?').get(req.params.id); - if (!complaint) return res.status(404).json({ error: '投诉不存在' }); - - const allowedFields = {}; - if (req.body.title) allowedFields.title = req.body.title; - if (req.body.description) allowedFields.description = req.body.description; - if (req.body.category) allowedFields.category = req.body.category; - if (req.body.priority) allowedFields.priority = req.body.priority; - if (req.body.status) allowedFields.status = req.body.status; - - if (Object.keys(allowedFields).length === 0) { - return res.status(400).json({ error: '没有可更新的字段' }); - } - - const assignments = {}; - if (req.body.assigned_planner_id !== undefined) assignments.assigned_planner_id = req.body.assigned_planner_id; - if (req.body.assigned_implementer_id !== undefined) assignments.assigned_implementer_id = req.body.assigned_implementer_id; - if (req.body.assigned_reviewer_id !== undefined) assignments.assigned_reviewer_id = req.body.assigned_reviewer_id; - - const allFields = { ...allowedFields, ...assignments }; - const setClauses = Object.keys(allFields).map(k => `${k} = ?`).join(', '); - const values = Object.values(allFields); - - db.prepare(`UPDATE complaints SET ${setClauses}, updated_at = datetime('now','localtime') WHERE id = ?`).run(...values, req.params.id); - - if (req.body.status && req.body.status !== complaint.status) { - db.prepare(`INSERT INTO responses(complaint_id, user_id, response_type, content) VALUES (?,?,?,?)`).run( - req.params.id, req.user.id, 'system', `状态变更为: ${getStatusLabel(req.body.status)}` - ); - db.prepare(`INSERT INTO audit_logs(user_id, username, action, entity_type, entity_id, details) VALUES (?,?,?,?,?,?)`).run( - req.user.id, req.user.username, 'update_status', 'complaint', req.params.id, - `状态从 ${complaint.status} 变更为 ${req.body.status}` - ); - } - - db.prepare(`INSERT INTO audit_logs(user_id, username, action, entity_type, entity_id, details) VALUES (?,?,?,?,?,?)`).run( - req.user.id, req.user.username, 'update', 'complaint', req.params.id, '更新投诉信息' - ); - - res.json({ message: '更新成功' }); -}); - -router.post('/:id/response', authenticate, (req, res) => { - const { content, response_type } = req.body; - if (!content) return res.status(400).json({ error: '内容不能为空' }); - const db = getDb(); - const complaint = db.prepare('SELECT * FROM complaints WHERE id = ?').get(req.params.id); - if (!complaint) return res.status(404).json({ error: '投诉不存在' }); - - db.prepare(`INSERT INTO responses(complaint_id, user_id, response_type, content) VALUES (?,?,?,?)`).run( - req.params.id, req.user.id, response_type || 'comment', content - ); - db.prepare(`UPDATE complaints SET updated_at = datetime('now','localtime') WHERE id = ?`).run(req.params.id); - - res.status(201).json({ message: '回复添加成功' }); -}); - -function getStatusLabel(status) { - const labels = { - pending: '待处理', under_review: '审查中', planned: '已计划', - in_progress: '实施中', in_review: '审核中', resolved: '已解决', - rejected: '已驳回', closed: '已关闭' - }; - return labels[status] || status; -} - -module.exports = router; diff --git a/opencode.txt b/opencode.txt deleted file mode 100644 index 221f280..0000000 --- a/opencode.txt +++ /dev/null @@ -1 +0,0 @@ -ses_0b2c2409dffegYwIl1HBMSlSLL \ No newline at end of file diff --git a/public/js/pages/complaint-detail.js b/public/js/pages/complaint-detail.js deleted file mode 100644 index f5ac690..0000000 --- a/public/js/pages/complaint-detail.js +++ /dev/null @@ -1,322 +0,0 @@ -const ComplaintDetailPage = { - complaintId: null, - - async render(id) { - this.complaintId = id; - return '
加载中...
暂无记录
'} -加载中...
加载中...
暂无投诉记录
-| # | -标题 | -玩家 | -分类 | -优先级 | -状态 | -规划者 | -实施者 | -审查者 | -更新时间 | -操作 | -
|---|---|---|---|---|---|---|---|---|---|---|
| ${c.id} | -${Utils.escapeHtml(c.title)} | -${Utils.escapeHtml(c.player_name)} | -${Utils.categoryBadge(c.category)} | -${Utils.priorityBadge(c.priority)} | -${Utils.statusBadge(c.status)} | -${c.planner_name || '-'} | -${c.implementer_name || '-'} | -${c.reviewer_name || '-'} | -${Utils.formatDate(c.updated_at)} | -- |
加载中...
暂无实施记录
-| # | -关联投诉 | -关联计划 | -状态 | -实施者 | -创建时间 | -操作 | -
|---|---|---|---|---|---|---|
| ${i.id} | -#${i.complaint_id} ${Utils.escapeHtml(i.complaint_title)} | -${Utils.escapeHtml(i.plan_title)} | -${Utils.statusBadge(i.status)} | -${i.implementer_name || '-'} | -${Utils.formatDate(i.created_at)} | -- - | -
加载中...
暂无计划
-| # | -计划标题 | -关联投诉 | -状态 | -规划者 | -创建时间 | -操作 | -
|---|---|---|---|---|---|---|
| ${p.id} | -${Utils.escapeHtml(p.title)} | -#${p.complaint_id} ${Utils.escapeHtml(p.complaint_title)} | -${Utils.statusBadge(p.status)} | -${p.planner_name || '-'} | -${Utils.formatDate(p.created_at)} | -- - | -
加载中...
暂无审查记录
-| # | -关联投诉 | -状态 | -审查者 | -审查意见 | -创建时间 | -操作 | -
|---|---|---|---|---|---|---|
| ${r.id} | -#${r.complaint_id} ${Utils.escapeHtml(r.complaint_title)} | -${Utils.statusBadge(r.status)} | -${r.reviewer_name || '-'} | - -${Utils.formatDate(r.created_at)} | -- - | -