Поллер: отлов ответов НРД (анкета участника и неизвестные документы)
После отправки анкеты участника (#M2MPF) НРД присылает ответ, схему которого мы ещё не разбираем. Фаза 1 — отловить его на боевом контуре: - входящий с корнем M2MTransferParticipantForm сохраняется как подлинный XML и логируется отдельно (ответ НРД на анкету); - любой неизвестный M2M-документ в default теперь тоже сохраняется (раньше молча пропускался), чтобы увидеть реальный тип/схему. Файлы: /var/lib/bj/.bj/nsd-replies/<время>-<tag>-id<N>-<тип>.xml (последние 100). Разбор ответа встроим в систему, когда увидим реальный документ на бою. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
This commit is contained in:
@@ -20,8 +20,8 @@
|
|||||||
"version": "2026.10.21.6"
|
"version": "2026.10.21.6"
|
||||||
},
|
},
|
||||||
"bj-server": {
|
"bj-server": {
|
||||||
"fingerprint": "6cf5639b24a45d8c39e1896ab11301d00de8eb4d40f4364be70c31fb2b5bb6df",
|
"fingerprint": "f47475987712791a70a636cfa23a2a8d28f1e1ad898d3ff505c595f828d64a0b",
|
||||||
"version": "2026.10.21.10"
|
"version": "2026.10.21.11"
|
||||||
},
|
},
|
||||||
"crypto-service.jar": {
|
"crypto-service.jar": {
|
||||||
"fingerprint": "2c4fd536bc9b1c20c69a70e8dcd374a61847b263aeb2e474c45debb01fbd8233",
|
"fingerprint": "2c4fd536bc9b1c20c69a70e8dcd374a61847b263aeb2e474c45debb01fbd8233",
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package lkgateway
|
||||||
|
|
||||||
|
// nsd_reply_capture.go — «отлов» ответов НРД, которые bj пока не разбирает
|
||||||
|
// (фаза 1). После отправки анкеты участника (#M2MPF) и других новых документов
|
||||||
|
// НРД присылает ответ; его тип/схему фиксируем на боевом контуре: сохраняем
|
||||||
|
// подлинный XML в файл и явно логируем, чтобы потом встроить разбор в систему
|
||||||
|
// для всех клиентов.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdadapter/igw"
|
||||||
|
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdxml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const nsdRepliesDir = "/var/lib/bj/.bj/nsd-replies"
|
||||||
|
|
||||||
|
var nsdReplyTypeRe = regexp.MustCompile(`[^A-Za-z0-9_.-]+`)
|
||||||
|
var nsdReplyRootRe = regexp.MustCompile(`<(?:[A-Za-z0-9_]+:)?([A-Za-z0-9_]+)[\s>]`)
|
||||||
|
|
||||||
|
// captureNSDReply сохраняет подлинный XML входящего пакета НРД, который bj пока не
|
||||||
|
// разбирает, и возвращает путь к файлу и имя корневого элемента. Храним последние
|
||||||
|
// 100 файлов, чтобы каталог не рос без предела.
|
||||||
|
func captureNSDReply(p igw.Package, docXML []byte, tag string) (path, root string) {
|
||||||
|
root = nsdReplyRoot(docXML)
|
||||||
|
if err := os.MkdirAll(nsdRepliesDir, 0o750); err != nil {
|
||||||
|
log.Printf("lk-gateway: nsd-replies: mkdir: %v", err)
|
||||||
|
return "", root
|
||||||
|
}
|
||||||
|
name := fmt.Sprintf("%s-%s-id%d-%s.xml", time.Now().Format("20060102-150405"), tag, p.ID, nsdReplySanitize(p.Type))
|
||||||
|
path = filepath.Join(nsdRepliesDir, name)
|
||||||
|
if err := os.WriteFile(path, docXML, 0o640); err != nil {
|
||||||
|
log.Printf("lk-gateway: nsd-replies: запись %s: %v", name, err)
|
||||||
|
return "", root
|
||||||
|
}
|
||||||
|
nsdRepliesTrim(100)
|
||||||
|
return path, root
|
||||||
|
}
|
||||||
|
|
||||||
|
// nsdReplyRoot — имя корневого элемента XML (без префикса), для лога.
|
||||||
|
func nsdReplyRoot(docXML []byte) string {
|
||||||
|
utf := string(nsdxml.DecodeWindows1251(docXML))
|
||||||
|
if m := nsdReplyRootRe.FindStringSubmatch(utf); m != nil {
|
||||||
|
return m[1]
|
||||||
|
}
|
||||||
|
return "?"
|
||||||
|
}
|
||||||
|
|
||||||
|
func nsdReplySanitize(t string) string {
|
||||||
|
t = nsdReplyTypeRe.ReplaceAllString(t, "_")
|
||||||
|
if t == "" {
|
||||||
|
return "notype"
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// nsdRepliesTrim оставляет keep самых свежих файлов (имена начинаются с метки времени).
|
||||||
|
func nsdRepliesTrim(keep int) {
|
||||||
|
ents, err := os.ReadDir(nsdRepliesDir)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var names []string
|
||||||
|
for _, e := range ents {
|
||||||
|
if !e.IsDir() {
|
||||||
|
names = append(names, e.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(names) <= keep {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sort.Strings(names) // по возрастанию метки времени — старые первыми
|
||||||
|
for _, n := range names[:len(names)-keep] {
|
||||||
|
_ = os.Remove(filepath.Join(nsdRepliesDir, n))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -264,8 +264,17 @@ func (s *Server) applyIncoming(ctx context.Context, p igw.Package) error {
|
|||||||
log.Printf("lk-gateway: поллер СПРАВОЧНИК M2M получен id=%d: участников %d, мест расчётов %d (состав изменился)",
|
log.Printf("lk-gateway: поллер СПРАВОЧНИК M2M получен id=%d: участников %d, мест расчётов %d (состав изменился)",
|
||||||
p.ID, len(hb.Participants.Participants), len(hb.SettlementPlaces.Places))
|
p.ID, len(hb.Participants.Participants), len(hb.SettlementPlaces.Places))
|
||||||
return nil
|
return nil
|
||||||
|
case strings.Contains(doc, "M2MTransferParticipantForm"):
|
||||||
|
// Ответ НРД на нашу анкету участника (#M2MPF): подтверждение регистрации.
|
||||||
|
// Схему ответа фиксируем на боевом контуре — сохраняем подлинный XML, чтобы
|
||||||
|
// затем встроить разбор в систему (для всех клиентов).
|
||||||
|
path, root := captureNSDReply(p, unpacked.DocXML, "participant-form")
|
||||||
|
log.Printf("lk-gateway: поллер ОТВЕТ НРД на анкету участника id=%d (%s): корень <%s>, сохранён %s", p.ID, p.Type, root, path)
|
||||||
|
return nil
|
||||||
default:
|
default:
|
||||||
log.Printf("lk-gateway: поллер пакет id=%d (%s) — неизвестный M2M-документ, пропуск", p.ID, p.Type)
|
// Неизвестный документ НРД — тоже «отлавливаем»: сохраняем подлинный XML для разбора.
|
||||||
|
path, root := captureNSDReply(p, unpacked.DocXML, "unknown")
|
||||||
|
log.Printf("lk-gateway: поллер пакет id=%d (%s) — неизвестный M2M-документ <%s>, сохранён %s", p.ID, p.Type, root, path)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user