From 48e8d4e6315abf7cad5dab6637ce30b94c4ded88 Mon Sep 17 00:00:00 2001 From: Zuev Date: Tue, 7 Apr 2026 17:42:58 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D1=84=D0=B0=D0=B9=D0=BB=20=D1=81=20=D0=B7=D0=B0=D0=B4=D0=B0?= =?UTF-8?q?=D1=87=D0=B0=D0=BC=D0=B8=20=D0=BF=D0=BE=20=D0=B4=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=D0=BC=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=BE=D0=B9=20=D0=B3?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BD=D1=8F=D1=82=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SCHEDULE_TASKS.md | 218 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 SCHEDULE_TASKS.md diff --git a/SCHEDULE_TASKS.md b/SCHEDULE_TASKS.md new file mode 100644 index 0000000..30c600d --- /dev/null +++ b/SCHEDULE_TASKS.md @@ -0,0 +1,218 @@ +# 📋 Задачи: Динамическая генерация расписания + +> Декомпозиция [`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: Формат (Очно/Онлайн) +- [ ] Визуальное предупреждение при конфликтах (аудитория/преподаватель уже заняты) +- [ ] Удаление правила с подтверждением