diff --git a/gg-admin-repo/src/main/java/gg/admin/repo/recruit/ApplicationAdminRepository.java b/gg-admin-repo/src/main/java/gg/admin/repo/recruit/ApplicationAdminRepository.java index 17b1cc34c..3645e8aec 100644 --- a/gg-admin-repo/src/main/java/gg/admin/repo/recruit/ApplicationAdminRepository.java +++ b/gg-admin-repo/src/main/java/gg/admin/repo/recruit/ApplicationAdminRepository.java @@ -3,6 +3,9 @@ import java.util.List; import java.util.Optional; +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.data.repository.query.Param; @@ -12,6 +15,50 @@ public interface ApplicationAdminRepository extends JpaRepository { Optional findByIdAndRecruitId(Long applicationId, Long recruitId); + /** + * id 조건에 일치하는 지원서 목록 반환 + * @param recruitId + * @param pageable + * @return + */ + @EntityGraph(attributePaths = {"user", "applicationAnswers", "applicationAnswers.question", + "applicationAnswers.question.checkLists"}) + Page findByRecruitIdAndIsDeletedFalseOrderByIdDesc(Long recruitId, Pageable pageable); + + /** + * id 조건 및 체크리스트 조건에 일치하는 지원서 목록 반환 + * @param recruitId + * @param questionId + * @param checkListIds + * @param pageable + */ + @EntityGraph(attributePaths = {"user", "applicationAnswers", "applicationAnswers.question", + "applicationAnswers.question.checkLists"}) + @Query("SELECT a FROM Application a WHERE a.isDeleted = false AND a.id IN " + + "(SELECT aa.application.id FROM ApplicationAnswerCheckList aa " + + "JOIN aa.checkList cl " + + "JOIN aa.application.recruit r " + + "WHERE r.id =:recruitId AND aa.question.id = :questionId AND cl.id IN :checkListIds) " + + "ORDER BY a.id DESC") + Page findAllByCheckList( + @Param("recruitId") Long recruitId, + @Param("questionId") Long questionId, + @Param("checkListIds") List checkListIds, + Pageable pageable); + + @EntityGraph(attributePaths = {"user", "applicationAnswers", "applicationAnswers.question", + "applicationAnswers.question.checkLists"}) + @Query("SELECT a FROM Application a WHERE a.isDeleted = false AND a.id IN " + + "(SELECT aa.application.id FROM ApplicationAnswerText aa " + + "JOIN aa.application.recruit r " + + "WHERE r.id =:recruitId AND aa.question.id = :questionId AND aa.answer LIKE CONCAT('%', :search, '%')) " + + "ORDER BY a.id DESC") + Page findAllByContainSearch( + @Param("recruitId") Long recruitId, + @Param("questionId") Long questionId, + @Param("search") String search, + Pageable pageable); + @Query("SELECT a FROM Application a " + "JOIN FETCH a.user " + "LEFT JOIN FETCH a.recruitStatus " diff --git a/gg-admin-repo/src/test/java/gg/admin/repo/recruit/ApplicationAdminRepositoryTest.java b/gg-admin-repo/src/test/java/gg/admin/repo/recruit/ApplicationAdminRepositoryTest.java index dd53f0f14..30c5a1a29 100644 --- a/gg-admin-repo/src/test/java/gg/admin/repo/recruit/ApplicationAdminRepositoryTest.java +++ b/gg-admin-repo/src/test/java/gg/admin/repo/recruit/ApplicationAdminRepositoryTest.java @@ -1,20 +1,30 @@ package gg.admin.repo.recruit; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import javax.persistence.EntityManager; import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.transaction.annotation.Transactional; import gg.data.recruit.application.Application; +import gg.data.recruit.application.ApplicationAnswerCheckList; +import gg.data.recruit.application.ApplicationAnswerText; import gg.data.recruit.application.RecruitStatus; +import gg.data.recruit.recruitment.CheckList; +import gg.data.recruit.recruitment.Question; import gg.data.recruit.recruitment.Recruitment; +import gg.data.recruit.recruitment.enums.InputType; import gg.data.user.User; import gg.utils.TestDataUtils; import gg.utils.annotation.IntegrationTest; @@ -93,4 +103,129 @@ void findSuccess() { Assertions.assertThat(res.get(1).getRecruitStatus()).isNull(); } } + + @Nested + @DisplayName("application by getRecruitmentApplications condition success test") + class ApplicationByGetRecruitmentApplicationsCondition { + Long wrongApplicationId; + Long userId; + Long recruitmentId; + Long applicationId; + Long questionId; + Long checkListId1; + Long checkListId2; + Long applicationAnswerCheckListId; + String search = "hello world"; + + @BeforeEach + public void init() { + Recruitment recruitment = testDataUtils.createNewRecruitment(); + User user = testDataUtils.createNewUser(); + + //target 1 + Application application = testDataUtils.createApplication(user, recruitment); + Question question = testDataUtils.createNewQuestion(recruitment, InputType.SINGLE_CHECK, "dubby", 2); + CheckList checkList = testDataUtils.createNewCheckList(question, "dd"); + ApplicationAnswerCheckList applicationAnswerCheckList = testDataUtils.createNewApplicationAnswerCheckList( + application, question, checkList); + + //target 2 + Application application2 = testDataUtils.createApplication(user, recruitment); + CheckList checkList2 = testDataUtils.createNewCheckList(question, "dd"); + ApplicationAnswerCheckList applicationAnswerCheckList2 = testDataUtils.createNewApplicationAnswerCheckList( + application2, question, checkList2); + + //must not contain + Application application3 = testDataUtils.createApplication(user, recruitment); + CheckList checkList3 = testDataUtils.createNewCheckList(question, "dd"); + ApplicationAnswerCheckList applicationAnswerCheckList3 = testDataUtils.createNewApplicationAnswerCheckList( + application, question, checkList3); + + // search target + ApplicationAnswerText answerText = testDataUtils.createNewApplicationAnswerText(application, question, + search); + ApplicationAnswerText answerText2 = testDataUtils.createNewApplicationAnswerText(application2, question, + "pp" + search + "pp"); + ApplicationAnswerText answerText3 = testDataUtils.createNewApplicationAnswerText(application3, question, + "pp" + search); + + wrongApplicationId = application3.getId(); + userId = user.getId(); + recruitmentId = recruitment.getId(); + applicationId = application.getId(); + questionId = question.getId(); + checkListId1 = checkList.getId(); + checkListId2 = checkList2.getId(); + applicationAnswerCheckListId = applicationAnswerCheckList.getId(); + + entityManager.flush(); + entityManager.clear(); + } + + @Nested + @DisplayName("findByRecruitIdAndIsDeletedFalse") + class FindByRecruitIdAndIsDeletedFalse { + + @Test + @DisplayName("조회 성공") + void success() { + //Arrange + Pageable pageable = PageRequest.of(0, 10); + + // Act + Page result = applicationAdminRepository.findByRecruitIdAndIsDeletedFalseOrderByIdDesc( + recruitmentId, + pageable); + + // Assert + Assertions.assertThat(result.getContent().size()).isEqualTo(3); + } + } + + @Nested + @DisplayName("FindAllByCheckList") + class FindAllByCheckList { + + @Test + @DisplayName("여러개의 조건 중 하나만 만족해도 조회가 성공") + void success() { + //Arrange + List checkListTargetId = new ArrayList<>(); + checkListTargetId.add(checkListId1); + checkListTargetId.add(checkListId2); + Pageable pageable = PageRequest.of(0, 10); + + // Act + Page result = applicationAdminRepository.findAllByCheckList(recruitmentId, + questionId, checkListTargetId, pageable); + + // Assert + Assertions.assertThat(result.getContent().size()).isEqualTo(2); + for (Application entity : result.getContent()) { + Assertions.assertThat(entity.getId()).isNotEqualTo(wrongApplicationId); + } + } + } + + @Nested + @DisplayName("findAllByContainSearch") + class FindAllByContainSearch { + + @Test + @DisplayName("search를 포함하면 조회 성공") + void success() { + //Arrange + Pageable pageable = PageRequest.of(0, 10); + + // Act + Page result = applicationAdminRepository.findAllByContainSearch(recruitmentId, + questionId, search, pageable); + + // Assert + Assertions.assertThat(result.getContent().size()).isEqualTo(3); + } + } + + } + } diff --git a/gg-data/src/main/java/gg/data/recruit/application/Application.java b/gg-data/src/main/java/gg/data/recruit/application/Application.java index c3da91b91..8e8f4ad23 100644 --- a/gg-data/src/main/java/gg/data/recruit/application/Application.java +++ b/gg-data/src/main/java/gg/data/recruit/application/Application.java @@ -1,5 +1,8 @@ package gg.data.recruit.application; +import java.util.HashSet; +import java.util.Set; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; @@ -10,6 +13,7 @@ import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; import javax.persistence.OneToOne; import gg.data.BaseTimeEntity; @@ -48,6 +52,9 @@ public class Application extends BaseTimeEntity { @Column(length = 15, nullable = false) private ApplicationStatus status; + @OneToMany(mappedBy = "application", fetch = FetchType.LAZY) + private Set applicationAnswers = new HashSet<>(); + public Application(User user, Recruitment recruit) { this.user = user; this.recruit = recruit; diff --git a/gg-data/src/main/java/gg/data/recruit/application/ApplicationAnswer.java b/gg-data/src/main/java/gg/data/recruit/application/ApplicationAnswer.java index 40f0613fe..cec03b60b 100644 --- a/gg-data/src/main/java/gg/data/recruit/application/ApplicationAnswer.java +++ b/gg-data/src/main/java/gg/data/recruit/application/ApplicationAnswer.java @@ -22,6 +22,7 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) @Inheritance(strategy = InheritanceType.JOINED) @DiscriminatorColumn(name = "answer_type") +@Getter public abstract class ApplicationAnswer extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/gg-data/src/main/java/gg/data/recruit/application/ApplicationAnswerCheckList.java b/gg-data/src/main/java/gg/data/recruit/application/ApplicationAnswerCheckList.java index 9c81b1d3e..8048dde5e 100644 --- a/gg-data/src/main/java/gg/data/recruit/application/ApplicationAnswerCheckList.java +++ b/gg-data/src/main/java/gg/data/recruit/application/ApplicationAnswerCheckList.java @@ -3,22 +3,19 @@ import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import javax.persistence.FetchType; -import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import gg.data.recruit.recruitment.CheckList; import gg.data.recruit.recruitment.Question; +import lombok.Getter; import lombok.NoArgsConstructor; @Entity @NoArgsConstructor(access = lombok.AccessLevel.PROTECTED) @DiscriminatorValue("CHECK_LIST") +@Getter public class ApplicationAnswerCheckList extends ApplicationAnswer { - - @Id - private Long id; - @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "check_list_id", nullable = false) private CheckList checkList; diff --git a/gg-data/src/main/java/gg/data/recruit/application/ApplicationAnswerText.java b/gg-data/src/main/java/gg/data/recruit/application/ApplicationAnswerText.java index 55ce791a1..c20d4b396 100644 --- a/gg-data/src/main/java/gg/data/recruit/application/ApplicationAnswerText.java +++ b/gg-data/src/main/java/gg/data/recruit/application/ApplicationAnswerText.java @@ -3,7 +3,6 @@ import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; -import javax.persistence.Id; import gg.data.recruit.recruitment.Question; import lombok.NoArgsConstructor; @@ -12,10 +11,6 @@ @NoArgsConstructor(access = lombok.AccessLevel.PROTECTED) @DiscriminatorValue("TEXT") public class ApplicationAnswerText extends ApplicationAnswer { - - @Id - private Long id; - @Column(length = 1000) private String answer; diff --git a/gg-recruit-api/src/main/java/gg/recruit/api/admin/controller/RecruitmentAdminController.java b/gg-recruit-api/src/main/java/gg/recruit/api/admin/controller/RecruitmentAdminController.java index b9da87e72..847bca06b 100644 --- a/gg-recruit-api/src/main/java/gg/recruit/api/admin/controller/RecruitmentAdminController.java +++ b/gg-recruit-api/src/main/java/gg/recruit/api/admin/controller/RecruitmentAdminController.java @@ -1,13 +1,17 @@ package gg.recruit.api.admin.controller; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import javax.validation.Valid; import javax.validation.constraints.Positive; +import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; @@ -28,13 +32,17 @@ import gg.recruit.api.admin.controller.request.SetFinalApplicationStatusResultReqDto; import gg.recruit.api.admin.controller.request.UpdateStatusRequestDto; import gg.recruit.api.admin.controller.response.CreatedRecruitmentResponse; +import gg.recruit.api.admin.controller.response.GetRecruitmentApplicationResponseDto; import gg.recruit.api.admin.controller.response.RecruitmentApplicantResultResponseDto; import gg.recruit.api.admin.controller.response.RecruitmentApplicantResultsResponseDto; import gg.recruit.api.admin.controller.response.RecruitmentsResponse; import gg.recruit.api.admin.service.RecruitmentAdminService; +import gg.recruit.api.admin.service.dto.GetRecruitmentApplicationsDto; import gg.recruit.api.admin.service.dto.UpdateApplicationStatusDto; import gg.recruit.api.admin.service.dto.UpdateRecruitStatusParam; import gg.utils.dto.PageRequestDto; +import gg.utils.exception.ErrorCode; +import gg.utils.exception.custom.BusinessException; import lombok.RequiredArgsConstructor; @RestController @@ -83,6 +91,40 @@ public ResponseEntity setFinalApplicationStatusResult( return new ResponseEntity<>(HttpStatus.CREATED); } + @GetMapping("/{recruitId}/applications") + public ResponseEntity getRecruitmentApplications( + @PathVariable @Positive Long recruitId, + @RequestParam(value = "question", required = false) Long questionId, + @RequestParam(value = "checks", required = false) String checks, + @RequestParam(value = "search", required = false) String search, + @PageableDefault(sort = "id", page = 1) Pageable page + ) { + GetRecruitmentApplicationsDto dto; + + Pageable parsedPage = PageRequest.of(page.getPageNumber() - 1, Math.min(page.getPageSize(), 20)); + List checkListIds = parseChecks(checks); + dto = new GetRecruitmentApplicationsDto(recruitId, questionId, checkListIds, search, parsedPage); + Page applicationsPage = recruitmentAdminService.findApplicationsWithAnswersAndUserWithFilter(dto); + return ResponseEntity.ok(GetRecruitmentApplicationResponseDto.applicationsPageToDto(applicationsPage)); + } + + /** + * 전달된 checkListId 목록 파싱 + * @param checks + * @return List + */ + private List parseChecks(String checks) { + try { + if (checks != null) { + return Arrays.stream(checks.split(",")).map(Long::parseLong).collect(Collectors.toList()); + } else { + return new ArrayList<>(); + } + } catch (Exception e) { + throw new BusinessException(ErrorCode.BAD_ARGU); + } + } + @DeleteMapping("/{recruitId}") public ResponseEntity deleteRecruitment(@PathVariable @Positive Long recruitId) { recruitmentAdminService.deleteRecruitment(recruitId); diff --git a/gg-recruit-api/src/main/java/gg/recruit/api/admin/controller/response/GetRecruitmentApplicationDto.java b/gg-recruit-api/src/main/java/gg/recruit/api/admin/controller/response/GetRecruitmentApplicationDto.java new file mode 100644 index 000000000..512bb9587 --- /dev/null +++ b/gg-recruit-api/src/main/java/gg/recruit/api/admin/controller/response/GetRecruitmentApplicationDto.java @@ -0,0 +1,121 @@ +package gg.recruit.api.admin.controller.response; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.mapstruct.AfterMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +import gg.data.recruit.application.Application; +import gg.data.recruit.application.ApplicationAnswer; +import gg.data.recruit.application.ApplicationAnswerCheckList; +import gg.data.recruit.application.ApplicationAnswerText; +import gg.data.recruit.application.enums.ApplicationStatus; +import gg.data.recruit.recruitment.CheckList; +import gg.data.recruit.recruitment.Question; +import gg.data.recruit.recruitment.enums.InputType; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class GetRecruitmentApplicationDto { + private Long applicationId; + private String intraId; + private ApplicationStatus status; + private List
forms = new ArrayList<>(); + + public GetRecruitmentApplicationDto(Long applicationId, String intraId, ApplicationStatus status) { + this.applicationId = applicationId; + this.intraId = intraId; + this.status = status; + } + + @NoArgsConstructor(access = AccessLevel.PROTECTED) + @Getter + protected static class Form { + private Long questionId; + private String question; + private InputType inputType; + private String answer; + private List checkedList = new ArrayList<>(); + + public Form(Long questionId, String question, InputType inputType) { + this.questionId = questionId; + this.question = question; + this.inputType = inputType; + } + + @Mapper + public interface MapStruct { + MapStruct INSTANCE = Mappers.getMapper(MapStruct.class); + + @Mapping(source = "id", target = "questionId") + Form entityToDto(Question question); + } + + @NoArgsConstructor(access = AccessLevel.PROTECTED) + @Getter + protected static class CheckListForm { + private Long checkId; + private String content; + + public CheckListForm(Long checkId, String content) { + this.checkId = checkId; + this.content = content; + } + + @Mapper + public interface MapStruct { + MapStruct INSTANCE = Mappers.getMapper(MapStruct.class); + + @Mapping(source = "id", target = "checkId") + CheckListForm entityToDto(CheckList checkList); + } + } + } + + @Mapper + public interface MapStruct { + MapStruct INSTANCE = Mappers.getMapper(MapStruct.class); + + @Mapping(source = "id", target = "applicationId") + @Mapping(source = "user.intraId", target = "intraId") + GetRecruitmentApplicationDto entityToDto(Application application); + + @AfterMapping + default void fillForms(Application application, @MappingTarget GetRecruitmentApplicationDto dto) { + Set questionIdSet = new HashSet<>(); + HashSet formSet = new LinkedHashSet<>(); + HashMap formMap = new HashMap<>(); + for (ApplicationAnswer answer : application.getApplicationAnswers()) { + Long questionId = answer.getQuestionId(); + if (!questionIdSet.contains(questionId)) { + questionIdSet.add(questionId); + Form form = Form.MapStruct.INSTANCE.entityToDto(answer.getQuestion()); + formSet.add(form); + formMap.put(questionId, form); + } + Form form = formMap.get(questionId); + if (answer instanceof ApplicationAnswerCheckList) { + ApplicationAnswerCheckList applicationAnswerCheckList = (ApplicationAnswerCheckList)answer; + CheckList checkList = applicationAnswerCheckList.getCheckList(); + form.checkedList.add(Form.CheckListForm.MapStruct.INSTANCE.entityToDto(checkList)); + } else if (answer instanceof ApplicationAnswerText) { + ApplicationAnswerText applicationAnswerText = (ApplicationAnswerText)answer; + form.answer = applicationAnswerText.getAnswer(); + } + } + dto.forms = formSet.stream().collect(Collectors.toList()); + } + } +} diff --git a/gg-recruit-api/src/main/java/gg/recruit/api/admin/controller/response/GetRecruitmentApplicationResponseDto.java b/gg-recruit-api/src/main/java/gg/recruit/api/admin/controller/response/GetRecruitmentApplicationResponseDto.java new file mode 100644 index 000000000..ed1d7ca20 --- /dev/null +++ b/gg-recruit-api/src/main/java/gg/recruit/api/admin/controller/response/GetRecruitmentApplicationResponseDto.java @@ -0,0 +1,36 @@ +package gg.recruit.api.admin.controller.response; + +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.data.domain.Page; + +import gg.data.recruit.application.Application; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class GetRecruitmentApplicationResponseDto { + private int page; + private boolean isLast; + private List applicationResults; + + public GetRecruitmentApplicationResponseDto(int page, boolean isLast, + List applicationResults) { + this.page = page; + this.isLast = isLast; + this.applicationResults = applicationResults; + } + + public static GetRecruitmentApplicationResponseDto applicationsPageToDto(Page applicationsPage) { + int page = applicationsPage.getPageable().getPageNumber() + 1; + boolean isLast = applicationsPage.isLast(); + List dto = applicationsPage.getContent() + .stream() + .map(GetRecruitmentApplicationDto.MapStruct.INSTANCE::entityToDto) + .collect(Collectors.toList()); + return new GetRecruitmentApplicationResponseDto(page, isLast, dto); + } +} diff --git a/gg-recruit-api/src/main/java/gg/recruit/api/admin/service/RecruitmentAdminService.java b/gg-recruit-api/src/main/java/gg/recruit/api/admin/service/RecruitmentAdminService.java index ae6045063..78730d54e 100644 --- a/gg-recruit-api/src/main/java/gg/recruit/api/admin/service/RecruitmentAdminService.java +++ b/gg-recruit-api/src/main/java/gg/recruit/api/admin/service/RecruitmentAdminService.java @@ -3,6 +3,7 @@ import java.time.LocalDateTime; import java.util.List; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; @@ -19,6 +20,7 @@ import gg.data.recruit.recruitment.Recruitment; import gg.data.recruit.recruitment.enums.InputType; import gg.recruit.api.admin.service.dto.Form; +import gg.recruit.api.admin.service.dto.GetRecruitmentApplicationsDto; import gg.recruit.api.admin.service.dto.UpdateApplicationStatusDto; import gg.recruit.api.admin.service.dto.UpdateRecruitStatusParam; import gg.utils.exception.ErrorCode; @@ -181,4 +183,21 @@ public List getRecruitmentApplicants(Long recruitId) { return applicationAdminRepository .findAllByRecruitmentIdWithUserAndRecruitStatusFetchJoinOrderByIdDesc(recruitId); } + + public Page findApplicationsWithAnswersAndUserWithFilter(GetRecruitmentApplicationsDto dto) { + Long recruitId = dto.getRecruitId(); + Long questionId = dto.getQuestionId(); + String search = dto.getSearch(); + Pageable pageable = dto.getPageable(); + List checkListIds = dto.getCheckListIds(); + + if (questionId != null && !checkListIds.isEmpty() && search == null) { + return applicationAdminRepository.findAllByCheckList(recruitId, questionId, checkListIds, pageable); + } else if (questionId != null && search != null && checkListIds.isEmpty()) { + return applicationAdminRepository.findAllByContainSearch(recruitId, questionId, search, pageable); + } else { + return applicationAdminRepository.findByRecruitIdAndIsDeletedFalseOrderByIdDesc(recruitId, pageable); + } + } + } diff --git a/gg-recruit-api/src/main/java/gg/recruit/api/admin/service/dto/GetRecruitmentApplicationsDto.java b/gg-recruit-api/src/main/java/gg/recruit/api/admin/service/dto/GetRecruitmentApplicationsDto.java new file mode 100644 index 000000000..1a70d9f0d --- /dev/null +++ b/gg-recruit-api/src/main/java/gg/recruit/api/admin/service/dto/GetRecruitmentApplicationsDto.java @@ -0,0 +1,27 @@ +package gg.recruit.api.admin.service.dto; + +import java.util.List; + +import org.springframework.data.domain.Pageable; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@Getter +public class GetRecruitmentApplicationsDto { + private Long recruitId; + private Long questionId; + private List checkListIds; + private String search; + private Pageable pageable; + + public GetRecruitmentApplicationsDto(Long recruitId, Long questionId, List checkListIds, String search, + Pageable pageable) { + this.recruitId = recruitId; + this.questionId = questionId; + this.checkListIds = checkListIds; + this.search = search; + this.pageable = pageable; + } +} diff --git a/gg-recruit-api/src/test/java/gg/recruit/api/admin/controller/RecruitmentAdminControllerUnitTest.java b/gg-recruit-api/src/test/java/gg/recruit/api/admin/controller/RecruitmentAdminControllerUnitTest.java index 70f127191..5dc6ef0c4 100644 --- a/gg-recruit-api/src/test/java/gg/recruit/api/admin/controller/RecruitmentAdminControllerUnitTest.java +++ b/gg-recruit-api/src/test/java/gg/recruit/api/admin/controller/RecruitmentAdminControllerUnitTest.java @@ -9,6 +9,7 @@ import javax.validation.ConstraintViolationException; import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -18,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.PageRequest; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ActiveProfiles; @@ -33,6 +35,7 @@ import gg.recruit.api.admin.controller.response.RecruitmentApplicantResultsResponseDto; import gg.recruit.api.admin.service.RecruitmentAdminService; import gg.utils.annotation.UnitTest; +import gg.utils.exception.custom.BusinessException; @UnitTest @WebMvcTest(controllers = RecruitmentAdminController.class) @@ -150,5 +153,34 @@ void successNotEmpty() { Assertions.assertThat(result.getInterviewDate()).isEqualTo(interviewDate); } + + @Nested + @DisplayName("getRecruitmentApplications") + class GetRecruitmentApplications { + @Test + @DisplayName("long으로 파싱할 수 없는 유효하지 않은 checks의 경우 exceptions 발생") + void invalidChecks() { + //Arrange + //Act + //Assert + Assertions.assertThatThrownBy( + () -> recruitmentAdminController.getRecruitmentApplications(1L, null, "d,d,d", null, + PageRequest.of(1, 10))) + .isInstanceOf(BusinessException.class); + + } + + @Test + @DisplayName("성공") + @Disabled + void success() { + //Arrange + //Act + recruitmentAdminController.getRecruitmentApplications(1L, null, "1,2,3", null, + PageRequest.of(1, 10)); + + //Assert + } + } } diff --git a/gg-recruit-api/src/test/java/gg/recruit/api/admin/controller/response/GetRecruitmentApplicationDtoTest.java b/gg-recruit-api/src/test/java/gg/recruit/api/admin/controller/response/GetRecruitmentApplicationDtoTest.java new file mode 100644 index 000000000..a4245013e --- /dev/null +++ b/gg-recruit-api/src/test/java/gg/recruit/api/admin/controller/response/GetRecruitmentApplicationDtoTest.java @@ -0,0 +1,102 @@ +package gg.recruit.api.admin.controller.response; + +import javax.persistence.EntityManager; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Transactional; + +import gg.admin.repo.recruit.ApplicationAdminRepository; +import gg.data.recruit.application.Application; +import gg.data.recruit.application.enums.ApplicationStatus; +import gg.data.recruit.recruitment.CheckList; +import gg.data.recruit.recruitment.Question; +import gg.data.recruit.recruitment.Recruitment; +import gg.data.recruit.recruitment.enums.InputType; +import gg.data.user.User; +import gg.utils.TestDataUtils; +import gg.utils.annotation.IntegrationTest; +import gg.utils.annotation.UnitTest; + +@IntegrationTest +@UnitTest +@Transactional +class GetRecruitmentApplicationDtoTest { + @Autowired + EntityManager entityManager; + + @Autowired + ApplicationAdminRepository applicationAdminRepository; + + @Autowired + TestDataUtils testDataUtils; + + @Nested + @DisplayName("MapperTest") + class MapperTest { + + @Test + @DisplayName("mapper에 조건에 맞게 모든 요소가 들어가 있어야 한다") + void success() { + String search = "hello world"; + + Recruitment recruitment = testDataUtils.createNewRecruitment(); + User user = testDataUtils.createNewUser(); + + Application application = testDataUtils.createApplication(user, recruitment); + Question question = testDataUtils.createNewQuestion(recruitment, InputType.MULTI_CHECK, "multi", 2); + CheckList checkList = testDataUtils.createNewCheckList(question, "dd"); + CheckList checkList2 = testDataUtils.createNewCheckList(question, "dd"); + testDataUtils.createNewApplicationAnswerCheckList(application, question, checkList); + testDataUtils.createNewApplicationAnswerCheckList(application, question, checkList2); + + Question question2 = testDataUtils.createNewQuestion(recruitment, InputType.SINGLE_CHECK, "single", 2); + testDataUtils.createNewApplicationAnswerCheckList(application, question2, checkList); + + Question question3 = testDataUtils.createNewQuestion(recruitment, InputType.TEXT, "text", 2); + testDataUtils.createNewApplicationAnswerText(application, question3, search); + + Long recruitmentId = recruitment.getId(); + Long applicationId = application.getId(); + String intraId = user.getIntraId(); + ApplicationStatus status = application.getStatus(); + + entityManager.flush(); + entityManager.clear(); + + //Arrange + Pageable pageable = PageRequest.of(0, 10); + Page all = applicationAdminRepository.findByRecruitIdAndIsDeletedFalseOrderByIdDesc( + recruitmentId, pageable); + Application applicationResult = all.getContent().get(0); + GetRecruitmentApplicationDto dto = GetRecruitmentApplicationDto.MapStruct.INSTANCE.entityToDto( + applicationResult); + + // Assert + Assertions.assertThat(dto.getApplicationId()).isEqualTo(applicationId); + Assertions.assertThat(dto.getIntraId()).isEqualTo(intraId); + Assertions.assertThat(dto.getStatus()).isEqualTo(status); + Assertions.assertThat(dto.getForms().size()).isEqualTo(3); + for (GetRecruitmentApplicationDto.Form form : dto.getForms()) { + if (form.getInputType().equals(InputType.TEXT)) { + Assertions.assertThat(form.getCheckedList().size()).isEqualTo(0); + Assertions.assertThat(form.getAnswer()).isEqualTo(search); + } + if (form.getInputType().equals(InputType.SINGLE_CHECK)) { + Assertions.assertThat(form.getCheckedList().size()).isEqualTo(1); + Assertions.assertThat(form.getAnswer()).isNull(); + } + if (form.getInputType().equals(InputType.MULTI_CHECK)) { + Assertions.assertThat(form.getCheckedList().size()).isEqualTo(2); + Assertions.assertThat(form.getAnswer()).isNull(); + } + } + } + } +} diff --git a/gg-recruit-api/src/test/java/gg/recruit/api/admin/service/RecruitmentAdminServiceTest.java b/gg-recruit-api/src/test/java/gg/recruit/api/admin/service/RecruitmentAdminServiceTest.java index f2d072ee5..65e95aa81 100644 --- a/gg-recruit-api/src/test/java/gg/recruit/api/admin/service/RecruitmentAdminServiceTest.java +++ b/gg-recruit-api/src/test/java/gg/recruit/api/admin/service/RecruitmentAdminServiceTest.java @@ -2,6 +2,7 @@ import static org.mockito.ArgumentMatchers.*; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -14,11 +15,15 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import gg.admin.repo.recruit.ApplicationAdminRepository; import gg.admin.repo.recruit.RecruitmentAdminRepository; import gg.data.recruit.application.Application; import gg.data.recruit.application.enums.ApplicationStatus; +import gg.recruit.api.admin.service.dto.GetRecruitmentApplicationsDto; import gg.recruit.api.admin.service.dto.UpdateApplicationStatusDto; import gg.utils.annotation.UnitTest; import gg.utils.exception.custom.NotExistException; @@ -75,4 +80,22 @@ void validApplicationStatus() { } } } + + @Nested + @DisplayName("getRecruitmentApplicationsFetchApplicationAnswersWithFilter") + class GetRecruitmentApplicationsFetchApplicationAnswersWithFilter { + @Test + @DisplayName("조회 성공") + void success() { + //Arrange + Pageable pageable = PageRequest.of(1, 10, Sort.by(new Sort.Order(Sort.Direction.DESC, "id"))); + GetRecruitmentApplicationsDto dto = new GetRecruitmentApplicationsDto(1L, null, new ArrayList<>(), null, + pageable); + recruitmentAdminService.findApplicationsWithAnswersAndUserWithFilter(dto); + + //Act + + //Assert + } + } } diff --git a/gg-utils/src/testFixtures/java/gg/utils/TestDataUtils.java b/gg-utils/src/testFixtures/java/gg/utils/TestDataUtils.java index 98acc7619..a6d35d665 100644 --- a/gg-utils/src/testFixtures/java/gg/utils/TestDataUtils.java +++ b/gg-utils/src/testFixtures/java/gg/utils/TestDataUtils.java @@ -9,6 +9,8 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import javax.persistence.EntityManager; + import org.springframework.stereotype.Component; import gg.auth.utils.AuthTokenProvider; @@ -44,8 +46,13 @@ import gg.data.pingpong.tournament.type.TournamentStatus; import gg.data.pingpong.tournament.type.TournamentType; import gg.data.recruit.application.Application; +import gg.data.recruit.application.ApplicationAnswerCheckList; +import gg.data.recruit.application.ApplicationAnswerText; +import gg.data.recruit.recruitment.CheckList; +import gg.data.recruit.recruitment.Question; import gg.data.recruit.application.RecruitStatus; import gg.data.recruit.recruitment.Recruitment; +import gg.data.recruit.recruitment.enums.InputType; import gg.data.user.User; import gg.data.user.UserImage; import gg.data.user.type.RacketType; @@ -121,6 +128,7 @@ public class TestDataUtils { private final RecruitmentRepository recruitmentRepository; private final ApplicationRepository applicationRepository; private final RecruitStatusRepository recruitStatusRepository; + private final EntityManager entityManager; public String getLoginAccessToken() { User user = User.builder() @@ -935,4 +943,31 @@ public RecruitStatus createRecruitStatus(Application application, LocalDateTime recruitStatusRepository.save(recruitStatus); return recruitStatus; } + + public Question createNewQuestion(Recruitment recruitment, InputType inputType, String quest, int sortNum) { + Question question = new Question(recruitment, inputType, quest, sortNum); + entityManager.persist(question); + return question; + } + + public CheckList createNewCheckList(Question question, String content) { + CheckList checkList = new CheckList(question, content); + entityManager.persist(checkList); + return checkList; + } + + public ApplicationAnswerCheckList createNewApplicationAnswerCheckList(Application application, Question question, + CheckList checkList) { + ApplicationAnswerCheckList applicationAnswerCheckList = new ApplicationAnswerCheckList(application, + question, checkList); + entityManager.persist(applicationAnswerCheckList); + return applicationAnswerCheckList; + } + + public ApplicationAnswerText createNewApplicationAnswerText(Application application, Question question, + String search) { + ApplicationAnswerText answerText = new ApplicationAnswerText(application, question, search); + entityManager.persist(answerText); + return answerText; + } }