diff --git a/internal/lkgateway/journal.go b/internal/lkgateway/journal.go index 398b3c9..30f3c5e 100644 --- a/internal/lkgateway/journal.go +++ b/internal/lkgateway/journal.go @@ -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}) + }) } diff --git a/internal/lkgateway/web/templates/admin_journal.html b/internal/lkgateway/web/templates/admin_journal.html index 82cc9ae..9c48d7f 100644 --- a/internal/lkgateway/web/templates/admin_journal.html +++ b/internal/lkgateway/web/templates/admin_journal.html @@ -1,25 +1,22 @@ {{define "content"}}
Все переводы в одной ленте — входящие и исходящие. Отдельные виды: Исходящие · Входящие.
- {{if .Rows}} +Все переводы в одной ленте — входящие и исходящие. Отдельные виды: Исходящие · Входящие. новые заявки появляются автоматически
| Время | Направление | Контрагент | ЦБ | Статус | |
|---|---|---|---|---|---|
| {{msk .Time}} | -{{if eq .DirKind "out"}}↗ Исходящий{{else}}↘ Входящий{{end}} | -{{.Party}} | -{{.SecN}} | -{{.StatusText}}{{if .Outcome}} {{.Outcome}}{{end}} {{.Live.Text}} |
- открыть → | -
| Переводов пока нет. | |||||
Переводов пока нет.
- {{end}}