Много разных изменений
This commit is contained in:
@@ -8,6 +8,8 @@ import com.magistr.app.service.AcademicCalendarGridService;
|
||||
import com.magistr.app.service.AcademicStructureService;
|
||||
import com.magistr.app.service.ScheduleGeneratorService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@@ -18,6 +20,8 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@@ -58,6 +62,32 @@ public class AcademicCalendarController {
|
||||
.toList();
|
||||
}
|
||||
|
||||
@GetMapping("/options")
|
||||
public List<AcademicCalendarDto> searchCalendarOptions(
|
||||
@RequestParam(defaultValue = "") String query,
|
||||
@RequestParam(required = false) Long academicYearId,
|
||||
@RequestParam(required = false) Long specialtyId,
|
||||
@RequestParam(required = false) Long profileId,
|
||||
@RequestParam(required = false) Long studyFormId,
|
||||
@RequestParam(required = false) Long selectedId,
|
||||
@RequestParam(defaultValue = "20") int limit) {
|
||||
String normalizedQuery = query == null ? "" : query.trim().toLowerCase(Locale.ROOT);
|
||||
List<AcademicCalendarDto> options = new ArrayList<>(calendarRepository.searchOptions(
|
||||
normalizedQuery,
|
||||
academicYearId,
|
||||
specialtyId,
|
||||
profileId,
|
||||
studyFormId,
|
||||
PageRequest.of(0, Math.max(1, Math.min(limit, 50)), Sort.by("title").ascending())
|
||||
).stream()
|
||||
.map(this::toCalendarDto)
|
||||
.toList());
|
||||
if (selectedId != null && options.stream().noneMatch(item -> selectedId.equals(item.id()))) {
|
||||
calendarRepository.findByIdWithDetails(selectedId).map(this::toCalendarDto).ifPresent(options::add);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<?> getCalendar(@PathVariable Long id) {
|
||||
return calendarRepository.findByIdWithDetails(id)
|
||||
|
||||
@@ -10,11 +10,15 @@ import com.magistr.app.model.Role;
|
||||
import com.magistr.app.repository.ClassroomRepository;
|
||||
import com.magistr.app.repository.EquipmentRepository;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/classrooms")
|
||||
@@ -40,6 +44,26 @@ public class ClassroomController {
|
||||
.toList();
|
||||
}
|
||||
|
||||
@GetMapping("/options")
|
||||
@RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE, Role.DEPARTMENT, Role.SCHEDULE_VIEWER})
|
||||
public List<ClassroomResponse> searchClassroomOptions(
|
||||
@RequestParam(defaultValue = "") String query,
|
||||
@RequestParam(required = false) Long selectedId,
|
||||
@RequestParam(defaultValue = "20") int limit) {
|
||||
String normalizedQuery = query == null ? "" : query.trim().toLowerCase(Locale.ROOT);
|
||||
List<ClassroomResponse> options = new ArrayList<>(classroomRepository.searchActiveOptions(
|
||||
normalizedQuery,
|
||||
LifecycleEntity.STATUS_ARCHIVED,
|
||||
PageRequest.of(0, Math.max(1, Math.min(limit, 50)), Sort.by("name").ascending())
|
||||
).stream()
|
||||
.map(this::mapToResponse)
|
||||
.toList());
|
||||
if (selectedId != null && options.stream().noneMatch(item -> selectedId.equals(item.getId()))) {
|
||||
classroomRepository.findById(selectedId).map(this::mapToResponse).ifPresent(options::add);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<?> createClassroom(@RequestBody ClassroomRequest request) {
|
||||
if (request.getName() == null || request.getName().isBlank()) {
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package com.magistr.app.controller;
|
||||
|
||||
import com.magistr.app.config.auth.AuthContext;
|
||||
import com.magistr.app.config.auth.RequireRoles;
|
||||
import com.magistr.app.dto.AcademicCalendarSubjectDto;
|
||||
import com.magistr.app.dto.CreateGroupRequest;
|
||||
import com.magistr.app.dto.GroupCalendarAssignmentDto;
|
||||
import com.magistr.app.dto.GroupResponse;
|
||||
import com.magistr.app.dto.PageResponse;
|
||||
import com.magistr.app.model.LifecycleEntity;
|
||||
import com.magistr.app.model.AcademicCalendarSubject;
|
||||
import com.magistr.app.model.Role;
|
||||
import com.magistr.app.model.Speciality;
|
||||
@@ -24,12 +27,19 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collections;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -98,6 +108,118 @@ public class GroupController {
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
public PageResponse<GroupResponse> getGroupsPage(
|
||||
@RequestParam(defaultValue = "") String query,
|
||||
@RequestParam(required = false) List<Long> educationFormId,
|
||||
@RequestParam(required = false) Long departmentId,
|
||||
@RequestParam(required = false) Integer yearStartStudy,
|
||||
@RequestParam(defaultValue = "true") boolean includeArchived,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "25") int size,
|
||||
@RequestParam(defaultValue = "name") String sort,
|
||||
@RequestParam(defaultValue = "asc") String direction) {
|
||||
Long effectiveDepartmentId = effectiveDepartmentId(departmentId);
|
||||
Specification<StudentGroup> specification = groupSpecification(
|
||||
query, educationFormId, effectiveDepartmentId, yearStartStudy, includeArchived
|
||||
);
|
||||
Page<StudentGroup> groups = groupRepository.findAll(
|
||||
specification,
|
||||
PageRequest.of(normalizePage(page), normalizeSize(size), groupSort(sort, direction))
|
||||
);
|
||||
return PageResponse.from(groups.map(this::mapToResponse));
|
||||
}
|
||||
|
||||
@GetMapping("/options")
|
||||
public List<GroupResponse> searchGroupOptions(
|
||||
@RequestParam(defaultValue = "") String query,
|
||||
@RequestParam(required = false) Long departmentId,
|
||||
@RequestParam(required = false) Long selectedId,
|
||||
@RequestParam(defaultValue = "20") int limit) {
|
||||
Long effectiveDepartmentId = effectiveDepartmentId(departmentId);
|
||||
List<GroupResponse> options = new ArrayList<>(groupRepository.findAll(
|
||||
groupSpecification(query, null, effectiveDepartmentId, null, false),
|
||||
PageRequest.of(0, Math.min(normalizeSize(limit), 50), Sort.by("name").ascending())
|
||||
).stream()
|
||||
.filter(group -> groupLifecycleService.isAvailableForSelection(group, businessTime.today()))
|
||||
.map(this::mapToResponse)
|
||||
.toList());
|
||||
if (selectedId != null && options.stream().noneMatch(item -> selectedId.equals(item.getId()))) {
|
||||
groupRepository.findById(selectedId)
|
||||
.filter(group -> effectiveDepartmentId == null
|
||||
|| Objects.equals(group.getDepartmentId(), effectiveDepartmentId))
|
||||
.filter(group -> groupLifecycleService.isAvailableForSelection(group, businessTime.today()))
|
||||
.map(this::mapToResponse)
|
||||
.ifPresent(options::add);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
private Long effectiveDepartmentId(Long requestedDepartmentId) {
|
||||
var currentUser = AuthContext.getCurrentUser();
|
||||
return currentUser != null && currentUser.role() == Role.DEPARTMENT
|
||||
? currentUser.departmentId()
|
||||
: requestedDepartmentId;
|
||||
}
|
||||
|
||||
private Specification<StudentGroup> groupSpecification(String query,
|
||||
List<Long> educationFormIds,
|
||||
Long departmentId,
|
||||
Integer yearStartStudy,
|
||||
boolean includeArchived) {
|
||||
return (root, criteriaQuery, builder) -> {
|
||||
List<jakarta.persistence.criteria.Predicate> predicates = new ArrayList<>();
|
||||
if (!includeArchived) {
|
||||
predicates.add(builder.notEqual(root.get("status"), LifecycleEntity.STATUS_ARCHIVED));
|
||||
}
|
||||
if (educationFormIds != null && !educationFormIds.isEmpty()) {
|
||||
predicates.add(root.get("educationForm").get("id").in(educationFormIds));
|
||||
}
|
||||
if (departmentId != null) {
|
||||
predicates.add(builder.equal(root.get("departmentId"), departmentId));
|
||||
}
|
||||
if (yearStartStudy != null) {
|
||||
predicates.add(builder.equal(root.get("yearStartStudy"), yearStartStudy));
|
||||
}
|
||||
String normalizedQuery = normalizeQuery(query);
|
||||
if (!normalizedQuery.isEmpty()) {
|
||||
String pattern = "%" + normalizedQuery + "%";
|
||||
predicates.add(builder.or(
|
||||
builder.like(builder.lower(root.get("name")), pattern),
|
||||
builder.like(builder.lower(root.get("speciality").get("specialityCode")), pattern),
|
||||
builder.like(builder.lower(root.get("speciality").get("specialityName")), pattern),
|
||||
builder.like(builder.lower(root.get("specialtyProfile").get("name")), pattern),
|
||||
builder.like(builder.lower(root.get("educationForm").get("name")), pattern)
|
||||
));
|
||||
}
|
||||
return builder.and(predicates.toArray(jakarta.persistence.criteria.Predicate[]::new));
|
||||
};
|
||||
}
|
||||
|
||||
private Sort groupSort(String field, String direction) {
|
||||
String property = switch (field) {
|
||||
case "yearStartStudy" -> "yearStartStudy";
|
||||
case "groupSize" -> "groupSize";
|
||||
case "status" -> "status";
|
||||
default -> "name";
|
||||
};
|
||||
return "desc".equalsIgnoreCase(direction)
|
||||
? Sort.by(property).descending()
|
||||
: Sort.by(property).ascending();
|
||||
}
|
||||
|
||||
private int normalizePage(int page) {
|
||||
return Math.max(page, 0);
|
||||
}
|
||||
|
||||
private int normalizeSize(int size) {
|
||||
return Math.max(1, Math.min(size, 100));
|
||||
}
|
||||
|
||||
private String normalizeQuery(String query) {
|
||||
return query == null ? "" : query.trim().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@GetMapping("/{departmentId}")
|
||||
public ResponseEntity<?> getGroupsByDepartmentId(@PathVariable Long departmentId) {
|
||||
logger.info("Получен запрос на получение списка групп для кафедры с ID - {}", departmentId);
|
||||
|
||||
@@ -12,10 +12,13 @@ import com.magistr.app.service.ScheduleGeneratorService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.http.HttpStatus.BAD_REQUEST;
|
||||
|
||||
@RestController
|
||||
@RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE, Role.STUDENT})
|
||||
public class SubgroupController {
|
||||
@@ -36,8 +39,16 @@ public class SubgroupController {
|
||||
}
|
||||
|
||||
@GetMapping("/api/subgroups")
|
||||
public List<SubgroupDto> getAll() {
|
||||
return subgroupRepository.findAllWithGroupsOrderByGroupNameAndName().stream()
|
||||
public List<SubgroupDto> getAll(@RequestParam(required = false) List<Long> groupId) {
|
||||
if (groupId != null && groupId.size() > 100) {
|
||||
throw new ResponseStatusException(BAD_REQUEST, "За один запрос можно получить подгруппы не более чем для 100 групп");
|
||||
}
|
||||
List<Subgroup> selected = groupId == null
|
||||
? subgroupRepository.findAllWithGroupsOrderByGroupNameAndName()
|
||||
: groupId.isEmpty()
|
||||
? List.of()
|
||||
: subgroupRepository.findByStudentGroupIdInOrderByGroupNameAndName(groupId.stream().distinct().toList());
|
||||
return selected.stream()
|
||||
.map(this::toDto)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.magistr.app.controller;
|
||||
|
||||
import com.magistr.app.config.auth.AuthContext;
|
||||
import com.magistr.app.config.auth.RequireRoles;
|
||||
import com.magistr.app.dto.CreateSubjectRequest;
|
||||
import com.magistr.app.dto.SubjectResponse;
|
||||
import com.magistr.app.dto.PageResponse;
|
||||
import com.magistr.app.model.LifecycleEntity;
|
||||
import com.magistr.app.model.Role;
|
||||
import com.magistr.app.model.Subject;
|
||||
@@ -11,9 +13,14 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@@ -53,6 +60,74 @@ public class SubjectController {
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE, Role.DEPARTMENT, Role.SCHEDULE_VIEWER})
|
||||
public PageResponse<SubjectResponse> getSubjectsPage(
|
||||
@RequestParam(defaultValue = "") String query,
|
||||
@RequestParam(required = false) Long departmentId,
|
||||
@RequestParam(defaultValue = "false") boolean includeArchived,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "25") int size,
|
||||
@RequestParam(defaultValue = "name") String sort,
|
||||
@RequestParam(defaultValue = "asc") String direction) {
|
||||
Long effectiveDepartmentId = effectiveDepartmentId(departmentId);
|
||||
var subjects = subjectRepository.findAll(
|
||||
subjectSpecification(query, effectiveDepartmentId, includeArchived),
|
||||
PageRequest.of(
|
||||
Math.max(page, 0),
|
||||
Math.max(1, Math.min(size, 100)),
|
||||
subjectSort(sort, direction)
|
||||
)
|
||||
);
|
||||
return PageResponse.from(subjects.map(this::toResponse));
|
||||
}
|
||||
|
||||
private Long effectiveDepartmentId(Long requestedDepartmentId) {
|
||||
var currentUser = AuthContext.getCurrentUser();
|
||||
return currentUser != null && currentUser.role() == Role.DEPARTMENT
|
||||
? currentUser.departmentId()
|
||||
: requestedDepartmentId;
|
||||
}
|
||||
|
||||
private Specification<Subject> subjectSpecification(String query,
|
||||
Long departmentId,
|
||||
boolean includeArchived) {
|
||||
return (root, criteriaQuery, builder) -> {
|
||||
List<jakarta.persistence.criteria.Predicate> predicates = new ArrayList<>();
|
||||
if (!includeArchived) {
|
||||
predicates.add(builder.notEqual(root.get("status"), LifecycleEntity.STATUS_ARCHIVED));
|
||||
}
|
||||
if (departmentId != null) {
|
||||
predicates.add(builder.equal(root.get("departmentId"), departmentId));
|
||||
}
|
||||
String normalizedQuery = query == null ? "" : query.trim().toLowerCase(Locale.ROOT);
|
||||
if (!normalizedQuery.isEmpty()) {
|
||||
String pattern = "%" + normalizedQuery + "%";
|
||||
predicates.add(builder.or(
|
||||
builder.like(builder.lower(root.get("name")), pattern),
|
||||
builder.like(builder.lower(root.get("code")), pattern)
|
||||
));
|
||||
}
|
||||
return builder.and(predicates.toArray(jakarta.persistence.criteria.Predicate[]::new));
|
||||
};
|
||||
}
|
||||
|
||||
private Sort subjectSort(String field, String direction) {
|
||||
String property = switch (field) {
|
||||
case "code" -> "code";
|
||||
case "departmentId" -> "departmentId";
|
||||
case "status" -> "status";
|
||||
default -> "name";
|
||||
};
|
||||
return "desc".equalsIgnoreCase(direction)
|
||||
? Sort.by(property).descending()
|
||||
: Sort.by(property).ascending();
|
||||
}
|
||||
|
||||
private SubjectResponse toResponse(Subject subject) {
|
||||
return new SubjectResponse(subject.getId(), subject.getName(), subject.getCode(), subject.getDepartmentId());
|
||||
}
|
||||
|
||||
@GetMapping("/{departmentId}")
|
||||
@RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE, Role.DEPARTMENT, Role.SCHEDULE_VIEWER})
|
||||
public ResponseEntity<?> getSubjectsByDepartmentId(@PathVariable Long departmentId) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.magistr.app.config.auth.RequireRoles;
|
||||
import com.magistr.app.dto.ReviewTeacherCreationRequest;
|
||||
import com.magistr.app.dto.TeacherCreationRequestResponse;
|
||||
import com.magistr.app.dto.UserResponse;
|
||||
import com.magistr.app.dto.PageResponse;
|
||||
import com.magistr.app.model.*;
|
||||
import com.magistr.app.repository.DepartmentRepository;
|
||||
import com.magistr.app.repository.TeacherCreationRequestRepository;
|
||||
@@ -12,11 +13,16 @@ import com.magistr.app.repository.TeacherDepartmentAssignmentRepository;
|
||||
import com.magistr.app.repository.UserRepository;
|
||||
import com.magistr.app.service.BusinessTimeService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@@ -55,6 +61,63 @@ public class TeacherCreationRequestController {
|
||||
.toList();
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
public PageResponse<TeacherCreationRequestResponse> getRequestsPage(
|
||||
@RequestParam(defaultValue = "") String query,
|
||||
@RequestParam(required = false) TeacherCreationRequestStatus status,
|
||||
@RequestParam(required = false) Long departmentId,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "25") int size,
|
||||
@RequestParam(defaultValue = "createdAt") String sort,
|
||||
@RequestParam(defaultValue = "desc") String direction) {
|
||||
var requests = teacherCreationRequestRepository.findAll(
|
||||
requestSpecification(query, status, departmentId),
|
||||
PageRequest.of(
|
||||
Math.max(page, 0),
|
||||
Math.max(1, Math.min(size, 100)),
|
||||
requestSort(sort, direction)
|
||||
)
|
||||
);
|
||||
return PageResponse.from(requests.map(this::toResponse));
|
||||
}
|
||||
|
||||
private Specification<TeacherCreationRequest> requestSpecification(String query,
|
||||
TeacherCreationRequestStatus status,
|
||||
Long departmentId) {
|
||||
return (root, criteriaQuery, builder) -> {
|
||||
List<jakarta.persistence.criteria.Predicate> predicates = new ArrayList<>();
|
||||
if (status != null) {
|
||||
predicates.add(builder.equal(root.get("status"), status));
|
||||
}
|
||||
if (departmentId != null) {
|
||||
predicates.add(builder.equal(root.get("department").get("id"), departmentId));
|
||||
}
|
||||
String normalizedQuery = query == null ? "" : query.trim().toLowerCase(Locale.ROOT);
|
||||
if (!normalizedQuery.isEmpty()) {
|
||||
String pattern = "%" + normalizedQuery + "%";
|
||||
predicates.add(builder.or(
|
||||
builder.like(builder.lower(root.get("username")), pattern),
|
||||
builder.like(builder.lower(root.get("fullName")), pattern),
|
||||
builder.like(builder.lower(root.get("jobTitle")), pattern),
|
||||
builder.like(builder.lower(root.get("department").get("departmentName")), pattern)
|
||||
));
|
||||
}
|
||||
return builder.and(predicates.toArray(jakarta.persistence.criteria.Predicate[]::new));
|
||||
};
|
||||
}
|
||||
|
||||
private Sort requestSort(String field, String direction) {
|
||||
String property = switch (field) {
|
||||
case "fullName" -> "fullName";
|
||||
case "username" -> "username";
|
||||
case "status" -> "status";
|
||||
default -> "createdAt";
|
||||
};
|
||||
return "asc".equalsIgnoreCase(direction)
|
||||
? Sort.by(property).ascending()
|
||||
: Sort.by(property).descending();
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/approve")
|
||||
@Transactional
|
||||
public ResponseEntity<?> approve(@PathVariable Long id, @RequestBody ReviewTeacherCreationRequest review) {
|
||||
|
||||
@@ -16,10 +16,13 @@ import com.magistr.app.service.BusinessTimeService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.http.HttpStatus.BAD_REQUEST;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/teacher-subjects")
|
||||
@RequireRoles({Role.ADMIN, Role.DEPARTMENT})
|
||||
@@ -53,15 +56,18 @@ public class TeacherSubjectController {
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<TeacherSubjectResponse> getAll() {
|
||||
return teacherSubjectRepository.findAll().stream()
|
||||
public List<TeacherSubjectResponse> getAll(@RequestParam(required = false) List<Long> subjectId) {
|
||||
if (subjectId != null && subjectId.size() > 100) {
|
||||
throw new ResponseStatusException(BAD_REQUEST, "За один запрос можно получить привязки не более чем для 100 дисциплин");
|
||||
}
|
||||
List<TeacherSubject> assignments = subjectId == null
|
||||
? teacherSubjectRepository.findAll()
|
||||
: subjectId.isEmpty()
|
||||
? List.of()
|
||||
: teacherSubjectRepository.findBySubjectIdInWithDetails(subjectId.stream().distinct().toList());
|
||||
return assignments.stream()
|
||||
.filter(ts -> canManage(ts.getUser(), ts.getSubject()))
|
||||
.map(ts -> new TeacherSubjectResponse(
|
||||
ts.getUserId(),
|
||||
ts.getUser().getUsername(),
|
||||
ts.getSubjectId(),
|
||||
ts.getSubject().getName()
|
||||
))
|
||||
.map(this::toResponse)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -136,4 +142,14 @@ public class TeacherSubjectController {
|
||||
)
|
||||
&& departmentId.equals(subject.getDepartmentId());
|
||||
}
|
||||
|
||||
private TeacherSubjectResponse toResponse(TeacherSubject teacherSubject) {
|
||||
return new TeacherSubjectResponse(
|
||||
teacherSubject.getUserId(),
|
||||
teacherSubject.getUser().getUsername(),
|
||||
teacherSubject.getUser().getFullName(),
|
||||
teacherSubject.getSubjectId(),
|
||||
teacherSubject.getSubject().getName()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.magistr.app.dto.CreateUserRequest;
|
||||
import com.magistr.app.dto.DepartmentTransferRequest;
|
||||
import com.magistr.app.dto.TeacherDepartmentAssignmentDto;
|
||||
import com.magistr.app.dto.UserResponse;
|
||||
import com.magistr.app.dto.PageResponse;
|
||||
import com.magistr.app.model.Department;
|
||||
import com.magistr.app.model.LifecycleEntity;
|
||||
import com.magistr.app.model.Role;
|
||||
@@ -22,11 +23,16 @@ import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@@ -80,6 +86,23 @@ public class UserController {
|
||||
return response;
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
public PageResponse<UserResponse> getUsersPage(
|
||||
@RequestParam(defaultValue = "") String query,
|
||||
@RequestParam(required = false) Role role,
|
||||
@RequestParam(required = false) Long departmentId,
|
||||
@RequestParam(defaultValue = "true") boolean includeArchived,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "25") int size,
|
||||
@RequestParam(defaultValue = "fullName") String sort,
|
||||
@RequestParam(defaultValue = "asc") String direction) {
|
||||
var users = userRepository.findAll(
|
||||
userSpecification(query, role, departmentId, includeArchived),
|
||||
PageRequest.of(normalizePage(page), normalizeSize(size), userSort(sort, direction))
|
||||
);
|
||||
return PageResponse.from(users.map(this::toUserResponse));
|
||||
}
|
||||
|
||||
@GetMapping("/teachers")
|
||||
@RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE, Role.DEPARTMENT, Role.SCHEDULE_VIEWER})
|
||||
public List<UserResponse> getTeachers() {
|
||||
@@ -91,6 +114,92 @@ public class UserController {
|
||||
return response;
|
||||
}
|
||||
|
||||
@GetMapping("/teachers/options")
|
||||
@RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE, Role.DEPARTMENT, Role.SCHEDULE_VIEWER})
|
||||
public List<UserResponse> searchTeacherOptions(
|
||||
@RequestParam(defaultValue = "") String query,
|
||||
@RequestParam(required = false) Long departmentId,
|
||||
@RequestParam(required = false) Long selectedId,
|
||||
@RequestParam(defaultValue = "20") int limit) {
|
||||
var currentUser = AuthContext.getCurrentUser();
|
||||
Long effectiveDepartmentId = currentUser != null && currentUser.role() == Role.DEPARTMENT
|
||||
? currentUser.departmentId()
|
||||
: departmentId;
|
||||
LocalDate today = businessTime.today();
|
||||
List<UserResponse> options = new ArrayList<>(userRepository.searchTeacherOptions(
|
||||
query == null ? "" : query.trim().toLowerCase(Locale.ROOT),
|
||||
Role.TEACHER,
|
||||
LifecycleEntity.STATUS_ARCHIVED,
|
||||
effectiveDepartmentId,
|
||||
today,
|
||||
PageRequest.of(0, Math.min(normalizeSize(limit), 50), Sort.by("fullName").ascending())
|
||||
).stream()
|
||||
.map(user -> effectiveDepartmentId == null
|
||||
? toUserResponse(user)
|
||||
: toUserResponse(user, effectiveDepartmentId))
|
||||
.toList());
|
||||
if (selectedId != null && options.stream().noneMatch(item -> selectedId.equals(item.getId()))) {
|
||||
userRepository.findById(selectedId)
|
||||
.filter(user -> user.getRole() == Role.TEACHER)
|
||||
.filter(user -> !user.isArchivedRecord())
|
||||
.filter(user -> effectiveDepartmentId == null
|
||||
|| teacherDepartmentService.hasAssignmentAtDate(user.getId(), effectiveDepartmentId, today))
|
||||
.map(user -> effectiveDepartmentId == null
|
||||
? toUserResponse(user)
|
||||
: toUserResponse(user, effectiveDepartmentId))
|
||||
.ifPresent(options::add);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
private Specification<User> userSpecification(String query,
|
||||
Role role,
|
||||
Long departmentId,
|
||||
boolean includeArchived) {
|
||||
return (root, criteriaQuery, builder) -> {
|
||||
List<jakarta.persistence.criteria.Predicate> predicates = new ArrayList<>();
|
||||
if (!includeArchived) {
|
||||
predicates.add(builder.notEqual(root.get("status"), LifecycleEntity.STATUS_ARCHIVED));
|
||||
}
|
||||
if (role != null) {
|
||||
predicates.add(builder.equal(root.get("role"), role));
|
||||
}
|
||||
if (departmentId != null) {
|
||||
predicates.add(builder.equal(root.get("departmentId"), departmentId));
|
||||
}
|
||||
String normalizedQuery = query == null ? "" : query.trim().toLowerCase(Locale.ROOT);
|
||||
if (!normalizedQuery.isEmpty()) {
|
||||
String pattern = "%" + normalizedQuery + "%";
|
||||
predicates.add(builder.or(
|
||||
builder.like(builder.lower(root.get("username")), pattern),
|
||||
builder.like(builder.lower(root.get("fullName")), pattern),
|
||||
builder.like(builder.lower(root.get("jobTitle")), pattern)
|
||||
));
|
||||
}
|
||||
return builder.and(predicates.toArray(jakarta.persistence.criteria.Predicate[]::new));
|
||||
};
|
||||
}
|
||||
|
||||
private Sort userSort(String field, String direction) {
|
||||
String property = switch (field) {
|
||||
case "username" -> "username";
|
||||
case "role" -> "role";
|
||||
case "status" -> "status";
|
||||
default -> "fullName";
|
||||
};
|
||||
return "desc".equalsIgnoreCase(direction)
|
||||
? Sort.by(property).descending()
|
||||
: Sort.by(property).ascending();
|
||||
}
|
||||
|
||||
private int normalizePage(int page) {
|
||||
return Math.max(page, 0);
|
||||
}
|
||||
|
||||
private int normalizeSize(int size) {
|
||||
return Math.max(1, Math.min(size, 100));
|
||||
}
|
||||
|
||||
@GetMapping("/teachers/{departmentId}")
|
||||
@RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE, Role.DEPARTMENT, Role.SCHEDULE_VIEWER})
|
||||
public ResponseEntity<?> getTeachersByDepartmentId(@PathVariable Long departmentId){
|
||||
|
||||
27
backend/src/main/java/com/magistr/app/dto/PageResponse.java
Normal file
27
backend/src/main/java/com/magistr/app/dto/PageResponse.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.magistr.app.dto;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record PageResponse<T>(
|
||||
List<T> items,
|
||||
int page,
|
||||
int size,
|
||||
long totalItems,
|
||||
int totalPages,
|
||||
boolean first,
|
||||
boolean last
|
||||
) {
|
||||
public static <T> PageResponse<T> from(Page<T> page) {
|
||||
return new PageResponse<>(
|
||||
page.getContent(),
|
||||
page.getNumber(),
|
||||
page.getSize(),
|
||||
page.getTotalElements(),
|
||||
page.getTotalPages(),
|
||||
page.isFirst(),
|
||||
page.isLast()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,14 @@ public class TeacherSubjectResponse {
|
||||
|
||||
private Long userId;
|
||||
private String username;
|
||||
private String fullName;
|
||||
private Long subjectId;
|
||||
private String subjectName;
|
||||
|
||||
public TeacherSubjectResponse(Long userId, String username, Long subjectId, String subjectName) {
|
||||
public TeacherSubjectResponse(Long userId, String username, String fullName, Long subjectId, String subjectName) {
|
||||
this.userId = userId;
|
||||
this.username = username;
|
||||
this.fullName = fullName;
|
||||
this.subjectId = subjectId;
|
||||
this.subjectName = subjectName;
|
||||
}
|
||||
@@ -22,6 +24,10 @@ public class TeacherSubjectResponse {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public Long getSubjectId() {
|
||||
return subjectId;
|
||||
}
|
||||
|
||||
@@ -3,14 +3,17 @@ package com.magistr.app.repository;
|
||||
import com.magistr.app.model.AcademicCalendar;
|
||||
import jakarta.persistence.LockModeType;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Lock;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface AcademicCalendarRepository extends JpaRepository<AcademicCalendar, Long> {
|
||||
public interface AcademicCalendarRepository extends JpaRepository<AcademicCalendar, Long>, JpaSpecificationExecutor<AcademicCalendar> {
|
||||
|
||||
long countByStudyFormId(Long studyFormId);
|
||||
|
||||
@@ -54,4 +57,49 @@ public interface AcademicCalendarRepository extends JpaRepository<AcademicCalend
|
||||
where calendar.id = :id
|
||||
""")
|
||||
Optional<AcademicCalendar> findByIdWithDetailsForUpdate(@Param("id") Long id);
|
||||
|
||||
@Query(value = """
|
||||
select calendar
|
||||
from AcademicCalendar calendar
|
||||
join fetch calendar.academicYear academicYear
|
||||
join fetch calendar.speciality speciality
|
||||
join fetch calendar.specialtyProfile specialtyProfile
|
||||
join fetch calendar.studyForm studyForm
|
||||
where (:query = ''
|
||||
or lower(calendar.title) like concat('%', :query, '%')
|
||||
or lower(academicYear.title) like concat('%', :query, '%')
|
||||
or lower(speciality.specialityCode) like concat('%', :query, '%')
|
||||
or lower(speciality.specialityName) like concat('%', :query, '%')
|
||||
or lower(specialtyProfile.name) like concat('%', :query, '%')
|
||||
or lower(studyForm.name) like concat('%', :query, '%'))
|
||||
and (:academicYearId is null or academicYear.id = :academicYearId)
|
||||
and (:specialtyId is null or speciality.id = :specialtyId)
|
||||
and (:profileId is null or specialtyProfile.id = :profileId)
|
||||
and (:studyFormId is null or studyForm.id = :studyFormId)
|
||||
""",
|
||||
countQuery = """
|
||||
select count(calendar)
|
||||
from AcademicCalendar calendar
|
||||
join calendar.academicYear academicYear
|
||||
join calendar.speciality speciality
|
||||
join calendar.specialtyProfile specialtyProfile
|
||||
join calendar.studyForm studyForm
|
||||
where (:query = ''
|
||||
or lower(calendar.title) like concat('%', :query, '%')
|
||||
or lower(academicYear.title) like concat('%', :query, '%')
|
||||
or lower(speciality.specialityCode) like concat('%', :query, '%')
|
||||
or lower(speciality.specialityName) like concat('%', :query, '%')
|
||||
or lower(specialtyProfile.name) like concat('%', :query, '%')
|
||||
or lower(studyForm.name) like concat('%', :query, '%'))
|
||||
and (:academicYearId is null or academicYear.id = :academicYearId)
|
||||
and (:specialtyId is null or speciality.id = :specialtyId)
|
||||
and (:profileId is null or specialtyProfile.id = :profileId)
|
||||
and (:studyFormId is null or studyForm.id = :studyFormId)
|
||||
""")
|
||||
Page<AcademicCalendar> searchOptions(@Param("query") String query,
|
||||
@Param("academicYearId") Long academicYearId,
|
||||
@Param("specialtyId") Long specialtyId,
|
||||
@Param("profileId") Long profileId,
|
||||
@Param("studyFormId") Long studyFormId,
|
||||
Pageable pageable);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,29 @@ package com.magistr.app.repository;
|
||||
|
||||
import com.magistr.app.model.Classroom;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.List;
|
||||
|
||||
public interface ClassroomRepository extends JpaRepository<Classroom, Long> {
|
||||
public interface ClassroomRepository extends JpaRepository<Classroom, Long>, JpaSpecificationExecutor<Classroom> {
|
||||
Optional<Classroom> findByName(String name);
|
||||
|
||||
List<Classroom> findByStatusNot(String status);
|
||||
|
||||
@Query("""
|
||||
select classroom
|
||||
from Classroom classroom
|
||||
where classroom.status <> :archivedStatus
|
||||
and (:query = ''
|
||||
or lower(classroom.name) like concat('%', :query, '%')
|
||||
or lower(coalesce(classroom.building, '')) like concat('%', :query, '%'))
|
||||
""")
|
||||
Page<Classroom> searchActiveOptions(@Param("query") String query,
|
||||
@Param("archivedStatus") String archivedStatus,
|
||||
Pageable pageable);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.magistr.app.repository;
|
||||
import com.magistr.app.model.StudentGroup;
|
||||
import jakarta.persistence.LockModeType;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Lock;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
@@ -10,7 +11,7 @@ import org.springframework.data.repository.query.Param;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface GroupRepository extends JpaRepository<StudentGroup, Long> {
|
||||
public interface GroupRepository extends JpaRepository<StudentGroup, Long>, JpaSpecificationExecutor<StudentGroup> {
|
||||
|
||||
List<StudentGroup> findByEducationFormId(Long educationFormId);
|
||||
|
||||
|
||||
@@ -35,6 +35,16 @@ public interface SubgroupRepository extends JpaRepository<Subgroup, Long> {
|
||||
""")
|
||||
List<Subgroup> findByStudentGroupIdOrderByNameAsc(@Param("groupId") Long groupId);
|
||||
|
||||
@Query("""
|
||||
select subgroup
|
||||
from Subgroup subgroup
|
||||
join fetch subgroup.studentGroup studentGroup
|
||||
where studentGroup.id in :groupIds
|
||||
and subgroup.status <> 'ARCHIVED'
|
||||
order by studentGroup.name, subgroup.name
|
||||
""")
|
||||
List<Subgroup> findByStudentGroupIdInOrderByGroupNameAndName(@Param("groupIds") List<Long> groupIds);
|
||||
|
||||
@Query("""
|
||||
select subgroup
|
||||
from Subgroup subgroup
|
||||
|
||||
@@ -2,11 +2,12 @@ package com.magistr.app.repository;
|
||||
|
||||
import com.magistr.app.model.Subject;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface SubjectRepository extends JpaRepository<Subject, Long> {
|
||||
public interface SubjectRepository extends JpaRepository<Subject, Long>, JpaSpecificationExecutor<Subject> {
|
||||
Optional<Subject> findByNameIgnoreCase(String name);
|
||||
|
||||
List<Subject> findByDepartmentId(Long departmentId);
|
||||
|
||||
@@ -3,10 +3,11 @@ package com.magistr.app.repository;
|
||||
import com.magistr.app.model.TeacherCreationRequest;
|
||||
import com.magistr.app.model.TeacherCreationRequestStatus;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TeacherCreationRequestRepository extends JpaRepository<TeacherCreationRequest, Long> {
|
||||
public interface TeacherCreationRequestRepository extends JpaRepository<TeacherCreationRequest, Long>, JpaSpecificationExecutor<TeacherCreationRequest> {
|
||||
|
||||
List<TeacherCreationRequest> findByDepartment_IdOrderByCreatedAtDesc(Long departmentId);
|
||||
|
||||
|
||||
@@ -12,6 +12,16 @@ public interface TeacherSubjectRepository extends JpaRepository<TeacherSubject,
|
||||
List<TeacherSubject> findByUserId(Long userId);
|
||||
List<TeacherSubject> findBySubjectId(Long subjectId);
|
||||
|
||||
@Query("""
|
||||
select teacherSubject
|
||||
from TeacherSubject teacherSubject
|
||||
join fetch teacherSubject.user
|
||||
join fetch teacherSubject.subject
|
||||
where teacherSubject.subjectId in :subjectIds
|
||||
order by teacherSubject.subject.name, teacherSubject.user.fullName
|
||||
""")
|
||||
List<TeacherSubject> findBySubjectIdInWithDetails(@Param("subjectIds") List<Long> subjectIds);
|
||||
|
||||
@Query(value = """
|
||||
select subject_link.user_id
|
||||
from teacher_subjects subject_link
|
||||
|
||||
@@ -4,14 +4,18 @@ import com.magistr.app.model.Role;
|
||||
import com.magistr.app.model.User;
|
||||
import jakarta.persistence.LockModeType;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Lock;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
|
||||
|
||||
Optional<User> findByUsername(String username);
|
||||
|
||||
@@ -25,6 +29,49 @@ public interface UserRepository extends JpaRepository<User, Long> {
|
||||
|
||||
List<User> findByRoleAndDepartmentIdAndStatusNot(Role role, Long departmentId, String status);
|
||||
|
||||
@Query(value = """
|
||||
select user
|
||||
from User user
|
||||
where user.role = :role
|
||||
and user.status <> :archivedStatus
|
||||
and (:query = ''
|
||||
or lower(user.username) like concat('%', :query, '%')
|
||||
or lower(user.fullName) like concat('%', :query, '%')
|
||||
or lower(coalesce(user.jobTitle, '')) like concat('%', :query, '%'))
|
||||
and (:departmentId is null or exists (
|
||||
select assignment.id
|
||||
from TeacherDepartmentAssignment assignment
|
||||
where assignment.teacher.id = user.id
|
||||
and assignment.department.id = :departmentId
|
||||
and assignment.validFrom <= :date
|
||||
and (assignment.validTo is null or assignment.validTo >= :date)
|
||||
))
|
||||
""",
|
||||
countQuery = """
|
||||
select count(user)
|
||||
from User user
|
||||
where user.role = :role
|
||||
and user.status <> :archivedStatus
|
||||
and (:query = ''
|
||||
or lower(user.username) like concat('%', :query, '%')
|
||||
or lower(user.fullName) like concat('%', :query, '%')
|
||||
or lower(coalesce(user.jobTitle, '')) like concat('%', :query, '%'))
|
||||
and (:departmentId is null or exists (
|
||||
select assignment.id
|
||||
from TeacherDepartmentAssignment assignment
|
||||
where assignment.teacher.id = user.id
|
||||
and assignment.department.id = :departmentId
|
||||
and assignment.validFrom <= :date
|
||||
and (assignment.validTo is null or assignment.validTo >= :date)
|
||||
))
|
||||
""")
|
||||
Page<User> searchTeacherOptions(@Param("query") String query,
|
||||
@Param("role") Role role,
|
||||
@Param("archivedStatus") String archivedStatus,
|
||||
@Param("departmentId") Long departmentId,
|
||||
@Param("date") LocalDate date,
|
||||
Pageable pageable);
|
||||
|
||||
@Lock(LockModeType.PESSIMISTIC_WRITE)
|
||||
@Query("select user from User user where user.id = :id")
|
||||
Optional<User> findByIdForUpdate(@Param("id") Long id);
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.mockito.ArgumentCaptor;
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -74,6 +75,52 @@ class TeacherSubjectControllerTest {
|
||||
assertThat(captor.getValue().getSubjectId()).isEqualTo(subject.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsAssignmentsOnlyForRequestedSubjects() {
|
||||
TeacherSubjectRepository teacherSubjectRepository = mock(TeacherSubjectRepository.class);
|
||||
UserRepository userRepository = mock(UserRepository.class);
|
||||
SubjectRepository subjectRepository = mock(SubjectRepository.class);
|
||||
TeacherDepartmentService teacherDepartmentService = mock(TeacherDepartmentService.class);
|
||||
User teacher = teacher();
|
||||
Subject subject = new Subject();
|
||||
subject.setId(20L);
|
||||
subject.setName("Высшая математика");
|
||||
TeacherSubject assignment = new TeacherSubject(teacher.getId(), subject.getId());
|
||||
setTeacherSubjectDetails(assignment, teacher, subject);
|
||||
when(teacherSubjectRepository.findBySubjectIdInWithDetails(List.of(subject.getId())))
|
||||
.thenReturn(List.of(assignment));
|
||||
TeacherSubjectController controller = new TeacherSubjectController(
|
||||
teacherSubjectRepository,
|
||||
userRepository,
|
||||
subjectRepository,
|
||||
teacherDepartmentService,
|
||||
BUSINESS_TIME
|
||||
);
|
||||
AuthContext.setCurrentUser(new AuthenticatedUser(1L, "admin", Role.ADMIN, null));
|
||||
|
||||
var result = controller.getAll(List.of(subject.getId()));
|
||||
|
||||
assertThat(result).singleElement().satisfies(response -> {
|
||||
assertThat(response.getSubjectId()).isEqualTo(subject.getId());
|
||||
assertThat(response.getUserId()).isEqualTo(teacher.getId());
|
||||
assertThat(response.getFullName()).isEqualTo(teacher.getFullName());
|
||||
});
|
||||
verify(teacherSubjectRepository).findBySubjectIdInWithDetails(List.of(subject.getId()));
|
||||
}
|
||||
|
||||
private void setTeacherSubjectDetails(TeacherSubject assignment, User teacher, Subject subject) {
|
||||
try {
|
||||
var userField = TeacherSubject.class.getDeclaredField("user");
|
||||
userField.setAccessible(true);
|
||||
userField.set(assignment, teacher);
|
||||
var subjectField = TeacherSubject.class.getDeclaredField("subject");
|
||||
subjectField.setAccessible(true);
|
||||
subjectField.set(assignment, subject);
|
||||
} catch (ReflectiveOperationException exception) {
|
||||
throw new AssertionError("Не удалось подготовить тестовую привязку", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private User teacher() {
|
||||
User teacher = new User();
|
||||
teacher.setId(10L);
|
||||
|
||||
Reference in New Issue
Block a user