Метод DELETE /api/v1/menu/custom/{id}

Документация API: Физическое удаление шаблона корпоративного меню (Delete) с проверкой ограничений целостности СУБД

Published

July 2, 2026

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) корпоративного меню. СУБД внутри атомарной транзакции проверяет права доступа, валидирует отсутствие активных блокировок со стороны контура потребления и каскадно вычищает все связанные строки.

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

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, останавливая операцию.
  • Шаг 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): Сервис Меню отправляет асинхронное бизнес-событие в топик Kafka masterdata.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
  }
}