sequenceDiagram
autonumber
actor Manager as Менеджер (Офисный аккаунт)
participant APP as Мобильное приложение (Flutter)
participant MS as Сервис Меню (FastAPI)
participant DB as База Данных (PostgreSQL)
participant KAFKA as Брокер событий (Kafka)
participant CS as Контур Потребления (Consume)
Manager->>APP: Корректирует название меню в интерфейсе
APP->>MS: HTTP PATCH /api/v1/menu/custom/102 (JSON с новым заголовком)
activate MS
note over MS: Шаг 3: Валидация JWT (account_type == OFFICE)
Note over MS, DB: Старт атомарной SQL-транзакции
MS->>DB: SELECT * FROM company_custom_menus WHERE id = 102 FOR UPDATE
activate DB
DB-->>MS: Возвращает текущее состояние записи
deactivate DB
note over MS: Шаг 6: Проверка принадлежности к home_group_id
note over MS: Шаг 7: Pydantic-слияние (Экстраполяция только переданных полей)
MS->>DB: UPDATE company_custom_menus SET menu_title = \$1 WHERE id = 102
Note over MS, DB: Коммит транзакции (COMMIT)
MS->>KAFKA: send_to_kafka_async("masterdata.menu.mutated.v1")
MS-->>APP: HTTP 200 OK (status: "UPDATED")
deactivate MS
note over KAFKA: Шаг 11: Асинхронная инвалидация кэша ИИ-аналитики
%% ДЕМОНСТРАЦИЯ ИНТЕГРАЦИОННОЙ СВЯЗИ
Note over CS, MS: Будущий вызов эталона при обеде сотрудников
CS->>MS: gRPC GetMenuTemplate(menu_template_id=102)
activate MS
MS-->>CS: gRPC MenuTemplateResponse (Массив свежих данных)
deactivate MS
Метод PATCH /api/v1/menu/custom/{id}
Документация API: Точечное обновление шаблона корпоративного меню (PATCH) с инвалидацией кэша через Apache Kafka
1 Функциональное назначение
Метод предназначен для частичного (точечного) изменения структуры кастомного меню, ранее созданного менеджером компании (account_type == OFFICE) под нужды сотрудников конкретного предприятия.
В отличие от тяжелого метода PUT, который требует полной перезаписи объекта, PATCH решает следующие задачи: 1. Экономия сетевого трафика (Low Overheads): Позволяет изменить только отдельные свойства меню (например, обновить название сета или точечно заменить один ингредиент в технологической карте), не гоняя по сети весь массив связанных блюд. 2. Асинхронный патч-нотификатор: Автоматически уведомляет ИИ-контур аналитики и сервис push-нотификаций об изменении шаблона меню через шину данных Apache Kafka, разгружая синхронный HTTP-поток. 3. Сохранение целостности внутренних связей: Подготавливает обновленную структуру, которая при следующем gRPC-вызове со стороны контура Потребления (Consume) мгновенно развернется в актуальную корзину списания.
2 Протокол взаимодействия (HTTP Контракт)
- Метод:
PATCH - Маршрут:
/api/v1/menu/custom/{id} - Формат данных:
application/json
2.1 Спецификация параметров пути (Path Parameters)
| Параметр | Тип | Обязательный | Описание | Пример значения |
|---|---|---|---|---|
id |
Integer | Да | Уникальный ID изменяемого кастомного меню в базе компании | 102 |
2.2 Спецификация заголовков (HTTP Headers)
| Заголовок | Обязательный | Описание | Пример значения |
|---|---|---|---|
Content-Type |
Да | Формат передаваемых данных | application/json |
Authorization |
Да | Токен авторизации (Access Token). Метод строго проверяет, что account_type == OFFICE. |
Bearer eyJhbGciOiJIUzI1Ni... |
X-Request-ID |
Да | Сквозной ID запроса для трассировки изменений | req-menu-patch-44aa |
2.3 Спецификация тела запроса (Request Body)
Все поля являются опциональными. Валидатор Pydantic на FastAPI обновляет только те ключи, которые физически переданы в JSON-пакете.
| Поле | Тип | Обязательный | Описание | Пример значения |
|---|---|---|---|---|
menu_title |
String | Нет | Новое текстовое название комплексного меню | "Пятничный Бургер-День v2" |
items_matrix |
Array | Нет | Модифицированный список канонических продуктов и порций | [..._name: "Котлета", qty: 1.0] |
2.3.1 Пример сырого JSON-запроса (Payload — Сценарий: менеджер меняет только название меню, не трогая блюда):
{
"menu_title": "Пятничный Бургер-День v2"
}3 Схема обработки запроса пользователя (Mermaid)
На диаграмме представлена логика частичного обновления (PATCH) корпоративного меню. Сервис меню изолированно фиксирует изменения в PostgreSQL, после чего асинхронно публикует событие в брокер Apache Kafka. Смежные сервисы (ИИ-аналитика и Уведомления) подхватывают этот сигнал, а основной контур Потребления (Consume) при следующем вызове заберет обновленный эталон через gRPC.
4 Расшифровка шагов
- Шаг 1 (
Manager -> APP): Менеджер предприятия на экране администратора корпоративного меню изменяет одно или несколько полей шаблона (например, переименовывает сет «Пятничный Бургер-День» в «Пятничный Бургер-День v2», оставляя состав блюд неизменным) и нажимает «Сохранить изменения». - Шаг 2 (
APP -> MS): Мобильное приложение Flutter через клиентDioотправляет запросHTTP PATCH /api/v1/menu/custom/{id}. В теле JSON передается исключительно измененный ключ"menu_title". В заголовках прокидывается токен авторизации и сквознойX-Request-ID. - Шаг 3 (
MS -> MS): Микросервис Меню (FastAPI) перехватывает запрос и валидирует JWT. Из токена извлекаетсяaccount_type. Если флаг равенHOME, сервис мгновенно отсекает операцию со статусомHTTP 403 Forbidden, так как контур корпоративного меню недоступен для обычных семейных аккаунтов. - Шаг 4 (
MS -> DB): Сервис открывает атомарную SQL-транзакцию вPostgreSQLи блокирует целевую строку шаблона меню компании:SELECT id, home_group_id, menu_title FROM company_custom_menus WHERE id = $1 FOR UPDATE. - Шаг 5 (
DB -> MS): База данных возвращает текущую физическую структуру записи из таблицы кастомных меню. - Шаг 6 (
MS -> MS): Проверка контекста безопасности (Security Scope): Сервис сверяет полеhome_group_idиз базы данных сhome_group_id, зашитым в токене менеджера. Если идентификаторы не совпадают (попытка изменить меню чужого офиса), транзакция откатывается (ROLLBACK) с ошибкойHTTP 403. - Шаг 7 (
MS -> MS): Pydantic-экстраполяция: FastAPI берет структуру из базы данных и накладывает на неё входящий JSON-патч с помощью встроенного механизма частичного обновленияresponse_model.model_validate(existing_db_model, update=patch_data). Изменяются только переданные поля, остальные сохраняют свои исходные значения. - Шаг 8 (
MS -> DB): Обновленный объект записывается обратно в базу данных:UPDATE company_custom_menus SET menu_title = $1, updated_at = NOW() WHERE id = $2. Транзакция успешно закрывается командойCOMMIT. - Шаг 9 (
MS -> KAFKA): Сервис Меню выступает в роли продюсера (Producer) и отправляет асинхронное системное уведомление в топик Kafkamasterdata.menu.mutated.v1. Сообщение содержит тип события"MENU_UPDATED",menu_template_idиhome_group_id. - Шаг 10 (
MS -> APP): Сервер возвращает мобильному приложению статусHTTP 200 OKс подтверждением частичной модификации. Экран администратора во Flutter разблокируется. - Шаг 11 (
KAFKA -> KAFKA): Асинхронное следствие: шина сообщений Kafka доставляет событие до ИИ-сервиса аналитики и модуля push-нотификаций. ИИ сбрасывает (инвалидирует) старый кэш структуры меню для этой компании, а сервис уведомлений отправляет пуш сотрудникам офиса: «Менеджер обновил меню! Проверьте состав на завтра». - Шаг 12 (
CS -> MS): Интеграционное взаимодействие (gRPC): Когда сотрудник этого предприятия садится обедать и запускает метод пакетного списания (из микросервисаConsume, Метод 1), транзакционный шлюз Потребления обращается к сервису Меню через внутренний gRPC-канал:gRPC GetMenuTemplate(menu_template_id=102). Сервис Меню мгновенно возвращает уже измененный, актуальный эталон технологической карты блюд по протоколу HTTP/2, изолируя основную базу холодильника от структуры хранения шаблонов меню.
5 Спецификация ответов сервера (Response Body) и ошибок
5.1 1. Успешный ответ (Success Response)
5.1.1 HTTP 200 OK (Ответ на Шаге 10)
Возвращается мобильному приложению после успешного коммита транзакции точечного обновления. В теле ответа возвращаются только модифицированные поля и метаданные сессии.
- Заголовки ответа (Response Headers):
Content-Type: application/json
- Тело ответа (Response Body):
{
"status": "MENU_PARTIALLY_UPDATED",
"data": {
"menu_template_id": 102,
"home_group_id": "company-office-center",
"updated_fields": {
"menu_title": "Пятничный Бургер-День v2"
},
"timestamp": "2026-07-02T00:35:02Z"
}
}5.2 2. Спецификация интеграционного gRPC-контракта (Шаг 12)
Описание структуры Protocol Buffers (menu_integration.proto) для внутреннего взаимодействия между транзакционным шлюзом Потребления (Consume) и сервисом Меню.
syntax = "proto3";
package masterdata.menu.v1;
service MenuIntegrationService {
// Вызывается контуром Consume для развертывания шаблона обеда в позиции списания
rpc GetMenuTemplate (MenuTemplateRequest) returns (MenuTemplateResponse);
}
message MenuTemplateRequest {
int32 menu_template_id = 1;
string home_group_id = 2;
}
message MenuItem {
string product_name = 1; // Каноническое имя (например, "Котлета говяжья")
double required_quantity = 2;// Требуемый объем/вес (например, 1.0)
string unit = 3; // Единица измерения ("шт", "кг")
}
message MenuTemplateResponse {
int32 menu_template_id = 1;
string menu_title = 2;
repeated MenuItem items = 3; // Массив позиций для каскадного списания из холодильника
}5.3 3. Вилки исключений и обработка ошибок
5.3.1 Ошибка: Попытка изменения глобального неизменяемого шаблона (HTTP 403 Forbidden)
Выбрасывается на Шаге 6, если менеджер передал в URL идентификатор, принадлежащий таблице системных мастер-данных (master_menu_templates), которые запрещено изменять пользователям.
{
"error_code": "ERR-MASTERDATA-IMMUTABLE",
"message": "Операция отклонена: системные шаблоны меню защищены от записи и доступны только для чтения.",
"details": {
"requested_id": 102,
"table_scope": "master_menu_templates"
}
}5.3.2 Ошибка: Нарушение бизнес-профиля аккаунта (HTTP 403 Forbidden — Шаг 3)
Выбрасывается, если метод вызван под учетной записью с типом HOME. Контур управления меню полностью изолирован от семейных аккаунтов.
{
"error_code": "ERR-OFFICE-CONTEXT-REQUIRED",
"message": "Доступ заблокирован: данный эндпоинт предназначен исключительно для корпоративных учетных записей предприятий.",
"details": {
"current_account_type": "HOME",
"action": "Block administrative UI elements on Flutter side."
}
}