доработал отображение учебного графика

This commit is contained in:
Zuev
2026-05-01 14:02:01 +03:00
parent 89c822a073
commit bf02efb8b8
28 changed files with 1644 additions and 1145 deletions

View File

@@ -21,7 +21,7 @@ public class AcademicCalendarController {
private final AcademicYearRepository academicYearRepository;
private final SpecialtiesRepository specialtiesRepository;
private final SpecialtyProfileRepository profileRepository;
private final CalendarStudyFormRepository studyFormRepository;
private final EducationFormRepository educationFormRepository;
private final ScheduleGeneratorService scheduleGeneratorService;
public AcademicCalendarController(AcademicCalendarRepository calendarRepository,
@@ -30,7 +30,7 @@ public class AcademicCalendarController {
AcademicYearRepository academicYearRepository,
SpecialtiesRepository specialtiesRepository,
SpecialtyProfileRepository profileRepository,
CalendarStudyFormRepository studyFormRepository,
EducationFormRepository educationFormRepository,
ScheduleGeneratorService scheduleGeneratorService) {
this.calendarRepository = calendarRepository;
this.calendarDayRepository = calendarDayRepository;
@@ -38,7 +38,7 @@ public class AcademicCalendarController {
this.academicYearRepository = academicYearRepository;
this.specialtiesRepository = specialtiesRepository;
this.profileRepository = profileRepository;
this.studyFormRepository = studyFormRepository;
this.educationFormRepository = educationFormRepository;
this.scheduleGeneratorService = scheduleGeneratorService;
}
@@ -51,13 +51,6 @@ public class AcademicCalendarController {
.toList();
}
@GetMapping("/study-forms")
public List<CalendarStudyFormDto> getStudyForms() {
return studyFormRepository.findAllByOrderByNameAsc().stream()
.map(form -> new CalendarStudyFormDto(form.getId(), form.getName(), form.getDescription()))
.toList();
}
@GetMapping("/{id}")
public ResponseEntity<?> getCalendar(@PathVariable Long id) {
return calendarRepository.findByIdWithDetails(id)
@@ -165,8 +158,8 @@ public class AcademicCalendarController {
if (!profile.getSpeciality().getId().equals(speciality.getId())) {
throw new IllegalArgumentException("Профиль не относится к выбранной специальности");
}
CalendarStudyForm studyForm = studyFormRepository.findById(request.studyFormId())
.orElseThrow(() -> new IllegalArgumentException("Форма обучения графика не найдена"));
EducationForm studyForm = educationFormRepository.findById(request.studyFormId())
.orElseThrow(() -> new IllegalArgumentException("Форма обучения не найдена"));
calendar.setTitle(request.title().trim());
calendar.setAcademicYear(year);
@@ -218,7 +211,7 @@ public class AcademicCalendarController {
AcademicYear year = calendar.getAcademicYear();
Speciality speciality = calendar.getSpeciality();
SpecialtyProfile profile = calendar.getSpecialtyProfile();
CalendarStudyForm studyForm = calendar.getStudyForm();
EducationForm studyForm = calendar.getStudyForm();
return new AcademicCalendarDto(
calendar.getId(),
calendar.getTitle(),

View File

@@ -2,6 +2,7 @@ package com.magistr.app.controller;
import com.magistr.app.model.EducationForm;
import com.magistr.app.model.StudentGroup;
import com.magistr.app.repository.AcademicCalendarRepository;
import com.magistr.app.repository.EducationFormRepository;
import com.magistr.app.repository.GroupRepository;
import org.springframework.http.ResponseEntity;
@@ -16,16 +17,19 @@ public class EducationFormController {
private final EducationFormRepository educationFormRepository;
private final GroupRepository groupRepository;
private final AcademicCalendarRepository academicCalendarRepository;
public EducationFormController(EducationFormRepository educationFormRepository,
GroupRepository groupRepository) {
GroupRepository groupRepository,
AcademicCalendarRepository academicCalendarRepository) {
this.educationFormRepository = educationFormRepository;
this.groupRepository = groupRepository;
this.academicCalendarRepository = academicCalendarRepository;
}
@GetMapping
public List<Map<String, Object>> getAll() {
return educationFormRepository.findAll().stream()
return educationFormRepository.findAllByOrderByNameAsc().stream()
.map(ef -> Map.<String, Object>of("id", ef.getId(), "name", ef.getName()))
.toList();
}
@@ -54,12 +58,17 @@ public class EducationFormController {
return ResponseEntity.notFound().build();
}
// Check if any groups use this education form
// Нельзя удалить форму обучения, пока она используется группами или графиками.
List<StudentGroup> linked = groupRepository.findByEducationFormId(id);
if (!linked.isEmpty()) {
return ResponseEntity.badRequest().body(Map.of(
"message", "Невозможно удалить: есть привязанные группы (" + linked.size() + ")"));
}
long linkedCalendars = academicCalendarRepository.countByStudyFormId(id);
if (linkedCalendars > 0) {
return ResponseEntity.badRequest().body(Map.of(
"message", "Невозможно удалить: есть привязанные календарные графики (" + linkedCalendars + ")"));
}
educationFormRepository.deleteById(id);
return ResponseEntity.ok(Map.of("message", "Форма обучения удалена"));

View File

@@ -294,6 +294,9 @@ public class GroupController {
|| !calendar.getSpecialtyProfile().getId().equals(group.getSpecialtyProfile().getId())) {
return ResponseEntity.badRequest().body(Map.of("message", "График не соответствует специальности и профилю группы"));
}
if (!calendar.getStudyForm().getId().equals(group.getEducationForm().getId())) {
return ResponseEntity.badRequest().body(Map.of("message", "График не соответствует форме обучения группы"));
}
StudentGroupCalendarAssignment assignment = assignmentRepository
.findByGroupIdAndAcademicYearIdWithDetails(group.getId(), academicYear.getId())

View File

@@ -1,8 +0,0 @@
package com.magistr.app.dto;
public record CalendarStudyFormDto(
Long id,
String name,
String description
) {
}

View File

@@ -27,7 +27,7 @@ public class AcademicCalendar {
@ManyToOne(optional = false)
@JoinColumn(name = "study_form_id", nullable = false)
private CalendarStudyForm studyForm;
private EducationForm studyForm;
@Column(name = "course_count", nullable = false)
private Integer courseCount;
@@ -72,11 +72,11 @@ public class AcademicCalendar {
this.specialtyProfile = specialtyProfile;
}
public CalendarStudyForm getStudyForm() {
public EducationForm getStudyForm() {
return studyForm;
}
public void setStudyForm(CalendarStudyForm studyForm) {
public void setStudyForm(EducationForm studyForm) {
this.studyForm = studyForm;
}

View File

@@ -1,42 +0,0 @@
package com.magistr.app.model;
import jakarta.persistence.*;
@Entity
@Table(name = "calendar_study_forms")
public class CalendarStudyForm {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 100)
private String name;
@Column(columnDefinition = "TEXT")
private String description;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@@ -10,6 +10,8 @@ import java.util.Optional;
public interface AcademicCalendarRepository extends JpaRepository<AcademicCalendar, Long> {
long countByStudyFormId(Long studyFormId);
@Query("""
select calendar
from AcademicCalendar calendar

View File

@@ -1,11 +0,0 @@
package com.magistr.app.repository;
import com.magistr.app.model.CalendarStudyForm;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface CalendarStudyFormRepository extends JpaRepository<CalendarStudyForm, Long> {
List<CalendarStudyForm> findAllByOrderByNameAsc();
}

View File

@@ -3,9 +3,12 @@ package com.magistr.app.repository;
import com.magistr.app.model.EducationForm;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface EducationFormRepository extends JpaRepository<EducationForm, Long> {
Optional<EducationForm> findByName(String name);
List<EducationForm> findAllByOrderByNameAsc();
}

View File

@@ -295,17 +295,6 @@ ON CONFLICT (academic_year_id, semester_type) DO NOTHING;
CREATE INDEX IF NOT EXISTS idx_semesters_dates ON semesters(start_date, end_date);
CREATE TABLE IF NOT EXISTS calendar_study_forms (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) UNIQUE NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO calendar_study_forms (name, description) VALUES
('очная', 'Очная форма обучения')
ON CONFLICT (name) DO NOTHING;
CREATE TABLE IF NOT EXISTS academic_calendar_activity_types (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(10) UNIQUE NOT NULL,
@@ -338,7 +327,7 @@ CREATE TABLE IF NOT EXISTS academic_calendars (
academic_year_id BIGINT NOT NULL REFERENCES academic_years(id) ON DELETE CASCADE,
specialty_id BIGINT NOT NULL REFERENCES specialties(id),
specialty_profile_id BIGINT NOT NULL REFERENCES specialty_profiles(id),
study_form_id BIGINT NOT NULL REFERENCES calendar_study_forms(id),
study_form_id BIGINT NOT NULL REFERENCES education_forms(id),
course_count INT NOT NULL DEFAULT 4,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
@@ -358,7 +347,7 @@ SELECT
study_form.id,
4
FROM academic_years ay
CROSS JOIN calendar_study_forms study_form
CROSS JOIN education_forms study_form
JOIN specialties specialty ON specialty.specialty_code IN ('09.03.01', '10.03.01')
JOIN specialty_profiles profile ON profile.specialty_id = specialty.id AND profile.name = 'Без профиля'
WHERE ay.title = '2025-2026'
@@ -569,7 +558,6 @@ COMMENT ON TABLE specialty_profiles IS 'Профили обучения внут
COMMENT ON TABLE time_slots IS 'Настраиваемая сетка временных слотов занятий';
COMMENT ON TABLE academic_years IS 'Учебные годы';
COMMENT ON TABLE semesters IS 'Семестры учебного года';
COMMENT ON TABLE calendar_study_forms IS 'Формы обучения календарного учебного графика';
COMMENT ON TABLE academic_calendar_activity_types IS 'Коды активностей календарного учебного графика';
COMMENT ON TABLE academic_calendars IS 'Календарные учебные графики по профилю, форме обучения и учебному году';
COMMENT ON TABLE academic_calendar_days IS 'Дневные ячейки календарного учебного графика';
@@ -666,10 +654,6 @@ COMMENT ON COLUMN specialty_profiles.specialty_id IS 'ID специальнос
COMMENT ON COLUMN specialty_profiles.name IS 'Название профиля обучения';
COMMENT ON COLUMN specialty_profiles.description IS 'Описание профиля обучения';
COMMENT ON COLUMN calendar_study_forms.id IS 'ID формы обучения графика';
COMMENT ON COLUMN calendar_study_forms.name IS 'Название формы обучения графика';
COMMENT ON COLUMN calendar_study_forms.description IS 'Описание формы обучения графика';
COMMENT ON COLUMN academic_calendar_activity_types.code IS 'Код активности из календарного графика';
COMMENT ON COLUMN academic_calendar_activity_types.name IS 'Название активности';
COMMENT ON COLUMN academic_calendar_activity_types.allow_schedule IS 'Разрешает ли код генерацию обычного расписания';
@@ -678,7 +662,7 @@ COMMENT ON COLUMN academic_calendars.title IS 'Название календар
COMMENT ON COLUMN academic_calendars.academic_year_id IS 'ID учебного года';
COMMENT ON COLUMN academic_calendars.specialty_id IS 'ID специальности';
COMMENT ON COLUMN academic_calendars.specialty_profile_id IS 'ID профиля обучения';
COMMENT ON COLUMN academic_calendars.study_form_id IS 'ID формы обучения графика';
COMMENT ON COLUMN academic_calendars.study_form_id IS 'ID формы обучения из education_forms';
COMMENT ON COLUMN academic_calendars.course_count IS 'Количество курсов в графике';
COMMENT ON COLUMN academic_calendar_days.calendar_id IS 'ID календарного учебного графика';