Мониторинг связи с НРД: участки Шлюз/НРД/M2M/Отправка, сквозная проверка справочником раз в 10 мин, история за сутки на «Статусе», цепочка в шапке (30 с), автоповтор недоставленных до 24 ч, баннер и статус «ожидает отправки» в кабинете клиента
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
@@ -33,7 +33,7 @@
|
||||
<h2 style="display:flex;align-items:center;gap:10px;flex-wrap:wrap">Заявка <code>{{slice .Claim.ID 0 8}}</code> · <span class="badge {{.Claim.Status}}">{{ruState .Claim.Status}}</span>{{if and (or .Fail (eq .Claim.Status "validated")) (can "operate")}}<form method="post" action="/admin/claims/{{.Claim.ID}}/resend" style="margin:0 0 0 auto" onsubmit="this.querySelector('button').disabled=true;this.querySelector('button').textContent='Отправляем…';"><button type="submit" class="btn">Отправить повторно</button></form>{{end}}{{if and (or (eq .Claim.Status "draft") (eq .Claim.Status "validated")) (can "operate")}}<form method="post" action="/admin/claims/{{.Claim.ID}}/delete-draft" style="margin:0{{if not (or .Fail (eq .Claim.Status "validated"))}} 0 0 auto{{end}}" onsubmit="return confirm('Удалить заявку? Она ещё не уходила в НРД. Если по ней бумаги заблокированы в QUIK МНП, блокировка будет снята.');"><button type="submit" class="btn" style="background:var(--card);color:var(--err,#c0392b);border:1px solid var(--err,#c0392b)">{{if eq .Claim.Status "draft"}}Удалить черновик{{else}}Удалить{{end}}</button></form>{{end}}</h2>
|
||||
{{with .Fail}}<div class="card" style="background:var(--err-weak,#fbe9e7);margin:8px 0">
|
||||
<b>Перевод не доставлен в НРД</b> — {{sendFailText .}}.<br>
|
||||
<span class="muted">Попытка {{.At.Format "02.01.2006 15:04:05"}}{{if .PackageID}}, пакет ИШ {{.PackageID}}{{end}}{{if gt .Attempts 1}}, попыток: {{.Attempts}}{{end}}. Отправка в DEPO не отмечена, блокировка в QUIK МНП сохраняется. Когда НРД снова будет доступен, нажмите «Отправить повторно» — уйдёт тот же запрос с тем же GUID.</span>
|
||||
<span class="muted">Попытка {{.At.Format "02.01.2006 15:04:05"}}{{if .PackageID}}, пакет ИШ {{.PackageID}}{{end}}{{if gt .Attempts 1}}, попыток: {{.Attempts}}{{end}}. Отправка в DEPO не отмечена, блокировка в QUIK МНП сохраняется. {{if .GaveUp}}Сутки прошли — автоповтор остановлен: отправьте вручную или удалите заявку.{{else}}Ничего нажимать не нужно: как только НРД снова ответит, bj сам отправит тот же запрос с тем же GUID (повтор — в течение суток). Кнопка «Отправить повторно» — если нужно попробовать прямо сейчас.{{end}}</span>
|
||||
</div>{{end}}
|
||||
{{$l := liveOut .Claim}}<div class="live live-big live-{{$l.Kind}}" data-live="out:{{.Claim.ID}}" data-sig="{{$l.Sig}}"{{if $l.Active}} data-active="1"{{else}} title="Завершено: состояние больше не проверяется"{{end}}>{{$l.Text}}</div>
|
||||
<table>
|
||||
|
||||
@@ -1,4 +1,58 @@
|
||||
{{define "content"}}
|
||||
{{if .NSDLink.Configured}}<div class="card" id="nsd">
|
||||
<h2 style="display:flex;align-items:center;gap:10px;flex-wrap:wrap">Связь с НРД
|
||||
<span class="badge {{if eq .NSDLink.State "ok"}}confirmed{{else if eq .NSDLink.State "err"}}rejected{{else}}awaiting_decision{{end}}">{{if eq .NSDLink.State "ok"}}работает{{else if eq .NSDLink.State "err"}}сбой{{else if eq .NSDLink.State "warn"}}с перебоями{{else}}нет данных{{end}}</span>
|
||||
{{if can "operate"}}<form method="post" action="/admin/status/nsd-probe" style="margin:0 0 0 auto"><button class="btn" type="submit">Проверить связь сейчас</button></form>{{end}}
|
||||
</h2>
|
||||
{{if .Probe}}<p class="muted">Запрос проверки отправлен — ответ НРД обычно приходит за 20–60 с. Страница обновится сама.</p>{{end}}
|
||||
<p class="muted" style="margin-top:0">Зелёный — только при свежем подтверждении от НРД, а не при отсутствии ошибок. {{if .NSDLink.Ago}}Последний ответ НРД — {{.NSDLink.Ago}}.{{end}}</p>
|
||||
<table>
|
||||
<thead><tr><th></th><th>Участок</th><th>Состояние</th><th>Чем проверяем</th></tr></thead>
|
||||
<tbody>
|
||||
{{range .NSDLink.Segs}}<tr>
|
||||
<td><span class="nsd-dot {{.State}}"></span></td>
|
||||
<td><strong>{{.Title}}</strong></td>
|
||||
<td>{{.Text}}{{with .Detail}}<br><code>{{.}}</code>{{end}}</td>
|
||||
<td class="muted" style="font-size:12.5px">{{if eq .Key "igw"}}bj обращается к шлюзу каждые 30 с{{else if eq .Key "nsd"}}журнал шлюза: ответы НРД на опросы входящих{{else if eq .Key "m2m"}}раз в 10 мин запрос справочника участников M2M и ответ на него — весь путь до сервиса M2M и обратно{{else}}подтверждение доставки каждого отправленного пакета{{end}}</td>
|
||||
</tr>{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3 style="margin:18px 0 6px;font-size:14px">За последние сутки <span class="muted" style="font-weight:400;font-size:12px">с {{.StripFrom}}, отрезок — 10 минут</span></h3>
|
||||
<div class="nsd-strip">
|
||||
{{range .Strip}}<div class="nsd-row"><span class="nsd-t">{{.Title}}</span><span class="nsd-cells">{{range .Cells}}<i class="{{.State}}" title="{{.Title}}"></i>{{end}}</span><span class="nsd-b muted">{{.Bad}}</span></div>{{end}}
|
||||
<div class="nsd-legend muted"><i class="ok"></i>работает <i class="warn"></i>перебои <i class="err"></i>сбой <i class="none"></i>нет данных</div>
|
||||
</div>
|
||||
|
||||
{{if .Fails}}<h3 style="margin:18px 0 6px;font-size:14px">Не доставлены в НРД</h3>
|
||||
<table>
|
||||
<thead><tr><th>Перевод</th><th>Причина</th><th>Первая попытка</th><th>Попыток</th><th></th></tr></thead>
|
||||
<tbody>{{range .Fails}}<tr>
|
||||
<td><a href="/admin/claims/{{.DealID}}"><code>{{slice .GUID 0 8}}</code></a></td>
|
||||
<td>{{.Text}}</td>
|
||||
<td>{{.FirstAt.Format "02.01 15:04"}}</td>
|
||||
<td>{{.Attempts}}</td>
|
||||
<td>{{if .GaveUp}}<span style="color:var(--err)">автоповтор остановлен (прошли сутки)</span>{{else}}<span class="muted">отправится сам, когда НРД ответит</span>{{end}}{{if can "operate"}}<form method="post" action="/admin/claims/{{.DealID}}/resend" style="margin:4px 0 0"><button class="btn" type="submit" style="padding:2px 10px;font-size:12px">Отправить сейчас</button></form>{{end}}</td>
|
||||
</tr>{{end}}</tbody>
|
||||
</table>{{end}}
|
||||
</div>
|
||||
<style>
|
||||
.nsd-dot { width:10px; height:10px; border-radius:50%; display:inline-block; background:var(--muted); }
|
||||
.nsd-dot.ok { background:var(--ok); } .nsd-dot.warn { background:var(--warn); } .nsd-dot.err { background:var(--err); }
|
||||
.nsd-strip { display:flex; flex-direction:column; gap:6px; }
|
||||
.nsd-row { display:grid; grid-template-columns:80px 1fr 130px; align-items:center; gap:10px; }
|
||||
.nsd-t { font-size:13px; font-weight:600; }
|
||||
.nsd-b { font-size:12px; text-align:right; }
|
||||
.nsd-cells { display:grid; grid-template-columns:repeat(144, 1fr); gap:1px; height:18px; }
|
||||
.nsd-cells i, .nsd-legend i { display:block; border-radius:2px; background:var(--border); }
|
||||
.nsd-cells i.ok, .nsd-legend i.ok { background:var(--ok); } .nsd-cells i.warn, .nsd-legend i.warn { background:var(--warn); }
|
||||
.nsd-cells i.err, .nsd-legend i.err { background:var(--err); } .nsd-cells i.unknown { background:var(--muted); opacity:.35; }
|
||||
.nsd-legend { display:flex; align-items:center; gap:6px; font-size:12px; margin-top:4px; }
|
||||
.nsd-legend i { width:10px; height:10px; display:inline-block; margin-left:8px; }
|
||||
@media (max-width:700px){ .nsd-row { grid-template-columns:1fr; } .nsd-b { text-align:left; } }
|
||||
</style>
|
||||
<script>setTimeout(function(){ if (!document.querySelector('main form input:focus')) location.reload(); }, {{if .Probe}}20000{{else}}60000{{end}});</script>
|
||||
{{end}}
|
||||
<div class="card">
|
||||
<h2>Статус системы — детально</h2>
|
||||
<table>
|
||||
|
||||
@@ -100,6 +100,15 @@ body {
|
||||
.theme-toggle svg { width:16px; height:16px; }
|
||||
.theme-toggle:hover { background:var(--border); color:var(--text); }
|
||||
.topbar .clock { font-size:11px; color:var(--muted); font-family:var(--mono); font-variant-numeric:tabular-nums; letter-spacing:-0.02em; }
|
||||
.nsd-chain { text-decoration:none; gap:6px; }
|
||||
.nsd-chain .seg { display:inline-flex; align-items:center; gap:4px; }
|
||||
.nsd-chain .seg i { width:8px; height:8px; border-radius:50%; background:var(--muted); display:inline-block; }
|
||||
.nsd-chain .seg.ok i { background:var(--ok); } .nsd-chain .seg.warn i { background:var(--warn); } .nsd-chain .seg.err i { background:var(--err); }
|
||||
.nsd-chain .seg.err { color:var(--err); } .nsd-chain .sep { color:var(--muted); font-weight:400; }
|
||||
.nsd-chain .ago { font-weight:400; color:var(--muted); }
|
||||
.nsd-chain.err { background:var(--err-weak); border-color:var(--err); }
|
||||
.nsd-chain.warn { background:var(--warn-weak); }
|
||||
@media (max-width:1100px){ .nsd-chain .seg > span, .nsd-chain .ago, .nsd-chain .sep { display:none } }
|
||||
.sys-block { display:inline-flex; flex-direction:column; align-items:center; gap:2px; line-height:1.1; }
|
||||
.sys-block .sys-status { padding:3px 9px 3px 7px; font-size:11.5px; }
|
||||
.user-dd .ddbtn.user-chip { display:inline-flex; flex-direction:column; align-items:flex-start; padding:4px 10px; border-radius:8px; margin:0; max-width:220px; }
|
||||
@@ -349,9 +358,7 @@ label { font-size:13px; font-weight:500; }
|
||||
</nav>
|
||||
<div class="topbar-right">
|
||||
<span class="sys-block">
|
||||
{{if and .IGWDown (not .IsMockMode)}}<span class="sys-status err" title="{{.IGWDetail}}" style="background:var(--err-weak,#fbe9e7);color:var(--err,#b3261e)">
|
||||
<span class="pulse"></span>ИШ НРД недоступен · {{.IGWDown}}</span>{{else if and .NSDDown (not .IsMockMode)}}<span class="sys-status err" title="{{.IGWDetail}}" style="background:var(--err-weak,#fbe9e7);color:var(--err,#b3261e)">
|
||||
<span class="pulse"></span>НРД не отвечает</span>{{else}}<span class="sys-status {{if .IsMockMode}}warn{{end}}" title="Состояние обмена с НРД">
|
||||
{{if and .NSDLink.Configured (not .IsMockMode)}}<a id="nsd-chain" href="/admin/status#nsd" class="sys-status nsd-chain {{.NSDLink.State}}" title="{{nsdSegTip .NSDLink}}">{{range $i, $s := .NSDLink.Segs}}{{if $i}}<span class="sep" aria-hidden="true">›</span>{{end}}<span class="seg {{$s.State}}"><i></i><span>{{$s.Title}}</span></span>{{end}}<span class="ago">{{if .NSDLink.Ago}}ответ НРД {{.NSDLink.Ago}}{{else}}ответа НРД ещё не было{{end}}</span></a>{{else}}<span class="sys-status {{if .IsMockMode}}warn{{end}}" title="Состояние обмена с НРД">
|
||||
<span class="pulse"></span>{{if .IsMockMode}}{{if devOn}}Эмуляция{{else}}Нет связи с НРД{{end}}{{else}}НРД на связи{{end}}</span>{{end}}
|
||||
<span class="clock">{{.Now}}</span>
|
||||
</span>
|
||||
@@ -555,6 +562,31 @@ label { font-size:13px; font-weight:500; }
|
||||
}
|
||||
poll();
|
||||
setInterval(poll, 12000); // каждые 12 сек
|
||||
// Цепочка связи с НРД в шапке: обновляется каждые 30 с, чтобы открытая страница не показывала старое.
|
||||
(function(){
|
||||
var el = document.getElementById('nsd-chain');
|
||||
if (!el) return;
|
||||
function esc(t){ return String(t).replace(/[&<>"]/g, function(c){ return {'&':'&','<':'<','>':'>','"':'"'}[c]; }); }
|
||||
function upd(){
|
||||
fetch('/admin/nsd-health.json', {cache:'no-store'}).then(function(r){ return r.json(); }).then(function(h){
|
||||
if (!h.configured) return;
|
||||
var html = '', tip = '';
|
||||
(h.segs||[]).forEach(function(s, i){
|
||||
if (i) html += '<span class="sep" aria-hidden="true">›</span>';
|
||||
html += '<span class="seg ' + esc(s.state) + '"><i></i><span>' + esc(s.title) + '</span></span>';
|
||||
tip += s.title + ': ' + s.text + (s.detail ? ' (' + s.detail + ')' : '') + '\n';
|
||||
});
|
||||
html += '<span class="ago">' + (h.ago ? 'ответ НРД ' + esc(h.ago) : 'ответа НРД ещё не было') + '</span>';
|
||||
el.innerHTML = html;
|
||||
el.className = 'sys-status nsd-chain ' + h.state;
|
||||
el.title = tip + 'Подробно — «Статус». Обновляется каждые 30 с.';
|
||||
}).catch(function(){
|
||||
el.className = 'sys-status nsd-chain warn';
|
||||
el.title = 'Нет связи с bj — состояние НРД не обновляется';
|
||||
});
|
||||
}
|
||||
setInterval(upd, 30000);
|
||||
})();
|
||||
// Онлайн-статус заявок в работе: строки с data-live и data-active опрашиваются, пока ждут.
|
||||
// Карточка заявки (live-big с data-sig): изменилось что-то кроме строки статуса — страница
|
||||
// перерисовывается сама; если оператор уже начал заполнять форму — только предложение обновить.
|
||||
|
||||
Reference in New Issue
Block a user