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

UC-STA-004. Воронка конверсии «просмотр → сделка»

Назначение

Застройщик смотрит воронку конверсии своей недвижимости за период — views → favorites → selections → leads → bookings → deals (с промежуточными bookingsPaid/dealsCompleted) и коэффициенты перехода между этапами. Сценарий только читает данные; лиды и клиенты в воронке обезличены — раскрываются только счётчики. Контракт операции — get-conversion-funnel.md; модель фильтра — dev-stats-filter.md; источник — суточный агрегат воронки funnel_daily (каждый этап считается устойчиво к дублям по бизнес-ключу). Контекст — dev-stats (ADR-0057).

Metadata

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

Актор

РольPermissions / scopeTenantRecord-level
Девелопер (застройщик)dev-stats.view; scope строго = свой DeveloperIdTenantId = DeveloperId = CompanyEntity.Id (ADR-0052)не применяется — воронка агрегатна (персональные данные лидов/клиентов не раскрываются)

DeveloperId = актор — головной компонент ключа funnel_daily и обязательный фильтр (fail-closed).

Предусловие

  • Feature flag dev-stats.funnel.enabled = true (иначе — A3, деградация до доступных этапов).
  • Агрегат funnel_daily наполняется: этап views — из фактов просмотров; нижние этапы — из событий контекстов favorites/selections(future)/crm с обогащением иерархии из измерения dim_property_object.
  • Токен застройщика содержит право dev-stats.view.

Триггер

ПолеЗначение
ИсточникUI (вкладка «Воронка» дашборда) → API call
Триггерящий элементдашборд /developer/stats (таб воронки) → GET /api/v1/dev-stats/funnel

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

Полный контракт операции — get-conversion-funnel.md; ниже — сценарий воронки.

  1. Ввод. Застройщик задаёт период (DateFrom/DateTo) и опционально срез иерархии (ProjectId/BuildingId/LayoutGroupId/LayoutId/PropertyObjectId). Разбивки по типу зрителя в воронке нет (она про конверсию объектов/иерархии — см. funnel_daily).
  2. Запрос. Воронка за период и (опционально) срез иерархии; DeveloperId — из токена.
  3. Проверка входа и доступа. DateFrom/DateTo обязательны и DateFrom ≤ DateTo (≤ срок хранения); уровни-фильтры — валидные идентификаторы; DeveloperId = актор (fail-closed); проверяется флаг dev-stats.funnel.enabled. Нарушение — VALIDATION_ERROR; чужой DeveloperId/нет права — FORBIDDEN; флаг выключен — деградация FUNNEL_DISABLED (A3).
  4. Чтение этапов. Данные читаются из funnel_daily в срезе DeveloperId = актор [∧ уровни] ∧ EventDate ∈ [DateFrom; DateTo] — только выбранная ветка иерархии и только затронутые месячные партиции по дате. Воронка «по проекту за период» = срез (DeveloperId, ProjectId); детализация — сужение среза до дочернего уровня.
  5. Расчёт счётчиков и конверсий (устойчивый к дублям). На срез — счётчик по каждому этапу: Views, Favorites, Selections, Leads, Bookings, BookingsPaid, Deals, DealsCompleted (каждый считается по своему бизнес-ключу — PropertyObjectViewId/FavoriteId/LeadId/BookingId/DealId — что даёт идемпотентность к at-least-once). Конверсии — производные при чтении: favorites/views, leads/favorites, bookings/leads, deals/bookings, сквозная deals/views (stage / prevStage с защитой от нуля: prev = 0null).
  6. Результат. Возвращается типизированный FunnelModel: список FunnelStage (StageName, Count, ConversionFromPrevPercent, ConversionFromTopPercent) в каноническом порядке этапов. Интерфейс рисует воронку с абсолютами и % перехода; недоступные этапы (источник не готов) помечены «нет данных» (A2); флаг выключен → только views/готовые этапы (A3).

Диаграмма

Интеграция и согласованность этапов

Сценарий read-only читает единую витрину funnel_daily без синхронных вызовов соседей, но её этапы наполняются разными источниками с независимой свежестью:

  • Этап views — из фактов просмотров (user-activity); нижние этапы (favorites/selections/leads/ bookings/deals) — из событий favorites/selections/crm, обогащённых иерархией из dim_property_object (событие несёт PropertyObjectId, но не иерархию). Контракты входов — events/README.md.
  • Порядок доставки этапов не важен: каждый источник вставляет частичную строку только со своим этапом, состояния сливаются по ключу (иерархия, EventDate). Верхние этапы считаются независимо от нижних — отставание crm не блокирует views/favorites.
  • Каждый этап идемпотентен по своему бизнес-ключу — повтор события (at-least-once) не растит счётчик. Окно eventual-consistency ≤ 5 мин; отстающий нижний этап отдаётся частичным счётчиком, конверсия к нему помечается «предварительно».

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

A1. Пустой период / срез

  1. На срезе нет ни одного этапа за период.
  2. Возвращается воронка из нулей (все Count = 0, конверсии null) — HTTP 200.

Завершается: фронт показывает пустую воронку.

A2. Нижний этап отстаёт (источник лагает)

  1. Верхние этапы (views/favorites) наполнены, нижний (leads/bookings/deals) отстаёт — контекст crm догоняет свою очередь событий.
  2. Отстающий этап отдаётся текущим (частичным) счётчиком; конверсия к нему помечается «предварительно». Верхние этапы считаются независимо от нижних (частичные состояния сливаются по ключу).

Возвращается к основному потоку на шаг 6 (результат с пометкой freshness).

A3. Воронка отключена флагом (dev-stats.funnel.enabled = false)

  1. Источники нижних этапов (selections/crm) ещё не готовы к проду.
  2. API отдаёт только этап views и уже подключённые этапы; выключенные этапы — с признаком Enabled = false; UI скрывает их либо показывает «скоро». Код деградации — FUNNEL_DISABLED.

Постусловие

  • Мутаций нет (read-only): агрегаты/факты/измерения не изменяются, доменные и интеграционные (Kafka) события не публикуются; dev-stats — листовая аналитика (собственных событий не порождает), а этапы воронки потребляет из контекстов favorites/selections/crm (см. funnel-daily-rollup.md).
  • Клиент получил воронку этапов + конверсии в scope своего DeveloperId; персональные данные лидов/клиентов не раскрыты (только счётчики).

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

КодHTTPУсловиеПоведение клиента
VALIDATION_ERROR400Нет дат / DateFrom > DateTo; невалидный уровень-идентификаторПоказать ошибку параметров
FORBIDDEN403DeveloperId ≠ актор; нет dev-stats.viewСкрыть данные
FUNNEL_DISABLED200/409Флаг dev-stats.funnel.enabled = false (A3) — деградация до доступных этаповПоказать частичную воронку/«скоро»
— (не ошибка)200Пустой период (A1) или отстающий этап (A2)Пустая/частичная воронка
5xx (ProblemDetails)500/503Отказ аналитической витрины pin_dev_statsРетрай позже

Acceptance Criteria

#КритерийПроверка
AC1Воронка «по проекту за период» сканирует только срез (DeveloperId, ProjectId) + затронутые партицииТест плана запроса
AC2Каждый этап считается по бизнес-ключу; повтор события не растит счётчик (идемпотентность)Тест: дубль lead-created/favorite-added не меняет этап
AC3Конверсии = stage/prev с защитой от нуля; prev=0null (без деления на ноль)Тест: срез с нулевым верхним этапом
AC4Отстающий нижний этап не блокирует верхние (частичные состояния сливаются по ключу)Тест A2: views есть при пустых deals
AC5Флаг dev-stats.funnel.enabled=false → частичная воронка с Enabled=false этапамиТест A3
AC6Подсчёт устойчив к дублям; DeveloperId=актор (чужой → 403)Тест изоляции + устойчивости к дублям
AC7Ответ типизирован (FunnelModel/FunnelStage); персональные данные не раскрытыРевью контракта ответа
AC8Порядок доставки этапов не влияет на результат; отставание нижнего этапа не блокирует верхниеТест: события этапов в разном порядке/с лагом дают тот же итог, views полон при пустых deals

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

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

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