Миллион изменений, создание вкладки "Расписание занятий"

This commit is contained in:
Zuev
2026-04-29 23:35:16 +03:00
parent 8f71b9b2b5
commit 96e9d8155f
42 changed files with 1718 additions and 4661 deletions

View File

@@ -233,110 +233,56 @@ CRUD доступен по:
| `PUT` | `/api/admin/schedule-rules/{id}` | Обновить правило |
| `DELETE` | `/api/admin/schedule-rules/{id}` | Удалить правило |
---
### `GET /api/lesson-types`
## Расписание (Lessons, deprecated)
Справочник типов занятий для конструктора правил расписания.
Старые эндпоинты ещё физически есть в коде до удаления старых контроллеров и таблиц. Новый просмотр расписания использует `GET /api/schedule`, а новые seed-данные создаются сразу в динамической модели.
### `GET /api/users/lessons`
Список всех занятий с разрешёнными именами (преподаватель, группа, дисциплина, аудитория).
**Ответ:**
**Ответ:**
```json
[
{
"id": 1,
"teacherName": "Тестовый преподаватель",
"groupName": "ИВТ-21-1",
"classroomName": "101 Ленинская",
"educationFormName": "Бакалавриат",
"subjectName": "Высшая математика",
"typeLesson": "Лекция",
"lessonFormat": "Очно",
"day": "Понедельник",
"week": "Верхняя",
"time": "11:40 - 13:10"
}
{ "id": 1, "name": "Лекция" },
{ "id": 2, "name": "Практика" }
]
```
### `GET /api/users/lessons/{teacherId}`
---
Занятия конкретного преподавателя.
## Кафедры и специальности
### `POST /api/users/lessons/create`
### Кафедры
Создание занятия.
| Метод | URL | Назначение |
|-------|-----|------------|
| `GET` | `/api/departments` | Список кафедр |
| `POST` | `/api/departments` | Создать кафедру |
| `PUT` | `/api/departments/{id}` | Обновить кафедру |
| `DELETE` | `/api/departments/{id}` | Удалить кафедру |
**Тело запроса:**
**Тело создания/обновления:**
```json
{
"teacherId": 2,
"groupId": 1,
"subjectId": 1,
"lessonFormat": "Очно",
"typeLesson": "Лекция",
"classroomId": 1,
"day": "Понедельник",
"week": "Верхняя",
"time": "11:40 - 13:10"
"departmentName": "Кафедра ИБ",
"departmentCode": 1
}
```
**Валидация:**
### Специальности
| Поле | Правило |
|------|---------|
| `teacherId` | Обязателен, ≠ 0 |
| `groupId` | Обязателен, ≠ 0 |
| `subjectId` | Обязателен, ≠ 0 |
| `lessonFormat` | `Очно` или `Онлайн` |
| `typeLesson` | `Лекция`, `Практическая работа`, `Лабораторная работа` |
| `classroomId` | Обязателен, ≠ 0 |
| `day` | Пн–Сб (на русском) |
| `week` | `Верхняя`, `Нижняя`, `Обе` |
| `time` | Обязателен |
| Метод | URL | Назначение |
|-------|-----|------------|
| `GET` | `/api/specialties` | Список специальностей |
| `POST` | `/api/specialties` | Создать специальность |
| `PUT` | `/api/specialties/{id}` | Обновить специальность |
| `DELETE` | `/api/specialties/{id}` | Удалить специальность |
### `PUT /api/users/lessons/update/{lessonId}`
Обновление занятия. Поддерживает partial update — передаются только изменённые поля.
**Тело ответа:**
**Тело создания/обновления:**
```json
{
"id": 5,
"teacherId": 1,
"groupId": 1,
"subjectId": 2,
"LessonFormat": "Онлайн",
"typeLesson": "Практическая работа",
"classroomId": 3,
"day": "Понедельник",
"week": "Верхняя",
"time": "9:40 - 11:10",
"updatedFields": {
"teacherId": 1,
"subjectId": 2,
"lessonFormat": "Онлайн",
"classroomId": 3,
"day": "Понедельник",
"time": "9:40 - 11:10"
},
"message": "Занятие успешно обновлено"
"specialityName": "Программная инженерия",
"specialityCode": "09.03.04"
}
```
### `DELETE /api/users/lessons/delete/{lessonId}`
Удаление занятия.
### `GET /api/users/lessons/ping`
Проверка доступности контроллера. Возвращает строку `pong`.
---
## Группы
@@ -375,11 +321,13 @@ CRUD доступен по:
"groupSize": 12,
"educationFormId": 1,
"departmentId": 1,
"course": 2,
"yearStartStudy": 2026,
"specialityCode": 1
}
```
`specialityCode` исторически содержит ID записи из `/api/specialties`; текущий курс вычисляется из `yearStartStudy`.
### `DELETE /api/groups/{id}`
Удаление группы.

View File

@@ -91,24 +91,6 @@
Праздник считается пропуском: занятие не переносится и не списывает академические часы.
### Временная старая сущность «Занятие» (Lesson)
`lessons` пока физически остаётся в схеме до удаления старого кода, но базовая миграция больше не заполняет её тестовыми данными. Новые экраны просмотра используют `GET /api/schedule`.
Каждая запись в расписании содержит:
| Поле | Описание | Пример |
|------|----------|--------|
| `teacher_id` | Преподаватель | 2 |
| `group_id` | Учебная группа | 1 |
| `subject_id` | Дисциплина | 3 |
| `lesson_format` | Формат проведения | `Очно`, `Онлайн` |
| `type_lesson` | Тип занятия | `Лекция`, `Практическая работа`, `Лабораторная работа` |
| `classroom_id` | Аудитория | 1 |
| `day` | День недели | `Понедельник` ... `Суббота` |
| `week` | Чётность недели | `Верхняя`, `Нижняя`, `Обе` |
| `time` | Временной слот | `8:00 - 9:30` |
### Временны́е слоты
Сетка пар хранится в `time_slots` и настраивается для каждого тенанта. При миграции создаются базовые слоты:
@@ -123,30 +105,12 @@
| 6 | 16:40 18:10 |
| 7 | 18:30 20:00 |
### Валидация при создании/обновлении
### Валидация правил расписания
- **Дни:** только `Понедельник` `Суббота` (`DayAndWeekValidator`)
- **Недели:** только `Верхняя`, `Нижняя`, `Обе`
- **Формат:** только `Очно`, `Онлайн` (`TypeAndFormatLessonValidator`)
- **Тип:** только `Лекция`, `Практическая работа`, `Лабораторная работа`
- Все ID (преподаватель, группа, дисциплина, аудитория) обязательны и не могут быть 0
### Временные старые данные к составлению расписания (Schedule Data)
Таблица `schedule_data` пока физически остаётся в схеме до удаления старого кода. Новая базовая миграция создаёт `schedule_rules` и `schedule_rule_groups` напрямую, без ETL из `schedule_data`.
| Поле | Описание |
|------|----------|
| `department_id` | Кафедра |
| `semester` | Номер семестра |
| `group_id` | Учебная группа |
| `subjects_id` | Дисциплина |
| `lesson_type_id` | Тип занятия |
| `number_of_hours` | Количество часов |
| `is_division` | Деление на подгруппы |
| `teacher_id` | Преподаватель |
| `semester_type` | Тип семестра (Весенний / Осенний) |
| `period` | Учебный год (напр. `2024/2025`) |
- **Правило:** обязательны дисциплина, семестр, дата начала, положительный лимит академических часов и хотя бы одна группа.
- **Слот:** день недели должен быть от 1 до 7, чётность недели обязательна.
- **Связанные сущности:** временной слот, преподаватель, аудитория и тип занятия должны существовать в БД.
- **Формат:** `lessonFormat` обязателен и хранится в слоте правила.
---

View File

@@ -117,33 +117,6 @@ erDiagram
BIGINT lesson_type_id FK,PK
}
lessons {
BIGSERIAL id PK
BIGINT teacher_id FK
BIGINT group_id FK
BIGINT subject_id FK
VARCHAR lesson_format
VARCHAR type_lesson
BIGINT classroom_id FK
VARCHAR day
VARCHAR week
VARCHAR time
}
schedule_data {
BIGSERIAL id PK
BIGINT department_id FK
INT semester
BIGINT group_id FK
BIGINT subjects_id FK
BIGINT lesson_type_id FK
INT number_of_hours
BOOLEAN is_division
BIGINT teacher_id FK
VARCHAR semester_type
VARCHAR period
}
time_slots {
BIGSERIAL id PK
INT order_number UK
@@ -212,26 +185,17 @@ erDiagram
departments ||--o{ users : "department_id"
departments ||--o{ student_groups : "department_id"
departments ||--o{ subjects : "department_id"
departments ||--o{ schedule_data : "department_id"
education_forms ||--o{ student_groups : "education_form_id"
specialties ||--o{ student_groups : "specialty_code"
student_groups ||--o{ subgroups : "group_id"
student_groups ||--o{ lessons : "group_id"
student_groups ||--o{ schedule_data : "group_id"
student_groups ||--o{ schedule_rule_groups : "group_id"
users ||--o{ lessons : "teacher_id"
users ||--o{ teacher_subjects : "user_id"
users ||--o{ teacher_lesson_types : "user_id"
users ||--o{ schedule_data : "teacher_id"
subjects ||--o{ lessons : "subject_id"
subjects ||--o{ teacher_subjects : "subject_id"
subjects ||--o{ teacher_lesson_types : "subject_id"
subjects ||--o{ schedule_data : "subjects_id"
subjects ||--o{ schedule_rules : "subject_id"
lesson_types ||--o{ teacher_lesson_types : "lesson_type_id"
lesson_types ||--o{ schedule_data : "lesson_type_id"
lesson_types ||--o{ schedule_rule_slots : "lesson_type_id"
classrooms ||--o{ lessons : "classroom_id"
classrooms ||--o{ schedule_rule_slots : "classroom_id"
classrooms ||--o{ classroom_equipments : "classroom_id"
equipments ||--o{ classroom_equipments : "equipment_id"
@@ -352,23 +316,6 @@ erDiagram
### Расписание
#### `lessons` — Временная таблица старой модели
Таблица пока остаётся в схеме до удаления старого кода, но новая базовая миграция больше не заполняет её тестовыми данными. Целевой источник расписания — `schedule_rules` и `schedule_rule_slots`.
| Колонка | Тип | Описание |
|---------|-----|----------|
| `id` | BIGSERIAL PK | ID |
| `teacher_id` | BIGINT FK → users | Преподаватель |
| `group_id` | BIGINT FK → student_groups | Группа |
| `subject_id` | BIGINT FK → subjects | Дисциплина |
| `lesson_format` | VARCHAR(255) | `Очно` / `Онлайн` |
| `type_lesson` | VARCHAR(255) | `Лекция` / `Практическая работа` / `Лабораторная работа` |
| `classroom_id` | BIGINT FK → classrooms | Аудитория |
| `day` | VARCHAR(255) | День недели |
| `week` | VARCHAR(255) | `Верхняя` / `Нижняя` / `Обе` |
| `time` | VARCHAR(255) | Временной слот |
#### `lesson_types` — Типы занятий (справочник)
| Колонка | Тип | Описание |
|---------|-----|----------|
@@ -394,26 +341,6 @@ erDiagram
| `subject_id` | BIGINT PK, FK → subjects (CASCADE) | Дисциплина |
| `lesson_type_id` | BIGINT PK, FK → lesson_types (CASCADE) | Тип занятия |
#### `schedule_data` — Временная таблица старой модели нагрузки
Таблица пока остаётся в схеме до удаления старого кода, но новая базовая миграция больше не использует её как источник seed-данных.
| Колонка | Тип | Описание |
|---------|-----|----------|
| `id` | BIGSERIAL PK | ID |
| `department_id` | BIGINT FK → departments | Кафедра |
| `semester` | INT | Номер семестра |
| `group_id` | BIGINT FK → student_groups | Группа |
| `subjects_id` | BIGINT FK → subjects | Дисциплина |
| `lesson_type_id` | BIGINT FK → lesson_types | Тип занятия |
| `number_of_hours` | INT | Количество часов |
| `is_division` | BOOLEAN | Деление на подгруппы |
| `teacher_id` | BIGINT FK → users | Преподаватель |
| `semester_type` | VARCHAR(255) | Весенний / Осенний |
| `period` | VARCHAR(255) | Учебный год |
### Динамическое расписание
#### `time_slots` — Временные слоты занятий
| Колонка | Тип | Описание |
|---------|-----|----------|

View File

@@ -50,8 +50,8 @@ docker compose up -d # Пересоздаёт с нуля
| Категория | Стиль | Пример |
|-----------|-------|--------|
| Классы | PascalCase | `LessonsController`, `LessonResponse` |
| Методы и переменные | camelCase | `getAllLessons()`, `teacherId` |
| Классы | PascalCase | `ScheduleController`, `RenderedLessonDto` |
| Методы и переменные | camelCase | `buildScheduleForTeacher()`, `teacherId` |
| Константы | UPPER_SNAKE_CASE | `ROLE_REDIRECTS` |
| Пакеты | lowercase | `com.magistr.app.controller` |
@@ -79,7 +79,7 @@ logger.error("Ошибка при сохранении: {}", e.getMessage(), e);
#### Валидация
- Для сложных правил — отдельные классы-валидаторы (`DayAndWeekValidator`, `TypeAndFormatLessonValidator`)
- Для сложных правил — отдельные сервисы или приватные методы в контроллере с понятным сообщением об ошибке
- Для простых — inline-проверки в контроллере с `ResponseEntity.badRequest()`
#### Импорты
@@ -257,7 +257,7 @@ com.magistr.app/
│ └── ConfigMapUpdater.java # Обновление K8s ConfigMap
├── controller/ # REST-контроллеры
│ ├── AuthController.java
│ ├── LessonsController.java
│ ├── ScheduleController.java
│ ├── ClassroomController.java
│ ├── DatabaseController.java
│ ├── UserController.java
@@ -269,7 +269,5 @@ com.magistr.app/
├── dto/ # Data Transfer Objects
├── model/ # JPA-сущности
├── repository/ # Spring Data JPA
└── utils/ # Валидаторы
├── DayAndWeekValidator.java
└── TypeAndFormatLessonValidator.java
└── utils/ # Общие утилиты
```

View File

@@ -29,7 +29,6 @@ frontend/
│ │ ├── layout.css # Раскладка (sidebar, topbar, content)
│ │ ├── components.css # Кнопки, таблицы, карточки, формы
│ │ ├── modals.css # Модальные окна
│ │ ├── department.css # Стили кафедры
│ │ └── departments-data.css # Стили создания кафедры/специальности
│ ├── js/
│ │ ├── main.js # Инициализация, маршрутизация, навигация
@@ -43,9 +42,8 @@ frontend/
│ │ ├── subjects.js # Управление дисциплинами
│ │ ├── equipments.js # Управление оборудованием
│ │ ├── edu-forms.js # Формы обучения
│ │ ├── schedule.js # Расписание занятий
│ │ ├── schedule.js # Конструктор правил, сетки пар и учебного графика
│ │ ├── database.js # Управление тенантами
│ │ ├── department.js # Кафедры
│ │ └── departments-data.js # Создание кафедры/специальности
│ ├── views/ # HTML-шаблоны представлений
│ │ ├── users.html
@@ -56,7 +54,6 @@ frontend/
│ │ ├── edu-forms.html
│ │ ├── schedule.html
│ │ ├── database.html
│ │ ├── department.html
│ │ └── departments-data.html
│ │
│ └── settings/ # ⚙️ Страница настроек (отдельный SPA)
@@ -106,10 +103,15 @@ frontend/
| `equipments` | Оборудование | `/api/equipments` |
| `classrooms` | Аудитории | `/api/classrooms` |
| `subjects` | Дисциплины | `/api/subjects` |
| `schedule` | Старый админский экран расписания до удаления | `/api/users/lessons`; новые правила — `/api/admin/schedule-rules` |
| `schedule` | Конструктор правил динамического расписания, сетка пар, учебные годы, семестры, праздники и матрица графика | `/api/admin/schedule-rules`, `/api/admin/time-slots`, `/api/admin/calendar`, `/api/lesson-types` |
| `database` | Тенанты | `/api/database` |
| `department` | Кафедры | `/api/departments` |
| `departments-data` | Создание кафедры/специальности | `/api/departments` |
| `departments-data` | Создание, редактирование и удаление кафедр/специальностей | `/api/departments`, `/api/specialties` |
### Особенности админских вкладок
- Вкладка `groups` загружает кафедры и специальности из справочников и при создании группы отправляет `yearStartStudy`; поле `specialityCode` содержит ID выбранной специальности.
- Вкладка `schedule` не обращается к старым `lessons` API. Создание и редактирование расписания выполняется через правила `/api/admin/schedule-rules`, где каждое правило содержит группы и набор слотов.
- Вкладка `departments-data` использует модальные формы редактирования и маршруты `PUT/DELETE /api/departments/{id}` и `PUT/DELETE /api/specialties/{id}`.
### Страница настроек (`/admin/settings/`)
@@ -226,8 +228,7 @@ export function isAuthenticatedAsAdmin() {
2. **`layout.css`** — Sidebar, topbar, content area, dropdown настроек, responsive
3. **`components.css`** — Кнопки, таблицы, карточки, badge, формы, theme-toggle
4. **`modals.css`** — Модальные окна
5. **`department.css`** — Стили страницы кафедр
6. **`departments-data.css`** — Стили создания кафедры/специальности
5. **`departments-data.css`** — Стили создания кафедры/специальности
### Темизация

View File

@@ -94,7 +94,7 @@ MDC.remove("tenant.id");
| `TenantConfigWatcher` | INFO, ERROR, WARN | Изменения ConfigMap, Flyway миграции |
| `ConfigMapUpdater` | INFO, WARN, ERROR | Обновление ConfigMap в K8s |
| `DataInitializer` | INFO | Инициализация БД при старте |
| `LessonsController` | INFO, DEBUG, ERROR | CRUD-операции с занятиями, валидация |
| `ScheduleController` | INFO, ERROR | Запросы динамического расписания и ошибки генерации |
### Паттерн использования