32d88fc42a
Перевод 97EAD00C (заявка ЛК #71741, 23.10.2026) исполнен в QUIK и НРД (MS010), но в ЛК статус не изменился, а сделка осталась «Подтверждена»: - связки guid → заявка ЛК искались с учётом регистра: мы шлём GUID строчными, НРД отвечает заглавными — OnLKOutcome молча не находил заявку. Ключи — строчными (lkGUIDKey), старые записи приводятся при загрузке; итог модуля ЛК (lk.transfer_requests) — lower(guid); - после MS010 по всем строкам поручения 16 сделка → «Исполнена» (CompleteSUB16), итог — в ЛК (outOnExecuted, каждый проход order-watch, повтор безвреден); в бою согласие контрагента — ещё не «Исполнена»: статус ждёт MS010, как в тесте «боевой ЛК → TEST3»; имитатор НРД — итог сразу. Тестовый 28 с боевым ЛК взял ту же заявку и после отказа МНП («запрещен для операции шорт» — бумагу уже заблокировал 9) брал её каждые 10 секунд: 108 сделок с новыми GUID и столько же писем. - Сделка заведена, а отправка не прошла (МНП не заблокировал) — заявка помечается взятой и связывается с переводом: дальше — оператор в карточке. - Боевой ЛК при тестовом профиле ИШ (lkProdOnTestNSD): заявки клиентов не берутся (кроме теста «боевой ЛК → тестовый НРД»), портфели не заполняются, предупреждение в настройках ЛК. - Письма: в теме и первой строке — сервер bj и контур НРД («[bj ТЕСТ …]» у тестового); письма о входящих — метка только у тестового. - История FSM — по МСК с датой, «текущее состояние» вместо «сейчас». - Карточка теста «боевой ЛК → тестовый НРД» — только в режиме разработки (включённый тест виден всегда, чтобы его выключить). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
384 lines
21 KiB
Go
384 lines
21 KiB
Go
package lkgateway
|
||
|
||
// Имитация НРД для заявок ЛК ЕСИА Финанс. Заявка проходит весь путь, как в бою:
|
||
//
|
||
// ЛК (depo_m2m) → журнал исходящих заявок bj → проверка шлюза (сверка списания с DEPO)
|
||
// отказ → сделка «Отклонена» → отказ в Fancy (DEPO) → ответ Fancy → ЛК «Отклонена» (lkflow.go)
|
||
// принять → блокировка в QUIK МНП → DEPO (BJ_SENT) → «отправка» в имитатор НРД
|
||
// → через lkEmuDecisionDelay решение контрагента → тот же обработчик, что у
|
||
// настоящего M2MTransferDecision (сделка, ЛК «Исполнена», QUIK, DEPO)
|
||
//
|
||
// В НРД ничего не уходит. Включается флагом LK.EmulateNSD и принудительно, если активен
|
||
// боевой ЛК, а профиль ИШ тестовый: настоящие заявки клиентов в тестовый НРД не шлём.
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"log"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2m"
|
||
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2mcore"
|
||
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdadapter/mock"
|
||
)
|
||
|
||
const lkEmuDecisionDelay = 30 * time.Second
|
||
|
||
// lkEmuNote дописывается к статусу в ЛК, чтобы было видно, что НРД не участвовал.
|
||
const lkEmuNote = " (тестовый режим: в НРД не отправлялось)"
|
||
|
||
// lkEmuSender — имитатор НРД для заявок ЛК; создаётся в Server.Run.
|
||
var lkEmuSender *mock.Sender
|
||
|
||
// nsdEmuMode — режим эмуляции НРД: "off" | "substitute" | "imitate". Выбор контура НРД
|
||
// (NSD.Profile — test3 / боевой) от режима НЕ зависит: при "off" заявка уходит в выбранный
|
||
// контур как есть. Режимы взаимоисключающие (ведутся флагами EmulateNSD / RealNSDTest.Enabled).
|
||
func nsdEmuMode(rc *RuntimeConfig) string {
|
||
s := rc.Snapshot()
|
||
switch {
|
||
case s.LK.EmulateNSD:
|
||
return "imitate"
|
||
case s.LK.RealNSDTest.Enabled:
|
||
return "substitute"
|
||
}
|
||
return "off"
|
||
}
|
||
|
||
// lkEmulateNSD — обрабатывается ли заявка ЛК слоем эмуляции (имитация или подмена личности).
|
||
// "off" — заявка идёт в выбранный контур НРД с реальными данными клиента (обычный путь).
|
||
func lkEmulateNSD(rc *RuntimeConfig) (bool, string) {
|
||
switch nsdEmuMode(rc) {
|
||
case "imitate":
|
||
return true, "полная имитация — в НРД ничего не отправляется, ответ формирует bj"
|
||
case "substitute":
|
||
return true, "подмена личности — в НРД уходит тест-инвестор вместо клиента"
|
||
}
|
||
return false, ""
|
||
}
|
||
|
||
// lkProcessEmulated — путь заявки ЛК без отправки в НРД. Возвращает "rejected" или
|
||
// "sent"; ошибка — заявку нужно повторить на следующем проходе (DEPO недоступна и т.п.).
|
||
func lkProcessEmulated(ctx context.Context, rc *RuntimeConfig, svc *Service, ep LKEndpoint, id int, claimReq CreateClaimRequest) (string, error) {
|
||
if lkEmuSender == nil {
|
||
return "", fmt.Errorf("имитатор НРД не запущен")
|
||
}
|
||
inv := claimReq.Investor
|
||
|
||
// Формат полей заявки не решает, берём ли её: решает проверка шлюза.
|
||
var secs []depoOutSec
|
||
acc, section := "", ""
|
||
for _, sec := range claimReq.Securities {
|
||
qty := sec.Quantity.Fractional
|
||
if qty == "" {
|
||
qty = strconv.FormatUint(sec.Quantity.Whole, 10)
|
||
}
|
||
secs = append(secs, depoOutSec{ISIN: sec.SecurityDetails.ISIN, Code: sec.SecurityCode, Qty: qty})
|
||
if len(sec.SettlementAccounts) > 0 && acc == "" {
|
||
loc := sec.SettlementAccounts[0].SettlementLocation
|
||
acc, section = loc.AccountID, loc.SectionID
|
||
}
|
||
}
|
||
res := runDepoCheckOutAccount(inv.LastName, inv.FirstName, inv.MiddleName, inv.Document.Series, inv.Document.Number, inv.BirthDate, secs, acc, section, claimReq.ClientAccount)
|
||
if !res.Ran {
|
||
return "", fmt.Errorf("сверка с DEPO не выполнена: %s", res.Err)
|
||
}
|
||
// Пакет собираем и при некорректных данных: заявка должна попасть в журнал и в Fancy.
|
||
req, err := svc.BuildRequestUnchecked(ctx, claimReq)
|
||
if err != nil {
|
||
return "", fmt.Errorf("сборка пакета: %w", err)
|
||
}
|
||
guid := string(req.Header.GUID)
|
||
log.Printf("lk-gateway: ЛК #%d (%s): сверка с DEPO — %s (инвестор %s, счёт %s)", id, shortGUID(guid), res.Summary, pick(res.Investor, "не найден"), pick(res.Account, "—"))
|
||
globalLKFlows.step(guid, id, "db", "Заявка из ЛК ЕСИА Финанс", fmt.Sprintf("Забрана коннектором bj из личного кабинета (заявка ЛК #%d).", id))
|
||
|
||
code, short, reason := "", "", ""
|
||
if res.Recommend != "confirm" {
|
||
code, short, reason = lkDepoRejectText(res)
|
||
} else if verr := req.Validate(); verr != nil {
|
||
short, reason = "Отказ: данные заявки не прошли проверку", "Отказ: данные заявки не прошли проверку формата НРД — "+verr.Error()
|
||
}
|
||
if reason != "" {
|
||
globalLKFlows.step(guid, id, "reject", "Проверка шлюза bj: отказ", reason)
|
||
lkGatewayReject(ctx, rc, svc, id, inv.ID, req, code, short, reason)
|
||
log.Printf("lk-gateway: ЛК #%d (%s): отказ шлюза, передан в Fancy — %s", id, shortGUID(guid), reason)
|
||
return "rejected", nil
|
||
}
|
||
globalLKFlows.step(guid, id, "crane", "Проверка шлюза bj: можно исполнить",
|
||
fmt.Sprintf("Сверка с DEPO: %s (инвестор %s, счёт %s).", res.Summary, pick(res.Investor, "—"), pick(res.Account, "—")))
|
||
|
||
// Выбор отправителя по режиму эмуляции НРД:
|
||
// imitate — внутренний имитатор (в НРД ничего не уходит, ответ формирует bj);
|
||
// substitute — реальная отправка в выбранный контур НРД; клиент из списка идёт с подменой
|
||
// блока инвестора на тест-инвестора, остальные — с настоящими данными.
|
||
sender := m2mcore.NSDSender(lkEmuSender) // imitate (по умолчанию в этом пути)
|
||
realNSD := false
|
||
if nsdEmuMode(rc) == "substitute" && svc.sender != nil {
|
||
realNSD = true
|
||
if realTestMatch(rc, inv.Document.Series, inv.Document.Number) {
|
||
asInv, asText := realTestInvestor(rc, req.Data.InvestorInformation.IdentityDocument.DocumentType)
|
||
realTestPut(RealTestRec{GUID: guid, ClaimID: id, At: time.Now(), As: asText,
|
||
Client: strings.TrimSpace(fmt.Sprintf("%s %s %s, %s %s", inv.LastName, inv.FirstName, inv.MiddleName, inv.Document.Series, inv.Document.Number))})
|
||
sender = substituteSender{real: svc.sender, inv: asInv}
|
||
globalLKFlows.step(guid, id, "gateway", "Подмена личности: в НРД ("+rc.Snapshot().NSD.Profile+") с данными тест-инвестора",
|
||
"У нас заявка проходит как клиент ("+inv.LastName+"): DEPO, QUIK МНП (боевая запись), статусы в ЛК. В НРД блок инвестора заменён на "+asText+"; GUID и референсы — настоящие.")
|
||
log.Printf("lk-gateway: ЛК #%d (%s): подмена личности — в НРД как %s", id, shortGUID(guid), asText)
|
||
} else {
|
||
// подмена включена, но клиент не в списке — отправляем в НРД с настоящими данными
|
||
sender = svc.sender
|
||
globalLKFlows.step(guid, id, "gateway", "В НРД ("+rc.Snapshot().NSD.Profile+") с настоящими данными",
|
||
"Подмена включена, но этот клиент не в списке — запрос уходит в НРД как есть.")
|
||
log.Printf("lk-gateway: ЛК #%d (%s): отправка в НРД с настоящими данными (вне списка подмены)", id, shortGUID(guid))
|
||
}
|
||
}
|
||
if _, err := svc.SendBuiltRequestVia(ctx, sender, req, inv.ID, nil); err != nil {
|
||
if strings.Contains(err.Error(), "QUIK") {
|
||
reason := "Отказ: не удалось заблокировать бумаги клиента — " + err.Error()
|
||
globalLKFlows.step(guid, id, "reject", "Блокировка в QUIK не удалась", err.Error())
|
||
lkGatewayReject(ctx, rc, svc, id, inv.ID, req, "QUIK", "Отказ: не удалось заблокировать ценные бумаги", reason)
|
||
return "rejected", nil
|
||
}
|
||
if realNSD {
|
||
// НРД не принял пакет — заявка «Не отправлена», повтор автоматически (sendfail.go); в ЛК — «В работе»
|
||
globalLKFlows.step(guid, id, "reject", "НРД не принял пакет — повтор автоматически", err.Error())
|
||
lkMarkSent(id)
|
||
lkLinkGUID(guid, ep, id)
|
||
return "sent", nil
|
||
}
|
||
return "", fmt.Errorf("имитация отправки: %w", err)
|
||
}
|
||
lkMarkSent(id)
|
||
lkLinkGUID(guid, ep, id)
|
||
globalLKFlows.update(guid, func(f *LKFlow) { f.Stage = "sent" })
|
||
sentTitle := "Бумаги заблокированы, заявка обработана шлюзом bj без отправки в НРД"
|
||
if devMode(rc) {
|
||
sentTitle = "Бумаги заблокированы, заявка передана имитатору НРД"
|
||
}
|
||
sentDetail := lkQuikSummary(guid) + fmt.Sprintf(" В НРД не отправлялась; ответ контрагента — через %s.", lkEmuDecisionDelayNow(rc))
|
||
if realNSD {
|
||
sentTitle, sentDetail = "Бумаги заблокированы, запрос перевода отправлен в НРД (TEST3)", lkQuikSummary(guid)+" Ждём решения контрагента от НРД."
|
||
}
|
||
globalLKFlows.step(guid, id, "gateway", sentTitle, sentDetail)
|
||
if pc, pb := lkPatch(ctx, ep, fmt.Sprintf("/claims/%d", id), []byte(`{"claim":{"status":"in_process"}}`)); pc >= 400 {
|
||
log.Printf("lk-gateway: ЛК #%d: PATCH «В работе» не прошёл: HTTP %d %s", id, pc, pb)
|
||
}
|
||
if realNSD {
|
||
log.Printf("lk-gateway: ЛК #%d (%s): сверка пройдена, бумаги заблокированы, отправлено в НРД TEST3 с подменой инвестора", id, shortGUID(guid))
|
||
return "sent", nil
|
||
}
|
||
log.Printf("lk-gateway: ЛК #%d (%s): сверка пройдена, бумаги заблокированы, заявка передана имитатору НРД — решение контрагента через %s", id, shortGUID(guid), lkEmuDecisionDelayNow(rc))
|
||
return "sent", nil
|
||
}
|
||
|
||
// lkQuikSummary — что сделал QUIK МНП по переводу (для шага пути).
|
||
func lkQuikSummary(guid string) string {
|
||
links := globalQuikLinks.ForGUID(guid)
|
||
if len(links) == 0 {
|
||
return "QUIK: блокировка не выполнялась (цикл QUIK выключен или клиент не найден в DEPO)."
|
||
}
|
||
var parts []string
|
||
for _, l := range links {
|
||
p := fmt.Sprintf("%s × %v — %s", l.ISIN, l.Qty, l.StateRu())
|
||
if l.InstrNo != "" {
|
||
p += ", поручение №" + l.InstrNo
|
||
}
|
||
if l.Err != "" {
|
||
p += ": " + l.Err
|
||
}
|
||
parts = append(parts, p)
|
||
}
|
||
return "QUIK: " + strings.Join(parts, "; ") + "."
|
||
}
|
||
|
||
// lkDepoRejectText — код отказа по результату сверки: short — короткий текст для клиента в ЛК
|
||
// («Отказ по результатам проверки депозитария: <причина> (код)»), text — с подробностями
|
||
// проверок для журнала bj и Fancy.
|
||
func lkDepoRejectText(res depoResult) (code, short, text string) {
|
||
var codes, labels, details []string
|
||
for _, s := range res.Secs {
|
||
for _, c := range s.Checks {
|
||
if c.OK {
|
||
continue
|
||
}
|
||
if c.Code != "" && !containsStr(codes, c.Code) {
|
||
codes = append(codes, c.Code)
|
||
labels = append(labels, m2mCodeLabel(c.Code))
|
||
}
|
||
details = append(details, c.Name+": "+c.Detail)
|
||
}
|
||
}
|
||
if len(codes) > 0 {
|
||
code = codes[0]
|
||
}
|
||
short = "Отказ по результатам проверки депозитария"
|
||
if len(labels) > 0 {
|
||
short += ": " + labels[0]
|
||
}
|
||
text = "Отказ по результатам проверки депозитария"
|
||
if len(labels) > 0 {
|
||
text += ": " + strings.Join(labels, "; ")
|
||
}
|
||
if len(details) > 0 {
|
||
text += ". Причина: " + strings.Join(details, "; ")
|
||
}
|
||
if r := []rune(text); len(r) > 1000 {
|
||
text = string(r[:1000]) + "…"
|
||
}
|
||
return code, short, text
|
||
}
|
||
|
||
func containsStr(list []string, s string) bool {
|
||
for _, x := range list {
|
||
if x == s {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
// lkPatchRejected — заявка ЛК «Отклонена» с причиной в client_notation; помечается
|
||
// обработанной, чтобы поллер не брал её повторно. Возвращает HTTP-код и ответ ЛК.
|
||
func lkPatchRejected(ctx context.Context, ep LKEndpoint, id int, reason string) (int, string) {
|
||
lkMarkSent(id)
|
||
body, _ := json.Marshal(map[string]any{"claim": map[string]any{"status": "rejected", "client_notation": reason}})
|
||
pc, pb := lkPatch(ctx, ep, fmt.Sprintf("/claims/%d", id), body)
|
||
if pc >= 400 || pc == 0 {
|
||
log.Printf("lk-gateway: ЛК #%d: PATCH «Отклонена» не прошёл: HTTP %d %s", id, pc, pb)
|
||
} else {
|
||
log.Printf("lk-gateway: ЛК #%d: отклонена — %s", id, reason)
|
||
}
|
||
return pc, pb
|
||
}
|
||
|
||
// lkDataError — заявку ЛК нельзя отправлять из-за её данных: повтор не поможет, отказываем.
|
||
type lkDataError struct{ Code, Text string }
|
||
|
||
func (e *lkDataError) Error() string { return e.Text }
|
||
|
||
// lkRejectInvalid — отказ по данным заявки в реальном режиме (без имитации, до отправки в НРД).
|
||
func lkRejectInvalid(ctx context.Context, rc *RuntimeConfig, ep LKEndpoint, id int, de *lkDataError) {
|
||
reason := "Отказ: заявка не может быть исполнена"
|
||
if de.Code != "" {
|
||
reason = "Отказ: " + m2mCodeLabel(de.Code)
|
||
}
|
||
log.Printf("lk-gateway: ЛК #%d: отказ по данным заявки — %s (%s)", id, de.Text, de.Code)
|
||
if pc, pb := lkPatchRejected(ctx, ep, id, reason); pc >= 400 || pc == 0 {
|
||
raiseSystemAlert(rc, "ЛК: не удалось отклонить заявку", fmt.Sprintf("Заявка ЛК #%d отклонена (%s), но статус в ЛК не изменён: HTTP %d %s", id, reason, pc, pb))
|
||
}
|
||
}
|
||
|
||
// consumeEmulatedDecisions — решения контрагента от имитатора НРД идут тем же путём,
|
||
// что настоящий M2MTransferDecision из ИШ.
|
||
func (s *Server) consumeEmulatedDecisions(ctx context.Context) {
|
||
for {
|
||
select {
|
||
case <-ctx.Done():
|
||
return
|
||
case d := <-lkEmuSender.Decisions():
|
||
if d == nil {
|
||
continue
|
||
}
|
||
guid := string(d.Header.GUID)
|
||
log.Printf("lk-gateway: имитатор НРД: решение контрагента по %s", shortGUID(guid))
|
||
conf, rej := 0, 0
|
||
for _, sec := range d.Data.Securities {
|
||
if sec.TransferDecision.Confirmation != nil {
|
||
conf++
|
||
} else {
|
||
rej++
|
||
}
|
||
}
|
||
respTitle := "Решение по заявке (без отправки в НРД)"
|
||
if devMode(s.rc) {
|
||
respTitle = "Решение контрагента (имитатор НРД)"
|
||
}
|
||
globalLKFlows.stepIfExists(guid, "response", respTitle, fmt.Sprintf("Принято %d, отклонено %d.", conf, rej))
|
||
s.applyDecisionEffects(ctx, d, "имитатор НРД", lkEmuNote)
|
||
}
|
||
}
|
||
}
|
||
|
||
// applyDecisionEffects — M2MTransferDecision контрагента: сделка, статус в таблице
|
||
// обмена Fancy и в ЛК, снятие блокировок QUIK по отказанным бумагам, событие DEPO.
|
||
// note дописывается к статусу в ЛК.
|
||
func (s *Server) applyDecisionEffects(ctx context.Context, decision *m2m.M2MTransferDecision, source, note string) {
|
||
if err := s.svc.ApplyDecision(ctx, decision); err != nil {
|
||
// Детерминированные ошибки (сделка уже в терминальном статусе — напр.
|
||
// повторный/встречный Decision, «недопустимый переход FSM») повтором
|
||
// не лечатся. Логируем и помечаем обработанным, иначе поллер зациклится.
|
||
log.Printf("lk-gateway: %s: Decision GUID=%s не применён (пропуск): %v", source, decision.Header.GUID, err)
|
||
return
|
||
}
|
||
// Разбираем исход по бумагам: сколько принято/отклонено и с какими кодами.
|
||
conf, rej := 0, 0
|
||
var rejCodes []string
|
||
for _, sec := range decision.Data.Securities {
|
||
if sec.TransferDecision.Confirmation != nil {
|
||
conf++
|
||
} else if sec.TransferDecision.Rejection != nil {
|
||
rej++
|
||
rejCodes = append(rejCodes, sec.TransferDecision.Rejection.Codes...)
|
||
}
|
||
}
|
||
lkStatus, lkReason, fancyStatus, fancyVerdict := "done", "Перевод исполнен", "done", "approved"
|
||
switch {
|
||
case conf == 0 && rej > 0: // полный отказ принимающей стороны
|
||
lkStatus, fancyStatus, fancyVerdict = "rejected", "declined", "rejected"
|
||
lkReason = "Отказ принимающей стороны: " + rejectCodesLabel(rejCodes)
|
||
case rej > 0: // частичный приём
|
||
lkReason = fmt.Sprintf("Частично исполнено: принято %d, отклонено %d (%s)", conf, rej, rejectCodesLabel(rejCodes))
|
||
default: // всё принято
|
||
lkReason = "Перевод исполнен"
|
||
}
|
||
// Итог для заявки модуля ЛК (bj-lk), если перевод оттуда.
|
||
if e := lkFinalizeByGUID(ctx, s.rc, string(decision.Header.GUID), fancyStatus, fancyVerdict, lkReason); e != nil {
|
||
log.Printf("lk-gateway: %s: finalize(%s) GUID=%s: %v", source, fancyStatus, decision.Header.GUID, e)
|
||
}
|
||
_ = note // клиенту в ЛК — только короткий итог, подробности в журнале bj
|
||
if (realTestGet(string(decision.Header.GUID)) != nil || source == "поллер") && conf > 0 {
|
||
// согласие контрагента — ещё не исполнение: «Исполнена» в ЛК — по MS010 на наше поручение 16
|
||
// (outOnExecuted, тест «боевой ЛК → TEST3» — realtest.go); имитатор НРД MS010 не шлёт — итог сразу
|
||
globalLKFlows.stepIfExists(string(decision.Header.GUID), "response", "Контрагент согласился — ждём исполнения НРД",
|
||
fmt.Sprintf("Принято %d, отклонено %d. Статус в ЛК — «В работе» до отчёта НРД об исполнении (MS010).", conf, rej))
|
||
} else {
|
||
OnLKOutcome(ctx, s.rc, string(decision.Header.GUID), lkStatus, lkReason)
|
||
}
|
||
quikOnDecision(ctx, s.rc, s.svc, decision)
|
||
go depoOnCounterpartyDecision(s.rc, decision)
|
||
}
|
||
|
||
func newLKEmuSender() *mock.Sender {
|
||
cfg := mock.DefaultConfig()
|
||
cfg.DecisionDelay = lkEmuDecisionDelay
|
||
cfg.NSDSenderCode = "MC0010300000"
|
||
return mock.NewSender(cfg)
|
||
}
|
||
|
||
// applyNSDEmuSettings переносит настройки эмулятора НРД (страница «Эмуляторы»)
|
||
// в конфиг работающего сендера — без пересоздания: канал Decisions() привязан
|
||
// к экземпляру. Вызывается при старте и после сохранения настроек.
|
||
func applyNSDEmuSettings(rc *RuntimeConfig) {
|
||
if lkEmuSender == nil {
|
||
return
|
||
}
|
||
s := rc.Snapshot().NSDEmu.Defaults()
|
||
cfg := mock.DefaultConfig()
|
||
cfg.NSDSenderCode = "MC0010300000"
|
||
cfg.DecisionDelay = time.Duration(s.DelaySec) * time.Second
|
||
switch s.Outcome {
|
||
case "reject":
|
||
cfg.DefaultOutcome = mock.OutcomeReject
|
||
case "timeout":
|
||
cfg.DefaultOutcome = mock.OutcomeTimeout
|
||
}
|
||
cfg.RejectionCode = s.RejectCode
|
||
lkEmuSender.Reconfigure(cfg)
|
||
}
|
||
|
||
// lkEmuDecisionDelayNow — текущая задержка решения имитатора (для текстов в ЛК).
|
||
func lkEmuDecisionDelayNow(rc *RuntimeConfig) time.Duration {
|
||
return time.Duration(rc.Snapshot().NSDEmu.Defaults().DelaySec) * time.Second
|
||
}
|