Журнал обмена: автопоявление новых заявок; карточка не прыгает наверх при автообновлении
Журнал: новый эндпоинт /admin/journal/rows (строки новее since, рендер тем же шаблоном
journalrow) + JS раз в 8 с подставляет новые заявки сверху с мягкой подсветкой (row-new),
без перезагрузки и без дёрганья; счётчик обновляется, плашка «нет переводов» убирается.
Таблица теперь рендерится всегда (tbody#journal-body), строка вынесена в {{define journalrow}}.
Карточка заявки: автообновление по смене подписи раньше делало location.reload() и сбрасывало
прокрутку наверх — мешало читать. Теперь bjReloadKeepScroll() сохраняет позицию прокрутки
(sessionStorage) и восстанавливает после перезагрузки; автообновление сохранено.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,9 +5,13 @@ package lkgateway
|
||||
// страницах). Для быстрого обзора всего потока с контрагентами.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2mcore"
|
||||
@@ -63,56 +67,91 @@ func incomingStatusOf(it *IncomingTransfer) incomingStatusVM {
|
||||
return incomingStatusVM{Class: c, Text: t, Outcome: o}
|
||||
}
|
||||
|
||||
// buildJournalRows — объединённая лента (исходящие + входящие), отсортированная от новых к старым.
|
||||
func buildJournalRows(ctx context.Context, svc *Service, store *IncomingStore) []journalRow {
|
||||
var rows []journalRow
|
||||
|
||||
// Исходящие (наши заявки в НРД).
|
||||
if page, err := svc.ListClaims(ctx, m2mcore.Filter{Limit: 100}); err == nil {
|
||||
for _, c := range page.Items {
|
||||
party := m2mPartyName(c.ReceivingDepositoryINN)
|
||||
if party != "" {
|
||||
party += " · ИНН " + c.ReceivingDepositoryINN
|
||||
} else {
|
||||
party = "ИНН " + c.ReceivingDepositoryINN
|
||||
}
|
||||
rows = append(rows, journalRow{
|
||||
Time: c.CreatedAt, Dir: "Исходящий", DirKind: "out",
|
||||
Party: party, SecN: len(c.Securities),
|
||||
Status: c.Status, StatusText: russianState(c.Status),
|
||||
Href: "/admin/claims/" + c.ID,
|
||||
LiveKey: "out:" + c.ID, Live: liveOutgoing(c),
|
||||
})
|
||||
}
|
||||
}
|
||||
// Входящие (переводы к нам).
|
||||
for _, it := range store.List() {
|
||||
party := m2mPartyName(it.SenderCode)
|
||||
if party == "" {
|
||||
party = m2mPartyName(it.SenderINN)
|
||||
}
|
||||
if party != "" {
|
||||
party += " · " + it.SenderCode
|
||||
} else {
|
||||
party = it.SenderCode
|
||||
}
|
||||
cls, txt, outcome := incomingJournalStatus(it)
|
||||
rows = append(rows, journalRow{
|
||||
Time: it.ReceivedAt, Dir: "Входящий", DirKind: "in",
|
||||
Party: party, SecN: len(it.Securities),
|
||||
Status: cls, StatusText: txt, Outcome: outcome,
|
||||
Href: "/admin/incoming/" + it.ID,
|
||||
LiveKey: "in:" + it.ID, Live: liveIncoming(it),
|
||||
})
|
||||
}
|
||||
sort.Slice(rows, func(i, j int) bool { return rows[i].Time.After(rows[j].Time) })
|
||||
return rows
|
||||
}
|
||||
|
||||
// registerJournal вешает /admin/journal — объединённая лента вход+исход.
|
||||
func registerJournal(mux *http.ServeMux, a *admin, svc *Service, store *IncomingStore) {
|
||||
registerLiveStatus(mux, svc, store) // «чего ждёт заявка» для строк в работе
|
||||
mux.HandleFunc("/admin/journal", func(w http.ResponseWriter, r *http.Request) {
|
||||
var rows []journalRow
|
||||
|
||||
// Исходящие (наши заявки в НРД).
|
||||
if page, err := svc.ListClaims(r.Context(), m2mcore.Filter{Limit: 100}); err == nil {
|
||||
for _, c := range page.Items {
|
||||
party := m2mPartyName(c.ReceivingDepositoryINN)
|
||||
if party != "" {
|
||||
party += " · ИНН " + c.ReceivingDepositoryINN
|
||||
} else {
|
||||
party = "ИНН " + c.ReceivingDepositoryINN
|
||||
}
|
||||
rows = append(rows, journalRow{
|
||||
Time: c.CreatedAt, Dir: "Исходящий", DirKind: "out",
|
||||
Party: party, SecN: len(c.Securities),
|
||||
Status: c.Status, StatusText: russianState(c.Status),
|
||||
Href: "/admin/claims/" + c.ID,
|
||||
LiveKey: "out:" + c.ID, Live: liveOutgoing(c),
|
||||
})
|
||||
}
|
||||
}
|
||||
// Входящие (переводы к нам).
|
||||
for _, it := range store.List() {
|
||||
party := m2mPartyName(it.SenderCode)
|
||||
if party == "" {
|
||||
party = m2mPartyName(it.SenderINN)
|
||||
}
|
||||
if party != "" {
|
||||
party += " · " + it.SenderCode
|
||||
} else {
|
||||
party = it.SenderCode
|
||||
}
|
||||
cls, txt, outcome := incomingJournalStatus(it)
|
||||
rows = append(rows, journalRow{
|
||||
Time: it.ReceivedAt, Dir: "Входящий", DirKind: "in",
|
||||
Party: party, SecN: len(it.Securities),
|
||||
Status: cls, StatusText: txt, Outcome: outcome,
|
||||
Href: "/admin/incoming/" + it.ID,
|
||||
LiveKey: "in:" + it.ID, Live: liveIncoming(it),
|
||||
})
|
||||
}
|
||||
sort.Slice(rows, func(i, j int) bool { return rows[i].Time.After(rows[j].Time) })
|
||||
|
||||
rows := buildJournalRows(r.Context(), svc, store)
|
||||
data := struct {
|
||||
page
|
||||
Rows []journalRow
|
||||
}{nowPage("Журнал обмена", "journal"), rows}
|
||||
render(w, a.journal, data)
|
||||
})
|
||||
|
||||
// /admin/journal/rows — только новые строки (появившиеся после since, unix-наносекунды).
|
||||
// Страница подставляет их сверху без перезагрузки. count — общее число строк (для счётчика).
|
||||
mux.HandleFunc("/admin/journal/rows", func(w http.ResponseWriter, r *http.Request) {
|
||||
rows := buildJournalRows(r.Context(), svc, store)
|
||||
var since int64
|
||||
if s := r.URL.Query().Get("since"); s != "" {
|
||||
since, _ = strconv.ParseInt(s, 10, 64)
|
||||
}
|
||||
type rowJSON struct {
|
||||
Key string `json:"key"`
|
||||
T int64 `json:"t"`
|
||||
HTML string `json:"html"`
|
||||
}
|
||||
out := make([]rowJSON, 0, 8)
|
||||
for _, row := range rows {
|
||||
t := row.Time.UnixNano()
|
||||
if since > 0 && t <= since {
|
||||
continue // уже есть на странице
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := a.journal.ExecuteTemplate(&buf, "journalrow", row); err != nil {
|
||||
continue
|
||||
}
|
||||
out = append(out, rowJSON{Key: row.LiveKey, T: t, HTML: buf.String()})
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"count": len(rows), "rows": out})
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user