diff --git a/internal/lkgateway/admin.go b/internal/lkgateway/admin.go index 34e2469..d3603fe 100644 --- a/internal/lkgateway/admin.go +++ b/internal/lkgateway/admin.go @@ -153,17 +153,21 @@ var templateFuncs = template.FuncMap{ "looksErr": looksErr, "intList": func(v ...int) []int { return v }, "nsdSegTip": nsdSegTip, - "devOn": authTemplateFuncs["devOn"], - "devLicensed": authTemplateFuncs["devLicensed"], - "mod": func(f string) bool { return globalRC == nil || moduleAllowed(globalRC, f) }, // модуль в лицензии - "modTitle": moduleTitle, // название признака лицензии по-человечески - "isIIS": incomingIsIIS, // входящий перевод на ИИС - "depoSubs": func() any { return depoWizardSubs }, // экраны шага 3 мастера - "authOpen": authOpen, - "internalDocs": func() bool { return internalDocsBuilt }, - "roleTitle": roleTitle, - "stageCls": func(r depoQueueRow) string { c, _ := r.StageRu(); return c }, - "stageText": func(r depoQueueRow) string { _, t := r.StageRu(); return t }, + "nsdSegPop": nsdSegPop, + // participantOK / participantBanner — анкета участника M2M принята НРД; плашка наверху раздела НРД, пока нет. + "participantOK": participantAccepted, + "participantBanner": participantBanner, + "devOn": authTemplateFuncs["devOn"], + "devLicensed": authTemplateFuncs["devLicensed"], + "mod": func(f string) bool { return globalRC == nil || moduleAllowed(globalRC, f) }, // модуль в лицензии + "modTitle": moduleTitle, // название признака лицензии по-человечески + "isIIS": incomingIsIIS, // входящий перевод на ИИС + "depoSubs": func() any { return depoWizardSubs }, // экраны шага 3 мастера + "authOpen": authOpen, + "internalDocs": func() bool { return internalDocsBuilt }, + "roleTitle": roleTitle, + "stageCls": func(r depoQueueRow) string { c, _ := r.StageRu(); return c }, + "stageText": func(r depoQueueRow) string { _, t := r.StageRu(); return t }, "deref": func(v *float64) float64 { if v == nil { return 0 diff --git a/internal/lkgateway/nsdhealth.go b/internal/lkgateway/nsdhealth.go index 73d169b..772dbc0 100644 --- a/internal/lkgateway/nsdhealth.go +++ b/internal/lkgateway/nsdhealth.go @@ -15,6 +15,7 @@ import ( "context" "encoding/json" "fmt" + "html/template" "log" "net/http" "os" @@ -234,12 +235,17 @@ func nsdHealthNow(rc *RuntimeConfig, now time.Time) NSDHealth { nsdH.Unlock() h.Segs = []NSDSeg{gw, ns, m, sd} + // Общий цвет — по участкам с известным состоянием: «проверка ещё не выполнялась» не делает + // серым работающий обмен. Серый — только когда по известным участкам связь не подтверждена. h.State = segOK for _, s := range h.Segs { - if segRank(s.State) > segRank(h.State) { + if s.State != segUnknown && segRank(s.State) > segRank(h.State) { h.State = s.State } } + if h.State == segOK && !nsdLinkOK(h) { + h.State = segUnknown + } h.Ago = agoText(h.LastAnswer, now) return h } @@ -553,6 +559,23 @@ func nsdClientNotice(h NSDHealth) string { return "" } +// nsdSegPop — всплывающая панель цепочки в шапке: участки с цветной точкой. Строки «последний ответ» +// нет — время ответа уже в участке «НРД». +func nsdSegPop(h NSDHealth) template.HTML { + esc := template.HTMLEscapeString + var b strings.Builder + b.WriteString(``) + for _, s := range h.Segs { + b.WriteString(`` + esc(s.Title) + `` + esc(s.Text)) + if s.Detail != "" { + b.WriteString(`` + esc(s.Detail) + ``) + } + b.WriteString(``) + } + b.WriteString(`Подробно — «Статус»`) + return template.HTML(b.String()) +} + // nsdSegTip — подсказка к цепочке в шапке: все участки построчно. func nsdSegTip(h NSDHealth) string { var b strings.Builder diff --git a/internal/lkgateway/participant_reg.go b/internal/lkgateway/participant_reg.go index 4ee377e..55f17c8 100644 --- a/internal/lkgateway/participant_reg.go +++ b/internal/lkgateway/participant_reg.go @@ -6,6 +6,7 @@ package lkgateway import ( "encoding/json" + "html/template" "os" "strings" "sync" @@ -138,3 +139,31 @@ func recordParticipantReply(status string, resp *m2m.M2MTransferResponse, rawFil r.RawFile = rawFile }) } + +// participantAccepted — последняя отправленная анкета принята НРД. +func participantAccepted() bool { + r := loadParticipantReg() + return !r.RespAt.IsZero() && !strings.EqualFold(r.Status, "ERROR") +} + +// participantBanner — плашка наверху раздела НРД: анкета не отправлена, ждёт ответа или отклонена. +// Пропадает, когда НРД принял анкету. Анкету шлём только в боевом контуре — в тестовом плашки нет. +func participantBanner() template.HTML { + if participantAccepted() || contourOf(currentNSDProfile()) != "prod" { + return "" + } + esc := template.HTMLEscapeString + r := loadParticipantReg() + cls, head, body := "stc-err", "Анкета участника M2M не отправлена", + "Без регистрации участника НРД не принимает запросы на переводы. Проверьте анкету и отправьте её." + switch { + case r.SentAt.IsZero(): + case r.RespAt.IsZero(): + cls, head, body = "stc-run", "Анкета участника M2M ждёт ответа НРД", "Отправлена "+r.SentAt.In(listMSK).Format("02.01.2006 15:04")+". Плашка пропадёт, когда НРД примет анкету." + case strings.EqualFold(r.Status, "ERROR"): + cls, head = "stc-err", "НРД отклонил анкету участника M2M" + body = r.RespAt.In(listMSK).Format("02.01.2006 15:04") + ": " + r.RespText + ". Исправьте и отправьте снова." + } + return template.HTML(`
✓ Анкета принята НРД — организация зарегистрирована в сервисе M2M.
{{end}}НРД требует анкету участника перед началом работы: без регистрации НРД не принимает запросы на переводы. bj заполняет анкету по реквизитам организации, даёт проверить и отправляет в НРД через шлюз (или её можно отправить письмом на M2MOST@nsd.ru).