Мастер: одинаковые отметки шагов, «Обновление» после «Лицензии», переходы

- ключ, подключённый мастером ключа, засчитывается сразу и в мастере, и на главной
  (сверка справочника шлюза отстаёт)
- точка в заголовке шага — то же «выполнено», что в колонке (с «Настроить позже»)
- выход из мастера — «Выйти из мастера», не «Настроить позже»
- шаг «Обновление» — сразу после «Лицензии»; на шаге 1 — указание на него, без второй кнопки
- «Далее»/«Назад» со шага вне пути (сделан установщиком) — соседи по порядку глав
- последний шаг при несделанном обязательном — «Вернуться к шагу …», не «Перейти к работе»

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
This commit is contained in:
fontvielle
2026-10-21 23:50:02 +03:00
parent f1065e369a
commit 90aacf9ef4
7 changed files with 65 additions and 33 deletions
+2
View File
@@ -351,6 +351,7 @@ func (h *setupHandlers) keyWizardUseMedia(w http.ResponseWriter, r *http.Request
h.keyWiz.set(3, "ok", "ИШ перезапущен и работает: "+ishMsg)
h.keyWiz.set(4, "ok", "Ключ с флешки подключён — проверьте подключения на странице «Статус»")
h.keyWiz.Done = true
keyWizInstalled.Store(true)
h.keyWiz.mu.Unlock()
_ = os.RemoveAll(staging)
refreshCertStore(h.rc)
@@ -593,6 +594,7 @@ func (h *setupHandlers) keyWizardInstall(w http.ResponseWriter, r *http.Request)
h.keyWiz.set(4, "ok", "Ключ установлен — проверьте подключения на странице «Статус»")
}
h.keyWiz.Done = true
keyWizInstalled.Store(true)
h.keyWiz.mu.Unlock()
// Ключ уже на флешке и в ИШ — распаковка staging больше не нужна.
+9 -5
View File
@@ -210,16 +210,20 @@ func TestWizardChaptersWithoutLicense(t *testing.T) {
rc, _ := NewRuntimeConfig(t.TempDir() + "/setup.json")
done := map[int]bool{2: true, 6: true}
path, chapters := wizPath(rc, done, 4)
want := []int{14, 1, 12, 4, 7, 8, 9, 13, 10} // обновление — первый пункт; без лицензии нет учёта DEPO, QUIK и модулей; база и Валидата — сделаны; учётные записи — перед проверкой
want := []int{1, 14, 12, 4, 7, 8, 9, 13, 10} // обновление — сразу после лицензии; без лицензии нет учёта DEPO, QUIK и модулей; база и Валидата — сделаны; учётные записи — перед проверкой
if fmt.Sprint(path) != fmt.Sprint(want) {
t.Fatalf("путь %v, ожидался %v", path, want)
}
if len(chapters) != 5 || !chapters[2].Current || chapters[2].Steps[0].N != 4 {
if len(chapters) != 4 || !chapters[1].Current || chapters[1].Steps[0].N != 4 {
t.Fatalf("главы: %+v", chapters)
}
if p, n := wizNeighbours(path, 4); p != 12 || n != 7 {
t.Fatalf("соседи шага 4: %d %d", p, n)
}
// шаг «База bj» сделан установщиком и в пути его нет (открыт по ссылке) — соседи по порядку глав, не начало
if p, n := wizNeighbours(path, 2); p != 14 || n != 12 {
t.Fatalf("соседи шага 2 вне пути: %d %d", p, n)
}
if d := wizDefaultStep(path, map[int]bool{1: true, 4: false}); d != 4 {
t.Fatalf("по умолчанию: %d", d)
}
@@ -233,10 +237,10 @@ func TestWizardChaptersWithoutLicense(t *testing.T) {
if len(wizModuleViews(rc, rc.Snapshot())) != 0 {
t.Fatal("без лицензии модулей в мастере быть не должно")
}
if chapters[2].Title != "Организация" {
t.Fatalf("без учёта DEPO и QUIK глава «Организация», а не %q", chapters[2].Title)
if chapters[1].Title != "Организация" {
t.Fatalf("без учёта DEPO и QUIK глава «Организация», а не %q", chapters[1].Title)
}
if l := wizChaptersLine(chapters); l != "Пять глав: обновление, сервер, организация, НРД, запуск." {
if l := wizChaptersLine(chapters); l != "Четыре главы: сервер, организация, НРД, запуск." {
t.Fatalf("подзаголовок: %q", l)
}
}
+6 -1
View File
@@ -198,6 +198,8 @@ type WizardData struct {
Chapters []WizChapter
Prev, Next int
StepDone bool
Unfinished int // обязательный несделанный шаг (последний шаг ведёт к нему, а не «к работе»)
UnfinishedT string // его название
StepRequired bool
StepTitle string
ChapterTitle string
@@ -293,7 +295,7 @@ func (h *setupHandlers) renderWizard(w http.ResponseWriter, r *http.Request) {
// установщиком. Без ?step — первый несделанный обязательный шаг, а если всё сделано — проверка.
d.Done.Users = doneUsers()
// Выполнено ли — одно правило с плашкой на главной (wizDone), с учётом «Настроить позже».
done := wizDone(r.Context(), h.rc, d.ValidataVer, d.Done.Certs || h.keyWiz.installed())
done := wizDone(r.Context(), h.rc, d.ValidataVer, d.Done.Certs || h.keyWiz.installed() || keyWizInstalled.Load())
// «отложено» показываем, только пока шаг не настроен по-настоящему
d.Deferred = map[int]bool{9: def.Steps[9] && !d.Done.Alerts, 11: def.Steps[11] && !d.Done.Modules, 13: def.Steps[13] && !d.Done.Users}
path, _ := wizPath(h.rc, done, 0)
@@ -307,6 +309,9 @@ func (h *setupHandlers) renderWizard(w http.ResponseWriter, r *http.Request) {
d.Path, d.Chapters = wizPath(h.rc, done, step)
d.Prev, d.Next = wizNeighbours(d.Path, step)
d.StepDone, d.StepRequired, d.StepTitle = done[step], wizRequired[step], wizStepTitles[step]
if u := wizDefaultStep(path, done); wizRequired[u] && !done[u] && u != step {
d.Unfinished, d.UnfinishedT = u, wizStepTitles[u]
}
for _, c := range d.Chapters {
if c.Current {
d.ChapterTitle = c.Title
+7 -1
View File
@@ -6,6 +6,7 @@ package lkgateway
import (
"context"
"sync"
"sync/atomic"
"time"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/cryptocli"
@@ -40,13 +41,18 @@ func setupProbes(rc *RuntimeConfig) (string, int) {
return setupProbeCache.validata, setupProbeCache.certs
}
// keyWizInstalled — мастер ключа дошёл до конца в этом запуске bj (ключ подключён, подпись подтверждена).
// Сверка справочника шлюза асинхронная и отстаёт; по этой отметке шаг «Ключ подписи» выполнен сразу —
// и в мастере, и в плашке на главной.
var keyWizInstalled atomic.Bool
// setupSteps — шаги мастера этой установки и выполнены ли они: ровно тот путь и те же
// правила, что в мастере (wizPath + wizDone), чтобы плашка на главной не расходилась с ним.
func setupSteps(rc *RuntimeConfig) []SetupStep {
validata, certs := setupProbes(rc)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
done := wizDone(ctx, rc, validata, certs > 0)
done := wizDone(ctx, rc, validata, certs > 0 || keyWizInstalled.Load())
path, _ := wizPath(rc, done, 0)
var steps []SetupStep
for _, n := range path {
@@ -85,7 +85,7 @@
<ol class="wiz-steps">
{{range .Chapters}}{{range .Steps}}<li><a href="/admin/wizard?step={{.N}}" class="wiz-st{{if .Done}} done{{end}}{{if .Current}} cur{{end}}"{{if .Current}} aria-current="step"{{end}}><span class="wiz-num">{{if not .Done}}{{.Num}}{{end}}</span><span>{{.Title}}</span></a></li>{{end}}{{end}}
</ol>
<div class="wiz-rail-foot">Всё сохраняется сразу — можно прерваться и продолжить позже.<br><a href="/admin/">Настроить позже →</a></div>
<div class="wiz-rail-foot">Всё сохраняется сразу — можно прерваться и продолжить позже.<br><a href="/admin/">Выйти из мастера →</a></div>
</aside>
<div class="wiz-card">
@@ -97,7 +97,7 @@
{{if eq .Step 1}}
{{/* восстановление из резервной копии — только на первой странице (/first-run), в мастере его нет */}}
<div class="card">
<h2><span class="dot {{if .Done.License}}ok{{else}}err{{end}}"></span>Лицензия</h2>
<h2><span class="dot {{if .StepDone}}ok{{else}}err{{end}}"></span>Лицензия</h2>
{{if .Done.License}}<p>Ключ продукта открывает модули bj и обновления.</p>
<dl class="wiz-kv">
<dt>Организация</dt><dd>{{.Lic.Tenant}}</dd>
@@ -109,11 +109,8 @@
{{if and .Update .Update.HasUpdate}}
<div class="wiz-info" style="display:block">
<p style="margin:0 0 6px;font-weight:600">Есть новая версия bj — {{.Update.Available}}</p>
<p class="muted" style="margin:0 0 10px;font-size:13px">С исправлениями ядра, установщика и служб. Поставьте её до настройки: bj перезапустится примерно на 10 секунд.<br>{{.Update.Message}}</p>
<form method="post" action="/admin/setup/update/apply" style="margin:0">
<input type="hidden" name="return_to" value="/admin/wizard?step=1">
<button type="submit" class="btn">Обновить сейчас</button>
</form>
<p class="muted" style="margin:0 0 10px;font-size:13px">Поставьте её до настройки — следующий шаг «Обновление».</p>
<a href="/admin/wizard?step=14" class="btn" style="text-decoration:none">К обновлению →</a>
</div>
{{else if .Done.License}}<div class="wiz-ok"><i class="wiz-ic"></i><span>Лицензия активна{{if .Update}}, установлена последняя версия bj{{end}}.</span></div>{{end}}
{{if .Done.License}}<details style="margin-top:12px"><summary style="cursor:pointer;color:var(--accent);font-size:13px">Сменить ключ продукта (обычно не нужно)</summary>
@@ -136,7 +133,7 @@
{{/* ============= ШАГ 3: Учёт DEPO (Fancy) — по экранам ============= */}}
{{if eq .Step 3}}
<div class="card">
<h2><span class="dot {{if .Done.Depo}}ok{{else}}err{{end}}"></span>Учётная система DEPO (Fancy)</h2>
<h2><span class="dot {{if .StepDone}}ok{{else}}err{{end}}"></span>Учётная система DEPO (Fancy)</h2>
{{if not .DepoLicensed}}
<p class="muted">Модуль обмена с DEPO и QUIK не входит в текущую лицензию — шаг пропускается.</p>
{{else}}
@@ -334,7 +331,7 @@
<tr><td>Обработка событий (процедуры)</td><td>{{if .Settings.Fancy.DepoProcCheckOK}}<span style="color:var(--ok)">✓ {{.Settings.Fancy.DepoProcCheck}}</span>{{else if .Settings.Fancy.DepoProcCheck}}<span style="color:var(--warn)">⚠ {{.Settings.Fancy.DepoProcCheck}}</span>{{else}}<span class="muted">не проверялась — «Перепроверить всё»</span>{{end}}</td></tr>
</table>
{{if .Settings.Fancy.Server}}{{template "accPrefix" .}}{{end}}
{{if .Done.Depo}}<p>Учёт DEPO подключён и проверен. Дальше — «Организация»: bj предложит реквизиты вашей организации из DEPO.</p>{{else}}<p class="muted" style="font-size:13px">Не всё проверено — можно продолжить настройку (шаг 4 и дальше) и вернуться сюда позже. Пока обработка не работает, записи bj ждут в таблицах обмена со статусом TODO и будут обработаны, когда она заработает.</p>{{end}}
{{if .Done.Depo}}<p>Учёт DEPO подключён и проверен. Дальше — «Организация»: bj предложит реквизиты вашей организации из DEPO.</p>{{else}}<p class="muted" style="font-size:13px">Не всё проверено — можно продолжить настройку (шаг «Организация» и дальше) и вернуться сюда позже. Пока обработка не работает, записи bj ждут в таблицах обмена со статусом TODO и будут обработаны, когда она заработает.</p>{{end}}
<form method="post" action="/admin/setup/wizard-depo/recheck" style="margin:0"><button type="submit" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">Перепроверить всё</button></form>
{{end}}
{{end}}
@@ -344,7 +341,7 @@
{{/* ============= ШАГ 2: База bj (PostgreSQL) ============= */}}
{{if eq .Step 2}}
<div class="card">
<h2><span class="dot {{if .Done.Postgres}}ok{{else}}err{{end}}"></span>База данных bj (PostgreSQL)</h2>
<h2><span class="dot {{if .StepDone}}ok{{else}}err{{end}}"></span>База данных bj (PostgreSQL)</h2>
<p>База самого bj: журнал заявок и поручений, история обмена с НРД и DEPO, пользователи.</p>
{{if and .Done.Postgres (pgLocal .Settings.Postgres.DSN)}}<div class="wiz-ok"><i class="wiz-ic"></i><span>База готова{{if .PGSummary}}: {{.PGSummary}}{{end}}. Её создал установщик — ничего делать не нужно.</span></div>
<details style="margin-top:14px" {{if .Flash}}open{{end}}><summary style="cursor:pointer;color:var(--accent)">Хранить базу bj на своём сервере PostgreSQL (необязательно)</summary>
@@ -426,7 +423,7 @@
{{/* ============= ШАГ 4: Наша организация ============= */}}
{{if eq .Step 4}}
<div class="card">
<h2><span class="dot {{if .Done.Org}}ok{{else}}err{{end}}"></span>Наша организация</h2>
<h2><span class="dot {{if .StepDone}}ok{{else}}err{{end}}"></span>Наша организация</h2>
<p>От имени вашей организации bj отправляет и принимает переводы. {{if .DepoLicensed}}Реквизиты bj берёт из DEPO — проверьте и сохраните.{{else}}Найдите себя в справочнике участников НРД.{{end}}</p>
{{if .Suggest}}{{if .Suggest.OrgName}}{{if not .Done.Org}}<div class="wiz-ok" style="margin:0 0 14px"><i class="wiz-ic"></i><span><strong>Определено из DEPO</strong>: {{.Suggest.OrgName}}, ИНН {{.Suggest.OrgINN}}, код {{.Suggest.OrgCode}}{{if .Suggest.AccountID}}, счёт НРД {{.Suggest.AccountID}}{{end}}. Поля ниже заполнены — проверьте и нажмите «Сохранить».</span></div>{{end}}{{end}}{{end}}
{{with .Fancy}}{{template "orgSection" .}}{{end}}
@@ -438,7 +435,7 @@
{{/* ============= ШАГ 5: QUIK МНП ============= */}}
{{if eq .Step 5}}
<div class="card">
<h2><span class="dot {{if .Done.Quik}}ok{{else}}err{{end}}"></span>QUIK</h2>
<h2><span class="dot {{if .StepDone}}ok{{else}}err{{end}}"></span>QUIK</h2>
<p>Адрес и логин модуля неторговых поручений — у вашего администратора QUIK. Если модуля ещё нет, его подключает ARQA Technologies — письмо для них готово ниже.</p>
{{if .DepoLicensed}}{{template "letterCard" (letter "arqa")}}{{end}}
{{if not .DepoLicensed}}<p class="muted">Модуль неторговых поручений не входит в текущую лицензию — шаг пропускается.</p>
@@ -455,7 +452,7 @@
{{/* ============= ШАГ 6: Валидата ============= */}}
{{if eq .Step 6}}
<div class="card">
<h2><span class="dot {{if .Done.Crypto}}ok{{else}}err{{end}}"></span>СКЗИ «Валидата Клиент L»</h2>
<h2><span class="dot {{if .StepDone}}ok{{else}}err{{end}}"></span>СКЗИ «Валидата Клиент L»</h2>
<p>СКЗИ «Валидата Клиент L» проверяет подписи НРД на квитанциях и сообщениях и нужно для работы с ключами и сертификатами (раздел «Ключ»).</p>
{{if .ValidataVer}}
<dl class="wiz-kv"><dt>Версия</dt><dd>{{.ValidataVer}}</dd>{{if devOn}}<dt>Каталог</dt><dd><code>/opt/Validata/VDCSP</code></dd>{{end}}</dl>
@@ -479,7 +476,7 @@
{{/* ============= ШАГ 7: Шлюз НРД (до ключа: ключ ставится в профиль ИШ) ============= */}}
{{if eq .Step 7}}
<div class="card">
<h2><span class="dot {{if .Done.NSD}}ok{{else}}err{{end}}"></span>Шлюз НРД</h2>
<h2><span class="dot {{if .StepDone}}ok{{else}}err{{end}}"></span>Шлюз НРД</h2>
<p class="muted" style="font-size:13.5px;margin:4px 0 0">bj сам ставит и настраивает шлюз (ИШ) и обменивается через него переводами с НРД. Доступ к контурам НРД — по заявке (как подать — ниже).</p>
{{template "igateInstall" .}}
<div class="wiz-info" style="margin:12px 0 0"><span><b>Доступ к контурам НРД</b> — заявкой на сайте НРД, письмами НРД не подключает. Страница сервиса <a href="https://www.nsd.ru/services/novye-servisy/moex-most-dlya-m2m/" target="_blank" rel="noopener">MOEX МОСТ для M2M</a>, раздел «Как подключиться»: заявка на тестирование — система МОСТ, тип «M2M Автотестирование», затем «M2M Подключение»; после тестирования — договор с НРД и боевой контур.</span></div>
@@ -507,7 +504,7 @@
{{/* ============= ШАГ 8: Ключ и сертификаты (после шлюза: ключ ставится в профиль ИШ) ============= */}}
{{if eq .Step 8}}
<div class="card">
<h2><span class="dot {{if .Done.Certs}}ok{{else}}err{{end}}"></span>Ключ и сертификаты</h2>
<h2><span class="dot {{if .StepDone}}ok{{else}}err{{end}}"></span>Ключ и сертификаты</h2>
<p>Ключ электронной подписи организации для обмена с НРД. Его выдаёт Удостоверяющий центр Московской биржи: вам передадут архив ключа и пароль.</p>
{{if not .IgateOK}}<div class="wiz-info"><span>Ключ ставится в шлюз НРД — сначала установите <a href="/admin/wizard?step=7">шлюз</a>.</span></div>
{{else if .KeyWiz}}<div class="keywiz">{{template "keyWizBody" .KeyWiz}}</div>{{end}}
@@ -549,7 +546,7 @@
{{/* ============= ШАГ 12: Время и сеть (глава «Сервер») ============= */}}
{{if eq .Step 12}}
<div class="card">
<h2><span class="dot {{if .Done.Server}}ok{{else}}warn{{end}}"></span>Время и сеть</h2>
<h2><span class="dot {{if .StepDone}}ok{{else}}warn{{end}}"></span>Время и сеть</h2>
<p>Часовой пояс, точное время и адрес сервера в сети.</p>
{{$tz := serverTimeZone}}
<form method="post" action="/admin/setup/timezone" id="wiz-tz" class="wiz-row">
@@ -626,7 +623,7 @@
{{/* ============= ШАГ 11: Модули по лицензии (глава «Запуск») ============= */}}
{{if eq .Step 11}}
<div class="card">
<h2><span class="dot {{if .Done.Modules}}ok{{else}}warn{{end}}"></span>Модули</h2>
<h2><span class="dot {{if .StepDone}}ok{{else}}warn{{end}}"></span>Модули</h2>
<p>Модули вашей лицензии уже установлены. Каждый настраивается в своём разделе — сейчас или позже.</p>
{{range .Modules}}
<div style="border:1px solid var(--border);border-radius:8px;padding:10px 12px;margin:8px 0">
@@ -647,7 +644,7 @@
{{/* ============= ШАГ 9: Оповещения и резервные копии ============= */}}
{{if eq .Step 9}}
<div class="card">
<h2><span class="dot {{if .Done.Alerts}}ok{{else}}warn{{end}}"></span>Оповещения и резервные копии</h2>
<h2><span class="dot {{if .StepDone}}ok{{else}}warn{{end}}"></span>Оповещения и резервные копии</h2>
<p>bj напишет на почту, если НРД не отвечает, перевод застрял{{if .DepoLicensed}}, DEPO не обработал поручение{{end}} или истекает ключ, — и будет сам делать копии сервера. Настройте почтовый сервер, затем кому писать: администратору системы — о сбоях и обновлениях, отделам — о переводах и поручениях. Обмен работает и без этого, но о сбое тогда никто не узнает вовремя.</p>
<div class="wiz-info" style="margin:10px 0"><span>Сейчас: почта — {{if and .Settings.Alerts.SMTP.Enabled .Settings.Alerts.SMTP.Host .Settings.Alerts.SMTP.To}}<b style="color:var(--ok)">✓ настроена</b>{{else}}<b style="color:var(--warn)">не настроена</b>{{end}}; Zabbix — {{if .Settings.Alerts.Zabbix.Enabled}}<b style="color:var(--ok)">✓ включён</b>{{else}}выключен{{end}}; резервные копии — {{if .Settings.Backup.Enabled}}<b style="color:var(--ok)">✓ включены</b>{{else}}<b style="color:var(--warn)">выключены</b>{{end}}. Шаг закрывается, когда есть оповещения (почта или Zabbix) и включены копии.</span></div>
<style>.wiz9 label:not(:has(input[type=checkbox])){display:block;margin:0}.wiz9 label input:not([type=checkbox]),.wiz9 label select,.wiz9 .grid input,.wiz9 .grid select{width:100%;box-sizing:border-box;margin-top:4px}</style>
@@ -666,7 +663,7 @@
{{/* ============= ШАГ 13: Учётные записи (глава «Запуск», перед проверкой) ============= */}}
{{if eq .Step 13}}
<div class="card">
<h2><span class="dot {{if .Done.Users}}ok{{else}}warn{{end}}"></span>Учётные записи</h2>
<h2><span class="dot {{if .StepDone}}ok{{else}}warn{{end}}"></span>Учётные записи</h2>
<p>У каждого, кто работает с bj, — своя учётная запись: свой пароль и свои права, а в журнале видно, кто что сделал. Заведите сотрудников по ролям; права, пароли и вход через Active Directory меняются позже в разделе Настройка → Пользователи.</p>
{{with .Fancy}}{{template "accountsAdd" (withRet . "/admin/wizard?step=13")}}{{template "accountsAD" (withRet . "/admin/wizard?step=13")}}
{{end}}
@@ -679,7 +676,7 @@
{{if eq .Step 10}}
<div class="card">
{{if devOn}}
<h2><span class="dot {{if .Done.TestRun}}ok{{else}}err{{end}}"></span>Тестовая заявка</h2>
<h2><span class="dot {{if .StepDone}}ok{{else}}err{{end}}"></span>Тестовая заявка</h2>
<p>Прогон полного цикла: создание заявки → валидация → подпись → отправка в НРД (или mock) → ожидание Decision → подтверждение.</p>
<div class="help-block">
@@ -7,6 +7,6 @@
{{define "wizNav"}}
<div class="wiz-nav">
{{if .Prev}}<a href="/admin/wizard?step={{.Prev}}" class="wiz-back">← Назад</a>{{else}}<span></span>{{end}}
{{if .Next}}{{if or .StepDone (not .StepRequired)}}<a href="/admin/wizard?step={{.Next}}" class="btn wiz-next">Далее →</a>{{else}}<a href="/admin/wizard?step={{.Next}}" class="btn wiz-skip" title="Можно вернуться к этому позже">Пропустить пока →</a>{{end}}{{else}}<a href="/admin/" class="btn wiz-next">Перейти к работе →</a>{{end}}
{{if .Next}}{{if or .StepDone (not .StepRequired)}}<a href="/admin/wizard?step={{.Next}}" class="btn wiz-next">Далее →</a>{{else}}<a href="/admin/wizard?step={{.Next}}" class="btn wiz-skip" title="Можно вернуться к этому позже">Пропустить пока →</a>{{end}}{{else if .Unfinished}}<a href="/admin/wizard?step={{.Unfinished}}" class="btn wiz-next">Вернуться к шагу «{{.UnfinishedT}}» →</a>{{else}}<a href="/admin/" class="btn wiz-next">Перейти к работе →</a>{{end}}
</div>
{{end}}
+23 -5
View File
@@ -1,6 +1,6 @@
package lkgateway
// Мастер настройки — 4 главы вместо 10 шагов (05.10.2026). Внутри глав — прежние шаги (адреса ?step=N не
// Мастер настройки — главы вместо длинного списка шагов (05.10.2026). Внутри глав — прежние шаги (адреса ?step=N не
// меняются: на них ссылаются проверка готовности, письма, кнопки «Исправить»). Шаг показывается, только
// если он нужен этой установке:
// - не входит в лицензию (учёт DEPO и QUIK — «quik-mnp», модули) — шага нет вовсе;
@@ -14,8 +14,7 @@ var wizChapters = []struct {
Title string
Steps []int
}{
{"Обновление", []int{14}},
{"Сервер", []int{1, 2, 6, 12}},
{"Сервер", []int{1, 14, 2, 6, 12}}, // обновление — сразу после лицензии: без неё обновлений нет
{"Учёт и организация", []int{3, 4, 5}},
{"НРД", []int{7, 8}},
{"Запуск", []int{11, 9, 13, 10}},
@@ -141,8 +140,27 @@ func wizNeighbours(path []int, current int) (prev, next int) {
return
}
}
if len(path) > 0 { // текущий шаг не в пути (открыт по прямой ссылке) — «Далее» ведёт в начало пути
next = path[0]
// текущий шаг не в пути (сделан установщиком, открыт по прямой ссылке) — соседи по порядку глав,
// а не начало мастера
pos := map[int]int{}
for _, c := range wizChapters {
for _, n := range c.Steps {
pos[n] = len(pos)
}
}
cur, known := pos[current]
if !known {
if len(path) > 0 {
next = path[0]
}
return
}
for _, n := range path {
if pos[n] < cur {
prev = n
} else if next == 0 {
next = n
}
}
return
}