Files
MC_Report/backend/routes/export.js

78 lines
5.0 KiB
JavaScript

const express = require('express');
const { query } = require('../db');
const { authenticate, requireRole } = require('../middleware/auth');
const router = express.Router();
const SL = { pending:'待处理', processing:'处理中', awaiting_info:'待补充', appealing:'申诉中', resolved:'已解决', rejected:'已驳回', closed:'已关闭' };
const TL = { report:'举报', suggestion:'建议', appeal:'申诉', result_appeal:'结果申诉' };
function toCSV(headers, rows, filename) {
let csv = '\uFEFF' + headers.join(',') + '\r\n';
for (const r of rows) {
csv += headers.map(h => { const v = String(r[h] ?? ''); return /[,"\n\r]/.test(v) ? '"' + v.replace(/"/g,'""') + '"' : v; }).join(',') + '\r\n';
}
return { csv, filename };
}
router.get('/tickets', authenticate, requireRole('owner'), async (req, res) => {
const { type, status, start_date, end_date } = req.query;
let q = `SELECT id as '编号', type as '类型', title as '标题', reporter_game_name as '提交者游戏名', reporter_game_uid as '提交者UID', target_game_name as '对象游戏名', target_game_uid as '对象UID', reason as '原因', description as '描述', status as '状态', priority as '优先级', created_at as '创建时间', updated_at as '更新时间' FROM tickets WHERE 1=1`;
const p = [];
if (type) { q += ' AND type = ?'; p.push(type); }
if (status) { q += ' AND status = ?'; p.push(status); }
if (start_date) { q += ' AND DATE(created_at) >= ?'; p.push(start_date); }
if (end_date) { q += ' AND DATE(created_at) <= ?'; p.push(end_date); }
q += ' ORDER BY created_at DESC';
const rows = await query(q, p);
if (req.query.format === 'csv') {
for (const r of rows) { r['类型'] = TL[r['类型']]||r['类型']; r['状态'] = SL[r['状态']]||r['状态']; }
const { csv, filename } = toCSV(['编号','类型','标题','提交者游戏名','提交者UID','对象游戏名','对象UID','原因','描述','状态','优先级','创建时间','更新时间'], rows, `tickets_${new Date().toISOString().slice(0,10)}.csv`);
res.setHeader('Content-Type','text/csv; charset=utf-8');
res.setHeader('Content-Disposition','attachment; filename='+filename);
return res.send(csv);
}
res.json(rows);
});
router.get('/reported-players', authenticate, requireRole('owner'), async (req, res) => {
const rows = await query(`SELECT target_game_name as '被举报人游戏名', target_game_uid as '被举报人UID', COUNT(*) as '被举报次数', SUM(CASE WHEN status='resolved' THEN 1 ELSE 0 END) as '已处理', SUM(CASE WHEN status IN ('pending','processing','awaiting_info') THEN 1 ELSE 0 END) as '处理中', SUM(CASE WHEN status='rejected' THEN 1 ELSE 0 END) as '已驳回', MAX(created_at) as '最近举报时间', GROUP_CONCAT(reason SEPARATOR ' | ') as '常见原因' FROM tickets WHERE type='report' AND target_game_name IS NOT NULL AND target_game_name != '' GROUP BY target_game_name, target_game_uid ORDER BY COUNT(*) DESC`);
if (req.query.format === 'csv') {
const { csv, filename } = toCSV(['被举报人游戏名','被举报人UID','被举报次数','已处理','处理中','已驳回','最近举报时间','常见原因'], rows, `reported_players_${new Date().toISOString().slice(0,10)}.csv`);
res.setHeader('Content-Type','text/csv; charset=utf-8');
res.setHeader('Content-Disposition','attachment; filename='+filename);
return res.send(csv);
}
res.json(rows);
});
router.get('/audit-logs', authenticate, requireRole('owner'), async (req, res) => {
const { start_date, end_date } = req.query;
let q = `SELECT id as '编号', username as '用户', action as '操作', entity_type as '对象', entity_id as '对象ID', details as '详情', created_at as '时间' FROM audit_logs WHERE 1=1`;
const p = [];
if (start_date) { q += ' AND DATE(created_at) >= ?'; p.push(start_date); }
if (end_date) { q += ' AND DATE(created_at) <= ?'; p.push(end_date); }
q += ' ORDER BY created_at DESC LIMIT 5000';
const rows = await query(q, p);
if (req.query.format === 'csv') {
const { csv, filename } = toCSV(['编号','用户','操作','对象','对象ID','详情','时间'], rows, `audit_${new Date().toISOString().slice(0,10)}.csv`);
res.setHeader('Content-Type','text/csv; charset=utf-8');
res.setHeader('Content-Disposition','attachment; filename='+filename);
return res.send(csv);
}
res.json(rows);
});
router.get('/dashboard', authenticate, requireRole('owner'), async (req, res) => {
const [[{c:total}], byType, byStatus, [{c:today}], [{c:usersTotal}], monthly] = await Promise.all([
query('SELECT COUNT(*) as c FROM tickets'),
query('SELECT type, COUNT(*) as c FROM tickets GROUP BY type'),
query('SELECT status, COUNT(*) as c FROM tickets GROUP BY status'),
query("SELECT COUNT(*) as c FROM tickets WHERE DATE(created_at) = CURDATE()"),
query('SELECT COUNT(*) as c FROM users'),
query("SELECT DATE_FORMAT(created_at,'%Y-%m') as m, COUNT(*) as c FROM tickets GROUP BY m ORDER BY m DESC LIMIT 12"),
]);
res.json({ total, byType, byStatus, today, usersTotal, monthly });
});
module.exports = router;