Метод GET /api/v1/fridge/stock

Метод ручного текстового ввода ресурсов. Документация микросервисов домена fridge

1 Часть 1: Системный контракт, схема инвентаря и валидация данных

Этот документ описывает внутреннюю логику, структуру чтения и спецификацию контракта для основного синхронного метода выгрузки ресурсов. Метод реализует логику атомарного чтения текущего живого инвентаря конкретной домашней группы или офиса, обеспечивая реактивную синхронизацию UI мобильного приложения Flutter сразу после завершения транзакций зачисления (ручного ввода, апрува чеков или медиа-модерации).

1.1 1. Функциональное назначение метода

Эндпоинт предназначен для получения актуального списка продуктов и их точного количества, находящихся в «цифровом холодильнике» конкретной семьи либо человека. Данные используются клиентским мобильным приложением FoodLifecycleApp (Flutter) для динамического построения интерфейсных карточек склада и плиток подбора ингредиентов (selectorNode).

Метод решает следующие критические задачи домена: 1. Реактивная синхронизация интерфейса: Выступает финальной точкой автоматического Pull-обновления (Шаг 22 в фото-контуре, Шаг 14 в ручном вводе), когда Flutter запрашивает актуальный стейт остатков сразу после успешного коммита транзакций добавления (HTTP 201). 2. Фильтрация нулевых балансов: Автоматически отсекает продукты питания, баланс которых опустился до нуля (\(quantity \le 0\)), предотвращая отображение фантомных карточек еды в интерфейсе. 3. Обогащение UI-метаданными: Возвращает JSON-ноды продуктов с привязанными уникальными ui_key и техническими словарями (metadata) для отрисовки сроков годности и категорий каталога.

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

  • Метод: GET
  • Маршрут: /api/v1/fridge/stock
  • Формат данных: application/json

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

Заголовок Обязательный Описание Пример значения
Content-Type Да Указывает на получение строго типизированного JSON-пакета application/json
Authorization Да Авторизационный токен в формате Bearer <JWT_TOKEN>. Выделяет user_id и home_group_id. Bearer eyJhbGciOiJIUzI1Ni...
X-Request-ID Да Сквозной ID запроса для распределенного логирования req-get-stock-11xx

1.2.2 Спецификация тела ответа (Response Body - 200 OK)

Поле Тип Обязательный Описание Пример значения
home_group_id String Да Уникальный строковый идентификатор домашней группы (семьи/офиса) "group-772-alpha"
nodes Array (Object) Да Массив объектов (нод) доступных на складе продуктов [...]

2 Часть 2: Схема обработки запроса и пошаговая транзакционность чтения

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

На диаграмме представлена внутренняя логика синхронного выгружения остатков склада. Бэкенд-шлюз FastAPI декодирует токен, извлекает параметры группы и выполняет оптимизированное по индексу чтение из реляционной СУБД PostgreSQL, возвращая строго типизированную Pydantic-структуру.

sequenceDiagram
    autonumber
    actor User as Пользователь (Экран Холодильника)
    participant APP as Мобильное приложение (Flutter)
    participant API as Бэкенд-шлюз (FastAPI)
    participant DB as Реляционная СУБД (PostgreSQL)

    User->>APP: Открывает экран склада / Триггер авто-обновления после зачисления
    APP->>API: HTTP GET /api/v1/fridge/stock (С токеном в заголовках)
    activate API
    
    Note over API: Шаг 3: Декодирование JWT (Выделение user_id и home_group_id)
    
    API->>DB: SELECT item_id, product_name, quantity, unit FROM fridge_inventory WHERE home_group_id = \$1 AND quantity > 0
    activate DB
    Note over DB: Оптимизированное чтение по индексу idx_fridge_stock_group
    DB-->>API: Массив строк из PostgreSQL (Record Set)
    deactivate DB
    
    Note over API: Шаг 6: Маппинг Record Set в Pydantic-схему (Генерация ui_key)
    
    API-->>APP: HTTP 200 OK (JSON-пакет с массивом nodes)
    deactivate API
    Note over APP: Лоадер исчезает, Flutter (Bloc/Riverpod) перерисовывает живой список продуктов

2.2 2. Расшифровка шагов пайплайна (Выгрузка остатков)

  • Шаг 1 (User -> APP): Пользователь переходит на главный экран цифрового холодильника, либо мобильное приложение автоматически инициирует Pull-запрос остатков после успешного закрытия карточки модерации или ручного ввода.

  • Шаг 2 (APP -> API): Клиент Dio отправляет запрос HTTP GET /api/v1/fridge/stock. В заголовках передается обязательный Bearer Access Token сессии и сквозной идентификатор трассировки X-Request-ID.

  • Шаг 3 (API -> API): Бэкенд-шлюз FastAPI принимает запрос, декодирует JWT-токен из Depends(security), верифицирует криптографическую подпись и извлекает параметры user_id и home_group_id. Метод универсален и изолирует данные контекста HOME от контекста OFFICE.

  • Шаг 4 (API -> DB): Шлюз инициализирует асинхронное соединение с базой данных PostgreSQL и выполняет параметризованный SQL-запрос на чтение. Для максимальной скорости выборки используется ранее созданный уникальный индекс idx_fridge_stock_group:

    SELECT item_id, product_name, quantity, unit 
    FROM fridge_inventory 
    WHERE home_group_id = \$1 AND quantity > 0 
    ORDER BY product_name ASC;
  • Шаг 5 (DB -> API): База данных мгновенно возвращает отфильтрованный массив строк (Record Set), содержащий только те продукты, чей баланс строго больше нуля.

  • Шаг 6 (API -> API): Маппинг и Pydantic-валидация: Шлюз итерирует полученные строки, упаковывает их в JSON-ноды и динамически генерирует строковый ключ ui_key на основе item_id (например, "prod_45"), необходимый Flutter для эффективного рендеринга элементов в ListView.

  • Шаг 7 (API -> APP): Сервер возвращает мобильному приложению статус HTTP 200 OK со структурированным JSON-пакетом остатков.

  • Шаг 8 (APP -> User): Flutter-клиент гасит лоадер ожидания, стейт-менеджер обновляет локальное состояние, и на экране холодильника мгновенно отрисовывается актуальный список продуктов с точным указанием весов и штук.


3 Часть 3: Спецификация ответов сервера, вилок исключений и таймаутов

3.1 1. Спецификация успешных ответов сервера (Success Response)

3.1.1 HTTP 200 OK (Ответ на Шаге 7)

Возвращается мобильному приложению Flutter после успешного выполнения SQL-запроса чтения и маппинга данных в Pydantic-схему.

  • Заголовки ответа (Response Headers):
    • Content-Type: application/json
  • Тело ответа (Response Body):
{
  "home_group_id": "group-772-alpha",
  "nodes": [
    {
      "id": 45,
      "name": "Молоко",
      "qty": 2.0,
      "unit": "л",
      "ui_key": "prod_45",
      "metadata": {
        "fat_percentage": 3.2,
        "expiration_date": "2026-07-15"
      }
    },
    {
      "id": 89,
      "name": "АПЕЛЬСИНЫ",
      "qty": 1.5,
      "unit": "кг",
      "ui_key": "prod_89",
      "metadata": {
        "variety": "Гренни Смит"
      }
    }
  ]
}

3.2 2. Спецификация ошибок и вилок исключений (Error Responses)

Все ошибки бэкенда возвращаются в едином стандартизированном формате RFC 7807 (Problem Details) или стандартном JSON-формате FastAPI HTTPException. Это позволяет Flutter-клиенту (Dio) однозначно парсить код ошибки и выводить корректный текст в модальных окнах.

3.2.1 Ошибка авторизации (HTTP 401 Unauthorized — Шаг 3)

Выбрасывается на Шаге 3, если Access Token просрочен, подпись невалидна, токен отсутствует в заголовках или не содержит обязательного поля группы.

{
  "error_code": "ERR-UNAUTHORIZED",
  "message": "Срок действия сессии истек или токен невалиден. Не удалось верифицировать права доступа к складу.",
  "details": {
    "action": "Trigger queued token refresh mechanism using refresh_token."
  }
}

3.2.2 Ошибка недоступности СУБД/Пул Соединений (HTTP 503 Service Unavailable — Шаг 4)

Выбрасывается шлюзом FastAPI, если реляционная база данных PostgreSQL находится под критической нагрузкой, пул соединений PgBouncer переполнен или время ожидания коннекта к СУБД истекло.

{
  "error_code": "ERR-DATABASE-OFFLINE",
  "message": "Инвентарная база данных временно недоступна. Пожалуйста, повторите попытку позже.",
  "details": {
    "reason": "PostgreSQL connection pool timeout or database node partition."
  }
}

3.3 3. Политика READ-таймаутов и отказоустойчивости (Deadlines)

Поскольку эндпоинт получения остатков является самым часто вызываемым роутом в мобильном приложении (триггерится при каждом открытии экрана и после каждого зачисления), к нему предъявляются повышенные требования по Latency.

3.3.1 Жесткий Read Deadline

  • Значение: timeout = 0.2s (200 миллисекунд).
  • Обоснование: Поиск строк в таблице fridge_inventory по индексированному полю home_group_id для одной семьи (в среднем от 20 до 100 строк) занимает на СУБД не более 2–5 мс. Ожидание ответа дольше 200 мс свидетельствует о системной деградации инфраструктуры.

3.3.2 Защита пула соединений за счет кэширования

Чтобы пиковые нагрузки чтения остатков множеством пользователей не приводили к ошибкам HTTP 503 (ERR-DATABASE-OFFLINE), рекомендуется кэшировать результат Pydantic-маппинга этого метода в распределенном In-Memory кэше Redis со сроком жизни (TTL) 60 секунд. Любой успешный INSERT/UPSERT (ручной ввод, подтверждение фото/голоса) обязан принудительно инвалидировать (стереть) кэш этой группы в Redis, заставляя следующий GET-запрос прочитать чистые данные напрямую из PostgreSQL.