feat: живой цикл M2M с НРД + мастер установки ключа на флешку

Инфраструктура M2M (живой обмен с НРД через ИШ):
- обработка M2MTransferResponse: ERROR(M2Mxx) → заявка Отклонена, сохранение
  ответа; INFO → ждём Decision; идемпотентность поллера
- fallback-корреляция ответов с нулевым GUID (M2M14/M2M17) по FIFO
- сырой XML ответа НРД в карточке заявки (для пересылки в ТП)
- тестовый пакет роботу приведён к эталону m2m_robot_samples (CostInfo=Yes,
  4 бумаги, IsolationStatus, DocumentSeries=сценарий); override паспорта
- редирект из теста сразу в карточку заявки

Мастер установки ключа Валидаты на флешку (admin/setup/keywizard):
- пошаговый: загрузка .7z+пароль → выбор флешки → запись → справочник
  сертификатов (CRL) → перезапуск+проверка ИШ → готово
- привилегированный воркер (bj-keymedia) в host-namespace через файл-обмен,
  bj-server остаётся в песочнице
- сохранение структуры профиля архива (spr<N>), перечисление съёмных USB

Прочее:
- пакет-доказательство для ТП НРД + форма регистрации участника M2M
- эталонные образцы робота (DOC/m2m_robot_samples)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
zuevav
2026-06-19 00:03:21 +03:00
parent 6e503433d4
commit 9737c787f9
110 changed files with 10771 additions and 1690 deletions
+18 -7
View File
@@ -40,15 +40,21 @@ func (s *Sender) Send(ctx context.Context, req *m2m.M2MTransferRequest) (*m2m.M2
if err := req.Validate(); err != nil {
return nil, fmt.Errorf("nsdadapter: req.Validate: %w", err)
}
body, err := nsdxml.Marshal(req)
if err != nil {
return nil, fmt.Errorf("nsdadapter: marshal Request: %w", err)
}
pkgType, err := RouteToPackageType(KindTransferRequest)
if err != nil {
return nil, err
}
pkgID, err := s.client.SendPackage(ctx, s.profile.Channel, string(pkgType), body)
// ИШ ждёт ZIP-архив (Type=ARCHIVE): doc.xml + config.xml с типом пакета.
// Сырой XML ИШ отвергает ("Bad signature" при распаковке). PackRequest
// собирает корректный ZIP с config.xml (#M2MTR).
body, err := igw.PackRequest(req, "M2MTransferRequest.xml")
if err != nil {
return nil, fmt.Errorf("nsdadapter: PackRequest: %w", err)
}
// ИШ резолвит канал по СОСТАВНОМУ коду: <код канала>+<код депонента>
// (так его формирует ИШ при создании канала: напр. TEST3+MC0413600000).
channel := s.profile.Channel + string(req.Header.SenderCode)
pkgID, err := s.client.SendPackage(ctx, channel, string(pkgType), body)
if err != nil {
return nil, fmt.Errorf("nsdadapter: SendPackage: %w", err)
}
@@ -67,7 +73,7 @@ func (s *Sender) SendDecision(ctx context.Context, decision *m2m.M2MTransferDeci
if err := decision.Validate(); err != nil {
return fmt.Errorf("nsdadapter: decision.Validate: %w", err)
}
body, err := nsdxml.Marshal(decision)
xmlBytes, err := nsdxml.Marshal(decision)
if err != nil {
return fmt.Errorf("nsdadapter: marshal Decision: %w", err)
}
@@ -75,7 +81,12 @@ func (s *Sender) SendDecision(ctx context.Context, decision *m2m.M2MTransferDeci
if err != nil {
return err
}
if _, err := s.client.SendPackage(ctx, s.profile.Channel, string(pkgType), body); err != nil {
body, err := igw.PackXML(xmlBytes, "M2MTransferDecision.xml", string(pkgType))
if err != nil {
return fmt.Errorf("nsdadapter: PackXML: %w", err)
}
channel := s.profile.Channel + string(decision.Header.SenderCode)
if _, err := s.client.SendPackage(ctx, channel, string(pkgType), body); err != nil {
return fmt.Errorf("nsdadapter: SendPackage: %w", err)
}
return nil