исправил качество правил и изменение расписания (не черновика)
This commit is contained in:
16
docs/API.md
16
docs/API.md
@@ -529,6 +529,8 @@ GET /api/schedule?groupId=1&startDate=2026-04-27&endDate=2026-05-03
|
||||
Повторное название года, повторный тип семестра или пересечение возвращаются как
|
||||
`409 Conflict`; неверный порядок дат и выход семестра за границы года — как `400 Bad Request`.
|
||||
При сужении учебного года все существующие семестры должны оставаться внутри новых границ.
|
||||
Создание семестра также создаёт для него пустую версию 1 со статусом `PUBLISHED` и названием
|
||||
`Основное расписание`, поэтому конструктор сразу может работать в обычном режиме.
|
||||
|
||||
**Тело создания/обновления кода активности:**
|
||||
```json
|
||||
@@ -661,10 +663,13 @@ CRUD доступен по:
|
||||
|
||||
`timeSlotId` должен ссылаться на базовый слот (`scopeApplyMode = DEFAULT`). Субботняя и ручные сетки не выбираются в правиле напрямую.
|
||||
|
||||
Создание, изменение и архивирование правил разрешены только внутри версии со статусом
|
||||
`DRAFT`; `scheduleVersionId` обязателен в payload. Версия должна относиться к указанному
|
||||
семестру. Без `versionId` список возвращает только правила текущей опубликованной версии,
|
||||
а конструктор всегда передаёт ID выбранного черновика.
|
||||
Создание, изменение и архивирование правил разрешены внутри версии со статусом `PUBLISHED`
|
||||
или `DRAFT`; `scheduleVersionId` обязателен в payload. Версия должна относиться к указанному
|
||||
семестру. Изменения `PUBLISHED`-версии сразу влияют на публичное расписание, изменения
|
||||
`DRAFT` остаются изолированными до публикации, а `ARCHIVED` доступна только для чтения.
|
||||
Без `versionId` список возвращает только правила текущей опубликованной версии. При update
|
||||
ID существующих слотов сохраняются; удаление слота, на который ссылаются точечные изменения,
|
||||
заявки преподавателей или решения по отсутствиям, отклоняется явной ошибкой `400`.
|
||||
|
||||
`subgroupIds` можно передавать только для лабораторного слота. Каждая подгруппа должна относиться к одной из групп правила. Если лабораторная проводится у нескольких групп одновременно, в одном слоте можно передать разные подгруппы этих групп, например `[10, 22]`. Для совместимости одиночный `subgroupId` тоже принимается, но новый формат — `subgroupIds`. Для лекций и практик оба поля должны быть пустыми, иначе API вернёт ошибку валидации. В одном слоте нельзя выбрать больше одной подгруппы одной и той же группы.
|
||||
|
||||
@@ -862,7 +867,8 @@ API возвращает `409 Conflict`; соседние интервалы и
|
||||
|
||||
Доступ имеют только `ADMIN` и `EDUCATION_OFFICE`. Для семестра допускается ровно одна
|
||||
версия со статусом `PUBLISHED`; остальные имеют статус `DRAFT` или `ARCHIVED`. Создание
|
||||
копии переносит правила, группы, слоты и подгруппы, сохраняя `versionGroupId` для diff.
|
||||
копии может использовать опубликованную версию или другой черновик и переносит правила,
|
||||
группы, слоты и подгруппы, сохраняя `versionGroupId` для diff.
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -267,23 +267,27 @@ constraint. При чтении API разворачивает периоды о
|
||||
- **Доступность аудитории:** `is_available=false` запрещает новые назначения, но не удаляет историю.
|
||||
- **Конфликты слотов:** сначала попарно проверяются слоты самого нового payload, включая точные дубли, затем — активные правила той же версии. Конфликт возникает при пересечении дня, базового временного слота, чётности (`BOTH` пересекается с любой чётностью) и активных недель слота, если совпадает преподаватель, аудитория или учебная группа. `ODD` и `EVEN` между собой не конфликтуют. Активные недели считаются из лимита часов типа занятия, недели начала, чётности и порядка слотов внутри правила; например, занятие на 1-3 неделях не конфликтует с тем же ресурсом с 4 недели. Для лабораторных занятий разные подгруппы одной группы могут идти параллельно, но занятие для всей группы конфликтует с любой её подгруппой. Backend возвращает `409 Conflict`; `conflictRule` присутствует только для конфликта с сохранённым правилом, а внутренний конфликт описывается полями и русскими причинами без искусственной записи.
|
||||
- **Конкурентная запись:** публичные методы `ScheduleRuleService` являются транзакционными. Создание блокирует строку семестра и выбранную версию, а update — правило, версию и старый/новый семестры в стабильном порядке. Публикация блокирует версию до завершения полной проверки, поэтому другой backend-pod не может дописать правило после валидации черновика.
|
||||
- **Связанные слоты:** update сохраняет ID переданных существующих слотов, чтобы не разрывать точечные изменения и операционный аудит. Удаление слота отклоняется, если на него ссылаются override, заявка преподавателя или решение по отсутствию.
|
||||
|
||||
### Черновики, версии и публикация
|
||||
|
||||
Правила каждого семестра принадлежат явной версии расписания. Жизненный цикл версии:
|
||||
|
||||
1. `DRAFT` создаётся пустым или как полная копия выбранной версии.
|
||||
2. Конструктор добавляет, изменяет и архивирует правила только в выбранном черновике.
|
||||
3. Полная проверка выявляет внутренние конфликты правил; diff сопоставляет правила по
|
||||
1. При создании семестра автоматически появляется пустая версия 1 `PUBLISHED` — основное расписание.
|
||||
2. Конструктор по умолчанию открывает опубликованное расписание: изменения его правил сразу
|
||||
видны конечным пользователям. Пользователь может переключиться на существующий `DRAFT`.
|
||||
3. `DRAFT` создаётся пустым через API или прямо в конструкторе как полная копия выбранной
|
||||
опубликованной либо черновой версии; после создания он сразу становится текущим.
|
||||
4. Полная проверка выявляет внутренние конфликты правил; diff сопоставляет правила по
|
||||
стабильному `version_group_id` и отдельно сравнивает сформированные занятия семестра.
|
||||
4. Публикация требует причины и в одной транзакции архивирует прежнюю публикацию, затем
|
||||
5. Публикация требует причины и в одной транзакции архивирует прежнюю публикацию, затем
|
||||
переводит проверенный черновик в `PUBLISHED`.
|
||||
5. Ранее опубликованная версия получает `ARCHIVED` и может быть восстановлена такой же
|
||||
6. Ранее опубликованная версия получает `ARCHIVED` и может быть восстановлена такой же
|
||||
атомарной операцией с обязательной причиной.
|
||||
|
||||
На уровне БД частичный уникальный индекс допускает только одну `PUBLISHED`-версию на
|
||||
семестр. Блокировки версии и набора версий семестра не позволяют публикации пересечься с
|
||||
редактированием черновика или конкурентной публикацией. Каждое создание, архивирование,
|
||||
редактированием расписания или конкурентной публикацией. Каждое создание, архивирование,
|
||||
публикация и восстановление записывается в неизменяемый журнал с автором, временем и
|
||||
причиной.
|
||||
|
||||
|
||||
@@ -950,7 +950,9 @@ V1 создаёт GiST exclusion constraint `ex_academic_years_no_overlap` дл
|
||||
Пара `semester_id + version_number` уникальна. Частичный индекс
|
||||
`uq_schedule_versions_published_semester` запрещает более одной строки `PUBLISHED` на
|
||||
семестр. Начальная загрузка V1 создаёт опубликованную версию 1 для каждого семестра и
|
||||
привязывает к ней существующие seed-правила.
|
||||
привязывает к ней существующие seed-правила. Для семестров, создаваемых через API после
|
||||
запуска, `AcademicPeriodService` в той же транзакции создаёт пустую опубликованную версию 1
|
||||
`Основное расписание` и две записи истории: создание и публикацию.
|
||||
|
||||
#### `schedule_version_history` — Аудит версий расписания
|
||||
|
||||
|
||||
@@ -115,7 +115,8 @@
|
||||
**Статус: реализовано в MVP.** Добавлены изолированные версии `DRAFT/PUBLISHED/ARCHIVED`,
|
||||
полное копирование правил, валидация и diff правил/занятий, атомарная публикация,
|
||||
восстановление предыдущей публикации и неизменяемый журнал действий. Экран качества умеет
|
||||
проверять черновики до публикации; изменения в них выполняются через конструктор правил.
|
||||
проверять черновики до публикации. Конструктор по умолчанию редактирует опубликованное
|
||||
расписание, позволяет переключиться на черновик и создать новую копию выбранной версии.
|
||||
|
||||
### Проблема и пользователи
|
||||
|
||||
@@ -128,8 +129,9 @@
|
||||
|
||||
### Пользовательский сценарий
|
||||
|
||||
1. Пользователь создаёт черновик расписания для выбранного семестра.
|
||||
2. Все изменения правил применяются только к черновику и не видны обычным пользователям.
|
||||
1. Пользователь редактирует основное опубликованное расписание напрямую либо создаёт
|
||||
черновик для выбранного семестра на основе открытой версии.
|
||||
2. Изменения опубликованной версии видны сразу, а изменения черновика изолированы до публикации.
|
||||
3. Перед публикацией система проверяет конфликты и показывает сравнение с текущей версией:
|
||||
добавленные, удалённые и изменённые занятия.
|
||||
4. Ответственный сотрудник указывает причину и публикует версию одной операцией.
|
||||
@@ -149,8 +151,9 @@
|
||||
### Связь с текущей системой
|
||||
|
||||
Текущая динамическая генерация получила явный контекст версии. Конструктор правил меняет
|
||||
только черновик, кабинеты просмотра используют только опубликованную версию, а анализатор
|
||||
качества позволяет выбрать опубликованную, черновую или архивную версию. Точечные изменения
|
||||
выбранную опубликованную или черновую версию, кабинеты просмотра используют только
|
||||
опубликованную, а анализатор качества позволяет выбрать опубликованную, черновую или
|
||||
архивную версию. Точечные изменения
|
||||
остаются привязаны к конкретной публикации и не переносятся в новый черновик автоматически.
|
||||
|
||||
### Дальнейшее развитие
|
||||
|
||||
@@ -180,7 +180,7 @@ frontend/
|
||||
| `department-workspace` | Кабинет кафедры: дисциплины, импорт, комментарии, преподаватели, привязка преподавателей, заявки на новых преподавателей и нагрузка | `/api/department/*`, `/api/department/teacher-requests`, `/api/workload/teachers` |
|
||||
| `schedule-view` | Просмотр расписаний: семестр выбирается в дополнительных фильтрах, для учебного периода строится двухнедельный диапазон, найденные расписания выбираются в переключателе, а `ADMIN` и `EDUCATION_OFFICE` редактируют конкретное занятие в боковой панели без изменения правила | `/api/schedule/semesters`, `/api/schedule/search`, `/api/edu-office/schedule/overrides`, `/api/admin/time-slots/effective` |
|
||||
| `teacher-absences` | Запросы преподавателей: отсутствия и мастер замены, согласование семестровых пожеланий, заявки на перенос, аудиторию или отмену | `/api/teacher-absences`, `/api/teacher-preferences`, `/api/teacher-change-requests`, `/api/users/teachers` |
|
||||
| `schedule` | Конструктор правил динамического расписания с подсказками согласованных пожеланий и выезжающей визуальной матрицей групп | `/api/admin/schedule-rules`, `/api/teacher-preferences`, `/api/admin/time-slots`, `/api/admin/calendar/years`, `/api/lesson-types`, `/api/subgroups` |
|
||||
| `schedule` | Конструктор правил: по умолчанию редактирует опубликованное расписание, позволяет переключиться на черновик или создать его из выбранной версии; содержит подсказки пожеланий и визуальную матрицу групп | `/api/admin/schedule-rules`, `/api/edu-office/schedule/versions`, `/api/teacher-preferences`, `/api/admin/time-slots`, `/api/admin/calendar/years`, `/api/lesson-types`, `/api/subgroups` |
|
||||
| `schedule-versions` | Контур публикации: текущая версия, черновики, diff, архив, восстановление и журнал | `/api/edu-office/schedule/versions` |
|
||||
| `schedule-quality` | Диагностика выбранной версии семестра: оценка, метрики, фильтруемые проблемы; для публикации — подтверждаемое локальное улучшение | `/api/edu-office/schedule/quality`, `/api/edu-office/schedule/quality/recommendations`, `/api/edu-office/schedule/overrides` |
|
||||
| `academic-calendar` | Учебные годы, семестры, создание календарных графиков, Excel-подобный редактор дневной сетки и привязка дисциплин к семестрам графика | `/api/admin/calendar`, `/api/admin/academic-calendars`, `/api/admin/academic-calendars/{id}/subjects`, `/api/admin/calendar/activity-types`, `/api/specialties`, `/api/specialties/{id}/profiles`, `/api/education-forms`, `/api/subjects` |
|
||||
@@ -219,9 +219,12 @@ frontend/
|
||||
- Вкладка `schedule-versions` доступна администратору и учебному отделу и оформлена как
|
||||
отдельный контур публикации. Верхняя карточка показывает версию, которую видят конечные
|
||||
пользователи; ниже расположены черновики, сравнение правил и занятий, архив и журнал.
|
||||
Новый черновик копирует выбранную опубликованную версию. Перед публикацией интерфейс
|
||||
В конструкторе опубликованная версия текущего семестра выбирается по умолчанию и выделяется
|
||||
зелёным контуром; её изменения применяются сразу. Селект позволяет перейти в медный режим
|
||||
черновика. Кнопка `Создать черновик` копирует выбранное опубликованное расписание или
|
||||
черновик и сразу открывает новую рабочую копию. Перед публикацией интерфейс
|
||||
одновременно запрашивает полную валидацию и diff, требует причину и блокирует действие
|
||||
при ошибках. Кнопки черновика сохраняют его ID в `localStorage` и открывают конструктор
|
||||
при ошибках. Кнопки версий передают их ID одноразово через `localStorage` и открывают конструктор
|
||||
или анализ качества в нужном контексте; архивную публикацию можно восстановить с причиной.
|
||||
- Компоновка `department-workspace` использует собственные CSS-сетки `department-workspace-filter-grid` и `department-workspace-actions-grid`: фильтры периода отделены от сеток расписания, загрузка дисциплин занимает широкую колонку, формы преподавателей выравниваются справа, а списки и таблицы идут полноширинными блоками ниже.
|
||||
- Вкладка `schedule-view` показывает найденные занятия в режиме одной активной таблицы. Пользователь выбирает, что смотреть: группу, преподавателя, аудиторию или кафедру; основная кнопка `Показать` расположена в заголовке блока параметров, а пустое состояние таблицы с подсказкой об обновлении содержит дополнительную кнопку `Показать расписание`. В дополнительных фильтрах доступен семестр из справочника `/api/schedule/semesters`, предназначенного только для чтения. Для текущего семестра сохраняется текущая двухнедельная точка просмотра, а при выборе другого семестра диапазон начинается с понедельника его первой недели. Frontend запрашивает две недели и собирает найденные расписания в переключатель результатов. На странице не выводится стек таблиц: виден один выбранный результат, а остальные доступны через чипы и кнопки предыдущего/следующего расписания; чипы результатов переносятся и отделены от счётчика стабильным отступом. Для режима кафедры и роли `DEPARTMENT` расписание ограничивается кафедрой пользователя; преподавательские и студенческие отдельные страницы пока остаются самостоятельными. Таблица строится как строки пар и столбцы дней недели. Нечётная неделя отображается в верхней половине ячейки, чётная — в нижней, а одинаковые занятия в обе недели схлопываются в цельную ячейку. Бейдж диапазона недель скрывается для занятий на весь семестр, а для занятий до конца семестра показывает только неделю начала в формате `(с 5 нед.)`. На мобильной ширине вместо широкой недельной матрицы показывается один день активного расписания с переключателем дней.
|
||||
|
||||
Reference in New Issue
Block a user