diff --git a/src/main/java/com/server/bbo_gak/domain/notification/service/NotificationScheduler.java b/src/main/java/com/server/bbo_gak/domain/notification/service/NotificationScheduler.java index e441867..3fab08b 100644 --- a/src/main/java/com/server/bbo_gak/domain/notification/service/NotificationScheduler.java +++ b/src/main/java/com/server/bbo_gak/domain/notification/service/NotificationScheduler.java @@ -65,8 +65,8 @@ private List> getRecruitAndScheduleMaps(List createNotifications(List> recruitsWithOneDayLeft) { diff --git a/src/main/java/com/server/bbo_gak/domain/recruit/controller/RecruitScheduleController.java b/src/main/java/com/server/bbo_gak/domain/recruit/controller/RecruitScheduleController.java new file mode 100644 index 0000000..6f477b9 --- /dev/null +++ b/src/main/java/com/server/bbo_gak/domain/recruit/controller/RecruitScheduleController.java @@ -0,0 +1,80 @@ +package com.server.bbo_gak.domain.recruit.controller; + +import com.server.bbo_gak.domain.recruit.dto.request.RecruitScheduleCreateRequest; +import com.server.bbo_gak.domain.recruit.dto.request.RecruitScheduleUpdateDeadLineRequest; +import com.server.bbo_gak.domain.recruit.dto.request.RecruitScheduleUpdateStageRequest; +import com.server.bbo_gak.domain.recruit.dto.response.RecruitScheduleGetResponse; +import com.server.bbo_gak.domain.recruit.entity.RecruitSchedule; +import com.server.bbo_gak.domain.recruit.service.RecruitScheduleService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/v1/recruits/{id}/recruit-schedule") +@RequiredArgsConstructor +public class RecruitScheduleController { + private final RecruitScheduleService recruitScheduleService; + + + /** + * 공고 일정 생성 + */ + @PostMapping("") + public ResponseEntity createRecruitSchedule( + @PathVariable("id") Long id, //공고 id + @RequestBody RecruitScheduleCreateRequest request) { + RecruitSchedule recruitSchedule = recruitScheduleService.createRecruitSchedule(id, request); + return ResponseEntity.ok().body(RecruitScheduleGetResponse.from(recruitSchedule)); + } + + /** + * 공고 일정 전체 조회 + */ + @GetMapping("") + public ResponseEntity> getRecruitScheduleList( + @PathVariable("id") Long id //공고 id + ) { + return ResponseEntity.ok().body(recruitScheduleService.getRecruitScheduleList(id)); + } + + /** + * 공고 일정 (공고단계) 업데이트 + */ + @PutMapping("/{recruit-schedule-id}/stage") + public ResponseEntity updateRecruitScheduleStage( + @PathVariable("id") Long id, //공고 id + @PathVariable("recruit-schedule-id") Long recruitScheduleId, + @RequestBody RecruitScheduleUpdateStageRequest request + ) { + recruitScheduleService.updateRecruitScheduleStage(id, recruitScheduleId, request.recruitScheduleStage()); + return ResponseEntity.ok().body(null); + } + + /** + * 공고 일정 (공고 마감일) 업데이트 + */ + @PutMapping("/{recruit-schedule-id}/deadLine") + public ResponseEntity updateDeadLine( + @PathVariable("id") Long id, //공고 id + @PathVariable("recruit-schedule-id") Long recruitScheduleId, + @RequestBody RecruitScheduleUpdateDeadLineRequest request + ) { + recruitScheduleService.updateDeadLine(id, recruitScheduleId, request.deadLine()); + return ResponseEntity.ok().body(null); + } + + /** + * 공고 일정 삭제 + */ + @DeleteMapping("/{recruit-schedule-id}") + public ResponseEntity deleteRecruitSchedule( + @PathVariable("id") Long id, //공고 id + @PathVariable("recruit-schedule-id") Long recruitScheduleId + ) { + recruitScheduleService.deleteRecruitSchedule(id, recruitScheduleId); + return ResponseEntity.ok().body(null); + } +} diff --git a/src/main/java/com/server/bbo_gak/domain/recruit/dao/RecruitScheduleRepository.java b/src/main/java/com/server/bbo_gak/domain/recruit/dao/RecruitScheduleRepository.java index 27c0f79..ec21f42 100644 --- a/src/main/java/com/server/bbo_gak/domain/recruit/dao/RecruitScheduleRepository.java +++ b/src/main/java/com/server/bbo_gak/domain/recruit/dao/RecruitScheduleRepository.java @@ -3,9 +3,14 @@ import com.server.bbo_gak.domain.recruit.entity.RecruitSchedule; import java.time.LocalDate; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; public interface RecruitScheduleRepository extends JpaRepository { List findAllByDeadLineBetween(LocalDate now, LocalDate nowPlusOneDay); + + List findAllByRecruitId(Long recruitId); + + Optional findByIdAndRecruitId(Long recruitScheduleId, Long recruitId); } diff --git a/src/main/java/com/server/bbo_gak/domain/recruit/dto/request/RecruitCreateRequest.java b/src/main/java/com/server/bbo_gak/domain/recruit/dto/request/RecruitCreateRequest.java index 82d0962..a07f655 100644 --- a/src/main/java/com/server/bbo_gak/domain/recruit/dto/request/RecruitCreateRequest.java +++ b/src/main/java/com/server/bbo_gak/domain/recruit/dto/request/RecruitCreateRequest.java @@ -10,7 +10,7 @@ public record RecruitCreateRequest( String title, String siteUrl, String recruitScheduleStage, - String deadline + String deadLine ) { public Recruit toEntity(User user, Season season) { diff --git a/src/main/java/com/server/bbo_gak/domain/recruit/dto/request/RecruitScheduleCreateRequest.java b/src/main/java/com/server/bbo_gak/domain/recruit/dto/request/RecruitScheduleCreateRequest.java index 4ec85de..99a9053 100644 --- a/src/main/java/com/server/bbo_gak/domain/recruit/dto/request/RecruitScheduleCreateRequest.java +++ b/src/main/java/com/server/bbo_gak/domain/recruit/dto/request/RecruitScheduleCreateRequest.java @@ -1,18 +1,17 @@ package com.server.bbo_gak.domain.recruit.dto.request; -import com.server.bbo_gak.domain.recruit.entity.RecruitSchedule; -import com.server.bbo_gak.domain.recruit.entity.RecruitScheduleStage; -import java.time.LocalDate; +import lombok.Builder; +@Builder public record RecruitScheduleCreateRequest( - RecruitScheduleStage recruitScheduleStage, - String deadline + String recruitScheduleStage, + String deadLine ) { - public static RecruitSchedule of(RecruitScheduleStage recruitScheduleStage, String deadline) { - return RecruitSchedule.builder() + public static RecruitScheduleCreateRequest of(String recruitScheduleStage, String deadLine) { + return RecruitScheduleCreateRequest.builder() .recruitScheduleStage(recruitScheduleStage) - .deadLine(LocalDate.parse(deadline)) + .deadLine(deadLine) .build(); } } diff --git a/src/main/java/com/server/bbo_gak/domain/recruit/dto/request/RecruitScheduleUpdateDeadLineRequest.java b/src/main/java/com/server/bbo_gak/domain/recruit/dto/request/RecruitScheduleUpdateDeadLineRequest.java new file mode 100644 index 0000000..7f30e6d --- /dev/null +++ b/src/main/java/com/server/bbo_gak/domain/recruit/dto/request/RecruitScheduleUpdateDeadLineRequest.java @@ -0,0 +1,6 @@ +package com.server.bbo_gak.domain.recruit.dto.request; + +//TODO: deadLine null값 안들어오게 처리하기 +public record RecruitScheduleUpdateDeadLineRequest(String deadLine) { + +} diff --git a/src/main/java/com/server/bbo_gak/domain/recruit/dto/request/RecruitScheduleUpdateStageRequest.java b/src/main/java/com/server/bbo_gak/domain/recruit/dto/request/RecruitScheduleUpdateStageRequest.java new file mode 100644 index 0000000..327f752 --- /dev/null +++ b/src/main/java/com/server/bbo_gak/domain/recruit/dto/request/RecruitScheduleUpdateStageRequest.java @@ -0,0 +1,7 @@ +package com.server.bbo_gak.domain.recruit.dto.request; + + +//TODO: recruitScheduleStage null값 안들어오게 처리하기 +public record RecruitScheduleUpdateStageRequest(String recruitScheduleStage) { + +} diff --git a/src/main/java/com/server/bbo_gak/domain/recruit/entity/RecruitSchedule.java b/src/main/java/com/server/bbo_gak/domain/recruit/entity/RecruitSchedule.java index f0f3b2d..1f3f1d6 100644 --- a/src/main/java/com/server/bbo_gak/domain/recruit/entity/RecruitSchedule.java +++ b/src/main/java/com/server/bbo_gak/domain/recruit/entity/RecruitSchedule.java @@ -47,10 +47,10 @@ public RecruitSchedule(Recruit recruit, RecruitScheduleStage recruitScheduleStag this.deadLine = deadLine; } - public static RecruitSchedule of(Recruit recruit, RecruitScheduleStage recruitScheduleStage, String deadLine) { + public static RecruitSchedule of(Recruit recruit, String recruitScheduleStage, String deadLine) { return RecruitSchedule.builder() .recruit(recruit) - .recruitScheduleStage(recruitScheduleStage) + .recruitScheduleStage(RecruitScheduleStage.findByValue(recruitScheduleStage)) .deadLine(LocalDate.parse(deadLine)).build(); } @@ -62,4 +62,13 @@ public void setRecruit(Recruit recruit) { this.recruit = recruit; } + public void updateRecruitScheduleStage(String stage) { + this.recruitScheduleStage = RecruitScheduleStage.findByValue(stage); + } + + public void updateDeadLine(String deadLine) { + // TODO: date 유효성 검사해서 이상한 날짜면 에러 리턴 + this.deadLine = LocalDate.parse(deadLine); + } + } diff --git a/src/main/java/com/server/bbo_gak/domain/recruit/service/RecruitScheduleService.java b/src/main/java/com/server/bbo_gak/domain/recruit/service/RecruitScheduleService.java index 196b406..8c80311 100644 --- a/src/main/java/com/server/bbo_gak/domain/recruit/service/RecruitScheduleService.java +++ b/src/main/java/com/server/bbo_gak/domain/recruit/service/RecruitScheduleService.java @@ -1,16 +1,22 @@ package com.server.bbo_gak.domain.recruit.service; +import com.server.bbo_gak.domain.recruit.dto.request.RecruitScheduleCreateRequest; +import com.server.bbo_gak.domain.recruit.dto.response.RecruitScheduleGetResponse; import com.server.bbo_gak.domain.recruit.entity.RecruitSchedule; import org.springframework.stereotype.Service; +import java.util.List; + @Service public interface RecruitScheduleService { - RecruitSchedule createRecruitSchedule(RecruitSchedule recruitSchedule); + RecruitSchedule createRecruitSchedule(Long id, RecruitScheduleCreateRequest request); + + List getRecruitScheduleList(Long recruitId); - void getRecruitScheduleList(); + void updateRecruitScheduleStage(Long recruitId, Long recruitScheduleId, String stage); - void updateRecruitSchedule(); + void updateDeadLine(Long recruitId, Long recruitScheduleId, String deadLine); - void deleteRecruitSchedule(); + void deleteRecruitSchedule(Long recruitId, Long recruitScheduleId); } diff --git a/src/main/java/com/server/bbo_gak/domain/recruit/service/RecruitScheduleServiceImpl.java b/src/main/java/com/server/bbo_gak/domain/recruit/service/RecruitScheduleServiceImpl.java index 04cc0de..1d019e5 100644 --- a/src/main/java/com/server/bbo_gak/domain/recruit/service/RecruitScheduleServiceImpl.java +++ b/src/main/java/com/server/bbo_gak/domain/recruit/service/RecruitScheduleServiceImpl.java @@ -1,33 +1,69 @@ package com.server.bbo_gak.domain.recruit.service; +import com.server.bbo_gak.domain.recruit.dao.RecruitRepository; import com.server.bbo_gak.domain.recruit.dao.RecruitScheduleRepository; +import com.server.bbo_gak.domain.recruit.dto.request.RecruitScheduleCreateRequest; +import com.server.bbo_gak.domain.recruit.dto.response.RecruitScheduleGetResponse; +import com.server.bbo_gak.domain.recruit.entity.Recruit; import com.server.bbo_gak.domain.recruit.entity.RecruitSchedule; +import com.server.bbo_gak.global.error.exception.ErrorCode; +import com.server.bbo_gak.global.error.exception.NotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.List; +import org.springframework.transaction.annotation.Transactional; + @Service @RequiredArgsConstructor public class RecruitScheduleServiceImpl implements RecruitScheduleService { private final RecruitScheduleRepository recruitScheduleRepository; + private final RecruitRepository recruitRepository; @Override - public void deleteRecruitSchedule() { - + @Transactional + public RecruitSchedule createRecruitSchedule(Long id, RecruitScheduleCreateRequest request) { + Recruit recruit = recruitRepository.findById(id) + .orElseThrow(() -> new NotFoundException(ErrorCode.RECRUIT_NOT_FOUND)); + return recruitScheduleRepository.save(RecruitSchedule.of(recruit, request.recruitScheduleStage(), request.deadLine())); } @Override - public void updateRecruitSchedule() { + @Transactional + public List getRecruitScheduleList(Long recruitId) { + return recruitScheduleRepository.findAllByRecruitId(recruitId).stream() + .map(RecruitScheduleGetResponse::from) + .toList(); + } + @Override + @Transactional + public void updateRecruitScheduleStage(Long recruitId, Long recruitScheduleId, String stage) { + RecruitSchedule recruitSchedule = recruitScheduleRepository.findByIdAndRecruitId( + recruitScheduleId, recruitId) + .orElseThrow(() -> new NotFoundException(ErrorCode.RECRUIT_SCHEDULE_NOT_FOUND)); + + recruitSchedule.updateRecruitScheduleStage(stage); } @Override - public void getRecruitScheduleList() { + @Transactional + public void updateDeadLine(Long recruitId, Long recruitScheduleId, String deadLine){ + RecruitSchedule recruitSchedule = recruitScheduleRepository.findByIdAndRecruitId( + recruitScheduleId, recruitId) + .orElseThrow(() -> new NotFoundException(ErrorCode.RECRUIT_SCHEDULE_NOT_FOUND)); + recruitSchedule.updateDeadLine(deadLine); } @Override - public RecruitSchedule createRecruitSchedule(RecruitSchedule recruitSchedule) { - return recruitScheduleRepository.save(recruitSchedule); + @Transactional + public void deleteRecruitSchedule(Long recruitId, Long recruitScheduleId) { + RecruitSchedule recruitSchedule = recruitScheduleRepository.findByIdAndRecruitId( + recruitScheduleId, recruitId) + .orElseThrow(() -> new NotFoundException(ErrorCode.RECRUIT_SCHEDULE_NOT_FOUND)); + + recruitScheduleRepository.deleteById(recruitSchedule.getId()); } } diff --git a/src/main/java/com/server/bbo_gak/domain/recruit/service/RecruitService.java b/src/main/java/com/server/bbo_gak/domain/recruit/service/RecruitService.java index 20dba03..a49504b 100644 --- a/src/main/java/com/server/bbo_gak/domain/recruit/service/RecruitService.java +++ b/src/main/java/com/server/bbo_gak/domain/recruit/service/RecruitService.java @@ -3,10 +3,10 @@ import com.server.bbo_gak.domain.recruit.dao.RecruitRepository; import com.server.bbo_gak.domain.recruit.dto.request.RecruitCreateRequest; import com.server.bbo_gak.domain.recruit.dto.response.RecruitGetInnerResponse; +import com.server.bbo_gak.domain.recruit.dto.request.RecruitScheduleCreateRequest; import com.server.bbo_gak.domain.recruit.dto.response.RecruitGetResponse; import com.server.bbo_gak.domain.recruit.entity.Recruit; import com.server.bbo_gak.domain.recruit.entity.RecruitSchedule; -import com.server.bbo_gak.domain.recruit.entity.RecruitScheduleStage; import com.server.bbo_gak.domain.recruit.entity.RecruitStatus; import com.server.bbo_gak.domain.recruit.entity.RecruitStatusCategory; import com.server.bbo_gak.domain.recruit.entity.Season; @@ -26,146 +26,148 @@ @RequiredArgsConstructor public class RecruitService { - private final RecruitRepository recruitRepository; - private final SeasonService seasonService; - private final RecruitScheduleService recruitScheduleService; - - public List getTotalRecruitList(User user) { - List recruits = recruitRepository.findAllByUserId(user.getId()); - - return recruits.stream() - .sorted((r1, r2) -> r2.getCreatedDate().compareTo(r1.getCreatedDate())) - .map(RecruitGetResponse::from) - .toList(); + private final RecruitRepository recruitRepository; + private final SeasonService seasonService; + private final RecruitScheduleService recruitScheduleService; + + public List getTotalRecruitList(User user) { + List recruits = recruitRepository.findAllByUserId(user.getId()); + + return recruits.stream() + .sorted((r1, r2) -> r2.getCreatedDate().compareTo(r1.getCreatedDate())) + .map(RecruitGetResponse::from) + .toList(); + } + + public List getRecruitListBySeason(User user, String seasonName) { + Season season = seasonService.getSeasonByName(user, seasonName); + return recruitRepository.findAllByUserIdAndSeason(user.getId(), season).stream() + .map(RecruitGetResponse::from) + .toList(); + } + + + // TODO: 진행중인 공고는 일정등록이 안된 것을 우선으로 그 이후에는 RecruitSchedule이 현재와 가까운 순으로 정렬한다. + public List getProgressingRecruitList(User user) { + + List recruits = recruitRepository.findAllByUserId(user.getId()); + + // 불합격이 아닌 공고중에서 스케줄이 비어 있거나 지난 일정만 등록되어 있는 공고 중에서 지원 상태가 불합격인 공고를 분리해냄 + Map> partitionedRecruitsByNeedingSchedule = partitionRecruits(recruits); + + List recruitsNeedingSchedule = partitionedRecruitsByNeedingSchedule.get(true); + List recruitsWithSchedule = partitionedRecruitsByNeedingSchedule.get(false); + + //recruitsWithSchedule에서 시간이 지나지 않은 스케줄 중에서 가장 현재와 가까운 걸 기준으로 정렬 + List sortedRecruitsWithSchedule = recruitsWithSchedule.stream() + .sorted(Comparator.comparing(this::getNearestUpcomingDate)) + .toList(); + + recruitsNeedingSchedule.addAll(sortedRecruitsWithSchedule); + + return recruitsNeedingSchedule.stream() + .map(RecruitGetResponse::from) + .toList(); + } + + private LocalDate getNearestUpcomingDate(Recruit recruit) { + return recruit.getScheduleList().stream() + .map(RecruitSchedule::getDeadLine) + .filter(deadLine -> deadLine.isAfter(LocalDate.now())) + .min(Comparator.naturalOrder()) + .orElse(LocalDate.MAX); + } + + private Map> partitionRecruits(List recruits) { + return recruits.stream() + .filter(recruit -> !RecruitStatusCategory.isRejectionStatus( + recruit.getRecruitStatus())) // 불합격 상태 필터링 + .collect(Collectors.partitioningBy(this::isNeedsScheduleUpdate)); + } + + private boolean isNeedsScheduleUpdate(Recruit recruit) { + List scheduleList = recruit.getScheduleList(); + return scheduleList.isEmpty() || scheduleList.stream() + .allMatch(schedule -> schedule.getDeadLine().isBefore(LocalDate.now())); + } + + public RecruitGetInnerResponse getRecruit(User user, Long recruitId) { + Recruit recruit = findRecruitByUserAndId(user, recruitId); + return RecruitGetInnerResponse.from(recruit); + } + + @Transactional + public RecruitGetResponse createRecruit(User user, RecruitCreateRequest request) { + + Season season = seasonService.getSeasonByName(user, request.season()); + Recruit recruit = request.toEntity(user, season); + // 공고 저장하여 id 확보 + Recruit savedRecruit = recruitRepository.save(recruit); + + addRecruitScheduleIfRequired(request, savedRecruit); + + // 공고에 공고 일정을 설정 + recruitRepository.save(recruit); + + return RecruitGetResponse.from(savedRecruit); + } + + private void addRecruitScheduleIfRequired(RecruitCreateRequest request, Recruit recruit) { + if (request.deadLine() != null && !request.deadLine().isEmpty()) { + RecruitSchedule recruitSchedule = recruitScheduleService.createRecruitSchedule( + recruit.getId(), + RecruitScheduleCreateRequest.of( + request.recruitScheduleStage(), request.deadLine()) + ); + recruit.addSchedule(recruitSchedule); } + } - public List getRecruitListBySeason(User user, String seasonName) { - Season season = seasonService.getSeasonByName(user, seasonName); - return recruitRepository.findAllByUserIdAndSeason(user.getId(), season).stream() - .map(RecruitGetResponse::from) - .toList(); - } - - - // TODO: 진행중인 공고는 일정등록이 안된 것을 우선으로 그 이후에는 RecruitSchedule이 현재와 가까운 순으로 정렬한다. - public List getProgressingRecruitList(User user) { - - List recruits = recruitRepository.findAllByUserId(user.getId()); - - // 불합격이 아닌 공고중에서 스케줄이 비어 있거나 지난 일정만 등록되어 있는 공고 중에서 지원 상태가 불합격인 공고를 분리해냄 - Map> partitionedRecruitsByNeedingSchedule = partitionRecruits(recruits); - - List recruitsNeedingSchedule = partitionedRecruitsByNeedingSchedule.get(true); - List recruitsWithSchedule = partitionedRecruitsByNeedingSchedule.get(false); - - //recruitsWithSchedule에서 시간이 지나지 않은 스케줄 중에서 가장 현재와 가까운 걸 기준으로 정렬 - List sortedRecruitsWithSchedule = recruitsWithSchedule.stream() - .sorted(Comparator.comparing(this::getNearestUpcomingDate)) - .toList(); + @Transactional + public void deleteRecruit(User user, Long recruitId) { + Recruit recruit = findRecruitByUserAndId(user, recruitId); - recruitsNeedingSchedule.addAll(sortedRecruitsWithSchedule); + recruitRepository.deleteById(recruit.getId()); + } - return recruitsNeedingSchedule.stream() - .map(RecruitGetResponse::from) - .toList(); - } + @Transactional + public RecruitGetResponse updateRecruitTitle(User user, Long recruitId, String title) { + Recruit recruit = findRecruitByUserAndId(user, recruitId); - private LocalDate getNearestUpcomingDate(Recruit recruit) { - return recruit.getScheduleList().stream() - .map(RecruitSchedule::getDeadLine) - .filter(deadLine -> deadLine.isAfter(LocalDate.now())) - .min(Comparator.naturalOrder()) - .orElse(LocalDate.MAX); - } + recruit.updateTitle(title); - private Map> partitionRecruits(List recruits) { - return recruits.stream() - .filter(recruit -> !RecruitStatusCategory.isRejectionStatus(recruit.getRecruitStatus())) // 불합격 상태 필터링 - .collect(Collectors.partitioningBy(this::isNeedsScheduleUpdate)); - } + return RecruitGetResponse.from(recruitRepository.save(recruit)); + } - private boolean isNeedsScheduleUpdate(Recruit recruit) { - List scheduleList = recruit.getScheduleList(); - return scheduleList.isEmpty() || scheduleList.stream() - .allMatch(schedule -> schedule.getDeadLine().isBefore(LocalDate.now())); - } + @Transactional + public RecruitGetResponse updateRecruitSeason(User user, Long recruitId, String seasonName) { + Recruit recruit = findRecruitByUserAndId(user, recruitId); + Season season = seasonService.getSeasonByName(user, seasonName); + recruit.updateSeason(season); - public RecruitGetInnerResponse getRecruit(User user, Long recruitId) { - Recruit recruit = findRecruitByUserAndId(user, recruitId); - return RecruitGetInnerResponse.from(recruit); - } + return RecruitGetResponse.from(recruitRepository.save(recruit)); + } - @Transactional - public RecruitGetResponse createRecruit(User user, RecruitCreateRequest request) { + @Transactional + public RecruitGetResponse updateRecruitStatus(User user, Long recruitId, String recruitStatus) { + Recruit recruit = findRecruitByUserAndId(user, recruitId); - Season season = seasonService.getSeasonByName(user, request.season()); - Recruit recruit = request.toEntity(user, season); - // 공고 저장하여 id 확보 - Recruit savedRecruit = recruitRepository.save(recruit); + recruit.updateRecruitStatus(RecruitStatus.findByValue(recruitStatus)); - addRecruitScheduleIfRequired(request, savedRecruit); + return RecruitGetResponse.from(recruitRepository.save(recruit)); + } - // 공고에 공고 일정을 설정 - recruitRepository.save(recruit); + @Transactional + public RecruitGetResponse updateRecruitSiteUrl(User user, Long recruitId, String siteUrl) { + Recruit recruit = findRecruitByUserAndId(user, recruitId); - return RecruitGetResponse.from(savedRecruit); - } - - private void addRecruitScheduleIfRequired(RecruitCreateRequest request, Recruit recruit) { - if (request.deadline() != null && !request.deadline().isEmpty()) { - RecruitSchedule recruitSchedule = recruitScheduleService.createRecruitSchedule( - RecruitSchedule.of(recruit, RecruitScheduleStage.findByValue(request.recruitScheduleStage()), - request.deadline()) - ); - recruit.addSchedule(recruitSchedule); - } - } + recruit.updateSiteUrl(siteUrl); - @Transactional - public void deleteRecruit(User user, Long recruitId) { - Recruit recruit = findRecruitByUserAndId(user, recruitId); + return RecruitGetResponse.from(recruitRepository.save(recruit)); + } - recruitRepository.deleteById(recruit.getId()); - } - - @Transactional - public RecruitGetResponse updateRecruitTitle(User user, Long recruitId, String title) { - Recruit recruit = findRecruitByUserAndId(user, recruitId); - - recruit.updateTitle(title); - - return RecruitGetResponse.from(recruitRepository.save(recruit)); - } - - @Transactional - public RecruitGetResponse updateRecruitSeason(User user, Long recruitId, String seasonName) { - Recruit recruit = findRecruitByUserAndId(user, recruitId); - Season season = seasonService.getSeasonByName(user, seasonName); - recruit.updateSeason(season); - - return RecruitGetResponse.from(recruitRepository.save(recruit)); - } - - @Transactional - public RecruitGetResponse updateRecruitStatus(User user, Long recruitId, String recruitStatus) { - Recruit recruit = findRecruitByUserAndId(user, recruitId); - - recruit.updateRecruitStatus(RecruitStatus.findByValue(recruitStatus)); - - return RecruitGetResponse.from(recruitRepository.save(recruit)); - } - - @Transactional - public RecruitGetResponse updateRecruitSiteUrl(User user, Long recruitId, String siteUrl) { - Recruit recruit = findRecruitByUserAndId(user, recruitId); - - recruit.updateSiteUrl(siteUrl); - - return RecruitGetResponse.from(recruitRepository.save(recruit)); - } - - private Recruit findRecruitByUserAndId(User user, Long recruitId) { - return recruitRepository.findByUserIdAndId(user.getId(), recruitId) - .orElseThrow(() -> new NotFoundException(ErrorCode.RECRUIT_NOT_FOUND)); - } + private Recruit findRecruitByUserAndId(User user, Long recruitId) { + return recruitRepository.findByUserIdAndId(user.getId(), recruitId) + .orElseThrow(() -> new NotFoundException(ErrorCode.RECRUIT_NOT_FOUND)); + } } diff --git a/src/main/java/com/server/bbo_gak/global/error/exception/ErrorCode.java b/src/main/java/com/server/bbo_gak/global/error/exception/ErrorCode.java index 067b420..58b0160 100644 --- a/src/main/java/com/server/bbo_gak/global/error/exception/ErrorCode.java +++ b/src/main/java/com/server/bbo_gak/global/error/exception/ErrorCode.java @@ -56,6 +56,11 @@ public enum ErrorCode { RECRUIT_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 공고를 찾을 수 없습니다"), RECRUIT_STATUS_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 공고 지원 상태를 찾을 수 없습니다"), RECRUIT_SCHEDULE_STAGE_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 공고 일정 단계를 찾을 수 없습니다"), + + + //RecruitSchedule + RECRUIT_SCHEDULE_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 공고일정을 찾을 수 없습니다"), + CARD_TAG_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 카드와 태그 매핑을 찾을 수 없습니다"), //CardMemo diff --git a/src/test/java/com/server/bbo_gak/domain/recruit/controller/RecruitScheduleControllerTest.java b/src/test/java/com/server/bbo_gak/domain/recruit/controller/RecruitScheduleControllerTest.java new file mode 100644 index 0000000..1b4485c --- /dev/null +++ b/src/test/java/com/server/bbo_gak/domain/recruit/controller/RecruitScheduleControllerTest.java @@ -0,0 +1,119 @@ +package com.server.bbo_gak.domain.recruit.controller; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.server.bbo_gak.domain.recruit.dto.request.RecruitScheduleCreateRequest; +import com.server.bbo_gak.domain.recruit.dto.request.RecruitScheduleUpdateDeadLineRequest; +import com.server.bbo_gak.domain.recruit.dto.request.RecruitScheduleUpdateStageRequest; +import com.server.bbo_gak.domain.recruit.dto.response.RecruitScheduleGetResponse; +import com.server.bbo_gak.domain.recruit.entity.RecruitScheduleStage; +import com.server.bbo_gak.global.AbstractRestDocsTests; +import com.server.bbo_gak.global.RestDocsFactory; +import java.util.List; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpMethod; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@SpringBootTest +@ActiveProfiles("test") +@Sql({"/all-data-delete.sql", "/recruit-schedule-test-data.sql"}) +class RecruitScheduleControllerTest extends AbstractRestDocsTests { + + private static final String DEFAULT_URL = "/api/v1/recruits/{id}/recruit-schedule"; + + @Autowired + private RestDocsFactory restDocsFactory; + + @Nested + class 공고일정_생성 { + + @Test + public void 성공() throws Exception { + RecruitScheduleCreateRequest request = new RecruitScheduleCreateRequest( + "1차 면접", + "2024-08-31" + ); + + mockMvc.perform( + restDocsFactory.createRequest(DEFAULT_URL, request, HttpMethod.POST, objectMapper, 1L)) + .andExpect(status().isOk()) + .andDo(restDocsFactory.getSuccessResource("[POST] 공고일정 생성 성공", "공고일정 생성", "RecruitSchedule", request, null)); + } + } + + @Nested + class 공고일정_전체조회 { + + // 첫 번째 response 객체 + RecruitScheduleGetResponse response1 = RecruitScheduleGetResponse.builder() + .id(1L) + .recruitScheduleStage(RecruitScheduleStage.CLOSING_DOCUMENT.getValue()) + .deadLine("2025-01-01T00:00:00") + .build(); + + // 두 번째 response 객체 + RecruitScheduleGetResponse response2 = RecruitScheduleGetResponse.builder() + .id(2L) + .recruitScheduleStage(RecruitScheduleStage.FIRST_INTERVIEW.getValue()) + .deadLine("2025-02-01T00:00:00") + .build(); + + @Test + public void 성공() throws Exception { + mockMvc.perform( + restDocsFactory.createRequest(DEFAULT_URL, null, HttpMethod.GET, objectMapper, 1L)) + .andExpect(status().isOk()) + .andDo(restDocsFactory.getSuccessResourceList("[GET] 공고일정 전체 조회 성공", "공고일정 전체 조회 ", "RecruitSchedule", List.of(), + List.of(response1, response2))); + } + } + + @Nested + class 공고일정_공고단계_업데이트 { + @Test + public void 성공() throws Exception { + RecruitScheduleUpdateStageRequest request = new RecruitScheduleUpdateStageRequest( + "2차 면접" + ); + + mockMvc.perform( + restDocsFactory.createRequest(DEFAULT_URL+"/{recruit-schedule-id}/stage", request, HttpMethod.PUT, objectMapper, 1L, 1L)) + .andExpect(status().isOk()) + .andDo(restDocsFactory.getSuccessResource("[PUT] 공고단계 업데이트 성공", "공고단계 업데이트", "RecruitSchedule", request, null)); + } + } + + @Nested + class 공고일정_마감일_업데이트 { + @Test + public void 성공() throws Exception { + RecruitScheduleUpdateDeadLineRequest request = new RecruitScheduleUpdateDeadLineRequest( + "2024-12-25" + ); + + mockMvc.perform( + restDocsFactory.createRequest(DEFAULT_URL+"/{recruit-schedule-id}/deadLine", request, HttpMethod.PUT, objectMapper, 1L, 1L)) + .andExpect(status().isOk()) + .andDo(restDocsFactory.getSuccessResource("[PUT] 공고마감일 업데이트 성공", "공고마감일 업데이트", "RecruitSchedule", request, null)); + } + } + + @Nested + class 공고일정_삭제 { + + @Test + public void 성공() throws Exception { + mockMvc.perform( + restDocsFactory.createRequest(DEFAULT_URL+"/{recruit-schedule-id}", null, HttpMethod.DELETE, objectMapper, 1L, 1L)) + .andExpect(status().isOk()) + .andDo(restDocsFactory.getSuccessResource("[DELETE] 공고일정 삭제 성공", "공고일정 삭제", "RecruitSchedule", null, null)); + } + } + +} \ No newline at end of file diff --git a/src/test/resources/recruit-schedule-test-data.sql b/src/test/resources/recruit-schedule-test-data.sql new file mode 100644 index 0000000..8f4176b --- /dev/null +++ b/src/test/resources/recruit-schedule-test-data.sql @@ -0,0 +1,59 @@ +INSERT INTO users (deleted, created_at, update_at, user_id, dtype, email, login_id, name, password, role) +VALUES (false, '2024-07-24 21:27:20.000000', '2024-07-24 21:27:21.000000', 1, 'AuthTestUser', 'test', 'test', 'test', + 'test', 'USER'), + (false, '2024-07-24 21:27:20.000000', '2024-07-24 21:27:21.000000', 2, 'AuthTestUser', 'test', 'test', 'test', + 'test', 'USER'), + (false, '2024-07-24 21:27:20.000000', '2024-07-24 21:27:21.000000', 3, 'AuthTestUser', 'test', 'test', 'test', + 'test', 'USER') +; + +-- Season 테이블에 데이터 삽입 +INSERT INTO recruit_season (recruit_season_id, name, user_id, deleted, update_at, created_at) +VALUES (1, '2024 상반기', 1, false, '2024-07-24 21:27:20.000000', '2024-07-24 21:27:21.000000'), + (2, '2024 하반기', 1, false, '2024-07-24 21:27:20.000000', '2024-07-24 21:27:21.000000'), + (3, '2024 상반기', 2, false, '2024-07-24 21:27:20.000000', '2024-07-24 21:27:21.000000'), + (4, '2024 하반기', 2, false, '2024-07-24 21:27:20.000000', '2024-07-24 21:27:21.000000'), + (5, '2024 상반기', 3, false, '2024-07-24 21:27:20.000000', '2024-07-24 21:27:21.000000'), + (6, '2024 하반기', 3, false, '2024-07-24 21:27:20.000000', '2024-07-24 21:27:21.000000') +; + +-- Recruit 테이블에 데이터 삽입 +INSERT INTO recruit (recruit_id, title, site_url, recruit_status, recruit_season_id, user_id, created_at, update_at, + deleted) +VALUES (1, 'Title for one day left', 'http://example.com/1', 'DOCUMENT_PASSED', 1, 1, CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP, false), + (2, 'Title for more than one day left', 'http://example.com/2', 'PREPARATION_IN_PROGRESS', 2, 1, + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, false), + (3, 'Title for more than one day left', 'http://example.com/2', 'PREPARATION_IN_PROGRESS', 3, 2, + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, false), + (4, 'Title for more than one day left', 'http://example.com/2', 'PREPARATION_IN_PROGRESS', 3, 2, + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, false), + (5, 'Title for more than one day left', 'http://example.com/2', 'PREPARATION_IN_PROGRESS', 4, 2, + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, false), + (6, 'Title for more than one day left', 'http://example.com/2', 'PREPARATION_IN_PROGRESS', 5, 3, + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, false), + (7, 'Title for more than one day left', 'http://example.com/2', 'PREPARATION_IN_PROGRESS', 6, 3, + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, false) +; + +-- RecruitSchedule 테이블에 데이터 삽입 +INSERT INTO recruit_schedule (recruit_schedule_id, recruit_id, recruit_schedule_stage, dead_line, deleted, update_at, created_at) +VALUES (1, 1, 'FIRST_INTERVIEW', TIMESTAMPADD(DAY, 3, CURRENT_TIMESTAMP), false, '2024-07-24 21:26:28.000000', '2024-07-24 21:26:28.000000'), + (2, 4, 'FIRST_INTERVIEW', TIMESTAMPADD(DAY, 3, CURRENT_TIMESTAMP), false, '2024-07-24 21:26:28.000000', '2024-07-24 21:26:28.000000'), + (3, 5, 'FIRST_INTERVIEW', TIMESTAMPADD(DAY, 1, CURRENT_TIMESTAMP), false, '2024-07-24 21:26:28.000000', '2024-07-24 21:26:28.000000'), + (4, 6, 'FIRST_INTERVIEW', TIMESTAMPADD(DAY, 2, CURRENT_TIMESTAMP), false, '2024-07-24 21:26:28.000000', '2024-07-24 21:26:28.000000'), + (5, 6, 'SECOND_INTERVIEW', TIMESTAMPADD(DAY, 3, CURRENT_TIMESTAMP), false, '2024-07-24 21:26:28.000000', '2024-07-24 21:26:28.000000'), + (6, 7, 'FIRST_INTERVIEW', TIMESTAMPADD(DAY, -2, CURRENT_TIMESTAMP), false, '2024-07-24 21:26:28.000000', '2024-07-24 21:26:28.000000'), + (7, 7, 'SECOND_INTERVIEW', TIMESTAMPADD(DAY, -1, CURRENT_TIMESTAMP), false, '2024-07-24 21:26:28.000000', '2024-07-24 21:26:28.000000'), + (8, 7, 'FINAL_INTERVIEW', TIMESTAMPADD(DAY, 1, CURRENT_TIMESTAMP), false, '2024-07-24 21:26:28.000000', '2024-07-24 21:26:28.000000') +; + +INSERT INTO card (deleted, copy_flag, access_time, card_id, created_at, update_at, user_id, content, title, recruit_id) +VALUES (false, false, '2024-07-24 21:22:04.000000', 1, '2024-07-24 21:22:07.000000', '2024-07-24 21:22:08.000000', 1, + 'test_contents', 'test_title', 1); +INSERT INTO card (deleted, copy_flag, access_time, card_id, created_at, update_at, user_id, content, title, recruit_id) +VALUES (false, false, '1970-01-01 00:00:00.001000', 2, '1970-01-01 00:00:00.001000', '1970-01-01 00:00:00.001000', 1, + 'testc', 'testc', 1); +INSERT INTO card (deleted, copy_flag, access_time, card_id, created_at, update_at, user_id, content, title, recruit_id) +VALUES (false, false, '1970-01-01 00:00:00.001000', 3, '1970-01-01 00:00:00.001000', '1970-01-01 00:00:00.001000', 1, + 'testc', 'testt', 1); \ No newline at end of file