bot-analytics-dashboard

BotAnalyticsDashboardService

src/stack/components/user/bot-analytics-dashboard/bot-analytics-dashboard.service.ts — агрегує дані hiring-боту і рекламних витрат у дашбордні метрики

Всі методи приймають dateFrom, dateTo і необов’язковий utm-фільтр. Дані тягнуться паралельно з двох ClickHouse-таблиць: hiring_bot_events і ad_spend.

Бізнес-логіка графіків, типи візуалізації, осі X/Y — у Google Docs: Візуалізації дашбордів.

Загальні правила діапазону (groupBy)

Для методів з groupBy (DAY / WEEK / MONTH) автоматично виставляються межі по яким групуються данні:

  • WEEK → понеділок–неділя
  • MONTH → перше–останнє число місяця

Якщо розширений dateTo виходить за сьогодні — обрізається по сьогодні. Поточний незавершений тиждень/місяць у виводі матиме dateTo = сьогодні. Записи без даних повертаються з null-значеннями — для безперервної осі X без пропусків.

Якщо dateFrom/dateTo у запиті не передано — контролер підставляє останні 6 місяців.

Логіка методів

Воронка (getFunnelSteps)

Фіксований порядок 13 кроків: start → name → age → vacancy → experience → devices → employmentType → workShift → voiceCommunication → salaryInfo → about → phoneNumber → lead_submit

Діапазон дат формує когорту: у воронку потрапляють користувачі, які мають подію start у цьому діапазоні. Для відібраних користувачів аналізуються всі подальші події анкети. Кроки, яких немає в даних за діапазон, — пропускаються (не повертаються з нулями). step_cr = users / users попереднього кроку; total_cr = users / users на start. drop_off — кількість унікальних користувачів, чия остання подія анкети є bot_message_sent для цього питання без подальшої відповіді або переходу. Повторне проходження анкети не створює окремий drop-off: враховується поточний стан за останніми подіями користувача.

Відновлення пропущених funnel-подій (repairMissingFunnelSteps)

POST /hiring-analytics/repairMissingFunnelSteps — ручний director-only repair для історичних подій, які не записалися через відомі confirmation-гілки бота. Body: { dateFrom, dateTo, apply?: boolean }, де дати обов’язкові у форматі YYYY-MM-DD.

За замовчуванням це dry-run (apply не передано або false): відповідь містить знайдені user_id, відсутній step_id і timestamp майбутньої вставки, але нічого не записує. З apply: true вставляються user_reply_received події. Повторний запуск idempotent: після вставки користувач більше не потрапляє у вибірку.

Repair відновлює лише два підтверджені дефекти: employmentType перед наявним workShift та voiceCommunication перед наявним salaryInfo. Він не намагається заповнювати всі пропущені кроки, щоб не перетворити реальний drop-off на штучне проходження воронки.

Воронка з витратами (getFunnel)

Якщо utm не передано — сервіс спершу запитує список унікальних utm_source за діапазон, потім паралельно тягне event-метрики і витрати по кожному source окремо. Якщо utm передано явно — автодискавері не запускається.

label для рядка будується з ненульових utm_*-полів, з’єднаних _. Поля fbclid, pixel_id, userAgent з лейблу виключаються навіть якщо непорожні.

cpl = spend / starts, cpa = spend / lead_submits. Якщо starts = 0 або lead_submits = 0 — відповідне значення null.

CPL / CPA динаміка (getCplCpa)

Ті самі правила groupBy-діапазону що й у getTrafficLeadsCR. Для кожного періоду паралельно читаються events (getPeriodicTrafficAndLeads) і витрати (getPeriodicSpend). Якщо за певний період немає ні подій, ні витрат — cpl = null, cpa = null.

Медіана часу відповіді (getStepMedianResponseTime)

Фіксований порядок 11 питань: name → age → vacancy → experience → devices → employmentType → workShift → voiceCommunication → salaryInfo → about → phoneNumber. Кроки start і lead_submit не входять — для них немає пари «запитання–відповідь».

is_slow = true якщо медіана > 60 секунд.

Сегментація по платформі (getPlatformSegmentation)

Відсотки округлюються до 1 знака. Останній елемент отримує залишок (100 − сума вже призначених відсотків) — щоб уникнути накопиченої помилки округлення.

Помилки (getTechnicalErrors)

Зберігається загальний формат із чотирма категоріями (timeout / input_error / api_fail / crm_error) у фіксованому порядку, навіть якщо count = 0. Кожен запис додатково має questions: масив { step_id, count } із кількістю помилок цього типу для кожного питання анкети.

UTM-фільтри (getUtmFilters)

Джерело — виключно події start (UTM пишеться тільки при запуску бота).

Повертає три структури:

  • utmAll — масив унікальних комбінацій усіх UTM-полів; записи де всі поля порожні — відкидаються
  • utmSources — плоский відсортований список непорожніх utm_source
  • utmManagers — плоский відсортований список непорожніх utm_manager

Призначений для підтягування фільтр-опцій у UI дешборду. Utm мітки відносно dateTo за вказаний місяць.

Місячне зведення (getMonthSummary)

Приймає будь-яку дату date всередині місяця — перше і останнє число цього місяця визначаються автоматично. Попередній місяць — аналогічно, зрушений на один місяць назад. Якщо передати, наприклад, 2026-06-15 — поточний діапазон буде 2026-06-01 … 2026-06-30, попередній — 2026-05-01 … 2026-05-31.

Запускає 4 паралельних запити: поточний місяць × events, попередній місяць × events, поточний місяць × spend, попередній місяць × spend. Якщо передано utm-фільтр — він застосовується однаково до всіх чотирьох запитів.

Повертає 4 метрики, кожна з полями current і delta_percent:

МетрикаcurrentПримітки
trafficунікальні користувачі на кроці start
leadsунікальні користувачі на кроці lead_submit
crleads / traffic × 100, округлення до 2 знаківnull якщо traffic = 0
spendсума витрат за місяць з ad_spend

delta_percent — відносна зміна, не різниця в процентних пунктах:

Округлення до 1 знака. null якщо previous = 0, previous = null або current = null.

Для CR це означає: якщо минулий місяць CR = 10%, поточний = 15% — delta_percent = 50% (CR зріс удвічі-з-половиною), а не 5 (не різниця в пп).

Нюанси

  • Гвард на контролері (director, technical_department) задекларований але закоментований — звичайні dashboard endpoints зараз відкриті. Виняток: repairMissingFunnelSteps доступний лише director.
  • mock/*-роути і BotAnalyticsDashboardMockService тимчасові — мають бути прибрані після завершення розробки дешборду.
  • getStepRawEventsDebug повертає сирі пари bot_message_sent + user_reply_received для ручної перевірки медіанного розрахунку.

Зв’язки