Skip to content

Commit

Permalink
✨ [Feature] 지원자 목록 확인 API (#804)
Browse files Browse the repository at this point in the history
  • Loading branch information
middlefitting authored Apr 5, 2024
1 parent bb9344a commit 66fb0cf
Show file tree
Hide file tree
Showing 15 changed files with 629 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -12,6 +15,50 @@
public interface ApplicationAdminRepository extends JpaRepository<Application, Long> {
Optional<Application> findByIdAndRecruitId(Long applicationId, Long recruitId);

/**
* id 조건에 일치하는 지원서 목록 반환
* @param recruitId
* @param pageable
* @return
*/
@EntityGraph(attributePaths = {"user", "applicationAnswers", "applicationAnswers.question",
"applicationAnswers.question.checkLists"})
Page<Application> 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<Application> findAllByCheckList(
@Param("recruitId") Long recruitId,
@Param("questionId") Long questionId,
@Param("checkListIds") List<Long> 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<Application> 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 "
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<Application> result = applicationAdminRepository.findByRecruitIdAndIsDeletedFalseOrderByIdDesc(
recruitmentId,
pageable);

// Assert
Assertions.assertThat(result.getContent().size()).isEqualTo(3);
}
}

@Nested
@DisplayName("FindAllByCheckList")
class FindAllByCheckList {

@Test
@DisplayName("여러개의 조건 중 하나만 만족해도 조회가 성공")
void success() {
//Arrange
List<Long> checkListTargetId = new ArrayList<>();
checkListTargetId.add(checkListId1);
checkListTargetId.add(checkListId2);
Pageable pageable = PageRequest.of(0, 10);

// Act
Page<Application> 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<Application> result = applicationAdminRepository.findAllByContainSearch(recruitmentId,
questionId, search, pageable);

// Assert
Assertions.assertThat(result.getContent().size()).isEqualTo(3);
}
}

}

}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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<ApplicationAnswer> applicationAnswers = new HashSet<>();

public Application(User user, Recruitment recruit) {
this.user = user;
this.recruit = recruit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -83,6 +91,40 @@ public ResponseEntity<Void> setFinalApplicationStatusResult(
return new ResponseEntity<>(HttpStatus.CREATED);
}

@GetMapping("/{recruitId}/applications")
public ResponseEntity<GetRecruitmentApplicationResponseDto> 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<Long> checkListIds = parseChecks(checks);
dto = new GetRecruitmentApplicationsDto(recruitId, questionId, checkListIds, search, parsedPage);
Page<Application> applicationsPage = recruitmentAdminService.findApplicationsWithAnswersAndUserWithFilter(dto);
return ResponseEntity.ok(GetRecruitmentApplicationResponseDto.applicationsPageToDto(applicationsPage));
}

/**
* 전달된 checkListId 목록 파싱
* @param checks
* @return List<Long>
*/
private List<Long> 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<Void> deleteRecruitment(@PathVariable @Positive Long recruitId) {
recruitmentAdminService.deleteRecruitment(recruitId);
Expand Down
Loading

0 comments on commit 66fb0cf

Please sign in to comment.