From d15450646430763e2bcad2bc64ceeba278053009 Mon Sep 17 00:00:00 2001 From: fontvielle Date: Tue, 13 Oct 2026 18:16:42 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B3=D1=80=D1=83=D0=B7=D0=B8=D1=82=D1=81=D1=8F?= =?UTF-8?q?=20=D0=B1=D1=8B=D1=81=D1=82=D1=80=D0=BE:=20=D1=81=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=BE=D0=BA=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D1=82=D0=B5=D0=BB=D0=B5=D0=B9=20=D0=9B=D0=9A=20=D0=B8?= =?UTF-8?q?=D0=B7=20=D0=BA=D1=8D=D1=88=D0=B0;=20=D0=BC=D0=BE=D1=80=D1=84?= =?UTF-8?q?=D0=B8=D0=BD=D0=B3=20=E2=80=94=20=D1=8F=D0=BA=D0=BE=D1=80=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=BF=D1=80=D0=B8=D0=B2=D1=8F=D0=B7=D0=BA=D0=B0?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D0=BA=D1=80=D1=83=D1=82=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) Страница «Настройка» висела до 10 с, когда ЛК ЕСИА Финанс медленный/недоступен: setupData синхронно тянул список пользователей из ЛК (lkUsersForSetup, таймаут 10 с). Теперь список берётся из кэша мгновенно, обновляется в фоне с коротким таймаутом (3 с); при недоступном ЛК показывается сохранённый список с пометкой. Страница больше не ждёт ЛК. 2) Морфинг карточки: добавлена якорная привязка — запоминаем верхний видимый блок и после обновления корректируем прокрутку, чтобы при изменении высоты карточек выше обзора вид не подпрыгивал (устраняет «прыгнула чуть выше»). Co-Authored-By: Claude Opus 4.8 --- internal/lkgateway/lkusers_admin.go | 49 ++++++++++++++++++-- internal/lkgateway/web/templates/layout.html | 5 ++ 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/internal/lkgateway/lkusers_admin.go b/internal/lkgateway/lkusers_admin.go index 1d2c0a2..b6f8678 100644 --- a/internal/lkgateway/lkusers_admin.go +++ b/internal/lkgateway/lkusers_admin.go @@ -16,6 +16,7 @@ import ( "net/http" "strconv" "strings" + "sync" "time" ) @@ -174,12 +175,52 @@ func (h *setupHandlers) lkUsersSave(w http.ResponseWriter, r *http.Request) { } // lkUsersForSetup — список для страницы настроек; ошибка базы — строкой, чтобы страница открылась. -func lkUsersForSetup(rc *RuntimeConfig) ([]LKUser, string) { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) +// lkUsersCache — список пользователей ЛК для страницы «Настройка». Берётся из кэша (мгновенно), +// обновляется в фоне с коротким таймаутом. Иначе медленный/недоступный ЛК вешал страницу на 10 с. +var lkUsersCache struct { + sync.Mutex + list []LKUser + errMsg string + at time.Time + refreshing bool +} + +func lkUsersRefresh(rc *RuntimeConfig) { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() list, err := lkListUsers(ctx, rc) + lkUsersCache.Lock() + defer lkUsersCache.Unlock() + lkUsersCache.refreshing = false + lkUsersCache.at = time.Now() if err != nil { - return nil, err.Error() + if lkUsersCache.list != nil { // ЛК не ответил — оставляем прошлый список с пометкой + lkUsersCache.errMsg = "ЛК сейчас недоступен — показан сохранённый список (" + err.Error() + ")" + return + } + lkUsersCache.errMsg = err.Error() + return } - return list, "" + lkUsersCache.list, lkUsersCache.errMsg = list, "" +} + +// lkUsersForSetup — список пользователей ЛК для страницы «Настройка»: отдаёт кэш сразу, при +// устаревании обновляет в фоне (страница не ждёт ЛК). Первый раз (холодный кэш) — одно короткое ожидание. +func lkUsersForSetup(rc *RuntimeConfig) ([]LKUser, string) { + lkUsersCache.Lock() + cold := lkUsersCache.at.IsZero() + stale := time.Since(lkUsersCache.at) > 20*time.Second + if !cold && stale && !lkUsersCache.refreshing { + lkUsersCache.refreshing = true + go lkUsersRefresh(rc) + } + list, msg := lkUsersCache.list, lkUsersCache.errMsg + lkUsersCache.Unlock() + if cold { + lkUsersRefresh(rc) // первый заход — подождём коротко (≤3 с), дальше всегда из кэша + lkUsersCache.Lock() + list, msg = lkUsersCache.list, lkUsersCache.errMsg + lkUsersCache.Unlock() + } + return list, msg } diff --git a/internal/lkgateway/web/templates/layout.html b/internal/lkgateway/web/templates/layout.html index ae8c002..22e2a62 100644 --- a/internal/lkgateway/web/templates/layout.html +++ b/internal/lkgateway/web/templates/layout.html @@ -893,9 +893,14 @@ button.dq-link, .dq-link { background:none !important; border:0 !important; padd .then(function(r){ return r.ok ? r.text() : Promise.reject(); }) .then(function(html){ if (dirty || (window.bjSelecting && window.bjSelecting())) return; // правит форму или выделяет — не трогаем + // Якорь: верхний видимый блок — чтобы при изменении высоты карточек выше обзора + // вид не «подпрыгивал». После морфинга вернём этот блок на то же место по вертикали. + var anchor = null, aTop = 0, cards = document.querySelectorAll('main .card, main aside'); + for (var ci = 0; ci < cards.length; ci++) { var r = cards[ci].getBoundingClientRect(); if (r.bottom > 0) { anchor = cards[ci]; aTop = r.top; break; } } var doc = new DOMParser().parseFromString(html, 'text/html'); bjMorphKids(document.querySelector('main'), doc.querySelector('main')); if (window.bjInitCopyBars) window.bjInitCopyBars(); + if (anchor && anchor.isConnected) { var d = anchor.getBoundingClientRect().top - aTop; if (d) window.scrollBy(0, d); } }) .catch(function(){}) .then(function(){ bjRefreshing = false; });