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

Event Contract: pin.identity.company-invitation-changed (CompanyInvitationChangedKafkaEvent)

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

Назначение

Фиксирует жизненный цикл CompanyInvitationEntity (ADR-0061): создание Pending и каждое разрешение (Accepted/Declined/Revoked/Expired). Публичное: notifications рассылает СМС адресату и руководителю («вас пригласили» / «приглашение принято/отклонено/отозвано»), crm ведёт воронку найма агентов, не запрашивая identity синхронно. Дом полей — entity: CompanyInvitationEntity.

Metadata

ПолеЗначение
Event typepin.identity.company-invitation-changed
Topicpin.identity.company-invitation-changed.v1
Partitions / key3 (низкий объём, ADR-0056) / key = InvitationId (порядок per-invitation)
ContractVersion1
Produceridentity: операции invite/accept/decline/revoke, job [expire-invitations]
Статусdraft
Атомарность публикации

Событие публикуется атомарно с переходом статуса приглашения: до фиксации перехода событие не отправляется, после — доставляется гарантированно (at-least-once).

Payload CompanyInvitationChangedKafkaEvent

Дом полей — entity: CompanyInvitationEntity (раздел «Доменные и интеграционные события»); здесь перечислено только то, что реально в событии.

ПолеТипОбязательностьИсточник (поле агрегата)CompatibilityОписание
InvitationIdGuidДаCompanyInvitationEntity.IdImmutableКлюч агрегата (= Kafka key).
CompanyIdGuidДаCompanyInvitationEntity.CompanyIdImmutableПриглашающая компания (агентство/брокер/партнёр).
PhoneNumberstringДаCompanyInvitationEntity.PhoneNumberAdditive onlyE.164 приглашаемого. Только для доверенных consumer-ов (notifications/crm — ACL топика); правило PII ниже.
TargetRoleIdGuidДаCompanyInvitationEntity.TargetRoleIdAdditive onlyРоль, назначаемая при accept (по умолчанию системная agent).
Statusstring (enum CompanyInvitationStatus)ДаCompanyInvitationEntity.StatusAdditive only (новые значения)Pending / Accepted / Declined / Expired / Revoked.
InvitedByUserIdGuidДаCompanyInvitationEntity.InvitedByUserIdImmutableКто пригласил.
AcceptedByUserIdGuid?НетCompanyInvitationEntity.AcceptedByUserIdAdditive onlyКто принял (при Status = Accepted); null иначе.
ExpiresAtDateTimeOffsetДаCompanyInvitationEntity.ExpiresAtImmutableСрок жизни приглашения.
VersionlongДаCompanyInvitationEntity.VersionImmutable semanticsДоменная версия после перехода; монотонный guard у потребителей.

Вложенных моделей нет. Version = 1 ⇔ создание Pending.

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

УсловиеПереход состоянияПубликация
Руководитель приглашает агента по телефону[*] → Pendingатомарно с созданием приглашения
Адресат принимает приглашениеPending → Accepted (+ AgentMembership, RoleAssignment в том же изменении)та же
Адресат отклоняет приглашениеPending → Declinedта же
Приглашающая сторона отзывает приглашениеPending → Revokedта же
Job expire-invitations (ExpiresAt <= now)Pending → Expiredта же

Переход Pending → Accepted дополнительно порождает agent-membership-changed (создание Active membership) — два отдельных топика, оба из одного согласованного изменения.

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

Consumer-контекстЭффектИдемпотентностьЗадержка (SLA)
notificationsСМС адресату при Pending («вас пригласили в <компанию>») и руководителю при Accepted/DeclinedMessageId + Version-guard< 1 мин
crmВоронка найма агентов (проекция статусов приглашений для экрана «Команда»/аналитики)то же< 5 мин
rewardsОпционально: заготовка партнёрско-комиссионного контекста агента при Accepted (уточняется приёмом membership-события)то же< 5 мин

Порядок и согласованность

  • Единственный терминальный переход. Pending разрешается ровно один раз (Accepted/Declined/Revoked/Expired); конкурентные попытки (адресат принимает в момент отзыва руководителем или экспирации job-ом) сериализуются в identity — побеждает первый зафиксированный переход, остальные операции получают отказ. Потребитель видит монотонный Version; поздний дубликат с меньшей версией — skip.
  • Accept порождает два топика. Pending → Accepted атомарно публикует это событие и agent-membership-changed (Active) в разных топиках — межтопиковый порядок не гарантирован. Кроме того, AcceptedByUserId может ссылаться на пользователя, чья реплика (user-upserted, другой топик) ещё не дошла до crm: потребитель терпит неизвестного пользователя и досогласует воронку найма на first-seen.
  • Идемпотентность. Повтор доставки (at-least-once) — no-op по MessageId + Version-guard; СМС адресату отправляется один раз на переход Pending.

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

  • ContractVersion = 1; при несоответствии версии события consumer логирует warning и продолжает (без upcasting).
  • Additive-only: новые optional-поля с default — без bump. Breaking — ContractVersion = 2 + топик pin.identity.company-invitation-changed.v2 + dual-publish + sunset .v1.
  • Unknown enum Status — fallback: сохранить строку без изменений, warning; неизвестный статус не считать активным.
  • Порядок per-invitation гарантирован key-ом; потребители держат Version-guard.

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

ПолеКласс данныхПравило
InvitationId, CompanyId, TargetRoleId, Status, InvitedByUserId, AcceptedByUserId, ExpiresAt, VersionInternalinclude
PhoneNumberPIIinclude только для доверенных consumer-ов — доступ к топику ограничен m2m ACL Kafka (notifications/crm/rewards); в логах маскируется +7***1234

Событие НЕ содержит secrets, токенов, ФИО и платёжных данных.

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

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

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