Aggregator Service — Kafka события
Consumer (подписки)
order.status.changed ✅ реализован
Источник: Order Service. Триггер: заказ сменил статус (кассир нажал Принять/Готов/Передан).
Действие: если channel != INTERNAL — найти binding по store_id + external_provider, добавить задачу в status_push_queue (StatusChangedConsumer).
Планируемые consumer’ы (🕓 M3)
Эти подписки описаны как проектные, но в коде отсутствуют (нет соответствующих @KafkaListener). Пока инвалидация menu_snapshots и stoplist происходит по TTL снапшота (1 час), не по событиям:
catalog.product.updated — 🕓
Источник: Catalog Service. Триггер: изменение товара в нашем каталоге.
Действие: инвалидация menu_snapshots для всех bindings где этот товар присутствует.
catalog.availability.changed — 🕓
Источник: Catalog Service. Триггер: изменился флаг available_in_aggregators или available_in_stores.
Действие: пересчёт снапшота + stoplist.
warehouse.product.stock.depleted — 🕓
Источник: Warehouse Service. Триггер: остаток товара упал ниже порога. Действие: добавить в стоп-лист (отразится при следующем pull агрегатором).
warehouse.product.stock.replenished — 🕓
Источник: Warehouse Service. Триггер: остаток восстановлен. Действие: убрать из стоп-листа.
stoplist.manual.changed — 🕓
Источник: Catalog Service. Триггер: менеджер вручную поставил/снял стоп.
Действие: то же что warehouse.stock.depleted/replenished — пересчёт.
Producer (публикуем)
aggregator.order.received
Когда: пришёл новый заказ от агрегатора, валидация прошла. Payload:
{
"event_id": "uuid",
"timestamp": "iso",
"version": 1,
"payload": {
"provider": "yandex-eda",
"external_order_id": "ye-123",
"binding_id": "uuid",
"store_id": "uuid",
"franchise_id": "uuid",
"items": [
{ "product_id": "uuid", "quantity": 1, "unit_price": 750, "modifiers": [...] }
],
"total": 1250.00,
"payment": { "type": "EXTERNAL_PREPAID", "status": "paid" },
"customer": { "first_name": "Иван", "phone_mask": "+7***1234" },
"delivery": { "type": "courier", "estimated_pickup_at": "2026-04-17T13:10:00Z" },
"comment": "..."
}
}Подписчик: Order Service — создаёт Order с соответствующим channel/payment_type.
aggregator.order.rejected
Когда: заказ не прошёл валидацию (item_not_found, out_of_stock, duplicate).
Payload: { provider, external_order_id, reason, details }.
Подписчики: Admin BFF (для алертов).
aggregator.order.cancelled_by_customer
Когда: Яндекс уведомил об отмене гостем.
Payload: { provider, external_order_id, internal_order_id, cancelled_at }.
Подписчик: Order Service — переводит заказ в CANCELLED, флаг cancelled_by_customer=true.
aggregator.status.pushed
Когда: успешно отправили смену статуса в агрегатор.
Payload: { binding_id, order_id, target_status, provider_response }.
Подписчик: для аудита, логов.
aggregator.status.push_failed
Когда: исчерпали retry. Подписчик: Admin BFF для алертов «push failed, требуется ручное вмешательство».
aggregator.binding.connected / aggregator.binding.disconnected
Когда: ТТ подключили/отключили от агрегатора. Подписчик: Admin BFF (для UI-нотификаций).
tips.received (BR 3.2)
Когда: получен webhook от Нетмонета о новых чаевых, валидирован, сохранён в tip_events.
Payload:
{
"event_id": "uuid",
"timestamp": "datetime",
"version": 1,
"payload": {
"tip_event_id": "uuid",
"binding_id": "uuid",
"provider": "netmonet",
"external_tip_id": "string",
"store_id": "uuid",
"franchise_id": "uuid",
"order_number": "string | null",
"table_number": "integer | null",
"waiter_id": "uuid | null",
"amount": "decimal",
"currency": "RUB",
"received_at": "datetime"
}
}Подписчики:
- Order Service —
TipsEventConsumer: еслиorder_number!= null → найти Order, обновитьtip_amount,waiter_id - User Service (в будущем) — агрегат в статистику по сотруднику для дашборда «Мои чаевые»
Топики
| Топик | Тип | Retention |
|---|---|---|
aggregator.orders | все события по заказам агрегаторов | 7 дней |
aggregator.bindings | connect/disconnect | 7 дней |
aggregator.status.sync | push результаты | 3 дня |