Миллион изменений, создание вкладки "Расписание занятий"
This commit is contained in:
112
docs/API.md
112
docs/API.md
@@ -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}`
|
||||
|
||||
Удаление группы.
|
||||
|
||||
@@ -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` обязателен и хранится в слоте правила.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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` — Временные слоты занятий
|
||||
| Колонка | Тип | Описание |
|
||||
|---------|-----|----------|
|
||||
|
||||
@@ -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/ # Общие утилиты
|
||||
```
|
||||
|
||||
@@ -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`** — Стили создания кафедры/специальности
|
||||
|
||||
### Темизация
|
||||
|
||||
|
||||
@@ -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 | Запросы динамического расписания и ошибки генерации |
|
||||
|
||||
### Паттерн использования
|
||||
|
||||
|
||||
Reference in New Issue
Block a user