# 📋 Задачи: Динамическая генерация расписания > Декомпозиция [`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: Формат (Очно/Онлайн) - [ ] Визуальное предупреждение при конфликтах (аудитория/преподаватель уже заняты) - [ ] Удаление правила с подтверждением