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>
245 lines
17 KiB
Markdown
245 lines
17 KiB
Markdown
# FoodAI — План реализации
|
||
|
||
## Обзор итераций
|
||
|
||
| # | Итерация | Цель | Зависит от |
|
||
|---|----------|------|------------|
|
||
| 0 | Фундамент | Go-проект, БД, авторизация, Flutter-каркас | — |
|
||
| 1 | AI-рекомендации рецептов | Gemini генерирует рецепты, Pexels фото, сохранение рецептов | 0 |
|
||
| 2 | Управление продуктами | CRUD продуктов, сроки хранения, ingredient_mappings | 0 |
|
||
| 3 | Распознавание продуктов | OCR чека, фото продуктов, фото блюд (Gemini Vision) | 1, 2 |
|
||
| 4 | Планирование меню | Меню на неделю, AI-генерация, список покупок, дневник | 1, 2 |
|
||
|
||
Дальнейшие итерации определяются приоритетами после MVP. Функциональность из TODO.md (дневник статистики, режим готовки, полировка) — следующий горизонт.
|
||
|
||
## Карта зависимостей
|
||
|
||
```
|
||
┌──────────────┐
|
||
│ 0. Фундамент │
|
||
└──────┬───────┘
|
||
│
|
||
┌────────────┴────────────┐
|
||
│ │
|
||
▼ ▼
|
||
┌────────────────────┐ ┌──────────────────┐
|
||
│ 1. AI-рекомендации │ │ 2. Продукты │
|
||
│ (Gemini+Pexels) │ │ + ingredient_ │
|
||
│ saved_recipes │ │ mappings │
|
||
└──────────┬─────────┘ └────────┬─────────┘
|
||
│ │
|
||
└────────────┬────────────┘
|
||
│
|
||
┌──────────┴──────────┐
|
||
│ │
|
||
▼ ▼
|
||
┌────────────────────┐ ┌─────────────────────┐
|
||
│ 3. Распознавание │ │ 4. Планирование │
|
||
│ продуктов │ │ меню │
|
||
│ (Gemini Vision) │ │ (Gemini+Pexels) │
|
||
└────────────────────┘ └─────────────────────┘
|
||
```
|
||
|
||
**Параллельная разработка:** итерации 1 и 2 могут выполняться параллельно. Итерации 3 и 4 — тоже параллельно после завершения 1 и 2.
|
||
|
||
---
|
||
|
||
## Итерация 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: AI-рекомендации рецептов
|
||
|
||
> **Детальный план:** [Iteration_1.md](./Iteration_1.md)
|
||
|
||
**Цель:** реализовать ключевую функцию — персонализированные рецепты, сгенерированные Gemini, с фотографиями из Pexels и возможностью сохранять понравившиеся.
|
||
|
||
**Зависимости:** итерация 0.
|
||
|
||
### User Stories
|
||
|
||
#### Backend
|
||
|
||
| ID | Story | Описание |
|
||
|----|-------|----------|
|
||
| 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 | Список с удалением, пустое состояние |
|
||
|
||
### Результат итерации
|
||
- Пользователь открывает вкладку «Рецепты» и видит 5 персонализированных рецептов с фото
|
||
- Может сохранить рецепт, просмотреть детали, удалить из сохранённых
|
||
- КБЖУ помечены «≈» как приблизительные
|
||
|
||
---
|
||
|
||
## Итерация 2: Управление продуктами
|
||
|
||
> **Детальный план:** [Iteration_2.md](./Iteration_2.md)
|
||
|
||
**Цель:** пользователь может вести список своих продуктов вручную — добавлять через автодополнение (ingredient_mappings), редактировать, удалять, отслеживать сроки. Рекомендации становятся персонализированными: Gemini учитывает имеющиеся продукты.
|
||
|
||
**Зависимости:** итерация 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: Распознавание продуктов
|
||
|
||
> **Детальный план:** [Iteration_3.md](./Iteration_3.md)
|
||
|
||
**Цель:** пользователь фотографирует чек, холодильник или блюдо — Gemini Vision распознаёт продукты и помогает заполнить список запасов.
|
||
|
||
**Зависимости:** итерации 1, 2.
|
||
|
||
### User Stories
|
||
|
||
#### Backend
|
||
|
||
| ID | Story | Описание |
|
||
|----|-------|----------|
|
||
| 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 | Описание |
|
||
|----|-------|----------|
|
||
| 3.6 | ScanScreen | Выбор режима: чек / продукты / блюдо. Камера + галерея |
|
||
| 3.7 | Экран подтверждения | Список с инлайн-редактированием, удалением, «Добавить ещё фото», CTA «В запасы» |
|
||
| 3.8 | Экран результата блюда | Фото, КБЖУ≈, кнопки «В дневник» / «Открыть рецепт» |
|
||
|
||
### Результат итерации
|
||
- Сфотографировал чек → список продуктов → подтвердил → добавил в запасы
|
||
- Сфотографировал холодильник → то же
|
||
- Сфотографировал блюдо → КБЖУ≈ → можно добавить в дневник
|
||
|
||
---
|
||
|
||
## Итерация 4: Планирование меню
|
||
|
||
> **Детальный план:** [Iteration_4.md](./Iteration_4.md)
|
||
|
||
**Цель:** пользователь получает полное меню на неделю от Gemini с учётом продуктов, целей и предпочтений. Автоматически формируется список покупок.
|
||
|
||
**Зависимости:** итерации 1, 2.
|
||
|
||
### User Stories
|
||
|
||
#### Backend
|
||
|
||
| ID | Story | Описание |
|
||
|----|-------|----------|
|
||
| 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 | Описание |
|
||
|----|-------|----------|
|
||
| 4.6 | MenuScreen | 7-дневный вид, skeleton на генерацию, кнопка «Сгенерировать» |
|
||
| 4.7 | Замена рецепта | Тап «Изменить» → выбор из saved_recipes или перегенерация |
|
||
| 4.8 | ShoppingListScreen | Список по категориям, чекбоксы, «Поделиться» |
|
||
| 4.9 | DiaryScreen | Записи за день, «+ Добавить» |
|
||
|
||
### Результат итерации
|
||
- Пользователь получает меню на неделю одним запросом к Gemini
|
||
- Все рецепты меню сохраняются в saved_recipes
|
||
- Из меню автоматически формируется список покупок (то, чего нет в запасах)
|
||
- Ведётся дневник питания
|
||
|
||
---
|
||
|
||
## Итоги
|
||
|
||
| Итерация | Цель | Ключевые API |
|
||
|----------|------|-------------|
|
||
| 0. Фундамент | Auth, профиль, каркас | Firebase |
|
||
| 1. AI-рекомендации | Рецепты + сохранение | Gemini, Pexels |
|
||
| 2. Продукты | CRUD запасов, ingredient_mappings | — |
|
||
| 3. Распознавание | OCR чека, фото продуктов/блюда | Gemini Vision |
|
||
| 4. Меню | Недельное меню, список покупок | Gemini, Pexels |
|
||
|
||
**MVP:** итерации 0–2 (авторизация + рекомендации + продукты) — пользователь получает персонализированные рецепты.
|
||
|
||
**Полный продукт:** итерации 0–4 — полный цикл: сфотографировал чек → получил меню → список покупок → дневник питания.
|