Оповещения: группа «Администратор системы», блок «Кому писать» открыт; копии: адрес хранилища как в проводнике
Администратору (адрес из «Почты») — сбои (ИШ, связь с НРД, службы bj, DEPO) и обновления (новый выпуск bj, ИШ, лицензия, сертификаты); отделам — переводы и поручения. Существующие установки: администратор получает всё, что получал. Новые оповещения: «Доступно обновление bj» (раз на выпуск), «Служба … не работает / снова работает» (модули и служба ключей Валидаты). Резервные копии: \\сервер\папка или сервер + общая папка по полям → //сервер/папка; понятная причина отказа доступа. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
@@ -110,6 +110,8 @@ type UpdateSettings struct {
|
||||
// сменили, данные прошлой проверки к новому каналу не относятся: предлагать
|
||||
// по ним обновление нельзя (кнопка «Обновить» уходила бы в пустоту).
|
||||
ComponentsChannel string `json:"components_channel,omitempty"`
|
||||
// Notified — выпуск, о котором уже написали администратору («Доступно обновление bj»): по разу на выпуск.
|
||||
Notified string `json:"notified,omitempty"`
|
||||
}
|
||||
|
||||
// NewsSettings — лента новостей (события системы, окна техработ, обновления
|
||||
@@ -358,6 +360,7 @@ func NewRuntimeConfig(path string) (*RuntimeConfig, error) {
|
||||
if err := rc.load(); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return nil, err
|
||||
}
|
||||
migrateNotifyRoutes(&rc.data) // и на новой установке: версия маршрутов сохранится с первой записью настроек
|
||||
// Гарантируем разумные дефолты.
|
||||
if rc.data.Crypto.Provider == "" {
|
||||
rc.data.Crypto.Provider = "stub"
|
||||
@@ -595,18 +598,25 @@ type IncomingNotifySettings struct {
|
||||
Emails string `json:"emails"` // депозитарий (через запятую)
|
||||
EmailsBack string `json:"emails_back"` // бэк-офис
|
||||
EmailsMiddle string `json:"emails_middle"` // мидл-офис
|
||||
// Маршрутизация: тип уведомления → группы через запятую (depo,back,middle).
|
||||
// Типы: incoming (входящие переводы), orders (сроки депо-поручений), quik (QUIK МНП), system (прочие).
|
||||
// Маршрутизация: тип уведомления → группы через запятую (admin,depo,back,middle). Адрес группы admin
|
||||
// (администратор системы) — Alerts.SMTP.To. Типы: incoming (входящие переводы), orders (сроки депо-поручений),
|
||||
// quik (QUIK МНП), infra (сбои шлюза, связи с НРД, служб, DEPO), updates (обновления, лицензия, сертификаты),
|
||||
// system (прочие).
|
||||
Routes map[string]string `json:"routes"`
|
||||
// RoutesV — версия маршрутов: 2 — с группой «Администратор системы» (06.10.2026), см. migrateNotifyRoutes.
|
||||
RoutesV int `json:"routes_v,omitempty"`
|
||||
}
|
||||
|
||||
// Группы получателей и типы уведомлений (порядок — для формы).
|
||||
var notifyGroups = []struct{ Key, Title string }{{"depo", "Депозитарий"}, {"back", "Бэк-офис"}, {"middle", "Мидл-офис"}}
|
||||
// Группы получателей и типы уведомлений (порядок — для формы). Администратор системы — тот, кто отвечает за
|
||||
// сервер и ИТ: ему сбои и обновления, которые отделам (депозитарий, бэк- и мидл-офис) не нужны.
|
||||
var notifyGroups = []struct{ Key, Title string }{{"admin", "Администратор"}, {"depo", "Депозитарий"}, {"back", "Бэк-офис"}, {"middle", "Мидл-офис"}}
|
||||
var notifyKinds = []struct{ Key, Title string }{
|
||||
{"incoming", "Входящие переводы (получен / принят)"},
|
||||
{"orders", "Сроки депо-поручений НРД (MS010 5 мин / 30 дней)"},
|
||||
{"quik", "QUIK МНП (ошибки блокировки / списания / зачисления)"},
|
||||
{"system", "Прочие системные оповещения"},
|
||||
{"infra", "Сбои: шлюз НРД, связь с НРД, службы bj, обмен с DEPO"},
|
||||
{"updates", "Обновления bj и шлюза, лицензия, сертификаты"},
|
||||
{"system", "Прочие оповещения"},
|
||||
}
|
||||
|
||||
// visibleNotifyKinds — типы уведомлений этой установки: ошибки QUIK МНП — только при модуле в лицензии.
|
||||
@@ -621,16 +631,18 @@ func visibleNotifyKinds(rc *RuntimeConfig) []struct{ Key, Title string } {
|
||||
return out
|
||||
}
|
||||
|
||||
// defaultNotifyRoutes — маршруты по умолчанию, пока оператор не настроил свои.
|
||||
var defaultNotifyRoutes = map[string]string{"incoming": "depo,back", "orders": "depo,back", "quik": "back,middle", "system": "back"}
|
||||
// defaultNotifyRoutes — маршруты по умолчанию, пока оператор не настроил свои: сбои и обновления —
|
||||
// администратору, переводы и поручения — отделам.
|
||||
var defaultNotifyRoutes = map[string]string{"incoming": "depo,back", "orders": "depo,back", "quik": "back,middle",
|
||||
"infra": "admin", "updates": "admin", "system": "admin,back"}
|
||||
|
||||
// RouteHas — получает ли группа уведомления данного типа.
|
||||
// RouteHas — получает ли группа уведомления данного типа (тип, которого нет в сохранённых маршрутах, — по умолчанию).
|
||||
func (s IncomingNotifySettings) RouteHas(kind, group string) bool {
|
||||
r := s.Routes
|
||||
if r == nil {
|
||||
r = defaultNotifyRoutes
|
||||
route, ok := s.Routes[kind]
|
||||
if !ok {
|
||||
route = defaultNotifyRoutes[kind]
|
||||
}
|
||||
for _, g := range strings.Split(r[kind], ",") {
|
||||
for _, g := range strings.Split(route, ",") {
|
||||
if strings.TrimSpace(g) == group {
|
||||
return true
|
||||
}
|
||||
@@ -651,7 +663,44 @@ func (s IncomingNotifySettings) GroupEmails(group string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Recipients — адреса всех групп, на которые направлен тип уведомления (без дублей).
|
||||
// migrateNotifyRoutes — переход на маршруты с группой «Администратор системы». До неё адрес из раздела «Почта»
|
||||
// получал все системные оповещения, а сбои и обновления были «прочими»: на существующей установке администратор
|
||||
// получает всё то же, что и раньше (лишнее снимается в таблице), сбои и обновления идут тем же группам, что
|
||||
// «прочие». Галочка «уведомления о переводах» заменена строкой таблицы: выключенная — пустой маршрут входящих.
|
||||
// Новая установка (ни адреса, ни маршрутов) — маршруты по умолчанию.
|
||||
func migrateNotifyRoutes(s *Settings) {
|
||||
in := &s.Incoming
|
||||
if in.RoutesV >= 2 {
|
||||
return
|
||||
}
|
||||
in.RoutesV = 2
|
||||
if in.Routes == nil && strings.TrimSpace(s.Alerts.SMTP.To) == "" && !in.Enabled {
|
||||
in.Enabled = true
|
||||
return
|
||||
}
|
||||
old := in.Routes
|
||||
if old == nil {
|
||||
old = map[string]string{"incoming": "depo,back", "orders": "depo,back", "quik": "back,middle", "system": "back"}
|
||||
}
|
||||
r := map[string]string{}
|
||||
for k, v := range old {
|
||||
r[k] = v
|
||||
}
|
||||
r["infra"], r["updates"] = old["system"], old["system"]
|
||||
if strings.TrimSpace(s.Alerts.SMTP.To) != "" {
|
||||
for _, k := range []string{"orders", "quik", "infra", "updates", "system"} {
|
||||
r[k] = strings.Trim(r[k]+",admin", ",")
|
||||
}
|
||||
}
|
||||
if !in.Enabled {
|
||||
r["incoming"] = ""
|
||||
}
|
||||
in.Enabled = true
|
||||
in.Routes = r
|
||||
}
|
||||
|
||||
// Recipients — адреса отделов (депозитарий, бэк-, мидл-офис), на которые направлен тип уведомления (без дублей);
|
||||
// с администратором системы — notifyRecipients.
|
||||
func (s IncomingNotifySettings) Recipients(kind string) string {
|
||||
seen := map[string]bool{}
|
||||
var out []string
|
||||
@@ -671,6 +720,15 @@ func (s IncomingNotifySettings) Recipients(kind string) string {
|
||||
return strings.Join(out, ", ")
|
||||
}
|
||||
|
||||
// notifyRecipients — все адреса типа уведомления: администратор системы (адрес из «Почты») и отделы.
|
||||
func notifyRecipients(s Settings, kind string) string {
|
||||
admin := ""
|
||||
if s.Incoming.RouteHas(kind, "admin") {
|
||||
admin = s.Alerts.SMTP.To
|
||||
}
|
||||
return mergeEmails(admin, s.Incoming.Recipients(kind))
|
||||
}
|
||||
|
||||
func (r *RuntimeConfig) UpdateIncoming(s IncomingNotifySettings) error {
|
||||
r.mu.Lock()
|
||||
r.data.Incoming = s
|
||||
|
||||
Reference in New Issue
Block a user