Files
magistr/SUPERVISOR_TASKS_IMPLEMENTATION_PLAN.md
2026-05-19 15:02:07 +03:00

32 KiB
Raw Blame History

План реализации задач после созвона с научным руководителем

Дата: 2026-05-19

1. Короткий вывод

В проекте уже есть основа для большей части требований: справочники, пользователи, кафедры, дисциплины, аудитории, динамическое расписание на базе schedule_rules, календарный учебный график, временные сетки и экран загруженности аудиторий. Главные недостающие архитектурные слои:

  • нормальная ролевая модель и серверная авторизация;
  • темпоральность: история принадлежности, версионирование связей и запрет ретроактивного изменения прошлого расписания;
  • мягкое удаление справочников вместо физического удаления;
  • отдельные рабочие места для кафедры и учебного отдела;
  • расширенные просмотры расписания и загруженности по преподавателям, аудиториям, кафедрам, парам и периодам.

Рекомендуемый порядок: сначала ввести темпоральный слой, жизненный цикл записей и роли, затем уже строить новые кабинеты и редакторы. Если начать с экранов, старые связи будут продолжать теряться при удалениях, переносах преподавателей и правках расписания.

2. Расшифровка задач руководителя в терминах системы

Формулировка Что реализуем
Загруженность по паре или преподавателю Аналитика занятости по временным слотам, преподавателям, аудиториям, кафедрам, дисциплинам и периодам.
Темпоральные базы данных Не отдельная БД, а темпоральная модель внутри текущей PostgreSQL БД каждого тенанта: периоды действия записей, история связей, версионирование расписания.
Сделать удаление аудиторий и т.д., чтобы нельзя было использовать в дальнейшем Мягкое удаление/архивация справочников. Исторические записи остаются, новые назначения на архивные сущности запрещены.
Перенос преподавателей с одной кафедры в другую без потери связи с прошлыми записями История принадлежности преподавателя к кафедрам через интервалы действия, а не простое перезаписывание users.department_id.
Уровни пользователей Расширение ролей и прав: администратор, кафедра, учебный отдел, преподаватель, студент. Проверки должны быть на backend, не только в UI.
Администратор: создание аудиторий, специальностей, кафедр и т.д. Довести справочники до полного CRUD + архив/восстановление + фильтр архивных записей.
Кафедра: смотреть предметы кафедры, грузить, связывать предметы, разделять, комментарии, расписания и загруженность Новый кабинет кафедры: дисциплины кафедры, импорт, описание дисциплин, привязки преподавателей и типов занятий, комментарии, расписание и нагрузка кафедры.
Учебный отдел: закреплять время и аудитории, редактировать расписание, закреплять предметы и преподавателей в аудитории в определённое время Новый кабинет учебного отдела: редактор расписания, фиксация времени/аудиторий/преподавателей, точечные переносы и отмены, проверка конфликтов.
Просмотр расписания разных аудиторий, преподавателей, кафедр Универсальный просмотр расписания с фильтрами по аудитории, преподавателю, группе, кафедре, дисциплине и периоду.

3. Текущее состояние проекта

Уже есть

  • Backend: Java 17, Spring Boot, JPA, Flyway, мультитенантность через отдельную БД на тенант.
  • Frontend: Vanilla JS, отдельные интерфейсы /admin/, /teacher/, /student/.
  • Роли сейчас ограничены ADMIN, TEACHER, STUDENT.
  • Авторизация возвращает UUID-токен и роль, но полноценной серверной проверки прав по эндпоинтам пока нет.
  • Динамическое расписание строится из:
    • schedule_rules;
    • schedule_rule_groups;
    • schedule_rule_slots;
    • schedule_rule_slot_subgroups;
    • time_slots;
    • academic_years / semesters;
    • academic_calendars.
  • GET /api/schedule сейчас принимает ровно один фильтр: groupId или teacherId.
  • Экран загруженности аудиторий уже есть и использует реальные аудитории, временные слоты и сгенерированное расписание.
  • У аудиторий есть is_available, но DELETE /api/classrooms/{id} физически удаляет запись.
  • Преподаватель сейчас имеет одно поле users.department_id; история переводов не хранится.
  • Дисциплины связаны с кафедрой через subjects.department_id; история смены кафедры дисциплины не хранится.
  • Привязка преподавателей к дисциплинам есть через teacher_subjects, но это простая текущая связь без периода действия, комментариев и разбиения по ответственности.

Основные проблемы текущей модели

  • Физическое удаление справочников опасно для истории расписания и отчётов.
  • Обновление текущих связей перезаписывает контекст прошлого.
  • Динамическое расписание пересчитывается из текущих правил, поэтому изменение правила может изменить отображение прошлого периода.
  • Ролевая модель не отражает учебный отдел и кафедру как самостоятельных пользователей.
  • Просмотр расписания и загруженности завязан на частные сценарии, а не на единый фильтруемый слой.

4. Целевая архитектурная идея

Ввести три базовых понятия:

  1. Жизненный цикл записи: активна, архивирована, недоступна для новых назначений.
  2. Период действия связи: преподаватель относится к кафедре не "навсегда", а с даты по дату.
  3. Версия расписания: прошлое расписание должно оставаться воспроизводимым, даже если текущие правила изменились.

После этого все новые экраны работают с одной логикой:

  • активные справочники доступны для выбора;
  • архивные записи видны в истории, но не выбираются для будущих назначений;
  • отчёты за прошлый период используют связи, действовавшие на дату занятия;
  • правки будущего расписания не ломают прошлые отчёты.

5. Этап 1. Роли и права доступа

Новые роли

Роль Назначение
ADMIN Полный доступ внутри тенанта: справочники, пользователи, роли, настройки.
EDUCATION_OFFICE Учебный отдел: расписание, аудитории, временные слоты, закрепления, конфликты.
DEPARTMENT Кафедра: дисциплины своей кафедры, преподаватели, комментарии, нагрузка, просмотр расписания.
TEACHER Преподаватель: личное расписание и, позже, заявки на изменения.
STUDENT Просмотр расписания.

ADMIN можно оставить как суперроль внутри одной БД тенанта. Управление тенантами через /api/database стоит разрешить только ADMIN.

Backend

  • Расширить Role.
  • Обновить AuthController.ROLE_REDIRECTS:
    • ADMIN -> /admin/;
    • EDUCATION_OFFICE -> /edu-office/;
    • DEPARTMENT -> /department/;
    • TEACHER -> /teacher/;
    • STUDENT -> /student/.
  • Добавить серверную авторизацию:
    • хранение активных сессий/токенов или проверяемый токен;
    • фильтр/интерцептор, который получает пользователя по Authorization;
    • аннотации или централизованные проверки прав в контроллерах.
  • Не полагаться на localStorage.role как на источник прав.

Frontend

  • Добавить оболочки /department/ и /edu-office/.
  • В админке скрывать пункты меню по роли, но считать это только UX-ограничением.
  • Все ошибки доступа показывать на русском: Недостаточно прав для выполнения операции.

6. Этап 2. Темпоральный слой и жизненный цикл справочников

Базовые поля жизненного цикла

Для справочников, которые могут участвовать в расписании или отчётах, добавить:

  • status (ACTIVE, ARCHIVED);
  • active_from;
  • active_to;
  • archived_at;
  • archived_by;
  • archive_reason.

Минимальный набор для первого внедрения:

  • classrooms;
  • equipments;
  • departments;
  • specialties;
  • specialty_profiles;
  • education_forms;
  • student_groups;
  • subgroups;
  • subjects;
  • users.

Для сущностей, где достаточно простого вывода из эксплуатации, можно начать с status, archived_at, archive_reason, а active_from/active_to добавить там, где требуется отчётность по датам.

Правила использования

  • GET по умолчанию возвращает только активные записи.
  • Для админских экранов добавить includeArchived=true.
  • DELETE заменить на архивирование.
  • Физическое удаление оставить только для записей, которые точно нигде не используются, либо убрать из публичных API.
  • В формах будущего расписания нельзя выбирать архивные аудитории, дисциплины, группы, подгруппы, кафедры и пользователей.
  • В историческом расписании архивные записи отображаются с пометкой, например Аудитория 101 (архив).

Аудитории

is_available и архивирование должны означать разные вещи:

  • is_available=false — аудитория временно недоступна, но может вернуться в работу;
  • status=ARCHIVED — аудитория выведена из эксплуатации и не может использоваться в новых назначениях.

UI-кнопку Удалить лучше заменить на Вывести из эксплуатации. Для администратора можно добавить отдельное действие Восстановить.

7. Этап 3. История переводов преподавателей между кафедрами

Модель данных

Добавить таблицу:

teacher_department_assignments

Поля:

  • id;
  • teacher_id;
  • department_id;
  • valid_from;
  • valid_to;
  • is_primary;
  • comment;
  • created_at;
  • created_by.

Текущий users.department_id можно временно оставить как денормализованное поле "текущая кафедра", но источником правды для отчётов должна стать таблица истории.

Правила

  • У преподавателя должна быть ровно одна основная кафедра на дату.
  • При переводе преподавателя:
    • закрывается старая запись valid_to;
    • создаётся новая запись valid_from;
    • старые расписания и отчёты используют кафедру, актуальную на дату занятия.
  • Привязки teacher_subjects не удаляются автоматически при переводе, иначе потеряется история. Для будущих назначений можно показывать предупреждение, если дисциплина не относится к текущей кафедре преподавателя.

API

  • GET /api/users/{id}/department-history
  • POST /api/users/{id}/department-transfer
  • GET /api/departments/{id}/teachers?date=YYYY-MM-DD
  • GET /api/users/teachers?departmentId=&date=

8. Этап 4. Версионирование расписания и точечные изменения

Текущая модель schedule_rules хороша для генерации будущего расписания, но недостаточна для неизменяемого прошлого и точечных переносов.

Версии правил

Добавить к правилам и слотам период действия:

  • valid_from;
  • valid_to;
  • status;
  • version_group_id или parent_rule_id;
  • created_by;
  • change_reason.

При редактировании правила:

  • если правка относится к будущему периоду, закрывать старую версию и создавать новую;
  • если правка исправляет ошибку текущего будущего правила, разрешить обновление только до даты начала действия;
  • прошлые периоды не пересчитывать по новым данным.

Точечные изменения расписания

Добавить таблицу:

schedule_overrides

Назначение: перенос, отмена или замена конкретной сгенерированной пары.

Поля:

  • id;
  • base_rule_slot_id;
  • lesson_date;
  • action (MOVE, CANCEL, REPLACE);
  • new_time_slot_id;
  • new_classroom_id;
  • new_teacher_id;
  • new_lesson_format;
  • comment;
  • created_by;
  • created_at.

Генератор расписания должен сначала строить базовые пары из правил, затем применять точечные изменения.

Закрепление времени, аудитории, преподавателя

В schedule_rule_slots или отдельной таблице закреплений добавить признаки:

  • time_locked;
  • classroom_locked;
  • teacher_locked;
  • locked_by;
  • locked_at;
  • lock_comment.

Учебный отдел сможет фиксировать назначения, а автоматические или массовые операции не должны менять закреплённые поля без явного подтверждения.

9. Этап 5. Проверка конфликтов

Перед сохранением правила, версии или override проверять:

  • преподаватель не занят в другом месте в тот же день/пару;
  • аудитория не занята другой парой;
  • вместимость аудитории не меньше суммы групп/подгрупп;
  • преподаватель привязан к дисциплине и нужному типу занятия;
  • выбранная аудитория активна и доступна на дату;
  • группа/подгруппа активна на дату;
  • кафедральные ограничения не нарушены, если дисциплина принадлежит другой кафедре.

Конфликты делить на:

  • ERROR — сохранение запрещено;
  • WARNING — можно сохранить с комментарием, если роль имеет право.

API:

  • POST /api/admin/schedule-rules/validate
  • POST /api/edu-office/schedule/validate
  • GET /api/schedule/conflicts?startDate=&endDate=

10. Этап 6. Кабинет кафедры

Функции

  • Просмотр дисциплин своей кафедры.
  • Создание и редактирование описания дисциплины.
  • Импорт дисциплин из файла. Формат нужно согласовать отдельно: CSV/XLSX, обязательные колонки, правила обновления.
  • Разделение данных дисциплины:
    • карточка дисциплины: название, код, кафедра, описание, комментарии;
    • преподаватели дисциплины;
    • типы занятий, которые может вести каждый преподаватель;
    • история изменений.
  • Комментарии к дисциплине, привязке или расписанию.
  • Просмотр расписания кафедры.
  • Просмотр загруженности кафедры:
    • по преподавателям;
    • по дисциплинам;
    • по неделям;
    • по типам занятий.

Backend

Новые или расширенные сущности:

  • subject_comments;
  • subject_department_assignments или темпоральные поля у subjects;
  • версионированные teacher_subjects;
  • teacher_lesson_types вывести в полноценный API, если таблица уже есть в схеме.

API:

  • GET /api/department/subjects
  • POST /api/department/subjects/import
  • GET /api/department/subjects/{id}
  • PUT /api/department/subjects/{id}
  • GET /api/department/subjects/{id}/teachers
  • PUT /api/department/subjects/{id}/teachers
  • GET /api/department/schedule
  • GET /api/department/workload
  • POST /api/department/comments

Frontend

Создать /department/ как отдельный кабинет:

  • subjects — дисциплины кафедры;
  • teachers — преподаватели кафедры и их дисциплины;
  • schedule — расписание кафедры;
  • workload — нагрузка;
  • comments или встроенные комментарии в карточках.

11. Этап 7. Кабинет учебного отдела

Функции

  • Просмотр расписания по группам, аудиториям, преподавателям и кафедрам.
  • Редактирование правил расписания.
  • Точечный перенос пары.
  • Отмена пары.
  • Замена преподавателя.
  • Замена аудитории.
  • Закрепление времени, аудитории и преподавателя.
  • Работа с конфликтами.
  • Просмотр загруженности аудиторий и преподавателей.

Backend

API:

  • GET /api/edu-office/schedule
  • POST /api/edu-office/schedule/overrides
  • PUT /api/edu-office/schedule/overrides/{id}
  • DELETE /api/edu-office/schedule/overrides/{id}
  • POST /api/edu-office/schedule/locks
  • DELETE /api/edu-office/schedule/locks/{id}
  • GET /api/edu-office/workload/classrooms
  • GET /api/edu-office/workload/teachers
  • GET /api/edu-office/conflicts

Frontend

Создать /edu-office/ с вкладками:

  • Расписание;
  • Редактор;
  • Конфликты;
  • Загруженность аудиторий;
  • Загруженность преподавателей;
  • Закрепления.

Текущий админский конструктор расписания можно переиспользовать, но его нужно отделить от справочников и адаптировать под роль учебного отдела.

12. Этап 8. Универсальный просмотр расписания

Сейчас GET /api/schedule требует ровно groupId или teacherId. Для новых требований нужен отдельный эндпоинт или расширение текущего.

Рекомендуется добавить новый эндпоинт:

GET /api/schedule/search

Фильтры:

  • startDate;
  • endDate;
  • groupId;
  • teacherId;
  • classroomId;
  • departmentId;
  • subjectId;
  • lessonTypeId;
  • timeSlotId;
  • parity;
  • includeArchived.

Правило: можно передавать один или несколько фильтров. Если фильтров нет, доступ зависит от роли:

  • ADMIN и EDUCATION_OFFICE могут видеть всё;
  • DEPARTMENT видит свою кафедру;
  • TEACHER видит себя;
  • STUDENT видит доступные группы или свою группу, когда появится привязка студента к группе.

Форматы отображения:

  • недельная сетка;
  • табличный список;
  • вид по аудиториям;
  • вид по преподавателям;
  • вид по кафедрам.

13. Этап 9. Загруженность по паре, преподавателю и кафедре

Показатели

  • занятость аудитории по дате и паре;
  • занятость преподавателя по дате и паре;
  • часы преподавателя за период;
  • часы кафедры за период;
  • распределение по дисциплинам;
  • распределение по типам занятий;
  • свободные аудитории на выбранную дату/пару;
  • перегрузки и конфликты.

API

  • GET /api/workload/classrooms
  • GET /api/workload/teachers
  • GET /api/workload/departments
  • GET /api/workload/time-slots
  • GET /api/workload/free-classrooms

Общие параметры:

  • startDate;
  • endDate;
  • date;
  • timeSlotId;
  • teacherId;
  • departmentId;
  • classroomId;
  • groupId;
  • lessonTypeId.

Реализация

На первом этапе считать загруженность из ScheduleGeneratorService и фильтровать уже сгенерированные пары. Если отчёты станут тяжёлыми, добавить кэш или материализованную таблицу фактических занятий на период.

Важно: после внедрения schedule_overrides отчёты должны считать уже итоговое расписание, а не только базовые правила.

14. Миграции Flyway

Существующие миграции не изменять. Новые изменения оформлять новыми файлами:

  1. V2__roles_and_lifecycle.sql
    • новые роли;
    • поля жизненного цикла для справочников;
    • индексы по status, active_from, active_to.
  2. V3__teacher_department_history.sql
    • история принадлежности преподавателей к кафедрам;
    • перенос текущих users.department_id в первую историческую запись.
  3. V4__subject_and_teacher_temporal_links.sql
    • комментарии;
    • версионированные связи преподавателей и дисциплин;
    • связь преподаватель + дисциплина + тип занятия с периодом действия.
  4. V5__schedule_versions_and_overrides.sql
    • версии правил;
    • точечные изменения расписания;
    • закрепления.
  5. V6__workload_indexes.sql
    • индексы под отчёты и фильтры расписания.

Нумерация может измениться, если в проекте появятся другие миграции до начала реализации.

15. Изменения документации

После каждого этапа обновлять:

  • docs/DATABASE.md;
  • docs/API.md;
  • docs/BUSINESS_LOGIC.md;
  • docs/FRONTEND.md;
  • docs/ARCHITECTURE.md, если меняются роли, авторизация или темпоральная модель.

Для больших этапов лучше добавлять отдельные документы:

  • docs/RBAC.md;
  • docs/TEMPORAL_MODEL.md;
  • docs/SCHEDULE_EDITING.md;
  • docs/WORKLOAD_ANALYTICS.md.

16. Проверка и критерии готовности

Backend

  • docker run --rm -v /mnt/HDD/magistr/magistr/backend:/app -w /app maven:3.9-eclipse-temurin-17 mvn -q clean compile
  • Прогон Flyway на чистой PostgreSQL БД.
  • Проверка, что старые данные мигрировали в темпоральные таблицы.
  • Тесты или ручные проверки:
    • архивная аудитория не выбирается для будущей пары;
    • старая пара с архивной аудиторией отображается;
    • перевод преподавателя не меняет кафедру в отчёте за прошлый период;
    • правка будущего правила не меняет прошлое расписание;
    • конфликт преподавателя/аудитории блокируется или предупреждается.

Frontend

  • node --check для изменённых JS-файлов.
  • git diff --check.
  • Ручной smoke-test в браузере:
    • вход под каждой ролью;
    • видимость нужных вкладок;
    • запрет доступа к чужим эндпоинтам;
    • создание/архивирование/восстановление справочников;
    • редактирование расписания учебным отделом;
    • просмотр расписания и загруженности по разным фильтрам.

17. Риски и вопросы, которые нужно уточнить

  1. Что именно руководитель имеет в виду под "темпоральными базами данных": только история изменений или полноценная bitemporal-модель с valid_time и transaction_time.
  2. Нужно ли хранить фактические проведённые занятия отдельно от планового расписания.
  3. Какие форматы файлов кафедра должна "грузить": XLSX, CSV, шаблон учебного плана, свободная таблица.
  4. Кто именно является пользователем роли DEPARTMENT: заведующий кафедрой, оператор кафедры или несколько сотрудников.
  5. Нужна ли студентам привязка к конкретной группе. Сейчас студент выбирает группу вручную.
  6. Должно ли удаление/архивирование кафедры запрещаться при наличии активных преподавателей, групп и дисциплин или разрешаться с каскадной архивацией.
  7. Нужно ли вести журнал всех действий пользователя для юридически значимой истории изменений.

18. Рекомендуемый порядок внедрения

  1. Роли и серверная авторизация.
  2. Мягкое удаление аудиторий и справочников.
  3. История переводов преподавателей.
  4. Версионирование расписания и точечные изменения.
  5. Проверка конфликтов.
  6. Универсальный schedule/search.
  7. Загруженность преподавателей, кафедр и пар.
  8. Кабинет кафедры.
  9. Кабинет учебного отдела.
  10. Финальная синхронизация документации и регрессионный smoke-test.

Такой порядок закрывает базовую целостность данных до того, как появятся новые роли и экраны, которые начнут массово создавать и менять расписание.