Files
Bridge-and-Join-s/internal/lkgateway/wizchapters.go
T
zuevav 73b246831c Пароль домена не попадает в страницу; домен в мастере — открыт, крупно, группы; справочник DEPO без переводов; 1С — бэк-офис
- Безопасность: в мастере подпись к паролю выводила сам сохранённый пароль служебной записи домена — теперь только «задан/введён» (тест: пароль не попадает в HTML). Список сессий в настройках больше не содержит токены (по ним можно было войти под чужим именем) — короткий ID.
- Домен: после подключения и «Доверять» блок остаётся открытым, страница возвращается к нему; карточка «Домен подключён» и «Найден домен» — крупно; поиск сотрудников и групп; группе — доступ с ролью (ADGroups), список групп с «убрать».
- «Обмен с DEPO»: справочник FTV_M2M_HANDBOOK считается и когда переводов ещё нет (на свежей установке писало «не создана»).
- Модуль 1С: onec-back «1С: база бэк-офиса (тест)» вместо мидл-офиса (в лицензиях не выдавался).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
2026-10-07 01:19:30 +03:00

223 lines
9.8 KiB
Go

package lkgateway
// Мастер настройки — 4 главы вместо 10 шагов (05.10.2026). Внутри глав — прежние шаги (адреса ?step=N не
// меняются: на них ссылаются проверка готовности, письма, кнопки «Исправить»). Шаг показывается, только
// если он нужен этой установке:
// - не входит в лицензию (учёт DEPO и QUIK — «quik-mnp», модули) — шага нет вовсе;
// - сделан установщиком без участия человека (база bj, Валидата) — шага нет в пути, в главе «Сервер»
// это показано строкой «сделано автоматически».
import "strings"
// wizChapters — главы мастера: название и шаги по порядку.
var wizChapters = []struct {
Title string
Steps []int
}{
{"Сервер", []int{1, 2, 6, 12}},
{"Учёт и организация", []int{3, 4, 5}},
{"НРД", []int{7, 8}},
{"Запуск", []int{11, 9, 13, 10}},
}
// wizStepTitles — короткие названия шагов (подзаголовки внутри главы).
var wizStepTitles = map[int]string{
1: "Лицензия", 2: "База bj", 6: "Валидата", 12: "Время и сеть",
3: "Учёт DEPO", 4: "Организация", 5: "QUIK МНП",
7: "Шлюз НРД", 8: "Ключ подписи",
11: "Модули", 9: "Оповещения и копии", 13: "Учётные записи", 10: "Проверка",
}
// wizRequired — шаги, без которых обмен не работает: на них мастер возвращает, пока они не сделаны.
var wizRequired = map[int]bool{1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true, 8: true}
// wizModules — лицензируемые модули, которые настраиваются в главе «Запуск».
var wizModules = []string{"web-cabinet", "esia-finance", "quik-export", "npr", "onec-back"}
// WizStep — шаг в пути мастера.
type WizStep struct {
N int // номер шага в адресе (?step=N)
Num int // порядковый номер в списке шагов слева (1, 2, 3… — только шаги этой установки)
Title string
Done bool
Current bool
}
// WizChapter — глава в шапке мастера.
type WizChapter struct {
N int
Title string
Steps []WizStep
Done bool
Current bool
First int // первый шаг главы (переход по клику)
}
// wizVisible — нужен ли шаг этой установке (лицензия и автоматика).
func wizVisible(rc *RuntimeConfig, n int, done map[int]bool) bool {
switch n {
case 2, 6: // база bj и Валидата обычно ставятся установщиком — шаг нужен, только если не сделано
return !done[n]
case 3, 5:
return moduleAllowed(rc, "quik-mnp")
case 11:
for _, m := range wizModules {
if moduleAllowed(rc, m) {
return true
}
}
return false
}
return true
}
// wizPath — шаги этой установки по порядку и главы для шапки.
func wizPath(rc *RuntimeConfig, done map[int]bool, current int) (path []int, chapters []WizChapter) {
for ci, ch := range wizChapters {
c := WizChapter{N: ci + 1, Title: ch.Title, Done: true}
for _, n := range ch.Steps {
if !wizVisible(rc, n, done) {
continue
}
path = append(path, n)
c.Steps = append(c.Steps, WizStep{N: n, Num: len(path), Title: wizStepTitles[n], Done: done[n], Current: n == current})
if c.First == 0 {
c.First = n
}
if !done[n] {
c.Done = false
}
if n == current {
c.Current = true
}
}
if len(c.Steps) == 0 {
continue
}
if len(c.Steps) == 1 { // в главе один шаг (остальные не в лицензии) — глава называется по нему: «Организация»
c.Title = c.Steps[0].Title
}
// переход по главе — на первый несделанный шаг главы
for _, s := range c.Steps {
if !s.Done {
c.First = s.N
break
}
}
chapters = append(chapters, c)
}
return path, chapters
}
// wizChaptersLine — подзаголовок мастера: «Четыре главы: сервер, организация, НРД, запуск».
func wizChaptersLine(chapters []WizChapter) string {
words := []string{"", "Одна глава", "Две главы", "Три главы", "Четыре главы"}
var names []string
for _, c := range chapters {
t := c.Title
if t != "НРД" && t != "" {
r := []rune(t)
t = strings.ToLower(string(r[:1])) + string(r[1:])
}
names = append(names, t)
}
if len(chapters) == 0 || len(chapters) >= len(words) {
return ""
}
return words[len(chapters)] + ": " + strings.Join(names, ", ") + "."
}
// wizNeighbours — предыдущий и следующий шаг в пути (0 — нет).
func wizNeighbours(path []int, current int) (prev, next int) {
for i, n := range path {
if n == current {
if i > 0 {
prev = path[i-1]
}
if i+1 < len(path) {
next = path[i+1]
}
return
}
}
if len(path) > 0 { // текущий шаг не в пути (открыт по прямой ссылке) — «Далее» ведёт в начало пути
next = path[0]
}
return
}
// wizDefaultStep — куда открывается мастер без ?step: первый обязательный несделанный шаг, иначе проверка.
func wizDefaultStep(path []int, done map[int]bool) int {
for _, n := range path {
if wizRequired[n] && !done[n] {
return n
}
}
return 10
}
// WizModule — модуль по лицензии в главе «Запуск»: что делает, готов ли, где настраивается.
type WizModule struct {
Key, Title, What, Status, Link string
Ready bool
}
// wizModuleViews — модули этой лицензии (без лицензии модуля в мастере нет).
func wizModuleViews(rc *RuntimeConfig, s Settings) []WizModule {
var out []WizModule
for _, key := range wizModules {
if !moduleAllowed(rc, key) {
continue
}
m := WizModule{Key: key}
switch key {
case "web-cabinet":
m.Title = "Личный кабинет клиента"
m.What = "Наш кабинет для клиентов депозитария: программа bj-lk на веб-сервере организации — клиент подаёт поручения на перевод, видит портфель и ход перевода."
m.Link = "/admin/setup?from=wizard#cabinet"
m.Ready = s.LK.APIKey != ""
m.Status = map[bool]string{true: "ключ кабинета выдан — кабинет подключается к bj", false: "кабинет ещё не установлен: скачайте дистрибутив и ключ в разделе модуля"}[m.Ready]
case "esia-finance":
m.Title = "ЕСИА Финанс"
m.What = "Интеграция с платформой личного кабинета ЕСИА Финанс: заявки клиентов на перевод приходят в bj, статусы и портфель уходят обратно."
m.Link = "/admin/setup?from=wizard#lk"
m.Ready = s.LK.PublicURL != "" && (s.LK.Prod.LastOK || s.LK.Test.LastOK)
switch {
case m.Ready:
m.Status = "адрес кабинета задан, связь с API проверена"
case s.LK.PublicURL != "":
m.Status = "адрес кабинета задан, связь с API ещё не проверена"
default:
m.Status = "не настроено: адрес кабинета и доступ к API"
}
case "quik-export":
m.Title = "Экспорт биржевой информации QUIK"
m.What = "Выгрузка данных QUIK (позиции, сделки, клиенты) в базу bj — для кабинета клиента и риск-монитора."
m.Link = "/admin/setup?from=wizard#quikexport"
m.Ready = unitState("bj-quikexport").State == "active"
m.Status = map[bool]string{true: "служба экспорта работает", false: "служба экспорта не запущена — настройте подключение в разделе модуля"}[m.Ready]
case "npr":
m.Title = "Риск-монитор НПР / УДС"
m.What = "Нормативы покрытия риска и уровень достаточности средств клиентов маржинальной торговли по данным экспорта QUIK."
m.Link = "/admin/setup?from=wizard#quikexport"
m.Ready = s.NPR.Enabled && s.NPR.DSN != ""
m.Status = map[bool]string{true: "включён, база экспорта QUIK подключена", false: "не включён"}[m.Ready]
case "onec-back":
m.Title = "1С: база бэк-офиса"
m.What = "Связь с базой 1С бэк-офиса (модуль в разработке): пока — подключение к опубликованной базе и его проверка."
m.Link = "/admin/setup?from=wizard#onec"
m.Ready = s.OneC.CheckOK
switch {
case m.Ready:
m.Status = "подключение к базе 1С проверено"
case s.OneC.URL != "":
m.Status = "адрес базы задан, проверка не прошла"
default:
m.Status = "не настроено: адрес публикации базы, пользователь и пароль 1С"
}
}
out = append(out, m)
}
return out
}