Событие «Запрошена карточка лида»: pin.referral.lead-attach-requested
Контракт интеграционного события «нужна карточка лида по реферальному приходу». Consumer-документы ссылаются сюда, не переописывая payload. Состав payload зафиксирован домом процесса привязки — attach-rules.md (шаг 6 нормативного алгоритма); здесь — полный контракт.
Назначение
Запрос контексту crm создать карточку лида «агент ↔ инвестор»: инвестор пришёл по реферальной
ссылке агента/застройщика («ЛИД … появляется в CRM-системе застройщика или агентства»). Публикуется
правилами привязки R1–R4 при выполнении INV-REF-002 (владелец ссылки — Agent или DeveloperManager,
активный) — в том числе без создания реферальной связи (правила R3/R4: пользователь зарегистрировался
сам либо просто открыл ссылку). Событие — запрос, а не факт: единственность активной пары
«инвестор + агент» и жизненный цикл карточки — зона crm (глоссарий «Лид»; дедупликация на стороне
потребителя). Контекст referral след лида не хранит (кроме флага LeadRequested журнала).
Metadata
| Поле | Значение |
|---|---|
| Имя события | pin.referral.lead-attach-requested (ReferralLeadAttachRequestedKafkaEvent) |
| Topic | pin.referral.lead-attachments.v1 |
| Partitions / key | 6 (ADR-0056) / key = InvestorUserId (порядок запросов per-инвестор) |
| ContractVersion | 1 |
| Producer | referral: consumer T1 (user-upserted.md, R1), consumer T2 (user-signed-in.md, R2/R3), операция T3 (validate-referral-link.md, R4, за флагом Referral:LinkOpenAttachEnabled) |
| Статус | draft |
Событие публикуется транзакционно (через outbox) вместе с изменениями процесса привязки: для R1/R2 —
согласованно со связью и журналом; для R3/R4 мутаций в referral может не быть — запись всё равно
согласована с отметкой идемпотентности потребителя / инкрементом VisitsCount. Собственного агрегата у
запроса нет — это событие-запрос процесса привязки; ключ агрегирования — InvestorUserId. Корреляция/actor
переносятся в служебном трейсе, не в payload.
Payload
Поля собираются процессом привязки из резолвнутой ссылки (referral-link.md) и созданной связи (referral-bond.md); дом состава — attach-rules.md, шаг 6.
| Поле | Тип | Обязательность | Источник (поле агрегата) | Compatibility | Описание |
|---|---|---|---|---|---|
InvestorUserId | Guid | Да | UserId триггер-события / актор T3 | Immutable | Инвестор-лид (= Kafka key, = ключ агрегирования); guard G2 гарантирует Type = Investor. |
AgentUserId | Guid | Да | ReferralLinkEntity.OwnerUserId | Immutable | Агент/менеджер застройщика — владелец ссылки, которому создаётся карточка лида (INV-REF-002: только Agent/DeveloperManager). |
AgentCompanyId | Guid? | Нет | ReferralLinkEntity.TenantId | Immutable | Компания агента (агентство/застройщик) — tenant карточки лида в CRM (ADR-0052); null не встречается при выполнении INV-REF-002, но тип nullable — как у источника. |
ReferralLinkId | Guid | Да | ReferralLinkEntity.Id | Immutable | Ссылка-источник лида (атрибуция в карточке). |
Channel | string (enum ReferralChannel) | Да | ReferralLinkEntity.Channel | Additive only (новые значения) | Канал привлечения (Direct/Telegram/…; дом enum — referral-link.md) — источник лида в CRM-аналитике. |
AppliedRule | string (enum ReferralAttachRule) | Да | правило матрицы привязки | Additive only (новые значения) | Как пришёл лид: R1 (регистрация), R2 (авторизация в окне), R3 (авторизация самостоятельно зарегистрировавшегося), R4 (открытие ссылки). R6 не публикуется — admin-коррекция лида не создаёт. Дом enum — attach-rules.md. |
BondId | Guid? | Нет | ReferralBondEntity.Id | Immutable | Созданная этим же срабатыванием реферальная связь; null — правила R3/R4 и повторные срабатывания при уже существующей активной связи (guard G5: лид без новой связи). |
RequestedAt | DateTimeOffset | Да | момент публикации процесса привязки | Immutable | Когда запрошен лид (дублирует время события для удобства потребителя). |
Вложенных моделей нет — все поля скалярные. ФИО/телефон инвестора в payload не входят: crm
заполняет карточку из собственной реплики пользователей (топик pin.identity.users.v1,
user-upserted.md).
Когда публикуется
Условие всех строк — guard-ы G1–G4 пройдены и выполнен INV-REF-002: владелец ссылки
Type ∈ {Agent, DeveloperManager} и Status = Active (модификатор R5 отсекает остальных —
attach-rules.md).
| Условие | Переход состояния | Согласованность |
|---|---|---|
| R1 — регистрация с реф-ссылкой (T1, user-upserted.md) | вместе с ReferralBondEntity: [*] → Active (BondId заполнен) | связь + журнал Initial + оба события — в одном согласованном изменении |
| R2 — авторизация в окне lazy-регистрации (T2, user-signed-in.md) | та же (BondId заполнен); при существующей активной связи (G5) — без перехода, BondId = null | то же |
| R3 — авторизация самостоятельно зарегистрировавшегося (T2) | переходов нет — связь не создаётся (INV-REF-003), BondId = null | событие + отметка идемпотентности — согласованно |
R4 — открытие ссылки авторизованным инвестором (T3, validate-referral-link.md; флаг Referral:LinkOpenAttachEnabled) | переходов нет, BondId = null | событие + инкремент VisitsCount — согласованно |
Потребители (consumers)
| Consumer-контекст | Потребитель | Эффект | Идемпотентность | Задержка (SLA) |
|---|---|---|---|---|
crm | referral-lead-attach-requested.md | Создание карточки лида «инвестор + агент» в CRM компании AgentCompanyId с атрибуцией (ReferralLinkId, Channel, AppliedRule, BondId) | MessageId + бизнес-ключ (InvestorUserId, AgentUserId): у пары одновременно только один активный лид — дубль/повтор → no-op (правило дедупа — глоссарий «Лид», attach-rules.md) | < 5 мин |
Единственный потребитель. referral шлёт запрос идемпотентно и повторно (несколько агентов могут
одновременно отслеживать одного инвестора — пары различаются AgentUserId); решение «создать новую
карточку / оставить существующую активную» принимает crm.
BondId может быть null (правила R3/R4) либо ссылаться на только что созданную связь; даже при
заполненном BondId crm не должен предполагать синхронную доступность связи в других контекстах —
реплика в rewards асинхронна, межтопикового порядка с pin.referral.bonds.v1 нет (лид способен дойти
до CRM раньше, чем rewards увидит связь). Окно согласованности: карточка лида появляется в CRM в
пределах SLA (< 5 мин) после срабатывания правила; в этот интервал у пары «инвестор+агент» карточки ещё
нет.
Гарантии и порядок для crm:
- Запросы по одному инвестору упорядочены ключом
InvestorUserId; по разным агентам различаютсяAgentUserId(несколько агентов ведут одного инвестора — пары независимы, дедуп по паре). - Лид-запрос может прийти раньше, чем
crmувидит пользователя в собственной репликеpin.identity.users.v1(ФИО/телефон):crmзаполняет карточку из своей реплики и обязан выдержать её лаг (retry/дозаполнение), а не отклонять запрос. - Событие — запрос-факт: version-guard не нужен, только дедуп по идентификатору сообщения и бизнес-ключу
пары (
InvestorUserId,AgentUserId); повтор при живой активной паре — no-op.
Версионирование и совместимость
- Версия — один
ContractVersion = 1; при несовпадении версии события потребитель логирует warning и продолжает (upcasting не предусмотрен). - Additive-only для minor: новые optional-поля с безопасным default — без bump.
- Breaking —
ContractVersion = 2+ новый топикpin.referral.lead-attachments.v2+ dual-publish + deadline миграцииcrm+ sunset.v1. - Unknown enum:
Channel→ fallbackOther+ warning;AppliedRule→ warning + обработка какR1(консервативно — карточка всё равно создаётся). - Событие — запрос-факт (append-only, не снапшот): version-guard потребителю не нужен, только
дедупликация по
MessageId+ бизнес-ключу пары.
Безопасность
| Поле | Класс данных | Правило |
|---|---|---|
InvestorUserId, AgentUserId, AgentCompanyId, ReferralLinkId, BondId | Internal | include |
Channel, AppliedRule, RequestedAt | Internal | include |
Событие НЕ содержит PII (ФИО/телефон — реплики потребителя из pin.identity.users.v1), secrets, slug
ссылки, сумм/коммерческих условий. Доступ к топику — m2m ACL Kafka (только crm). Tenant-правило:
карточка создаётся в scope AgentCompanyId (ADR-0052) — crm не должен раскрывать лид другим компаниям.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (1): GET /api/investors/referral-links/{url}/validate — Публичная валидация slug + счётчик переходов
- Use Cases (3): UC-REF-003. Регистрация по реферальной ссылке → связь + лид (правило R1), UC-REF-004. Авторизация со ссылкой в окне lazy-регистрации → связь + лид (правило R2), UC-REF-005. Авторизация: «Имя заполнено И регистрация ≥ 4 ч» → только лид, без связи (правило R3)
- События (2): Event Contract: pin.crm.lead-created (LeadCreatedEvent), Event Contract: pin.referral.bond-created (ReferralBondCreatedKafkaEvent)
- Консюмеры (4): Consumer: Referral Events → Notifications (notifications-referral-events), Consumer: ReferralRegisteredConsumer (pin.referral.lead-attachments.v1), Consumer: ReferralUserSignedInConsumer (pin.identity.sign-ins.v1), Consumer: ReferralUserUpsertedConsumer (pin.identity.users.v1)
- registries (1): Реестр событий PIN
Связанные документы
- Дом процесса и состава payload: attach-rules.md (матрица R1–R6, INV-REF-002, шаг 6); источники полей: referral-link.md, referral-bond.md.
- Producer-операции: user-upserted.md (T1), user-signed-in.md (T2), validate-referral-link.md (T3).
- Соседний контракт: bond-created.md (
rewards/notifications/messenger— связь, не лид). - Потребитель: crm/README.md (карточка лида, единственность активной пары).