Перейти к основному содержимому

UC-NTF-001. Пользователь настраивает уведомления

Назначение

Описывает сценарий: пользователь (инвестор/агент/менеджер) настраивает свои уведомления — включает/выключает виды по каналам, задаёт тихие часы, локаль, GlobalMute. Связан с контекстом notifications.

Metadata

ПолеЗначение
IDUC-NTF-001
Контекстnotifications (см. docs/06-services/notifications/)
Типосновной
Статусdraft

Актор

ПолеЗначение
РольИнвестор / Агент / МенеджерПИН (любой авторизованный пользователь)
Permissions / scopenotifications.settings.manage (own action)
TenantTenantId = CompanyId или null для инвестора (ADR-0052)
Record-levelвладелец = UserId (актор)

Предусловие

  • Пользователь авторизован (JWT identity).
  • Каталог NotificationKind/Channel и дефолтная политика заданы (SEED).

Триггер

ПолеЗначение
ИсточникUI button (экран «Уведомления» в профиле)
Триггерящий элементстраница /profile → раздел уведомлений (front-end)

Основной Поток

  1. Пользователь открывает экран «Уведомления»; фронт запрашивает текущие настройки.
  2. Фронт вызывает GET /api/public/notifications/settings (get-my-notification-settings) — получает эффективную модель (дефолты + явные переопределения, тихие часы, локаль).
  3. Пользователь меняет переключатели (Kind × Channel), задаёт тихие часы (22:00–08:00, Europe/Moscow), выбирает локаль; фронт формирует запрос.
  4. Фронт вызывает PUT /api/public/notifications/settings (update-my-notification-settings) с моделью настроек.
  5. Система проверяет вход: уникальность пар (Kind, Channel), валидный IANA-timezone тихих часов, поддерживаемая локаль; при нарушении — VALIDATION_FAILED.
  6. Настройки пользователя UserNotificationSettingsEntity создаются или обновляются (набор предпочтений NotificationPreferenceEntity заменяется целиком); Critical-виды остаются включёнными — opt-out по ним игнорируется (INV-UNS-4); кэш резолва настроек инвалидируется (событие SettingsChanged).
  7. Изменения сохраняются в одном согласованном изменении; в ответ возвращается эффективная модель настроек.
  8. Фронт обновляет UI (перезапрос настроек); переключатели отражают сохранённое состояние.

Альтернативные Потоки

A1. Первая настройка (записи нет)

  1. На шаге 3 записи UserNotificationSettingsEntity ещё нет — GET вернул чистые дефолты (IsDefault = true).
  2. PUT создаёт запись настроек для пользователя (владелец = UserId, tenant по ADR-0052).

Возвращается к основному потоку на шаг 7.

A2. Конкурентная правка (две вкладки)

  1. На шаге 7 обнаружена конкурентная правка (версия записи устарела) → NTF_SETTINGS_CONFLICT (409).
  2. Фронт перечитывает настройки и предлагает повторить (побеждает первая зафиксированная правка; проигравший видит конфликт и переприменяет свой набор поверх актуального).

A3. Включение GlobalMute

  1. Пользователь включает GlobalMute. Запись сохраняется (INV-UNS-3): при последующих уведомлениях создаются только каналы для Urgency = Critical (безопасность/деньги/дедлайны), остальные подавляются.
  2. In-app лента при этом продолжает наполняться (подавление касается interruptive-каналов доставки, не самой записи уведомления) — детали резолва каналов см. notification.md.

A4. Попытка отключить Critical-вид

  1. Пользователь снимает переключатель у Critical-вида (напр. SecurityNewSignIn, BookingPaid, BookingExpiringSoon). PUT принимается (валидна запись предпочтения), но при резолве каналов opt-out по Critical игнорируется (INV-UNS-4) — вид всё равно доставляется; пользователь может лишь сменить набор каналов, но не отключить вид полностью.

A5. Тихие часы через полночь

  1. Пользователь задаёт окно 22:00–08:00 (Start > End → интервал через полночь), timezone Europe/Moscow.
  2. Запись валидна; при последующих уведомлениях interruptive-каналы в этом окне деферятся (ScheduledAt = открытие окна, INV-UNS-5/INV-NTF-4), не-interruptive (InApp/Email) — доставляются сразу.

Постусловие

  • UserNotificationSettingsEntity пользователя отражает выбор; резолв каналов при последующих уведомлениях использует новые настройки (INV-UNS-3/4/5).
  • Появилась/обновилась запись настроек + owned Preferences; при первом сохранении — владелец = UserId, tenant по ADR-0052.
  • Опубликовано доменное SettingsChanged; кэш резолва настроек инвалидирован. Kafka-события не публикуются (внутренние настройки, ADR-0056).
  • Уже созданные (in-flight) уведомления, для которых каналы были резолвлены до сохранения, новые настройки не пересматривают — правила применяются к уведомлениям, создаваемым после инвалидации кэша.

Инварианты и валидации

  • INV-UNS-1 — одна запись на UserId; правит только владелец (UserId = actor), иначе NTF_SETTINGS_FORBIDDEN (здесь own-action, посторонний доступ исключён).
  • INV-UNS-2 — пары (Kind, Channel) в Preferences уникальны; дубль → VALIDATION_FAILED (NTF_PREFERENCE_DUPLICATE на уровне записи предпочтения).
  • INV-UNS-3 — резолв канала = явное IsEnabled, иначе дефолт политики Kind; GlobalMute оставляет только Urgency = Critical.
  • INV-UNS-4Critical-вид нельзя отключить (opt-out игнорируется), можно лишь сменить каналы.
  • INV-UNS-5 — тихие часы применяются только к interruptive-каналам; пустое QuietHours → без дефера.
  • Валидация входа: (Kind, Channel) уникальны; QuietHours.Start/End — валидное локальное время, TimeZoneId — существующая IANA-зона (окно может пересекать полночь); Locale — поддерживаемый BCP-47. Нарушение любого — VALIDATION_FAILED (400), запись не меняется.

Возможные Ошибки

КодHTTPУсловиеПоведение клиента
UNAUTHENTICATED401нет/невалидный JWTредирект на вход
VALIDATION_FAILED400дубли (Kind, Channel) / невалидный timezone/локальподсветить поля
NTF_SETTINGS_CONFLICT409конкурентная правкаперечитать и повторить

Доменные События

СобытиеКогда поднимаетсяSubscribers
SettingsChangedпосле сохранения настроекin-process invalidate кэша резолва настроек

Kafka Интеграционные События

СобытиеTopicКогда публикуетсяConsumers
не публикуется (внутренние настройки, ADR-0056)

Acceptance Criteria

#КритерийПроверка
AC1Выключенный (Kind, Channel) не порождает уведомление этого каналаинтеграционный: opt-out + событие → нет NotificationEntity этого канала
AC2Critical-вид (напр. SecurityNewSignIn) нельзя отключитьпопытка opt-out → уведомление всё равно создаётся
AC3Тихие часы деферят interruptive-канал (не Critical)событие в окне → ScheduledAt = открытие окна
AC4Первая настройка создаёт запись; повтор — идемпотентен по содержимомуPUT дважды → одна запись, одинаковый результат
AC5GlobalMute подавляет всё, кроме Criticalвключить mute + событие не-Critical → нет уведомлений interruptive-каналов; Critical — доставляется
AC6Дубль (Kind, Channel) отклоняетсяPUT с двумя одинаковыми парами → VALIDATION_FAILED, запись не меняется (INV-UNS-2)
AC7Конкурентная правка — первая побеждаетпараллельные PUT двух вкладок → одна успех, вторая NTF_SETTINGS_CONFLICT
AC8Невалидный timezone/локаль отклоняетсяPUT с несуществующей IANA-зоной/неподдерживаемой локалью → VALIDATION_FAILED

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные Документы