44f00a006a
- bj-igate-admin: пустые строки в числовых/интервальных параметрах канала ИШ разбирал с ошибкой и не загружал канал («There are no active channels», API 500 «more than one Channel…»); теперь NULL, как пишет интерфейс ИШ; старые каналы чинятся при «Создать канал»; папка архивов 1042S — по имени - проверка «Шлюз НРД»: канал контура действительно загружен и включён (api/admin/channels/wsl) - «Учёт DEPO»: нет процедур bj2depo — жёлтое «не забудьте доустановить», не блокирует - HTTPS: перенаправление с http:// включено по умолчанию, только когда HTTPS реально слушает порт - мастер: шаг «Учётные записи» (роли, создание, «Придумать пароль») перед проверкой; письма с проверки убраны; форма «создать» не меняет существующую запись с тем же логином Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
210 lines
9.1 KiB
Go
210 lines
9.1 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"}
|
|
|
|
// 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]
|
|
}
|
|
out = append(out, m)
|
|
}
|
|
return out
|
|
}
|