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 type | pin.identity.company-invitation-changed |
| Topic | pin.identity.company-invitation-changed.v1 |
| Partitions / key | 3 (низкий объём, ADR-0056) / key = InvitationId (порядок per-invitation) |
| ContractVersion | 1 |
| Producer | identity: операции invite/accept/decline/revoke, job [expire-invitations] |
| Статус | draft |
Событие публикуется атомарно с переходом статуса приглашения: до фиксации перехода событие не отправляется, после — доставляется гарантированно (at-least-once).
Payload CompanyInvitationChangedKafkaEvent
Дом полей — entity: CompanyInvitationEntity (раздел «Доменные и
интеграционные события»); здесь перечислено только то, что реально в событии.
| Поле | Тип | Обязательность | Источник (поле агрегата) | Compatibility | Описание |
|---|---|---|---|---|---|
InvitationId | Guid | Да | CompanyInvitationEntity.Id | Immutable | Ключ агрегата (= Kafka key). |
CompanyId | Guid | Да | CompanyInvitationEntity.CompanyId | Immutable | Приглашающая компания (агентство/брокер/партнёр). |
PhoneNumber | string | Да | CompanyInvitationEntity.PhoneNumber | Additive only | E.164 приглашаемого. Только для доверенных consumer-ов (notifications/crm — ACL топика); правило PII ниже. |
TargetRoleId | Guid | Да | CompanyInvitationEntity.TargetRoleId | Additive only | Роль, назначаемая при accept (по умолчанию системная agent). |
Status | string (enum CompanyInvitationStatus) | Да | CompanyInvitationEntity.Status | Additive only (новые значения) | Pending / Accepted / Declined / Expired / Revoked. |
InvitedByUserId | Guid | Да | CompanyInvitationEntity.InvitedByUserId | Immutable | Кто пригласил. |
AcceptedByUserId | Guid? | Нет | CompanyInvitationEntity.AcceptedByUserId | Additive only | Кто принял (при Status = Accepted); null иначе. |
ExpiresAt | DateTimeOffset | Да | CompanyInvitationEntity.ExpiresAt | Immutable | Срок жизни приглашения. |
Version | long | Да | CompanyInvitationEntity.Version | Immutable 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/Declined | MessageId + 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, Version | Internal | include |
PhoneNumber | PII | include только для доверенных consumer-ов — доступ к топику ограничен m2m ACL Kafka (notifications/crm/rewards); в логах маскируется +7***1234 |
Событие НЕ содержит secrets, токенов, ФИО и платёжных данных.
Обратные ссылки
Автоссылки: где используется этот документ.
- Домен (CQRS) (1): Entity / Aggregate: CompanyInvitationEntity
- API (4): POST /api/v1/companies/my/agent-invitations — Приглашение агента по номеру телефона, POST /api/v1/companies/my/agent-invitations/{invitationId}/revoke — Отзыв приглашения агента, POST /api/v1/me/agent-invitations/{invitationId}/accept — Принятие приглашения в агентство, POST /api/v1/me/agent-invitations/{invitationId}/decline — Отклонение приглашения в агентство
- Use Cases (1): UC-IDN-009. Приглашение агента по телефону и его принятие
- События (1): Event Contract: pin.identity.agent-membership-changed (AgentMembershipChangedKafkaEvent)
- index.md (1): Identity Service (Pin.Identity)
- registries (1): Реестр событий PIN
Связанные документы
- Сущность: company-invitation.md; соседнее событие: agent-membership-changed.md.
- Источники (операции): invite-agent-by-phone.md, revoke-invitation.md, accept-invitation.md, decline-invitation.md.
- Решение: ADR-0061.