diff --git a/dodam-api/src/main/java/b1nd/dodamapi/recruit/RecruitController.java b/dodam-api/src/main/java/b1nd/dodamapi/recruit/RecruitController.java new file mode 100644 index 00000000..2af521c3 --- /dev/null +++ b/dodam-api/src/main/java/b1nd/dodamapi/recruit/RecruitController.java @@ -0,0 +1,52 @@ +package b1nd.dodamapi.recruit; + +import b1nd.dodamapi.common.response.Response; +import b1nd.dodamapi.common.response.ResponseData; +import b1nd.dodamcore.recruit.application.RecruitService; +import b1nd.dodamcore.recruit.application.dto.req.RecruitReq; +import b1nd.dodamcore.recruit.application.dto.res.RecruitPageRes; +import b1nd.dodamcore.recruit.application.dto.res.RecruitRes; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/recruit") +@RequiredArgsConstructor +public class RecruitController { + + private final RecruitService recruitService; + + @GetMapping + public ResponseData getRecruits(@RequestParam(name = "page") int page) { + RecruitPageRes recruitList = recruitService.getRecruitsByPaging(page); + return ResponseData.ok("채용 의뢰서 조회 성공", recruitList); + } + + @GetMapping("/{id}") + public ResponseData getRecruitById(@PathVariable int id) { + RecruitRes recruit = recruitService.getRecruitById(id); + return ResponseData.ok("채용 의뢰서 단일 조회 성공", recruit); + } + + @PostMapping + public Response createRecruit(@RequestBody @Valid RecruitReq createRecruitReq) { + recruitService.createRecruit(createRecruitReq); + return Response.created("채용 의뢰서 작성 성공"); + } + + @PatchMapping("/{id}") + public Response modifyRecruit( + @PathVariable int id, + @RequestBody RecruitReq modifyRecruitReq + ) { + recruitService.modifyRecruit(id, modifyRecruitReq); + return Response.ok("채용 의뢰서 수정 성공"); + } + + @DeleteMapping("/{id}") + public Response deleteRecruit(@PathVariable int id) { + recruitService.deleteRecruit(id); + return Response.ok("채용 의뢰서 삭제 성공"); + } +} diff --git a/dodam-api/src/main/java/b1nd/dodamapi/schedule/ScheduleController.java b/dodam-api/src/main/java/b1nd/dodamapi/schedule/ScheduleController.java index ca570d5d..ef5f7bd8 100644 --- a/dodam-api/src/main/java/b1nd/dodamapi/schedule/ScheduleController.java +++ b/dodam-api/src/main/java/b1nd/dodamapi/schedule/ScheduleController.java @@ -35,9 +35,7 @@ public Response modifySchedule( } @DeleteMapping("/{id}") - public Response deleteSchedule( - @PathVariable int id - ) { + public Response deleteSchedule(@PathVariable int id) { scheduleService.deleteSchedule(id); return Response.ok("일정 삭제 성공"); } @@ -61,9 +59,7 @@ public ResponseData> getScheduleByDate( } @GetMapping("/search/keyword") - public ResponseData> getScheduleByKeyword( - @RequestParam("keyword") String keyword - ) { + public ResponseData> getScheduleByKeyword(@RequestParam("keyword") String keyword) { List scheduleList = scheduleService.getScheduleByKeyword(keyword); return ResponseData.ok("해당 키워드의 일정 조회 성공", scheduleList); } diff --git a/dodam-core/src/main/java/b1nd/dodamcore/banner/application/BannerService.java b/dodam-core/src/main/java/b1nd/dodamcore/banner/application/BannerService.java index 88fa27be..867e27bb 100644 --- a/dodam-core/src/main/java/b1nd/dodamcore/banner/application/BannerService.java +++ b/dodam-core/src/main/java/b1nd/dodamcore/banner/application/BannerService.java @@ -7,7 +7,6 @@ import b1nd.dodamcore.banner.repository.BannerRepository; import b1nd.dodamcore.common.util.ModifyUtil; import lombok.RequiredArgsConstructor; -import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -42,11 +41,11 @@ public void modifyBanner(int id, BannerReq modifyBannerReq) { @Transactional(rollbackFor = Exception.class) public void deleteBanner(int id) { - try { - bannerRepository.deleteById(id); - } catch (EmptyResultDataAccessException e) { - throw new BannerNotFoundException(); - } + + Banner banner = bannerRepository.findById(id) + .orElseThrow(BannerNotFoundException::new); + + bannerRepository.delete(banner); } @Transactional(rollbackFor = Exception.class) diff --git a/dodam-core/src/main/java/b1nd/dodamcore/bus/application/BusService.java b/dodam-core/src/main/java/b1nd/dodamcore/bus/application/BusService.java index a2257768..1d51dcd5 100644 --- a/dodam-core/src/main/java/b1nd/dodamcore/bus/application/BusService.java +++ b/dodam-core/src/main/java/b1nd/dodamcore/bus/application/BusService.java @@ -14,7 +14,6 @@ import b1nd.dodamcore.member.domain.entity.Student; import b1nd.dodamcore.member.repository.StudentRepository; import lombok.RequiredArgsConstructor; -import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -104,11 +103,11 @@ public void modifyBus(int id, BusReq modifyBusReq) { @Transactional(rollbackFor = Exception.class) public void deleteBus(int id) { - try { - busRepository.deleteById(id); - } catch (EmptyResultDataAccessException e) { - throw new BusNotFoundException(); - } + + Bus bus = busRepository.findById(id) + .orElseThrow(BusNotFoundException::new); + + busRepository.delete(bus); } @Transactional(rollbackFor = Exception.class) diff --git a/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/RecruitService.java b/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/RecruitService.java new file mode 100644 index 00000000..d51a5b53 --- /dev/null +++ b/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/RecruitService.java @@ -0,0 +1,84 @@ +package b1nd.dodamcore.recruit.application; + +import b1nd.dodamcore.common.util.ModifyUtil; +import b1nd.dodamcore.member.application.MemberSessionHolder; +import b1nd.dodamcore.member.domain.entity.Member; +import b1nd.dodamcore.recruit.application.dto.req.RecruitReq; +import b1nd.dodamcore.recruit.application.dto.res.RecruitPageRes; +import b1nd.dodamcore.recruit.application.dto.res.RecruitRes; +import b1nd.dodamcore.recruit.domain.entity.Recruit; +import b1nd.dodamcore.recruit.domain.exception.RecruitNotFoundException; +import b1nd.dodamcore.recruit.repository.RecruitFileRepository; +import b1nd.dodamcore.recruit.repository.RecruitRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class RecruitService { + + private final RecruitRepository recruitRepository; + private final RecruitFileRepository recruitFileRepository; + private final MemberSessionHolder memberSessionHolder; + + public RecruitPageRes getRecruitsByPaging(int page) { + + Pageable pageRequest = PageRequest.of(page - 1, 10); + Page recruitList = recruitRepository.findAllByOrderByIdDesc(pageRequest); + + int pageNum = recruitList.getTotalPages() - recruitList.getNumber(); + Integer nextPage = pageNum <= 1 ? null : pageNum; + + return RecruitPageRes.of(recruitList, nextPage); + } + + public RecruitRes getRecruitById(int id) { + + Recruit recruit = recruitRepository.findByIdWithJoin(id) + .orElseThrow(RecruitNotFoundException::new); + + return RecruitRes.of(recruit); + } + + @Transactional(rollbackFor = Exception.class) + public void createRecruit(RecruitReq createRecruitReq) { + + Member member = memberSessionHolder.current(); + Recruit recruit = createRecruitReq.mapToRecruit(member); + + recruitRepository.save(recruit); + } + + @Transactional(rollbackFor = Exception.class) + public void modifyRecruit(int id, RecruitReq dto) { + + Recruit recruit = recruitRepository.findById(id) + .orElseThrow(RecruitNotFoundException::new); + + recruitFileRepository.deleteByRecruit_Id(id); + + recruit.updateRecruit( + ModifyUtil.modifyIfNotNull(dto.name(), recruit.getName()), + ModifyUtil.modifyIfNotNull(dto.location(), recruit.getLocation()), + ModifyUtil.modifyIfNotNull(dto.duty(), recruit.getDuty()), + ModifyUtil.modifyIfNotNull(dto.etc(), recruit.getEtc()), + ModifyUtil.modifyIfNotZero(dto.personnel(), recruit.getPersonnel()), + ModifyUtil.modifyIfNotNull(dto.image(), recruit.getImage()), + dto.updateRecruitFile(recruit) + ); + } + + @Transactional(rollbackFor = Exception.class) + public void deleteRecruit(int id) { + + Recruit recruit = recruitRepository.findById(id) + .orElseThrow(RecruitNotFoundException::new); + + recruitRepository.delete(recruit); + } +} diff --git a/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/dto/Pdf.java b/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/dto/Pdf.java new file mode 100644 index 00000000..73c5e370 --- /dev/null +++ b/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/dto/Pdf.java @@ -0,0 +1,16 @@ +package b1nd.dodamcore.recruit.application.dto; + +import b1nd.dodamcore.recruit.domain.entity.RecruitFile; +import jakarta.validation.constraints.NotEmpty; + +import java.util.List; +import java.util.stream.Collectors; + +public record Pdf(@NotEmpty String url, @NotEmpty String name) { + + public static List of(List recruitFiles) { + return recruitFiles.stream().map(recruitFile -> + new Pdf(recruitFile.getPdfUrl(), recruitFile.getPdfName()) + ).collect(Collectors.toList()); + } +} diff --git a/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/dto/req/RecruitReq.java b/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/dto/req/RecruitReq.java new file mode 100644 index 00000000..c44af814 --- /dev/null +++ b/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/dto/req/RecruitReq.java @@ -0,0 +1,47 @@ +package b1nd.dodamcore.recruit.application.dto.req; + +import b1nd.dodamcore.member.domain.entity.Member; +import b1nd.dodamcore.recruit.application.dto.Pdf; +import b1nd.dodamcore.recruit.domain.entity.Recruit; +import b1nd.dodamcore.recruit.domain.entity.RecruitFile; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; + +import java.util.List; +import java.util.stream.Collectors; + +public record RecruitReq(@NotEmpty String name, @NotEmpty String location, @NotEmpty String duty, + String etc, @Positive @NotNull int personnel, @NotEmpty String image, List pdfs) { + + public Recruit mapToRecruit(Member member) { + Recruit recruit = Recruit.builder() + .writer(member) + .name(name) + .location(location) + .duty(duty) + .etc(etc) + .personnel(personnel) + .image(image) + .build(); + + pdfs.forEach(pdf -> recruit.addRecruitFile( + RecruitFile.builder() + .pdfName(pdf.name()) + .pdfUrl(pdf.url()) + .build() + )); + + return recruit; + } + + public List updateRecruitFile(Recruit recruit) { + return pdfs.stream().map(pdf -> + RecruitFile.builder() + .recruit(recruit) + .pdfName(pdf.name()) + .pdfUrl(pdf.url()) + .build() + ).collect(Collectors.toList()); + } +} diff --git a/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/dto/res/RecruitPageRes.java b/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/dto/res/RecruitPageRes.java new file mode 100644 index 00000000..b8d246fc --- /dev/null +++ b/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/dto/res/RecruitPageRes.java @@ -0,0 +1,29 @@ +package b1nd.dodamcore.recruit.application.dto.res; + +import b1nd.dodamcore.recruit.domain.entity.Recruit; +import org.springframework.data.domain.Page; + +import java.util.List; +import java.util.stream.Collectors; + +public record RecruitPageRes(List recruitList, Integer nextPage) { + + public static RecruitPageRes of(Page recruitPage, Integer nextPage) { + return new RecruitPageRes(recruitPage.getContent().stream().map(recruit -> + new RecruitListRes( + recruit.getId(), + recruit.getWriter().getName(), + recruit.getName(), + recruit.getLocation(), + recruit.getDuty(), + recruit.getEtc(), + recruit.getPersonnel(), + recruit.getImage() + )).collect(Collectors.toList()), + nextPage); + } + + public record RecruitListRes(int id, String writer, String name, String location, String duty, + String etc, int personnel, String image) { + } +} diff --git a/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/dto/res/RecruitRes.java b/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/dto/res/RecruitRes.java new file mode 100644 index 00000000..2656bab0 --- /dev/null +++ b/dodam-core/src/main/java/b1nd/dodamcore/recruit/application/dto/res/RecruitRes.java @@ -0,0 +1,23 @@ +package b1nd.dodamcore.recruit.application.dto.res; + +import b1nd.dodamcore.recruit.application.dto.Pdf; +import b1nd.dodamcore.recruit.domain.entity.Recruit; + +import java.util.List; + +public record RecruitRes(String writer, String name, String location, String duty, + String etc, int personnel, String image, List pdfs) { + + public static RecruitRes of(Recruit recruit) { + return new RecruitRes( + recruit.getWriter().getName(), + recruit.getName(), + recruit.getLocation(), + recruit.getDuty(), + recruit.getEtc(), + recruit.getPersonnel(), + recruit.getImage(), + Pdf.of(recruit.getRecruitFiles()) + ); + } +} diff --git a/dodam-core/src/main/java/b1nd/dodamcore/recruit/domain/entity/Recruit.java b/dodam-core/src/main/java/b1nd/dodamcore/recruit/domain/entity/Recruit.java new file mode 100644 index 00000000..bb492d6c --- /dev/null +++ b/dodam-core/src/main/java/b1nd/dodamcore/recruit/domain/entity/Recruit.java @@ -0,0 +1,74 @@ +package b1nd.dodamcore.recruit.domain.entity; + +import b1nd.dodamcore.common.entity.BaseEntity; +import b1nd.dodamcore.member.domain.entity.Member; +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; +import lombok.*; +import org.hibernate.annotations.DynamicUpdate; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@Entity(name = "recruit") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@DynamicUpdate +public class Recruit extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @NotNull + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "fk_writer_id") + private Member writer; // 작성자 + + @NotNull + private String name; + + @NotNull + private String location; + + @NotNull + private String duty; //직무 + + @Column(columnDefinition = "LONGTEXT") + private String etc; //추가 내용 + + @NotNull + private int personnel; //모집 인원 + + @NotNull + private String image; //채용 공고 이미지 + + @OneToMany(mappedBy = "recruit", cascade = CascadeType.ALL) + private List recruitFiles = new ArrayList<>(); + + @Builder + public Recruit(Member writer, String name, String location, String duty, String etc, int personnel, String image) { + this.writer = writer; + this.name = name; + this.location = location; + this.duty = duty; + this.etc = etc; + this.personnel = personnel; + this.image = image; + } + + public void addRecruitFile(RecruitFile recruitFile) { + this.recruitFiles.add(recruitFile); + recruitFile.setRecruit(this); + } + + public void updateRecruit(String name, String location, String duty, String etc, int personnel, String image, List recruitFiles) { + this.name = name; + this.location = location; + this.duty = duty; + this.etc = etc; + this.personnel = personnel; + this.image = image; + this.recruitFiles = recruitFiles; + } +} diff --git a/dodam-core/src/main/java/b1nd/dodamcore/recruit/domain/entity/RecruitFile.java b/dodam-core/src/main/java/b1nd/dodamcore/recruit/domain/entity/RecruitFile.java new file mode 100644 index 00000000..2cc6fd74 --- /dev/null +++ b/dodam-core/src/main/java/b1nd/dodamcore/recruit/domain/entity/RecruitFile.java @@ -0,0 +1,35 @@ +package b1nd.dodamcore.recruit.domain.entity; + +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; +import lombok.*; + +@Getter +@Entity(name = "recruit_file") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class RecruitFile { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @NotNull + @Setter + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "fk_recruit_id") + private Recruit recruit; + + @NotNull + @Column(columnDefinition = "LONGTEXT") + private String pdfUrl; + + @NotNull + private String pdfName; + + @Builder + public RecruitFile(Recruit recruit, String pdfUrl, String pdfName) { + this.recruit = recruit; + this.pdfUrl = pdfUrl; + this.pdfName = pdfName; + } +} diff --git a/dodam-core/src/main/java/b1nd/dodamcore/recruit/domain/exception/RecruitExceptionCode.java b/dodam-core/src/main/java/b1nd/dodamcore/recruit/domain/exception/RecruitExceptionCode.java new file mode 100644 index 00000000..cd2c83d1 --- /dev/null +++ b/dodam-core/src/main/java/b1nd/dodamcore/recruit/domain/exception/RecruitExceptionCode.java @@ -0,0 +1,30 @@ +package b1nd.dodamcore.recruit.domain.exception; + +import b1nd.dodamcore.common.exception.ExceptionCode; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@RequiredArgsConstructor +public enum RecruitExceptionCode implements ExceptionCode { + + NOT_FOUND(HttpStatus.NOT_FOUND, "없는 채용의뢰"); + + private final HttpStatus status; + private final String message; + + @Override + public HttpStatus getHttpStatus() { + return this.status; + } + + @Override + public String getExceptionName() { + return this.name(); + } + + @Override + public String getMessage() { + return this.message; + } + +} \ No newline at end of file diff --git a/dodam-core/src/main/java/b1nd/dodamcore/recruit/domain/exception/RecruitNotFoundException.java b/dodam-core/src/main/java/b1nd/dodamcore/recruit/domain/exception/RecruitNotFoundException.java new file mode 100644 index 00000000..3ea7f160 --- /dev/null +++ b/dodam-core/src/main/java/b1nd/dodamcore/recruit/domain/exception/RecruitNotFoundException.java @@ -0,0 +1,14 @@ +package b1nd.dodamcore.recruit.domain.exception; + +import b1nd.dodamcore.common.exception.custom.CustomException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.NOT_FOUND) +public class RecruitNotFoundException extends CustomException { + + public RecruitNotFoundException() { + super(RecruitExceptionCode.NOT_FOUND); + } + +} \ No newline at end of file diff --git a/dodam-core/src/main/java/b1nd/dodamcore/recruit/repository/RecruitFileRepository.java b/dodam-core/src/main/java/b1nd/dodamcore/recruit/repository/RecruitFileRepository.java new file mode 100644 index 00000000..2b128fb9 --- /dev/null +++ b/dodam-core/src/main/java/b1nd/dodamcore/recruit/repository/RecruitFileRepository.java @@ -0,0 +1,11 @@ +package b1nd.dodamcore.recruit.repository; + +import b1nd.dodamcore.recruit.domain.entity.RecruitFile; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface RecruitFileRepository extends JpaRepository { + + void deleteByRecruit_Id(int Id); +} \ No newline at end of file diff --git a/dodam-core/src/main/java/b1nd/dodamcore/recruit/repository/RecruitRepository.java b/dodam-core/src/main/java/b1nd/dodamcore/recruit/repository/RecruitRepository.java new file mode 100644 index 00000000..3e53fa6c --- /dev/null +++ b/dodam-core/src/main/java/b1nd/dodamcore/recruit/repository/RecruitRepository.java @@ -0,0 +1,21 @@ +package b1nd.dodamcore.recruit.repository; + +import b1nd.dodamcore.recruit.domain.entity.Recruit; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface RecruitRepository extends JpaRepository { + + @EntityGraph(attributePaths = {"writer"}) + Page findAllByOrderByIdDesc(Pageable pageRequest); + + @Query("select r from recruit r LEFT JOIN FETCH r.recruitFiles JOIN FETCH r.writer where r.id = :id") + Optional findByIdWithJoin(int id); +} \ No newline at end of file diff --git a/dodam-core/src/main/java/b1nd/dodamcore/schedule/application/ScheduleService.java b/dodam-core/src/main/java/b1nd/dodamcore/schedule/application/ScheduleService.java index 0ba7c801..eda85907 100644 --- a/dodam-core/src/main/java/b1nd/dodamcore/schedule/application/ScheduleService.java +++ b/dodam-core/src/main/java/b1nd/dodamcore/schedule/application/ScheduleService.java @@ -9,7 +9,6 @@ import b1nd.dodamcore.schedule.domain.exception.ScheduleNotFoundException; import b1nd.dodamcore.schedule.repository.ScheduleRepository; import lombok.RequiredArgsConstructor; -import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -53,11 +52,11 @@ public void modifySchedule(int id, ScheduleReq modifyScheduleReq) { @Transactional(rollbackFor = Exception.class) public void deleteSchedule(int id) { - try { - scheduleRepository.deleteById(id); - } catch (EmptyResultDataAccessException e) { - throw new ScheduleNotFoundException(); - } + + Schedule schedule = scheduleRepository.findById(id) + .orElseThrow(ScheduleNotFoundException::new); + + scheduleRepository.delete(schedule); } public List getSchedules(int page, int limit) { diff --git a/dodam-infra/src/main/java/b1nd/dodaminfra/security/common/SecurityConfig.java b/dodam-infra/src/main/java/b1nd/dodaminfra/security/common/SecurityConfig.java index 5875430b..12321655 100644 --- a/dodam-infra/src/main/java/b1nd/dodaminfra/security/common/SecurityConfig.java +++ b/dodam-infra/src/main/java/b1nd/dodaminfra/security/common/SecurityConfig.java @@ -97,6 +97,10 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .requestMatchers(PATCH, "/banner/**").hasRole(ADMIN) .requestMatchers(DELETE, "/banner/**").hasRole(ADMIN) + .requestMatchers(POST, "/recruit").hasRole(TEACHER) + .requestMatchers(PATCH, "/recruit/**").hasRole(TEACHER) + .requestMatchers(DELETE, "/recruit/**").hasRole(TEACHER) + .anyRequest().authenticated() .and() .exceptionHandling()