Мониторинг связи с НРД: участки Шлюз/НРД/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
+3 -36
View File
@@ -6,7 +6,6 @@ import (
"fmt"
"log"
"net/http"
"os"
"time"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2m"
@@ -100,7 +99,7 @@ func NewServer(cfg ServerConfig) (*Server, error) {
log.Printf("lk-gateway: профиль ИШ %q неизвестен (%v) — fallback mock", s.NSD.Profile, perr)
} else {
prof.IGWBaseURL = s.NSD.IGWBaseURL // override URL из setup.json
cl := igw.NewClient(s.NSD.IGWBaseURL)
cl := igw.NewClient(s.NSD.IGWBaseURL, igw.WithDeliveryHook(nsdDelivery))
sender = nsdadapter.NewSender(prof, igwClientAdapter{c: cl})
igwClient = cl
// Канал ИШ резолвится по составному коду <канал>+<депонент>.
@@ -215,6 +214,7 @@ func NewServer(cfg ServerConfig) (*Server, error) {
registerOutgoing(mux, adminTpl, outDeps)
registerManual(mux, rc, svc) // ручной ввод исходящих переводов по бумажным поручениям
registerJournal(mux, adminTpl, svc, incomingStore)
registerNSDHealth(mux, rc) // связь с НРД: шапка (json) и «Проверить связь сейчас»
registerLKAPI(mux, rc, svc) // API для модуля личного кабинета клиента (bj-lk)
registerHealth(mux)
registerQuikOps(mux, rc, svc)
@@ -322,7 +322,7 @@ func (s *Server) Run(ctx context.Context) error {
// Авто-обновление справочника участников M2M раз в сутки (только реальный
// ИШ). Тик суточный — в heartbeat-монитор не регистрируем, только recover.
go superviseLoop(ctx, "handbook-refresh", s.rc, s.handbookRefreshLoop)
go superviseLoop(ctx, "nsd-health", s.rc, s.nsdHealthLoop) // связь с НРД: проверка раз в 10 мин (справочник), история, автоповтор
// QUIK МНП: автоматический цикл M2M (регистрация до отправки — хук в
// SendBuiltRequest; подпись/снятие/зачисление по MS010 — опросчик).
@@ -470,39 +470,6 @@ func registerHandbookRequest(mux *http.ServeMux, igwClient *igw.Client, channel
})
}
// handbookRefreshLoop раз в сутки обновляет справочник участников M2M, чтобы он
// оставался актуальным. Первый запрос — вскоре после старта, если справочника
// ещё нет или он старше суток. Работает только в реальном режиме ИШ.
func (s *Server) handbookRefreshLoop(ctx context.Context) {
if s.igwClient == nil || s.igwChannel == "" {
<-ctx.Done() // нечего делать в mock-режиме, но не выходим (иначе supervise перезапустит)
return
}
const interval = 24 * time.Hour
initial := time.Minute
if fi, err := os.Stat(handbookFile); err == nil && time.Since(fi.ModTime()) < interval {
initial = interval - time.Since(fi.ModTime()) // справочник свежий — ждём до суток
}
timer := time.NewTimer(initial)
defer timer.Stop()
log.Printf("lk-gateway: авто-обновление справочника M2M включено (интервал %s, первый запрос через %s)", interval, initial.Round(time.Second))
for {
select {
case <-ctx.Done():
return
case <-timer.C:
cctx, cancel := context.WithTimeout(ctx, 20*time.Second)
if id, err := sendHandbookRequest(cctx, s.igwClient, s.igwChannel); err != nil {
log.Printf("lk-gateway: авто-обновление справочника: %v", err)
} else {
log.Printf("lk-gateway: авто-обновление справочника: запрос отправлен (#M2MHR id=%s)", id)
}
cancel()
timer.Reset(interval)
}
}
}
// registerSetCallback — служебный POST /admin/api/callback-url для
// эмулятора ЛК, чтобы сообщить gateway свой URL. Если URL уже сохранён
// в runtime-конфиге (пользователь явно настроил его через UI), запрос