Skip to content

Commit

Permalink
feat-be: 프로세스 목록 조회 API 필터링 및 정렬 구현 (#756)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: cutehumanS2 <[email protected]>
  • Loading branch information
2 people authored and Chocochip101 committed Oct 5, 2024
1 parent 2e05f2b commit 7434b61
Show file tree
Hide file tree
Showing 16 changed files with 520 additions and 26 deletions.
2 changes: 1 addition & 1 deletion backend/src/docs/asciidoc/process.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

==== 성공

operation::process/read[snippets="http-request,request-cookies,query-parameters,http-response,response-fields"]
operation::process/read-filter-and-order[snippets="http-request,request-cookies,query-parameters,http-response,response-fields"]

==== 실패: 존재하지 않는 대시보드

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.cruru.applicant.domain;

import com.cruru.applicant.domain.dto.ApplicantCard;
import com.cruru.applicant.exception.badrequest.ApplicantSortException;
import java.util.Arrays;
import java.util.Comparator;

public enum ApplicantSortOption {

ASC(Comparator.naturalOrder()),
DESC(Comparator.reverseOrder());

private final Comparator<Comparable> comparator;

ApplicantSortOption(Comparator<Comparable> comparator) {
this.comparator = comparator;
}

public static Comparator<ApplicantCard> getCombinedComparator(String sortByCreatedAt, String sortByScore) {
ApplicantSortOption createdAtOption = convertToSortOption(sortByCreatedAt);
ApplicantSortOption scoreOption = convertToSortOption(sortByScore);

return createdAtOption.getCreatedAtComparator()
.thenComparing(scoreOption.getScoreComparator());
}

private static ApplicantSortOption convertToSortOption(String sortOption) {
return Arrays.stream(ApplicantSortOption.values())
.filter(option -> option.name().equalsIgnoreCase(sortOption))
.findAny()
.orElseThrow(ApplicantSortException::new);
}

private Comparator<ApplicantCard> getCreatedAtComparator() {
return Comparator.comparing(ApplicantCard::createdAt, comparator);
}

private Comparator<ApplicantCard> getScoreComparator() {
return Comparator.comparing(ApplicantCard::averageScore, comparator);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.cruru.applicant.domain;

import com.cruru.applicant.domain.dto.ApplicantCard;
import com.cruru.applicant.exception.badrequest.EvaluationStatusException;
import java.util.Arrays;
import java.util.function.Predicate;

public enum EvaluationStatus {

ALL(card -> true),
NOT_EVALUATED(ApplicantCard::hasEvaluation),
EVALUATED(ApplicantCard::hasNoEvaluation);

private final Predicate<ApplicantCard> predicate;

EvaluationStatus(Predicate<ApplicantCard> predicate) {
this.predicate = predicate;
}

public static boolean matches(ApplicantCard card, String evaluationStatus) {
return Arrays.stream(EvaluationStatus.values())
.filter(status -> status.name().equalsIgnoreCase(evaluationStatus))
.findAny()
.orElseThrow(EvaluationStatusException::new)
.predicate
.test(card);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,12 @@ public record ApplicantCard(
public ApplicantCardResponse toResponse() {
return new ApplicantCardResponse(id, name, createdAt, isRejected, (int) evaluationCount, averageScore);
}

public boolean hasEvaluation() {
return evaluationCount == 0;
}

public boolean hasNoEvaluation() {
return evaluationCount > 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.cruru.applicant.exception.badrequest;

import com.cruru.advice.badrequest.BadRequestException;

public class ApplicantSortException extends BadRequestException {

private static final String MESSAGE = "지원하는 정렬 조건이 아닙니다.";

public ApplicantSortException() {
super(MESSAGE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.cruru.applicant.exception.badrequest;

import com.cruru.advice.badrequest.BadRequestException;

public class EvaluationStatusException extends BadRequestException {

private static final String MESSAGE = "지원하는 평가 상태가 아닙니다.";

public EvaluationStatusException() {
super(MESSAGE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import com.cruru.applicant.controller.request.ApplicantUpdateRequest;
import com.cruru.applicant.controller.response.ApplicantResponse;
import com.cruru.applicant.domain.Applicant;
import com.cruru.applicant.domain.ApplicantSortOption;
import com.cruru.applicant.domain.EvaluationStatus;
import com.cruru.applicant.domain.dto.ApplicantCard;
import com.cruru.applicant.domain.repository.ApplicantRepository;
import com.cruru.applicant.exception.ApplicantNotFoundException;
Expand Down Expand Up @@ -113,8 +115,26 @@ public ApplicantResponse toApplicantResponse(Applicant applicant) {
);
}

public List<ApplicantCard> findApplicantCards(List<Process> processes) {
return applicantRepository.findApplicantCardsByProcesses(processes);
public List<ApplicantCard> findApplicantCards(
List<Process> processes,
Double minScore,
Double maxScore,
String evaluationStatus,
String sortByCreatedAt,
String sortByScore
) {
List<ApplicantCard> applicantCards = applicantRepository.findApplicantCardsByProcesses(processes);

return applicantCards.stream()
.filter(card -> filterByScore(card, minScore, maxScore))
.filter(card -> EvaluationStatus.matches(card, evaluationStatus))
.sorted(ApplicantSortOption.getCombinedComparator(sortByCreatedAt, sortByScore))
.toList();
}

private boolean filterByScore(ApplicantCard card, Double minScore, Double maxScore) {
double avgScore = card.averageScore();
return minScore <= avgScore && avgScore <= maxScore;
}

public List<ApplicantCard> findApplicantCards(Process process) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,17 @@ public class ProcessController {
@RequireAuthCheck(targetId = "dashboardId", targetDomain = Dashboard.class)
@GetMapping
public ResponseEntity<ProcessResponses> read(
LoginProfile loginProfile, @RequestParam(name = "dashboardId") Long dashboardId
LoginProfile loginProfile,
@RequestParam(name = "dashboardId") Long dashboardId,
@RequestParam(name = "minScore", required = false, defaultValue = "0.00") Double minScore,
@RequestParam(name = "maxScore", required = false, defaultValue = "5.00") Double maxScore,
@RequestParam(name = "evaluationStatus", required = false, defaultValue = "ALL") String evaluationStatus,
@RequestParam(name = "sortByCreatedAt", required = false, defaultValue = "DESC") String sortByCreatedAt,
@RequestParam(name = "sortByScore", required = false, defaultValue = "DESC") String sortByScore
) {
ProcessResponses processes = processFacade.readAllByDashboardId(dashboardId);
ProcessResponses processes = processFacade.readAllByDashboardId(
dashboardId, minScore, maxScore, evaluationStatus, sortByCreatedAt, sortByScore
);
return ResponseEntity.ok().body(processes);
}

Expand Down
13 changes: 11 additions & 2 deletions backend/src/main/java/com/cruru/process/facade/ProcessFacade.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,19 @@ public void create(ProcessCreateRequest request, long dashboardId) {
processService.create(request, dashboard);
}

public ProcessResponses readAllByDashboardId(long dashboardId) {
public ProcessResponses readAllByDashboardId(
long dashboardId,
Double minScore,
Double maxScore,
String evaluationStatus,
String sortByCreatedAt,
String sortByScore
) {
ApplyForm applyForm = applyFormService.findByDashboardId(dashboardId);
List<Process> processes = processService.findAllByDashboard(dashboardId);
List<ApplicantCard> applicantCards = applicantService.findApplicantCards(processes);

List<ApplicantCard> applicantCards = applicantService.findApplicantCards(
processes, minScore, maxScore, evaluationStatus, sortByCreatedAt, sortByScore);

List<ProcessResponse> processResponses = processes.stream()
.map(process -> toProcessResponse(process, applicantCards))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package com.cruru.applicant.domain;

import static org.assertj.core.api.Assertions.assertThat;

import com.cruru.applicant.domain.dto.ApplicantCard;
import com.cruru.util.fixture.ApplicantCardFixture;
import com.cruru.util.fixture.DefaultFilterAndOrderFixture;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;


@DisplayName("지원자 정렬 테스트")
class ApplicantSortOptionTest {

@Nested
@DisplayName("지원자의 지원 날짜 정렬 테스트")
class getCreatedAtComparatorTest {

@DisplayName("지원자의 지원 날짜 기준으로 오름차순으로 정렬한다.")
@ParameterizedTest
@ValueSource(strings = {"ASC", "asc"})
void getCreatedAtComparator_asc(String sortByCreatedAt) {
// given
LocalDateTime createdAt1 = LocalDateTime.of(2024, 10, 2, 20, 0);
LocalDateTime createdAt2 = LocalDateTime.of(2024, 10, 3, 20, 0);
ApplicantCard applicantCard1 = ApplicantCardFixture.evaluatedApplicantCard(createdAt1);
ApplicantCard applicantCard2 = ApplicantCardFixture.evaluatedApplicantCard(createdAt2);
List<ApplicantCard> applicantCards = Arrays.asList(applicantCard2, applicantCard1);

// when
applicantCards.sort(ApplicantSortOption.getCombinedComparator(
sortByCreatedAt,
DefaultFilterAndOrderFixture.DEFAULT_SORT_BY_SCORE
));

// then
assertThat(applicantCards).containsExactly(applicantCard1, applicantCard2);
}

@DisplayName("지원자의 지원 날짜 기준으로 내림차순으로 정렬한다.")
@ParameterizedTest
@ValueSource(strings = {"DESC", "desc"})
void getCreatedAtComparator_desc(String sortByCreatedAt) {
// given
LocalDateTime createdAt1 = LocalDateTime.of(2024, 10, 2, 20, 0);
LocalDateTime createdAt2 = LocalDateTime.of(2024, 10, 3, 20, 0);
ApplicantCard applicantCard1 = ApplicantCardFixture.evaluatedApplicantCard(createdAt1);
ApplicantCard applicantCard2 = ApplicantCardFixture.evaluatedApplicantCard(createdAt2);
List<ApplicantCard> applicantCards = Arrays.asList(applicantCard1, applicantCard2);

// when
applicantCards.sort(ApplicantSortOption.getCombinedComparator(
sortByCreatedAt,
DefaultFilterAndOrderFixture.DEFAULT_SORT_BY_SCORE

));

// then
assertThat(applicantCards).containsExactly(applicantCard2, applicantCard1);
}
}

@Nested
@DisplayName("지원자의 평균 점수 정렬 테스트")
class getScoreComparatorTest {

@DisplayName("지원자의 평균 점수 기준으로 오름차순으로 정렬한다.")
@ParameterizedTest
@ValueSource(strings = {"ASC", "asc"})
void getScoreComparator_asc(String sortByCreatedAt) {
// given
LocalDateTime createdAt = LocalDateTime.of(2024, 10, 2, 20, 0);
ApplicantCard applicantCard1 = ApplicantCardFixture.evaluatedApplicantCard(createdAt);
ApplicantCard applicantCard2 = ApplicantCardFixture.evaluatedApplicantCard(createdAt);
List<ApplicantCard> applicantCards = Arrays.asList(applicantCard1, applicantCard2);

// when
applicantCards.sort(ApplicantSortOption.getCombinedComparator(
DefaultFilterAndOrderFixture.DEFAULT_SORT_BY_CREATED_AT,
sortByCreatedAt
));

// then
assertThat(applicantCards).containsExactly(applicantCard2, applicantCard1);
}

@DisplayName("지원자의 평균 점수 기준으로 내림차순으로 정렬한다.")
@ParameterizedTest
@ValueSource(strings = {"DESC", "desc"})
void getScoreComparator_desc(String sortByCreatedAt) {
// given
LocalDateTime createdAt = LocalDateTime.of(2024, 10, 2, 20, 0);
ApplicantCard applicantCard1 = ApplicantCardFixture.evaluatedApplicantCard(createdAt);
ApplicantCard applicantCard2 = ApplicantCardFixture.evaluatedApplicantCard(createdAt);
List<ApplicantCard> applicantCards = Arrays.asList(applicantCard2, applicantCard1);

// when
applicantCards.sort(ApplicantSortOption.getCombinedComparator(
DefaultFilterAndOrderFixture.DEFAULT_SORT_BY_CREATED_AT,
sortByCreatedAt
));

// then
assertThat(applicantCards).containsExactly(applicantCard1, applicantCard2);
}
}
}
Loading

0 comments on commit 7434b61

Please sign in to comment.