sequenceDiagram
autonumber
actor Manager as Менеджер (Офисный аккаунт)
participant APP as Мобильное приложение (Flutter)
participant MS as Сервис Меню (FastAPI)
participant DB as База Данных (PostgreSQL)
participant KAFKA as Брокер событий (Kafka)
Manager->>APP: Нажимает "Удалить шаблон меню"
APP->>MS: HTTP DELETE /api/v1/menu/custom/102 (JWT в Header)
activate MS
note over MS: Шаг 3: Валидация JWT (account_type == OFFICE)
Note over MS, DB: Старт атомарной SQL-транзакции
MS->>DB: SELECT home_group_id FROM company_custom_menus WHERE id = 102 FOR UPDATE
activate DB
DB-->>MS: Возвращает home_group_id существующего шаблона
deactivate DB
note over MS: Шаг 6: Проверка соответствия home_group_id компании
MS->>DB: Проверка наличия активных сессий обедов сотрудников с menu_template_id = 102
activate DB
DB-->>MS: Активных сессий не обнаружено (Удаление разрешено)
deactivate DB
MS->>DB: DELETE FROM company_custom_menus WHERE id = 102
note over DB: Шаг 9: СУБД каскадно удаляет строки из company_menu_items (ON DELETE CASCADE)
Note over MS, DB: Коммит транзакции (COMMIT)
MS->>KAFKA: send_to_kafka_async("masterdata.menu.mutated.v1")
MS-->>APP: HTTP 200 OK (status: "DELETED")
deactivate MS
note over APP: Карточка меню исчезает из списка доступных шаблонов во Flutter
Метод DELETE /api/v1/menu/custom/{id}
Документация API: Физическое удаление шаблона корпоративного меню (Delete) с проверкой ограничений целостности СУБД
1 Функциональное назначение
Метод предназначен для безвозвратного удаления существующего кастомного шаблона комплексного меню менеджером предприятия (account_type == OFFICE).
Метод решает три ключевые задачи: 1. Каскадное очищение (Cascading Delete): При удалении родительской шапки меню из таблицы company_custom_menus, СУБД на уровне внешних ключей (ON DELETE CASCADE) автоматически вычищает все связанные строки технологической карты из дочерней таблицы company_menu_items. 2. Контроль целостности (Integrity Check): Метод защищает систему от нарушения бизнес-логики. Если удаляемый шаблон прямо сейчас назначен в качестве активного обеденного сета в контуре Потребления (Consume), база данных заблокирует операцию удаления (RESTRICT), предотвращая сбой текущих сессий сотрудников. 3. Очистка распределенного кэша: Генерирует асинхронный сигнал в Apache Kafka для полной очистки поисковых индексов и эмбеддингов ИИ-сервиса аналитики для этой компании.
2 Протокол взаимодействия (HTTP Контракт)
- Метод:
DELETE - Маршрут:
/api/v1/menu/custom/{id} - Формат данных:
application/json(только ответ)
2.1 Спецификация параметров пути (Path Parameters)
| Параметр | Тип | Обязательный | Описание | Пример значения |
|---|---|---|---|---|
id |
Integer | Да | Уникальный ID удаляемого кастомного меню компании | 102 |
2.2 Спецификация заголовков (HTTP Headers)
| Заголовок | Обязательный | Описание | Пример значения |
|---|---|---|---|
Authorization |
Да | Токен авторизации (Access Token) для проверки прав администратора офиса |
Bearer eyJhbGciOiJIUzI1Ni... |
X-Request-ID |
Да | Сквозной ID запроса для трассировки операции удаления | req-menu-delete-99aa |
2.3 Спецификация структуры ответа (Response Body)
При успешном удалении тело ответа содержит только статус операции и метаданные, подтверждающие очистку.
2.3.1 Пример JSON-ответа (Payload):
{
"status": "MENU_TEMPLATE_DELETED",
"data": {
"menu_template_id": 102,
"home_group_id": "company-office-center",
"cascading_rows_removed": "ALL",
"timestamp": "2026-07-02T00:55:00Z"
}
}3 Схема обработки запроса пользователя (Mermaid)
На диаграмме представлена логика физического удаления (DELETE) корпоративного меню. СУБД внутри атомарной транзакции проверяет права доступа, валидирует отсутствие активных блокировок со стороны контура потребления и каскадно вычищает все связанные строки.
4 Расшифровка шагов
- Шаг 1 (
Manager -> APP): Менеджер предприятия в списке корпоративных шаблонов обедов нажимает на иконку «Удалить» напротив неактуального меню (например, сid = 102) и подтверждает действие в модальном окне Flutter-приложения. - Шаг 2 (
APP -> MS): Приложение отправляет запросHTTP DELETE /api/v1/menu/custom/{id}. Запрос не содержит тела. Идентификатор сессии авторизации и сквознойX-Request-IDпрокидываются в заголовках. - Шаг 3 (
MS -> MS): Микросервис Меню (FastAPI) декодирует JWT-токен и проверяет клеймaccount_type. Если запрос пришел из домашнего контура (HOME), сервис мгновенно прерывает операцию с ошибкойHTTP 403 Forbidden, изолируя корпоративный контур управления от обычных пользователей. - Шаг 4 (
MS -> DB): Сервис Меню открывает атомарную SQL-транзакцию вPostgreSQLи блокирует строку шаблона:SELECT home_group_id FROM company_custom_menus WHERE id = $1 FOR UPDATE. - Шаг 5 (
DB -> MS): База данных возвращаетhome_group_idсуществующего шаблона.- Вилка исключений: Если возвращено 0 строк, сервис генерирует ошибку
HTTP 404 Not Found, останавливая операцию.
- Вилка исключений: Если возвращено 0 строк, сервис генерирует ошибку
- Шаг 6 (
MS -> MS): Проверка контекста безопасности (Multi-Tenancy): Сервис сверяетhome_group_idиз базы данных сhome_group_id, зашитым в токене менеджера. Если идентификаторы не совпадают (попытка удалить меню другого офиса), транзакция откатывается (ROLLBACK) со статусомHTTP 403 Forbidden. - Шаг 7 (
MS -> DB): Контроль целостности данных (Integrity Check): Прежде чем выполнить удаление, бэкенд делает запрос в лог активных сессий потребления, проверяя, не обедают ли сотрудники этим сетом прямо сейчас. - Шаг 8 (
DB -> MS): База данных подтверждает, что активных связей нет, и удаление не приведет к аварийному завершению бизнес-логики списания продуктов на складе.- Вилка исключений (Активная сессия): Если обнаружена живая связь, СУБД возвращает ошибку блокировки (
RESTRICT). Транзакция откатывается, а менеджер получаетHTTP 409 Conflict(запрет удаления используемого меню).
- Вилка исключений (Активная сессия): Если обнаружена живая связь, СУБД возвращает ошибку блокировки (
- Шаг 9 (
MS -> DB): Каскадное очищение (Cascading Delete): Бэкенд выполняет финальную команду удаления шапки:DELETE FROM company_custom_menus WHERE id = $1. На уровне СУБД срабатывает триггер внешнего ключаON DELETE CASCADE, и база данных автоматически вычищает все связанные строки технологической карты из дочерней таблицыcompany_menu_itemsза один такт. - Шаг 10 (
MS -> API): Транзакция фиксируется в PostgreSQL командойCOMMIT. Все строки физически стираются из памяти. - Шаг 11 (
MS -> KAFKA): Сервис Меню отправляет асинхронное бизнес-событие в топик Kafkamasterdata.menu.mutated.v1с типом"MENU_DELETED". ИИ-сервер аналитики КБЖУ сбрасывает (инвалидирует) поисковые индексы для этогоmenu_template_id. - Шаг 12 (
MS -> APP): Сервер возвращает мобильному приложению статусHTTP 200 OK. Во Flutter-приложении карточка удаленного меню плавно исчезает из списка доступных шаблонов.
5 Спецификация ответов сервера (Response Body) и ошибок
5.1 1. Успешный ответ (Success Response)
5.1.1 HTTP 200 OK (Ответ на Шаге 12)
Возвращается мобильному приложению после успешного коммита транзакции каскадного удаления. Родительский шаблон и все связанные строки технологической карты полностью стерты из СУБД.
- Заголовки ответа (Response Headers):
Content-Type: application/json
- Тело ответа (Response Body):
{
"status": "MENU_TEMPLATE_DELETED",
"data": {
"menu_template_id": 102,
"home_group_id": "company-office-center",
"cascading_rows_removed": "ALL",
"timestamp": "2026-07-02T00:55:02Z"
}
}5.2 2. Вилки исключений и обработка ошибок
5.2.1 Ошибка: Блокировка удаления из-за активного использования меню (HTTP 409 Conflict — Шаг 8)
Выбрасывается на Шаге 8, если СУБД или бизнес-логика фиксируют, что сотрудники предприятия прямо сейчас находятся в процессе фиксации обеда (Meal Session) по данному menu_template_id. Физическое удаление заблокировано ограничением RESTRICT.
{
"error_code": "ERR-MENU-IN-USE",
"message": "Удаление отклонено: данный шаблон меню прямо сейчас используется сотрудниками в активной сессии питания.",
"details": {
"reason": "Foreign key integrity restriction (RESTRICT) triggered by active consumption loop.",
"menu_template_id": 102,
"active_sessions_count": 4
}
}5.2.2 Ошибка: Шаблон меню не найден или уже удален (HTTP 404 Not Found — Шаг 5)
Выбрасывается на Шаге 5, если переданный id отсутствует в базе данных.
{
"error_code": "ERR-DELETE-TARGET-NOT-FOUND",
"message": "Указанный шаблон меню не найден. Возможно, он был удален другим администратором.",
"details": {
"requested_id": 102
}
}