feat(bj): модуль ЛК на PostgreSQL, обмен с DEPO, боевой ЛК с имитатором НРД, QUIK, НПР

- bj_exchange и «кран» Fancy убраны: данные модуля ЛК в PostgreSQL (схема lk)
- трансферные таблицы FTV_M2M: создание при установке, события, справочник участников
- сверка с DEPO: счёт, ФИО, дата рождения, ИИС по договору, квал-инвестор из опции DEPO
- боевой ЛК ЕСИА Финанс при тестовом ИШ: журнал, отказ через DEPO, статусы в ЛК
- онлайн-лицензирование в bj, план тестирования M2M для контрагента (интерактивный HTML)
- модули QUIK МНП, экспорт QUIK, НПР/УДС; документы и тестовые материалы

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
zuevav
2026-09-15 19:41:12 +03:00
parent 59fc774f08
commit da12a9ec1c
132 changed files with 29969 additions and 1089 deletions
+79
View File
@@ -0,0 +1,79 @@
//go:build live
package lkgateway
// Живой прогон обмена с DEPO через трансферные таблицы (наша копия BJ_DEPO_DATA).
// go test -c -tags live -o depo_live.test ./internal/lkgateway/ ; запуск от bj.
import (
"context"
"database/sql"
"fmt"
"testing"
"time"
)
func TestDepoLiveEvents(t *testing.T) {
rc, err := NewRuntimeConfig("/var/lib/bj/.bj/setup.json")
if err != nil {
t.Fatal(err)
}
guid := fmt.Sprintf("%08X-0000-4000-8000-%012X", time.Now().Unix()&0xFFFFFFFF, time.Now().UnixNano()&0xFFFFFFFFFFFF)
t.Logf("GUID %s", guid)
lines := []depoLine{{Ref: "M2MTEST0001", ISIN: "RU000A107UL4", Code: "RU000A107UL4", Amount: 1, Place: "36MC0413600000F00"}}
ok := depoUpsertOrder(rc, guid, "IN", "MC0034900000", "Погонщикова Виктория Олеговна", "4500 000000", "4500", "000000", lines, "ТЕСТ bj: проверка трансферных таблиц")
t.Logf("FTV_M2M создано: %v", ok)
depoEvent(rc, guid, "", "BJ_RECEIVED", "", "ТЕСТ: входящий получен")
depoEvent(rc, guid, "M2MTEST0001", "BJ_DECISION_REJECT", "M2M04", "ТЕСТ: отказ")
depoEvent(rc, guid, "M2MTEST0001", "BJ_DECISION_REJECT", "M2M04", "ТЕСТ: повтор (не должен записаться)")
db, err := depoEventsDB(rc)
if err != nil {
t.Fatal(err)
}
defer db.Close()
ctx := context.Background()
var n int
db.QueryRowContext(ctx, "SELECT COUNT(*) FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g", sql.Named("g", guid)).Scan(&n)
t.Logf("событий BJ_* записано: %d (ожидаем 2)", n)
var lineN int
db.QueryRowContext(ctx, "SELECT COUNT(*) FROM dbo.FTV_M2M_LINES WHERE GUID=@g", sql.Named("g", guid)).Scan(&lineN)
var did sql.NullInt64
db.QueryRowContext(ctx, "SELECT DEPONENT_ID FROM dbo.FTV_M2M WHERE GUID=@g", sql.Named("g", guid)).Scan(&did)
t.Logf("строк: %d, DEPONENT_ID (SELF_ID по паспорту): %v", lineN, did)
// джоба bj2depo на SQL002 обрабатывает за секунды: ждём DOC
for i := 0; i < 12; i++ {
time.Sleep(5 * time.Second)
depoPollEvents(ctx, rc)
if l := globalDepoLinks.Get(guid); l != nil && l.DocNo != "" {
t.Logf("джоба создала поручение DEPO: DOC=%s (через %d с)", l.DocNo, (i+1)*5)
break
}
}
// имитация ответа джобы bj2depo
if _, err := db.ExecContext(ctx, "INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, STATE, EVENT_TYPE, EVENT_DATE, COMMENT) VALUES (@g, NULL, 'TODO', 'DEPO_ORDER_CREATED', GETDATE(), '75069999')", sql.Named("g", guid)); err != nil {
t.Fatal(err)
}
if _, err := db.ExecContext(ctx, "INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, STATE, EVENT_TYPE, EVENT_DATE, COMMENT) VALUES (@g, 'M2MTEST0001', 'TODO', 'DEPO_ERROR', GETDATE(), N'ТЕСТ: бумага не найдена')", sql.Named("g", guid)); err != nil {
t.Fatal(err)
}
depoPollEvents(ctx, rc)
l := globalDepoLinks.Get(guid)
if l == nil {
t.Fatal("связка DEPO не создана")
}
t.Logf("после чтения: DocNo=%s errors=%v last=%s", l.DocNo, l.Errors, l.LastEvent)
var todo, done int
db.QueryRowContext(ctx, "SELECT COUNT(*) FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g AND EVENT_TYPE LIKE 'DEPO[_]%' AND STATE='TODO'", sql.Named("g", guid)).Scan(&todo)
db.QueryRowContext(ctx, "SELECT COUNT(*) FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g AND EVENT_TYPE LIKE 'DEPO[_]%' AND STATE='SUCCESS'", sql.Named("g", guid)).Scan(&done)
t.Logf("DEPO-события: TODO=%d SUCCESS=%d (ожидаем 0/2)", todo, done)
// уборка тестовых строк
for _, tbl := range []string{"FTV_M2M_EVENTS", "FTV_M2M_LINES", "FTV_M2M"} {
if _, err := db.ExecContext(ctx, "DELETE FROM dbo."+tbl+" WHERE GUID=@g", sql.Named("g", guid)); err != nil {
t.Logf("уборка %s: %v", tbl, err)
}
}
t.Log("тестовые строки удалены")
}