Files
MC_Report/backend/routes/complaints.js

209 lines
9.0 KiB
JavaScript

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;