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

Event Contract: pin.messenger.conference-ended (ConferenceEndedEvent)

Версионируемый контракт интеграционного Kafka-события «конференция сделки завершена». Consumer-документы ссылаются сюда, не переописывая payload.

Назначение

Фиксирует завершение группового звонка в чате сделки (conference-session.md): факт видеовстречи, её длительность и состав участников. Публичное, потому что конференция всегда привязана к сделке (Deal-чат) — у события есть внешний потребитель, синхронно недостижимый: crm кладёт карточку в таймлайн сделки («видеовстреча, 32 мин, 3 участника»), notifications/аналитика используют факт звонка. 1:1-звонки такого события не имеют (person-to-person, вне сделки). Producer — messenger (завершение конференции или reaper пустой комнаты).

Metadata

ПолеЗначение
Event typepin.messenger.conference-ended
Topicpin.messenger.conference-ended.v1
Partitions / key3 / ключ = DealId (порядок конференций в рамках сделки)
ContractVersion1
Producermessenger
Статусdraft

Событие публикуется атомарно с переходом конференции Active → Ended/Failed (транзакционный outbox): после фиксации — доставляется at-least-once.

Payload ConferenceEndedEvent

Публичные бизнес-поля завершённой конференции. Snapshot ссылается на дом сущности (conference-session.md); ростер — bounded-список без ФИО.

ПолеТипОбязательностьИсточник (поле агрегата)CompatibilityОписание
ConferenceIdGuidДаConferenceSessionEntity.IdImmutableИдентификатор конференции; ключ идемпотентности.
ChatIdGuidДа.ChatIdImmutableDeal-чат конференции.
DealIdGuidДа.DealIdImmutableСделка; Kafka-ключ.
TenantIdGuidДа.DealTenantIdImmutableDeveloperCompanyId сделки.
MediaTypeCallMediaTypeДа.MediaTypeAdditiveАудио/видео.
StatusConferenceStatusДа.StatusAdditiveEnded / Failed.
EndReasonConferenceEndReasonДа.EndReasonAdditiveПричина завершения.
StartedByUserIdGuidДа.StartedByUserIdImmutableИнициатор.
StartedAtDateTimeOffsetДа.StartedAtСтарт.
EndedAtDateTimeOffsetДа.EndedAtЗавершение.
DurationSecondsintДа.DurationSecondsДлительность (≥ 0).
PeakParticipantCountintДа.PeakParticipantCountМаксимум одновременных участников.
ParticipantsConferenceParticipantSnapshotEntry[]Даростер (call-participant.md)AdditiveИтоговый ростер (bounded).
WasRecordedboolДаналичие записиAdditiveВелась ли запись.
RecordingIdGuid?НетCallRecordingEntity.IdImmutableЗапись (если велась); готовность придёт отдельным событием.

Вложенная модель ConferenceParticipantSnapshotEntry

ПолеТипОбязательностьОграниченияОписание
UserIdGuidДалогическая ссылка identityУчастник.
RoleParticipantRole?НетenumОрг-роль на момент участия.
JoinedAtDateTimeOffsetДаВход.
LeftAtDateTimeOffset?НетВыход.
DurationSecondsint?Нет>= 0Время в конференции.

Enum'ы (CallMediaType, ConferenceStatus, ConferenceEndReason, ParticipantRole) — из conference-session.md/call-participant.md, не дублируются.

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

УсловиеПереход состоянияПубликация
Штатное/аварийное завершение конференции (опустошение / модератор / закрытие чата / reaper / фатальный сбой)Active → Ended / Active → Failed (conference-session.md INV-CONF-4)атомарно со сменой состояния (outbox)

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

Consumer-контекстConsumerSide effectsИдемпотентностьЗадержка (SLA)
crmconference-timeline-entryКарточка «видеовстреча по сделке» в таймлайне сделкиConferenceId (first-seen)< 1 мин
notificationsconference-ended-notify (опц.)Уведомление «встреча завершена / вы пропустили»ConferenceId< 5 мин
dev-statsconference-fact (опц.)Факт звонка для аналитики застройщика (ClickHouse)ConferenceId< 5 мин

Гарантии доставки, порядок и обработка отказов

  • Ровно один факт завершения на конференцию. Публикуется единожды при переходе Active → Ended/Failed (INV-CONF-4), независимо от инициатора (опустошение / модератор / закрытие чата / conference-room-reaper / фатальный сбой). At-least-once-редоставка возможна — все потребители дедуплицируют по ConferenceId (first-seen); повторная доставка не плодит карточку в таймлайне и не задваивает факт аналитики.
  • Порядок per-DealId. Несколько конференций одной сделки упорядочены ключом DealId; между сделками порядка нет. Частичный отказ fan-out (crm / notifications / dev-stats) независим — падение одного не откатывает других.
  • Запись — отдельным событием. WasRecorded/RecordingId фиксируют факт записи, но её готовность приходит позже контрактом call-recording-available.md (после выгрузки в file storage); потребитель, ждущий файл, ориентируется на WasRecorded = true и сверяет RecordingId.
  • Retry/DLQ. Транзиентный сбой потребителя — ретрай по offset; poison после исчерпания попыток → DLQ + алерт, offset продвигается.

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

  • Версия — один ContractVersion = 1; consumer при рассогласовании логирует предупреждение и продолжает. Суффикс .v1.
  • Additive-only для minor; breakingContractVersion = 2 + топик .v2 + dual-publish.
  • Ростер Participants — bounded; unknown enum — fallback с предупреждением.

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

ПолеКласс данныхПравило
Participants[].UserId/StartedByUserIdInternalinclude (id, не ФИО)
ConferenceId/ChatId/DealId/TenantId/RecordingIdInternalinclude
прочие (MediaType/Status/EndReason/timestamps/counts/WasRecorded)Publicinclude

Событие НЕ содержит ФИО/телефонов, тела сообщений, медиа, токенов; ростер — только id/роли/тайминги.

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

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

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