(function () { const state = { bizType: 'pdf', items: [], currentRel: null }; function $(id) { return document.getElementById(id); } async function api(path, opts) { const r = await fetch(path, opts); const t = await r.text(); let j; try { j = JSON.parse(t); } catch { j = { error: t }; } if (!r.ok) throw new Error(j.error || r.statusText); return j; } function esc(s) { const d = document.createElement('div'); d.textContent = s == null ? '' : String(s); return d.innerHTML; } function showToast(msg, ok) { const el = $('toast'); el.textContent = msg; el.className = 'rx-toast ' + (ok ? 'ok' : 'err'); setTimeout(() => { el.textContent = ''; el.className = 'rx-toast'; }, 4000); } function statusBadge(rec) { if (rec.ok) return '成功'; return '失败'; } function fmtTime(at) { if (!at) return '-'; try { return new Date(at).toLocaleString('zh-CN'); } catch { return esc(at); } } async function load() { const data = await api('/t/api/prescription/records?bizType=' + encodeURIComponent(state.bizType) + '&limit=200'); state.items = data.items || []; const tb = $('tbody'); if (!state.items.length) { tb.innerHTML = '暂无记录(可先触发同步或点「重建索引」)'; $('hint').textContent = '索引文件:api-logs/prescription-audit.jsonl'; return; } tb.innerHTML = state.items.map((rec) => { const hint = rec.fileId || rec.prescriptionHint || '-'; const rowClass = rec.ok ? '' : ' class="rx-fail"'; return '' + '' + fmtTime(rec.at) + '' + '' + esc(rec.channel) + '' + '' + esc(rec.httpStatus) + '' + '' + esc(rec.msgCode != null ? rec.msgCode : '-') + '' + '' + esc(rec.traceId || '-') + '' + '' + esc(hint) + '' + '' + statusBadge(rec) + (rec.errorSummary ? ' ' + esc(rec.errorSummary) : '') + '' + '' + esc(rec.durationMs) + 'ms' + '' + ' ' + '' + ''; }).join(''); $('hint').textContent = '共 ' + state.items.length + ' 条 · 类型 ' + state.bizType; tb.querySelectorAll('button[data-act]').forEach((btn) => { btn.onclick = () => { const rel = btn.dataset.rel; if (btn.dataset.act === 'detail') openDetail(rel); else retryRecord(rel); }; }); } async function openDetail(rel) { state.currentRel = rel; const d = await api('/t/api/logs/detail?rel=' + encodeURIComponent(rel)); $('detail-view').textContent = JSON.stringify(d, null, 2); $('replay-view').style.display = 'none'; $('replay-view').textContent = ''; $('drawer').hidden = false; } async function retryRecord(rel) { if (!confirm('向政务云回放该条出站请求(块2)?\n\n注意:仅内网运维重放,不会更新 transit/xk-api 状态。')) return; state.currentRel = rel; try { const result = await api('/t/api/test/send', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ rel: rel, useOutbound: true }), }); $('replay-view').style.display = 'block'; $('replay-view').textContent = '回放结果:\n' + JSON.stringify(result, null, 2); $('drawer').hidden = false; $('detail-view').textContent = '(重试完成,见下方回放结果;可点详情查看完整 api-log)'; showToast('回放完成 HTTP ' + (result.httpStatus || '-'), result.ok !== false); } catch (e) { showToast(e.message, false); } } document.querySelectorAll('.rx-tabs .tab').forEach((tab) => { tab.onclick = () => { document.querySelectorAll('.rx-tabs .tab').forEach((t) => t.classList.remove('active')); tab.classList.add('active'); state.bizType = tab.dataset.biz; load().catch((e) => showToast(e.message, false)); }; }); $('btn-refresh').onclick = () => load().catch((e) => showToast(e.message, false)); $('btn-rebuild').onclick = async () => { if (!confirm('扫描 api-logs 全量重建 prescription-audit.jsonl?')) return; try { const r = await api('/t/api/prescription/rebuild', { method: 'POST' }); showToast('已索引 ' + r.count + ' 条', true); await load(); } catch (e) { showToast(e.message, false); } }; $('btn-close-drawer').onclick = () => { $('drawer').hidden = true; }; $('btn-retry').onclick = () => { if (state.currentRel) retryRecord(state.currentRel); }; load().catch((e) => showToast(e.message, false)); })();