18 KiB
🔌 REST API
Все эндпоинты имеют префикс /api/. Ответы возвращаются в формате JSON.
Аутентификация
POST /api/auth/login
Вход в систему.
Тело запроса:
{
"username": "admin",
"password": "admin"
}
Успешный ответ (200):
{
"success": true,
"message": "OK",
"token": "550e8400-e29b-41d4-a716-446655440000",
"role": "ADMIN",
"redirect": "/admin/",
"departmentId": 1,
"userId": 1
}
Ошибка (401):
{
"success": false,
"message": "Неверное имя пользователя или пароль",
"token": null,
"role": null,
"redirect": null,
"departmentId": null,
"userId": null
}
После получения токена клиент должен передавать его в заголовке:
Authorization: Bearer <token>
Пользователи
GET /api/users
Список всех пользователей.
Ответ:
[
{ "id": 1, "username": "admin", "role": "ADMIN", "fullName": "Иванов Админ Иванович", "jobTitle": "Доцент", "departmentName": "Кафедра ИБ" },
{ "id": 2, "username": "Тестовый преподаватель", "role": "TEACHER", "fullName": "Петров Препод Петрович", "jobTitle": "Профессор", "departmentName": "Кафедра ВТ" }
]
GET /api/users/teachers
Список только преподавателей (роль TEACHER).
GET /api/users/teachers/{departmentId}
Список преподавателей привязанных к конкретной кафедре (роль TEACHER, код кафедры departmentId).
POST /api/users
Создание пользователя.
Тело запроса:
{
"username": "teacher1",
"password": "password",
"role": "TEACHER",
"fullName": "Test Teacher",
"jobTitle": "Proffessor",
"departmentId": 1
}
Валидация:
username— обязателен и уникаленpassword— минимум 4 символаrole—ADMIN,TEACHERилиSTUDENTfullName— обязателенdepartmentId— обязателен
DELETE /api/users/{id}
Удаление пользователя.
Динамическое расписание
Новая модель расписания строится из правил (schedule_rules) и слотов (schedule_rule_slots). Фактические занятия рендерятся на диапазон дат только для дней, где календарный учебный график группы имеет код, разрешающий обычные пары.
GET /api/schedule
Получение расписания группы или преподавателя за период.
Параметры:
| Параметр | Обязателен | Описание |
|---|---|---|
groupId |
Да, если нет teacherId |
ID учебной группы |
teacherId |
Да, если нет groupId |
ID преподавателя |
startDate |
Да | Начало периода в формате YYYY-MM-DD |
endDate |
Да | Конец периода в формате YYYY-MM-DD |
Передаётся ровно один параметр: groupId или teacherId. Максимальный диапазон — 120 дней. Если у группы нет назначения календарного графика на учебный год даты, расписание для неё возвращается пустым списком.
Пример:
GET /api/schedule?groupId=1&startDate=2026-04-27&endDate=2026-05-03
Ответ:
[
{
"scheduleRuleId": 10,
"scheduleRuleSlotId": 31,
"date": "2026-04-27",
"dayOfWeek": 1,
"dayName": "Понедельник",
"weekNumber": 13,
"parity": "ODD",
"timeSlotId": 3,
"timeSlotOrder": 3,
"startTime": "11:40:00",
"endTime": "13:10:00",
"subjectId": 1,
"subjectName": "Высшая математика",
"teacherId": 2,
"teacherName": "Петров Препод Петрович",
"classroomId": 1,
"classroomName": "101 Ленинская",
"lessonTypeId": 1,
"lessonTypeName": "Лекция",
"lessonFormat": "Очно",
"subgroupId": null,
"groupIds": [1],
"groupNames": ["ИВТ-21-1"],
"activityType": "Т",
"totalAcademicHours": 72,
"consumedAcademicHoursBeforeLesson": 24,
"remainingAcademicHoursAfterLesson": 46
}
]
GET /api/admin/time-slots
Список временных слотов занятий. CRUD доступен по:
| Метод | URL | Назначение |
|---|---|---|
GET |
/api/admin/time-slots |
Список слотов |
POST |
/api/admin/time-slots |
Создать слот |
PUT |
/api/admin/time-slots/{id} |
Обновить слот |
DELETE |
/api/admin/time-slots/{id} |
Удалить слот |
Тело создания/обновления:
{
"orderNumber": 1,
"startTime": "08:00:00",
"endTime": "09:30:00",
"durationMinutes": 90
}
Учебные годы, семестры и коды календарного графика
| Метод | URL | Назначение |
|---|---|---|
GET |
/api/admin/calendar/years |
Учебные годы с семестрами |
POST |
/api/admin/calendar/years |
Создать учебный год |
PUT |
/api/admin/calendar/years/{id} |
Обновить учебный год |
DELETE |
/api/admin/calendar/years/{id} |
Удалить учебный год |
GET |
/api/admin/calendar/years/{academicYearId}/semesters |
Семестры учебного года |
POST |
/api/admin/calendar/years/{academicYearId}/semesters |
Создать семестр |
PUT |
/api/admin/calendar/semesters/{id} |
Обновить семестр |
GET |
/api/admin/calendar/activity-types |
Справочник кодов активностей графика |
POST |
/api/admin/calendar/activity-types |
Создать код активности |
PUT |
/api/admin/calendar/activity-types/{id} |
Обновить код активности |
DELETE |
/api/admin/calendar/activity-types/{id} |
Удалить код активности |
Тело создания/обновления кода активности:
{
"code": "Т",
"name": "Теоретическое обучение",
"allowSchedule": true,
"colorCode": "#22c55e",
"displayOrder": 10,
"description": "Обычные пары разрешены"
}
Календарные учебные графики
| Метод | URL | Назначение |
|---|---|---|
GET |
/api/admin/academic-calendars |
Список графиков, фильтры academicYearId, specialtyId, profileId |
GET |
/api/admin/academic-calendars/study-forms |
Формы обучения графика |
GET |
/api/admin/academic-calendars/{id} |
Один график |
POST |
/api/admin/academic-calendars |
Создать график |
PUT |
/api/admin/academic-calendars/{id} |
Обновить график |
DELETE |
/api/admin/academic-calendars/{id} |
Удалить график |
GET |
/api/admin/academic-calendars/{id}/grid |
Дневная сетка графика |
PUT |
/api/admin/academic-calendars/{id}/grid |
Полное сохранение дневной сетки |
Тело создания/обновления графика:
{
"title": "09.03.04 очная форма 2025-2026",
"academicYearId": 1,
"specialtyId": 2,
"specialtyProfileId": 3,
"studyFormId": 1,
"courseCount": 4
}
Ячейка сетки графика:
{
"calendarId": 1,
"courseNumber": 1,
"date": "2025-09-01",
"weekNumber": 1,
"dayOfWeek": 1,
"activityTypeId": 1
}
POST /api/admin/schedule-rules
Создание правила динамического расписания.
{
"subjectId": 1,
"semesterId": 1,
"activeFromDate": "2026-02-01",
"totalAcademicHours": 72,
"groupIds": [1, 2],
"slots": [
{
"dayOfWeek": 1,
"parity": "BOTH",
"timeSlotId": 3,
"subgroupId": null,
"teacherId": 2,
"classroomId": 1,
"lessonTypeId": 1,
"lessonFormat": "Очно"
}
]
}
CRUD доступен по:
| Метод | URL | Назначение |
|---|---|---|
GET |
/api/admin/schedule-rules |
Список правил, фильтры semesterId, groupId |
GET |
/api/admin/schedule-rules/{id} |
Одно правило |
POST |
/api/admin/schedule-rules |
Создать правило |
PUT |
/api/admin/schedule-rules/{id} |
Обновить правило |
DELETE |
/api/admin/schedule-rules/{id} |
Удалить правило |
GET /api/lesson-types
Справочник типов занятий для конструктора правил расписания.
Ответ:
[
{ "id": 1, "name": "Лекция" },
{ "id": 2, "name": "Практика" }
]
Кафедры и специальности
Кафедры
| Метод | URL | Назначение |
|---|---|---|
GET |
/api/departments |
Список кафедр |
POST |
/api/departments |
Создать кафедру |
PUT |
/api/departments/{id} |
Обновить кафедру |
DELETE |
/api/departments/{id} |
Удалить кафедру |
Тело создания/обновления:
{
"departmentName": "Кафедра ИБ",
"departmentCode": 1
}
Специальности
| Метод | URL | Назначение |
|---|---|---|
GET |
/api/specialties |
Список специальностей |
POST |
/api/specialties |
Создать специальность |
PUT |
/api/specialties/{id} |
Обновить специальность |
DELETE |
/api/specialties/{id} |
Удалить специальность |
GET |
/api/specialties/{id}/profiles |
Профили выбранной специальности |
POST |
/api/specialties/{id}/profiles |
Создать профиль |
PUT |
/api/specialties/{id}/profiles/{profileId} |
Обновить профиль |
DELETE |
/api/specialties/{id}/profiles/{profileId} |
Удалить профиль |
Тело создания/обновления:
{
"specialityName": "Программная инженерия",
"specialityCode": "09.03.04"
}
При создании специальности автоматически создаётся профиль Без профиля.
Тело создания/обновления профиля:
{
"name": "Безопасность автоматизированных систем",
"description": "Необязательное описание"
}
Группы
GET /api/groups
Список всех групп.
Ответ:
[
{
"id": 1,
"name": "ИВТ-21-1",
"groupSize": 25,
"educationFormId": 1,
"educationFormName": "Бакалавриат",
"departmentId": 1,
"yearStartStudy": 2023,
"course": 3,
"semester": 6,
"specialtyId": 1,
"specialtyCode": "09.03.04",
"specialtyName": "Программная инженерия",
"specialtyProfileId": 2,
"specialtyProfileName": "Без профиля",
"specialityCode": 1
}
]
GET /api/groups/{departmentId}
Список всех групп привязанных к конкретной кафедре.
POST /api/groups
Создание группы.
{
"name": "ИВТ-11",
"groupSize": 12,
"educationFormId": 1,
"departmentId": 1,
"yearStartStudy": 2026,
"specialtyId": 1,
"specialtyProfileId": 2
}
specialtyId и specialtyProfileId обязательны. Поле specialityCode сохранено как legacy-alias для старых клиентов и исторически содержит ID записи из /api/specialties. Текущий курс вычисляется из yearStartStudy.
Название группы не является уникальным полем: допускается несколько групп с одинаковым name.
PUT /api/groups/{id}
Редактирование группы.
{
"name": "ИВТ-11",
"groupSize": 24,
"educationFormId": 1,
"departmentId": 1,
"yearStartStudy": 2026,
"specialtyId": 1,
"specialtyProfileId": 2
}
DELETE /api/groups/{id}
Удаление группы.
Календарные графики группы
| Метод | URL | Назначение |
|---|---|---|
GET |
/api/groups/{id}/calendar-assignments |
Список назначений графиков группе |
PUT |
/api/groups/{id}/calendar-assignments |
Создать или заменить назначение на учебный год |
DELETE |
/api/groups/{id}/calendar-assignments/{assignmentId} |
Удалить назначение |
Тело назначения графика:
{
"academicYearId": 1,
"calendarId": 5
}
Назначаемый график должен относиться к тому же учебному году, специальности и профилю, что и группа.
Аудитории
GET /api/classrooms
Список аудиторий с привязанным оборудованием.
Ответ:
[
{
"id": 1,
"name": "101 Ленинская",
"capacity": 120,
"isAvailable": true,
"equipments": [
{ "id": 1, "name": "Проектор" },
{ "id": 4, "name": "Интерактивная доска" }
]
}
]
POST /api/classrooms
Создание аудитории.
{
"name": "404 Лаборатория",
"capacity": 30,
"isAvailable": true,
"equipmentIds": [1, 2, 3]
}
PUT /api/classrooms/{id}
Обновление аудитории (partial update).
DELETE /api/classrooms/{id}
Удаление аудитории.
Дисциплины
GET /api/subjects
Список всех дисциплин.
{
"name": "Физика",
"code": null,
"departmentId": 1
}
GET /api/subjects/{departmentId}
Список всех дисциплин привязанных к кафедре.
POST /api/subjects
{
"name": "Физика",
"code": null,
"departmentId": 1
}
DELETE /api/subjects/{id}
Удаление дисциплины.
Оборудование
GET /api/equipments
Список всего оборудования.
POST /api/equipments
{ "name": "3D-принтер" }
DELETE /api/equipments/{id}
Удаление оборудования.
Формы обучения
GET /api/education-forms
Список форм обучения.
Ответ:
[
{ "id": 1, "name": "Бакалавриат" },
{ "id": 2, "name": "Магистратура" }
]
POST /api/education-forms
{ "name": "Аспирантура" }
DELETE /api/education-forms/{id}
Удаление формы обучения. Невозможно, если к ней привязаны группы.
Привязка «Преподаватель ↔ Дисциплина»
GET /api/teacher-subjects
Список всех привязок.
Ответ:
[
{
"userId": 2,
"userName": "Тестовый преподаватель",
"subjectId": 1,
"subjectName": "Высшая математика"
}
]
POST /api/teacher-subjects
{
"userId": 2,
"subjectId": 3
}
DELETE /api/teacher-subjects
{
"userId": 2,
"subjectId": 3
}
Управление тенантами (Базы данных)
GET /api/database/status
Статус текущего подключения (определяется по домену запроса).
Ответ:
{
"tenant": "default",
"connected": true,
"configured": true,
"name": "Default",
"url": "jdbc:postgresql://db:5432/app_db"
}
GET /api/database/tenants
Список всех тенантов.
POST /api/database/tenants
Добавление нового тенанта.
{
"name": "СВФУ",
"domain": "swsu",
"url": "jdbc:postgresql://db-host:5432/swsu_db",
"username": "dbuser",
"password": "dbpass"
}
Логика:
- Создаёт HikariCP пул для нового тенанта
- Запускает Flyway миграции на его БД
- Обновляет Kubernetes ConfigMap
DELETE /api/database/tenants/{domain}
Удаление тенанта.
POST /api/database/test
Тест подключения к произвольной БД (без регистрации тенанта).
{
"url": "jdbc:postgresql://host:5432/testdb",
"username": "user",
"password": "pass"
}
Ответ:
{
"success": true,
"message": "Подключение успешно!"
}
Коды ответов
| Код | Описание |
|---|---|
200 |
Успех |
400 |
Ошибка валидации (с message в теле) |
401 |
Неверные учётные данные |
404 |
Ресурс / тенант не найден |
500 |
Внутренняя ошибка сервера |