feat: implement Iteration 0 foundation (backend + Flutter client)

Backend (Go):
- Project structure with chi router, pgxpool, goose migrations
- JWT auth (access/refresh tokens) with Firebase token verification
- NoopTokenVerifier for local dev without Firebase credentials
- PostgreSQL user repository with atomic profile updates (transactions)
- Mifflin-St Jeor calorie calculation based on profile data
- REST API: POST /auth/login, /auth/refresh, /auth/logout, GET/PUT /profile, GET /health
- Middleware: auth, CORS (localhost wildcard), logging, recovery, request_id
- Unit tests (51 passing) and integration tests (testcontainers)
- Docker Compose setup with postgres healthcheck and graceful shutdown

Flutter client:
- Riverpod state management with GoRouter navigation
- Firebase Auth (email/password + Google sign-in with web popup support)
- Platform-aware API URLs (web/Android/iOS)
- Dio HTTP client with JWT auth interceptor and concurrent refresh handling
- Secure token storage
- Screens: Login, Register, Home (tabs: Menu, Recipes, Products, Profile)
- Unit tests (17 passing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dbastrikin
2026-02-20 13:14:58 +02:00
commit 24219b611e
140 changed files with 13062 additions and 0 deletions

499
docs/plans/Summary.md Normal file
View File

@@ -0,0 +1,499 @@
# FoodAI — План реализации
## Обзор итераций
| # | Итерация | Цель | Зависит от |
|---|----------|------|------------|
| 0 | Фундамент | Go-проект, БД, авторизация, Flutter-каркас | — |
| 1 | Справочник ингредиентов и рецепты | Наполнение БД рецептами, маппинг ингредиентов | 0 |
| 2 | Управление продуктами | CRUD продуктов, сроки хранения | 0 |
| 3 | AI-ядро | Очереди, Gemini-адаптер, rate limiter, budget guard | 0 |
| 4 | AI-распознавание | OCR чека, фото продуктов, фото блюд | 2, 3 |
| 5 | Каталог рецептов | Поиск, фильтры, «из моих продуктов», замены | 1, 2 |
| 6 | Планирование меню | Меню на неделю, AI-генерация, список покупок | 3, 5 |
| 7 | Дневник питания | Записи, порции, трекер воды, калории | 5 |
| 8 | Режим готовки | Пошаговая готовка, таймеры | 5 |
| 9 | Рекомендации и статистика | Рекомендации на главной, графики, тренды | 6, 7 |
| 10 | Полировка | Онбординг, пустые состояния, уведомления, отзывы | 9 |
## Карта зависимостей
```
┌──────────────┐
│ 0. Фундамент │
└──────┬───────┘
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌────────────────┐ ┌───────────┐ ┌────────────────┐
│ 1. Справочник │ │ 2. Продук-│ │ 3. AI-ядро │
│ ингредиентов │ │ ты │ │ (очереди, │
│ + рецепты │ │ │ │ Gemini) │
└───────┬────────┘ └─────┬─────┘ └───────┬────────┘
│ │ │
│ ┌────┴────┐ │
│ │ │ │
│ │ ┌────┴──────────┘
│ │ │
│ ▼ ▼
│ ┌──────────────────┐
│ │ 4. AI-распозна- │
│ │ вание │
│ └──────────────────┘
│ │
└─────┬─────┘
┌────────────────┐
│ 5. Каталог │
│ рецептов │
└───────┬────────┘
┌──────────┼──────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 6. Меню │ │ 7. Днев- │ │ 8. Режим │
│ + список │ │ ник пита-│ │ готовки │
│ покупок │ │ ния │ │ │
└─────┬────┘ └─────┬────┘ └──────────┘
│ │
└──────┬─────┘
┌────────────────┐
│ 9. Рекоменда- │
│ ции + стат-ка │
└───────┬────────┘
┌────────────────┐
│ 10. Полировка │
└────────────────┘
```
**Параллельная разработка:** итерации 1, 2, 3 могут выполняться параллельно. Итерации 6, 7, 8 — тоже параллельно после завершения 5.
---
## Итерация 0: Фундамент
> **Детальный план:** [Iteration_0.md](./Iteration_0.md)
**Цель:** развернуть скелет проекта, базу данных, авторизацию и каркас мобильного приложения. После итерации можно зарегистрироваться, войти и увидеть пустые экраны.
**Зависимости:** нет.
### User Stories
#### Backend
| ID | Story | Описание |
|----|-------|----------|
| 0.1 | Инициализация Go-проекта | Структура проекта (cmd/, internal/, pkg/), go.mod, конфигурация (envconfig), логгер (slog), graceful shutdown |
| 0.2 | PostgreSQL + миграции | Подключение к PostgreSQL (pgx), система миграций (goose или golang-migrate). Начальная миграция: таблица `users` |
| 0.3 | HTTP-сервер + роутер | HTTP-сервер (net/http или chi), middleware (CORS, request ID, logging, recovery), healthcheck endpoint |
| 0.4 | Firebase Auth интеграция | Firebase Admin SDK. Middleware для верификации Firebase idToken. Выдача собственного JWT. Эндпоинты: `POST /auth/login`, `POST /auth/refresh`, `POST /auth/logout` |
| 0.5 | Таблица users | Миграция: users (id, firebase_uid, email, name, avatar_url, параметры тела, цель, preferences JSONB, plan, created_at). CRUD-сервис |
| 0.6 | Docker Compose | docker-compose.yml: PostgreSQL, приложение. Makefile с основными командами (migrate, run, test) |
#### Flutter
| ID | Story | Описание |
|----|-------|----------|
| 0.7 | Инициализация Flutter-проекта | Создание проекта, структура (features/, core/, shared/), подключение основных пакетов (dio, riverpod/bloc, go_router) |
| 0.8 | Firebase Auth во Flutter | Пакеты firebase_auth, google_sign_in, sign_in_with_apple. Экраны: вход (email + Google + Apple), регистрация. Хранение JWT в secure storage |
| 0.9 | Навигация и каркас экранов | Bottom Tab Bar (5 вкладок), пустые заглушки для каждого экрана, роутинг |
| 0.10 | API-клиент | Dio-клиент с interceptors: JWT-токен, refresh, error handling. Базовые модели (User) |
### Результат итерации
- Можно зарегистрироваться через email / Google / Apple
- Войти в приложение и увидеть 5 вкладок с заглушками
- Backend отвечает на healthcheck и auth-запросы
- БД содержит таблицу users с данными зарегистрированных пользователей
---
## Итерация 1: Справочник ингредиентов и рецепты
**Цель:** наполнить БД каноническими ингредиентами и рецептами из Spoonacular. Это фундамент для всех фичей, связанных с рецептами, поиском и маппингом продуктов.
**Зависимости:** итерация 0.
### User Stories
| ID | Story | Описание |
|----|-------|----------|
| 1.1 | Таблица ingredient_mappings | Миграция: id, canonical_name, spoonacular_id, aliases (JSONB), category, default_unit, нутриенты на 100г, storage_days. Индексы: GIN по aliases, UNIQUE по spoonacular_id |
| 1.2 | Импорт ингредиентов из Spoonacular | CLI-команда / джоб: запрос Spoonacular Ingredient API → сохранение ~1000 базовых ингредиентов в ingredient_mappings |
| 1.3 | Таблица recipes | Миграция: id, source, spoonacular_id, title, description, cuisine, difficulty, prep_time_min, калории, БЖУ, servings, image_url, ingredients (JSONB), steps (JSONB), tags (JSONB), avg_rating, review_count, created_by. Индексы: GIN по ingredients, full-text по title |
| 1.4 | Импорт рецептов из Spoonacular | CLI-команда / джоб: импорт 5 00010 000 популярных рецептов. Маппинг ингредиентов рецепта на ingredient_mappings через spoonacular_id |
| 1.5 | Перевод рецептов | Batch-джоб: перевод title, description, steps через Gemini Flash-Lite. Результат сохраняется в БД (поля title_ru, description_ru или отдельная таблица переводов) |
| 1.6 | Базовая локализация aliases | Перевод aliases топ-200 ингредиентов на русский. Batch через Gemini или ручной маппинг |
### Результат итерации
- БД содержит ~1 000 ингредиентов с русскими алиасами и нутриентами
- БД содержит 5 00010 000 рецептов с переводами, ингредиентами, шагами, нутриентами
- Каждый ингредиент рецепта связан с ingredient_mappings через spoonacular_id
---
## Итерация 2: Управление продуктами
**Цель:** пользователь может вести список своих продуктов вручную — добавлять, редактировать, удалять, отслеживать сроки.
**Зависимости:** итерация 0.
### User Stories
#### Backend
| ID | Story | Описание |
|----|-------|----------|
| 2.1 | Таблица products | Миграция: id, user_id, mapping_id (FK nullable), name, quantity, unit (ENUM), category, storage_days, added_at, expires_at (computed). Индексы по user_id, expires_at |
| 2.2 | Products CRUD API | `GET /products` (фильтры: category, expiring), `POST /products`, `PUT /products/{id}`, `DELETE /products/{id}`, `DELETE /products` (очистить все) |
| 2.3 | Частичное использование | `PATCH /products/{id}/consume` — уменьшить количество. Если количество = 0, предложить удаление |
| 2.4 | Массовое добавление | `POST /products/batch` — добавление нескольких продуктов за раз (после распознавания). Обработка дубликатов: проверка по mapping_id, предложение объединить |
| 2.5 | Дефолтные сроки хранения | `GET /products/storage-defaults`, `PUT /products/storage-defaults`. Хранение в user preferences (JSONB в таблице users) |
| 2.6 | Fuzzy matching при добавлении | При добавлении продукта — поиск по ingredient_mappings.aliases. Если найдено — автозаполнение: mapping_id, category, unit, storage_days, нутриенты |
#### Flutter
| ID | Story | Описание |
|----|-------|----------|
| 2.7 | Экран «Мои продукты» | Список продуктов по категориям, поиск, chip-фильтры, badge «осталось X дней» |
| 2.8 | Добавление/редактирование продукта | Форма: название, количество, единица, категория, период хранения. Выпадающее меню добавления (+) |
| 2.9 | Частичное использование | Модалка «Сколько осталось?» при свайпе или тапе |
| 2.10 | Очистить и перезаполнить | Контекстное меню (···) → подтверждение → очистка |
| 2.11 | Настройки сроков хранения | Экран из Профиля: список категорий с редактируемыми днями |
| 2.12 | Пустое состояние | Иллюстрация + CTA «Сфотографируйте продукты или сканируйте чек» |
### Результат итерации
- Пользователь может вручную добавить продукты, указать количество и сроки
- Видит «осталось X дней» для каждого продукта
- Может частично использовать продукт, удалить, очистить всё
- При добавлении — автоматический подбор категории, единицы, срока через fuzzy match
---
## Итерация 3: AI-ядро
**Цель:** построить инфраструктуру для AI-запросов: очереди, rate limiter, budget guard, адаптер Gemini. После итерации можно отправлять AI-запросы через API с контролем расхода.
**Зависимости:** итерация 0.
### User Stories
| ID | Story | Описание |
|----|-------|----------|
| 3.1 | AI Service интерфейсы | Go-интерфейсы: FoodRecognizer, RecipeGenerator, MenuPlanner, NutritionEstimator. Структуры запросов и ответов |
| 3.2 | Gemini-адаптер | Реализация интерфейсов через Gemini API (google/generative-ai-go). Structured JSON output. Обработка ошибок, retries |
| 3.3 | Таблица ai_tasks | Миграция: id, user_id, task_type, status, priority, input/output_tokens, estimated_cost, queue/process_time_ms, created_at, completed_at. Индексы по user_id, status, created_at |
| 3.4 | Priority Queue Manager | Две очереди (chan в Go): paid (N воркеров), free (1 воркер). Распределение RPM между очередями. Горутины-воркеры |
| 3.5 | Rate Limiter (per-user) | Token bucket на горутинах. Конфигурируемые лимиты по тарифу (free: 20 req/час, paid: 100 req/час). HTTP 429 при превышении |
| 3.6 | Budget Guard | Подсчёт дневных затрат по ai_tasks. Пороги: 80% → warn, 100% → free stop, 120% → all stop. Счётчик сбрасывается в полночь |
| 3.7 | AI API эндпоинты (заглушки) | `POST /ai/recognize-receipt`, `/ai/recognize-products`, `/ai/recognize-dish`, `/ai/suggest-recipes`, `/ai/generate-menu`, `/ai/substitute`. Возвращают task_id (HTTP 202). `GET /ai/tasks/{id}` для polling |
| 3.8 | Логирование и мониторинг | Каждый AI-запрос логируется в ai_tasks с токенами и стоимостью. Эндпоинт `/admin/ai-stats` для просмотра затрат |
### Результат итерации
- AI-запросы проходят через очередь с приоритетами
- Paid-пользователи обслуживаются быстрее
- Расход бюджета контролируется, при превышении — graceful degradation
- Все запросы логируются с точной стоимостью
- API эндпоинты принимают запросы и возвращают результат через polling
---
## Итерация 4: AI-распознавание
**Цель:** пользователь может фотографировать чеки, продукты и блюда — AI распознаёт и предлагает результат для корректировки.
**Зависимости:** итерации 2 (продукты), 3 (AI-ядро).
### User Stories
#### Backend
| ID | Story | Описание |
|----|-------|----------|
| 4.1 | OCR чека | Реализация FoodRecognizer.RecognizeReceipt: фото → Gemini Flash (vision) → structured JSON (name, quantity, unit, category, price, confidence). Маппинг результатов на ingredient_mappings |
| 4.2 | Распознавание продуктов (фото) | FoodRecognizer.RecognizeProducts: фото → Gemini Flash → JSON. Поддержка мультифото (объединение результатов, дедупликация). Маппинг на ingredient_mappings |
| 4.3 | Распознавание блюда | FoodRecognizer.RecognizeDish: фото → Gemini Flash → dish_name, weight, calories, БЖУ, confidence. Full-text search по recipes.title для привязки к рецепту из БД |
| 4.4 | Авто-маппинг нераспознанных | Если fuzzy match по aliases не нашёл ингредиент → разовый запрос к Gemini: определить canonical_name → сохранить в ingredient_mappings. Следующий запрос с таким же продуктом — без AI |
| 4.5 | Загрузка фото | Эндпоинт для multipart upload фото. Сохранение в S3. Передача URL в AI-задачу |
#### Flutter
| ID | Story | Описание |
|----|-------|----------|
| 4.6 | Экран камеры (чек) | Видоискатель, кнопка съёмки, выбор из галереи. Отправка на backend |
| 4.7 | Экран камеры (еда) | Переключатель «Готовое блюдо» / «Продукты». Съёмка, отправка |
| 4.8 | Экран загрузки AI | Анимация «Распознаём...» с индикатором. Polling по task_id |
| 4.9 | Экран корректировки (чек/фото продуктов) | Список распознанных продуктов. Инлайн-редактирование: название, количество, единица, категория, срок хранения. Чекбоксы, удаление, добавление вручную, «Сделать ещё фото». Предупреждения о дубликатах. CTA «Добавить в мои продукты» |
| 4.10 | Экран результата (фото блюда) | Фото, название, калории, БЖУ. Подтверждение / корректировка. Слайдер порции. Выбор приёма пищи. CTA «Записать в дневник» |
| 4.11 | Обработка ошибок AI | Экран «Не удалось распознать» → «Переснять» / «Ввести вручную» |
### Результат итерации
- Пользователь фотографирует чек → получает список продуктов → корректирует → добавляет в запасы
- Фотографирует холодильник (несколько фото) → то же
- Фотографирует блюдо → видит калории и БЖУ → может записать в дневник
- Нераспознанные ингредиенты автоматически добавляются в справочник
---
## Итерация 5: Каталог рецептов
**Цель:** пользователь может просматривать, искать и фильтровать рецепты. Видит, какие ингредиенты есть в запасах, а каких не хватает. Может добавить рецепт в избранное.
**Зависимости:** итерации 1 (рецепты в БД), 2 (продукты для проверки наличия).
### User Stories
#### Backend
| ID | Story | Описание |
|----|-------|----------|
| 5.1 | Поиск и фильтрация рецептов | `GET /recipes` — фильтры: cuisine, difficulty, prep_time, calories_max, meal_type, diet_tags. Full-text search по title. Пагинация (cursor-based) |
| 5.2 | «Из моих продуктов» | Фильтр: сопоставление ingredients[].mapping_id с products.mapping_id пользователя. Ранжирование по доле совпадения. На каждом рецепте: «Есть всё ✓» / «-N прод.» |
| 5.3 | Карточка рецепта с наличием | `GET /recipes/{id}` — рецепт + для каждого ингредиента: есть ✅ / нет ❌ / замена 🔄. Итог: «Всё есть» / «Не хватает N» |
| 5.4 | Замены ингредиентов | При ❌ — поиск замены: сначала в таблице ingredient_substitutions, затем (если нет) — запрос к Gemini, результат кешируется |
| 5.5 | Избранное | `POST /recipes/{id}/favorite`, `DELETE /recipes/{id}/favorite`. Таблица favorites (user_id, recipe_id). `GET /recipes?favorite=true` |
| 5.6 | Дозапрос Spoonacular | Если в локальной БД мало результатов по фильтрам — запрос к Spoonacular API (findByIngredients, complexSearch). Новые рецепты сохраняются в БД |
#### Flutter
| ID | Story | Описание |
|----|-------|----------|
| 5.7 | Экран каталога рецептов | Сетка 2 колонки, поиск, chip-фильтры, кнопка «Из моих продуктов», панель фильтров (bottom sheet), бесконечный скролл |
| 5.8 | Карточка рецепта | Фото, рейтинг, метаинформация (время/сложность/кухня), калории/БЖУ, регулятор порций, список ингредиентов с ✅/❌/🔄, описание. CTA «Начать готовить», «Добавить в меню» |
| 5.9 | Замены ингредиентов | Строка «→ Замена: пармезан (есть)» под ингредиентом с 🔄 |
| 5.10 | Кнопка «Добавить в список покупок» | Недостающие ингредиенты → формирование позиций для списка покупок |
### Результат итерации
- Пользователь ищет рецепты, фильтрует по кухне/сложности/времени/калориям
- Видит, что можно приготовить из имеющихся продуктов
- Для каждого рецепта — отметки наличия ингредиентов и предложения замен
- Может добавить рецепт в избранное
---
## Итерация 6: Планирование меню
**Цель:** пользователь может составлять меню на неделю — вручную или через AI-генерацию. Формируется список покупок.
**Зависимости:** итерации 3 (AI-ядро для генерации), 5 (каталог рецептов).
### User Stories
#### Backend
| ID | Story | Описание |
|----|-------|----------|
| 6.1 | Таблицы menu_plans и menu_items | Миграции. menu_plans: id, user_id, week_start, template_name. menu_items: id, menu_plan_id, day_of_week, meal_type, recipe_id, servings |
| 6.2 | Menu CRUD | `GET /menu?week=`, `POST /menu/items`, `PUT /menu/items/{id}`, `DELETE /menu/items/{id}`. Подсчёт калорий за день/неделю |
| 6.3 | AI-генерация меню | `POST /ai/generate-menu`: backend отбирает кандидатов из БД (SQL по фильтрам + наличие ингредиентов) → формирует промпт с recipe_id → Gemini ранжирует → backend сохраняет menu_items |
| 6.4 | Шаблоны меню | `POST /menu/templates` (сохранить), `GET /menu/templates` (список), `POST /menu/from-template/{id}` (применить). История прошлых меню |
| 6.5 | Таблица shopping_lists | Миграция: id, user_id, menu_plan_id, items (JSONB). Автогенерация из меню: ингредиенты рецептов имеющиеся продукты = список |
| 6.6 | Shopping list API | `GET /shopping-list`, `POST /shopping-list/generate`, `PUT /shopping-list/items/{idx}`, `PATCH /shopping-list/items/{idx}/check`, `POST /shopping-list/items` (ручная позиция) |
#### Flutter
| ID | Story | Описание |
|----|-------|----------|
| 6.7 | Экран меню | Понедельный календарь, слоты по приёмам пищи, калорийность за день, drag-and-drop, контекстное меню (···), пустые слоты с подсказками |
| 6.8 | Добавление блюда в слот | Модалка выбора дня + приёма пищи. Переход в каталог рецептов для выбора |
| 6.9 | AI-генерация | Кнопка ⚡ → экран параметров (период, кухня, сложность, из моих продуктов, калории) → генерация → отображение результата с возможностью заменить отдельные блюда |
| 6.10 | Шаблоны и история | Выпадающее меню: сохранить как шаблон, загрузить из шаблона, из истории |
| 6.11 | Экран списка покупок | Список по категориям, чекбоксы, свайп-удаление, ручное добавление, итого, «Поделиться», «Пересчитать из меню» |
| 6.12 | Переходный экран «Составить меню?» | После добавления продуктов (чек/фото) → предложение сгенерировать меню с выбором параметров |
### Результат итерации
- Пользователь составляет меню на неделю — вручную или AI-генерацией
- AI подбирает рецепты из нашей БД с учётом продуктов, калорий, предпочтений
- Формируется список покупок (автоматически из меню запасы)
- Можно сохранять шаблоны и повторять удачные меню
- После сканирования чека — плавный переход к генерации меню
---
## Итерация 7: Дневник питания
**Цель:** пользователь ведёт учёт съеденного — записывает приёмы пищи, отслеживает калории и БЖУ, регулирует порции.
**Зависимости:** итерация 5 (рецепты для добавления из каталога).
### User Stories
#### Backend
| ID | Story | Описание |
|----|-------|----------|
| 7.1 | Таблица meal_diary | Миграция: id, user_id, date, meal_type, recipe_id (nullable), name, portions, calories, protein, fat, carbs, source (menu/photo/manual/recipe), created_at |
| 7.2 | Diary CRUD | `GET /diary?date=`, `POST /diary`, `PUT /diary/{id}`, `DELETE /diary/{id}`. Подсчёт итогов дня (калории, БЖУ) |
| 7.3 | Из меню в дневник | При отметке «съедено» на главном экране → автосоздание записи в дневнике. Списание ингредиентов из продуктов |
| 7.4 | Трекер воды | Таблица water_tracker (user_id, date, glasses). `GET /stats/water?date=`, `PUT /stats/water` |
| 7.5 | База продуктов для быстрого поиска | Endpoint для поиска по ingredient_mappings: `GET /ingredients/search?q=банан` → название + нутриенты на порцию. Для перекусов без рецепта |
#### Flutter
| ID | Story | Описание |
|----|-------|----------|
| 7.6 | Экран дневника питания | Навигация по дням, круговой прогресс калорий, прогресс-бары БЖУ, приёмы пищи, порции, «+ Добавить» |
| 7.7 | Модалка добавления | Варианты: сфотографировать, из меню, из каталога, из избранного, быстрый поиск продукта, вручную |
| 7.8 | Указание порции | Слайдер 0.5x3x при добавлении из рецепта. Пересчёт калорий/БЖУ |
| 7.9 | Быстрый поиск продукта | Поле поиска → результаты из ingredient_mappings → тап → добавить в дневник с указанием количества |
| 7.10 | Трекер воды | Ряд стаканов внизу дневника, тап = +1/-1 |
| 7.11 | Главный экран — карточка калорий | Круговой прогресс, тап → переход в дневник |
| 7.12 | Главный экран — «Сегодня в меню» | Список из menu_items на сегодня с чекбоксами «съедено» |
### Результат итерации
- Пользователь записывает приёмы пищи: из меню, каталога, фото или вручную
- Регулирует порции, видит калории и БЖУ за день
- На главном экране — прогресс калорий и чекбоксы «съедено»
- Трекер воды
---
## Итерация 8: Режим готовки
**Цель:** пользователь готовит блюдо по пошаговой инструкции с таймерами. После завершения — запись в дневник и оценка.
**Зависимости:** итерация 5 (карточка рецепта для запуска).
### User Stories
| ID | Story | Описание |
|----|-------|----------|
| 8.1 | Экран пошаговой готовки | Фото шага, заголовок, описание (крупный шрифт), навигация «Назад»/«Далее», свайп, точечный индикатор прогресса |
| 8.2 | Таймеры | Кнопка «Запустить таймер» на шагах с timer_seconds. Обратный отсчёт. Пауза, стоп. Несколько таймеров параллельно |
| 8.3 | Панель активных таймеров | Фиксирована внизу. Показывает все запущенные таймеры с оставшимся временем |
| 8.4 | Уведомления таймера | Push-уведомление + звук при завершении таймера. Модалка «Готово!» |
| 8.5 | Keep screen on | Экран не гаснет в режиме готовки (wakelock) |
| 8.6 | Закрытие с подтверждением | Кнопка ✕ → «Прервать готовку?» |
| 8.7 | Экран завершения | «Приятного аппетита!» → «Записать в дневник» (с выбором порций), «Оценить рецепт», «Поделиться фото». Автосписание ингредиентов из запасов |
### Результат итерации
- Пользователь готовит по шагам с фото и описанием
- Запускает таймеры (параллельно), получает уведомления
- По завершении — запись в дневник, оценка рецепта, списание продуктов
---
## Итерация 9: Рекомендации и статистика
**Цель:** приложение проактивно рекомендует рецепты. Пользователь видит аналитику своего питания.
**Зависимости:** итерации 6 (меню), 7 (дневник — данные для статистики).
### User Stories
#### Рекомендации
| ID | Story | Описание |
|----|-------|----------|
| 9.1 | Рекомендации на главном экране | Карусель «Рекомендуем приготовить». Алгоритм: (1) рецепты из продуктов с истекающим сроком, (2) полное совпадение ингредиентов, (3) предпочтения кухни. Endpoint: `GET /recipes/recommended` |
| 9.2 | «Готовили недавно» | Секция на главном экране и в каталоге. Endpoint: `GET /recipes/recent` — последние 5 приготовленных (из meal_diary с source=recipe) |
| 9.3 | Секция «Для вас» в каталоге | Персональные рекомендации на основе: оценок, предпочтений кухонь, истории. Endpoint: `GET /recipes/recommended?section=personal` |
| 9.4 | Подсказки в пустых слотах меню | При пустом слоте меню — рекомендация на основе оставшихся калорий + продукты с истекающим сроком |
#### Статистика
| ID | Story | Описание |
|----|-------|----------|
| 9.5 | Endpoint статистики | `GET /stats?period=week|month|3months` — калории/БЖУ по дням, средние, тренды, самые частые блюда. Агрегация по meal_diary |
| 9.6 | Экран статистики | Переключатель периода, столбчатая диаграмма калорий, stacked bar БЖУ, тренды (↑↓→), топ блюд |
| 9.7 | Переход из главного экрана | Тап по прогресс-бару калорий → дневник или статистика |
### Результат итерации
- На главном экране — рекомендации (приоритет на истекающие продукты) и «Готовили недавно»
- В каталоге — секция «Для вас»
- В меню — умные подсказки в пустых слотах
- Графики калорий и БЖУ за неделю/месяц/3 месяца
---
## Итерация 10: Полировка
**Цель:** довести приложение до продуктового качества — онбординг, пустые состояния, уведомления, отзывы, переходные экраны.
**Зависимости:** итерация 9.
### User Stories
#### Онбординг
| ID | Story | Описание |
|----|-------|----------|
| 10.1 | Экраны онбординга | 5 шагов: приветствие (свайп-карточки), параметры тела, цель + расчёт нормы, ограничения + предпочтения кухонь, предложение добавить продукты |
| 10.2 | Сохранение данных онбординга | `PUT /profile` с параметрами из онбординга. Сохранение предпочтений кухонь в preferences |
| 10.3 | Флаг прохождения онбординга | Показывать только при первом входе. Флаг в secure storage |
#### Пустые состояния и ошибки
| ID | Story | Описание |
|----|-------|----------|
| 10.4 | Пустые состояния всех экранов | Иллюстрация + текст + CTA для: продуктов, меню, дневника, статистики, рецептов (избранные) |
| 10.5 | Состояния ошибок | Нет сети (баннер + оффлайн-данные), ошибка AI (переснять / ввести вручную), ошибка сервера (повторить) |
| 10.6 | Toast с отменой | При удалении записи из дневника, продукта — toast «Удалено» + кнопка «Отменить» (5 сек) |
#### Уведомления
| ID | Story | Описание |
|----|-------|----------|
| 10.7 | Push-уведомления (FCM) | Интеграция Firebase Cloud Messaging. Flutter: запрос разрешений, обработка |
| 10.8 | Уведомления о сроках продуктов | Backend: cron-джоб утром → push «Молоко — осталось 1 день. Использовать в рецепте?» |
| 10.9 | Напоминания о приёмах пищи | По расписанию (настраиваемое): «Время обеда! В меню: ...» |
| 10.10 | Вечернее напоминание о воде | «Вы выпили 5 из 8 стаканов воды сегодня» |
#### Отзывы
| ID | Story | Описание |
|----|-------|----------|
| 10.11 | Таблица reviews | Миграция: id, user_id, recipe_id, rating, text, photo_url, created_at. Пересчёт avg_rating, review_count в recipes |
| 10.12 | API отзывов | `GET /recipes/{id}/reviews` (пагинация), `POST /recipes/{id}/reviews` |
| 10.13 | UI отзывов | Секция в карточке рецепта, модалка написания отзыва (звёзды + текст + фото), полный список отзывов |
#### Профиль
| ID | Story | Описание |
|----|-------|----------|
| 10.14 | Экран профиля | Аватар, параметры, цель, ограничения, предпочтения кухонь, ссылки (статистика, избранное, отзывы, сроки хранения, настройки) |
| 10.15 | Настройки приложения | Экран: уведомления (вкл/выкл по типам), тема (светлая/тёмная/системная), норма воды, язык |
### Результат итерации
- Новый пользователь проходит онбординг и сразу получает персонализированный опыт
- Все экраны имеют осмысленные пустые состояния
- Ошибки обрабатываются gracefully
- Push-уведомления о сроках, приёмах пищи, воде
- Можно оставлять отзывы к рецептам
- Полноценный профиль с настройками
---
## Итоги по объёму
| Итерация | Backend stories | Flutter stories | Всего |
|----------|----------------|-----------------|-------|
| 0. Фундамент | 6 | 4 | 10 |
| 1. Ингредиенты + рецепты | 6 | 0 | 6 |
| 2. Продукты | 6 | 6 | 12 |
| 3. AI-ядро | 8 | 0 | 8 |
| 4. AI-распознавание | 5 | 6 | 11 |
| 5. Каталог рецептов | 6 | 4 | 10 |
| 6. Меню + покупки | 6 | 6 | 12 |
| 7. Дневник питания | 5 | 7 | 12 |
| 8. Режим готовки | 0 | 7 | 7 |
| 9. Рекомендации + стат-ка | 4 | 3 | 7 |
| 10. Полировка | 5 | 10 | 15 |
| **Итого** | **57** | **53** | **110** |
## Приоритеты для MVP
Минимально жизнеспособный продукт — итерации **06**:
- Авторизация, продукты, AI-распознавание, рецепты, меню, список покупок
- Позволяет пройти основной пользовательский сценарий: купил продукты → сфотографировал чек → получил меню → составил список покупок
- **68 stories** из 110 (62%)
Итерации 710 — расширение до полного продукта.