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

Документация API: Физическое удаление авторского рецепта (Delete) с автоматической каскадной очисткой технологической карты

Published

July 2, 2026

1 Функциональное назначение

Метод предназначен для безвозвратного удаления существующего авторского кулинарного рецепта пользователем в рамках своей группы (home_group_id).

Метод решает три ключевые задачи: 1. Каскадное очищение (Cascading Delete): При удалении родительской шапки рецепта из таблицы user_custom_recipes, СУБД на уровне внешних ключей (ON DELETE CASCADE) автоматически вычищает все связанные строки технологической карты из дочерней таблицы user_recipe_ingredients. 2. Контроль контекста безопасности: Гарантирует превентивную валидацию прав владения кулинарной книгой, исключая несанкционированное удаление данных чужой семьи или офиса. 3. Сброс поисковых индексов ИИ: Генерирует асинхронный сигнал в Apache Kafka для полной очистки поисковых индексов и эмбеддингов ИИ-сервиса аналитики КБЖУ для этой группы.

2 Протокол взаимодействия (HTTP Контракт)

  • Метод: DELETE
  • Маршрут: /api/v1/recipes/custom/{id}
  • Формат данных: application/json (только ответ)

2.1 Спецификация параметров пути (Path Parameters)

Параметр Тип Обязательный Описание Пример значения
id Integer Да Уникальный ID удаляемого кастомного рецепта пользователя 412

2.2 Спецификация заголовков (HTTP Headers)

Заголовок Обязательный Описание Пример значения
Authorization Да Токен авторизации (Access Token) для проверки прав владения кулинарной книгой Bearer eyJhbGciOiJIUzI1Ni...
X-Request-ID Да Сквозной ID запроса для трассировки операции удаления req-rec-delete-77aa

2.3 Спецификация структуры ответа (Response Body)

При успешном удалении тело ответа содержит только статус операции и метаданные, подтверждающие очистку.

2.3.1 Пример JSON-ответа (Payload):

{
  "status": "RECIPE_TEMPLATE_DELETED",
  "data": {
    "recipe_id": 412,
    "home_group_id": "group-772-alpha",
    "cascading_rows_removed": "ALL",
    "timestamp": "2026-07-02T01:10:00Z"
  }
}

3 Схема обработки запроса пользователя (Mermaid)

На диаграмме представлена логика физического удаления авторского рецепта. СУБД внутри атомарной транзакции проверяет права доступа группы, а затем за счет реляционного правила ON DELETE CASCADE мгновенно очищает родительскую шапку и дочерние строки ингредиентов, отправляя сигнал инвалидации в Apache Kafka.

sequenceDiagram
    autonumber
    actor User as Пользователь (Экран Книги)
    participant APP as Мобильное приложение (Flutter)
    participant RS as Сервис Рецептов (FastAPI)
    participant DB as База Данных (PostgreSQL)
    participant KAFKA as Брокер событий (Kafka)

    User->>APP: Нажимает иконку "Удалить рецепт" и подтверждает действие
    APP->>RS: HTTP DELETE /api/v1/recipes/custom/412 (С JWT в Header)
    activate RS
    
    note over RS: Шаг 3: Декодирование JWT (Извлечение home_group_id)
    
    Note over RS, DB: Старт атомарной SQL-транзакции
    RS->>DB: SELECT home_group_id FROM user_custom_recipes WHERE id = 412 FOR UPDATE
    activate DB
    DB-->>RS: Возвращает home_group_id владельца рецепта
    deactivate DB
    
    note over RS: Шаг 6: Проверка соответствия home_group_id группы
    
    RS->>DB: DELETE FROM user_custom_recipes WHERE id = 412
    activate DB
    note over DB: Шаг 8: Автоматическая очистка user_recipe_ingredients (ON DELETE CASCADE)
    DB-->>RS: Подтверждение удаления родительской и дочерних строк
    deactivate DB
    
    Note over RS, DB: Коммит транзакции (COMMIT)
    
    RS->>KAFKA: send_to_kafka_async("masterdata.recipes.mutated.v1")
    RS-->>APP: HTTP 200 OK (status: "DELETED")
    deactivate RS
    
    note over APP: Карточка рецепта исчезает из кулинарной книги во Flutter

4 Расшифровка шагов

  • Шаг 1 (User -> APP): Пользователь в своей приватной кулинарной книге нажимает кнопку «Удалить рецепт» на карточке пирога или супа и подтверждает действие в системном диалоговом окне.
  • Шаг 2 (APP -> RS): Мобильное приложение через Dio отправляет запрос HTTP DELETE /api/v1/recipes/custom/{id}. Идентификатор рецепта передается в качестве Path-параметра. В заголовке Authorization прокидывается Access Token.
  • Шаг 3 (RS -> RS): Микросервис Рецептов (FastAPI) перехватывает запрос и декодирует JWT-токен, вытягивая параметр home_group_id для превентивной проверки прав доступа.
  • Шаг 4 (RS -> DB): Сервис открывает атомарную SQL-транзакцию в PostgreSQL и блокирует строку рецепта: SELECT home_group_id FROM user_custom_recipes WHERE id = $1 FOR UPDATE.
  • Шаг 5 (DB -> RS): База данных возвращает home_group_id записи, зафиксированной в СУБД.
  • Шаг 6 (RS -> RS): Проверка контекста безопасности (Security Scope): Сервис сверяет home_group_id из базы данных с home_group_id, зашитым в токене пользователя. Если они не совпадают (попытка удалить чужой рецепт), транзакция немедленно откатывается (ROLLBACK) со статусом HTTP 403 Forbidden.
  • Шаг 7 (RS -> DB): Физическое каскадное удаление: Сервис отправляет DML-команду удаления шапки: DELETE FROM user_custom_recipes WHERE id = $1.
  • Шаг 8 (DB -> DB): Внутри СУБД срабатывает триггер внешнего ключа ON DELETE CASCADE. База данных автоматически и за один проход физически уничтожает все дочерние строки технологической карты ингредиентов данного рецепта в таблице user_recipe_ingredients, исключая появление «сиротских» данных.
  • Шаг 9 (DB -> RS): СУБД возвращает бэкенду подтверждение об успешной очистке всех связанных каскадных строк.
  • Шаг 10 (RS -> RS): Транзакция фиксируется в PostgreSQL командой COMMIT. Изменения вступают в силу.
  • Шаг 11 (RS -> KAFKA): Сервис Рецептов публикует асинхронное событие в топик Kafka masterdata.recipes.mutated.v1 с типом "RECIPE_DELETED". ИИ-сервер аналитики перехватывает этот сигнал и полностью удаляет векторные эмбеддинги рецепта из своего графа поиска.
  • Шаг 12 (RS -> APP): FastAPI возвращает мобильному приложению статус 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": "RECIPE_TEMPLATE_DELETED",
  "data": {
    "recipe_id": 412,
    "home_group_id": "group-772-alpha",
    "cascading_rows_removed": "ALL",
    "timestamp": "2026-07-02T01:10:02Z"
  }
}

5.2 2. Вилки исключений и обработка ошибок

5.2.1 Ошибка: Рецепт не найден или уже удален (HTTP 404 Not Found)

Выбрасывается, если переданный в URL id кастомного рецепта полностью отсутствует в таблице user_custom_recipes.

{
  "error_code": "ERR-RECIPE-DELETE-TARGET-NOT-FOUND",
  "message": "Указанный рецепт не найден в кулинарной книге. Возможно, он был удален ранее.",
  "details": {
    "requested_id": 412
  }
}

5.2.2 Ошибка: Нарушение прав доступа при удалении (HTTP 403 Forbidden)

Выбрасывается бэкенд-шлюзом FastAPI, если home_group_id из JWT-токена пользователя не совпадает с идентификатором владельца рецепта в базе данных. Система блокирует попытку межгруппового удаления данных.

{
  "error_code": "ERR-RECIPE-CROSS-GROUP-DELETE",
  "message": "Операция отклонена: у вас нет прав на удаление рецептов из чужого контура.",
  "details": {
    "reason": "Security token home_group_id mismatch with target user_custom_recipes scope."
  }
}