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

Consumer: Referral Events → Notifications (notifications-referral-events)

Маппит события сервиса referral (реферальные связи, привязки лидов) в NotificationKind и ставит уведомления в очередь доставки. Дом payload — referral/events/; целевой агрегат — notification.md; общий алгоритм постановки в очередь — crm-events.md.

Назначение

Consumer group notifications-referral-events уведомляет реферера о создании/закрытии реферальной связи и о привязке приведённого лида. persist-then-send; кросс-контекстной записи нет.

Owning-сервис и триггер

ПолеЗначение
Сервис/контекстnotifications
Consumer groupnotifications-referral-events
Producerreferral
ContractVersionожидается 1; несовпадение — warn-and-proceed

Топики и маппинг событий

ТопикСобытие → NotificationKindПолучательUrgency
pin.referral.bonds.v1bond-created → ReferralBondChanged; bond-closed → ReferralBondChanged (Replaced/Revoked)реферер (владелец связи)Normal
pin.referral.lead-attachments.v1lead-attach-requested → ReferralLeadAttachedреферер / агент-владелец лидаNormal

Дом полей payload — referral/events/bond-created.md, bond-closed.md, lead-attach-requested.md; поля уведомления — ReferralNotificationPayload (notification.md).

Контекст события

  • Actor = система; tenant — по получателю. Метаданные события (идентификатор сообщения, агрегат-источник, время) доступны из конверта события.
  • Идемпотентность: consumer-level по ключу notifications-referral:{EventType}:{MessageId}; business-level — dedup-unique (INV-NTF-1).

Входные данные / payload события

Поле payloadТипОбяз.Как используется
BondId/LeadIdGuidДаid для ReferralNotificationPayload
ReferrerUserIdGuidДаполучатель fan-out
ReferredUserId/ReferredNameGuid?/строкаНетReferred { Id, Name }
State/Reasonenum/строкаДаState (Created/Replaced/Revoked/Attached)

Алгоритм

Общая логика постановки в очередь — crm-events.md:

  1. Восстановить контекст события и сверить ContractVersion (несовпадение — warn-and-proceed); проверить идемпотентность по идентификатору сообщения — дубль пропускается (ack, no-op).
  2. Проверить payload; при нарушении контракта события — NTF_INVALID_PAYLOAD (в DLQ).
  3. Определить получателей (реферер / агент-владелец лида); применить дедуп; если получателей нет — no-recipients (ack).
  4. Для каждого получателя определить каналы по виду и срочности, резолвить шаблон и адрес (адрес — мягкая деградация при отсутствии); применить тихие часы. Если шаблон не резолвится — NTF_TEMPLATE_NOT_RESOLVED (в DLQ).
  5. Создать уведомление в статусе Pending и отрендерить тело; повтор события не создаёт дубль (dedup по ключу fan-out, INV-NTF-1). Уведомление и отметка идемпотентности фиксируются в одном согласованном изменении (persist-then-send).
  6. Успех → ack; retriable-сбой → retry; non-retriable → DLQ.

Диаграмма

Идемпотентность и replay

  • Consumer-level по MessageId; business-level — dedup-unique (INV-NTF-1). Replay: да.

Retry и DLQ

СбойКодRetryDLQ
Инфраструктурныйbackoff + пауза партицииНет
Нарушение payloadNTF_INVALID_PAYLOADНетДа + алерт
Шаблон не резолвитсяNTF_TEMPLATE_NOT_RESOLVEDНетДа + алерт

Наблюдаемость и безопасность

  • Logs: consumed/skipped-duplicate/no-recipients/enqueued/failed.
  • Metrics: notifications_enqueued_total{kind,channel}, notifications_consumer_lag_seconds{source=referral}.
  • Безопасность: system-actor; ФИО приведённого — PII (маска); m2m ACL Kafka.

Кросс-сервисные контракты

Ожидает от referral: at-least-once, устойчивый MessageId, партиционирование по AggregateId (связь/лид — порядок событий одной связи сохраняется в партиции), ContractVersion; непустые BondId/LeadId и ReferrerUserId; State (Created/Replaced/Revoked/Attached), опциональные ReferredUserId/ReferredName.

Предоставляет: NotificationEntity(Pending) рефереру (владельцу связи) / агенту-владельцу лида; обратной записи в referral нет; исходящее событие — только dispatched (dispatcher).

Порядок, версии и неизвестные события

  • Порядок. bond-createdbond-closed(Replaced/Revoked) по одной связи — последовательные факты, каждый даёт своё уведомление; кросс-событийной сверки нет (не source of truth реферальной системы). Повтор и переупорядочивание безопасны (dedup INV-NTF-1).
  • Неизвестное значение State (новый под-тип связи/привязки) → не маппится в Kindignored-event (ack), лог; неизвестный тип события на pin.referral.*.v1ignored-event (ack).
  • Версия ниже/выше 1 — warn-and-proceed по известным полям; отсутствие обязательного известного поля → NTF_INVALID_PAYLOAD (DLQ).

Постусловие

  • Получателю (реферер/агент-владелец лида) по каждому разрешённому каналу создан ровно один NotificationEntity(Pending); отметка идемпотентности и уведомления — в одном согласованном изменении; offset подтверждён после фиксации.
  • ФИО приведённого (ReferredName) доставлено только адресату; в логах маскируется.
  • Обратной записи в referral нет.

Acceptance criteria

  • Given bond-created, When consumer обрабатывает, Then рефереру создаётся NotificationEntity(Pending, Kind=ReferralBondChanged, State=Created).
  • Given bond-closed с причиной, When обработка, Then рефереру создаётся уведомление ReferralBondChanged со State ∈ {Replaced, Revoked}.
  • Given lead-attach-requested, When обработка, Then агенту-владельцу лида создаётся ReferralLeadAttached.
  • Given повторная доставка того же события, When обработка, Then skipped-duplicate (ack), дублей нет.
  • Given неизвестное значение State, When маппинг, Then ignored-event (ack), уведомления нет.

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

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

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