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

Панель Метрик Застройщика: dev-stats-overview (аналитика внимания)

Продуктовая ЛК-панель застройщика над DevStats.Api (ADR-0057): обзор внимания к его недвижимости — просмотры, уникальные зрители, длительность, повторность, топы по иерархии и воронка конверсии за период. Это не ops-борд ingestion (здоровье консюмеров/лаги/пакетной вставки — отдельно, см. dev-stats-ingest-overview.md): здесь только бизнес-метрики, отдаваемые read-only через ClickHouse pin_dev_stats (dedup-tolerant argMax/*Merge, без FINAL). Дом мер — ../facts/property-object-view-daily-rollup.md, ../facts/funnel-daily-rollup.md, ../facts/session-facts.md; фильтр — DevStatsFilterModel (../domain/dev-stats-filter.md).

Назначение

Отвечает на бизнес-вопрос застройщика: кто, сколько, как долго и как часто смотрит мои проекты/здания/ группы планировок/планировки/объекты и как это внимание конвертируется в лиды и сделки за выбранный период и срез иерархии. Панель — клиентская композиция ключевых метрик области developers (сводка, динамика, топы, сегменты аудитории, воронка): фронт вызывает нужные из 12 read-only методов и собирает дашборд (backend-эндпоинта «get-dashboard» нет — каждая плитка = отдельный метод, ADR-0056). Owning team — PIN Analytics (dev-stats); аудитория — застройщик. Панель не показывает операционные данные (user-activity), историю цен (invest-analytics) и персональные данные зрителей.

Область Действия

ПолеЗначение
ИнструментПродуктовая ЛК-панель застройщика поверх DevStats.Api (read-only), не Grafana
Источник данныхClickHouse pin_dev_stats (property_object_view_daily, funnel_daily, property_object_view_facts, session_facts)
РазделЛК застройщика → «Аналитика внимания»
Сервисыdev-stats (owner); upstream — user-activity, chessboard, favorites/selections/crm (через факты/воронку)
Аудиториязастройщик (роль developer, permission dev-stats.view)
Primary SLOFreshness фактов < 5 мин; API p95 ≤ 300 мс (rollup-панели), ≤ 600 мс (сырые факты)
Статусdraft

Переменные

Переменные панели = поля DevStatsFilterModel (дом — ../domain/dev-stats-filter.md). DeveloperId не выбирается пользователем — он жёстко проставляется из claims актора (tenant isolation, ADR-0052) и подставляется в каждый запрос как TenantId = DeveloperId. DateFrom/DateTo обязательны и режут партиции toYYYYMM до сканирования (см. подсказку по индексам в property-object-view-daily-rollup.md).

ПеременнаяТипИсточникЗначение по умолчаниюБезопасность
tenantId (DeveloperId)TenantId (UUID)claims актора (не из UI)= акторОбязателен, = актор; fail-closed при чужом id
DateFromDateUI (обязателен)today − 30dОбязателен; ограничивает партиции
DateToDateUI (обязателен)todayОбязателен; DateTo ≥ DateFrom
ProjectIdPropertyObjectIdUUID?UI (dim_* актора)*Префикс иерархии; только свой DeveloperId
ViewerKindenum? (Guest/Agent/Investor)UI*Bounded (3 значения)
ViewSourceenum? (Catalog/Search/Selection/Favorites/News/DirectLink/Unknown)UI*Bounded (7 значений)
granularityenum (day/week/month)UIdayBounded; шаг рядов динамики
groupByenum (уровень иерархии / ViewerKind / ViewSource)UIprojectBounded; разбивка топов/сегментов

Панели

Каждая панель питается отдельным read-only методом области developers (*Merge/argMax без FINAL). Пороги — не алерты (это продуктовый борд), а справочные ориентиры «нормы внимания».

PanelМетодАгрегацияФакт/роллап
kpi-summaryget-views-summaryuniqMerge(Views), uniqMerge(UniqViewers), sumMerge(DurationSum)/uniqMerge(Views)property_object_view_daily
views-trendget-views-trenduniqMerge(Views) по бакету (granularity) + сравнение периодовproperty_object_view_daily
unique-viewersget-unique-viewersuniqMerge(UniqViewers) + разрез ViewerKind + рядproperty_object_view_daily (uniqState)
repeat-frequencyget-repeat-frequencyраспределение визитов на пару «зритель × объект»property_object_view_facts
duration-histogramget-duration-histogramбины длительностей + avg/medianproperty_object_view_facts
hierarchy-funnelget-hierarchy-funnelстадии по иерархии + top-N детей (dim_*)property_object_view_daily
top-objectsget-top-objectsтоп объектов уровня по Views/DurationSum (постранично)property_object_view_daily / dim_*
conversion-funnelget-conversion-funneluniqMerge этапов + конверсииfunnel_daily
session-depthget-session-depthобъектов за сессию (avg/median/p90 + распределение)property_object_view_facts
activity-heatmapget-activity-heatmapчас × день недели (таймзона)property_object_view_facts
bounce-signalget-bounce-signalдоля коротких просмотров по объектуproperty_object_view_facts
time-to-first-viewget-time-to-first-viewTTFV опубликованных объектовdim_property_object + property_object_view_facts

Топы (top-objects) и bounce-signal/time-to-first-view пагинируются постранично; воронка следует флагу dev-stats.funnel.enabled (нижние этапы активны по мере готовности selections/crm).

Метрики

Смысл мер — из property-object-view-daily-rollup.md (§ «Определение measures»), funnel-daily-rollup.md, session-facts.md. Все меры dedup-tolerant: uniq-состояния устойчивы к open/closed-склейке (по PropertyObjectViewId) и at-least-once.

МетрикаТип (CH)Чтение (без FINAL)Смысл
ViewsAggregateFunction(uniq, UUID)uniqMerge(Views)Число просмотров (= uniqState(PropertyObjectViewId), open+closed = 1).
UniqViewersAggregateFunction(uniq, UUID)uniqMerge(UniqViewers)Уникальные зрители (coalesce(UserId, AnonymousId)).
RepeatViewsпроизводнаяuniqMerge(Views) − uniqMerge(UniqViewers)Просмотры сверх первого на зрителя.
DurationSumAggregateFunction(sum, Int32)sumMerge(DurationSum)Суммарная длительность (open-строки вносят 0).
AvgDurationпроизводная/avgsumMerge(DurationSum)/uniqMerge(Views)Средняя длительность на просмотр (нуль-смещение учтено).
SessionDurationInt32 (session_facts)argMax(DurationSeconds, IngestionTime) по SessionIdДлительность сессий, где смотрели объекты актора.
Funnel.*AggregateFunction(uniq, UUID)uniqMerge(<этап>)Views/Favorites/Selections/Leads/Bookings/BookingsPaid/Deals/DealsCompleted.

Разбивки задаёт groupBy: по уровню иерархии — удлинение префикса order key; по аудитории (ViewerKind) — хвостовой компонент property_object_view_daily; по источнику (ViewSource) — из property_object_view_facts.

Сигналы Logs И Traces

Продуктовый борд не поднимает alert-ы — операционное здоровье (ingest-lag, пакетная вставка, 5xx, freshness) живёт в ops-борде dev-stats-ingest-overview.md. Здесь фиксируется только аудит чтения и корреляция запросов метрик.

СигналQueryCorrelation
Logs (чтение метрик)service=dev-stats area=developers operation=~"get-.*" outcome=okcorrelationId, tenantId (= DeveloperId)
Logs (отказ доступа)service=dev-stats outcome=forbidden reason=scope-mismatchcorrelationId, tenantId, requestedDeveloperId
Traces (панель)service=dev-stats span=~"get-.*" (per-metric CH-запросы)correlationId, causationId

Экспорт панели по фильтрам аудируется отдельно (permission dev-stats.export) — см. ../reports/dev-stats-export.md.

Маскирование Sensitive Data

  • Панель показывает только агрегаты: счётчики (Views/UniqViewers/RepeatViews), длительности, доли по ViewerKind/ViewSource, этапы воронки. ФИО, телефоны, UserId/AnonymousId/SessionId зрителей наружу не выводятся (ADR-0057 §6).
  • DeveloperId подставляется из claims, а не из UI: застройщик не может запросить чужой scope (fail-closed).
  • Сессионные метрики отдаются только в пересечении с просмотрами объектов актора (SessionId ∈ property_object_view_facts WHERE DeveloperId = актор).
  • Скриншоты панели для инцидентов/презентаций маскируют tenantId и любые id в аннотациях.

Промежуточные состояния и eventual consistency

Панель читает аналитическое хранилище с окном свежести ≤ 5 мин — застройщик в переходные моменты может видеть не финальную картину. Это ожидаемое поведение, а не ошибка данных:

  • Окно свежести (≤ 5 мин). Свежий просмотр/только что опубликованный объект появляется в панелях с задержкой batch-ingestion. До этого панель показывает прежние значения (для нового объекта — 0), без ошибки.
  • Open/closed длительность. Пока closed-факт просмотра не пришёл, просмотр учтён по open-строке (DurationSeconds = 0) — Views уже корректен, но AvgDuration/гистограмма могут кратко занижаться; при чтении склейка по PropertyObjectViewId (argMax(*, IngestionTime)) выбирает closed-строку. Число просмотров не задваивается.
  • Отставание воронки. Нижние этапы (Favorites/Selections/Leads/Bookings/Deals) питаются из разных источников и могут отставать от Views — конверсия временно занижается, затем догоняется. Флаг dev-stats.funnel.enabled управляет активностью нижних этапов.
  • Деградация подписи иерархии. Если строка измерения (dim_project/dim_building/…) ещё не долетела, панель резолвит метрику по id и показывает id вместо имени (плейсхолдер), затем подтягивает Name. Метрика при этом корректна (иерархия факта денормализована).
  • Dimension gap объекта. Просмотры по объекту, чья строка dim_property_object не долетела, временно отсутствуют в фактах (парковка на late-dimension) и появляются после backfill измерения; видно в ops-борде (dev-stats-ingest-overview.md, панель dim-miss).

Проверка

ПроверкаОжидаемый Результат
Empty stateПри отсутствии просмотров панели показывают 0/нет данных без ошибок запроса.
Tenant isolationВсе панели содержат только данные DeveloperId = актор; подмена id → 403.
Обязательные фильтрыЗапрос без DateFrom/DateTo отклоняется валидацией (400); DateTo ≥ DateFrom.
ДедупПовтор доставки события (open+closed, at-least-once) не задваивает Views/этапы воронки.
Time rangeПанели корректны на 7d и 1y (до TTL 3 года) диапазонах.
Funnel flagПри dev-stats.funnel.enabled = false панель conversion-funnel скрыта/показывает только views.
СвежестьПросмотр виден в панелях в пределах окна ≤ 5 мин; до этого — прежние значения без ошибки.
Деградация подписиПри отсутствующей строке dim_* метрика корректна, подпись показывает id вместо имени.
Отставание воронкиПри лаге источников нижние этапы < Views; конверсия догоняется, ошибок нет.
No FINALВсе панельные запросы используют argMax/*Merge без FINAL (dedup-tolerant чтение).

Топология панели

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

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

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