411 lines
19 KiB
Markdown
411 lines
19 KiB
Markdown
# Техническое задание: Платформа для онлайн-курсов
|
||
|
||
## 1. Описание проекта
|
||
|
||
Веб-платформа для публикации и прохождения онлайн-курсов с отслеживанием прогресса студентов и системой тестирования. Проект закрепляет навыки CRUD, вложенных сущностей (курс → уроки → тесты), разграничения прав и совместной работы через Git.
|
||
|
||
**Команда:** 2–3 человека
|
||
|
||
---
|
||
|
||
## 2. Функциональные требования
|
||
|
||
### 2.1. Аутентификация и регистрация
|
||
- Регистрация, вход, выход (Laravel Breeze)
|
||
- Восстановление пароля (опционально)
|
||
|
||
### 2.2. Каталог курсов
|
||
- Просмотр списка доступных курсов с пагинацией
|
||
- Фильтрация по категориям
|
||
- Детальная страница курса (описание, автор, список уроков, рейтинг)
|
||
- Запись на курс (кнопка «Записаться»)
|
||
|
||
### 2.3. Управление курсами (преподаватель/админ)
|
||
- CRUD курсов: название, описание, обложка, категория, уровень сложности
|
||
- Вложенный CRUD уроков внутри курса:
|
||
- Название урока, порядковый номер, тип (текст/видео)
|
||
- Текстовое содержание (WYSIWYG или markdown)
|
||
- URL видео (опционально, YouTube/Vimeo embed)
|
||
- CRUD тестов/викторин к урокам:
|
||
- Вопросы с вариантами ответов
|
||
- Один или несколько правильных ответов
|
||
- Проходной балл (в процентах)
|
||
|
||
### 2.4. Прохождение курса (студент)
|
||
- Список записанных курсов (личный кабинет)
|
||
- Последовательное прохождение уроков
|
||
- Отметка урока как пройденного
|
||
- Сдача теста после урока (или в конце курса)
|
||
- Просмотр результатов теста (правильные/неправильные ответы)
|
||
- Прогресс-бар прохождения курса (в процентах)
|
||
|
||
### 2.5. Панель преподавателя
|
||
- Мои курсы (созданные мной)
|
||
- Список студентов по каждому курсу
|
||
- Прогресс каждого студента
|
||
- Статистика: количество курсов, студентов, средний балл
|
||
|
||
### 2.6. Панель администратора
|
||
- Управление пользователями и ролями
|
||
- Модерация курсов (скрыть/показать)
|
||
- Общая статистика платформы
|
||
|
||
---
|
||
|
||
## 3. Структура базы данных
|
||
|
||
### Таблица `users`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| name | VARCHAR(255) | Имя |
|
||
| email | VARCHAR(255) | Email (unique) |
|
||
| password | VARCHAR(255) | Хеш пароля |
|
||
| role | ENUM('admin', 'instructor', 'student') | Роль |
|
||
| 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 | |
|
||
|
||
### Таблица `courses`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| title | VARCHAR(255) | Название курса |
|
||
| slug | VARCHAR(255) | URL-идентификатор (unique) |
|
||
| description | TEXT | Описание |
|
||
| cover_image | VARCHAR(255) \| NULL | Обложка курса |
|
||
| difficulty_level | ENUM('beginner', 'intermediate', 'advanced') | Уровень |
|
||
| instructor_id | BIGINT UNSIGNED | Преподаватель (FK → users) |
|
||
| category_id | BIGINT UNSIGNED | Категория (FK → categories) |
|
||
| is_published | BOOLEAN | Опубликован ли |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `lessons`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| course_id | BIGINT UNSIGNED | Курс (FK → courses) |
|
||
| title | VARCHAR(255) | Название урока |
|
||
| position | INT | Порядковый номер |
|
||
| content_type | ENUM('text', 'video') | Тип контента |
|
||
| content | TEXT | Содержимое (текст или URL видео) |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `quizzes`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| lesson_id | BIGINT UNSIGNED | Урок (FK → lessons) |
|
||
| title | VARCHAR(255) | Название теста |
|
||
| passing_score | INT | Проходной балл (%) |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `quiz_questions`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| quiz_id | BIGINT UNSIGNED | Тест (FK → quizzes) |
|
||
| question | TEXT | Вопрос |
|
||
| position | INT | Порядковый номер |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `quiz_answers`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| question_id | BIGINT UNSIGNED | Вопрос (FK → quiz_questions) |
|
||
| answer_text | VARCHAR(255) | Текст ответа |
|
||
| is_correct | BOOLEAN | Правильный ли |
|
||
| created_at | TIMESTAMP | |
|
||
| updated_at | TIMESTAMP | |
|
||
|
||
### Таблица `course_user` (запись на курс)
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| course_id | BIGINT UNSIGNED | FK → courses |
|
||
| user_id | BIGINT UNSIGNED | FK → users |
|
||
| progress | INT | Прогресс в процентах (0–100) |
|
||
| enrolled_at | TIMESTAMP | Дата записи |
|
||
| completed_at | TIMESTAMP \| NULL | Дата завершения |
|
||
|
||
### Таблица `lesson_progress`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| user_id | BIGINT UNSIGNED | FK → users |
|
||
| lesson_id | BIGINT UNSIGNED | FK → lessons |
|
||
| is_completed | BOOLEAN | Пройден ли |
|
||
| completed_at | TIMESTAMP \| NULL | |
|
||
|
||
### Таблица `quiz_results`
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| id | BIGINT UNSIGNED | Первичный ключ |
|
||
| user_id | BIGINT UNSIGNED | FK → users |
|
||
| quiz_id | BIGINT UNSIGNED | FK → quizzes |
|
||
| score | INT | Набранный балл (%) |
|
||
| passed | BOOLEAN | Сдал ли |
|
||
| taken_at | TIMESTAMP | Дата прохождения |
|
||
|
||
---
|
||
|
||
## 4. Маршруты и контроллеры
|
||
|
||
```php
|
||
// Публичные маршруты
|
||
Route::get('/', [CourseController::class, 'index'])->name('courses.index');
|
||
Route::get('/courses/{course:slug}', [CourseController::class, 'show'])->name('courses.show');
|
||
|
||
// Аутентифицированные маршруты
|
||
Route::middleware('auth')->group(function () {
|
||
// Запись на курс
|
||
Route::post('/courses/{course}/enroll', [CourseController::class, 'enroll'])->name('courses.enroll');
|
||
|
||
// Прохождение курса
|
||
Route::prefix('my-courses')->name('my-courses.')->group(function () {
|
||
Route::get('/', [StudentCourseController::class, 'index'])->name('index');
|
||
Route::get('/{course}', [StudentCourseController::class, 'show'])->name('show');
|
||
Route::get('/{course}/lessons/{lesson}', [LessonController::class, 'show'])->name('lessons.show');
|
||
Route::post('/{course}/lessons/{lesson}/complete', [LessonController::class, 'complete'])->name('lessons.complete');
|
||
|
||
// Тесты
|
||
Route::get('/{course}/quizzes/{quiz}', [QuizController::class, 'take'])->name('quizzes.take');
|
||
Route::post('/{course}/quizzes/{quiz}/submit', [QuizController::class, 'submit'])->name('quizzes.submit');
|
||
});
|
||
|
||
// Преподаватель
|
||
Route::prefix('instructor')->name('instructor.')->middleware('can:be-instructor')->group(function () {
|
||
Route::resource('courses', InstructorCourseController::class);
|
||
Route::resource('courses.lessons', InstructorLessonController::class)->scoped(['courses' => 'course']);
|
||
Route::resource('lessons.quizzes', InstructorQuizController::class)->scoped(['lessons' => 'lesson']);
|
||
Route::resource('quizzes.questions', InstructorQuestionController::class)->scoped(['quizzes' => 'quiz']);
|
||
Route::get('courses/{course}/students', [InstructorCourseController::class, 'students'])->name('courses.students');
|
||
Route::get('dashboard', [InstructorDashboardController::class, 'index'])->name('dashboard');
|
||
});
|
||
|
||
// Админка
|
||
Route::prefix('admin')->name('admin.')->middleware('can:access-admin-panel')->group(function () {
|
||
Route::resource('users', AdminUserController::class);
|
||
Route::patch('courses/{course}/toggle-publish', [AdminCourseController::class, 'togglePublish'])->name('courses.publish');
|
||
Route::get('dashboard', [AdminDashboardController::class, 'index'])->name('dashboard');
|
||
});
|
||
});
|
||
```
|
||
|
||
### Контроллеры
|
||
- `CourseController` — публичный каталог и запись
|
||
- `StudentCourseController` — прохождение курсов студентом
|
||
- `LessonController` — просмотр уроков и отметка пройденными
|
||
- `QuizController` — прохождение тестов
|
||
- `InstructorCourseController` — CRUD курсов преподавателем
|
||
- `InstructorLessonController` — CRUD уроков
|
||
- `InstructorQuizController` — CRUD тестов
|
||
- `InstructorQuestionController` — CRUD вопросов
|
||
- `InstructorDashboardController` — панель преподавателя
|
||
- `AdminUserController`, `AdminCourseController`, `AdminDashboardController` — админка
|
||
|
||
---
|
||
|
||
## 5. Роли, Gates и Policies
|
||
|
||
### Роли
|
||
| Роль | Описание |
|
||
|---|---|
|
||
| `admin` | Полный доступ, модерация курсов, управление пользователями |
|
||
| `instructor` | Создание/редактирование своих курсов, просмотр студентов |
|
||
| `student` | Запись на курсы, прохождение уроков и тестов |
|
||
|
||
### Gates
|
||
|
||
```php
|
||
Gate::define('be-instructor', function (User $user) {
|
||
return in_array($user->role, ['admin', 'instructor']);
|
||
});
|
||
|
||
Gate::define('access-admin-panel', function (User $user) {
|
||
return $user->role === 'admin';
|
||
});
|
||
```
|
||
|
||
### Policies
|
||
|
||
**CoursePolicy:**
|
||
- `view` — все (если опубликован), автор или admin (если не опубликован)
|
||
- `create` — instructor или admin
|
||
- `update`, `delete` — автор курса или admin
|
||
- `enroll` — student (не записан на этот курс)
|
||
|
||
**LessonPolicy:**
|
||
- `view` — студент, записанный на курс, или преподаватель/admin
|
||
- `create`, `update`, `delete` — преподаватель курса или admin
|
||
|
||
**QuizPolicy:**
|
||
- `view` — студент, записанный на курс, или преподаватель/admin
|
||
- `take` — студент, записанный на курс
|
||
- `create`, `update`, `delete` — преподаватель курса или admin
|
||
|
||
**QuizResultPolicy:**
|
||
- `view` — владелец результата или преподаватель/admin
|
||
- `create` — все студенты
|
||
|
||
---
|
||
|
||
## 6. Требования к интерфейсу (Bootstrap 5)
|
||
|
||
### Компоненты
|
||
- **Навбар** — логотип, каталог, «Мои курсы», профиль
|
||
- **Карточки курсов** — обложка, название, автор, уровень (бейдж), прогресс-бар
|
||
- **Страница курса** — описание, список уроков (accordion), кнопка записи
|
||
- **Страница урока** — контент (текст или embed видео), кнопка «Отметить пройденным»
|
||
- **Прогресс-бар** — Bootstrap `progress` для отображения прохождения курса
|
||
- **Accordion** — список уроков курса
|
||
- **Тест** — форма с radio/checkbox, кнопка отправки, результат
|
||
- **Дашборд преподавателя** — статистика (карточки), таблица студентов
|
||
- **Адаптивная сетка** — `col-md-4` для карточек, `col-md-8` + `col-md-4` для основного контента и сайдбара
|
||
|
||
### Цветовая схема уровней сложности
|
||
- Начальный — `bg-success`
|
||
- Средний — `bg-warning`
|
||
- Продвинутый — `bg-danger`
|
||
|
||
---
|
||
|
||
## 7. Git-workflow для команды
|
||
|
||
### Распределение модулей (для 2 человек)
|
||
|
||
| Участник | Модуль | Ветки |
|
||
|---|---|---|
|
||
| **Участник A** | Курсы, категории, обложки, дашборд преподавателя | `feature/courses-crud`, `feature/categories`, `feature-course-covers` |
|
||
| **Участник B** | Уроки, тесты, вопросы, прохождение тестов | `feature/lessons-crud`, `feature/quizzes`, `feature-quiz-questions` |
|
||
|
||
### Распределение модулей (для 3 человек)
|
||
|
||
| Участник | Модуль | Ветки |
|
||
|---|---|---|
|
||
| **Участник A** | Курсы, категории, загрузка обложек | `feature/courses-crud`, `feature/categories`, `feature-course-covers` |
|
||
| **Участник B** | Уроки, тесты, вопросы | `feature/lessons-crud`, `feature/quizzes`, `feature-quiz-questions` |
|
||
| **Участник C** | Запись на курс, прогресс, прохождение тестов, UI | `feature/enrollment`, `feature-progress-tracking`, `feature-student-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/courses-crud
|
||
checkout develop
|
||
branch feature/lessons-crud
|
||
checkout feature/courses-crud
|
||
commit id: "feat: add courses CRUD with covers"
|
||
commit id: "feat: add categories CRUD"
|
||
checkout feature/lessons-crud
|
||
commit id: "feat: add lessons CRUD"
|
||
commit id: "feat: add quizzes system"
|
||
checkout develop
|
||
merge feature/courses-crud tag: "PR + review"
|
||
merge feature/lessons-crud 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/courses-crud
|
||
checkout develop
|
||
branch feature/lessons-crud
|
||
checkout develop
|
||
branch feature/enrollment
|
||
checkout feature/courses-crud
|
||
commit id: "feat: add courses CRUD with covers"
|
||
commit id: "feat: add instructor dashboard"
|
||
checkout feature/lessons-crud
|
||
commit id: "feat: add lessons CRUD"
|
||
commit id: "feat: add quizzes system"
|
||
checkout feature/enrollment
|
||
commit id: "feat: add enrollment flow"
|
||
commit id: "feat: add progress tracking"
|
||
checkout develop
|
||
merge feature/courses-crud tag: "PR + review"
|
||
merge feature/lessons-crud tag: "PR + review"
|
||
merge feature/enrollment tag: "PR + review"
|
||
checkout main
|
||
merge develop tag: "release v1.1"
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Критерии приёмки
|
||
|
||
### Обязательно
|
||
- [ ] CRUD курсов с загрузкой обложки
|
||
- [ ] CRUD уроков внутри курса (вложенный ресурс)
|
||
- [ ] CRUD тестов и вопросов (минимум 3 ответа на вопрос, 1+ правильный)
|
||
- [ ] Запись на курс (студент)
|
||
- [ ] Отметка уроков пройденными
|
||
- [ ] Прохождение теста с подсчётом результата
|
||
- [ ] Прогресс-бар прохождения курса
|
||
- [ ] Панель преподавателя (мои курсы, студенты)
|
||
- [ ] Policies: студент не может редактировать курсы, преподаватель не может редактировать чужие курсы
|
||
- [ ] Адаптивная Bootstrap-вёрстка
|
||
- [ ] Flash-сообщения
|
||
- [ ] Пагинация каталога курсов
|
||
- [ ] Git-история: минимум 3 ветки на участника, PR с ревью
|
||
|
||
### Дополнительно (бонусные баллы)
|
||
- [ ] Рейтинг курса (средняя оценка студентов)
|
||
- [ ] Сертификат при завершении курса (генерация PDF)
|
||
- [ ] Поиск курсов по названию/описанию
|
||
- [ ] Фильтрация по уровню сложности
|
||
- [ ] Тесты: перемешивание вопросов и ответов
|
||
- [ ] Soft Deletes для курсов и уроков
|
||
|
||
---
|
||
|
||
## 9. Дополнительные задания (для продвинутых)
|
||
|
||
1. **Рейтинг курсов:** студенты оценивают курс после завершения (1–5 звёзд)
|
||
2. **Сертификаты:** генерация PDF-сертификата при 100% прохождении
|
||
3. **Обсуждения:** комментарии к урокам (вложенные, как на YouTube)
|
||
4. **Тесты:** таймер на прохождение теста, автоматическая отправка по истечении
|
||
5. **API:** RESTful API для каталога курсов и прохождения
|
||
6. **Тесты:** PHPUnit Feature-тесты для QuizController
|
||
7. **Events & Listeners:** событие «урок пройден» → обновление прогресса
|
||
|
||
---
|
||
|
||
## 10. Рекомендуемые материалы
|
||
|
||
- Laravel Docs: https://laravel.com/docs/12.x
|
||
- Nested Resources: https://laravel.com/docs/12.x/controllers#restful-nested-resources
|
||
- Bootstrap 5 Progress: https://getbootstrap.com/docs/5.3/components/progress/
|
||
- Bootstrap 5 Accordion: https://getbootstrap.com/docs/5.3/components/accordion/
|
||
- Laravel Policies: https://laravel.com/docs/12.x/authorization#creating-policies
|