Мониторинг связи с НРД: участки Шлюз/НРД/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:
zuevav
2026-09-25 16:17:25 +03:00
parent a0f6ce9f7b
commit 4cbefa41d5
18 changed files with 1075 additions and 62 deletions
+35 -3
View File
@@ -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 {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[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): изменилось что-то кроме строки статуса — страница
// перерисовывается сама; если оператор уже начал заполнять форму — только предложение обновить.