BR 4.2 — External Menu × агрегаторы + override модификаторов
Backlog — не работаем
Это черновик-памятка. Запускаем работу только после того как BR 4.1 в проде и стабильна. До тех пор просто копим идеи и вопросы здесь.
Зачем
BR 4.1 даёт конструктор + монитор. Этого мало для агрегаторов — Yandex.Eda и Коала требуют:
- Завышенную цену под комиссию агрегатора (25-35% сверху) — есть в 4.1 через
override_price. ✅ - Иерархичные модификаторы с возможностью переименовать — например, в Yandex показать «Цельное молоко» вместо нашего «Молоко обычное», или скрыть «Овсяное» которое мы не доставляем. Этого нет в 4.1.
- Push в их API при каждом изменении меню. Этого нет в 4.1 (там только pull через JSON и tv_screen рендер).
Плюс есть тех. долг в Aggregator Service: listener catalog.product.updated для инвалидации menu_snapshots не реализован. Заодно закроем.
§1. Что входит
1.1. Override модификаторов
Расширяем external_menu двумя таблицами:
-
external_menu_modifier_groupexternal_menu_item_id(FK)original_modifier_group_id★ (FK на каталог, не nullable)override_name— переименовать «Молоко» → «Тип молока»override_min/override_max— изменить обязательность выбораvisible— можно скрыть всю группу для этого канала
-
external_menu_modifier_optionexternal_menu_modifier_group_id(FK)original_modifier_option_id★ (FK на каталог)override_name— «Овсяное» → «Овсяное (без сахара)»override_price_delta— другая доплата для каналаvisible
Принцип якоря работает на всех уровнях — orphan-обработка для group и option как в 4.1 для items.
UI: при раскрытии товара в редакторе появляется блок «Модификаторы» с возможностью per-group / per-option overrides. Свернуто по дефолту (вид как в 4.1) — раскрывают только если что-то меняют.
1.2. Канал yandex_eda
- Поле
binding.external_menu_id(UUID, NULL) в Aggregator Service - При rebuild() в
MenuSnapshotService:- Если NULL → текущий flow (вычисляемое меню каталога) — не сломан
- Если задан → HTTP к Catalog Service
GET /internal/catalog/external-menus/{id}/resolve?store_id=Y→ возвращает уже резолвенный CatalogMenu с применёнными overrides
YandexEdaConnectorничего не меняет — остаётся iерархичный JSON формат (group → options c min/max)
1.3. Канал koala
Аналогично yandex_eda, но через свой connector. Конкретные поля API Коалы — открытый вопрос §3.
1.4. Listener external_menu.updated
В Aggregator Service:
- Подписаться на Kafka-топик
external_menu.updated(публикуется в Catalog Service в 4.1) - При получении event’а — найти все
bindings WHERE external_menu_id = X, инвалидировать ихmenu_snapshots - Заодно реализовать
catalog.product.updatedlistener (текущий тех. долг — TODO вEvents.md)
1.5. Логи push’ей
При каждом push в агрегатор — запись в aggregator_push_logs (или расширить существующий aggregator_logs):
- timestamp, binding_id, response_code, payload_hash
- Чтобы потом разбираться «почему Yandex не подхватил новую цену»
§2. Что НЕ входит (даже сюда)
Эти штуки откладываем явно, чтобы 4.2 не разрослась:
- ❌ Override фотографий per-канал — в 4.1 единые с каталога, оставляем так. Если Yandex попросит специфичные фотки — отдельная BR
- ❌ Версионирование external_menu (история опубликованных) — отдельная BR если потребуется откат
- ❌ A/B тесты меню в Yandex — нет
- ❌ Сезонность / расписание (завтраки 7-11) — отдельная BR
- ❌ Маркетинговые баннеры / акции — отдельная BR
- ❌ Канал-специфичные поля (allergies, vegan-tag, калорийность для Yandex) — может быть P3 если у Yandex это обязательно
§3. Открытые вопросы — узнать когда возьмём в работу
- Yandex.Eda API requirements — какие поля обязательны (allergies, нутриенты, теги, размеры фотографий)? Сейчас у нас этих полей в каталоге нет. Если обязательны — потребуется расширение
productsили channel_specific JSONB на external_menu_item. - Yandex API rate limits — как часто можно пушить меню? Возможно есть тротлинг и при частых правках в external_menu мы упрёмся.
- Yandex stop-list webhook — у них есть push-механизм для оперативного скрытия товара? Или только наш pull через
GET /stoplist? - API Коалы — где документация? Какой формат меню? Push или pull? Какие поля? Без этого 4.2 не запускается.
product_external_mappings— текущая планируемая таблица в Catalog Service для маппингаproduct_id → external_skuper-provider. Не пересекается ли с external_menu_item? Скорее всего нет (это про синхронизацию заказов обратно), но проверить.- MenuSnapshotService TTL — сейчас жёсткие 1ч. С listener’ом мы инвалидируем сразу — нужно ли всё ещё держать TTL как backup или можно убрать?
- Несколько external_menu для одного binding — может ли binding ссылаться сразу на несколько (например, основное меню + сезонное)? Дефолт: один binding = один external_menu. Если потом надо несколько — отдельная BR.
- Override модификатора + push в Yandex — Yandex принимает иерархично, значит нам не нужно «разворачивать» в плоский список как для PayKeeper. Прямой маппинг group/option работает.
- Конфликт UI — текущая планируемая админка агрегаторов (binding settings) vs наш конструктор external_menu. После 4.1 владелец редактирует меню в
/external-menus. Если в админке агрегатора есть отдельная вкладка с переопределениями — её можно будет убрать или сделать read-only ссылкой на external_menu. Уточнить во время декомпозиции 4.2.
§4. Зависимости
- ⛔ BR 4.1 в проде — сущности
external_menu,external_menu_item, UI конструктора, событиеexternal_menu.updatedуже работают. - ⛔ Yandex sandbox / тестовый stand — нужен доступ к ним для smoke-тестов push’ей. Сейчас работает pull через наш endpoint, для push надо OAuth-токены и разрешение на push API.
- ⛔ API Коалы — документация и тестовый аккаунт. Это внешний блокер Коалы.
§5. Файлы под create/edit (предварительно)
CREATE
-
08-Specs/Интеграции/External Menu × Aggregators.md— бизнес-спека интеграции -
09-Frontend Specs/Админка Франшизы/Внешние меню — Конструктор модификаторов.md— расширение фронт-спеки 4.1 -
07-Tasks/Decomposition/4.2 External Menu Aggregators/Overview.md -
07-Tasks/Decomposition/4.2 External Menu Aggregators/Catalog Service.md -
07-Tasks/Decomposition/4.2 External Menu Aggregators/Aggregator Service.md -
07-Tasks/Decomposition/4.2 External Menu Aggregators/Admin Franchise.md
EDIT
-
03-Services/Catalog Service/Data Model.md— 2 новые таблицы (modifier_group + option) -
03-Services/Catalog Service/API.md— расширить endpoint/external-menus/{id}/resolveформатом с модификаторами -
03-Services/Aggregator Service/Data Model.md— полеbinding.external_menu_id -
03-Services/Aggregator Service/API.md— без изменений (pull-flow тот же) -
03-Services/Aggregator Service/Events.md— listenerexternal_menu.updatedиcatalog.product.updated -
03-Services/Aggregator Service/Overview.md— расширение зоны ответственности -
08-Specs/Интеграции/PayKeeper.md— никак не трогаем (для PK уже есть свой expand-flow в BR 3.4) -
08-Specs/Агрегаторы доставки/Yandex.Eda Adapter.md— добавить блок про external_menu source
CODE
-
erp-catalog-service— миграции, entities (modifier_group, modifier_option), расширениеExternalMenuResolveService, расширение controller -
erp-aggregator-service— миграция (binding.external_menu_id), KafkaListener дляexternal_menu.updated, расширениеMenuSnapshotService.rebuild() -
erp-admin/web— раскрытие товара в редакторе → форма модификатор-overrides
§6. Ссылки
- BR 4.1 — родительская
- BR 3.4 — паттерн expand для PayKeeper (используется в логике резолва меню при разворачивании структурных модификаторов в плоский список — для канала
paykeeperесли когда-нибудь подружим External Menu с PayKeeper тоже) - Aggregator Service
- Агрегаторы доставки
- Aggregator · Events — текущий TODO про
catalog.product.updated