cfc97787d0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
176 lines
15 KiB
HTML
176 lines
15 KiB
HTML
{{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"}}раз в час (и по кнопке «Проверить связь сейчас») запрос справочника участников 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>
|
||
<thead><tr><th></th><th>Подсистема</th><th>Состояние</th><th>Сообщение</th><th>Детали</th></tr></thead>
|
||
<tbody>
|
||
{{range .Checks}}
|
||
<tr>
|
||
<td><span class="dot {{if .OK}}ok{{else}}err{{end}}"></span></td>
|
||
<td><strong>{{.Name}}</strong></td>
|
||
<td>{{if .OK}}<span style="color: var(--ok)">OK</span>{{else}}<span style="color: var(--err)">FAIL</span>{{end}}</td>
|
||
<td>{{.Message}}</td>
|
||
<td><code>{{.Detail}}</code></td>
|
||
</tr>
|
||
{{end}}
|
||
</tbody>
|
||
</table>
|
||
<p class="muted" style="margin-top: 16px">Проверка выполнена в {{.CheckedAt.Format "15:04:05 02.01.2006"}}.</p>
|
||
</div>
|
||
|
||
{{/* Диагностика: службы, здоровье фоновых компонентов, справочник, тестовые прогоны (раньше — Настройка → Диагностика) */}}
|
||
<div class="card" id="sec-units"><a id="diag"></a>
|
||
<h2><span class="dot {{$bad := 0}}{{range .Units}}{{if not .OK}}{{$bad = 1}}{{end}}{{end}}{{if eq $bad 1}}err{{else}}ok{{end}}"></span>Службы</h2>
|
||
<p class="muted">Ядро и модули работают отдельными службами: если служба модуля не запущена, его раздел в bj отвечает ошибкой связи. Здесь видно состояние и можно перезапустить службу, не заходя на сервер.</p>
|
||
<div class="tw" style="margin-top:8px"><table>
|
||
<thead><tr>{{if devOn}}<th>Служба</th>{{end}}<th>Что делает</th><th>Состояние</th><th>С</th><th></th></tr></thead>
|
||
<tbody>{{range .Units}}<tr>
|
||
{{if devOn}}<td style="white-space:nowrap"><code>{{.Unit}}</code></td>{{end}}
|
||
<td>{{.Title}}</td>
|
||
<td>{{if .OK}}<span class="badge ok">работает</span>{{else}}<span class="badge err">не работает{{if devOn}}: {{.State}}{{with .Sub}} / {{.}}{{end}}{{with .Result}} ({{.}}){{end}}{{end}}</span>{{end}}</td>
|
||
<td class="muted">{{if .Since}}{{.Since}}{{else}}—{{end}}</td>
|
||
<td style="white-space:nowrap"><form method="post" action="/admin/setup/units/restart" style="margin:0" onsubmit="return confirm('{{if .Core}}Перезапустить bj? Страница настроек на несколько секунд станет недоступна.{{else}}Перезапустить «{{.Title}}»?{{end}}');">
|
||
<input type="hidden" name="section" value="sec-units"><input type="hidden" name="unit" value="{{.Unit}}">
|
||
<button type="submit" class="btn btn-secondary">Перезапустить</button></form></td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
</div>
|
||
|
||
|
||
<div class="card">
|
||
<h2>Здоровье сервиса</h2>
|
||
<p class="muted">Фоновые компоненты под самоконтролем: при сбое перезапускаются автоматически, при зависании — перезапуск всего сервиса и запись в «Новости». Ниже — живой статус.</p>
|
||
{{if .Health}}
|
||
<div class="tw" style="margin-top:12px">
|
||
<table>
|
||
<thead><tr><th>Компонент</th><th>Состояние</th><th>Последняя отметка</th><th>Последняя ошибка</th></tr></thead>
|
||
<tbody>
|
||
{{range .Health}}
|
||
<tr>
|
||
<td>{{healthLabel .Name}}</td>
|
||
<td>{{if .Healthy}}<span class="sys-status" style="--dot:#16a34a">работает</span>{{else}}<span class="sys-status" style="--dot:#dc2626">не отвечает</span>{{end}}</td>
|
||
<td class="mono">{{dur .Age}} назад</td>
|
||
<td class="muted">{{if .LastErr}}{{.LastErr}}{{else}}—{{end}}</td>
|
||
</tr>
|
||
{{end}}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{{else}}
|
||
<p class="muted" style="margin-top:10px">Компоненты ещё не зарегистрированы (сервис только запущен).</p>
|
||
{{end}}
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h2>Справочник участников M2M</h2>
|
||
<p class="muted">Справочник участников M2M из НРД: по нему bj узнаёт код участника по ИНН контрагента. bj запрашивает его сам; кнопка — запросить сейчас.</p>
|
||
<form method="post" action="/admin/setup/handbook-request" style="margin-top:12px"><button type="submit" class="btn">Запросить справочник у НРД</button></form>
|
||
<p class="muted" style="font-size:12px;margin-top:8px">Ответ НРД приходит через 30–60 секунд.</p>
|
||
</div>
|
||
|
||
{{if devOn}} <div class="card">
|
||
<h2>Тестовый пакет роботу НРД</h2>
|
||
<p class="muted">Робот <code>MC0012500000</code> эмулирует вторую сторону перевода. Выберите сценарий — bj-server отправит эталонный запрос через шлюз НРД, ответ придёт во входящие. Требуется настроенный шлюз НРД и ключ подписи.</p>
|
||
<form method="post" action="/admin/setup/test-nsd" style="margin-top:12px;display:flex;gap:8px;flex-wrap:wrap;align-items:center">
|
||
<select name="scenario">
|
||
<option value="2001">2001 — Принять все бумаги</option>
|
||
<option value="2002">2002 — Принять частично</option>
|
||
<option value="1111">1111 — Ответ с отказом</option>
|
||
<option value="3333">3333 — Робот принимающая сторона</option>
|
||
</select>
|
||
<button type="submit" class="btn">Отправить роботу</button>
|
||
</form>
|
||
<p class="muted" style="margin-top:6px;font-size:12px">Ответ робота — асинхронно (~30-60 сек) во входящие шлюза НРД.</p>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h2>Сквозной тестовый прогон (mock/реальный)</h2>
|
||
<p class="muted">Заявка с предзаполненными данными через всю цепочку до финального статуса.</p>
|
||
<form method="post" action="/admin/setup/test-run" style="margin-top:12px"><button type="submit" class="btn">Запустить тестовую заявку</button></form>
|
||
{{if .LastTest}}
|
||
<div style="margin-top:16px;padding:14px;background:var(--card-2);border:1px solid var(--border);border-radius:10px">
|
||
<strong>Последний прогон</strong>
|
||
<table style="margin-top:8px">
|
||
<tbody>
|
||
<tr><td style="width:160px" class="muted">Статус</td><td>{{if .LastTest.OK}}<span style="color:var(--ok)">✓ успешно</span>{{else}}<span style="color:var(--err)">✗ не прошёл</span>{{end}}</td></tr>
|
||
<tr><td class="muted">FSM-статус</td><td><code>{{.LastTest.FinalStatus}}</code></td></tr>
|
||
<tr><td class="muted">ClaimID</td><td><code>{{.LastTest.ClaimID}}</code> {{if .LastTest.ClaimID}}<a href="/admin/claims/{{.LastTest.ClaimID}}">→ карточка</a>{{end}}</td></tr>
|
||
<tr><td class="muted">Сообщение</td><td>{{.LastTest.Message}}</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{{end}}
|
||
</div>{{end}}
|
||
|
||
<div class="card">
|
||
<h2>DEPO: что нужно bj</h2>
|
||
<p class="muted">Снимок объектов bj в базе DEPO: трансферные таблицы и процедуры FTV_M2M* (даты изменения), зависимости поставки Fancy, права логина bj, счётчики по статусам. Данных клиентов в снимке нет. bj снимает его раз в сутки и хранит историю — по ней видно, что и когда меняли в DEPO.</p>
|
||
<a class="btn" href="/admin/status/depo-snapshot" target="_blank" rel="noopener">Снять снимок сейчас (JSON)</a>
|
||
</div>
|
||
|
||
{{if devOn}}{{/* дорожная карта разработки — только в режиме разработки */}}
|
||
<div class="card">
|
||
<h2>Что подключается на следующих этапах</h2>
|
||
<table>
|
||
<tr><td class="muted" style="width:240px">PostgreSQL (схема m2m_core)</td><td>M2-шаг-3: pgx-репозиторий вместо MemoryRepository. Миграция готова — <code>migrations/m2m-core/001__deals.sql</code>.</td></tr>
|
||
<tr><td class="muted">crypto-service · Валидата PKCS#11</td><td>M4: установить АПК «Валидата Клиент L» (<code>zpki</code>, <code>zsdk</code>), выставить <code>BJ_CRYPTO_PROVIDER=validata</code> и путь к <code>libvdpkcs11.so</code>. Проверка — Health PKCS#11 должна вернуть <code>provider=validata, ok=true</code>.</td></tr>
|
||
<tr><td class="muted">nsd-adapter · ИШ НРД</td><td>M3: установить ИШ, выставить <code>BJ_NSD_PROFILE=guest-gost</code> или иной, <code>BJ_NSD_IGW_URL=http://localhost:8080</code>. Без этого сейчас используется <code>nsdadapter/mock</code> с эмуляцией ответов через 3 сек.</td></tr>
|
||
<tr><td class="muted">Реальный ЛК (ESIA Finance)</td><td>M4: согласовать <code>docs/lk-contract/v1/openapi.yaml</code> с командой ЛК, выставить <code>BJ_LK_CALLBACK_URL</code> на реальный адрес. Сейчас callback идёт в встроенный lk-emulator.</td></tr>
|
||
</table>
|
||
</div>
|
||
{{end}}
|
||
{{end}}
|