05-statistics

Статистика

src/statistics/ · project-golden/src/statistics/ — операції партнера по TU, прив’язка сум до команди по днях і місячна статистика операторів і TU для всіх ролей.

Дані: project_statistics, project_statistics_temp, project_statistics_relations · Swagger: тег Statistics · План: StatisticsService

Що робить модуль

  • Зберігає операції партнера по TU: завершені дні — project_statistics, незавершені — project_statistics_temp.
  • Щодня прив’язує суми TU до власників дня — оператора, тімліда, client manager — у relations.
  • Віддає місячну статистику операторів і TU для director, тімліда, топа, client manager і оператора; незавершені дні накладаються на читанні.

Спільні кроки — у StatisticsService; правила, що поки свої в кожного проєкту (запис relations, незавершені дні), — в адаптерах src/statistics/adapters/ (реалізовано Golden). Дані Stack читаються напряму з колекцій, без RMQ.

Потік даних

КрокХтоКоли
Оперативна вигрузка: незавершені дні (сьогодні, до 13:06 — ще й вчора) по кожній активній адмін панелі → project_statistics_temp; далі платформа створює порожні favorites для нових пар (06-favorites)project-goldenщохвилини
Денна вигрузка: завершені дні з дня після останнього записаного → project_statistics; невідома TU запускає refresh TUproject-golden13:06 за Києвом, 3 спроби з паузою 30 хв
Relations нових днів (нижче), потім у фоні копія статистики в ClickHouse (10-metrics)платформа, internal updateпісля денної вигрузки
Перерахунок favoritesплатформа (06-favorites)після relations
Видалення завершених днів з project_statistics_tempproject-goldenпісля перерахунку

Історія TU до появи в агенції пишеться в project_statistics як зовнішня під час init TU.

Логіка

Relations дня

Для кожного дня після останнього в relations адаптер будує записи дня (Golden — перенесений legacy updateCollection):

  • TU продовжує фінальний запис попереднього дня або починається з події створення.
  • Події доби з tu_logs ділять TU на відрізки; операції відрізка дістаються його власникам. Власника змінюють лише логін оператора, зняття оператора, що працює, зняття його тімліда і зміна client manager; призначення лише фіксує поточний запис.
  • Після TU — записи операторів без TU з журналу Stack log_operators: заблоковані за добу, нові, з яких зняли всі TU, і порожні активні.
  • Службові типи операцій і подарунки в relations не входять.
  • День без статистики пропускається.

Порожні relations (без міграції) — помилка: перший день рахується від уже записаного.

Relations місяця на читанні

Записані дні місяця плюс незавершені дні з project_statistics_temp: від останнього записаного дня адаптер будує кожен день знімка тим самим алгоритмом, що й запис; фільтр роуту застосовується й до них. Якщо останній записаний день — у попередньому місяці, оператори, заблоковані чи видалені до початку місяця, в незавершені дні не потрапляють.

Рядок оператора

  • Сума дня — сума relations оператора; null, коли суми немає, а день red чи gray.
  • Стан дня: до першої події місяця з журналу оператора (log_operators) — зворотний до неї; день без подій після блокування — red, після видалення чи зняття тімліда — gray; інакше — день оператора Stack (stack_operator_days).
  • Статус місяця — за останньою подією журналу. Кількість TU — TU з фінальним активним записом в останній день; у поточному місяці — TU, закріплені зараз у project_tus.
  • Сторінка оператора: рядок оператора і рядки TU з relations, у поточному місяці — ще й TU, закріплені за ним зараз. Для TU без оператора сума дня — сума його TU; TU без суми не показуються.

Рядок TU

Стан дня — зі стану TU в першому записі дня, рольових і lifecycle-подій tu_logs; стан переходить на наступний день. Сума — relations TU. TU, видалена весь місяць, не показується, якщо зараз не закріплена за оператором. Ім’я, вік, фото — project_tus; рівень та історія рівня — картка TU у Stack; оператор бачить ім’я без прізвища. У списках — TU, активні в місяці або з сумою.

Топ

Тімліди топа за місяць — з журналу тімлідів і stack_log_top_managers. Дні, коли тімліда не було в топа, сірі й без суми; у списку операторів — дні поза відрізками оператора в топа.

Online

Джерело — відрізки online платформи.

  • День online — доба проєкту або з onlinePeriod: shift зміна: від години початку робочих годин тімліда до тієї ж години наступного дня. Тімлід зміни: у командах тімліда — він сам; у director, топа і на сторінці оператора — поточний тімлід оператора; без тімліда чи годин — доба.
  • Online дня — сума перетинів відрізків із днем; відрізок без тривалості — 1 с. online — Hч Mм (хвилини вниз, менше хвилини — 0ч 1м), onlineSec — секунди, null при нулі. Не показується в день без суми зі станом red чи gray; у топа поза відрізками оператора — online: null, onlineSec: 0.
  • У команді тімліда і топа online рахується лише за час оператора в цьому тімліді (тімлідах топа).
  • Online рахується оператору з relations, журналом або днями оператора в місяці; рядок TU без оператора — null.
  • TU — лише на сторінці оператора: час відкриття TU в Electron цього оператора; TU без relations і подій у місяці — null, на сторінці TU без оператора — 0ч 0м.
  • isOnline — програма оператора підключена зараз (Electron Gateway, поки stack-golden); лише для оператора в стані none, інакше false. Без Gateway — усі false, запит не падає. sleepMode не переноситься: у legacy він вимкнений.

API для frontend

Усі роути приймають project, month (YYYY-MM) і поля нижче. operatorProjectId: null — TU без оператора, clientManagerProjectId: null — TU без client manager. Роути операторів приймають onlinePeriod: day | shift.

РоутРоліПоля
POST /v2/statistics/operatorsdirector—
POST /v2/statistics/team-lead-operatorsdirector, тімлідteamLeadProjectId
POST /v2/statistics/top-manager-operatorstop manager, directorteamLeadProjectId?, topManagerUserId?
POST /v2/statistics/operatoroperator, directoroperatorProjectId
POST /v2/statistics/team-lead-operatordirector, тімлідteamLeadProjectId, operatorProjectId
POST /v2/statistics/top-manager-operatortop manager, directoroperatorProjectId, teamLeadProjectId?, topManagerUserId?
POST /v2/statistics/tusdirector—
POST /v2/statistics/client-manager-tusdirector, client managerclientManagerProjectId
POST /v2/statistics/team-lead-tusdirector, тімлідteamLeadProjectId
POST /v2/statistics/top-manager-tustop manager, directorteamLeadProjectId?, clientManagerProjectId?, topManagerUserId?
POST /v2/statistics/client-manager-team-lead-tusclient manager, directorteamLeadProjectIds, clientManagerProjectId?

Топ без teamLeadProjectId — усі його тімліди за місяць; без topManagerUserId — топ з токена.

Internal API

РоутХто викликаєНавіщо
POST /v2/internal/statistics-relations/updateproject-goldenrelations нових днів після денної вигрузки
POST /v2/internal/statistics-relations/users-sum— (замінює RMQ GoldenUsersStatisticsSum Stack)суми операторів і тімлідів за весь час
POST /v2/internal/statistics-relations/hr-operators— (замінює RMQ getStatisticsFromFamily)статистика операторів для HR

Звірка з legacy

  • Запис relations на дампі від legacy-записів попереднього дня: 01.12.2025–12.09.2026 — 402 231 запис проти 402 222 legacy, розбіжності в 10 днях по 1–4 записи. До грудня 2025 legacy рахував без логінів операторів — ця історія переноситься міграцією.
  • HTTP-звірка з stack-golden (той самий токен директора, 23 випадки кожного роуту, зокрема TU без оператора, client manager, топ з усіма тімлідами): липень і серпень 2026 — 0 розбіжностей по сумах і станах днів, підсумку, статусу, кількості TU, імені, фото і рівню TU; вересень до 27.09 — 0, решта — рядки з temp-даних legacy за 28–29.09, яких локально немає.
  • Online: по добі — 0 розбіжностей у липні–вересні, крім 1 с на legacy-дублі відрізка. По змінах дві відмінності від legacy: директорський список legacy обрізає останню зміну місяця опівночі; сторінка оператора в топа з усіма тімлідами в legacy online не обрізає. Тут обидва випадки рахуються повністю й однаково.
  • Незавершені дні 28–29.09 проти legacy createAdditionalRelations на тих самих temp-даних — 2237 і 1256 записів, 0 розбіжностей.

Нюанси

  • Оновлення relations — одне на проєкт одночасно (лок 1 година); повторний запуск під час роботи — 409.
  • Legacy-копія алгоритму незавершених днів не перевіряла блокування оператора; тут обидва шляхи використовують один алгоритм.
  • Legacy падає на тімліді, якого немає в топа; тут такі дні сірі.

Борг

  • Ролі не обмежують teamLeadProjectId, operatorProjectId і clientManagerProjectId своїм scope — як у legacy: тімлід може запросити чужу команду.
  • users-sum і hr-operators реалізовані, але Stack досі ходить у legacy через RMQ.