ProjectFavorite
Collection project_favorites — спільний документ favorite для пари TU + RU. Schema, indexes і ProjectFavoriteRepository оголошені в @it-monkeys/backend-common, типи — у @it-monkeys/contracts (ProjectFavoriteBase).
Поля
- project — проєкт, до якого належить favorite.
- tuId — посилання на ProjectTU.
- ruId — ID RU на партнері, збережений як string: Golden і Prime — числовий ID, Chathouse і Udate — ULID.
- type —
new,normal,naughtyабоaggressive; ручне поле оператора. - contacts.status —
yesабоno. - contacts.email — email RU у нижньому регістрі, якщо він зберігається для цього проєкту.
- blockedByRU — RU заблокував TU; оновлюється з partner-даних.
- blockedByTU — RU вручну заблокований для TU у наших робочих логіках.
- firstPaidAt / lastPaidAt — перша й остання платна дія RU для TU;
null, поки платної дії не було. - profit — profit пари TU + RU у форматі конкретного проєкту: сума витрат у Golden і Chathouse, кількість платних вхідних повідомлень у Prime і Udate.
- gifts — кількість подарунків, якщо проєкт повертає їх у джерелі.
- isWhite — RU входить у white-list цієї TU.
- createdAt / updatedAt — час створення й останньої зміни документа.
У документі немає directorId: scope директора визначає TU.
Profit
| Проєкт | profit | gifts |
|---|---|---|
| Golden | Сума витрат | ✅ |
| Prime | Кількість платних вхідних повідомлень | — |
| Chathouse | Сума витрат разом із free credits | ✅ |
| Udate | Кількість платних вхідних повідомлень | 🟡 |
В Udate поле gifts зараз є, але джерело для його заповнення не реалізоване.
Поточні collections по проєктах
Реальні поля з production-дампу на 2026-09-03.
| Проєкт | Collection | Документів | Ключ пари | Платна активність | Блокування | Ручні поля |
|---|---|---|---|---|---|---|
| Golden | golden_favorites | 163 090 | ladyId_api + manId_api, unique | startDate, lastDate + time (рядки), profit, gifts | blocked (1 doc), blockedByTU; фактичні блоки RU — у golden_block_lists | type, contactDetails.{status,email}, name, note + updateNote, language |
| Prime | prime_favorites | 365 749 | ladyId + manId | firstManMessage, lastManMessage, lastMessage (ms), messageCounter; messages[] завжди порожній | blocked, isBlackList | type, contacts, note + updateNote |
| Chathouse | chathouse_favorites | 328 384 | ladyDbId + manUlid, unique | firstProfitAt, lastProfitAt (Date), profit, gifts, isFree | blocked, isBlackList | type (NEW), contacts, name, note + updateNote |
| Udate | udate_favorites | 78 253 | ladyUlid + manUlid | тільки messages[] з timestamp і messageId (у середньому 24, максимум 4 161 на пару) | blocked, isBlackList | type (NEW), contacts, name, note + updateNote, isCheckedNew |
Окремі агрегати для dashboard (*_favorite_news, *_favorite_uniques, prime_favorite_actions) до цієї сутності не належать і розглядаються разом зі статистикою.
Legacy → canonical для Golden
| Canonical | Legacy golden_favorites | Правило |
|---|---|---|
_id | _id | зберігається |
tuId | ladyId_api | через project_tus.partnerIds.idApi у scope директора; пари без TU не переносяться |
ruId | manId_api | String(manId_api) |
type | type | New → new і далі за назвою |
contacts.status | contactDetails.status | Yes → yes; legacy-поле contacts не читається: воно розходиться зі contactDetails.status у 363 документах, а код на читанні бере саме contactDetails |
contacts.email | contactDetails.email | у нижньому регістрі; порожній рядок не переноситься |
blockedByRU | blocked або пара в golden_block_lists.blockList | 4 022 пари беруться з блок-листів, поле blocked майже не заповнене |
blockedByTU | blockedByTU | як є |
firstPaidAt | startDate | початок доби Kyiv; у legacy new Date('YYYY-MM-DD') давав UTC-північ |
lastPaidAt | lastDate + time | локальний час Kyiv: golden_global_mans.lastMessageAt побудований саме так |
profit | profit | як є |
gifts | gifts | як є |
isWhite | — | false; white-list рахує Electron |
| — | name (79 033 непорожніх) | у canonical favorite не входить; використовується як резерв для profile.name у ProjectGlobalFavorite |
| — | note, updateNote (65 222 непорожніх) | переносяться окремим кроком у NotesService; до того лишаються в legacy |
| — | language (943 непорожніх) | не переноситься, потрібне рішення |
334 пари не мають lastDate, 92 — startDate: це порожні пари, створені Electron до першої вигрузки статистики; у canonical вони отримують null. Точний firstPaidAt з часом доби можна отримати з golden_statistics під час імпорту статистики.
Міграція: POST /migrations/favorites/apply у project-application-api, idempotent, повторний запуск оновлює документи за _id.
Використання в legacy Golden
| Хто | Що робить | Джерело |
|---|---|---|
Frontend /favorites/getFavoritByLady | favorites TU + overlay golden_temp_statistics, блок-лист, профіль RU | GoldenFavoritesService.getFavoritesByLady |
Frontend /favorites/updateFavoritById, updateFavoriteByIdForGlobal | ручні поля: type, contactDetails, name, note, language, blockedByTU | GoldenFavoritesService.updateFavoritById |
Frontend /favorites/getFavoritesByMan* | favorites RU для сторінки Global Favorites, фільтри contacts і gifts | GoldenFavoritesService.getFavoritesByMan |
Frontend /favorites/getCalendarByLady | календар New / Unique з golden_calendars + overlay temp | GoldenFavoritesService.getCalendarByLady |
Electron /getBaseFavorites, /getFavorites, /getFavoritesEmailInfo | базовий snapshot по TU оператора, overlay temp і email-статуси | ElectronApiService |
Electron /createFavoritesForPairs | upsert порожніх пар, які з’явились у temp | GoldenFavoritesService.createFavoritesForPairs |
| Вигрузка статистики | updateFavoritesV2: агрегат golden_statistics ∪ golden_other_bonuses → profit, gifts, startDate, lastDate, time | StatisticsDownloadingService |
initFavorites | докачує історію бонусів нових TU і створює пари | ручний роут, раніше — після TU sync |
| Розширення mail і extension | читають favorites TU для NAF/FANM-логік | MailExtensionService, GoldenExtensionRepository |
| Global Favorites | перерахунок golden_global_mans з усіх favorites | GlobalManService.generateGlobalFavorites |
Electron
ProjectFavorite є базовим server snapshot для Electron FavoriteService. Electron накладає на нього поточний statistics/message overlay.
isActiveне зберігається — рахується зlastPaidAtза однією platform-настройкою; default7днів.isFreeloaderдодається з глобальної RU-логіки.chatLimit,mailLimitі follow-up state залишаються runtime-станом Electron та в collection не зберігаються.
Профіль, avatar і online RU для глобальних фаворитів зберігаються у ProjectGlobalFavorite. Нотатки зберігатимуться окремо від favorite.
Індекси
- unique
{ project: 1, tuId: 1, ruId: 1 }—uniq_project_favorite_pair; { tuId: 1, lastPaidAt: -1 }—project_favorite_tu_list, список favorites TU;{ project: 1, ruId: 1 }—project_favorite_ru_links, зв’язки RU з TU для Global Favorites.
Заповнити
- Вирішити, чи потрібен legacy
languagefavorite, і де він живе, якщо потрібен. - Затвердити джерело paid activity для календаря
New / Uniqueбез зберігання необмеженого масиву подій уProjectFavorite. - Перенести
noteу NotesService до вимкнення legacy favorites.