From f010ffc4671bacc136540d31551eb4ec4f8d97c6 Mon Sep 17 00:00:00 2001 From: Zuev Date: Tue, 19 May 2026 15:02:07 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=BB=D0=B0=D0=BD=20=D1=80=D0=BE=D0=BB?= =?UTF-8?q?=D0=B5=D0=B2=D0=BE=D0=B3=D0=BE=20=D0=B4=D0=BE=D1=81=D1=82=D1=83?= =?UTF-8?q?=D0=BF=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SUPERVISOR_TASKS_IMPLEMENTATION_PLAN.md | 541 ++++++++++++++++++++++++ 1 file changed, 541 insertions(+) create mode 100644 SUPERVISOR_TASKS_IMPLEMENTATION_PLAN.md diff --git a/SUPERVISOR_TASKS_IMPLEMENTATION_PLAN.md b/SUPERVISOR_TASKS_IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..66b5d9b --- /dev/null +++ b/SUPERVISOR_TASKS_IMPLEMENTATION_PLAN.md @@ -0,0 +1,541 @@ +# План реализации задач после созвона с научным руководителем + +Дата: 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. + +Такой порядок закрывает базовую целостность данных до того, как появятся новые роли и экраны, которые начнут массово создавать и менять расписание.