Техническое задание: Портфолио-платформа для фрилансеров
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. Маршруты и контроллеры
// Публичные маршруты
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
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 |
Правила
- Ветка
develop — основная ветка разработки
- Каждый участник создаёт фич-ветки от
develop
- Минимум 3 PR на участника
- Обязательный код-ревью перед мёржем
- Conventional Commits
Визуализация workflow (2 участника)
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 участника)
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. Критерии приёмки
Обязательно
Дополнительно (бонусные баллы)
9. Дополнительные задания (для продвинутых)
- Мессенджер: встроенный чат между клиентом и фрилансером по заявке
- Контракт: генерация PDF-документа с условиями при принятии заявки
- Уведомления: email и on-site уведомления при новых заявках/ответах/отзывах
- Фильтры: расширенная фильтрация (рейтинг, стоимость, навыки)
- Тесты: PHPUnit Feature-тесты для ReviewController и ProposalController
- API: RESTful API для каталога фрилансеров и создания заявок
- Queue: асинхронная отправка email-уведомлений
10. Рекомендуемые материалы