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: Экран перерисовывается: выводится таймлайн приемов пищи (Обед, Перекус)
Метод GET /api/v1/consume/history
Документация API: Извлечение исторического среза потребления (Журнала питания) для агрегации метрик КБЖУ
- INVENTORY-2XX Backlog — Краткое Описание задачи 1.
- INVENTORY-1XX Refinement (Уточнение) — Краткое Описание задачи 2.
1 Функциональное назначение
Метод предназначен для извлечения хронологического списка всех фактов потребления продуктов питания и готовых блюд членами конкретной семьи или сотрудниками офиса (home_group_id).
Метод решает следующие задачи:
- Хронологический аудит питания: Формирует структурированный лог съеденных товаров (как одиночных перекусов, так и комплексных обедов) с привязкой к уникальным сессиям потребления (
session_id). - Подготовка дата-сета для MLOps: Выгружает точные float-значения списанных порций (например,
0.125от пирога), что позволяет смежному аналитическому контуру производить сквозной расчет калорийности, макро- и микронутриентов (КБЖУ). - Пагинация и оптимизация: Ограничивает глубину выборки с помощью 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.
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
}
}