526 lines
20 KiB
Markdown
526 lines
20 KiB
Markdown
# 🗄 База данных
|
||
|
||
## Общая информация
|
||
|
||
- **СУБД:** PostgreSQL (локально `postgres:alpine3.23`, продакшн — managed PostgreSQL)
|
||
- **Управление схемой:** Flyway (программный запуск)
|
||
- **Hibernate DDL:** Отключён (`ddl-auto=none`)
|
||
- **Расширения:** `pgcrypto` (bcrypt-хеширование паролей)
|
||
- **Мультитенантность:** Каждый тенант = отдельная БД
|
||
|
||
---
|
||
|
||
## ER-диаграмма
|
||
|
||
```mermaid
|
||
erDiagram
|
||
departments {
|
||
BIGSERIAL id PK
|
||
VARCHAR name
|
||
BIGINT code UK
|
||
}
|
||
|
||
specialties {
|
||
BIGSERIAL id PK
|
||
VARCHAR name
|
||
VARCHAR specialty_code
|
||
}
|
||
|
||
users {
|
||
BIGSERIAL id PK
|
||
VARCHAR username UK
|
||
VARCHAR password
|
||
VARCHAR role
|
||
VARCHAR full_name
|
||
VARCHAR job_title
|
||
BIGINT department_id FK
|
||
TIMESTAMP created_at
|
||
TIMESTAMP updated_at
|
||
}
|
||
|
||
education_forms {
|
||
BIGSERIAL id PK
|
||
VARCHAR name UK
|
||
TEXT description
|
||
TIMESTAMP created_at
|
||
}
|
||
|
||
student_groups {
|
||
BIGSERIAL id PK
|
||
VARCHAR name UK
|
||
BIGINT group_size
|
||
BIGINT education_form_id FK
|
||
BIGINT department_id FK
|
||
BIGINT specialty_code FK
|
||
BIGINT year_start_study
|
||
TIMESTAMP created_at
|
||
}
|
||
|
||
subgroups {
|
||
BIGSERIAL id PK
|
||
BIGINT group_id FK
|
||
VARCHAR name
|
||
INT student_capacity
|
||
}
|
||
|
||
subjects {
|
||
BIGSERIAL id PK
|
||
VARCHAR name UK
|
||
VARCHAR code
|
||
BIGINT department_id FK
|
||
TEXT description
|
||
TIMESTAMP created_at
|
||
}
|
||
|
||
lesson_types {
|
||
BIGSERIAL id PK
|
||
VARCHAR name UK
|
||
VARCHAR color_code
|
||
INT duration_minutes
|
||
}
|
||
|
||
equipments {
|
||
BIGSERIAL id PK
|
||
VARCHAR name UK
|
||
TEXT description
|
||
VARCHAR inventory_number
|
||
}
|
||
|
||
classrooms {
|
||
BIGSERIAL id PK
|
||
VARCHAR name UK
|
||
INT capacity
|
||
VARCHAR building
|
||
INT floor
|
||
BOOLEAN is_available
|
||
TEXT description
|
||
TIMESTAMP created_at
|
||
}
|
||
|
||
classroom_equipments {
|
||
BIGINT classroom_id FK,PK
|
||
BIGINT equipment_id FK,PK
|
||
INT quantity
|
||
TEXT notes
|
||
}
|
||
|
||
teacher_subjects {
|
||
BIGINT user_id FK,PK
|
||
BIGINT subject_id FK,PK
|
||
VARCHAR qualification_level
|
||
INT experience_years
|
||
}
|
||
|
||
teacher_lesson_types {
|
||
BIGINT user_id FK,PK
|
||
BIGINT subject_id FK,PK
|
||
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
|
||
TIME start_time
|
||
TIME end_time
|
||
INT duration_minutes
|
||
}
|
||
|
||
academic_years {
|
||
BIGSERIAL id PK
|
||
VARCHAR title UK
|
||
DATE start_date
|
||
DATE end_date
|
||
}
|
||
|
||
semesters {
|
||
BIGSERIAL id PK
|
||
BIGINT academic_year_id FK
|
||
VARCHAR semester_type
|
||
DATE start_date
|
||
DATE end_date
|
||
}
|
||
|
||
holidays {
|
||
BIGSERIAL id PK
|
||
DATE date
|
||
BIGINT academic_year_id FK
|
||
VARCHAR description
|
||
}
|
||
|
||
academic_calendar_matrix {
|
||
BIGSERIAL id PK
|
||
BIGINT semester_id FK
|
||
INT course_number
|
||
BIGINT specialty_id FK
|
||
INT week_number
|
||
VARCHAR activity_type
|
||
}
|
||
|
||
schedule_rules {
|
||
BIGSERIAL id PK
|
||
BIGINT subject_id FK
|
||
BIGINT semester_id FK
|
||
DATE active_from_date
|
||
INT total_academic_hours
|
||
}
|
||
|
||
schedule_rule_groups {
|
||
BIGINT schedule_rule_id FK,PK
|
||
BIGINT group_id FK,PK
|
||
}
|
||
|
||
schedule_rule_slots {
|
||
BIGSERIAL id PK
|
||
BIGINT schedule_rule_id FK
|
||
INT day_of_week
|
||
VARCHAR parity
|
||
BIGINT time_slot_id FK
|
||
BIGINT subgroup_id FK
|
||
BIGINT teacher_id FK
|
||
BIGINT classroom_id FK
|
||
BIGINT lesson_type_id FK
|
||
VARCHAR lesson_format
|
||
}
|
||
|
||
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"
|
||
academic_years ||--o{ semesters : "academic_year_id"
|
||
academic_years ||--o{ holidays : "academic_year_id"
|
||
semesters ||--o{ academic_calendar_matrix : "semester_id"
|
||
semesters ||--o{ schedule_rules : "semester_id"
|
||
specialties ||--o{ academic_calendar_matrix : "specialty_id"
|
||
schedule_rules ||--o{ schedule_rule_groups : "schedule_rule_id"
|
||
schedule_rules ||--o{ schedule_rule_slots : "schedule_rule_id"
|
||
time_slots ||--o{ schedule_rule_slots : "time_slot_id"
|
||
subgroups ||--o{ schedule_rule_slots : "subgroup_id"
|
||
users ||--o{ schedule_rule_slots : "teacher_id"
|
||
```
|
||
|
||
---
|
||
|
||
## Описание таблиц
|
||
|
||
### Справочники высшего уровня
|
||
|
||
#### `departments` — Кафедры
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID кафедры |
|
||
| `name` | VARCHAR(255) | Название кафедры |
|
||
| `code` | BIGINT UNIQUE | Код кафедры |
|
||
|
||
#### `specialties` — Специальности
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID специальности |
|
||
| `name` | VARCHAR(255) | Название специальности |
|
||
| `specialty_code` | VARCHAR(255) | Код ФГОС (напр. `10.03.01`) |
|
||
|
||
### Пользователи
|
||
|
||
#### `users` — Пользователи системы
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID пользователя |
|
||
| `username` | VARCHAR(50) UNIQUE | Логин |
|
||
| `password` | VARCHAR(255) | bcrypt-хеш пароля |
|
||
| `role` | VARCHAR(20) | `ADMIN`, `TEACHER`, `STUDENT` |
|
||
| `full_name` | VARCHAR(255) | ФИО |
|
||
| `job_title` | VARCHAR(255) | Должность |
|
||
| `department_id` | BIGINT FK → departments | Кафедра |
|
||
| `created_at` | TIMESTAMP | Дата создания |
|
||
| `updated_at` | TIMESTAMP | Дата обновления (авто-триггер) |
|
||
|
||
> **Триггер:** `update_users_updated_at` автоматически обновляет `updated_at` при любом `UPDATE`.
|
||
|
||
### Учебный процесс
|
||
|
||
#### `education_forms` — Формы обучения
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID |
|
||
| `name` | VARCHAR(100) UNIQUE | Название (Бакалавриат, Магистратура, Специалитет) |
|
||
| `description` | TEXT | Описание |
|
||
|
||
#### `student_groups` — Учебные группы
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID |
|
||
| `name` | VARCHAR(100) UNIQUE | Название группы (напр. `ИВТ-21-1`) |
|
||
| `group_size` | BIGINT | Количество студентов |
|
||
| `education_form_id` | BIGINT FK → education_forms | Форма обучения |
|
||
| `department_id` | BIGINT FK → departments | Кафедра |
|
||
| `specialty_code` | BIGINT FK → specialties | Специальность |
|
||
| `year_start_study` | BIGINT | Год начала обучения, используется для вычисления текущего курса |
|
||
|
||
#### `subgroups` — Подгруппы
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID |
|
||
| `group_id` | BIGINT FK → student_groups (CASCADE) | Родительская группа |
|
||
| `name` | VARCHAR(100) | Название подгруппы |
|
||
| `student_capacity` | INT | Количество студентов |
|
||
|
||
#### `subjects` — Дисциплины
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID |
|
||
| `name` | VARCHAR(200) UNIQUE | Название |
|
||
| `code` | VARCHAR(20) | Код предмета |
|
||
| `department_id` | BIGINT FK → departments | Кафедра |
|
||
| `description` | TEXT | Описание |
|
||
|
||
### Аудиторный фонд
|
||
|
||
#### `classrooms` — Аудитории
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID |
|
||
| `name` | VARCHAR(50) UNIQUE | Название (напр. `101 Ленинская`) |
|
||
| `capacity` | INT CHECK(> 0) | Вместимость |
|
||
| `building` | VARCHAR(50) | Корпус |
|
||
| `floor` | INT | Этаж |
|
||
| `is_available` | BOOLEAN | Доступна для назначения пар |
|
||
| `description` | TEXT | Описание |
|
||
|
||
#### `equipments` — Оборудование
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID |
|
||
| `name` | VARCHAR(50) UNIQUE | Название |
|
||
| `description` | TEXT | Описание |
|
||
| `inventory_number` | VARCHAR(50) | Инвентарный номер |
|
||
|
||
#### `classroom_equipments` — Привязка оборудования к аудиториям
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `classroom_id` | BIGINT PK, FK → classrooms (CASCADE) | Аудитория |
|
||
| `equipment_id` | BIGINT PK, FK → equipments (CASCADE) | Оборудование |
|
||
| `quantity` | INT CHECK(> 0) | Количество единиц |
|
||
| `notes` | TEXT | Примечания |
|
||
|
||
### Расписание
|
||
|
||
#### `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` — Типы занятий (справочник)
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID |
|
||
| `name` | VARCHAR(50) UNIQUE | Название типа |
|
||
| `color_code` | VARCHAR(7) | HEX-цвет для UI (напр. `#FF6B6B`) |
|
||
| `duration_minutes` | INT | Длительность (по умолчанию 90) |
|
||
|
||
### Связи «Преподаватель ↔ Дисциплина»
|
||
|
||
#### `teacher_subjects` — Квалификация преподавателей
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `user_id` | BIGINT PK, FK → users (CASCADE) | Преподаватель |
|
||
| `subject_id` | BIGINT PK, FK → subjects (CASCADE) | Дисциплина |
|
||
| `qualification_level` | VARCHAR(50) | Уровень квалификации |
|
||
| `experience_years` | INT | Стаж |
|
||
|
||
#### `teacher_lesson_types` — Типы занятий преподавателя
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `user_id` | BIGINT PK, FK → users (CASCADE) | Преподаватель |
|
||
| `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` — Временные слоты занятий
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID |
|
||
| `order_number` | INT UNIQUE | Номер пары в дне |
|
||
| `start_time` | TIME | Время начала |
|
||
| `end_time` | TIME | Время окончания |
|
||
| `duration_minutes` | INT | Длительность в минутах |
|
||
|
||
#### `academic_years` — Учебные годы
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID |
|
||
| `title` | VARCHAR(20) UNIQUE | Название, например `2025-2026` |
|
||
| `start_date` | DATE | Дата начала |
|
||
| `end_date` | DATE | Дата окончания |
|
||
|
||
#### `semesters` — Семестры
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID |
|
||
| `academic_year_id` | BIGINT FK → academic_years | Учебный год |
|
||
| `semester_type` | VARCHAR(20) | `autumn` или `spring` |
|
||
| `start_date` | DATE | Дата начала, от неё считается неделя 1 |
|
||
| `end_date` | DATE | Дата окончания |
|
||
|
||
#### `holidays` — Праздники и исключения
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID |
|
||
| `date` | DATE | Неучебная дата |
|
||
| `academic_year_id` | BIGINT FK → academic_years | Учебный год |
|
||
| `description` | VARCHAR(255) | Описание |
|
||
|
||
#### `academic_calendar_matrix` — Матрица учебного графика
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID |
|
||
| `semester_id` | BIGINT FK → semesters | Семестр |
|
||
| `course_number` | INT | Курс |
|
||
| `specialty_id` | BIGINT FK → specialties | Специальность |
|
||
| `week_number` | INT | Номер недели семестра |
|
||
| `activity_type` | VARCHAR(20) | `THEORY`, `EXAM`, `VACATION`, `PRACTICE` |
|
||
|
||
#### `schedule_rules` — Правила расписания
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID |
|
||
| `subject_id` | BIGINT FK → subjects | Дисциплина |
|
||
| `semester_id` | BIGINT FK → semesters | Семестр |
|
||
| `active_from_date` | DATE | Дата начала действия правила |
|
||
| `total_academic_hours` | INT | Лимит академических часов |
|
||
|
||
#### `schedule_rule_groups` — Группы правила
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `schedule_rule_id` | BIGINT PK, FK → schedule_rules | Правило |
|
||
| `group_id` | BIGINT PK, FK → student_groups | Группа |
|
||
|
||
#### `schedule_rule_slots` — Слоты правила
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|----------|
|
||
| `id` | BIGSERIAL PK | ID |
|
||
| `schedule_rule_id` | BIGINT FK → schedule_rules | Правило |
|
||
| `day_of_week` | INT CHECK(1–7) | День недели: 1 — понедельник |
|
||
| `parity` | VARCHAR(10) | `BOTH`, `EVEN`, `ODD` |
|
||
| `time_slot_id` | BIGINT FK → time_slots | Временной слот |
|
||
| `subgroup_id` | BIGINT FK → subgroups, NULL | Подгруппа |
|
||
| `teacher_id` | BIGINT FK → users | Преподаватель |
|
||
| `classroom_id` | BIGINT FK → classrooms | Аудитория |
|
||
| `lesson_type_id` | BIGINT FK → lesson_types | Тип занятия |
|
||
| `lesson_format` | VARCHAR(30) | `Очно` или `Онлайн` |
|
||
|
||
---
|
||
|
||
## Flyway миграции
|
||
|
||
### Правила работы
|
||
|
||
1. Все миграции находятся в `backend/src/main/resources/db/migration/`
|
||
2. Формат имени: `V{номер}__{описание}.sql` (напр. `V1__init.sql`, `V2__add_departments.sql`)
|
||
3. **ЗАПРЕЩЕНО** изменять уже закоммиченные файлы миграций — это сломает контрольные суммы Flyway
|
||
4. Flyway запускается **программно** при первом обращении к БД тенанта (`TenantConfigWatcher.initDatabaseForTenant()`)
|
||
5. Настройка `baselineOnMigrate=true` — если в БД уже есть данные, Flyway начнёт с baseline
|
||
|
||
### Текущие миграции
|
||
|
||
| Файл | Описание |
|
||
|------|----------|
|
||
| `V1__init.sql` | Инициализация: справочники, динамическое расписание, тестовые правила, триггеры, комментарии |
|
||
|
||
### Накатывание на существующих тенантов
|
||
|
||
Для применения новой миграции к уже существующим тенантам необходимо перезапустить backend:
|
||
|
||
```bash
|
||
# Kubernetes
|
||
kubectl rollout restart deployment backend -n magistr
|
||
|
||
# Docker Compose (локально)
|
||
docker compose restart backend
|
||
```
|
||
|
||
### Полный сброс БД (локально)
|
||
|
||
```bash
|
||
docker compose down -v # Удаляет volumes (данные)
|
||
docker compose up -d # Пересоздаёт БД с нуля
|
||
```
|