c476cfd7a3
- Карточка заявки: операторский «Путь заявки» (claimTimelineOperator) короткими понятными вехами + сворачиваемый технический журнал (buildClaimTimeline); QUIK показан пошагово (register/sign/deny/deposit) с верными временами, отказ регистрации больше не выглядит успехом (quikActionFailTitles). - Баннеры: «в НРД не отправлено», когда пакет сформирован, но не ушёл (нет submitted_to_nsd и не Fail); заголовок карточки и бейдж учитывают это; красный баннер QUIK при отказе, строгий «нет бумаг» только при реальном шорт/недостатке. - НРД-ответ «Поручение с исходящим номером … уже существует» теперь REG_DUP (предупреждение-дубль, не красный «Отказ в регистрации»); не влияет на od.Rejected. - Idempotency: submitguard.go (inflightSubmit) + защита точек подачи от двойного срабатывания (manual + auto-loop, двойной клик): submitOutgoingFor, submitIncomingOrderFor, QuikOutRegister (ранний возврат при registered/signed). - DEPO-алерты: водяной знак depoFailAlertedID засеивается текущим MAX(ID) на старте процесса (Hlth03) — после рестарта bj больше не поднимает повторно алерты про уже известные ошибки обработки событий BJ_*. - Тесты: claimnotsent_render_test.go (хелперы + рендер карточки). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XG3zNMMf2LMoDpXo22mwMX
41 lines
2.3 KiB
Go
41 lines
2.3 KiB
Go
package lkgateway
|
|
|
|
import "sync"
|
|
|
|
// submitguard.go — защита от повторной/параллельной отправки одного и того же
|
|
// поручения в НРД. Триггеры подачи независимы (авто-поллер m2mauto + ручная
|
|
// кнопка в карточке + возможный двойной клик), а точка отправки одна. НРД и так
|
|
// отсекает дубль по исходящему номеру (идемпотентность на стороне НРД — см.
|
|
// isDuplicateOrderAbort), но полагаться только на это нельзя: дубль поднимает
|
|
// лишние документы и путает оператора. Поэтому защищаемся и на стороне bj.
|
|
//
|
|
// beginSubmit даёт атомарный «захват» по ключу: пока один вызов шлёт поручение,
|
|
// параллельный получит false и повторно отправлять не станет. После отправки
|
|
// (успех или ошибка) ключ освобождается endSubmit — поэтому законный повтор
|
|
// после ошибки работает. Идемпотентность «после УСПЕШНОЙ подачи» обеспечивается
|
|
// отдельно — проверкой сохранённого факта подачи в сторе (в submitOutgoingFor /
|
|
// submitIncomingOrderFor): если поручение уже подано, повторно в НРД не шлём.
|
|
var inflightSubmit = struct {
|
|
mu sync.Mutex
|
|
set map[string]bool
|
|
}{set: map[string]bool{}}
|
|
|
|
// beginSubmit пытается захватить ключ. true — захвачено (можно слать); false —
|
|
// по этому ключу уже идёт отправка в другом вызове.
|
|
func beginSubmit(key string) bool {
|
|
inflightSubmit.mu.Lock()
|
|
defer inflightSubmit.mu.Unlock()
|
|
if inflightSubmit.set[key] {
|
|
return false
|
|
}
|
|
inflightSubmit.set[key] = true
|
|
return true
|
|
}
|
|
|
|
// endSubmit освобождает ключ (вызывать через defer сразу после beginSubmit).
|
|
func endSubmit(key string) {
|
|
inflightSubmit.mu.Lock()
|
|
delete(inflightSubmit.set, key)
|
|
inflightSubmit.mu.Unlock()
|
|
}
|