diff --git a/backend/src/main/java/com/magistr/app/controller/GroupController.java b/backend/src/main/java/com/magistr/app/controller/GroupController.java index c5eb562..257be18 100755 --- a/backend/src/main/java/com/magistr/app/controller/GroupController.java +++ b/backend/src/main/java/com/magistr/app/controller/GroupController.java @@ -46,7 +46,9 @@ public class GroupController { g.getEducationForm().getId(), g.getEducationForm().getName(), g.getDepartmentId(), + g.getEnrollmentYear(), g.getCourse(), + g.getSemester(), g.getSpecialityCode() )) .toList(); @@ -82,8 +84,8 @@ public class GroupController { @PostMapping public ResponseEntity> createGroup(@RequestBody CreateGroupRequest request) { - logger.info("Получен запрос на создание новой группы: name = {}, groupSize = {}, educationFormId = {}, departmentId = {}, course = {}", - request.getName(), request.getGroupSize(), request.getEducationFormId(), request.getDepartmentId(), request.getCourse()); + logger.info("Получен запрос на создание новой группы: name = {}, groupSize = {}, educationFormId = {}, departmentId = {}, enrollmentYear = {}", + request.getName(), request.getGroupSize(), request.getEducationFormId(), request.getDepartmentId(), request.getEnrollmentYear()); try { if (request.getName() == null || request.getName().isBlank()) { String errorMessage = "Название группы обязательно"; @@ -110,8 +112,8 @@ public class GroupController { logger.error("Ошибка валидации: {}", errorMessage); return ResponseEntity.badRequest().body(Map.of("message", errorMessage)); } - if (request.getCourse() == null || request.getCourse() == 0) { - String errorMessage = "Курс обязателен"; + if (request.getEnrollmentYear() == null || request.getEnrollmentYear() == 0) { + String errorMessage = "Год начала обучения обязателен"; logger.error("Ошибка валидации: {}", errorMessage); return ResponseEntity.badRequest().body(Map.of("message", errorMessage)); } @@ -131,7 +133,7 @@ public class GroupController { group.setGroupSize(request.getGroupSize()); group.setEducationForm(efOpt.get()); group.setDepartmentId(request.getDepartmentId()); - group.setCourse(request.getCourse()); + group.setEnrollmentYear(request.getEnrollmentYear()); group.setSpecialityCode(request.getSpecialityCode()); groupRepository.save(group); @@ -144,7 +146,9 @@ public class GroupController { group.getEducationForm().getId(), group.getEducationForm().getName(), group.getDepartmentId(), + group.getEnrollmentYear(), group.getCourse(), + group.getSemester(), group.getSpecialityCode())); } catch (Exception e ) { logger.error("Ошибка при создании группы: {}", e.getMessage(), e); diff --git a/backend/src/main/java/com/magistr/app/dto/CreateGroupRequest.java b/backend/src/main/java/com/magistr/app/dto/CreateGroupRequest.java index 94561b0..c8963e6 100755 --- a/backend/src/main/java/com/magistr/app/dto/CreateGroupRequest.java +++ b/backend/src/main/java/com/magistr/app/dto/CreateGroupRequest.java @@ -6,7 +6,7 @@ public class CreateGroupRequest { private Long groupSize; private Long educationFormId; private Long departmentId; - private Integer course; + private Integer enrollmentYear; private Long specialityCode; public String getName() { @@ -41,12 +41,12 @@ public class CreateGroupRequest { this.departmentId = departmentId; } - public Integer getCourse() { - return course; + public Integer getEnrollmentYear() { + return enrollmentYear; } - public void setCourse(Integer course) { - this.course = course; + public void setEnrollmentYear(Integer enrollmentYear) { + this.enrollmentYear = enrollmentYear; } public Long getSpecialityCode() { diff --git a/backend/src/main/java/com/magistr/app/dto/GroupResponse.java b/backend/src/main/java/com/magistr/app/dto/GroupResponse.java index b93cd93..7356515 100755 --- a/backend/src/main/java/com/magistr/app/dto/GroupResponse.java +++ b/backend/src/main/java/com/magistr/app/dto/GroupResponse.java @@ -8,17 +8,24 @@ public class GroupResponse { private Long educationFormId; private String educationFormName; private Long departmentId; + private Integer enrollmentYear; private Integer course; + private Integer semester; private Long specialityCode; - public GroupResponse(Long id, String name, Long groupSize, Long educationFormId, String educationFormName, Long departmentId, Integer course, Long specialityCode) { + public GroupResponse(Long id, String name, Long groupSize, Long educationFormId, + String educationFormName, Long departmentId, + Integer enrollmentYear, Integer course, Integer semester, + Long specialityCode) { this.id = id; this.name = name; this.groupSize = groupSize; this.educationFormId = educationFormId; this.educationFormName = educationFormName; this.departmentId = departmentId; + this.enrollmentYear = enrollmentYear; this.course = course; + this.semester = semester; this.specialityCode = specialityCode; } @@ -46,10 +53,18 @@ public class GroupResponse { return departmentId; } + public Integer getEnrollmentYear() { + return enrollmentYear; + } + public Integer getCourse() { return course; } + public Integer getSemester() { + return semester; + } + public Long getSpecialityCode() { return specialityCode; } diff --git a/backend/src/main/java/com/magistr/app/model/StudentGroup.java b/backend/src/main/java/com/magistr/app/model/StudentGroup.java index f901887..93cac8d 100755 --- a/backend/src/main/java/com/magistr/app/model/StudentGroup.java +++ b/backend/src/main/java/com/magistr/app/model/StudentGroup.java @@ -1,5 +1,6 @@ package com.magistr.app.model; +import com.magistr.app.utils.CourseCalculator; import jakarta.persistence.*; @Entity @@ -23,8 +24,8 @@ public class StudentGroup { @Column(name = "department_id", nullable = false) private Long departmentId; - @Column(name = "course", nullable = false) - private Integer course; + @Column(name = "enrollment_year", nullable = false) + private Integer enrollmentYear; @Column(name="specialty_code", nullable = false) private Long specialityCode; @@ -72,12 +73,30 @@ public class StudentGroup { this.departmentId = departmentId; } - public Integer getCourse() { - return course; + public Integer getEnrollmentYear() { + return enrollmentYear; } - public void setCourse(Integer course) { - this.course = course; + public void setEnrollmentYear(Integer enrollmentYear) { + this.enrollmentYear = enrollmentYear; + } + + /** + * Вычисляемый курс на основе года начала обучения. + */ + @Transient + public Integer getCourse() { + if (enrollmentYear == null) return null; + return CourseCalculator.calculateCourse(enrollmentYear); + } + + /** + * Вычисляемый семестр на основе года начала обучения. + */ + @Transient + public Integer getSemester() { + if (enrollmentYear == null) return null; + return CourseCalculator.calculateSemester(enrollmentYear); } public Long getSpecialityCode() { diff --git a/backend/src/main/java/com/magistr/app/utils/CourseCalculator.java b/backend/src/main/java/com/magistr/app/utils/CourseCalculator.java new file mode 100644 index 0000000..ecc3bff --- /dev/null +++ b/backend/src/main/java/com/magistr/app/utils/CourseCalculator.java @@ -0,0 +1,53 @@ +package com.magistr.app.utils; + +import java.time.LocalDate; + +/** + * Утилитный класс для вычисления курса и семестра группы + * на основе года начала обучения. + */ +public final class CourseCalculator { + + private CourseCalculator() { + } + + /** + * Вычисляет текущий курс группы. + * До сентября студент ещё на старом курсе, с сентября — на следующем. + * + * @param enrollmentYear год начала обучения (напр. 2023) + * @return номер курса (1, 2, 3, ...) + */ + public static int calculateCourse(int enrollmentYear) { + LocalDate now = LocalDate.now(); + int currentYear = now.getYear(); + int currentMonth = now.getMonthValue(); + + // С сентября начинается новый учебный год + if (currentMonth >= 9) { + return currentYear - enrollmentYear + 1; + } else { + return currentYear - enrollmentYear; + } + } + + /** + * Вычисляет текущий семестр группы. + * Сентябрь–январь → нечётный (осенний) семестр, февраль–август → чётный (весенний). + * + * @param enrollmentYear год начала обучения (напр. 2023) + * @return номер семестра (1, 2, 3, ...) + */ + public static int calculateSemester(int enrollmentYear) { + int course = calculateCourse(enrollmentYear); + int currentMonth = LocalDate.now().getMonthValue(); + + // Сентябрь–январь: осенний (нечётный) семестр + // Февраль–август: весенний (чётный) семестр + if (currentMonth >= 9 || currentMonth <= 1) { + return (course - 1) * 2 + 1; + } else { + return (course - 1) * 2 + 2; + } + } +} diff --git a/backend/src/main/resources/db/migration/V2__editScheduleData.sql b/backend/src/main/resources/db/migration/V2__editScheduleData.sql index a99481c..405747f 100644 --- a/backend/src/main/resources/db/migration/V2__editScheduleData.sql +++ b/backend/src/main/resources/db/migration/V2__editScheduleData.sql @@ -26,4 +26,25 @@ VALUES (1, 1, 1, 1, 3, 2, true, 1, 'autumn', '2024-2025'), (1, 1, 1, 1, 1, 2, true, 2, 'autumn', '2024-2025'), (1, 2, 2, 2, 3, 4, false, 2, 'autumn', '2024-2025'), (1, 3, 1, 4, 2, 1, false, 1, 'autumn', '2024-2025'), - (1, 4, 2, 5, 1, 7, true, 1, 'autumn', '2024-2025'); \ No newline at end of file + (1, 4, 2, 5, 1, 7, true, 1, 'autumn', '2024-2025'); + +-- ========================================== +-- Год начала обучения вместо статического курса +-- ========================================== + +ALTER TABLE student_groups +ADD COLUMN IF NOT EXISTS enrollment_year INT; + +-- Обратный расчёт: enrollment_year = текущий_год - course + 1 +-- (для месяцев до сентября курс ещё не увеличился) +UPDATE student_groups +SET enrollment_year = EXTRACT(YEAR FROM NOW())::INT - course + + CASE WHEN EXTRACT(MONTH FROM NOW()) >= 9 THEN 1 ELSE 0 END; + +ALTER TABLE student_groups +ALTER COLUMN enrollment_year SET NOT NULL; + +ALTER TABLE student_groups +DROP COLUMN IF EXISTS course; + +COMMENT ON COLUMN student_groups.enrollment_year IS 'Год начала обучения группы'; \ No newline at end of file diff --git a/docs/DATABASE.md b/docs/DATABASE.md index c025547..05c0d75 100644 --- a/docs/DATABASE.md +++ b/docs/DATABASE.md @@ -51,7 +51,8 @@ erDiagram BIGINT group_size BIGINT education_form_id FK BIGINT department_id FK - INT course + INT enrollment_year + INT specialty_code FK TIMESTAMP created_at } @@ -220,7 +221,10 @@ erDiagram | `group_size` | BIGINT | Количество студентов | | `education_form_id` | BIGINT FK → education_forms | Форма обучения | | `department_id` | BIGINT FK → departments | Кафедра | -| `course` | INT CHECK(1–6) | Курс | +| `enrollment_year` | INT NOT NULL | Год начала обучения (напр. 2023) | +| `specialty_code` | INT FK → specialties | Код специальности | + +> **Примечание:** Курс и семестр **вычисляются динамически** на основе `enrollment_year` и текущей даты (утилита `CourseCalculator.java`). В БД не хранятся. #### `subgroups` — Подгруппы | Колонка | Тип | Описание | @@ -341,6 +345,7 @@ erDiagram | Файл | Описание | |------|----------| | `V1__init.sql` | Инициализация: все таблицы, тестовые данные, триггеры, комментарии | +| `V2__editScheduleData.sql` | Добавление `specialty_code`, тестовые данные расписания, замена `course` → `enrollment_year` | ### Накатывание на существующих тенантов diff --git a/frontend/admin/js/views/groups.js b/frontend/admin/js/views/groups.js index cd23644..33b7a65 100755 --- a/frontend/admin/js/views/groups.js +++ b/frontend/admin/js/views/groups.js @@ -17,7 +17,7 @@ export async function initGroups() { populateEfSelects(educationForms); await loadGroups(); } catch (e) { - groupsTbody.innerHTML = '