15 KiB
Реализация динамического расписания
Этот файл фиксирует текущий результат внедрения динамической системы расписания: что уже добавлено в проект, как связаны база данных, backend, API и frontend, как работает генератор расписания и какие ограничения остаются для следующего этапа.
Статус внедрения
Реализован первый рабочий срез динамического расписания. Новая модель теперь является базовой схемой проекта и создаётся сразу в V1__init.sql.
Старые таблицы lessons и schedule_data, старые контроллеры, DTO, модели, репозитории и админские вызовы старых API удалены. Новая модель расписания является единственным базовым способом хранения и генерации расписания.
Что добавлено
База данных
Динамическая схема встроена в базовую Flyway-миграцию V1__init.sql.
Она создаёт следующие таблицы:
| Таблица | Назначение |
|---|---|
time_slots |
Настраиваемая сетка пар: номер, время начала, время окончания, длительность |
academic_years |
Учебные годы |
semesters |
Семестры учебного года, от start_date считается первая неделя |
holidays |
Праздники и исключённые даты, когда занятия не проводятся |
academic_calendar_matrix |
Матрица учебного графика по семестру, курсу, специальности и неделе |
schedule_rules |
Правила проведения дисциплины с лимитом академических часов |
schedule_rule_groups |
Привязка правила к одной или нескольким группам |
schedule_rule_slots |
Конкретные шаблонные слоты правила: день, чётность, пара, преподаватель, аудитория, тип и формат |
Миграция также создаёт тестовые данные сразу в новой модели:
- Создаёт базовые временные слоты.
- Создаёт учебные годы
2024-2025и2025-2026. - Заполняет матрицу учебного графика базовым статусом
THEORY. - Создаёт тестовые
schedule_rulesдля групп и дисциплин. - Создаёт привязки групп в
schedule_rule_groups, включая потоковую лекцию. - Создаёт шаблонные занятия в
schedule_rule_slots.
Backend
Добавлены JPA-модели для новой схемы:
TimeSlotAcademicYearSemesterHolidayAcademicCalendarMatrixScheduleRuleScheduleRuleSlotAcademicActivityTypeScheduleParity
Добавлены DTO для API:
TimeSlotDtoAcademicYearDtoSemesterDtoHolidayDtoAcademicCalendarMatrixDtoScheduleRuleDtoScheduleRuleSlotDtoRenderedLessonDto
Добавлены репозитории для новых таблиц:
TimeSlotRepositoryAcademicYearRepositorySemesterRepositoryHolidayRepositoryAcademicCalendarMatrixRepositoryScheduleRuleRepositoryScheduleRuleSlotRepository
Добавлены сервисы:
| Сервис | Назначение |
|---|---|
AcademicDateService |
Календарная математика: семестр по дате, номер недели, чётность, праздник, курс группы, тип активности |
ScheduleGeneratorService |
Генерация расписания группы или преподавателя на диапазон дат |
Старый ручной слой занятий удалён: в backend больше нет LessonsController, ScheduleDataController, сущностей Lesson/ScheduleData, их DTO, репозиториев и валидаторов старых строк расписания.
API
Добавлен новый endpoint просмотра расписания:
GET /api/schedule?groupId=1&startDate=2026-04-27&endDate=2026-05-03
GET /api/schedule?teacherId=2&startDate=2026-04-27&endDate=2026-05-03
Правило запроса: нужно передать ровно один идентификатор — groupId или teacherId.
Ответ — массив RenderedLessonDto, где у каждого занятия есть:
- полная дата занятия;
- номер дня недели;
- название дня;
- номер недели семестра;
- чётность;
- временной слот;
- дисциплина;
- преподаватель;
- аудитория;
- тип и формат занятия;
- группы;
- лимит часов правила;
- уже списанные часы перед занятием;
- остаток часов после занятия.
Добавлены административные API:
| API | Назначение |
|---|---|
/api/admin/time-slots |
CRUD временных слотов |
/api/admin/calendar/years |
CRUD учебных годов |
/api/admin/calendar/years/{academicYearId}/semesters |
Семестры учебного года |
/api/admin/calendar/semesters/{id} |
Обновление семестра |
/api/admin/calendar/holidays |
CRUD праздников |
/api/admin/calendar/matrix |
Получение и массовое сохранение матрицы учебного графика |
/api/admin/schedule-rules |
CRUD правил расписания |
Авторизация
Ответ POST /api/auth/login расширен полем userId.
Frontend сохраняет его в localStorage, чтобы кабинет преподавателя мог запрашивать личное расписание через:
GET /api/schedule?teacherId={userId}&startDate=...&endDate=...
Frontend
Заглушки кабинетов заменены на базовые экраны просмотра расписания.
Админский экран schedule показывает текущие schedule_rules и сетку time_slots через новые admin API.
Кабинет студента
Файл: frontend/student/index.html.
Текущая модель пользователя-студента не содержит прямой связи с учебной группой, поэтому экран работает через выбор группы:
- Загружает список групп из
/api/groups. - Сохраняет выбранную группу в
localStorage.studentGroupId. - Строит недельный диапазон дат.
- Запрашивает расписание через
GET /api/schedule?groupId=.... - Показывает занятия по дням недели.
Кабинет преподавателя
Файл: frontend/teacher/index.html.
Экран использует localStorage.userId, который появляется после нового ответа авторизации:
- Определяет текущую неделю.
- Позволяет переключать неделю стрелками или через выбор даты.
- Запрашивает расписание через
GET /api/schedule?teacherId=.... - Показывает занятия преподавателя в недельной сетке.
- В занятии отображаются дисциплина, время, тип, аудитория и группы.
Как работает генерация расписания
Генерация выполняется на backend по запросу клиента. Фактические занятия не хранятся отдельными строками на каждую дату, а вычисляются из правил.
Расписание группы
Алгоритм buildScheduleForGroup(groupId, startDate, endDate):
- Проверяет корректность диапазона дат.
- Находит группу.
- Для каждой даты диапазона определяет семестр.
- Вычисляет номер недели семестра.
- Вычисляет текущий курс группы по
year_start_study. - Проверяет матрицу учебного графика.
- Пропускает дату, если это праздник или не
THEORY. - Загружает правила группы для найденного семестра.
- Для каждого правила считает, сколько академических часов уже проведено до текущей даты.
- Если лимит
total_academic_hoursещё не исчерпан, проецирует подходящие слоты правила на текущую дату.
Расписание преподавателя
Алгоритм buildScheduleForTeacher(teacherId, startDate, endDate) похож, но правила ищутся не по группе, а по преподавателю в schedule_rule_slots.teacher_id.
В ответе занятие дополнительно содержит все группы, привязанные к правилу. Это поддерживает потоковые лекции.
Чётность недели
Чётность вычисляется от semesters.start_date.
Настройка:
app.schedule.even-week-is-upper=false
По умолчанию:
- нечётная неделя семестра соответствует
ODD; - чётная неделя семестра соответствует
EVEN.
Если app.schedule.even-week-is-upper=true, соответствие инвертируется.
Праздники
Праздник считается пропуском занятия:
- занятие не отображается в расписании;
- академические часы не списываются;
- дисциплина фактически продолжается дольше, пока не будет исчерпан лимит часов.
Лимит часов
Каждый schedule_rules.total_academic_hours задаёт общий лимит академических часов дисциплины.
Генератор симулирует период от active_from_date до запрошенной даты и считает только реально проведённые слоты:
- один слот = 2 академических часа;
- праздники не считаются;
- недели с активностью не
THEORYне считаются; - после достижения лимита правило перестаёт выводиться.
Поток данных
Новая базовая БД сразу создаёт рабочую модель так:
V1__init.sql
-> time_slots
-> academic_years / semesters / holidays / academic_calendar_matrix
-> schedule_rules
-> schedule_rule_groups
-> schedule_rule_slots
Для просмотра:
frontend
-> GET /api/schedule
-> ScheduleController
-> ScheduleGeneratorService
-> schedule_rules + schedule_rule_slots + calendar tables
-> RenderedLessonDto[]
Что проверено
Проверка backend:
docker run --rm -v /mnt/HDD/magistr/magistr/backend:/app -w /app maven:3.9-eclipse-temurin-17 mvn -q -DskipTests compile
Результат: компиляция backend прошла успешно.
Проверка миграции:
- Поднят временный PostgreSQL-контейнер на образе
postgres:alpine3.23. - Применён
V1__init.sql. - Проверены контрольные количества строк новой модели.
Контрольный результат:
| Объект | Количество |
|---|---|
schedule_rules |
4 |
schedule_rule_groups |
5 |
schedule_rule_slots |
4 |
time_slots |
7 |
academic_calendar_matrix |
132 |
lessons |
таблица отсутствует |
schedule_data |
таблица отсутствует |
Также выполнен:
git diff --check
Результат: замечаний по whitespace нет.
Ограничения текущего среза
Этот срез создаёт рабочее ядро динамического расписания, но ещё не закрывает весь продуктовый объём.
Ограничения:
- строгая проверка конфликтов преподавателей, аудиторий и групп при сохранении правил ещё не реализована;
- студент пока выбирает группу вручную, потому что в модели пользователя нет связи
student -> group.
Следующий этап
Рекомендуемый следующий этап:
- Реализовать backend-валидацию конфликтов:
- преподаватель не может вести разные занятия одновременно;
- аудитория не может быть занята двумя разными занятиями одновременно;
- группа не может быть на двух занятиях одновременно, кроме корректного деления по подгруппам.
- Добавить модель связи студента с учебной группой, чтобы кабинет студента не требовал ручного выбора группы.
- Добавить отдельное управление подгруппами и подключить выбор
subgroupIdв конструкторе правил.
Связанные документы
SCHEDULE_PROPOSAL.md— исходная концепция динамического расписания.SCHEDULE_TASKS.md— исходная декомпозиция задач.docs/API.md— описание REST API.docs/DATABASE.md— описание схемы БД.docs/BUSINESS_LOGIC.md— бизнес-правила.docs/FRONTEND.md— frontend-архитектура.