(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));
})();