19 KiB
Концепция динамической генерации расписания
Данный документ представляет собой подробное архитектурное описание новой системы управления расписанием. Система переходит от статического хранения каждой отдельной пары к параметрическому: мы сохраняем правила проведения дисциплины и календарную сетку, а фактическое расписание на любую дату вычисляется «на лету» (генерируется).
1. Подробное описание компонентов системы
Новая архитектура строится на строгом разделении данных на три логических слоя: Календарь (основа отсчета времени), Правила (шаблоны занятий) и Генератор (движок рендеринга фактического расписания).
1.1 Справочная база времени (Календарный учебный график)
Чтобы система понимала, когда можно ставить пары, а когда нет, вводится понятие календарного графика. Он состоит из трех взаимосвязанных сущностей:
- Академические периоды (Учебные года и Семестры). Мы задаем жесткую дату начала каждого семестра и года (например,
02.09.2024). Именно от этой точки отсчитывается "Неделя 1", которая по умолчанию классифицируется как первая нечетная (верхняя). Это избавляет систему от уязвимостей, связанных с плавающими днями начала учебы, високосными годами и смещениями дней недели. - Справочник исключений (Праздники и Выходные). В этой таблице хранятся конкретные даты
YYYY-MM-DD, когда университет юридически или физически закрыт (например, государственные праздники). Если по правилу пара должна быть в этот день, алгоритм будет знать, что её нужно пропустить без штрафов и ошибок. - Матрица учебного графика. Это цифровая копия эксель-таблицы (Курс/Специальность -> Номер недели -> Тип деятельности). Типы могут включать
THEORY(Теория, пары идут в штатном режиме),EXAM(Э - экзаменационная сессия),VACATION(К - каникулы),PRACTICE(У, П - практика). Если, например, у 3-го курса на 18-й неделе стоит статусEXAM, алгоритм даже не будет пытаться генерировать для них теоретические лекции, а отобразит блок «Экзаменационная сессия».
1.2 Движок правил (Schedule Rules)
Старый подход подразумевал, что каждая пара в базе (каждая клеточка) это изолированная запись lessons ("понедельник, 1-я пара, математика"). Новая система вводит сущность сводного Правила Дисциплины. Одно правило описывает расписание целого курса по конкретному предмету для конкретной студенческой группы.
Базовые параметры (Лимиты Правила):
subject_id— ID преподаваемой дисциплины.group_id— ID группы (или потока).startDate— Дата или номер недели, с которой предмет начинает читаться (поскольку не все предметы идут строго с 1-й недели семестра).totalHours— Полный объем выделенных академических часов. Это важнейший лимитатор, который обеспечивает автоматическую остановку генерации: как только заявленные часы будут вычитаны, предмет перестает отображаться в расписании студентов на последующих неделях.
Массив паттернов (Слоты правила): Само "тело" правила разбивается на подчиненные слоты. Если предмет идет в Пн и Ср, это будет 2 слота внутри одного Правила. Слот содержит:
dayOfWeek: день недели (1-7, Пн-Вс).parity: тип четности оборачивания (1 - каждую неделю, 2 - по четным/нижняя, 3 - по нечетным/верхняя).lessonOrder: порядковый номер пары в дне (1, 2, 3 и т.д.).subgroup_id: флаг подгруппы (Вся группа / Подгруппа A / Подгруппа B). Это гарантирует, что мы сможем ставить разным подгруппам пересекающиеся занятия в разных аудиториях без алгоритмических конфликтов.
1.3 Генератор (Рендерер) расписания
Это слой бизнес-логики (служба ScheduleGeneratorService в Java), который работает исключительно в оперативной памяти бэкенда и производит расчет расписания "on-demand" (по требованию) при запросе от клиента фронтенда.
Пошаговый алгоритм работы генератора:
- Фронтенд (Интерфейс пользователя) запрашивает: "Дай мне расписание группы ИТ-21 на конкретный период, например, с 14 октября по 20 октября".
- Генератор вычисляет, что 14 октября соответствует, к примеру, 7-й неделе семестра (вычисление от
startDateкалендаря). - Он сверяется с Матрицей учебного графика. Если у ИТ-21 сейчас стоит
VACATION(Каникулы) илиPRACTICE(Практика, выходящая за пределы ВУЗа), он сразу возвращает пустой ответ или ответ со статусом периода. - Если статус недели позволяет проводить занятия (
THEORY), генератор поднимает из Базы Данных все активные Правила для группы ИТ-21. - Механика Лимитатора часов: Для каждого правила алгоритм "симулирует" или "проматывает" прогон времени с даты старта этого правила до текущей запрошенной недели. Он вычитает успешно проведенные часы, игнорируя даты, попавшие в справочник праздников.
- Если у предмета лимит
totalHoursдостиг значения0, программа понимает, что курс вычитан, и предмет не отображается. Если часы еще остались, алгоритм проецирует шаблоны (слоты правила) на запрошенную текущую неделю с учетом четности, аудиторий и подгрупп, отдавая готовый JSON массив в браузер пользователя.
2. Архитектурные Решения и Открытые Вопросы
На основе обсуждений были задокументированы следующие концептуальные решения по архитектуре:
Утвержденные решения
- Реакция на праздники (Динамическое вытеснение / Сдвиг):
Решение: Алгоритм должен переносить пару, пара в праздничный день не сгорает.
Алгоритм будет воспринимать праздник просто как "пропуск хода", не отнимая проведенные часы от
totalHours. Соответственно, предмет будет автоматически забирать время со смещением в будущее (продлевая дату окончания вычитки), пока преподаватель честно не выработает положенный объем часов. - Массивы vs Связанные таблицы (Использование строгой Нормализации БД):
Решение: Мы делаем нормализацию через связанные таблицы в PostgreSQL, так как это правильнее с архитектурной точки зрения.
Мы не будем использовать сырые массивы (типа
INTEGER[]) или JSONB колонки внутри одной строки с правилом. Будет реализована структура отношения "One-to-Many":- Главная таблица:
schedule_rules(хранит лимиты и даты старта). - Подчиненная таблица:
schedule_rule_slots(хранит конкретный день, четность, номер пары, прикрепленные к ID главного правила через Foreign Key). Это позволит базе данных легко и быстро строить сложные выборки в стиле "Покажи загруженность кабинета №21 во вторник на второй паре по четным неделям", исключая тяжелый парсинг JSON.
- Главная таблица:
- Поддержка Подгрупп внутри слотов:
Решение: Модель обязательно должна учитывать деление на подгруппы.
В таблицы параметров слотов (
schedule_rule_slots) и в бизнес-логику генератора будет введено полеsubgroup_id(Id подгруппы). Алгоритм генератора сможет рендерить два предмета для одной группы одновременно и без конфликтов, если они ассоциированы с разными подгруппами одной материнской группы.
Открытые вопросы (Требуют обсуждения с коллегами)
- Смена аудиторий и преподавателей в рамках одного Правила:
Вопрос: Как хранить в базе ситуацию, когда у предмета "Программирование" лекции в понедельник читает лектор Иванов (Аудитория 100), а лабораторные в среду ведет практик Петров (Аудитория 102В)?
- Вариант А (независимые правила): Считать их абсолютно разными независимыми Правилами. Одно правило — на лекции Иванова (со своим собственным лимитом лекционных часов
totalHours), другое правило — на лабораторные Петрова (со своим лимитом практических часов). У каждого — свои слоты. - Вариант Б (слоты с обогащенным контекстом): Считать это одним большим Правилом по предмету "Программирование", но при этом хранить
teacher_id,lesson_formatиclassroom_idне в главной строке Правила, а в каждой строке параметрических Слотов (schedule_rule_slots). Таким образом расходуя общийtotalHoursдисциплины. Статус: Открытый архитектурный вопрос. Решение будет принято после обсуждения.
- Вариант А (независимые правила): Считать их абсолютно разными независимыми Правилами. Одно правило — на лекции Иванова (со своим собственным лимитом лекционных часов
3. Подробный План Действий по Реализации
Интеграция новой архитектуры затронет весь стек приложения (DB -> Backend -> API -> Frontend). Работу предлагается вести строго поэтапно:
Этап 1. База Данных (Flyway Миграции и Дата-Скрипты)
- Схема Календарного графика:
academic_years(id, title, start_date, end_date).holidays(id, date, academic_year_id, description).academic_calendar_matrix(id, academic_year_id, target_group_id, week_number, activity_type_enum).
- Схема Движка Правил:
schedule_rules(id, subject_id, target_group_id, active_from_date, total_academic_hours).schedule_rule_slots(id, schedule_rule_id, day_of_week, parity_type, lesson_order_id, subgroup_id, [аудитория_id?, преподаватель_id? зависит от решения открытого вопроса]).
- Скрипт Миграции (Data ETL): Написание SQL/Java скрипта, который вычитает "старые" статические записи из таблицы
lessons, скомпонует их по логическим правилам и перенесет в новые таблицыschedule_rules, чтобы не потерять текущую информацию. Только после этого можно удалять старую таблицуlessons.
Этап 2. Бэкенд и Вычислительное Ядро (Java + Spring Boot)
AcademicDateService.java— сервис утилит для календарной математики (перевод дат в недели, получение типа четности недели, проверка попадания дня в справочникholidays).ScheduleRuleRepository.java— JPA репозитории для извлечения графа правил из базы данных, с оптимизацией N+1 проблемы через JOIN со слотами.ScheduleGeneratorService.java— Сердце системы. Основной метод:List<RenderedLesson> buildSchedule(Group group, LocalDate startDate, LocalDate endDate). Реализует всю бизнес-логику из пункта 1.3 (вычитание часов, пропуск праздников).- Адаптация валидаторов пересечения аудиторий: теперь валидатор должен работать не на уровне "каждой пары", а симулировать весь семестр на этапе сохранения нового Правила в панели администратора.
Этап 3. Обновление REST API (Контроллеры)
- Эндпоинт получения расписания (
GET /api/schedule) нужно перевести на диапазонную модель. Теперь он должен принимать параметры:?groupId=123&startDate=2024-10-14&endDate=2024-10-20. Ответ сервера должен представлять собой массив объектов с закрепленными полными датамиYYYY-MM-DDвместо абстрактных названий дней недели. - Создание CRUD-контроллеров для админки панели управления:
/api/admin/calendar/matrix(настройка каникул и сессий по курсам/неделям)./api/admin/calendar/holidays(добавление исключений)./api/admin/schedule-rules(управление жизненным циклом Правил и их слотами).
Этап 4. Интерфейсы Frontend (Vanilla JS + HTML)
- Страницы просмотра (Студенты и Преподаватели):
- Реализация переключателя календарных дат (Date Picker или кнопки-перелистывания недель).
- Логика, которая при свайпе или клике запрашивает у API конкретный диапазон дат и перерисовывает DOM дерево.
- Панель Администратора (SPA интерфейсы):
- Вкладка "Учебный график": Визуальная сетка-матрица (52 недели по горизонтали, Курсы/Специальности по вертикали), где админ может закрашивать пересечения разными цветами, назначая статусы (Практика, Каникулы, Теория).
- Вкладка "Конструктор Правил": Глобально новый визуальный инструмент расписания. Админ выбирает Группу и Дисциплину, задает
totalHours, а затем динамически добавляет строчки массива слотов через кнопу "Добавить занятие" со списками (Selects) для Дня Недели, Времени, Подгруппы и Четности.