385 lines
18 KiB
Markdown
385 lines
18 KiB
Markdown
# Техническое задание: Портфолио-платформа для фрилансеров
|
||
|
||
## 1. Описание проекта
|
||
|
||
Веб-сервис для публикации портфолио фрилансеров, поиска заказов и обмена отзывами. Проект закрепляет навыки CRUD, работы с рейтингами и отзывами, разграничения прав и совместной работы через Git.
|
||
|
||
**Команда:** 2–3 человека
|
||
|
||
---
|
||
|
||
## 2. Функциональные требования
|
||
|
||
### 2.1. Аутентификация и регистрация
|
||
- Регистрация, вход, выход (Laravel Breeze)
|
||
- При регистрации выбирается роль: фрилансер или клиент
|
||
|
||
### 2.2. Профиль фрилансера
|
||
- Публичный профиль: имя, фото, описание, навыки, контакты, рейтинг
|
||
- Редактирование профиля (только владелец)
|
||
- Загрузка аватара (валидация: jpg/png, макс. 2MB)
|
||
- Список навыков (свободный ввод или выбор из тегов)
|
||
|
||
### 2.3. Портфолио-проекты
|
||
- CRUD проектов в портфолио: название, описание, изображения (до 5), технологии/навыки, ссылка на проект
|
||
- Привязка проекта к навыкам (многие-ко-многим)
|
||
- Публичный просмотр на странице профиля
|
||
- Сортировка по дате, рейтингу
|
||
|
||
### 2.4. Каталог фрилансеров
|
||
- Просмотр списка фрилансеров с пагинацией
|
||
- Фильтрация по навыкам/категориям услуг
|
||
- Поиск по имени и описанию
|
||
- Карточка фрилансера (аватар, имя, рейтинг, основные навыки)
|
||
|
||
### 2.5. Заявки на проекты (клиент → фрилансер)
|
||
- Клиент создаёт заявку: описание проекта, бюджет, срок, требуемые навыки
|
||
- Привязка заявки к фрилансеру
|
||
- Фрилансер видит входящие заявки
|
||
- Фрилансер отвечает на заявку (принять / отклонить с комментарием)
|
||
- Статусы заявки: новая, принята, отклонена, завершена
|
||
|
||
### 2.6. Отзывы и рейтинги
|
||
- Клиент оставляет отзыв фрилансеру после завершения заявки
|
||
- Поля отзыва: текст, рейтинг (1–5 звёзд)
|
||
- Средний рейтинг фрилансера (автоматический пересчёт)
|
||
- Отзывы отображаются на странице профиля
|
||
|
||
### 2.7. Панель фрилансера
|
||
- Мой профиль (редактирование)
|
||
- Мои проекты портфолио
|
||
- Входящие заявки (статусы, ответы)
|
||
- Мой рейтинг и отзывы
|
||
|
||
### 2.8. Панель клиента
|
||
- Мои заявки (статусы)
|
||
- Создание новой заявки (выбор фрилансера)
|
||
- Оставить отзыв (после завершения)
|
||
|
||
### 2.9. Панель администратора
|
||
- Управление пользователями
|
||
- Модерация отзывов (скрыть/удалить)
|
||
- Общая статистика
|
||
|
||
---
|
||
|
||
## 3. Структура базы данных
|
||
|
||
### Таблица `users`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| name | VARCHAR(255) | Имя |
|
||
| email | VARCHAR(255) | Email (unique) |
|
||
| password | VARCHAR(255) | Хеш пароля |
|
||
| role | ENUM('admin', 'freelancer', 'client') | Роль |
|
||
| bio | TEXT \| NULL | О себе |
|
||
| avatar | VARCHAR(255) \| NULL | Аватар |
|
||
| rating | DECIMAL(3, 2) \| NULL | Средний рейтинг (0.00–5.00) |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `skills`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| name | VARCHAR(255) | Название навыка (unique) |
|
||
| slug | VARCHAR(255) | URL-идентификатор (unique) |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `freelancer_skill` (многие-ко-многим)
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| freelancer_id | BIGINT UNSIGNED | FK → users |
|
||
| skill_id | BIGINT UNSIGNED | FK → skills |
|
||
|
||
### Таблица `portfolio_projects`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| freelancer_id | BIGINT UNSIGNED | FK → users |
|
||
| title | VARCHAR(255) | Название проекта |
|
||
| description | TEXT | Описание |
|
||
| project_url | VARCHAR(255) \| NULL | Ссылка на проект |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `portfolio_skill` (многие-ко-многим)
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| portfolio_project_id | BIGINT UNSIGNED | FK → portfolio_projects |
|
||
| skill_id | BIGINT UNSIGNED | FK → skills |
|
||
|
||
### Таблица `project_proposals`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| client_id | BIGINT UNSIGNED | Клиент (FK → users) |
|
||
| freelancer_id | BIGINT UNSIGNED | Фрилансер (FK → users) |
|
||
| title | VARCHAR(255) | Название заявки |
|
||
| description | TEXT | Описание проекта |
|
||
| budget | DECIMAL(10, 2) \| NULL | Бюджет |
|
||
| deadline | DATE \| NULL | Срок выполнения |
|
||
| status | ENUM('new', 'accepted', 'rejected', 'completed') | Статус |
|
||
| response_message | TEXT \| NULL | Ответ фрилансера |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `reviews`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| client_id | BIGINT UNSIGNED | Автор отзыва (FK → users) |
|
||
| freelancer_id | BIGINT UNSIGNED | Получатель отзыва (FK → users) |
|
||
| proposal_id | BIGINT UNSIGNED \| NULL | Связанная заявка (FK → project_proposals) |
|
||
| rating | TINYINT | Рейтинг (1–5) |
|
||
| comment | TEXT | Текст отзыва |
|
||
| is_published | BOOLEAN | Опубликован ли |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
---
|
||
|
||
## 4. Маршруты и контроллеры
|
||
|
||
```php
|
||
// Публичные маршруты
|
||
Route::get('/', [FreelancerController::class, 'index'])->name('freelancers.index');
|
||
Route::get('/freelancers/{user}', [FreelancerController::class, 'show'])->name('freelancers.show');
|
||
|
||
// Аутентифицированные маршруты
|
||
Route::middleware('auth')->group(function () {
|
||
// Профиль
|
||
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
|
||
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
|
||
|
||
// Портфолио (фрилансер)
|
||
Route::prefix('my-portfolio')->name('portfolio.')->middleware('can:be-freelancer')->group(function () {
|
||
Route::resource('projects', PortfolioProjectController::class);
|
||
Route::get('dashboard', [FreelancerDashboardController::class, 'index'])->name('dashboard');
|
||
});
|
||
|
||
// Заявки
|
||
Route::prefix('proposals')->name('proposals.')->group(function () {
|
||
Route::post('/send/{freelancer}', [ProposalController::class, 'store'])->name('send');
|
||
Route::get('/my-sent', [ProposalController::class, 'mySent'])->name('my-sent'); // клиент
|
||
Route::get('/incoming', [ProposalController::class, 'incoming'])->name('incoming'); // фрилансер
|
||
Route::post('/{proposal}/accept', [ProposalController::class, 'accept'])->name('accept');
|
||
Route::post('/{proposal}/reject', [ProposalController::class, 'reject'])->name('reject');
|
||
});
|
||
|
||
// Отзывы
|
||
Route::resource('reviews', ReviewController::class)->only(['store', 'update', 'destroy']);
|
||
|
||
// Админка
|
||
Route::prefix('admin')->name('admin.')->middleware('can:access-admin-panel')->group(function () {
|
||
Route::resource('users', AdminUserController::class);
|
||
Route::resource('skills', AdminSkillController::class);
|
||
Route::patch('reviews/{review}/toggle-publish', [AdminReviewController::class, 'togglePublish'])->name('reviews.publish');
|
||
Route::get('dashboard', [AdminDashboardController::class, 'index'])->name('dashboard');
|
||
});
|
||
});
|
||
```
|
||
|
||
### Контроллеры
|
||
- `FreelancerController` — публичный каталог и профиль
|
||
- `ProfileController` — редактирование профиля
|
||
- `PortfolioProjectController` — CRUD проектов портфолио
|
||
- `ProposalController` — создание и управление заявками
|
||
- `ReviewController` — создание/удаление отзывов
|
||
- `FreelancerDashboardController` — панель фрилансера
|
||
- `AdminUserController`, `AdminSkillController`, `AdminReviewController`, `AdminDashboardController` — админка
|
||
|
||
---
|
||
|
||
## 5. Роли, Gates и Policies
|
||
|
||
### Роли
|
||
| Роль | Описание |
|
||
|---|---|
|
||
| `admin` | Полный доступ, модерация отзывов, управление пользователями |
|
||
| `freelancer` | Управление профилем, портфолио, просмотр входящих заявок, ответы |
|
||
| `client` | Создание заявок, просмотр каталога, написание отзывов |
|
||
|
||
### Gates
|
||
|
||
```php
|
||
Gate::define('be-freelancer', function (User $user) {
|
||
return in_array($user->role, ['admin', 'freelancer']);
|
||
});
|
||
|
||
Gate::define('be-client', function (User $user) {
|
||
return in_array($user->role, ['admin', 'client']);
|
||
});
|
||
|
||
Gate::define('access-admin-panel', function (User $user) {
|
||
return $user->role === 'admin';
|
||
});
|
||
```
|
||
|
||
### Policies
|
||
|
||
**PortfolioProjectPolicy:**
|
||
- `view` — все (публичное портфолио)
|
||
- `create`, `update`, `delete` — только владелец портфолио или admin
|
||
|
||
**ProposalPolicy:**
|
||
- `create` — client
|
||
- `view` — клиент (отправитель), фрилансер (получатель), admin
|
||
- `accept`, `reject` — только фрилансер (получатель)
|
||
|
||
**ReviewPolicy:**
|
||
- `create` — client (после завершения заявки)
|
||
- `view` — все (если опубликован), автор или admin (если не опубликован)
|
||
- `delete` — автор отзыва или admin
|
||
|
||
---
|
||
|
||
## 6. Требования к интерфейсу (Bootstrap 5)
|
||
|
||
### Компоненты
|
||
- **Навбар** — логотип, каталог фрилансеров, панель (мои заявки / портфолио), профиль
|
||
- **Карточки фрилансеров** — аватар (круглый), имя, рейтинг (звёзды), навыки (бейджи)
|
||
- **Профиль фрилансера** — большое фото, описание, навыки (бейджи), проекты портфолио (карточки), отзывы
|
||
- **Звёздный рейтинг** — интерактивные звёзды (radio buttons + стилизация или простой JS)
|
||
- **Карточки проектов портфолио** — изображение, название, описание, технологии (бейджи)
|
||
- **Форма заявки** — описание, бюджет (число), срок (дата), требуемые навыки
|
||
- **Таблица заявок** — статусы (цветные бейджи), действия (принять/отклонить)
|
||
- **Дашборд фрилансера** — статистика (карточки), входящие заявки, рейтинг
|
||
- **Адаптивная сетка** — `col-md-4` для карточек, `col-md-3` + `col-md-9` для профиля
|
||
|
||
### Цветовая схема статусов заявок
|
||
- Новая — `bg-info`
|
||
- Принята — `bg-success`
|
||
- Отклонена — `bg-danger`
|
||
- Завершена — `bg-secondary`
|
||
|
||
---
|
||
|
||
## 7. Git-workflow для команды
|
||
|
||
### Распределение модулей (для 2 человек)
|
||
|
||
| Участник | Модуль | Ветки |
|
||
|---|---|---|
|
||
| **Участник A** | Профили, навыки, портфолио, каталог | `feature/profiles`, `feature-skills`, `feature-portfolio-crud` |
|
||
| **Участник B** | Заявки, отзывы, рейтинги, UI | `feature/proposals`, `feature-proposal-responses`, `feature-proposal-statuses` |
|
||
|
||
### Распределение модулей (для 3 человек)
|
||
|
||
| Участник | Модуль | Ветки |
|
||
|---|---|---|
|
||
| **Участник A** | Профили, навыки, портфолио | `feature/profiles`, `feature-skills`, `feature-portfolio-crud` |
|
||
| **Участник B** | Заявки, статусы, ответы фрилансера | `feature/proposals`, `feature-proposal-responses`, `feature-proposal-statuses` |
|
||
| **Участник C** | Отзывы, рейтинги, каталог фрилансеров, UI | `feature-reviews`, `feature-ratings`, `feature-freelancer-catalog` |
|
||
|
||
### Правила
|
||
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/profiles
|
||
checkout develop
|
||
branch feature/proposals
|
||
checkout feature/profiles
|
||
commit id: "feat: add profiles and skills"
|
||
commit id: "feat: add portfolio CRUD"
|
||
checkout feature/proposals
|
||
commit id: "feat: add proposals system"
|
||
commit id: "feat: add proposal responses"
|
||
checkout develop
|
||
merge feature/profiles tag: "PR + review"
|
||
merge feature/proposals 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/profiles
|
||
checkout develop
|
||
branch feature/proposals
|
||
checkout develop
|
||
branch feature-reviews
|
||
checkout feature/profiles
|
||
commit id: "feat: add profiles and skills"
|
||
commit id: "feat: add portfolio images"
|
||
checkout feature/proposals
|
||
commit id: "feat: add proposals system"
|
||
commit id: "feat: add proposal responses"
|
||
checkout feature-reviews
|
||
commit id: "feat: add reviews and ratings"
|
||
commit id: "feat: add freelancer catalog"
|
||
checkout develop
|
||
merge feature/profiles tag: "PR + review"
|
||
merge feature/proposals tag: "PR + review"
|
||
merge feature-reviews tag: "PR + review"
|
||
checkout main
|
||
merge develop tag: "release v1.1"
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Критерии приёмки
|
||
|
||
### Обязательно
|
||
- [ ] Регистрация с выбором роли (фрилансер / клиент)
|
||
- [ ] Редактирование профиля с загрузкой аватара
|
||
- [ ] CRUD проектов портфолио с изображениями
|
||
- [ ] Навыки (многие-ко-многим фрилансер ↔ навыки)
|
||
- [ ] Каталог фрилансеров с пагинацией и фильтрацией по навыкам
|
||
- [ ] Создание заявки клиентом
|
||
- [ ] Фрилансер может принять/отклонить заявку
|
||
- [ ] Оставление отзыва клиентом (после завершения заявки)
|
||
- [ ] Расчёт среднего рейтинга фрилансера
|
||
- [ ] Policies: фрилансер не видит чужие входящие заявки, клиент не может управлять чужими заявками
|
||
- [ ] Адаптивная Bootstrap-вёрстка
|
||
- [ ] Flash-сообщения
|
||
- [ ] Git-история: минимум 3 ветки на участника, PR с ревью
|
||
|
||
### Дополнительно (бонусные баллы)
|
||
- [ ] Поиск фрилансеров по имени и описанию
|
||
- [ ] Несколько изображений в проекте портфолио (галерея)
|
||
- [ ] Сортировка фрилансеров по рейтингу, дате регистрации
|
||
- [ ] Уведомления (email при новой заявке / ответе)
|
||
- [ ] Мягкое удаление отзывов
|
||
- [ ] Тесты: PHPUnit Feature-тесты для ProposalController
|
||
- [ ] API: RESTful API для каталога фрилансеров
|
||
|
||
---
|
||
|
||
## 9. Дополнительные задания (для продвинутых)
|
||
|
||
1. **Мессенджер:** встроенный чат между клиентом и фрилансером по заявке
|
||
2. **Контракт:** генерация PDF-документа с условиями при принятии заявки
|
||
3. **Уведомления:** email и on-site уведомления при новых заявках/ответах/отзывах
|
||
4. **Фильтры:** расширенная фильтрация (рейтинг, стоимость, навыки)
|
||
5. **Тесты:** PHPUnit Feature-тесты для ReviewController и ProposalController
|
||
6. **API:** RESTful API для каталога фрилансеров и создания заявок
|
||
7. **Queue:** асинхронная отправка email-уведомлений
|
||
|
||
---
|
||
|
||
## 10. Рекомендуемые материалы
|
||
|
||
- Laravel Docs: https://laravel.com/docs/12.x
|
||
- Laravel Many-to-Many: https://laravel.com/docs/12.x/eloquent-relationships#many-to-many
|
||
- Laravel File Uploads: https://laravel.com/docs/12.x/filesystem#file-uploads
|
||
- Bootstrap 5 Cards: https://getbootstrap.com/docs/5.3/components/card/
|
||
- Laravel Policies: https://laravel.com/docs/12.x/authorization#creating-policies
|