# 🗄 База данных ## Общая информация - **СУБД:** 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 INT course 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 } 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" student_groups ||--o{ subgroups : "group_id" student_groups ||--o{ lessons : "group_id" student_groups ||--o{ schedule_data : "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" lesson_types ||--o{ teacher_lesson_types : "lesson_type_id" lesson_types ||--o{ schedule_data : "lesson_type_id" classrooms ||--o{ lessons : "classroom_id" classrooms ||--o{ classroom_equipments : "classroom_id" equipments ||--o{ classroom_equipments : "equipment_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 | Кафедра | | `course` | INT CHECK(1–6) | Курс | #### `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` — Основное расписание занятий | Колонка | Тип | Описание | |---------|-----|----------| | `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` — Данные к составлению расписания | Колонка | Тип | Описание | |---------|-----|----------| | `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) | Учебный год | --- ## 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 # Пересоздаёт БД с нуля ```