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_sourceutmManagers— плоский відсортований список непорожніх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 | |
| cr | leads / 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для ручної перевірки медіанного розрахунку.
Зв’язки
- Читає: hiring-bot-events —
hiring_bot_eventsClickHouse - Читає: meta-ad-spend —
ad_spendClickHouse