UC-REF-006. Ссылка от инвестора (не агента/застройщика) → связь без лида (модификатор R5)
Назначение
Привязка по ссылке, владелец которой не является агентом или застройщиком (например, инвестор
пригласил друга): реферальная связь для будущих выплат создаётся по базовому правилу (R1 при регистрации,
R2 при авторизации в окне lazy-регистрации), но лид в CRM НЕ создаётся — модификатор R5
NonAgentOwnerNoLead, инвариант INV-REF-002 attach-rules.md. Связан с
контекстом referral.
| Поле | Значение |
|---|
| ID | UC-REF-006 |
| Контекст | referral (см. docs/06-services/referral/) |
| Тип | альтернативный (модификатор базовых потоков UC-REF-003/004) |
| Статус | draft |
Актор
| Поле | Значение |
|---|
| Роль | System (обработка события регистрации/входа, m2m ACL Kafka); реферер — Инвестор (или PartnerManager/PinEmployee — «не агент/застройщик» по таблице ролей attach-rules.md); приглашённый — Инвестор |
| Permissions / scope | permission не применяется (system actor) |
| Tenant | TenantId = link.TenantId: у реферера-инвестора null (ADR-0052) — связь и журнал вне tenant-а |
| Record-level | чтение связей — по ReferredUserId (getMyReferrer) и ReferrerUserId (список приглашённых владельца) |
Предусловие
- Владелец ссылки имеет
ReferralUserReplica.Type ∈ {Investor, PartnerManager, PinEmployee} — «не
агент/застройщик» (таблица «Термины и роли владельца ссылки»
attach-rules.md); человек, работающий и как агент, имеет отдельную учётку
Agent — для правил важен тип учётки-владельца конкретной ссылки.
- Приглашённый открыл ссылку — cookie
referralUrl жива
(UC-REF-002).
- Выполнены условия базового правила: регистрация с
ReferrerUrl (R1,
UC-REF-003) или авторизация в окне lazy-регистрации
(R2, UC-REF-004).
- Guard-ы G1–G5 attach-rules.md выполнены.
Триггер
| Поле | Значение |
|---|
| Источник | Kafka event |
| Триггерящий элемент | T1: событие регистрации UserUpsertedKafkaEvent (pin.identity.users.v1, first-seen c ReferrerUrl) — основной поток; T2: событие входа UserSignedInKafkaEvent (pin.identity.sign-ins.v1) — альтернатива A1 |
Основной Поток
Поток повторяет UC-REF-003 (общий алгоритм привязки,
attach-rules.md); ниже — дельта модификатора R5.
- Инвестор-реферер делится своей ссылкой (UC-REF-001);
его друг открывает её (cookie) и регистрируется;
identity публикует UserUpsertedKafkaEvent с
ReferrerUrl = slug.
- Контекст и guard-ы. Идемпотентность first-seen (INV-REF-R1), guard-ы G1–G5; тип владельца ссылки
читается как
owner.Type = Investor.
- Базовое правило R1 — создание связи. Создаётся
ReferralBondEntity: ReferredUserId = event.UserId, ReferrerUserId = link.OwnerUserId, ReferralLinkId = link.Id, TenantId = link.TenantId (null у инвестора), BoundAt = время события, AppliedRule = R1, Status = Active.
Проверка INV-REF-002: owner.Type ∉ {Agent, DeveloperManager} → лид не требуется (модификатор R5;
в AppliedRule R5 не записывается — его след — поле LeadRequested = false журнала, enum
ReferralAttachRule attach-rules.md). Фиксируется доменное событие
ReferralBondCreatedEvent.
- Журнал
Initial. Пишется запись c AppliedRule = R1, LeadRequested = false, денормализация
LinkUrl/ReferrerUserId/TenantId = null.
- Интеграционные события. Публикуется только
ReferralBondCreatedKafkaEvent
(pin.referral.bonds.v1, key = ReferredUserId, ReferrerCompanyId = null);
ReferralLeadAttachRequestedKafkaEvent НЕ публикуется (INV-REF-002).
- Согласованность. Связь + журнал + событие фиксируются в одном согласованном изменении; событие
подтверждается (ack).
- Downstream.
rewards фиксирует основание будущей выплаты рефереру-инвестору по BondId;
notifications уведомляет реферера; messenger создаёт контакт «инвестор ↔ инвестор» (поток
«Реферальная привязка» service-boundaries.md). crm
события не получает — карточка лида не создаётся.
Альтернативные Потоки
A1. Привязка при авторизации (базовое правило R2)
- Триггер T2 (
UserSignedInKafkaEvent c ReferrerUrl), условие окна lazy-регистрации выполнено
(UC-REF-004) → связь создаётся с
AppliedRule = R2, LeadRequested = false; lead-событие не публикуется. Далее — шаги 6–7 основного
потока.
A2. Владелец-инвестор, авторизация вне окна (R3 + R5)
IsNameFilled = true И регистрация ≥ 4 ч → базовое правило R3 связь не создаёт
(UC-REF-005), а R5 отменяет лид → полный
no-op (structured log). Событие подтверждается. Завершение.
A3. Ссылка менеджера застройщика (DeveloperManager)
- Контрпример границы правила:
DeveloperManager — «агент/застройщик» по таблице ролей, R5 НЕ
применяется, лид создаётся — сценарий идёт по
UC-REF-003/UC-REF-004
без модификатора. Человек «застройщик и одновременно агент» действует той учёткой, которой принадлежит
ссылка.
Постусловие
- Создана
ReferralBondEntity (Status = Active, TenantId = null при реферере-инвесторе) — основание
будущей выплаты рефереру; getMyReferrer приглашённого возвращает реферера-инвестора.
- Журнальная запись
Initial с LeadRequested = false — след применения R5 для метрик.
- Карточка лида в CRM НЕ создана; топик
pin.referral.lead-attachments.v1 события не получил.
Возможные Ошибки
| Код | HTTP | Условие | Поведение |
|---|
| — (no-op, warning) | — (Kafka consumer, HTTP нет) | Guard G1–G4 не пройден | Метрика no-op guard-ов |
| — (идемпотентный no-op) | — | G5/INV-REF-001: активная связь уже есть (лид в этом кейсе тоже не создаётся — R5) | Событие подтверждается |
| retry consumer-а | — | Реплика владельца/пользователя отсутствует | Retry |
| DLQ после исчерпания retry | — | Инфраструктурный сбой | Runbook referral-consumer-lag (README.md) |
Доменные События
| Событие | Когда поднимается | Subscribers |
|---|
ReferralBondCreatedEvent | Создание связи (шаг 3) | in-proc аудит |
ReferralRegistrationRecordedEvent | Запись журнала Initial (шаг 4) | in-proc |
Kafka Интеграционные События
| Событие | Topic | Когда публикуется | Consumers |
|---|
ReferralBondCreatedKafkaEvent | pin.referral.bonds.v1 | Связь создана (шаг 5) | rewards (основание выплат), notifications (уведомление рефереру), messenger (контакт «инвестор ↔ инвестор») |
ReferralLeadAttachRequestedKafkaEvent | pin.referral.lead-attachments.v1 | Не публикуется в этом сценарии (R5/INV-REF-002) | — |
Acceptance Criteria
| # | Критерий | Проверка |
|---|
| AC1 | Регистрация по ссылке инвестора → связь Active создана, lead-событие отсутствует | интеграционный тест: топик lead-attachments пуст |
| AC2 | Журнальная запись имеет LeadRequested = false при AppliedRule = R1 | проверка колонок журнала |
| AC3 | ReferralBondCreatedKafkaEvent опубликован с ReferrerCompanyId = null (реферер-инвестор) | контракт-тест события |
| AC4 | Ссылка PartnerManager/PinEmployee даёт тот же исход, что и инвесторская (таблица ролей) | параметризованный тест по типам владельца |
| AC5 | Ссылка DeveloperManager → лид СОЗДАЁТСЯ (граница R5) | контрпример-тест A3 |
| AC6 | Авторизация вне окна по ссылке инвестора → полный no-op (R3 + R5): ни связи, ни лида, ни журнала | тест A2 |
| AC7 | rewards получает BondId и может строить начисление рефереру-инвестору | e2e с rewards-стабом |
| AC8 | Повторная доставка события регистрации/входа не создаёт дублей связи/журнала (INV-REF-R1 + MessageId/G5) | двойная доставка |
Обратные ссылки
Автоссылки: где используется этот документ.
Связанные Документы