90 lines
3.6 KiB
HTML
90 lines
3.6 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>同步流水</title>
|
|
<link rel="stylesheet" href="/static/static.css">
|
|
</head>
|
|
<body>
|
|
<div class="wrap">
|
|
<header>
|
|
<h1>同步流水</h1>
|
|
<nav class="nav"><a href="/">首页</a><a href="/logs">文件日志</a><a href="/test">测试</a></nav>
|
|
</header>
|
|
<div class="toolbar">
|
|
<label>锚定日 <input type="date" id="anchor"></label>
|
|
<label>类型
|
|
<select id="step"></select>
|
|
</label>
|
|
<button class="primary" id="load">刷新</button>
|
|
</div>
|
|
<h3 style="color:var(--muted);font-size:0.95rem">同步任务</h3>
|
|
<div style="overflow:auto;margin-bottom:1.5rem">
|
|
<table><thead><tr>
|
|
<th>日期</th><th>类型</th><th>状态</th><th>总数</th><th>成功</th><th>失败</th><th>开始</th><th>结束</th>
|
|
</tr></thead><tbody id="jobs"></tbody></table>
|
|
</div>
|
|
<h3 style="color:var(--muted);font-size:0.95rem">业务记录(拉取 → 上报 → 回调)</h3>
|
|
<div style="overflow:auto">
|
|
<table><thead><tr>
|
|
<th>ID</th><th>日期</th><th>类型</th><th>批次</th><th>biz_key</th><th>拉取校验</th>
|
|
<th>上报</th><th>推送明文</th><th>HTTP</th><th>回调</th><th>trace</th><th></th>
|
|
</tr></thead><tbody id="items"></tbody></table>
|
|
</div>
|
|
</div>
|
|
<script src="/static/app.js"></script>
|
|
<script>
|
|
const anchorFromUrl = qs('anchor_date');
|
|
if (anchorFromUrl) document.getElementById('anchor').value = anchorFromUrl;
|
|
|
|
(async () => {
|
|
const m = await loadMeta();
|
|
if (m && m.stepOptions) fillStepSelect(document.getElementById('step'), m.stepOptions);
|
|
})();
|
|
|
|
async function load() {
|
|
const anchor = document.getElementById('anchor').value;
|
|
const step = document.getElementById('step').value;
|
|
let url = '/api/runs?limit=100';
|
|
if (anchor) url += '&anchor_date=' + anchor;
|
|
if (step) url += '&step=' + step;
|
|
const data = await api(url);
|
|
|
|
document.getElementById('jobs').innerHTML = (data.jobs || []).map(j => `<tr>
|
|
<td>${esc(j.anchorDate)}</td>
|
|
<td>${esc(j.stepLabel || j.step)}</td>
|
|
<td>${badge(j.status, j.statusLabel)}</td>
|
|
<td>${j.totalCount}</td><td>${j.successCount}</td><td>${j.failedCount}</td>
|
|
<td>${fmtTimeAny(j.startedAt)}</td>
|
|
<td>${fmtTimeAny(j.finishedAt)}</td>
|
|
</tr>`).join('') || '<tr><td colspan="8">无任务</td></tr>';
|
|
|
|
document.getElementById('items').innerHTML = (data.items || []).map(it => {
|
|
const r = it.record;
|
|
const cb = it.callback;
|
|
const ve = it.errors && it.errors.valid ? (it.errors.formatted || it.errors.raw) : (it.errors && it.errors.raw) || '';
|
|
const veShort = ve.length > 80 ? ve.slice(0, 80) + '…' : ve;
|
|
const ll = r.LastLog;
|
|
return `<tr>
|
|
<td>${r.ID}</td>
|
|
<td>${esc(r.AnchorDate)}</td>
|
|
<td>${esc(it.stepLabel || it.step)}</td>
|
|
<td>${r.JobID}</td>
|
|
<td><code>${esc(r.BizKey)}</code></td>
|
|
<td>${veShort ? '<span class="badge warn">有</span> ' + esc(veShort) : badge('success', '通过')}</td>
|
|
<td>${badge(r.PushStatus, it.pushStatusLabel)}</td>
|
|
<td>${it.hasPushPlain ? '<span class="badge ok">有</span>' : '<span class="badge warn">无</span>'}</td>
|
|
<td>${ll ? ll.HTTPCode + ' / ' + (ll.MsgCode && ll.MsgCode.Valid ? ll.MsgCode.Int64 : '-') + ' / ' + ll.DurationMs + 'ms' : '-'}</td>
|
|
<td>${badge(cb.callback_status, it.callbackStatusLabel)} ${esc(cb.error_message || '')}</td>
|
|
<td>${ll ? esc(ll.TraceID) : '-'}</td>
|
|
<td><a href="/runs/record?id=${r.ID}">详情</a></td>
|
|
</tr>`;
|
|
}).join('') || '<tr><td colspan="12">无记录</td></tr>';
|
|
}
|
|
|
|
document.getElementById('load').onclick = () => load().catch(e => alert(e.message));
|
|
load().catch(e => alert(e.message));
|
|
</script>
|
|
</body>
|
|
</html>
|