219 lines
14 KiB
Markdown
219 lines
14 KiB
Markdown
# 📋 Задачи: Динамическая генерация расписания
|
||
|
||
> Декомпозиция [`SCHEDULE_PROPOSAL.md`](SCHEDULE_PROPOSAL.md) на подзадачи для доски планирования.
|
||
> Категории: **Backend**, **Frontend**, **DevOps/DB**
|
||
|
||
---
|
||
|
||
## DevOps / Database
|
||
|
||
### Flyway-миграция: Временные слоты
|
||
- [ ] Создать миграцию: таблица `time_slots` (id, order_number, start_time, end_time, duration_minutes)
|
||
- [ ] Добавить CHECK-ограничения (start_time < end_time, duration_minutes > 0, order_number > 0)
|
||
|
||
---
|
||
|
||
### Flyway-миграция: Учебные годы и семестры
|
||
- [ ] Создать миграцию: таблица `academic_years` (id, title, start_date, end_date)
|
||
- [ ] Создать миграцию: таблица `semesters` (id, academic_year_id FK, semester_type ENUM, start_date, end_date)
|
||
- [ ] Добавить CHECK-ограничения и индексы
|
||
|
||
---
|
||
|
||
### Flyway-миграция: Праздники
|
||
- [ ] Создать миграцию: таблица `holidays` (id, date, academic_year_id FK, description)
|
||
- [ ] Добавить уникальный индекс на (date, academic_year_id)
|
||
|
||
---
|
||
|
||
### Flyway-миграция: Матрица учебного графика
|
||
- [ ] Создать миграцию: таблица `academic_calendar_matrix` (id, semester_id FK, course_number, specialty_id FK, week_number, activity_type ENUM)
|
||
- [ ] Добавить ENUM: `THEORY`, `EXAM`, `VACATION`, `PRACTICE`
|
||
- [ ] Добавить уникальный индекс на (semester_id, course_number, specialty_id, week_number)
|
||
|
||
---
|
||
|
||
### Flyway-миграция: Правила расписания
|
||
- [ ] Создать миграцию: таблица `schedule_rules` (id, subject_id FK, semester_id FK, active_from_date, total_academic_hours)
|
||
- [ ] Создать миграцию: связующая таблица `schedule_rule_groups` (schedule_rule_id FK, group_id FK, PK составной)
|
||
- [ ] Создать миграцию: таблица `schedule_rule_slots` (id, schedule_rule_id FK, day_of_week, parity ENUM, time_slot_id FK, subgroup_id FK NULL, teacher_id FK, classroom_id FK, lesson_type_id FK, lesson_format)
|
||
- [ ] Добавить CHECK на day_of_week (1–7)
|
||
- [ ] Добавить ENUM: `BOTH`, `EVEN`, `ODD`
|
||
|
||
---
|
||
|
||
### ETL-миграция данных
|
||
- [ ] Написать SQL/Java скрипт миграции `schedule_data` → `schedule_rules` + `schedule_rule_groups`
|
||
- [ ] Маппинг `number_of_hours` → `total_academic_hours`
|
||
- [ ] Маппинг привязок групп
|
||
- [ ] Написать SQL/Java скрипт миграции `lessons` → `schedule_rule_slots`
|
||
- [ ] Трансформация `day` (строка) → `day_of_week` (INT 1–6)
|
||
- [ ] Трансформация `time` (строка) → `time_slot_id` (FK)
|
||
- [ ] Трансформация `week` (строка) → `parity` (ENUM)
|
||
- [ ] Группировка записей с одинаковым (subject_id, group_id) в одно правило
|
||
- [ ] Верификация мигрированных данных (количество записей, целостность FK)
|
||
- [ ] Создать миграцию на удаление устаревших таблиц `lessons` и `schedule_data` (после верификации)
|
||
|
||
---
|
||
|
||
## Backend (Java + Spring Boot)
|
||
|
||
### JPA-сущности (Model)
|
||
- [ ] Создать Entity: `TimeSlot`
|
||
- [ ] Создать Entity: `AcademicYear`
|
||
- [ ] Создать Entity: `Semester` (связь ManyToOne → AcademicYear)
|
||
- [ ] Создать Entity: `Holiday` (связь ManyToOne → AcademicYear)
|
||
- [ ] Создать Entity: `AcademicCalendarMatrix` (связи на Semester, Specialty)
|
||
- [ ] Создать Entity: `ScheduleRule` (связи на Subject, Semester)
|
||
- [ ] Создать Entity: `ScheduleRuleSlot` (связи на ScheduleRule, TimeSlot, Teacher, Classroom, LessonType)
|
||
- [ ] Настроить ManyToMany-связь ScheduleRule ↔ StudentGroup через `schedule_rule_groups`
|
||
|
||
---
|
||
|
||
### DTO
|
||
- [ ] Создать DTO: `TimeSlotDto`
|
||
- [ ] Создать DTO: `AcademicYearDto`, `SemesterDto`
|
||
- [ ] Создать DTO: `HolidayDto`
|
||
- [ ] Создать DTO: `AcademicCalendarMatrixDto`
|
||
- [ ] Создать DTO: `ScheduleRuleDto`, `ScheduleRuleSlotDto`
|
||
- [ ] Создать DTO: `RenderedLessonDto` (ответ генератора расписания)
|
||
|
||
---
|
||
|
||
### Repository
|
||
- [ ] Создать `TimeSlotRepository`
|
||
- [ ] Создать `AcademicYearRepository`
|
||
- [ ] Создать `SemesterRepository` (метод findByDateRange)
|
||
- [ ] Создать `HolidayRepository` (метод findByAcademicYearId)
|
||
- [ ] Создать `AcademicCalendarMatrixRepository` (метод findBySemesterAndCourseAndSpecialty)
|
||
- [ ] Создать `ScheduleRuleRepository` с JOIN FETCH (решение N+1 проблемы)
|
||
- [ ] Метод: findByGroupIdAndSemesterId (через schedule_rule_groups)
|
||
- [ ] Метод: findByTeacherIdAndSemesterId (через schedule_rule_slots.teacher_id)
|
||
|
||
---
|
||
|
||
### Сервис: AcademicDateService
|
||
- [ ] Метод: перевод произвольной даты → номер недели семестра
|
||
- [ ] Метод: определение чётности недели с учётом настройки тенанта
|
||
- [ ] Метод: проверка попадания даты в справочник `holidays`
|
||
- [ ] Метод: вычисление текущего курса группы (`текущий_учебный_год - year_start_study + 1`)
|
||
- [ ] Метод: определение семестра по дате
|
||
- [ ] Написать юнит-тесты для AcademicDateService
|
||
|
||
---
|
||
|
||
### Сервис: ScheduleGeneratorService
|
||
- [ ] Метод: `buildScheduleForGroup(groupId, startDate, endDate)` — расписание группы
|
||
- [ ] Определение семестра по диапазону дат
|
||
- [ ] Вычисление номера недели и курса группы
|
||
- [ ] Проверка типа деятельности через матрицу графика
|
||
- [ ] Загрузка активных правил для группы
|
||
- [ ] Симуляция прогона часов (подсчёт consumed_hours)
|
||
- [ ] Пропуск праздников при подсчёте часов
|
||
- [ ] Проекция слотов на запрошенную неделю с учётом чётности и подгрупп
|
||
- [ ] Метод: `buildScheduleForTeacher(teacherId, startDate, endDate)` — расписание преподавателя
|
||
- [ ] Поиск правил по teacher_id в слотах
|
||
- [ ] Обогащение ответа списком групп из schedule_rule_groups
|
||
- [ ] Написать юнит-тесты для ScheduleGeneratorService
|
||
- [ ] Написать интеграционные тесты (полный цикл с тестовой БД)
|
||
|
||
---
|
||
|
||
### Кеширование
|
||
- [ ] Реализовать кеш списка праздников по учебному году
|
||
- [ ] Реализовать кеш матрицы учебного графика по ключу (course, specialty_id, semester_id)
|
||
- [ ] Реализовать кеш consumed_hours для каждого правила
|
||
- [ ] Реализовать инвалидацию кеша праздников при CRUD-операциях с holidays
|
||
- [ ] Реализовать инвалидацию кеша consumed_hours при изменении правил или праздников
|
||
|
||
---
|
||
|
||
### Валидация
|
||
- [ ] Адаптировать валидатор пересечения аудиторий (симуляция всего семестра при сохранении правила)
|
||
- [ ] Валидация пересечения преподавателей (один преподаватель не может вести две пары одновременно)
|
||
- [ ] Валидация пересечения групп (одна группа не может быть на двух занятиях одновременно, кроме подгрупп)
|
||
|
||
---
|
||
|
||
### REST API: Контроллеры
|
||
- [ ] `GET /api/schedule` — Новый эндпоинт расписания (параметры: groupId/teacherId + startDate + endDate)
|
||
- [ ] Пометить `GET /api/users/lessons` как `@Deprecated` (обратная совместимость)
|
||
- [ ] CRUD: `POST/GET/PUT/DELETE /api/admin/time-slots`
|
||
- [ ] CRUD: `POST/GET/PUT/DELETE /api/admin/calendar/years`
|
||
- [ ] CRUD: `GET/PUT /api/admin/calendar/semesters` (вложены в years)
|
||
- [ ] CRUD: `POST/GET/PUT/DELETE /api/admin/calendar/holidays`
|
||
- [ ] CRUD: `GET/PUT /api/admin/calendar/matrix` (массовое сохранение матрицы)
|
||
- [ ] CRUD: `POST/GET/PUT/DELETE /api/admin/schedule-rules`
|
||
- [ ] Включая вложенные слоты и привязку групп
|
||
- [ ] Написать интеграционные тесты для API
|
||
|
||
---
|
||
|
||
### Удаление устаревшего кода
|
||
- [ ] Удалить/рефакторить старый `LessonsController` (после миграции фронтенда)
|
||
- [ ] Удалить/рефакторить старый `ScheduleDataController`
|
||
- [ ] Удалить старые Entity: `Lesson`, `ScheduleData`
|
||
- [ ] Удалить старые Repository и Service для lessons/schedule_data
|
||
|
||
---
|
||
|
||
## Frontend (Vanilla JS + HTML/CSS)
|
||
|
||
### Просмотр расписания: Студенты
|
||
- [ ] Реализовать переключатель дат (Date Picker / кнопки-стрелки по неделям)
|
||
- [ ] Переключить API-запросы на новый `GET /api/schedule?groupId=...&startDate=...&endDate=...`
|
||
- [ ] Рендеринг расписания по дням и временным слотам
|
||
- [ ] Отображение статуса периода (Каникулы / Практика / Экзамены), если неделя не учебная
|
||
- [ ] Отображение информации о подгруппах (два занятия рядом для разных подгрупп)
|
||
|
||
---
|
||
|
||
### Просмотр расписания: Преподаватели
|
||
- [ ] Реализовать переключатель дат (Date Picker / кнопки-стрелки по неделям)
|
||
- [ ] Переключить API-запросы на новый `GET /api/schedule?teacherId=...&startDate=...&endDate=...`
|
||
- [ ] Отображение всех групп, привязанных к каждому занятию
|
||
- [ ] Отображение подгрупп, если преподаватель ведёт у подгруппы
|
||
|
||
---
|
||
|
||
### Панель администратора: Вкладка «Временные слоты»
|
||
- [ ] Создать UI-страницу настройки временных слотов
|
||
- [ ] CRUD-интерфейс: добавление/редактирование/удаление пар
|
||
- [ ] Отображение таблицы: номер пары → время начала → время окончания → длительность
|
||
- [ ] Валидация на фронтенде (пересечение времён, корректность данных)
|
||
|
||
---
|
||
|
||
### Панель администратора: Вкладка «Учебный график»
|
||
- [ ] Создать UI: выбор учебного года и семестра
|
||
- [ ] Создать UI: CRUD учебных годов и семестров
|
||
- [ ] Создать UI: CRUD праздников (список дат с описанием)
|
||
- [ ] Создать визуальную сетку-матрицу:
|
||
- [ ] Горизонтальная ось — номера недель
|
||
- [ ] Вертикальная ось — Курс + Специальность
|
||
- [ ] Цветовая кодировка ячеек: Теория/Экзамены/Каникулы/Практика
|
||
- [ ] Клик/драг для массового назначения статуса
|
||
- [ ] Сохранение матрицы через API `PUT /api/admin/calendar/matrix`
|
||
|
||
---
|
||
|
||
### Панель администратора: Вкладка «Конструктор Правил»
|
||
- [ ] Создать UI: список существующих правил с фильтрацией (по группе, предмету, семестру)
|
||
- [ ] Форма создания/редактирования правила:
|
||
- [ ] Мультиселект групп (для потоковых лекций)
|
||
- [ ] Выбор дисциплины (subject)
|
||
- [ ] Выбор семестра
|
||
- [ ] Ввод totalHours (академические часы)
|
||
- [ ] Ввод даты начала (active_from_date)
|
||
- [ ] Динамический массив слотов (кнопка «Добавить занятие»):
|
||
- [ ] Select: День недели
|
||
- [ ] Select: Временной слот (из таблицы time_slots)
|
||
- [ ] Select: Чётность (Обе/Чётная/Нечётная)
|
||
- [ ] Select: Подгруппа (опционально)
|
||
- [ ] Select: Преподаватель
|
||
- [ ] Select: Аудитория
|
||
- [ ] Select: Тип занятия (Лекция/Практика/Лаба)
|
||
- [ ] Select: Формат (Очно/Онлайн)
|
||
- [ ] Визуальное предупреждение при конфликтах (аудитория/преподаватель уже заняты)
|
||
- [ ] Удаление правила с подтверждением
|