sequenceDiagram
autonumber
actor User as Пользователь (Экран добавления)
participant APP as Мобильное приложение (Flutter)
participant API as Бэкенд-шлюз (FastAPI)
participant DB as Реляционная СУБД (PostgreSQL)
participant KAFKA as Брокер событий (Kafka/Redpanda)
participant WK as ИИ-Воркер (Python Consumer)
participant AI as Сервис ИИ (OpenAI API gpt-4o)
User->>APP: Вводит текст ("Яйцо"), нажимает "Добавить"
APP->>API: HTTP POST /api/products (JSON с name)
activate API
note over API: Шаг 3: Первичная Pydantic-валидация и нормализация строки
API->>DB: SELECT status FROM food_elements WHERE input_name = 'яйцо'
activate DB
DB-->>API: Элемент отсутствует (null)
deactivate DB
Note over API, DB: Старт атомарной SQL-транзакции
API->>DB: INSERT INTO food_elements (input_name, status) VALUES ('яйцо', 'PENDING')
activate DB
DB-->>API: Успешная фиксация PENDING-записи
deactivate DB
Note over API, DB: Коммит атомарной транзакции (COMMIT)
API->>KAFKA: Отправка сообщения в топик "product-generation"
API-->>APP: HTTP 202 Accepted (Задача принята к исполнению)
deactivate API
note over APP: Пользователь видит продукт со статусом "Синтез карточки элемента..."
%% Асинхронный контур
activate WK
KAFKA->>WK: Извлечение сообщения {"input_name": "яйцо"}
note over WK: Шаг 11: Формирование строгого JSON-промпта с Pydantic-схемой
WK->>AI: Запрос Structured Outputs (gpt-4o)
activate AI
AI-->>WK: Валидный JSON (Паспорт элемента Eg: БЖУ, валентность, фазы)
deactivate AI
WK->>DB: UPDATE food_elements SET chemical_card = JSONB, status = 'READY' WHERE input_name = 'яйцо'
activate DB
DB-->>WK: Успешное сохранение мастер-данных
deactivate DB
deactivate WK
note over APP: При следующем пуллинге/обновлении экрана продукт переходит в статус READY с полной БЖУ-карточкой
Метод POST /api/products
Документация API: Асинхронное зачисление и ИИ-генерация карточки элемента «Периодической системы продуктов» через событийно-ориентированный контур Kafka
1 Функциональное назначение
Метод является главной входной точкой для регистрации новых кулинарных ресурсов в экосистеме. Он обрабатывает текстовые запросы от мобильного приложения на Dart (Flutter) или текстовые строки, полученные в результате распознавания магазинных чеков (OCR).
Метод решает три критические задачи:
- Быстрый неблокирующий прием (Async Acknowledge): Шлюз FastAPI мгновенно фиксирует факт запроса в базе данных со статусом
PENDING, избавляя пользователя от необходимости ожидать завершения работы ИИ-генератора. - Событийное квотирование нагрузки (Kafka Queueing): Задача инкапсулируется в сообщение и отправляется в брокер сообщений Kafka (Redpanda). Это защищает ИИ-воркеры от пиковых нагрузок и упорядочивает транзакции.
- Автоматическое ИИ-обогащение Master Data: Изолированный Python-контур (Consumer) перехватывает задачу, обращается к LLM по технологии Structured Outputs и сохраняет детальный физико-химический паспорт продукта в гибком поле
JSONBбазы данных PostgreSQL.
2 Протокол взаимодействия (HTTP Контракт)
- Метод:
POST - Маршрут:
/api/products - Формат данных:
application/json
2.1 Спецификация заголовков (HTTP Headers)
| Заголовок | Обязательный | Описание | Пример значения |
|---|---|---|---|
Content-Type |
Да | Указывает на передачу строго типизированного JSON-пакета | application/json |
X-Request-ID |
Да | Сквозной ID для трассировки логов между FastAPI и ИИ-воркером через Kafka | req-atomic-egg-77bb |
2.2 Спецификация тела запроса (Request Body)
| Поле | Тип | Обязательный | Описание | Пример значения |
|---|---|---|---|---|
name |
String | Да | Сырая текстовая строка (наименование продукта или CV-тег) | "Яйцо куриное С1" |
2.2.1 Пример сырого JSON-запроса (Payload):
{
"name": "Яйцо куриное С1"
}3 Схема обработки запроса пользователя (Mermaid)
4 Расшифровка шагов работы метода
Шаг 1 (
User -> APP): Пользователь в интерфейсе приложения вбивает наименование нового продукта (например,"Креветки"или"Яйцо куриное"), который еще не заведен в глобальную периодическую систему продуктов, и нажимает кнопку «Добавить».Шаг 2 (
APP -> API): Мобильное приложение Flutter сериализует данные в JSON-пакет и отправляет запросHTTP POST /api/products. В заголовках передается сквозной идентификатор трассировкиX-Request-IDдля логирования асинхронного трека.Шаг 3 (
API -> API): Бэкенд-шлюзFastAPIпринимает входящий пакет, очищает строку ввода от лишних пробелов и приводит символы к нижнему регистру для исключения дублирования ключей в базе данных.Шаг 4 (
API -> DB): Шлюз отправляет быстрый индексный запрос вPostgreSQLдля проверки наличия элемента.- Вилка кэширования (Элемент найден): Если элемент уже существует со статусом
READY, база мгновенно возвращает готовое содержимоеJSONB. Шлюз отдает его во Flutter со статусомHTTP 200 OK, завершая цепочку. - Вилка генерации (Элемент обратно отсутствует): Если элемент не найден, шлюз открывает атомарную транзакцию и выполняет первичный
INSERTсо статусомPENDING.
- Вилка кэширования (Элемент найден): Если элемент уже существует со статусом
Шаг 5 (
DB -> API): База данных успешно фиксирует запись-заглушку и возвращает подтверждение. Транзакция закрывается командойCOMMIT.Шаг 6 (
API -> KAFKA): Шлюз (в роли Kafka Producer) асинхронно упаковывает задачу в JSON{"input_name": "креветки"}и публикует событие в брокер сообщений Kafka (топикproduct-generation).Шаг 7 (
API -> APP): Сервер мгновенно возвращает мобильному приложению статус успешной постановки в очередьHTTP 202 Accepted. Во Flutter-приложении элемент появляется в интерфейсе со специальным анимированным индикатором “Синтез карточки элемента…”.Шаг 8 (
KAFKA -> WK): Изолированный фоновый микросервисAI Worker(Kafka Consumer) вычитывает сообщение из топикаproduct-generationдля обработки. Разделение очередей защищает систему от превышения лимитов (Rate Limits) со стороны ИИ-провайдера.Шаг 9 (
WK -> WK): ИИ-Воркер формирует системный промпт и накладывает на него строгую структуру данныхPydantic(технология Structured Outputs), гарантируя получение валидной физико-химической схемы без риска галлюцинаций.Шаг 10 (
WK -> AI): Воркер инициирует HTTPS-запрос к API-модели (например,gpt-4o). Модель выполняет комплексный экспертный анализ продукта, рассчитывая БЖУ, группу, период, температурные переходы и кулинарную валентность.Шаг 11 (
AI -> WK): Нейросеть возвращает строго структурированный JSON-паспорт элемента, который нативно проходит валидацию через Pydantic-модель внутри воркера.Шаг 12 (
WK -> DB): Воркер открывает транзакцию вPostgreSQLи выполняет команду обновления данных:UPDATE food_elements SET chemical_card = \$1, status = 'READY' WHERE input_name = \$2;База сохраняет JSON-документ в поле
JSONB, индекс триграмм обновляется. При следующем обновлении экрана (пуллинге) Flutter-приложение забирает из базы полностью сформированную карточку элементаREADY.
5 Спецификация ответов сервера (Response Body) и ошибок
5.1 Успешные ответы (Success Responses)
5.1.1 HTTP 202 Accepted (Ответ на Шаге 7)
Возвращается мобильному приложению Flutter мгновенно, подтверждая успешный запуск асинхронной транзакции и публикацию задачи в Kafka.
- Заголовки ответа (Response Headers):
Content-Type: application/json
- Тело ответа (Response Body):
{
"status": "PENDING",
"message": "Product added to processing queue."
}5.1.2 HTTP 200 OK (Прямой кэш-ответ на Шаге 4)
Возвращается, если продукт уже присутствует в глобальном справочнике. Поле chemical_card извлекается из JSONB-хранилища PostgreSQL.
{
"status": "READY",
"card": {
"symbol": "Eg",
"official_name": "Яйцо",
"group_name": "Протеины",
"period": 3,
"calories_per_100g": 157.0,
"proteins": 12.7,
"fats": 11.5,
"carbs": 0.7,
"cooking_valence": 2,
"chemical_props": {
"phase_transitions": {
"62C-65C": "Начало денатурации белка, потеря прозрачности",
"65C-70C": "Загустевание желтка, сохранение пластичности",
"above_80C": "Полное затвердевание. Риск выделения сероводорода"
},
"activators": ["Кислота (H+ для пашот-эффекта)"],
"inhibitors": ["Жир (блокирует аэрацию белков)"]
},
"common_ocr_synonyms": ["яйцо", "яйца куриные", "egg", "яйцо кур с1"]
}
}5.2 Спецификация интеграционного gRPC-контракта Стриминга (Шаг 10)
Описание внутренней внутренней структуры Protocol Buffers (ai_generation.proto) для обмена данными между ИИ-воркером и LLM-шлюзом в структурированном JSON-режиме.
syntax = "proto3";
package masterdata.periodic.v1;
service FoodSynthesisService {
// Получение строго типизированной карты химических свойств продукта
rpc SynthesizeFoodCard (FoodSynthesisRequest) returns (FoodSynthesisResponse);
}
message FoodSynthesisRequest {
string product_name = 1;
}
message FoodSynthesisResponse {
string json_structured_output = 1; // Сериализованная Pydantic-модель MasterFoodCard
}5.3 Вилки исключений и обработка ошибок
5.3.1 Ошибка валидации структуры входных данных (HTTP 422 Unprocessable Entity — Шаг 4)
Выбрасывается бэкенд-шлюзом FastAPI, если входящее имя продукта передано пустым или нарушает строковый тип данных.
{
"error_code": "ERR-VALIDATION-FAILED",
"message": "Переданный JSON-пакет содержит пустое или некорректное имя продукта.",
"details": [
{
"loc": ["body", "name"],
"msg": "Поле наименования элемента не может быть пустой строкой",
"type": "value_error.str.min_length"
}
]
}5.3.2 Ошибка краха ИИ-синтеза воркера (Фоновая фиксация — Шаг 12)
Если на Шаге 10 провайдер ИИ выдал ошибку (Rate Limit / Timeout) или Pydantic-валидатор воркера зафиксировал сбой структуры ответа, транзакция ИИ-коммита отменяется, а статус в БД переводится в аварийный режим.
{
"error_code": "ERR-ATOMIC-SYNTHESIS-FAILED",
"message": "Асинхронный синтез элемента прерван: ошибка генерации карточки ИИ-провайдером.",
"details": {
"input_name": "яйцо куриное с1",
"status": "FAILED",
"action": "Task put back in Dead Letter Queue for reprocessing or manual admin auditing."
}
}