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
Метод DELETE /api/v1/recipes/custom/{id}
Документация API: Физическое удаление авторского рецепта (Delete) с автоматической каскадной очисткой технологической карты
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.
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): Сервис Рецептов публикует асинхронное событие в топик Kafkamasterdata.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."
}
}