feat: anonymous ticket reply via tracking cookie
This commit is contained in:
@@ -277,6 +277,17 @@ router.post('/:id/response', authenticate, async (req, res) => {
|
|||||||
res.status(201).json({ message: '回复成功' });
|
res.status(201).json({ message: '回复成功' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.post('/:id/anon-response', async (req, res) => {
|
||||||
|
const { content } = req.body;
|
||||||
|
if (!content) return res.status(400).json({ error: '内容不能为空' });
|
||||||
|
const cookieToken = req.headers.cookie?.split('; ').find(c => c.startsWith('tracking_token='))?.split('=')[1];
|
||||||
|
if (!cookieToken) return res.status(403).json({ error: '无权操作,请先提交工单获取追踪标识' });
|
||||||
|
const t = await getRow('SELECT * FROM tickets WHERE id = ? AND tracking_token = ?', [req.params.id, cookieToken]);
|
||||||
|
if (!t) return res.status(404).json({ error: '工单不存在或无权操作' });
|
||||||
|
await query("INSERT INTO responses(ticket_id, content, is_staff) VALUES (?,?,0)", [req.params.id, content]);
|
||||||
|
res.status(201).json({ message: '补充成功' });
|
||||||
|
});
|
||||||
|
|
||||||
async function notifyStatusChange(ticket, newStatus) {
|
async function notifyStatusChange(ticket, newStatus) {
|
||||||
if (ticket.user_id) {
|
if (ticket.user_id) {
|
||||||
await sendEmailForTicket(ticket, 'ticket_updated', { ticket_status: SL[newStatus]||newStatus, ticket_status_color: SC[newStatus]||'#6b7280' });
|
await sendEmailForTicket(ticket, 'ticket_updated', { ticket_status: SL[newStatus]||newStatus, ticket_status_color: SC[newStatus]||'#6b7280' });
|
||||||
|
|||||||
@@ -46,7 +46,15 @@ const HomePage = {
|
|||||||
API.get(`/tickets/tracking?token=${token}`).then(tickets => {
|
API.get(`/tickets/tracking?token=${token}`).then(tickets => {
|
||||||
const t = tickets.find(x=>x.id===id);
|
const t = tickets.find(x=>x.id===id);
|
||||||
if(!t) return;
|
if(!t) return;
|
||||||
document.getElementById('tkd').innerHTML = `<div class="detail-section"><h4>基本信息</h4><div class="detail-row"><span class="lbl">标题</span><span class="val fb">${U.esc(t.title)}</span></div><div class="detail-row"><span class="lbl">类型</span><span class="val">${U.badge(t.type,'type')}</span></div><div class="detail-row"><span class="lbl">状态</span><span class="val">${U.badge(t.status,'status')}</span></div><div class="detail-row"><span class="lbl">提交者</span><span class="val">${U.esc(t.reporter_game_name)} (UID: ${U.esc(t.reporter_game_uid)})</span></div></div><p class="tm ts">如需回复请<a href="#/register" style="color:var(--p)">注册账号</a></p>`;
|
document.getElementById('tkd').innerHTML = `<div class="detail-section"><h4>基本信息</h4><div class="detail-row"><span class="lbl">标题</span><span class="val fb">${U.esc(t.title)}</span></div><div class="detail-row"><span class="lbl">类型</span><span class="val">${U.badge(t.type,'type')}</span></div><div class="detail-row"><span class="lbl">状态</span><span class="val">${U.badge(t.status,'status')}</span></div><div class="detail-row"><span class="lbl">提交者</span><span class="val">${U.esc(t.reporter_game_name)} (UID: ${U.esc(t.reporter_game_uid)})</span></div>${t.description?`<div class="detail-row"><span class="lbl">详情</span><span class="val">${U.esc(t.description)}</span></div>`:''}</div>
|
||||||
|
<form id="anon-reply-form" class="mt-4"><div class="form-group"><textarea id="anon-reply-content" rows="3" placeholder="追加补充信息..."></textarea></div><button type="submit" class="btn btn-p btn-sm"><i class="fas fa-reply"></i> 补充</button></form>`;
|
||||||
|
document.getElementById('anon-reply-form').onsubmit = async e => {
|
||||||
|
e.preventDefault();
|
||||||
|
const c = document.getElementById('anon-reply-content').value.trim();
|
||||||
|
if (!c) return;
|
||||||
|
try { await API.post(`/tickets/${id}/anon-response`, { content: c }); alert('补充成功'); App.closeModal(); }
|
||||||
|
catch(ex) { alert(ex.message); }
|
||||||
|
};
|
||||||
}).catch(()=>{});
|
}).catch(()=>{});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user