feat: auto-close resolved/rejected after 7 days, closed only owner can modify

This commit is contained in:
2026-07-12 18:15:10 +08:00
parent 33078855cb
commit f87411b3b4

View File

@@ -16,7 +16,14 @@ const SO = { processing:1, awaiting_info:2, appealing:3, pending:4, resolved:5,
async function siteUrl() { const r = await getRow("SELECT v FROM settings WHERE k='site_url'"); return r?.v || 'http://localhost:3100'; }
async function autoCloseExpired() {
try {
await query(`UPDATE tickets SET status = 'closed' WHERE status IN ('resolved','rejected') AND updated_at < NOW() - INTERVAL 7 DAY`);
} catch {}
}
router.get('/', authenticate, async (req, res) => {
await autoCloseExpired();
let q = `SELECT t.*, u.game_name as user_game_name, u.username as submitter, a.game_name as assignee_name
FROM tickets t LEFT JOIN users u ON t.user_id = u.id LEFT JOIN users a ON t.assigned_to = a.id`;
const conds = [], params = [];
@@ -78,6 +85,7 @@ router.get('/tracking', async (req, res) => {
});
router.get('/:id', authenticate, async (req, res) => {
await autoCloseExpired();
const t = await getRow(`SELECT t.*, u.game_name as user_game_name, u.username as submitter, a.game_name as assignee_name, a.username as assignee_username
FROM tickets t LEFT JOIN users u ON t.user_id = u.id LEFT JOIN users a ON t.assigned_to = a.id WHERE t.id = ?`, [req.params.id]);
if (!t) return res.status(404).json({ error: '工单不存在' });
@@ -172,7 +180,7 @@ router.post('/', optionalAuth, upload.array('files', 5), finalizeUpload, validat
router.put('/:id', authenticate, requireRole('owner','admin'), async (req, res) => {
const t = await getRow('SELECT * FROM tickets WHERE id = ?', [req.params.id]);
if (!t) return res.status(404).json({ error: '工单不存在' });
if (t.status === 'closed') return res.status(400).json({ error: '已关闭的工单不可再修改' });
if (t.status === 'closed' && req.user.role !== 'owner') return res.status(400).json({ error: '已关闭的工单仅服主可操作' });
const fields = {};
if (req.body.status) fields.status = req.body.status;
if (req.body.priority) fields.priority = req.body.priority;
@@ -199,7 +207,7 @@ router.post('/:id/claim', authenticate, requireRole('owner','admin'), async (req
const t = await getRow('SELECT * FROM tickets WHERE id = ?', [req.params.id]);
if (!t) return res.status(404).json({ error: '工单不存在' });
if (t.assigned_to && t.assigned_to !== req.user.id && t.status !== 'pending') return res.status(400).json({ error: '该工单已被他人认领' });
if (t.status === 'closed') return res.status(400).json({ error: '已关闭的工单不可认领' });
if (t.status === 'closed' && req.user.role !== 'owner') return res.status(400).json({ error: '已关闭的工单仅服主可操作' });
if (t.type === 'suggestion' && req.user.role !== 'owner') return res.status(403).json({ error: '建议工单仅服主可处理' });
if ((t.type === 'result_appeal' || t.is_admin_complaint) && req.user.role !== 'owner') return res.status(403).json({ error: '该工单仅服主可处理' });
@@ -220,7 +228,7 @@ router.post('/:id/claim', authenticate, requireRole('owner','admin'), async (req
router.post('/:id/transfer', authenticate, requireRole('owner','admin'), async (req, res) => {
const t = await getRow('SELECT * FROM tickets WHERE id = ?', [req.params.id]);
if (!t) return res.status(404).json({ error: '工单不存在' });
if (t.status === 'closed') return res.status(400).json({ error: '已关闭的工单不可转交' });
if (t.status === 'closed' && req.user.role !== 'owner') return res.status(400).json({ error: '已关闭的工单仅服主可操作' });
if (t.assigned_to !== req.user.id && req.user.role !== 'owner') return res.status(403).json({ error: '只能转交自己负责的工单' });
const { to_user_id, reason } = req.body;
if (!to_user_id) return res.status(400).json({ error: '请选择转交目标' });