ЛК: портфель ДЕПО «для всех клиентов» — одно соединение и один запрос к DEPO, свой цикл раз в 5 мин; итог в настройках свёрнут

На 9 после включения «для всех» (23.10.2026) каждый проход открывал по два
соединения с DEPO на каждый из 1158 договоров — около 50 входов в MSSQL в
секунду без перерыва, а проход шёл в цикле опроса заявок ЛК и задерживал его.

- Остатки всех клиентов — одним запросом (fsql.Dp03), одно соединение с DEPO
  на проход; по списку — запрос по клиенту на том же соединении. На боевой
  DEPO: общий запрос 127 мс, совпадает с запросом по клиенту (58 договоров).
- Свой цикл lkPortfolioLoop (здоровье «lk-portfolio»): по списку — каждые
  10 с, для всех клиентов — раз в 5 мин. Опрос заявок ЛК больше не ждёт.
- Проходы по очереди: фоновый и «Заполнить сейчас» не шлют один портфель
  дважды; принудительный проход пропускает отправленные за последнюю минуту.
- ЛК не отвечает — проход прерывается; одна и та же ошибка отправки не
  повторяется в журнале каждый проход. Ошибка чтения DEPO снимается, когда
  портфель снова читается без изменений.
- Настройки: итог (договоров, отправлено, нечего отправлять, с ошибкой),
  последние 5 отправок; договоры с ошибкой и все договоры — под раскрытием.
  «Отправлять нечего» — не ошибка (поле Nothing).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
This commit is contained in:
fontvielle
2026-10-23 14:45:16 +03:00
parent 21d5f33394
commit a0ffbfa721
10 changed files with 377 additions and 33 deletions
+53
View File
@@ -4,7 +4,9 @@ import (
"net/http"
"net/http/httptest"
"path/filepath"
"strings"
"testing"
"time"
)
func TestLKPaperTypeAndPairs(t *testing.T) {
@@ -36,6 +38,57 @@ func TestLKContractByAccount(t *testing.T) {
}
}
// Остатки общего запроса раскладываются по портфелям пар как в Dp01: совпадение или «портфель + пробел».
func TestLKPortfolioByPort(t *testing.T) {
all := map[string][]DepoPosition{
"БР-1001 Гравс Геннадий": {{AccountNum: "ВТНКЦ01", ISIN: "RU2", SecurityName: "Б"}, {AccountNum: "ВТНКЦ01", ISIN: "RU1", SecurityName: "А"}},
"БР-10011 Другой": {{AccountNum: "ВТНКЦ02", ISIN: "RU3", SecurityName: "В"}},
"БР-7": {{AccountNum: "ВТНКЦ07", ISIN: "RU4", SecurityName: "Г"}},
}
by := lkPortfolioByPort(all, []lkPortfolioPair{{LK: "ДД-1", Depo: "БР-1001"}, {LK: "ДД-7", Depo: "БР-7"}, {LK: "ДД-9", Depo: "БР-9"}})
if p := by["БР-1001"]; len(p) != 2 || p[0].ISIN != "RU1" || p[1].ISIN != "RU2" {
t.Fatalf("БР-1001: %+v", p)
}
if len(by["БР-7"]) != 1 || len(by["БР-9"]) != 0 || len(by) != 2 {
t.Fatalf("%+v", by)
}
}
// Итог заполнения: «нечего отправлять» — не ошибка, все договоры — под раскрытием.
func TestLKPortfolioSummaryView(t *testing.T) {
lkPortfolioState.Lock()
saved := lkPortfolioState.m
now := time.Now()
lkPortfolioState.m = map[string]*LKPortfolioSyncState{
"ДД-1": {LK: "ДД-1", Depo: "БР-1-1", CheckedAt: now, Nothing: lkPortfolioNothing([]string{"x"}), Skipped: []string{"x"}},
"ДД-1006": {LK: "ДД-1006", Depo: "БР-1047", CheckedAt: now, PushedAt: now, Lines: 1},
"ДД-1007": {LK: "ДД-1007", Depo: "БР-1048", CheckedAt: now, Err: "DEPO: i/o timeout"},
"ДД-816": {LK: "ДД-816", Depo: "БР-859", CheckedAt: now, PushedAt: now, Lines: 3, manual: true},
}
lkPortfolioState.Unlock()
defer func() { lkPortfolioState.Lock(); lkPortfolioState.m = saved; lkPortfolioState.Unlock() }()
sm := lkPortfolioSummary()
if sm.Total != 3 || sm.Sent != 1 || sm.Nothing != 1 || sm.Errors != 1 || sm.Skipped != 1 || sm.Manual == nil || len(sm.Recent) != 1 {
t.Fatalf("%+v", sm)
}
a, err := newAdmin()
if err != nil {
t.Fatal(err)
}
rec := httptest.NewRecorder()
render(rec, a.setup, SetupData{page: nowPage("Настройка", "setup")})
body := rec.Body.String()
for _, want := range []string{"Итог заполнения", "Все договоры (3)", "Договоры с ошибкой (1)", "Последние обновления", "Заливка одному клиенту"} {
if !strings.Contains(body, want) {
t.Errorf("нет %q", want)
}
}
if strings.Contains(body, "ошибка — отправлять нечего") {
t.Error("«отправлять нечего» показано как ошибка")
}
}
// Маршруты карточки «Заполнение портфеля ДЕПО» зарегистрированы (29.09–22.10 их не было — 404).
func TestLKPortfolioRoutes(t *testing.T) {
mux := http.NewServeMux()