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

POST /api/admin/notifications/templates — Создать шаблон уведомления

Назначение

Администратор платформы заводит черновик шаблона уведомления (NotificationTemplateEntity) для пары (Kind, Locale): набор плейсхолдеров и тело на каждый канал (ChannelTemplateEntity). Шаблон создаётся в статусе Draft; публикация — отдельной операцией update-template (Publish). Owning-контекст — notifications, feature group templates.

Metadata

ПолеЗначение
Сервис/контекстnotifications
API areaadmin
Feature grouptemplates
Статусdraft
Документdocs/06-services/notifications/api/admin/create-template.md

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

ПроверкаЗначение
Actor typeOperator(Admin)
Auth policy / Permissionsnotifications.templates.manage
Scope (RBAC)платформа (каталог шаблонов)
Tenant isolationне tenanted (платформенный каталог, ADR-0052 п.3)
Auditcreate (CreatedByUserId)

HTTP-контракт

ПолеЗначение
MethodPOST
Route/api/admin/notifications/templates
Success status201
Idempotency headerне применяется
Correlationсквозной trace через заголовок traceparent

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
KindbodyNotificationKindДазначение enum
LocalebodystringДаBCP-47 из поддерживаемых
NamebodystringДа1..128
Descriptionbodystring?Нет<=512
DefaultUrgencybodyNotificationUrgency?Нетdefault Normal
PlaceholdersbodyIReadOnlyList<NotificationPlaceholder>Данепустой; допустимые для Kind
ChannelBodiesbodyIReadOnlyList<ChannelBodyInput>Да≥ 1; уникальны по Channel

ChannelBodyInput{ Channel (Channel), Subject? (string), Title? (string), Body (string), Format (TemplateBodyFormat), IsEnabled (bool) }; плейсхолдеры тела ⊆ Placeholders (INV-TPL-2).

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

Проекция NotificationTemplateEntity: TemplateId (Guid), Kind, Locale, Code ("{Kind}:{Locale}"), Name, Status (Draft), Placeholders, Channels (IReadOnlyList<ChannelBodyModel>), Version, CreatedAt.

Алгоритм

  1. Контекст и доступ. Операцию выполняет администратор платформы; требуется право notifications.templates.manage; каталог шаблонов платформенный (не tenanted). Нет права → FORBIDDEN.
  2. Проверка входа. Kind/Locale/Channel — допустимые значения enum; плейсхолдеры, использованные в теле, входят в объявленный Placeholders (INV-TPL-2, иначе → NTF_TEMPLATE_UNKNOWN_PLACEHOLDER); задан хотя бы один ChannelBody, ChannelBodies уникальны по Channel. Прочие нарушения формата/ограничений → VALIDATION_FAILED.
  3. Проверка уникальности. Шаблона с Code = "{Kind}:{Locale}" ещё не должно существовать; если он уже есть → NTF_TEMPLATE_DUPLICATE.
  4. Создание. Завести шаблон в статусе Draft с вычисленным Code и телом на каждый указанный канал.
  5. Согласованность. Шаблон вместе с телами каналов сохраняется в одном согласованном изменении; кэш резолва шаблонов инвалидируется (сигнал TemplateChanged).
  6. Результат. Возвращается карточка созданного шаблона (Status = Draft), HTTP 201.

Диаграмма

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
FORBIDDEN403нет notifications.templates.manage«Недостаточно прав»no retry
VALIDATION_FAILED400enum/тело/каналы вне ограничений«Проверьте шаблон»no retry
NTF_TEMPLATE_UNKNOWN_PLACEHOLDER400плейсхолдер тела вне Placeholders (INV-TPL-2)«Недопустимый плейсхолдер»no retry
NTF_TEMPLATE_DUPLICATE409шаблон (Kind, Locale) уже есть«Шаблон уже существует»no retry

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

  • Предусловие. Актор авторизован и имеет notifications.templates.manage; шаблона с Code = "{Kind}:{Locale}" ещё нет; Kind/Locale/каналы — допустимые значения; тела содержат только плейсхолдеры из объявленного Placeholders.
  • Постусловие (успех). Создан шаблон в Status = Draft (Version = 1, CreatedByUserId = актор) с телами на каждый указанный канал; кэш резолва инвалидирован. Draft не участвует в резолве (INV-TPL-5): до публикации (update-template.mdActive) enqueue/консюмеры для этого Kind/ Locale его не выбирают — если действующего шаблона нет, они получат NTF_TEMPLATE_NOT_RESOLVED.

Валидации (полностью)

  • Kind ∈ enum NotificationKind; Locale — поддерживаемый BCP-47; Name 1..128; Description <=512.
  • Placeholders непуст и состоит из значений NotificationPlaceholder, допустимых для данного Kind (подмножество, задаваемое PayloadModel источника).
  • ChannelBodies ≥ 1, уникальны по Channel; каждый Channel ∈ enum; Format ∈ {Text,Html,Markdown}.
  • Все плейсхолдеры {...}, встречающиеся в Subject/Title/Body, входят в Placeholders (INV-TPL-2), иначе NTF_TEMPLATE_UNKNOWN_PLACEHOLDER. Любое иное нарушение формата/ограничений → VALIDATION_FAILED.
  • Уникальность Code (INV-TPL-1): гонка двух параллельных создателей одной пары (Kind, Locale) — один побеждает, второй получает NTF_TEMPLATE_DUPLICATE (страхуется уникальным ключом Code).

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

  • Новые каналы/плейсхолдеры — additive. Logs: template-created (Code, Kind); metrics: notifications_templates_created_total.

Acceptance criteria

  • Given админ и отсутствующий (Kind, Locale), when создаёт шаблон с ≥ 1 каналом и корректными плейсхолдерами, then 201, Status = Draft, Version = 1; шаблон не резолвится при enqueue до публикации.
  • Given шаблон для (Kind, Locale) уже существует, when создание, then 409 NTF_TEMPLATE_DUPLICATE.
  • Given тело содержит плейсхолдер вне Placeholders, when создание, then 400 NTF_TEMPLATE_UNKNOWN_PLACEHOLDER, шаблон не создан.
  • Given пустой ChannelBodies или дубликат Channel, when создание, then 400 VALIDATION_FAILED.
  • Given актор без notifications.templates.manage, when создание, then 403 FORBIDDEN.

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

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

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