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

UC-REF-002. Аноним открыл реферальную ссылку (cookie 30 дней, validateReferralLink)

Назначение

Гость (аноним) переходит по реферальному URL с query-параметром referralUrl={slug}: slug фиксируется в cookie referralUrl на 30 дней, ссылка проверяется операцией validateReferralLink, гостю показывается имя пригласившего, а переход учитывается счётчиком VisitsCount. Привязка на этом шаге НЕ выполняется — она произойдёт позже по правилам attach-rules.md при регистрации/авторизации (UC-REF-003…006). Связан с контекстом referral.

Metadata

ПолеЗначение
IDUC-REF-002
Контекстreferral (см. docs/06-services/referral/)
Типосновной
Статусdraft

Актор

ПолеЗначение
РольГость (аноним); альтернатива A2 — авторизованный Инвестор (триггер T3)
Permissions / scopepermissions не требуются: validateReferralLink — публичная анонимная операция (attach-rules.md, раздел «Безопасность»)
Tenantне применяется (аноним); slug из URL — недоверенный вход (валидация формата)
Record-levelне применяется (публичное чтение обезличенных данных ссылки)

Предусловие

  • Реферер сформировал и передал URL вида {origin}{path}?referralUrl={slug} (UC-REF-001).
  • Ссылка существует и не удалена (DeletedAt == null) — иначе альтернатива A1.

Триггер

ПолеЗначение
ИсточникUI (открытие URL)
Триггерящий элементЛюбая страница фронта, открытая с query-параметром referralUrl (лендинг, карточка объекта, подборка и т.д.)

Основной Поток

  1. Гость открывает URL с ?referralUrl={slug} (из мессенджера, соцсети, QR-кода — канал зафиксирован в Channel ссылки, referral-link.md).
  2. Клиент сохраняет slug в cookie: referralUrl={slug}; path=/; max-age=2592000 (30 дней); SameSite=Lax. Повторное открытие с другим slug перезаписывает cookie (действует последняя ссылка).
  3. Клиент вызывает публичную операцию validateReferralLink со slug (validate-referral-link.md). Идемпотентность не требуется (чтение + атомарный счётчик).
  4. Проверка ссылки:
    • slug — недоверенный вход из URL/cookie: нормализация lowercase + формат ^[a-z0-9][a-z0-9-]{2,63}$ (шаг 2 нормативного алгоритма attach-rules.md); мусор → isValid = false без обращения к хранилищу.
    • Ссылка ищется по slug среди неудалённых (guard G1). Не найдена/удалена → isValid = false.
    • Найдена → isValid = true; referrerDisplayName = имя владельца (Name.FirstName из реплики ReferralUserReplica; PII сверх имени не раскрывается). Полный URL не строится (зона клиента).
    • При isValid = true — атомарный инкремент счётчика переходов VisitsCount (без гонок и без optimistic-конфликта, referral-link.md, поле VisitsCount).
    • Переход построчно не хранится — единственный след — счётчик (simplicity-first, ADR-0056).
    • Результат: { isValid, url, referrerDisplayName? }.
  5. Клиент при isValid = true показывает плашку «Вас пригласил {referrerDisplayName}»; при isValid = false — очищает cookie и ничего не показывает (edge case «битый slug», attach-rules.md).
  6. Гость пользуется каталогом; при последующем входе/регистрации auth-модалка читает cookie и подставляет slug полем referrerUrl во вход/регистрацию (UC-IDN-001) — продолжение в UC-REF-003/004/005.

Альтернативные Потоки

A1. Slug не существует, ссылка удалена или переименована

  1. Ссылка по slug не резолвится (в т.ч. старый slug после переименования — история slug-ов не ведётся, INV-REF-L6) → isValid = false, VisitsCount не инкрементируется.
  2. Клиент очищает cookie. Завершение — привязка по этой ссылке невозможна.

A2. Ссылку открыл авторизованный инвестор (триггер T3)

  1. Шаги 1–4 совпадают; дополнительно при авторизованном инвесторе (actor.UserId != null) и включённом флаге Referral:LinkOpenAttachEnabled запускается процесс привязки по триггеру T3: guard-ы G1–G4, правило R4 матрицы — связь НЕ создаётся, лид запрашивается, если владелец ссылки — агент/застройщик (INV-REF-002); полный алгоритм — attach-rules.md.
  2. Публикуется ReferralLeadAttachRequestedKafkaEvent (BondId = null, AppliedRule = R4). Возврат к шагу 5 основного потока.
  1. Cookie уже установлена ранее (в пределах 30 дней) — клиент её не трогает и validateReferralLink не вызывает.
  2. Slug доедет до referral при входе/регистрации. Завершение.

Постусловие

  • Cookie referralUrl со slug действует 30 дней (или очищена при isValid = false).
  • VisitsCount ссылки увеличен на 1 — база конверсии «переход → регистрация → связь» в партнёрской статистике per-канал.
  • Реферальная связь, журнал и лид НЕ созданы (кроме A2/R4 — только лид): гость ещё не идентифицирован.

Возможные Ошибки

КодHTTPУсловиеПоведение клиента
ValidationError400Slug длиннее 64 символов / пустой параметрНе показывать плашку; cookie не ставить
— (isValid = false в успешном ответе)200Ссылка не найдена / удалена / формат slug мусорный (G1)Очистить cookie, UI без приглашения

Доменные События

СобытиеКогда поднимаетсяSubscribers
Не поднимаются: переход фиксируется только счётчиком VisitsCount (ADR-0056)

Kafka Интеграционные События

СобытиеTopicКогда публикуетсяConsumers
ReferralLeadAttachRequestedKafkaEventpin.referral.lead-attachments.v1Только альтернатива A2 (триггер T3, правило R4, флаг Referral:LinkOpenAttachEnabled)crm (карточка лида, дедуп пары «инвестор+агент»)

Acceptance Criteria

#КритерийПроверка
AC1Открытие URL с валидным slug ставит cookie referralUrl c max-age = 2592000e2e: проверка cookie
AC2validateReferralLink для живой ссылки → isValid = true + referrerDisplayName из Name.FirstName реплики владельцаинтеграционный тест
AC3Каждый успешный validate инкрементирует VisitsCount ровно на 1; параллельные вызовы не теряют инкрементыконкурентный тест атомарного инкремента
AC4Несуществующий/удалённый slug → isValid = false, VisitsCount не растёт, клиент чистит cookieтест + e2e
AC5Формально невалидный slug (спецсимволы, > 64) не приводит к запросу в хранилищеunit-тест валидатора
AC6Повторное открытие с другим slug перезаписывает cookie (действует последняя ссылка)e2e
AC7Анонимный вызов не создаёт связь/журнал/лидпроверка отсутствия записей после validate
AC8При выключенном флаге Referral:LinkOpenAttachEnabled открытие ссылки авторизованным инвестором (T3) не публикует lead-событие — только инкремент VisitsCountтест с флагом off
AC9T3 при владельце-неагенте (R5/INV-REF-002) → lead-событие не публикуется даже при включённом флагетест владельца-инвестора

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

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

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