Метод GET /api/v1/consume/history

Документация API: Извлечение исторического среза потребления (Журнала питания) для агрегации метрик КБЖУ

Published

July 1, 2026

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

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

Метод решает следующие задачи:

  1. Хронологический аудит питания: Формирует структурированный лог съеденных товаров (как одиночных перекусов, так и комплексных обедов) с привязкой к уникальным сессиям потребления (session_id).
  2. Подготовка дата-сета для MLOps: Выгружает точные float-значения списанных порций (например, 0.125 от пирога), что позволяет смежному аналитическому контуру производить сквозной расчет калорийности, макро- и микронутриентов (КБЖУ).
  3. Пагинация и оптимизация: Ограничивает глубину выборки с помощью Query-параметров (limit/offset) для снижения нагрузки на сеть и быстрой отрисовки бесконечного скролла (Infinite Scroll) во Flutter.

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

  • Метод: GET
  • Маршрут: /api/v1/consume/history
  • Формат данных: application/json (только ответ)

2.1 Спецификация параметров запроса (Query Parameters)

Параметр Тип Обязательный Описание Пример значения
start_date String (ISO) Нет Нижняя временная граница выборки (по умолчанию — текущие сутки) 2026-07-01T00:00:00Z
end_date String (ISO) Нет Верхняя временная граница выборки 2026-07-01T23:59:59Z
limit Integer Нет Количество возвращаемых сессий за один запрос (Максимум = 100) 20
offset Integer Нет Смещение для постраничной навигации 0

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

Заголовок Обязательный Описание Пример значения
Authorization Да Токен авторизации (Access Token). Бэкенд извлекает из него home_group_id. Bearer eyJhbGciOiJIUzI1Ni...
X-Request-ID Да Сквозной ID запроса для профилирования скорости аналитической выборки req-cons-hist-11bb

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

Метод группирует результаты по уникальным сессиям приема пищи (session_id), внутри которых лежит массив конкретных съеденных позиций и их порций.

Поле Тип Описание Пример значения
session_id String Уникальный ID приема пищи (генерируется на Шаге 11 Метода 1) sess-meal-99aa-11ef
consumed_quantity Float Какая именно доля или абсолютный вес были съедены 0.1250
menu_template_id Integer Ссылка на шаблон корпоративного меню, если списание автоматическое 102 (или null)

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

{
  "home_group_id": "group-772-alpha",
  "total_sessions_found": 42,
  "limit": 20,
  "offset": 0,
  "history": [
    {
      "session_id": "sess-meal-99aa-11ef",
      "consumed_at": "2026-07-01T23:45:00Z",
      "user_id": "usr-8822-fa41",
      "menu_template_id": null,
      "items": [
        {
          "product_name": "Яблочный пирог классический",
          "consumed_quantity": 0.125,
          "unit": "шт"
        },
        {
          "product_name": "Йогурт Danone Клубника",
          "consumed_quantity": 1.0,
          "unit": "шт"
        }
      ]
    }
  ]
}

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

На диаграмме представлена логика извлечения истории питания. База данных выполняет группировку строк по session_id, формируя древовидную структуру комплексных обедов или перекусов для корректного рендеринга во Flutter.

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/consume/history?limit=20 (JWT в Header)
    activate API
    
    note over API: Шаг 3: Чтение home_group_id из клеймов JWT-токена
    
    API->>DB: SELECT FROM consumption_log WHERE home_group_id = \$1 AND date...
    activate DB
    note over DB: Шаг 5: Фильтрация по временному диапазону (start_date / end_date)
    note over DB: Шаг 6: Группировка позиций по session_id и применение LIMIT/OFFSET
    DB-->>API: Возвращает структурированный массив сессий с вложенными товарами
    deactivate DB
    
    API-->>APP: HTTP 200 OK (JSON с древовидной историей и float-порциями)
    deactivate API
    note over APP: Экран перерисовывается: выводится таймлайн приемов пищи (Обед, Перекус)

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

  • Шаг 1 (User -> APP): Пользователь переходит на вкладку «Журнал питания» (или профиль здоровья) в мобильном приложении, чтобы посмотреть историю своего потребления за день, неделю или месяц.
  • Шаг 2 (APP -> API): Flutter-приложение отправляет запрос HTTP GET /api/v1/consume/history. В Query-параметрах передается limit=20 (для первой страницы бесконечной прокрутки) и опциональные фильтры дат. В заголовке Authorization передается Access Token.
  • Шаг 3 (API -> API): Бэкенд-шлюз FastAPI принимает запрос, генерирует сквозной X-Request-ID и декодирует токен. Из него извлекается параметр home_group_id, который гарантирует, что пользователь увидит историю потребления только в рамках своей семьи или своего предприятия.
  • Шаг 4 (API -> DB): Шлюз отправляет в СУБД PostgreSQL оптимизированный SQL-запрос. Метод не может читать таблицу инвентаря напрямую, так как съеденные продукты там уменьшают вес или удаляются. Запрос идет в специальную историческую таблицу consumption_log.
  • Шаг 5 (DB -> DB): СУБД выполняет первичную фильтрацию по индексам: отсекает записи, принадлежащие чужим группам, и ограничивает выборку временными рамками start_date и end_date (по умолчанию берутся текущие сутки).
  • Шаг 6 (DB -> DB): СУБД группирует отдельные строки съеденных продуктов (например, яблочный пирог и йогурт) по уникальному session_id, чтобы Flutter-приложение могло отрисовать их как один комплексный прием пищи, а не как разрозненный список. К выборке применяются параметры пагинации LIMIT и OFFSET.
  • Шаг 7 (DB -> API): База данных отдает бэкенду структурированный массив строк. Благодаря правильным составным индексам, время выполнения аналитической выборки не превышает 25 мс.
  • Шаг 8 (API -> APP): FastAPI валидирует данные через Pydantic-модель и возвращает клиенту статус HTTP 200 OK. Мобильное приложение Flutter принимает JSON. Экран мгновенно обновляется: данные выводятся в виде удобного хронологического таймлайна, где для каждого продукта указана точная съеденная float-порция (например, 0.1250 пирога).

5 Спецификация серверной логики (SQL) и вилок исключений

5.1 1. Архитектурный SQL-запрос журнала питания (Шаги 5–6)

Для того чтобы база данных отдавала не разрозненные строки, а сразу сформированные приемы пищи («1-е, 2-е и компот»), используется агрегационная функция json_agg(). Это позволяет сгруппировать массив съеденных продуктов внутри каждого уникального session_id на уровне СУБД.

SELECT 
    cl.session_id,
    cl.consumed_at,
    cl.user_id,
    cl.menu_template_id,
    -- Агрегируем вложенные продукты в JSON-массив для Flutter
    JSON_AGG(
        JSON_BUILD_OBJECT(
            'product_name', cl.product_name,
            'consumed_quantity', cl.consumed_quantity::FLOAT,
            'unit', cl.unit
        )
    ) AS items
FROM consumption_log cl
WHERE cl.home_group_id = \$1
  AND cl.consumed_at BETWEEN COALESCE(\$2, NOW() - INTERVAL '24 hours') AND COALESCE(\$3, NOW())
GROUP BY cl.session_id, cl.consumed_at, cl.user_id, cl.menu_template_id
ORDER BY cl.consumed_at DESC
LIMIT \$4 
OFFSET \$5;

5.2 2. Спецификация ответов сервера (Response Body)

5.2.1 HTTP 200 OK (Ответ на Шаге 8)

Возвращается при успешной выборке логов. Каждая сессия содержит точные float-коэффициенты порций, которые были рассчитаны на Шаге 7 Метода 1 (например, 0.1250 от яблочного пирога).

{
  "home_group_id": "group-772-alpha",
  "total_sessions_found": 1,
  "limit": 20,
  "offset": 0,
  "history": [
    {
      "session_id": "sess-meal-99aa-11ef",
      "consumed_at": "2026-07-01T23:45:00Z",
      "user_id": "usr-8822-fa41",
      "menu_template_id": null,
      "items": [
        {
          "product_name": "Яблочный пирог классический",
          "consumed_quantity": 0.125,
          "unit": "шт"
        },
        {
          "product_name": "Йогурт Danone Клубника",
          "consumed_quantity": 1.0,
          "unit": "шт"
        }
      ]
    }
  ]
}

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

5.3.1 Ошибка: Невалидный формат временного диапазона (HTTP 422 Unprocessable Entity)

Выбрасывается на Шаге 3, если параметры start_date или end_date переданы в некорректном строковом формате, который не соответствует стандарту ISO 8601.

{
  "error_code": "ERR-INVALID-DATE-FORMAT",
  "message": "Передан некорректный формат даты в параметрах фильтрации.",
  "details": [
    {
      "loc": ["query", "start_date"],
      "msg": "Input should be a valid datetime or date in ISO 8601 format",
      "type": "datetime_from_date_parsing"
    }
  ]
}

5.3.2 Ошибка: Превышение максимального лимита пагинации (HTTP 400 Bad Request)

Выбрасывается, если клиент передал в Query-параметрах limit со значением больше 100, нарушая архитектурные ограничения безопасности API.

{
  "error_code": "ERR-HISTORY-LIMIT-EXCEEDED",
  "message": "Превышен максимальный лимит размера страницы логов (максимум — 100 сессий).",
  "details": {
    "requested_limit": 500,
    "allowed_max_limit": 100
  }
}