# План реализации задач после созвона с научным руководителем Дата: 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. История переводов преподавателей между кафедрами ### Модель данных Добавить таблицу: ```sql 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`. При редактировании правила: - если правка относится к будущему периоду, закрывать старую версию и создавать новую; - если правка исправляет ошибку текущего будущего правила, разрешить обновление только до даты начала действия; - прошлые периоды не пересчитывать по новым данным. ### Точечные изменения расписания Добавить таблицу: ```sql 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`. Для новых требований нужен отдельный эндпоинт или расширение текущего. Рекомендуется добавить новый эндпоинт: ```http 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. Такой порядок закрывает базовую целостность данных до того, как появятся новые роли и экраны, которые начнут массово создавать и менять расписание.