Files
Bridge-and-Join-s/internal/lkgateway/seedstore.go
T

226 lines
9.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package lkgateway
import (
"context"
"fmt"
"strings"
"sync"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2m"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2mcore"
)
// SeedStore — in-memory FansyStore с предзаполненными тестовыми данными
// (5 клиентов с депо-счетами и портфелями), соответствующими
// docs/fansy-contract/v1/examples/seed-data.sql. Используется в
// dev-стенде без реальной БД.
type SeedStore struct {
mu sync.Mutex // защищает динамическую регистрацию клиентов ЛК
clients map[string]*m2mcore.Client
accounts map[string][]m2mcore.DepoAccount // by client_id
}
// NewSeedStore собирает SeedStore с фиксированными тестовыми клиентами.
func NewSeedStore() *SeedStore {
s := &SeedStore{
clients: make(map[string]*m2mcore.Client),
accounts: make(map[string][]m2mcore.DepoAccount),
}
s.addClient("11111111-1111-1111-1111-111111111111",
"Иванов", "Иван", "Иванович",
m2m.DocCode21, "4512", "654321")
s.addClient("22222222-2222-2222-2222-222222222222",
"Петров", "Пётр", "Петрович",
m2m.DocCode21, "4513", "654322")
s.addClient("33333333-3333-3333-3333-333333333333",
"Сидоров", "Сидор", "Сидорович",
m2m.DocCode21, "4514", "654323")
s.addClient("44444444-4444-4444-4444-444444444444",
"Кузнецов", "Сергей", "Михайлович",
m2m.DocCode03, "111", "222333")
s.addClient("55555555-5555-5555-5555-555555555555",
"Соколова", "Анна", "Викторовна",
m2m.DocCode21, "4516", "654325")
// Тест-клиенты плана M2M (Приложение 2): К1 Бубликов, К2 Баранкин — те же, что у ЗЕНИТа и
// ГроттБьерн (16.09.2026 ГроттБьерн подтвердил своих клиентов; Мостовиков не используется).
// ФИО и документ — как в тест-плане (m2mTestClients), чтобы EnrichRequest
// собирал СОГЛАСОВАННУЮ личность (раньше подставлялся Петров из 2222).
s.addClient(seedClientK1,
"Бубликов", "Павел", "Иванович",
m2m.DocCode21, "3453", "289090")
s.addClient(seedClientK2,
"Баранкин", "Сергей", "Сергеевич",
m2m.DocCode21, "3763", "290089")
s.addAccount("11111111-1111-1111-1111-111111111111",
"DP789456", "31MC0021900000F01", "P001", "7702070139")
s.addAccount("11111111-1111-1111-1111-111111111111",
"AA789451", "33MC0021900000F02", "F002", "7802031669")
// Тест-инвестор робота: место расчётов = наш реальный тестовый счёт в НРД
// (депкод MC0413600000, счёт HL171004001C, раздел 36MC0413600000F00,
// расчётный депозитарий НРД ИНН 7702165310). Иначе НРД отвечает M2M19
// «недопустимое место расчетов».
s.addAccount("22222222-2222-2222-2222-222222222222",
"MC0413600000", "HL171004001C", "36MC0413600000F00", "7702165310")
s.addAccount("33333333-3333-3333-3333-333333333333",
"DP300400", "31MC0030000000B01", "B001", "0702345678")
s.addAccount("55555555-5555-5555-5555-555555555555",
"DP500600", "31MC0050000000C01", "C001", "0710987654")
// К1/К2 списывают с нашего реального тестового счёта в НРД (мы — Списывающий),
// то же место расчётов, что у тест-инвестора робота (иначе НРД: M2M19).
s.addAccount(seedClientK1,
"MC0413600000", "HL171004001C", "36MC0413600000F00", "7702165310")
s.addAccount(seedClientK2,
"MC0413600000", "HL171004001C", "36MC0413600000F00", "7702165310")
return s
}
// Идентификаторы сид-клиентов тест-плана M2M (реальный депозитарий, не робот).
const (
seedClientK1 = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" // Бубликов Павел Иванович (К1)
seedClientK2 = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" // Баранкин Сергей Сергеевич (К2)
)
// Clients возвращает копию слайса клиентов (для UI выбора).
func (s *SeedStore) Clients() []*m2mcore.Client {
out := make([]*m2mcore.Client, 0, len(s.clients))
for _, c := range s.clients {
out = append(out, c)
}
return out
}
func (s *SeedStore) addClient(id, last, first, middle string, doc m2m.IdentityDocumentCode, series, number string) {
s.clients[id] = &m2mcore.Client{
ID: id,
LastName: last,
FirstName: first,
MiddleName: middle,
Document: m2mcore.ClientDocument{
DocumentType: doc,
Series: series,
Number: number,
},
}
}
func (s *SeedStore) addAccount(clientID, dep, acc, sec, depINN string) {
s.accounts[clientID] = append(s.accounts[clientID], m2mcore.DepoAccount{
ClientID: clientID,
DeponentCode: dep,
AccountID: m2m.AccountID(acc),
SectionID: sec,
DepositoryINN: m2m.OrganizationINN(depINN),
})
}
// EnsureLKClient регистрирует (или обновляет) клиента Личного кабинета в
// справочнике: анкета берётся из учётной записи ЛК, депо-счета — из его
// портфеля (bj_client_positions). Нужно, чтобы EnrichRequest мог собрать
// M2MTransferRequest по заявке, поданной клиентом из кабинета.
func (s *SeedStore) EnsureLKClient(u LKUser, positions []ClientPosition, settle m2mcore.DepoAccount) {
s.mu.Lock()
defer s.mu.Unlock()
last, first, middle := u.LastName, u.FirstName, u.MiddleName
if last == "" && u.FullName != "" {
parts := strings.Fields(u.FullName)
if len(parts) > 0 {
last = parts[0]
}
if len(parts) > 1 {
first = parts[1]
}
if len(parts) > 2 {
middle = parts[2]
}
}
docType := u.DocType
if docType == "" {
docType = "21" // паспорт РФ
}
s.clients[u.Contract] = &m2mcore.Client{
ID: u.Contract,
LastName: last,
FirstName: first,
MiddleName: middle,
Document: m2mcore.ClientDocument{
DocumentType: m2m.IdentityDocumentCode(docType),
Series: u.DocSeries,
Number: u.DocNumber,
},
}
// Депо-счета клиента — уникальные комбинации НРД-реквизитов места расчётов.
// У позиций из таблицы обмена реквизиты свои; у депозитарных позиций их нет
// (только внутренний счёт ВТНКЦ…) — тогда берём фиксированные реквизиты нашего
// депозитария в НРД (settle: deponent/account/section/ИНН из настроек).
seen := map[string]bool{}
var accs []m2mcore.DepoAccount
for _, p := range positions {
a := m2mcore.DepoAccount{
ClientID: u.Contract,
DeponentCode: p.DeponentCode,
AccountID: m2m.AccountID(p.AccountID),
SectionID: p.SectionID,
DepositoryINN: m2m.OrganizationINN(p.SettlementIN),
}
if a.DeponentCode == "" {
a.DeponentCode = settle.DeponentCode
}
if a.AccountID == "" {
a.AccountID = settle.AccountID
}
if a.SectionID == "" {
a.SectionID = settle.SectionID
}
if a.DepositoryINN == "" {
a.DepositoryINN = settle.DepositoryINN
}
key := a.DeponentCode + "|" + string(a.AccountID) + "|" + a.SectionID
if a.AccountID == "" || seen[key] {
continue
}
seen[key] = true
accs = append(accs, a)
}
if len(accs) == 0 && settle.AccountID != "" {
// вообще нет позиций с реквизитами — один счёт по настройкам
settle.ClientID = u.Contract
accs = append(accs, settle)
}
if len(accs) > 0 {
s.accounts[u.Contract] = accs
}
}
// GetClientByID — реализация FansyStore.
func (s *SeedStore) GetClientByID(_ context.Context, id string) (*m2mcore.Client, error) {
s.mu.Lock()
defer s.mu.Unlock()
c, ok := s.clients[id]
if !ok {
return nil, fmt.Errorf("seedstore: клиент %s не найден", id)
}
return c, nil
}
// GetDepoAccounts — реализация FansyStore.
func (s *SeedStore) GetDepoAccounts(_ context.Context, clientID string, _ m2m.OrganizationINN) ([]m2mcore.DepoAccount, error) {
s.mu.Lock()
defer s.mu.Unlock()
accs := s.accounts[clientID]
if len(accs) == 0 {
return nil, fmt.Errorf("seedstore: нет счетов у клиента %s", clientID)
}
return accs, nil
}
// GetBalances — реализация FansyStore. На M2 возвращает пустой список,
// потому что баланс проверяется при подаче заявки в самой UI (через демо-кнопку).
func (s *SeedStore) GetBalances(_ context.Context, _ string, _ []m2m.SecurityCode) ([]m2mcore.SecurityBalance, error) {
return nil, nil
}
// Verify тип SeedStore удовлетворяет m2mcore.FansyStore.
var _ m2mcore.FansyStore = (*SeedStore)(nil)