ЛК: портфель ДЕПО «для всех клиентов» — одно соединение и один запрос к 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
@@ -677,7 +677,7 @@
</form>
<div id="lk-portfolio" style="margin-top:14px;padding:12px 14px;border:1px solid var(--border);border-radius:10px">
<b>Заполнение портфеля ДЕПО бумагами из DEPO</b> {{if .Settings.LK.PortfolioSync.Enabled}}<span class="badge confirmed">включено{{if .Settings.LK.PortfolioSync.AllClients}} — все клиенты{{else}} — по списку{{end}}</span>{{else}}<span class="badge draft">выключено</span>{{end}}
<p class="muted" style="margin:6px 0 8px;font-size:13px">Каждые 10 секунд, в том же проходе, что и приём заявок, bj сверяет портфель клиента в DEPO и отправляет его в ЛК (<code>/portfolio/import</code>, портфель ДЕПО) — только если остатки изменились. Номер гос. регистрации — из экспорта QUIK, раздел счёта — «Основной», хранение — НРД. Бумаги без номера гос. регистрации не отправляются (ЛК отвергает весь импорт).</p>
<p class="muted" style="margin:6px 0 8px;font-size:13px">Для клиентов из списка — каждые 10 секунд, для всех клиентов — раз в 5 минут (одним запросом к DEPO) bj сверяет портфель клиента в DEPO и отправляет его в ЛК (<code>/portfolio/import</code>, портфель ДЕПО) — только если остатки изменились. Номер гос. регистрации — из экспорта QUIK, раздел счёта — «Основной», хранение — НРД. Бумаги без номера гос. регистрации не отправляются (ЛК отвергает весь импорт).</p>
<form method="post" action="/admin/setup/lk-portfolio" style="margin:0 0 8px">
{{if .Settings.LK.PortfolioSync.Enabled}}<button class="btn" type="submit" name="action" value="off" style="background:var(--card);color:var(--err);border:1px solid var(--err)">Выключить заполнение портфеля ДЕПО бумагами</button>
<button class="btn" type="submit" name="action" value="sync">Заполнить сейчас</button>
@@ -746,7 +746,14 @@
q.addEventListener('focus',function(){ if(!list.innerHTML) load(); });
})();
</script>
{{range lkPortfolioStates}}<div class="muted" style="font-size:12.5px;margin-top:8px"><b>{{.LK}}</b> ← {{.Depo}}: {{if .Err}}<span style="color:var(--err)">ошибка — {{.Err}}</span>{{else if .PushedAt.IsZero}}сверено {{.CheckedAt.Format "15:04:05"}}, отправки ещё не было{{else}}отправлено {{.PushedAt.Format "02.01 15:04:05"}} ({{.Lines}} бумаг), сверено {{.CheckedAt.Format "15:04:05"}}{{end}}{{if .Skipped}}<details style="margin-top:2px"><summary style="cursor:pointer">пропущено бумаг: {{len .Skipped}} — без номера гос. регистрации</summary>{{range .Skipped}}<div>{{.}}</div>{{end}}</details>{{end}}</div>{{end}}
{{with lkPortfolioSummary}}{{if or .Manual .Total}}<div style="margin-top:10px;padding-top:10px;border-top:1px dashed var(--border);font-size:12.5px">
{{with .Manual}}<div class="muted" style="margin-bottom:6px">Заливка одному клиенту: {{template "lkpfRow" .}}</div>{{end}}
{{if .Total}}<div><b>Итог заполнения:</b> договоров {{.Total}} · отправлено в ЛК {{.Sent}} · нечего отправлять {{.Nothing}}{{if .Errors}} · <span style="color:var(--err)">с ошибкой {{.Errors}}</span>{{end}}{{if .Skipped}} · бумаг без номера гос. регистрации {{.Skipped}}{{end}}</div>
<div class="muted">{{if not .LastPush.IsZero}}последняя отправка {{.LastPush.Format "02.01 15:04:05"}} · {{end}}последняя сверка {{.LastCheck.Format "15:04:05"}}</div>
{{if .Recent}}<div style="margin-top:6px">Последние обновления:</div>{{range .Recent}}<div class="muted" style="margin-top:2px">{{template "lkpfRow" .}}</div>{{end}}{{end}}
{{if .Failed}}<details style="margin-top:6px"><summary style="cursor:pointer;color:var(--err)">Договоры с ошибкой ({{len .Failed}})</summary>{{range .Failed}}<div class="muted" style="margin-top:4px">{{template "lkpfRow" .}}</div>{{end}}</details>{{end}}
<details style="margin-top:6px"><summary style="cursor:pointer">Все договоры ({{.Total}})</summary>{{range .All}}<div class="muted" style="margin-top:4px">{{template "lkpfRow" .}}</div>{{end}}</details>{{end}}
</div>{{end}}{{end}}
</div>
<div id="lk-real-nsd" style="margin-top:14px;padding:12px 14px;border:1px solid var(--border);border-radius:10px;{{if .Settings.LK.RealNSDTest.Enabled}}background:var(--warn-weak){{end}}">
<b>Тест «боевой ЛК → тестовый контур НРД»</b> {{if .Settings.LK.RealNSDTest.Enabled}}<span class="badge awaiting_decision">включён</span>{{else}}<span class="badge draft">выключен</span>{{end}}
@@ -1144,3 +1151,5 @@
})();
</script>
{{end}}
{{/* строка договора в итоге заполнения портфеля ДЕПО (LKPortfolioSyncState) */}}
{{define "lkpfRow"}}<b>{{.LK}}</b> ← {{.Depo}}: {{if .Err}}<span style="color:var(--err)">ошибка — {{.Err}}</span>{{else if .Nothing}}{{.Nothing}}{{else if .PushedAt.IsZero}}сверено {{.CheckedAt.Format "15:04:05"}}, отправки ещё не было{{else}}отправлено {{.PushedAt.Format "02.01 15:04:05"}} ({{.Lines}} бумаг), сверено {{.CheckedAt.Format "15:04:05"}}{{end}}{{if .Skipped}}<details style="margin-top:2px"><summary style="cursor:pointer">пропущено бумаг: {{len .Skipped}} — без номера гос. регистрации</summary>{{range .Skipped}}<div>{{.}}</div>{{end}}</details>{{end}}{{end}}