diff --git a/backend/routes/tickets.js b/backend/routes/tickets.js index 0885194..ef9a0e2 100644 --- a/backend/routes/tickets.js +++ b/backend/routes/tickets.js @@ -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: '请选择转交目标' });