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;