feat: implement Iteration 1 — AI recipe recommendations
Backend:
- Add Groq LLM client (llama-3.3-70b) for recipe generation with JSON
retry strategy (retries only on parse errors, not API errors)
- Add Pexels client for parallel photo search per recipe
- Add saved_recipes table (migration 004) with JSONB fields
- Add GET /recommendations endpoint (profile-aware prompt building)
- Add POST/GET/GET{id}/DELETE /saved-recipes CRUD endpoints
- Wire gemini, pexels, recommendation, savedrecipe packages in main.go
Flutter:
- Add Recipe, SavedRecipe models with json_serializable
- Add RecipeService (getRecommendations, getSavedRecipes, save, delete)
- Add RecommendationsNotifier and SavedRecipesNotifier (Riverpod)
- Add RecommendationsScreen with skeleton loading and refresh FAB
- Add RecipeDetailScreen (SliverAppBar, nutrition tooltip, steps with timer)
- Add SavedRecipesScreen with Dismissible swipe-to-delete and empty state
- Update RecipesScreen to TabBar (Recommendations / Saved)
- Add /recipe-detail route outside ShellRoute (no bottom nav)
- Extend ApiClient with getList() and deleteVoid()
Project:
- Add CLAUDE.md with English-only rule for comments and commit messages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,16 +5,12 @@
|
||||
| # | Итерация | Цель | Зависит от |
|
||||
|---|----------|------|------------|
|
||||
| 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 |
|
||||
| 1 | AI-рекомендации рецептов | Gemini генерирует рецепты, Pexels фото, сохранение рецептов | 0 |
|
||||
| 2 | Управление продуктами | CRUD продуктов, сроки хранения, ingredient_mappings | 0 |
|
||||
| 3 | Распознавание продуктов | OCR чека, фото продуктов, фото блюд (Gemini Vision) | 1, 2 |
|
||||
| 4 | Планирование меню | Меню на неделю, AI-генерация, список покупок, дневник | 1, 2 |
|
||||
|
||||
Дальнейшие итерации определяются приоритетами после MVP. Функциональность из TODO.md (дневник статистики, режим готовки, полировка) — следующий горизонт.
|
||||
|
||||
## Карта зависимостей
|
||||
|
||||
@@ -23,57 +19,28 @@
|
||||
│ 0. Фундамент │
|
||||
└──────┬───────┘
|
||||
│
|
||||
┌────────────────┼────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌────────────────┐ ┌───────────┐ ┌────────────────┐
|
||||
│ 1. Справочник │ │ 2. Продук-│ │ 3. AI-ядро │
|
||||
│ ингредиентов │ │ ты │ │ (очереди, │
|
||||
│ + рецепты │ │ │ │ Gemini) │
|
||||
└───────┬────────┘ └─────┬─────┘ └───────┬────────┘
|
||||
│ │ │
|
||||
│ ┌────┴────┐ │
|
||||
│ │ │ │
|
||||
│ │ ┌────┴──────────┘
|
||||
│ │ │
|
||||
│ ▼ ▼
|
||||
│ ┌──────────────────┐
|
||||
│ │ 4. AI-распозна- │
|
||||
│ │ вание │
|
||||
│ └──────────────────┘
|
||||
│ │
|
||||
└─────┬─────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────┐
|
||||
│ 5. Каталог │
|
||||
│ рецептов │
|
||||
└───────┬────────┘
|
||||
│
|
||||
┌──────────┼──────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ 6. Меню │ │ 7. Днев- │ │ 8. Режим │
|
||||
│ + список │ │ ник пита-│ │ готовки │
|
||||
│ покупок │ │ ния │ │ │
|
||||
└─────┬────┘ └─────┬────┘ └──────────┘
|
||||
│ │
|
||||
└──────┬─────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────┐
|
||||
│ 9. Рекоменда- │
|
||||
│ ции + стат-ка │
|
||||
└───────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────┐
|
||||
│ 10. Полировка │
|
||||
└────────────────┘
|
||||
┌────────────┴────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌────────────────────┐ ┌──────────────────┐
|
||||
│ 1. AI-рекомендации │ │ 2. Продукты │
|
||||
│ (Gemini+Pexels) │ │ + ingredient_ │
|
||||
│ saved_recipes │ │ mappings │
|
||||
└──────────┬─────────┘ └────────┬─────────┘
|
||||
│ │
|
||||
└────────────┬────────────┘
|
||||
│
|
||||
┌──────────┴──────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌────────────────────┐ ┌─────────────────────┐
|
||||
│ 3. Распознавание │ │ 4. Планирование │
|
||||
│ продуктов │ │ меню │
|
||||
│ (Gemini Vision) │ │ (Gemini+Pexels) │
|
||||
└────────────────────┘ └─────────────────────┘
|
||||
```
|
||||
|
||||
**Параллельная разработка:** итерации 1, 2, 3 могут выполняться параллельно. Итерации 6, 7, 8 — тоже параллельно после завершения 5.
|
||||
**Параллельная разработка:** итерации 1 и 2 могут выполняться параллельно. Итерации 3 и 4 — тоже параллельно после завершения 1 и 2.
|
||||
|
||||
---
|
||||
|
||||
@@ -115,33 +82,46 @@
|
||||
|
||||
---
|
||||
|
||||
## Итерация 1: Справочник ингредиентов и рецепты
|
||||
## Итерация 1: AI-рекомендации рецептов
|
||||
|
||||
**Цель:** наполнить БД каноническими ингредиентами и рецептами из Spoonacular. Это фундамент для всех фичей, связанных с рецептами, поиском и маппингом продуктов.
|
||||
> **Детальный план:** [Iteration_1.md](./Iteration_1.md)
|
||||
|
||||
**Цель:** реализовать ключевую функцию — персонализированные рецепты, сгенерированные Gemini, с фотографиями из Pexels и возможностью сохранять понравившиеся.
|
||||
|
||||
**Зависимости:** итерация 0.
|
||||
|
||||
### User Stories
|
||||
|
||||
#### Backend
|
||||
|
||||
| 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 000–10 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.1 | Gemini-клиент | Пакет internal/gemini. Интерфейс RecipeGenerator. GenerateRecipes(prompt) → []Recipe. Retry на невалидный JSON |
|
||||
| 1.2 | Pexels-клиент | Пакет internal/pexels. SearchPhoto(query) → image_url. Параллельные запросы |
|
||||
| 1.3 | Таблица saved_recipes | Миграция: id, user_id, title, description, cuisine, difficulty, prep/cook_time_min, servings, image_url, ingredients (JSONB), steps (JSONB), tags (JSONB), nutrition (JSONB), source, saved_at |
|
||||
| 1.4 | GET /recommendations | Формирует промпт из профиля пользователя → Gemini → Pexels → ответ с image_url |
|
||||
| 1.5 | CRUD saved_recipes | POST /saved-recipes, GET /saved-recipes, GET /saved-recipes/{id}, DELETE /saved-recipes/{id} |
|
||||
|
||||
#### Flutter
|
||||
|
||||
| ID | Story | Описание |
|
||||
|----|-------|----------|
|
||||
| 1.6 | RecommendationsScreen | Список карточек, skeleton-загрузка, кнопка 🔄 для перегенерации |
|
||||
| 1.7 | RecipeDetailScreen | Фото, КБЖУ≈, ингредиенты, шаги, кнопка сохранить |
|
||||
| 1.8 | SavedRecipesScreen | Список с удалением, пустое состояние |
|
||||
|
||||
### Результат итерации
|
||||
- БД содержит ~1 000 ингредиентов с русскими алиасами и нутриентами
|
||||
- БД содержит 5 000–10 000 рецептов с переводами, ингредиентами, шагами, нутриентами
|
||||
- Каждый ингредиент рецепта связан с ingredient_mappings через spoonacular_id
|
||||
- Пользователь открывает вкладку «Рецепты» и видит 5 персонализированных рецептов с фото
|
||||
- Может сохранить рецепт, просмотреть детали, удалить из сохранённых
|
||||
- КБЖУ помечены «≈» как приблизительные
|
||||
|
||||
---
|
||||
|
||||
## Итерация 2: Управление продуктами
|
||||
|
||||
**Цель:** пользователь может вести список своих продуктов вручную — добавлять, редактировать, удалять, отслеживать сроки.
|
||||
> **Детальный план:** [Iteration_2.md](./Iteration_2.md)
|
||||
|
||||
**Цель:** пользователь может вести список своих продуктов вручную — добавлять через автодополнение (ingredient_mappings), редактировать, удалять, отслеживать сроки. Рекомендации становятся персонализированными: Gemini учитывает имеющиеся продукты.
|
||||
|
||||
**Зависимости:** итерация 0.
|
||||
|
||||
@@ -177,39 +157,13 @@
|
||||
|
||||
---
|
||||
|
||||
## Итерация 3: AI-ядро
|
||||
## Итерация 3: Распознавание продуктов
|
||||
|
||||
**Цель:** построить инфраструктуру для AI-запросов: очереди, rate limiter, budget guard, адаптер Gemini. После итерации можно отправлять AI-запросы через API с контролем расхода.
|
||||
> **Детальный план:** [Iteration_3.md](./Iteration_3.md)
|
||||
|
||||
**Зависимости:** итерация 0.
|
||||
**Цель:** пользователь фотографирует чек, холодильник или блюдо — Gemini Vision распознаёт продукты и помогает заполнить список запасов.
|
||||
|
||||
### 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-ядро).
|
||||
**Зависимости:** итерации 1, 2.
|
||||
|
||||
### User Stories
|
||||
|
||||
@@ -217,36 +171,34 @@
|
||||
|
||||
| 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-задачу |
|
||||
| 3.1 | OCR чека | POST /ai/recognize-receipt: фото → Gemini Flash (vision) → JSON (name, qty, unit, confidence). Fuzzy match по ingredient_mappings |
|
||||
| 3.2 | Фото продуктов | POST /ai/recognize-products: 1–3 фото → параллельные Gemini-запросы → дедупликация → JSON |
|
||||
| 3.3 | Распознавание блюда | POST /ai/recognize-dish: фото → Gemini → {dish_name, weight_g, КБЖУ≈, confidence} |
|
||||
| 3.4 | Авто-маппинг | Нераспознанный продукт → Gemini классифицирует → сохраняет в ingredient_mappings |
|
||||
| 3.5 | S3 / multipart | Загрузка фото: multipart или presigned URL |
|
||||
|
||||
#### Flutter
|
||||
|
||||
| ID | Story | Описание |
|
||||
|----|-------|----------|
|
||||
| 4.6 | Экран камеры (чек) | Видоискатель, кнопка съёмки, выбор из галереи. Отправка на backend |
|
||||
| 4.7 | Экран камеры (еда) | Переключатель «Готовое блюдо» / «Продукты». Съёмка, отправка |
|
||||
| 4.8 | Экран загрузки AI | Анимация «Распознаём...» с индикатором. Polling по task_id |
|
||||
| 4.9 | Экран корректировки (чек/фото продуктов) | Список распознанных продуктов. Инлайн-редактирование: название, количество, единица, категория, срок хранения. Чекбоксы, удаление, добавление вручную, «Сделать ещё фото». Предупреждения о дубликатах. CTA «Добавить в мои продукты» |
|
||||
| 4.10 | Экран результата (фото блюда) | Фото, название, калории, БЖУ. Подтверждение / корректировка. Слайдер порции. Выбор приёма пищи. CTA «Записать в дневник» |
|
||||
| 4.11 | Обработка ошибок AI | Экран «Не удалось распознать» → «Переснять» / «Ввести вручную» |
|
||||
| 3.6 | ScanScreen | Выбор режима: чек / продукты / блюдо. Камера + галерея |
|
||||
| 3.7 | Экран подтверждения | Список с инлайн-редактированием, удалением, «Добавить ещё фото», CTA «В запасы» |
|
||||
| 3.8 | Экран результата блюда | Фото, КБЖУ≈, кнопки «В дневник» / «Открыть рецепт» |
|
||||
|
||||
### Результат итерации
|
||||
- Пользователь фотографирует чек → получает список продуктов → корректирует → добавляет в запасы
|
||||
- Фотографирует холодильник (несколько фото) → то же
|
||||
- Фотографирует блюдо → видит калории и БЖУ → может записать в дневник
|
||||
- Нераспознанные ингредиенты автоматически добавляются в справочник
|
||||
- Сфотографировал чек → список продуктов → подтвердил → добавил в запасы
|
||||
- Сфотографировал холодильник → то же
|
||||
- Сфотографировал блюдо → КБЖУ≈ → можно добавить в дневник
|
||||
|
||||
---
|
||||
|
||||
## Итерация 5: Каталог рецептов
|
||||
## Итерация 4: Планирование меню
|
||||
|
||||
**Цель:** пользователь может просматривать, искать и фильтровать рецепты. Видит, какие ингредиенты есть в запасах, а каких не хватает. Может добавить рецепт в избранное.
|
||||
> **Детальный план:** [Iteration_4.md](./Iteration_4.md)
|
||||
|
||||
**Зависимости:** итерации 1 (рецепты в БД), 2 (продукты для проверки наличия).
|
||||
**Цель:** пользователь получает полное меню на неделю от Gemini с учётом продуктов, целей и предпочтений. Автоматически формируется список покупок.
|
||||
|
||||
**Зависимости:** итерации 1, 2.
|
||||
|
||||
### User Stories
|
||||
|
||||
@@ -254,246 +206,39 @@
|
||||
|
||||
| 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). Новые рецепты сохраняются в БД |
|
||||
| 4.1 | Таблицы menu_plans, menu_items | Миграции. menu_items → saved_recipes |
|
||||
| 4.2 | Таблица meal_diary | Миграция. Записи приёмов пищи |
|
||||
| 4.3 | POST /ai/generate-menu | Gemini генерирует 21 рецепт, Pexels параллельно, сохранение в БД |
|
||||
| 4.4 | Menu CRUD | GET /menu?week=, PUT /menu/items/{id}, DELETE /menu/items/{id} |
|
||||
| 4.5 | Shopping list | POST /shopping-list/generate (SQL-агрегация без Gemini), GET, PATCH check |
|
||||
|
||||
#### Flutter
|
||||
|
||||
| ID | Story | Описание |
|
||||
|----|-------|----------|
|
||||
| 5.7 | Экран каталога рецептов | Сетка 2 колонки, поиск, chip-фильтры, кнопка «Из моих продуктов», панель фильтров (bottom sheet), бесконечный скролл |
|
||||
| 5.8 | Карточка рецепта | Фото, рейтинг, метаинформация (время/сложность/кухня), калории/БЖУ, регулятор порций, список ингредиентов с ✅/❌/🔄, описание. CTA «Начать готовить», «Добавить в меню» |
|
||||
| 5.9 | Замены ингредиентов | Строка «→ Замена: пармезан (есть)» под ингредиентом с 🔄 |
|
||||
| 5.10 | Кнопка «Добавить в список покупок» | Недостающие ингредиенты → формирование позиций для списка покупок |
|
||||
| 4.6 | MenuScreen | 7-дневный вид, skeleton на генерацию, кнопка «Сгенерировать» |
|
||||
| 4.7 | Замена рецепта | Тап «Изменить» → выбор из saved_recipes или перегенерация |
|
||||
| 4.8 | ShoppingListScreen | Список по категориям, чекбоксы, «Поделиться» |
|
||||
| 4.9 | DiaryScreen | Записи за день, «+ Добавить» |
|
||||
|
||||
### Результат итерации
|
||||
- Пользователь ищет рецепты, фильтрует по кухне/сложности/времени/калориям
|
||||
- Видит, что можно приготовить из имеющихся продуктов
|
||||
- Для каждого рецепта — отметки наличия ингредиентов и предложения замен
|
||||
- Может добавить рецепт в избранное
|
||||
- Пользователь получает меню на неделю одним запросом к Gemini
|
||||
- Все рецепты меню сохраняются в saved_recipes
|
||||
- Из меню автоматически формируется список покупок (то, чего нет в запасах)
|
||||
- Ведётся дневник питания
|
||||
|
||||
---
|
||||
|
||||
## Итерация 6: Планирование меню
|
||||
## Итоги
|
||||
|
||||
**Цель:** пользователь может составлять меню на неделю — вручную или через AI-генерацию. Формируется список покупок.
|
||||
| Итерация | Цель | Ключевые API |
|
||||
|----------|------|-------------|
|
||||
| 0. Фундамент | Auth, профиль, каркас | Firebase |
|
||||
| 1. AI-рекомендации | Рецепты + сохранение | Gemini, Pexels |
|
||||
| 2. Продукты | CRUD запасов, ingredient_mappings | — |
|
||||
| 3. Распознавание | OCR чека, фото продуктов/блюда | Gemini Vision |
|
||||
| 4. Меню | Недельное меню, список покупок | Gemini, Pexels |
|
||||
|
||||
**Зависимости:** итерации 3 (AI-ядро для генерации), 5 (каталог рецептов).
|
||||
**MVP:** итерации 0–2 (авторизация + рекомендации + продукты) — пользователь получает персонализированные рецепты.
|
||||
|
||||
### 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.5x–3x при добавлении из рецепта. Пересчёт калорий/БЖУ |
|
||||
| 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
|
||||
|
||||
Минимально жизнеспособный продукт — итерации **0–6**:
|
||||
|
||||
- Авторизация, продукты, AI-распознавание, рецепты, меню, список покупок
|
||||
- Позволяет пройти основной пользовательский сценарий: купил продукты → сфотографировал чек → получил меню → составил список покупок
|
||||
- **68 stories** из 110 (62%)
|
||||
|
||||
Итерации 7–10 — расширение до полного продукта.
|
||||
**Полный продукт:** итерации 0–4 — полный цикл: сфотографировал чек → получил меню → список покупок → дневник питания.
|
||||
|
||||
Reference in New Issue
Block a user