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

Event Contract: pin.calendar.event-owner-changed (CalendarEventOwnerChangedEvent)

Версионируемый контракт интеграционного события «Владелец календарного события переназначен». Consumer- документы ссылаются сюда, не переописывая payload. Дом полей агрегата — entity: CalendarEventEntity.

Назначение

Фиксирует переназначение владельца открытого события: у события в Planned/InProgress изменён OwnerUserId (в пределах того же TenantId) — оно перешло из календаря прежнего агента в календарь нового исполнителя сделки. Единственный источник переназначения — консюмер deal-событий CRM (../consumers/crm-deal-events.md) при смене исполнителя (deal-members-changed ChangeType = ExecutorChanged) или уходе агента из сделки (Removed, в т.ч. увольнение через CRM-offboarding); санкция — INV-CAL-8 (calendar-event.md). Публичное: notifications уведомляет нового владельца о появившемся у него обязательстве (NotificationKind = CalendarEventOwnerChanged), при необходимости — прежнего владельца о снятии — без синхронного запроса в calendar. Напоминания при переназначении не пересоздаются (моменты DueAt неизменны — сместился только владелец); их доставку по новому владельцу обеспечивает calendar.

Metadata

ПолеЗначение
Event typepin.calendar.event-owner-changed
Topicpin.calendar.event-owner-changed.v1
Partitions / key3 (ADR-0056) / key = EventId (порядок событий одного календарного события)
ContractVersion1
Producercalendar: консюмер deal-событий CRM (../consumers/crm-deal-events.md) — переназначение владельца открытого события при смене исполнителя / уходе агента (INV-CAL-8)
Статусdraft

Событие публикуется атомарно с изменением агрегата (сменой OwnerUserId в том же согласованном изменении): пока переназначение не зафиксировано, событие не отправляется; после фиксации — доставляется гарантированно (at-least-once, transactional outbox), ключ = EventId.

События Origin = Appointment не переназначаются (управляются жизненным циклом записи, INV-CAL-3) и этим контрактом не покрываются.

Payload CalendarEventOwnerChangedEvent

Дом полей — entity: CalendarEventEntity; здесь перечислено только то, что реально в событии. OwnerUserIdновое значение после переназначения.

ПолеТипОбязательностьИсточник (поле агрегата)CompatibilityОписание
EventIdGuidДаCalendarEventEntity.IdImmutableКлюч агрегата (= Kafka key).
TenantIdGuidДаCalendarEventEntity.TenantIdImmutableКомпания владельца календаря (ADR-0052); при переназначении не меняется (новый исполнитель — тот же AgencyCompanyId).
OwnerUserIdGuidДаCalendarEventEntity.OwnerUserIdImmutable (значение на момент публикации)Новый владелец — исполнитель сделки, получатель обязательства.
PreviousOwnerUserIdGuidДапрежний OwnerUserId событияAdditive onlyПрежний владелец — у него событие снято из календаря; получатель уведомления о снятии.
Kindstring (enum EventKind)ДаCalendarEventEntity.KindAdditive only (новые значения)Вид события (BookingDeadline / Meeting / Showing / Consultation / Task).
Originstring (enum EventOrigin)ДаCalendarEventEntity.OriginAdditive only (новые значения)CrmBooking (дедлайн брони следует за исполнителем) / Manual (встреча ушедшего агента); Appointment не встречается.
StartAtDateTimeOffsetДаCalendarEventEntity.StartAtImmutable (значение на момент публикации)Начало события, UTC — для текста уведомления.
EndAtDateTimeOffset?НетCalendarEventEntity.EndAtImmutable (значение на момент публикации)Конец, UTC; null — точечное событие (дедлайн/задача).
TimeZonestringДаCalendarEventEntity.TimeZoneAdditive onlyIANA-таймзона — для отображения локального времени в уведомлении.
DealIdGuid?НетCalendarEventEntity.DealIdAdditive onlyСделка CRM — причина переназначения (логическая ссылка).

Когда публикуется

УсловиеПереход состоянияПубликация
Смена исполнителя сделки (deal-members-changed ExecutorChanged) — открытый BookingDeadline следует за новым исполнителемстатус не меняется; OwnerUserId изменёнатомарно с изменением агрегата
Уход агента из сделки (Removed / увольнение) — открытые встречи/задачи ушедшего агента переходят к исполнителюстатус не меняется; OwnerUserId изменёнта же

Каждое фактическое переназначение публикуется отдельным сообщением. Совпадение источника и цели (OwnerUserId уже = новый исполнитель) события не порождает.

Потребители (consumers)

Consumer-контекстSide effectsИдемпотентностьЗадержка (SLA)
notificationsУведомление о переназначении (NotificationKind = CalendarEventOwnerChanged): новому владельцу — «на вас переведено обязательство сделки»; прежнему (PreviousOwnerUserId) — «обязательство снято». Консюмер — ../../notifications/consumers/calendar-events.mdMessageId< 5 мин

Версионирование и совместимость

  • ContractVersion = 1; при несовпадении версии контракта потребитель логирует предупреждение и продолжает обработку. Топик кодирует версию по конвенции суффиксом .v1.
  • Additive-only для minor: новые поля добавляются как optional с безопасным default — без bump.
  • BreakingContractVersion = 2 + новый топик pin.calendar.event-owner-changed.v2 + dual-publish + deadline миграции consumers + sunset .v1.
  • Unknown enum (Kind/Origin) — fallback: сохранить строку как есть, warning, уведомление с нейтральным текстом (fail safe).

Безопасность

ПолеКласс данныхПравило
EventId, TenantId, OwnerUserId, PreviousOwnerUserId, Kind, Origin, StartAt, EndAt, TimeZone, DealIdInternalinclude (только идентификаторы, время и enum)
  • PII (ФИО/телефоны) в событии отсутствует по дизайну — display-данные владельцев консюмеры берут из своей реплики pin.identity.users.v1; Title/Description/Location в payload не входят.
  • Доступ к топику — m2m ACL Kafka (notifications).

Доставка, retry/DLQ и восстановление

  • Гарантия. At-least-once после атомарной публикации из outbox; ключ = EventId — переназначения одного события приходят по порядку. Дубликаты возможны — консюмер идемпотентен по MessageId.
  • Retry. Транзиентный сбой консюмера → повтор с backoff; порядок по ключу сохраняется.
  • DLQ. Неразбираемое/несовместимое сообщение после конечного числа попыток — в DLQ с алертом; остальные события не блокируются.
  • Кросс-топик порядок. С event-status-changed порядок между топиками не гарантирован: если событие уже отменено/завершено на момент обработки переназначения, уведомление о новом владельце не отправляется (fail-safe по бизнес-состоянию, warning); финальную картину даёт уведомление об отмене/завершении.
  • Промежуточное состояние. Новый владелец видит событие в своём календаре сразу; уведомление — после обработки события (обычно < 5 мин).

Acceptance-критерии (контракт потребления)

  • Переназначение при смене исполнителя. Дано: открытый BookingDeadline сделки переназначен с A на B. Когда notifications обрабатывает событие. Тогда B получает уведомление CalendarEventOwnerChanged о новом дедлайне; A — уведомление о снятии.
  • Переназначение встречи ушедшего агента. Дано: открытая Meeting со-агента C переназначена на исполнителя E. Когда notifications обрабатывает событие. Тогда E уведомлён о встрече.
  • Идемпотентность. Дано: сообщение доставлено дважды. Тогда дубль уведомления не создаётся.
  • Терминальное событие. Дано: событие уже отменено на момент обработки переназначения. Тогда уведомление о новом владельце не отправляется (fail-safe), warning.

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

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

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