401 lines
19 KiB
Markdown
401 lines
19 KiB
Markdown
# Техническое задание: Каталог рецептов с рейтингом
|
||
|
||
## 1. Описание проекта
|
||
|
||
Кулинарная платформа для публикации рецептов, комментирования и оценки блюд. Проект закрепляет навыки CRUD, отношений многие-ко-многим (рецепт ↔ ингредиенты), расчёта среднего рейтинга, разграничения прав и совместной работы через Git.
|
||
|
||
**Команда:** 2–3 человека
|
||
|
||
---
|
||
|
||
## 2. Функциональные требования
|
||
|
||
### 2.1. Аутентификация и регистрация
|
||
- Регистрация, вход, выход (Laravel Breeze)
|
||
|
||
### 2.2. Каталог рецептов
|
||
- Просмотр списка рецептов с пагинацией
|
||
- Фильтрация по категориям (завтрак, обед, ужин, выпечка, десерты, напитки)
|
||
- Поиск по названию и ингредиентам
|
||
- Сортировка по дате добавления, рейтингу, времени приготовления
|
||
- Детальная страница рецепта
|
||
|
||
### 2.3. Управление рецептами (автор/админ)
|
||
- CRUD рецептов: название, описание, категория, время приготовления, количество порций, изображение, сложность
|
||
- Пошаговое приготовление (несколько шагов, каждое — отдельная запись)
|
||
- Привязка ингредиентов с количеством и единицей измерения
|
||
- Загрузка изображения рецепта (валидация: jpg/png, макс. 2MB)
|
||
|
||
### 2.4. Ингредиенты
|
||
- Справочник ингредиентов (название, единица измерения по умолчанию)
|
||
- CRUD ингредиентов (админ)
|
||
- Привязка к рецептам (многие-ко-многим) с указанием количества и единицы
|
||
|
||
### 2.5. Комментарии
|
||
- Добавление комментариев к рецепту
|
||
- Удаление своего комментария
|
||
- Удаление любого комментария (админ/автор рецепта)
|
||
- Пагинация комментариев
|
||
|
||
### 2.6. Оценки и рейтинг
|
||
- Оценка рецепта (1–5 звёзд) — один голос на пользователя
|
||
- Отображение среднего рейтинга на карточке и странице рецепта
|
||
- Автоматический пересчёт среднего рейтинга
|
||
|
||
### 2.7. Избранное
|
||
- Добавление рецепта в избранное
|
||
- Список избранных рецептов (личный кабинет)
|
||
|
||
### 2.8. Личный кабинет автора
|
||
- Мои рецепты
|
||
- Статистика: количество рецептов, средний рейтинг, количество комментариев
|
||
- Избранные рецепты
|
||
|
||
### 2.9. Панель администратора
|
||
- Управление пользователями и ролями
|
||
- CRUD ингредиентов (справочник)
|
||
- Модерация рецептов и комментариев
|
||
- Общая статистика
|
||
|
||
---
|
||
|
||
## 3. Структура базы данных
|
||
|
||
### Таблица `users`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| name | VARCHAR(255) | Имя |
|
||
| email | VARCHAR(255) | Email (unique) |
|
||
| password | VARCHAR(255) | Хеш пароля |
|
||
| role | ENUM('admin', 'author', 'user') | Роль |
|
||
| avatar | VARCHAR(255) \| NULL | Аватар |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `categories`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| name | VARCHAR(255) | Название категории |
|
||
| slug | VARCHAR(255) | URL-идентификатор (unique) |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `recipes`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| title | VARCHAR(255) | Название рецепта |
|
||
| slug | VARCHAR(255) | URL-идентификатор (unique) |
|
||
| description | TEXT | Краткое описание |
|
||
| image | VARCHAR(255) \| NULL | Изображение |
|
||
| category_id | BIGINT UNSIGNED | Категория (FK → categories) |
|
||
| author_id | BIGINT UNSIGNED | Автор (FK → users) |
|
||
| cooking_time | INT | Время приготовления (минуты) |
|
||
| servings | INT | Количество порций |
|
||
| difficulty | ENUM('easy', 'medium', 'hard') | Сложность |
|
||
| rating | DECIMAL(3, 2) \| NULL | Средний рейтинг (0.00–5.00) |
|
||
| rating_count | INT | Количество оценок |
|
||
| is_published | BOOLEAN | Опубликован ли |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `recipe_steps`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| recipe_id | BIGINT UNSIGNED | Рецепт (FK → recipes) |
|
||
| step_number | INT | Порядковый номер шага |
|
||
| description | TEXT | Описание шага |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `ingredients`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| name | VARCHAR(255) | Название ингредиента (unique) |
|
||
| default_unit | VARCHAR(50) | Единица измерения по умолчанию (г, мл, шт, ст.л.) |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `ingredient_recipe` (многие-ко-многим)
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| recipe_id | BIGINT UNSIGNED | FK → recipes |
|
||
| ingredient_id | BIGINT UNSIGNED | FK → ingredients |
|
||
| quantity | DECIMAL(10, 2) | Количество |
|
||
| unit | VARCHAR(50) | Единица измерения |
|
||
|
||
### Таблица `comments`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| body | TEXT | Текст комментария |
|
||
| user_id | BIGINT UNSIGNED | Автор (FK → users) |
|
||
| recipe_id | BIGINT UNSIGNED | Рецепт (FK → recipes) |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `ratings`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| user_id | BIGINT UNSIGNED | FK → users |
|
||
| recipe_id | BIGINT UNSIGNED | FK → recipes |
|
||
| rating | TINYINT | Оценка (1–5) |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP |
|
||
|
||
### Таблица `recipe_user` (избранное)
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| user_id | BIGINT UNSIGNED | FK → users |
|
||
| recipe_id | BIGINT UNSIGNED | FK → recipes |
|
||
| added_at | TIMESTAMP | |
|
||
|
||
---
|
||
|
||
## 4. Маршруты и контроллеры
|
||
|
||
```php
|
||
// Публичные маршруты
|
||
Route::get('/', [RecipeController::class, 'index'])->name('recipes.index');
|
||
Route::get('/recipes/{recipe:slug}', [RecipeController::class, 'show'])->name('recipes.show');
|
||
|
||
// Аутентифицированные маршруты
|
||
Route::middleware('auth')->group(function () {
|
||
// Оценки
|
||
Route::post('/recipes/{recipe}/rate', [RatingController::class, 'store'])->name('recipes.rate');
|
||
|
||
// Избранное
|
||
Route::get('/favorites', [FavoriteController::class, 'index'])->name('favorites.index');
|
||
Route::post('/recipes/{recipe}/favorite', [FavoriteController::class, 'toggle'])->name('recipes.favorite');
|
||
|
||
// Комментарии
|
||
Route::resource('recipes.comments', CommentController::class)->scoped(['recipes' => 'recipe'])->only(['store', 'destroy']);
|
||
|
||
// Мои рецепты (автор)
|
||
Route::prefix('my-recipes')->name('my-recipes.')->middleware('can:be-author')->group(function () {
|
||
Route::resource('recipes', AuthorRecipeController::class);
|
||
Route::get('dashboard', [AuthorDashboardController::class, 'index'])->name('dashboard');
|
||
});
|
||
|
||
// Админка
|
||
Route::prefix('admin')->name('admin.')->middleware('can:access-admin-panel')->group(function () {
|
||
Route::resource('users', AdminUserController::class);
|
||
Route::resource('ingredients', AdminIngredientController::class);
|
||
Route::patch('recipes/{recipe}/toggle-publish', [AdminRecipeController::class, 'togglePublish'])->name('recipes.publish');
|
||
Route::get('dashboard', [AdminDashboardController::class, 'index'])->name('dashboard');
|
||
});
|
||
});
|
||
```
|
||
|
||
### Контроллеры
|
||
- `RecipeController` — публичный каталог и детальный просмотр
|
||
- `AuthorRecipeController` — CRUD рецептов автором
|
||
- `RatingController` — добавление/обновление оценки
|
||
- `FavoriteController` — управление избранным
|
||
- `CommentController` — создание/удаление комментариев
|
||
- `AuthorDashboardController` — панель автора
|
||
- `AdminUserController`, `AdminIngredientController`, `AdminRecipeController`, `AdminDashboardController` — админка
|
||
|
||
---
|
||
|
||
## 5. Роли, Gates и Policies
|
||
|
||
### Роли
|
||
| Роль | Описание |
|
||
|---|---|
|
||
| `admin` | Полный доступ, модерация, CRUD ингредиентов, управление пользователями |
|
||
| `author` | Создание/редактирование своих рецептов, управление комментариями к своим рецептам |
|
||
| `user` | Просмотр каталога, комментирование, оценка, добавление в избранное |
|
||
|
||
### Gates
|
||
|
||
```php
|
||
Gate::define('be-author', function (User $user) {
|
||
return in_array($user->role, ['admin', 'author']);
|
||
});
|
||
|
||
Gate::define('access-admin-panel', function (User $user) {
|
||
return $user->role === 'admin';
|
||
});
|
||
```
|
||
|
||
### Policies
|
||
|
||
**RecipePolicy:**
|
||
- `view` — все (если опубликован), автор/admin (если не опубликован)
|
||
- `create` — author или admin
|
||
- `update`, `delete` — автор рецепта или admin
|
||
- `rate` — user (ещё не оценивал этот рецепт)
|
||
|
||
**CommentPolicy:**
|
||
- `create` — все аутентифицированные
|
||
- `delete` — автор комментария, автор рецепта или admin
|
||
|
||
**IngredientPolicy:**
|
||
- `view` — все
|
||
- `create`, `update`, `delete` — только admin
|
||
|
||
---
|
||
|
||
## 6. Требования к интерфейсу (Bootstrap 5)
|
||
|
||
### Компоненты
|
||
- **Навбар** — логотип, каталог, «Избранное», «Мои рецепты», профиль
|
||
- **Карточки рецептов** — изображение, название, категория (бейдж), время (иконка + текст), рейтинг (звёзды)
|
||
- **Страница рецепта** — большое изображение, описание, ингредиенты (таблица), шаги приготовления (нумерованный список), комментарии, форма оценки
|
||
- **Звёздный рейтинг** — интерактивные звёзды (radio buttons + стилизация)
|
||
- **Форма рецепта** — Bootstrap-форма с загрузкой изображения, динамическое добавление шагов и ингредиентов
|
||
- **Таблица ингредиентов** — название, количество, единица измерения
|
||
- **Дашборд автора** — статистика (карточки), список моих рецептов
|
||
- **Адаптивная сетка** — `col-md-4` для карточек, `col-md-8` + `col-md-4` для основного контента и сайдбара
|
||
|
||
### Цветовая схема сложности
|
||
- Лёгкий — `bg-success`
|
||
- Средний — `bg-warning`
|
||
- Сложный — `bg-danger`
|
||
|
||
### Цветовая схема категорий
|
||
- Завтрак — `bg-info`
|
||
- Обед — `bg-primary`
|
||
- Ужин — `bg-dark`
|
||
- Выпечка — `bg-warning`
|
||
- Десерты — `bg-pink`
|
||
- Напитки — `bg-light text-dark`
|
||
|
||
---
|
||
|
||
## 7. Git-workflow для команды
|
||
|
||
### Распределение модулей (для 2 человек)
|
||
|
||
| Участник | Модуль | Ветки |
|
||
|---|---|---|
|
||
| **Участник A** | Рецепты, категории, шаги, ингредиенты, каталог | `feature/recipes-crud`, `feature/categories`, `feature-recipe-steps` |
|
||
| **Участник B** | Оценки, комментарии, избранное, дашборд, UI | `feature-ingredients`, `feature-recipe-ingredients-m2m`, `feature-ratings` |
|
||
|
||
### Распределение модулей (для 3 человек)
|
||
|
||
| Участник | Модуль | Ветки |
|
||
|---|---|---|
|
||
| **Участник A** | Рецепты, категории, шаги приготовления, загрузка изображений | `feature/recipes-crud`, `feature/categories`, `feature-recipe-steps` |
|
||
| **Участник B** | Ингредиенты, многие-ко-многим, оценки, расчёт рейтинга | `feature-ingredients`, `feature-recipe-ingredients-m2m`, `feature-ratings` |
|
||
| **Участник C** | Комментарии, избранное, каталог, UI, дашборд | `feature-comments`, `feature-favorites`, `feature-recipe-catalog-ui` |
|
||
|
||
### Правила
|
||
1. Ветка `develop` — основная ветка разработки
|
||
2. Каждый участник создаёт фич-ветки от `develop`
|
||
3. Минимум 3 PR на участника
|
||
4. Обязательный код-ревью перед мёржем
|
||
5. Conventional Commits
|
||
|
||
### Визуализация workflow (2 участника)
|
||
|
||
```mermaid
|
||
gitGraph
|
||
commit id: "init" tag: "v1.0"
|
||
branch develop
|
||
checkout develop
|
||
branch feature/recipes-crud
|
||
checkout develop
|
||
branch feature-ingredients
|
||
checkout feature/recipes-crud
|
||
commit id: "feat: add recipes CRUD"
|
||
commit id: "feat: add recipe steps"
|
||
checkout feature-ingredients
|
||
commit id: "feat: add ingredients m2m"
|
||
commit id: "feat: add ratings system"
|
||
checkout develop
|
||
merge feature/recipes-crud tag: "PR + review"
|
||
merge feature-ingredients tag: "PR + review"
|
||
checkout main
|
||
merge develop tag: "release v1.1"
|
||
```
|
||
|
||
### Визуализация workflow (3 участника)
|
||
|
||
```mermaid
|
||
gitGraph
|
||
commit id: "init" tag: "v1.0"
|
||
branch develop
|
||
checkout develop
|
||
branch feature/recipes-crud
|
||
checkout develop
|
||
branch feature-ingredients
|
||
checkout develop
|
||
branch feature-comments
|
||
checkout feature/recipes-crud
|
||
commit id: "feat: add recipes CRUD"
|
||
commit id: "feat: add recipe categories"
|
||
checkout feature-ingredients
|
||
commit id: "feat: add ingredients m2m"
|
||
commit id: "feat: add ratings system"
|
||
checkout feature-comments
|
||
commit id: "feat: add recipe comments"
|
||
commit id: "feat: add favorites system"
|
||
checkout develop
|
||
merge feature/recipes-crud tag: "PR + review"
|
||
merge feature-ingredients tag: "PR + review"
|
||
merge feature-comments tag: "PR + review"
|
||
checkout main
|
||
merge develop tag: "release v1.1"
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Критерии приёмки
|
||
|
||
### Обязательно
|
||
- [ ] CRUD рецептов с загрузкой изображений
|
||
- [ ] CRUD категорий рецептов
|
||
- [ ] Шаги приготовления (CRUD внутри рецепта)
|
||
- [ ] Справочник ингредиентов (CRUD, только админ)
|
||
- [ ] Привязка ингредиентов к рецептам (многие-ко-многим с количеством и единицей)
|
||
- [ ] Оценка рецепта (1–5 звёзд, один голос на пользователя)
|
||
- [ ] Автоматический расчёт среднего рейтинга
|
||
- [ ] Комментарии к рецептам (создание + удаление)
|
||
- [ ] Избранное (добавление/удаление)
|
||
- [ ] Панель автора (мои рецепты, статистика)
|
||
- [ ] Policies: пользователь не может редактировать чужие рецепты
|
||
- [ ] Адаптивная Bootstrap-вёрстка
|
||
- [ ] Flash-сообщения
|
||
- [ ] Пагинация каталога и комментариев
|
||
- [ ] Git-история: минимум 3 ветки на участника, PR с ревью
|
||
|
||
### Дополнительно (бонусные баллы)
|
||
- [ ] Поиск рецептов по названию и ингредиентам
|
||
- [ ] Фильтрация по времени приготовления и сложности
|
||
- [ ] Сортировка по рейтингу, дате, времени приготовления
|
||
- [ ] Печать рецепта (кнопка «Распечатать» → print-friendly страница)
|
||
- [ ] Мягкое удаление рецептов и комментариев
|
||
- [ ] Тесты: PHPUnit Feature-тесты для RatingController
|
||
- [ ] API: RESTful API для каталога рецептов
|
||
|
||
---
|
||
|
||
## 9. Дополнительные задания (для продвинутых)
|
||
|
||
1. **Масштабирование рецепта:** калькулятор для пересчёта ингредиентов на другое количество порций
|
||
2. **Планировщик питания:** календарь с рецептами на неделю
|
||
3. **Коллекции:** пользовательские подборки рецептов (как плейлисты)
|
||
4. **Тесты:** PHPUnit Feature-тесты для RecipeController и RatingController
|
||
5. **API:** RESTful API для каталога и создания рецептов
|
||
6. **Queue:** асинхронный пересчёт рейтинга при большом количестве оценок
|
||
7. **Events & Listeners:** событие «новая оценка» → пересчёт рейтинга
|
||
|
||
---
|
||
|
||
## 10. Рекомендуемые материалы
|
||
|
||
- Laravel Docs: https://laravel.com/docs/12.x
|
||
- Laravel Many-to-Many with Pivot: https://laravel.com/docs/12.x/eloquent-relationships#many-to-many
|
||
- Bootstrap 5 Cards: https://getbootstrap.com/docs/5.3/components/card/
|
||
- Laravel Aggregates: https://laravel.com/docs/12.x/queries#aggregates
|
||
- Laravel Policies: https://laravel.com/docs/12.x/authorization#creating-policies
|