0063. Аналитика кампаний автобиддера: join с ad_campaigns и кампании без статистики
Копия ADR из репозитория:
backend/docs/adr/0063-autobidder-campaign-analytics-join.md. Канонический текст — в репозитории; портал обновляется командойnpm run sync:adr.
- Статус: accepted
- Дата: 2026-09-28
- Связанные задачи: OP-69 (аналитика 5.4), OP-224 (управление кампаниями, ADR-0054), OP-68 (бюджеты)
Контекст и проблема
GET /autobidder/analytics/campaigns агрегировал только ad_campaign_stats и
отдавал advertId + метрики. UI списка кампаний читает именно этот эндпоинт и
был вынужден показывать заглушку «Кампания {id}», autoEnabled: false и
склеивать стратегии на клиенте. Нужно отдавать данные кампании
(name, status, autoEnabled, strategyId, dailyBudget, syncedAt) и
решить, что делать с кампаниями, у которых нет статистики за период.
Решение
- Объединение множеств «кампании кабинета» ∪ «статистика за период»:
- кампания со статистикой — метрики + поля кампании;
- статистика без записи в
ad_campaigns(ещё не синхронизирована) — метрики, поля кампанииnull; - кампания без статистики за период — нулевые метрики
(
drr/cpo=null), чтобы продавец видел и кампании без показов.
- Исключения из п. «без статистики»: удалённые кампании (
status = -1) не добавляются; при фильтреnmIdне добавляются (нет данных, что артикул в кампании есть). dailyBudget— изad_campaign_budgets.daily_limit_rub(мягко удалённые лимиты не учитываются),null— лимита нет.- Кеш: кешируется только агрегат статистики (как раньше). Данные кампаний
и бюджетов читаются на каждый запрос двумя запросами по индексу кабинета —
иначе
PATCH /autobidder/campaigns/:idбыл бы невидим до истечения TTL. - Сортировка/пагинация: расход по убыванию, затем
advertIdпо возрастанию (числовой). Кампании без расхода — в хвосте. Пагинация in-memory поверх объединённого списка;meta.totalвключает кампании без статистики.
Последствия
- FE может убрать client-side merge (
withStrategyBindings) и заглушки имён. meta.totalвырос на число кампаний без статистики; первая страница по-прежнему — самые «дорогие» кампании.- Мерж выполняется в памяти: число кампаний кабинета — сотни, не тысячи;
при росте — перенести в SQL
FULL OUTER JOINсLIMIT/OFFSET. - Тай-брейк сортировки сменился со строкового на числовой
advertId.