feat(admin): dev-страница «Эмуляторы» — переключатели и настройки эмуляции НРД и МНП QUIK
В режиме разработки новая страница /admin/emulators с двумя клавишами: - «Эмулятор НРД» (флаг LK.EmulateNSD): заявки ЛК и ручного ввода идут через внутренний имитатор, в НРД ничего не отправляется. Настройки: задержка решения (0–300 с), исход (подтвердить/отклонить/не отвечать), код отказа M2M01–M2M09/18. Сендер перенастраивается в рантайме через потокобезопасный mock.Sender.Reconfigure — без перезапуска. - «Эмулятор МНП QUIK» (флаг QuikMNP.Emulate): шаги цикла без записи в боевую БД МНП. Настройки: задержка шага (0–60 с), точечные отказы по шагам (регистрация/подпись/отказ/зачисление) — эмулированный отказ ведёт себя как боевой: шаг OK=false, состояние не продвигается, повтор на следующем проходе. Настройки хранятся в setup.json (nsd_emu/mnp_emu), обратная совместимость — дефолты. Баннер «Режим эмуляции» показывается и при ручном включении эмуляторов (раньше зависел только от отсутствия ИШ). Пункт меню «Эмуляторы» и две плитки на дашборде — только в dev. Пути занесены в devOnlyPath. Попутно: исправлен race в TestLicenseActivateAndRenew (cleanup восстанавливал DefaultLicensePublicKey без moduleEnforceMu — падал под -race и на чистом HEAD).
This commit is contained in:
@@ -474,7 +474,28 @@ func quikActive(s QuikMNPSettings) bool {
|
||||
|
||||
// quikEmulated — шаг цикла в режиме имитации: связка переходит в состояние state, в МНП ничего не
|
||||
// пишется (нет пользователя QUIK для блокировок и лимитов); события в DEPO — как при реальной работе.
|
||||
func quikEmulated(rc *RuntimeConfig, guid, isin, action, state, what string) *QuikLink {
|
||||
// Настройки MNPEmu (страница «Эмуляторы»): StepDelaySec — пауза перед шагом (повторяет ожидание ответа
|
||||
// МНП), Fail<Шаг> — шаг завершается отказом, как боевой: состояние не продвигается, повтор на следующем проходе.
|
||||
func quikEmulated(ctx context.Context, rc *RuntimeConfig, guid, isin, action, state, what string) (*QuikLink, error) {
|
||||
emu := rc.Snapshot().MNPEmu
|
||||
if emu.StepDelaySec > 0 {
|
||||
d := time.Duration(emu.StepDelaySec) * time.Second
|
||||
if d > 60*time.Second {
|
||||
d = 60 * time.Second
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return globalQuikLinks.Get(guid, isin), ctx.Err()
|
||||
case <-time.After(d):
|
||||
}
|
||||
}
|
||||
if emu.FailFor(action) {
|
||||
err := fmt.Errorf("имитация отказа МНП: %s (настройки эмулятора, страница «Эмуляторы»)", what)
|
||||
globalQuikLinks.update(guid, isin, func(l *QuikLink) {
|
||||
l.Steps = append(l.Steps, QuikStep{At: time.Now(), Action: action, Reply: err.Error(), OK: false})
|
||||
})
|
||||
return quikFail(rc, guid, isin, action, err), err
|
||||
}
|
||||
l := globalQuikLinks.update(guid, isin, func(l *QuikLink) {
|
||||
if l.InstrNo == "" {
|
||||
l.InstrNo = "ИМИТ-" + shortGUID(guid)
|
||||
@@ -484,7 +505,7 @@ func quikEmulated(rc *RuntimeConfig, guid, isin, action, state, what string) *Qu
|
||||
})
|
||||
log.Printf("quik: %s %s (имитация, в МНП не записано): %s × %v", shortGUID(guid), what, isin, l.Qty)
|
||||
go depoOnQuik(rc, guid, isin, action, l.InstrNo)
|
||||
return l
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func quikStep(action string, aid int64, r QuikActionResult, ok bool) QuikStep {
|
||||
@@ -504,7 +525,7 @@ func QuikOutRegister(ctx context.Context, rc *RuntimeConfig, guid, clientID, con
|
||||
l.Direction, l.ClientID, l.Contract, l.ISIN, l.Qty = "out", clientID, contract, isin, qty
|
||||
})
|
||||
if s.Emulate {
|
||||
return quikEmulated(rc, guid, isin, "register", "registered", "вывод зарегистрирован, бумаги заблокированы"), nil
|
||||
return quikEmulated(ctx, rc, guid, isin, "register", "registered", "вывод зарегистрирован, бумаги заблокированы")
|
||||
}
|
||||
if strings.TrimSpace(recipientName) == "" {
|
||||
recipientName = m2mPartyName(recipientINN) // наименование из справочника НРД
|
||||
@@ -558,7 +579,7 @@ func QuikOutSign(ctx context.Context, rc *RuntimeConfig, guid, isin, comment str
|
||||
return l, nil
|
||||
}
|
||||
if s.Emulate {
|
||||
return quikEmulated(rc, guid, isin, "sign", "signed", "поручение подписано, бумаги списаны"), nil
|
||||
return quikEmulated(ctx, rc, guid, isin, "sign", "signed", "поручение подписано, бумаги списаны")
|
||||
}
|
||||
var no float64
|
||||
fmt.Sscan(l.InstrNo, &no)
|
||||
@@ -592,7 +613,7 @@ func QuikOutDeny(ctx context.Context, rc *RuntimeConfig, guid, isin, reason stri
|
||||
return l, nil
|
||||
}
|
||||
if s.Emulate {
|
||||
return quikEmulated(rc, guid, isin, "deny", "denied", "поручение снято ("+reason+")"), nil
|
||||
return quikEmulated(ctx, rc, guid, isin, "deny", "denied", "поручение снято ("+reason+")")
|
||||
}
|
||||
var no float64
|
||||
fmt.Sscan(l.InstrNo, &no)
|
||||
@@ -625,7 +646,7 @@ func QuikInDeposit(ctx context.Context, rc *RuntimeConfig, guid, clientID, contr
|
||||
l.Direction, l.ClientID, l.Contract, l.ISIN, l.Qty = "in", clientID, contract, isin, qty
|
||||
})
|
||||
if s.Emulate {
|
||||
return quikEmulated(rc, guid, isin, "deposit", "deposited", "ввод зарегистрирован, бумаги зачислены"), nil
|
||||
return quikEmulated(ctx, rc, guid, isin, "deposit", "deposited", "ввод зарегистрирован, бумаги зачислены")
|
||||
}
|
||||
if strings.TrimSpace(senderINN) != "" {
|
||||
_ = quikEnsureRecipient(ctx, s, senderINN, senderName)
|
||||
|
||||
Reference in New Issue
Block a user