Files
shop-server/docs/superpowers/specs/2026-05-18-notifications-improvements-design.md
T

7.2 KiB
Raw Blame History

Улучшение системы оповещений

Дата: 2026-05-18

Проблемы

  1. Дублирование кода входа в Telegram — настройка authCodeDuplicate не работает, т.к. resolveAuthCodeTargets импортирован, но не вызывается в диспетчере уведомлений.
  2. Двойное уведомление о новом заказеorder:created и order:created:admin оба проходят через resolveAdminNotificationTargets('order:created'), что даёт 2 одинаковых оповещения админу.
  3. Пропущен PAID в русских лейблах статусов — в telegram-templates.js и email-templates.js нет ключа PAID.
  4. Текст оповещений слишком краткий — нет подсказок о следующих действиях для пользователя и админа.

Решения

1. Дублирование кода входа (гибридный подход)

  • Письмо с кодом входа уходит мгновенно через прямой вызов sendLoginCodeEmail в issueEmailCode (без изменений).
  • В dispatchNotification для AUTH_CODE_REQUESTED используется отдельный резолвер resolveAuthCodeTargets вместо общих resolveUserNotificationTargets / resolveAdminNotificationTargets.
  • resolveAuthCodeTargets уже правильно реализован: отправляет email пользователю, а Telegram — только если payload.isAdmin === true и включена настройка authCodeDuplicate.

Изменяемые файлы:

  • server/src/index.js — добавить условие в dispatchNotification для AUTH_CODE_REQUESTED

2. Двойное уведомление о новом заказе

  • Удалить ORDER_CREATED из adminEventFieldMap в preferences.js.
  • Админ получает уведомление о новом заказе только через событие 'order:created:admin'.

Изменяемые файлы:

  • server/src/lib/notifications/preferences.js — удалить [ORDER_CREATED]: "newOrder" из adminEventFieldMap

3. Добавление PAID в лейблы статусов

  • PAID: 'Оплачен' в telegram-templates.js и email-templates.js.

Изменяемые файлы:

  • server/src/lib/notifications/templates/telegram-templates.js
  • server/src/lib/notifications/templates/email-templates.js

4. Расширение текста оповещений

4a. Пользователь: Заказ создан

В событие ORDER_CREATED передаётся deliveryType:

  • deliveryType === 'pickup'"Ваш заказ №XXX успешно создан. Товаров: X | Сумма: XXX ₽. Ожидает оплаты."
  • deliveryType === 'delivery'"Ваш заказ №XXX успешно создан. Товаров: X | Сумма: XXX ₽. Оплата будет доступна после уточнения стоимости доставки."

Изменяемые файлы:

  • server/src/routes/user-orders.js — добавить deliveryType в payload события ORDER_CREATED
  • server/src/lib/notifications/templates/telegram-templates.js — обновить renderOrderCreatedTg
  • server/src/lib/notifications/templates/email-templates.js — обновить renderOrderCreatedEmail

4b. Новое событие: deliveryFeeAdjusted

При корректировке стоимости доставки (PATCH /api/admin/orders/:id/delivery-fee) отправлять пользователю уведомление:

  • Текст: "Стоимость доставки скорректирована. Ожидает оплаты."

Событие: 'order:deliveryFeeAdjusted' — добавляется в NOTIFICATION_EVENTS.

Изменяемые файлы:

  • shared/constants/notification-events.js — добавить DELIVERY_FEE_ADJUSTED
  • shared/constants/notification-events.d.ts — добавить 'order:deliveryFeeAdjusted'
  • server/src/lib/notifications/preferences.js — добавить DELIVERY_FEE_ADJUSTED в userEventFieldMap
  • server/src/routes/api/admin-orders.js — emit DELIVERY_FEE_ADJUSTED после обновления
  • server/src/routes/user/notifications.js — добавить поле в API
  • client/src/pages/me/ui/sections/NotificationsPage.tsx — добавить переключатель
  • server/src/lib/notifications/templates/telegram-templates.js — новый рендерер
  • server/src/lib/notifications/templates/email-templates.js — новый рендерер
  • server/src/lib/notifications/channels/telegram-channel.js — зарегистрировать шаблон
  • server/src/lib/notifications/channels/email-channel.js — зарегистрировать шаблон
  • server/src/index.js — подписаться на событие

Также нужно добавить миграцию Prisma для поля deliveryFeeAdjusted в NotificationPreference.

4c. Админ: Новый заказ

В событие 'order:created:admin' передаётся deliveryType:

  • deliveryType === 'delivery' → добавить "Скорректируйте стоимость доставки"
  • deliveryType === 'pickup' → как сейчас

Изменяемые файлы:

  • server/src/routes/user-orders.js — добавить deliveryType в payload 'order:created:admin'
  • server/src/lib/notifications/templates/telegram-templates.js — обновить renderAdminOrderCreatedTg
  • server/src/lib/notifications/templates/email-templates.js — обновить renderAdminOrderCreatedEmail

Схема данных

NotificationPreference (добавить поле)

model NotificationPreference {
  id                   String @id @default(cuid())
  userId               String @unique
  globalEnabled        Boolean @default(true)
  orderCreated         Boolean @default(true)
  orderStatusChanged   Boolean @default(true)
  orderMessageReceived Boolean @default(true)
  paymentStatusChanged Boolean @default(true)
  deliveryFeeAdjusted  Boolean @default(true)  // НОВОЕ
  createdAt            DateTime @default(now())
  updatedAt            DateTime @updatedAt
}

Обработка ошибок

  • Если при delivery-fee эмите не указан userId — событие не отправляется.
  • Если рендерер не найден для события — telegram-channel.js и email-channel.js логируют warning (существующее поведение).

Тестирование

  • server/src/lib/notifications/__tests__/preferences.test.js — добавить тест для resolveAuthCodeTargets в контексте dispatchNotification
  • Проверить, что после удаления ORDER_CREATED из adminEventFieldMap админ не получает дубликат