diff --git a/internal/lkgateway/participant_form.go b/internal/lkgateway/participant_form.go index b0a95bc..73d73cf 100644 --- a/internal/lkgateway/participant_form.go +++ b/internal/lkgateway/participant_form.go @@ -5,10 +5,12 @@ package lkgateway // участников M2M, иначе сервис МОСТ отбивает запросы кодом M2M14 «Код ЭДО НРД // отправителя отсутствует в справочнике участников M2M». Здесь bj формирует // заполненную анкету (предзаполнение из автоопределения DEPO), даёт проверить -// (валидация + показ XML в windows-1251) и предлагает отправить её в НРД обычным -// каналом — письмом на M2MOST@nsd.ru с вложением (не через M2M-шлюз). +// (валидация + показ XML в windows-1251) и отправляет её в НРД: в боевом контуре — +// через ИШ пакетом #M2MPF (businessLine=MOST, подпись боевым контейнером на стороне +// ИШ), либо запасным каналом — письмом на M2MOST@nsd.ru с вложением. import ( + "context" "encoding/base64" "fmt" "html" @@ -18,6 +20,8 @@ import ( "time" "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2m" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdadapter" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdadapter/igw" "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdxml" ) @@ -215,7 +219,7 @@ func sendSMTPAttach(s SMTPSettings, subject, body, filename string, attach []byt // registerParticipantForm — страница «Регистрация участника M2M»: форма, проверка, // скачивание анкеты, отправка письмом на M2MOST@nsd.ru. -func registerParticipantForm(mux *http.ServeMux, rc *RuntimeConfig) { +func registerParticipantForm(mux *http.ServeMux, rc *RuntimeConfig, igwClient *igw.Client, igwChannel string) { mux.HandleFunc("/admin/setup/participant-form", func(w http.ResponseWriter, r *http.Request) { d := participantFormPrefill(rc) action := "" @@ -277,12 +281,41 @@ func registerParticipantForm(mux *http.ServeMux, rc *RuntimeConfig) { } } + if action == "send_igw" { + switch { + case len(errs) > 0: + flash, flashKind = "Анкета не прошла проверку — исправьте поля ниже.", "err" + case igwClient == nil || igwChannel == "": + flash, flashKind = "Боевой ИШ не настроен (раздел «НРД», профиль prod-*). Отправка через шлюз недоступна — переключитесь в боевой контур.", "err" + default: + body, encErr := participantFormBytes(d) + if encErr != nil { + flash, flashKind = "кодировка windows-1251: "+encErr.Error(), "err" + break + } + pkg, _ := nsdadapter.RouteToPackageType(nsdadapter.KindParticipantForm) + zipBody, zErr := igw.PackXML(body, "M2MTransferParticipantForm.xml", string(pkg)) + if zErr != nil { + flash, flashKind = "упаковка пакета: "+zErr.Error(), "err" + break + } + ctx, cancel := context.WithTimeout(r.Context(), 20*time.Second) + id, sErr := igwClient.SendPackage(ctx, igwChannel, string(pkg), zipBody) + cancel() + if sErr != nil { + flash, flashKind = "отправка через ИШ не удалась: "+sErr.Error(), "err" + break + } + flash, flashKind = "Анкета отправлена через ИШ ("+string(pkg)+", пакет id="+id+"). Ждём подтверждения регистрации от НРД.", "ok" + } + } + xmlPreview := participantFormXML(d) - participantFormRender(w, d, errs, action, flash, flashKind, xmlPreview) + participantFormRender(w, d, errs, action, flash, flashKind, xmlPreview, igwClient != nil && igwChannel != "") }) } -func participantFormRender(w http.ResponseWriter, d participantFormData, errs []string, action, flash, flashKind, xmlPreview string) { +func participantFormRender(w http.ResponseWriter, d participantFormData, errs []string, action, flash, flashKind, xmlPreview string, igwReady bool) { w.Header().Set("Content-Type", "text/html; charset=utf-8") inp := func(name, label, val, ph string) string { return `` @@ -301,6 +334,11 @@ func participantFormRender(w http.ResponseWriter, d participantFormData, errs [] } flashHTML = `
` + html.EscapeString(flash) + `
` } + igwBtn := `` + igwStatus := `

Боевой ИШ: настроен — анкету можно отправить через шлюз (#M2MPF).

` + if !igwReady { + igwStatus = `

Боевой ИШ: не настроен (раздел «НРД», профиль prod-*). Пока доступны «Скачать» и «Отправить письмом».

` + } page := `Регистрация участника M2M

Регистрация участника сервиса M2M (МОСТ)

Требование НРД при подключении: зарегистрировать организацию в справочнике участников M2M. -Без регистрации сервис отбивает запросы кодом M2M14. Отправляется обычным каналом — письмом -на ` + participantFormRecipient + ` с вложенной анкетой (не через M2M-шлюз). Поля предзаполнены -из автоопределения DEPO — проверьте юридические реквизиты.

+Без регистрации сервис отбивает запросы кодом M2M14. В боевом контуре анкета подаётся через ИШ +пакетом #M2MPF (businessLine=MOST, подпись боевым контейнером на стороне ИШ); письмо на +` + participantFormRecipient + ` оставлено запасным каналом. Поля предзаполнены из +автоопределения DEPO — проверьте юридические реквизиты.

` + flashHTML + vErr + `
Организация @@ -332,12 +371,13 @@ pre{background:#f6f8fa;border:1px solid #ddd;border-radius:8px;padding:12px;over
` + inp("depo", "Код депозитария (DepositoryPlace)", d.DepoCode, "MC0000000000") + inp("broker", "Код брокера (BrokerPlace, если брокер)", d.BrokerCode, "оставьте пустым, если не брокер") + `
Отправка -` + inp("to", "Кому (НРД)", participantFormRecipient, participantFormRecipient) + ` +` + igwStatus + inp("to", "Кому (НРД), запасной канал", participantFormRecipient, participantFormRecipient) + `
- +` + igwBtn + ` +

Предпросмотр анкеты (windows-1251)

diff --git a/internal/lkgateway/server.go b/internal/lkgateway/server.go index 913db44..803890c 100644 --- a/internal/lkgateway/server.go +++ b/internal/lkgateway/server.go @@ -232,9 +232,9 @@ func NewServer(cfg ServerConfig) (*Server, error) { registerSetCallback(mux, svc, rc) registerSeedListing(mux, store) registerHandbookRequest(mux, igwClient, igwChannel) - registerChainTest(mux, rc, igwClient != nil) // «Проверить всю цепочку» (мастер, глава «Запуск») - registerParticipantForm(mux, rc) // анкета участника M2M (регистрация в НРД, participant_form.go) - registerClientProfile(mux, rc) // профиль клиента: выгрузка эталона и предзаполнение (client_profile.go) + registerChainTest(mux, rc, igwClient != nil) // «Проверить всю цепочку» (мастер, глава «Запуск») + registerParticipantForm(mux, rc, igwClient, igwChannel) // анкета участника M2M (регистрация в НРД, participant_form.go) + registerClientProfile(mux, rc) // профиль клиента: выгрузка эталона и предзаполнение (client_profile.go) return &Server{ cfg: cfg, @@ -305,10 +305,10 @@ func (s *Server) Run(ctx context.Context) error { // Все фоновые горутины — под supervise: паника ловится recover(), пишется // в лог и в ленту, горутина перезапускается. Один сбой не роняет сервис. go superviseLoop(ctx, "consume-decisions", s.rc, s.consumeDecisions) - go superviseLoop(ctx, "igw-watch", s.rc, s.igwWatchLoop) // сторож ИШ НРД: оповещения и автоперезапуск - go superviseLoop(ctx, "units-watch", s.rc, s.unitsWatchLoop) // службы модулей и ключей Валидаты: письмо администратору + go superviseLoop(ctx, "igw-watch", s.rc, s.igwWatchLoop) // сторож ИШ НРД: оповещения и автоперезапуск + go superviseLoop(ctx, "units-watch", s.rc, s.unitsWatchLoop) // службы модулей и ключей Валидаты: письмо администратору go superviseLoop(ctx, "igate-share", s.rc, s.igateShareSyncLoop) // папки шлюза «через домен»: список доступа = группы и сотрудники bj - go handbookLoop(ctx, s.rc) // справочник участников M2M с сервера лицензий (для установок без шлюза НРД) + go handbookLoop(ctx, s.rc) // справочник участников M2M с сервера лицензий (для установок без шлюза НРД) // Поллер входящих от НРД (только в реальном режиме ИШ): забирает // ответы робота/контрагента и применяет их через ApplyDecision. diff --git a/internal/nsdadapter/router.go b/internal/nsdadapter/router.go index 8420868..7f4fdff 100644 --- a/internal/nsdadapter/router.go +++ b/internal/nsdadapter/router.go @@ -10,6 +10,7 @@ const ( PackageM2MTransferRequest PackageType = "#M2MTR" PackageM2MTransferDecision PackageType = "#M2MTD" PackageM2MTransferError PackageType = "#M2MER" + PackageM2MParticipantForm PackageType = "#M2MPF" PackageSUBBR PackageType = "SUBBR" PackageSUBER PackageType = "SUBER" PackageSUB16 PackageType = "SUB16" @@ -40,7 +41,7 @@ func RouteToPackageType(kind MessageKind) (PackageType, error) { case KindHandbookRequest: return PackageAssetsInvestment, nil case KindParticipantForm: - return PackageAssetsInvestment, nil + return PackageM2MParticipantForm, nil } return "", fmt.Errorf("nsdadapter: не задан PackageType для kind=%q", kind) }