#2 - тест, #5 - готово. Разовое редактирование занятия без изменений правила

This commit is contained in:
Zuev
2026-07-22 00:15:35 +03:00
parent ee876f1acd
commit 92ff87a917
33 changed files with 3324 additions and 251 deletions

View File

@@ -263,30 +263,44 @@ Bearer-токен проверяется на backend. Frontend-скрытие
Учебный отдел может создать изменение конкретной пары:
- `CANCEL` — отменить пару;
- `MOVE` — перенести пару на другой фактический интервал или в другую аудиторию;
- `REPLACE` — заменить преподавателя, аудиторию или формат.
- `MOVE` — перенести пару на другую учебную дату того же семестра или другой фактический интервал;
- `REPLACE` без изменения даты и времени заменить преподавателя, аудиторию или формат.
`CANCEL` не принимает новые ресурсы. Для `MOVE` обязателен новый временной слот или
аудитория, для `REPLACE` — преподаватель, аудитория или формат. Дополнительные изменения
можно объединять в одном payload, но основное действие должно фактически менять свою
часть пары. Формат ограничен значениями `Очно` и `Онлайн`.
`CANCEL` не принимает целевую дату и новые ресурсы. Для `MOVE` обязателен новый временной
слот, для `REPLACE` — преподаватель, аудитория или формат. Перенос даты требует явно
выбранного слота эффективной сетки целевого дня. Дополнительные изменения преподавателя,
аудитории и формата можно объединить с `MOVE`, но основное действие должно фактически
менять свою часть пары. Формат ограничен значениями `Очно` и `Онлайн`; дисциплина и тип
занятия не изменяются.
Изменения не переписывают базовое правило, а накладываются поверх сгенерированного
расписания на конкретную дату. Перед записью `ScheduleOverrideService`:
расписания. Исходная пара всегда идентифицируется как `baseRuleSlotId + lessonDate`, а
`targetLessonDate` хранит только новую дату единственного занятия. Перед записью
`ScheduleOverrideService`:
1. захватывает transaction advisory lock PostgreSQL для tenant-БД и даты;
1. захватывает transaction advisory lock PostgreSQL для исходных и целевых дат в стабильном порядке;
2. строит базовый день для всех групп без интерактивного лимита широкого поиска;
3. доказывает существование исходной пары с учётом семестра, календарного графика,
чётности, недели начала, лимита часов и lifecycle;
4. применяет сохранённые overrides и кандидат общей логикой `ScheduleQueryService`;
5. проверяет полуоткрытые временные интервалы `[start, end)` и итоговые ресурсы;
6. сохраняет изменение только при отсутствии конфликта.
4. для новой даты проверяет границы того же семестра, активность правила и дисциплины,
учебный день всех затронутых групп и эффективную сетку времени;
5. проверяет lifecycle итоговых преподавателя и аудитории на целевую дату;
6. применяет сохранённые overrides и кандидат общей логикой `ScheduleQueryService`;
7. проверяет полуоткрытые временные интервалы `[start, end)` и итоговые ресурсы целевого дня;
8. сохраняет изменение только при отсутствии конфликта.
В поиске `MOVE` удаляет исходное вхождение и добавляет занятие в целевой день, включая
случай, когда в запрос попала только целевая дата. День недели, номер недели и чётность
вычисляются заново по семестру, а расход академических часов остаётся у исходной пары.
`CANCEL` скрывает занятие. Удаление override возвращает текущий вариант, который снова
формируется базовым правилом.
Совпадение преподавателя или аудитории в пересекающееся время всегда является конфликтом.
Для общей учебной группы занятие целой группы конфликтует с любой её подгруппой; разные
подгруппы одной группы могут идти параллельно при свободных преподавателях и аудиториях.
Конфликт возвращается как `409 Conflict` с русским сообщением. Блокировка PostgreSQL общая
для backend-pod, поэтому два конкурентных изменения одной даты проверяются последовательно.
для backend-pod, поэтому два конкурентных переноса с разных исходных дат на одну целевую
дату и одинаковые ресурсы проверяются последовательно.
---