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

PUT /api/public/notifications/settings — Обновить мои настройки уведомлений

Назначение

Авторизованный пользователь меняет свои настройки уведомлений (UserNotificationSettingsEntity) — per-Kind per-Channel opt-in/out, тихие часы, локаль, GlobalMute. Запись создаётся при первом сохранении и полностью заменяет набор Preferences. Виды Critical отключить нельзя — их opt-out игнорируется (INV-UNS-4). Owning-контекст — notifications, feature group settings.

Metadata

ПолеЗначение
Сервис/контекстnotifications
API areapublic (JWT пользователя)
Feature groupsettings
Статусdraft
Документdocs/06-services/notifications/api/public/update-my-notification-settings.md

Актор и доступ

ПроверкаЗначение
Actor typeUser
Auth policy / Permissionsnotifications.settings.manage (own)
Record-levelвладелец = UserId (актор); правит только свои настройки (INV-UNS-1)
Tenant isolationTenantId = компания пользователя или null, из claims (не из body)
Auditupdate (Version)

HTTP-контракт

ПолеЗначение
MethodPUT
Route/api/public/notifications/settings
Success status200
Idempotency headerне применяется (полная замена — идемпотентна по содержимому)
Correlationсквозной trace через заголовок traceparent

Входные данные / параметры запроса

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
UserIdidentity claims (JWT sub)GuidДане Guid.Emptyвладелец записи
LocalebodystringНетBCP-47 из поддерживаемых; default ru
GlobalMutebodyboolНетdefault false
QuietHoursbodyQuietHoursInput?Нет{ Start, End, TimeZoneId }; TimeZoneId — валидный IANA
PreferencesbodyIReadOnlyList<PreferenceInput>Нетэлементы уникальны по (Kind, Channel) (INV-UNS-2); Kind/Channel — значения enum

PreferenceInput{ Kind (NotificationKind), Channel (Channel), IsEnabled (bool) }. QuietHoursInput{ Start (TimeOnly), End (TimeOnly), TimeZoneId (string) }.

Модель ответа NotificationSettingsModel

Та же модель, что в get-my-notification-settings.md (эффективные настройки после применения).

Алгоритм

  1. Контекст и доступ. Операцию выполняет авторизованный пользователь (UserId из JWT; нет валидного JWT → UNAUTHENTICATED); правит только свои настройки (INV-UNS-1).
  2. Проверка входа. Элементы Preferences уникальны по (Kind, Channel) (INV-UNS-2); Kind/Channel — допустимые значения enum; Locale из поддерживаемых; QuietHours.TimeZoneId — валидный IANA. Нарушение → VALIDATION_FAILED.
  3. Применение. Если запись настроек есть — заменить набор Preferences, обновить Locale/GlobalMute/ тихие часы; если записи нет — создать её для UserId/TenantId. Для видов Critical opt-out игнорируется (INV-UNS-4).
  4. Согласованность. Агрегат настроек вместе с owned-Preferences сохраняется в одном согласованном изменении; правка не должна затирать конкурентную (при конфликте версий — NTF_SETTINGS_CONFLICT); кэш резолва настроек инвалидируется (сигнал SettingsChanged).
  5. Результат. Возвращаются эффективные настройки после применения (та же модель, что в чтении), HTTP 200.

Диаграмма

Побочные эффекты

ТипДетали
Кэш / проекцииинвалидация кэша резолва настроек (сигнал SettingsChanged)

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
UNAUTHENTICATED401нет/невалидный JWT«Требуется авторизация»no retry
VALIDATION_FAILED400дубли (Kind, Channel) / невалидный Kind/Channel/TimeZoneId/Locale«Проверьте настройки»no retry
NTF_SETTINGS_CONFLICT409конкурентная правка (конфликт версий)«Повторите сохранение»retry (перечитать)

Семантика применения

  • Полная замена Preferences. Переданный список целиком заменяет прежний набор переопределений: пары (Kind, Channel), отсутствующие в запросе, возвращаются к дефолту политики (их прежнее явное значение теряется). Пустой Preferences = «сбросить все переопределения к дефолтам». Дубли (Kind, Channel) в запросе запрещены (INV-UNS-2).
  • Critical неотключаем. Для видов Urgency = Critical opt-out игнорируется (INV-UNS-4): даже переданный IsEnabled = false не отключает доставку (канал сменить можно, полностью замолчать — нет).
  • Не ретроактивно. Новые настройки влияют только на уведомления, резолвящиеся после сохранения; уже созданные NotificationEntity (Pending/Sent) не переоцениваются.
  • Тихие часы. QuietHours — окно в локальном времени пользователя; Start > End трактуется как интервал через полночь. TimeZoneId — валидный IANA, иначе VALIDATION_FAILED. Применяются только к interruptive-каналам (INV-UNS-5); Critical их игнорирует.
  • Первое сохранение создаёт запись для UserId/TenantId (INV-UNS-1); последующие — обновляют.

Пред- и постусловия

  • Предусловие. Пользователь авторизован; правит только свои настройки; вход валиден (уникальность (Kind, Channel), enum, IANA, поддерживаемый Locale).
  • Постусловие (успех). Запись настроек создана/обновлена (Version ++), Preferences заменены, Locale/GlobalMute/QuietHours применены (с учётом INV-UNS-4); кэш резолва инвалидирован; возвращена эффективная модель.

Совместимость и наблюдаемость

  • Новые виды Kind/каналы — additive (появляются в дефолтах). Удаление enum-значений — breaking.
  • Logs: settings-updated (UserId, число Preferences); metrics: notifications_settings_updated_total.

Acceptance criteria

  • Given пользователь без записи настроек, when сохраняет Preferences+QuietHours, then 200, запись создана, возвращены эффективные настройки.
  • Given ранее заданное переопределение, отсутствующее в новом запросе, when сохранение, then эта пара (Kind, Channel) вернулась к дефолту (IsDefault = true в чтении).
  • Given попытка opt-out для Critical-вида, when сохранение, then 200, но доставка Critical сохранена (INV-UNS-4).
  • Given дубль (Kind, Channel) или невалидный TimeZoneId/Locale/Kind/Channel, when сохранение, then 400 VALIDATION_FAILED.
  • Given конкурентная правка с устаревшей версией, when сохранение, then 409 NTF_SETTINGS_CONFLICT; проигравший перечитывает и повторяет.
  • Given отсутствует/просрочен JWT, when сохранение, then 401 UNAUTHENTICATED.

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

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

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