Журнал: кэш разбора пакетов ИШ и черновиков MF010 по времени изменения — обход каталога не перечитывает файлы
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4YVo4UMopqT5DzicqpPjL
This commit is contained in:
@@ -20,6 +20,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdadapter/igw"
|
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdadapter/igw"
|
||||||
@@ -310,10 +311,46 @@ func collectOrderResponseRaw(contrDID string) []respFile {
|
|||||||
|
|
||||||
// readPkgText собирает текст всех документов пакета: распакованные xml/htm/rpt и
|
// readPkgText собирает текст всех документов пакета: распакованные xml/htm/rpt и
|
||||||
// содержимое вложенных zip (декодирует windows-1251). Возвращает текст и время.
|
// содержимое вложенных zip (декодирует windows-1251). Возвращает текст и время.
|
||||||
|
// pkgTextCache — разобранный текст пакетов ИШ по каталогу: пакет после получения не меняется,
|
||||||
|
// поэтому перечитывать его файлы при каждом обходе журнала незачем. Ключ проверки — время
|
||||||
|
// изменения каталога пакета и число файлов в нём.
|
||||||
|
var pkgTextCache = struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
m map[string]pkgTextEntry
|
||||||
|
}{m: map[string]pkgTextEntry{}}
|
||||||
|
|
||||||
|
type pkgTextEntry struct {
|
||||||
|
dirMod time.Time
|
||||||
|
n int
|
||||||
|
body string
|
||||||
|
mt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
func readPkgText(dir string) (string, time.Time) {
|
func readPkgText(dir string) (string, time.Time) {
|
||||||
|
var dirMod time.Time
|
||||||
|
if st, err := os.Stat(dir); err == nil {
|
||||||
|
dirMod = st.ModTime()
|
||||||
|
}
|
||||||
|
files, _ := os.ReadDir(dir)
|
||||||
|
pkgTextCache.mu.Lock()
|
||||||
|
if e, ok := pkgTextCache.m[dir]; ok && e.dirMod.Equal(dirMod) && e.n == len(files) {
|
||||||
|
pkgTextCache.mu.Unlock()
|
||||||
|
return e.body, e.mt
|
||||||
|
}
|
||||||
|
pkgTextCache.mu.Unlock()
|
||||||
|
body, mt := readPkgTextUncached(dir, files)
|
||||||
|
pkgTextCache.mu.Lock()
|
||||||
|
if len(pkgTextCache.m) > 20000 { // защита от бесконечного роста при чистке каталога
|
||||||
|
pkgTextCache.m = map[string]pkgTextEntry{}
|
||||||
|
}
|
||||||
|
pkgTextCache.m[dir] = pkgTextEntry{dirMod: dirMod, n: len(files), body: body, mt: mt}
|
||||||
|
pkgTextCache.mu.Unlock()
|
||||||
|
return body, mt
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPkgTextUncached(dir string, files []os.DirEntry) (string, time.Time) {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
var mt time.Time
|
var mt time.Time
|
||||||
files, _ := os.ReadDir(dir)
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
if f.IsDir() {
|
if f.IsDir() {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -153,6 +153,40 @@ type OutgoingDeal struct {
|
|||||||
// scanForm16Drafts живьём читает INBOX и собирает исходящие сделки: все черновики
|
// scanForm16Drafts живьём читает INBOX и собирает исходящие сделки: все черновики
|
||||||
// с <order_t_id>16</order_t_id>, сгруппированные по based_on (GUID). Объединяет с
|
// с <order_t_id>16</order_t_id>, сгруппированные по based_on (GUID). Объединяет с
|
||||||
// состоянием подачи из стора. store может быть nil (тогда без состояния подачи).
|
// состоянием подачи из стора. store может быть nil (тогда без состояния подачи).
|
||||||
|
// mf010Cache — текст черновиков MF010 по пути и времени изменения: файл после получения не
|
||||||
|
// меняется, а обход журнала читал их все при каждом показе.
|
||||||
|
var mf010Cache = struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
m map[string]mf010Entry
|
||||||
|
}{m: map[string]mf010Entry{}}
|
||||||
|
|
||||||
|
type mf010Entry struct {
|
||||||
|
mod time.Time
|
||||||
|
size int64
|
||||||
|
body string
|
||||||
|
}
|
||||||
|
|
||||||
|
func mf010Text(p string, info os.FileInfo) (string, bool) {
|
||||||
|
mf010Cache.mu.Lock()
|
||||||
|
if e, ok := mf010Cache.m[p]; ok && e.mod.Equal(info.ModTime()) && e.size == info.Size() {
|
||||||
|
mf010Cache.mu.Unlock()
|
||||||
|
return e.body, true
|
||||||
|
}
|
||||||
|
mf010Cache.mu.Unlock()
|
||||||
|
b, err := os.ReadFile(p)
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
body := string(decodeCP1251(b))
|
||||||
|
mf010Cache.mu.Lock()
|
||||||
|
if len(mf010Cache.m) > 20000 {
|
||||||
|
mf010Cache.m = map[string]mf010Entry{}
|
||||||
|
}
|
||||||
|
mf010Cache.m[p] = mf010Entry{mod: info.ModTime(), size: info.Size(), body: body}
|
||||||
|
mf010Cache.mu.Unlock()
|
||||||
|
return body, true
|
||||||
|
}
|
||||||
|
|
||||||
func scanForm16Drafts(store *OutgoingStore) []*OutgoingDeal {
|
func scanForm16Drafts(store *OutgoingStore) []*OutgoingDeal {
|
||||||
byGUID := map[string]*OutgoingDeal{}
|
byGUID := map[string]*OutgoingDeal{}
|
||||||
_ = filepath.Walk(igwInboxDir, func(p string, info os.FileInfo, err error) error {
|
_ = filepath.Walk(igwInboxDir, func(p string, info os.FileInfo, err error) error {
|
||||||
@@ -162,11 +196,10 @@ func scanForm16Drafts(store *OutgoingStore) []*OutgoingDeal {
|
|||||||
if !strings.HasSuffix(strings.ToUpper(p), "_MF010.XML") {
|
if !strings.HasSuffix(strings.ToUpper(p), "_MF010.XML") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
b, e := os.ReadFile(p)
|
body, ok := mf010Text(p, info)
|
||||||
if e != nil {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
body := string(decodeCP1251(b))
|
|
||||||
if of16(body, "order_t_id") != "16" { // только списание (16), не 16/1
|
if of16(body, "order_t_id") != "16" { // только списание (16), не 16/1
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user