Skip to content

Commit

Permalink
[REFACTOR] 지원서 조회하기와 관련된 API에 대한 리팩토링 (#208)
Browse files Browse the repository at this point in the history
* refactor: 지원서 모아보기 API

* refactor: 칸반 전체 목록 조회 API

* refactor: 칸반 전형별 목록 조회 API

* refactor: 캘린더 지원서 월별 조회 API

* refactor: 캘린더 지원서 일별 목록 조회 API

* refactor: 전형일이 작성되어 있지 않은 지원서 목록 조회 API
  • Loading branch information
shindong96 authored May 28, 2024
1 parent f375b24 commit 663c555
Show file tree
Hide file tree
Showing 7 changed files with 442 additions and 372 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,31 @@ void findAllByUserId_application() {
.body("success", equalTo(true));
}

@DisplayName("지원서 모아보기 기능을 종료 여부 필터링에 해당되는 경우 테스트한다.")
@Test
void findAllByUserId_application_filter_completed() {
// given
int page = 1;
int size = 10;
String sort = "processType";
String accessToken = signUp("[email protected]", Provider.GOOGLE);
assignDesiredPositions(accessToken);
SimpleApplicationRequest simpleApplicationRequest = new SimpleApplicationRequest("카카오", "개발자", null,
new SimpleApplicationProcessRequest("TO_APPLY", "서류전형", null));
post(accessToken, "/v1/applications/simple", simpleApplicationRequest);

String uri = String.format(
"%s?page=%d&size=%d&sort=%s&completed=%s", "/v1/applications", page, size, sort, "false");

// when
ValidatableResponse response = get(accessToken, uri);

// then
response.statusCode(HttpStatus.OK.value())
.body("success", equalTo(true))
.body("data.content.size()", is(1));
}

@DisplayName("지원서를 작성하는 기능을 테스트한다")
@Test
void create_application() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,21 @@
@RequiredArgsConstructor
public class MyApplicationResponse {

private final long applicationId;
private final String companyName;
private final String position;
private final String specificPosition;
private final ProcessResponse process;
private final long applicationId;
private final String companyName;
private final String position;
private final String specificPosition;
private final Boolean isCompleted;
private final ProcessResponse process;

public static MyApplicationResponse of(Application application, Process process) {
return new MyApplicationResponse(
application.getId(),
application.getCompanyName(),
application.getPosition(),
application.getSpecificPosition(),
ProcessResponse.from(process)
);
}
public static MyApplicationResponse of(Application application, Process process) {
return new MyApplicationResponse(
application.getId(),
application.getCompanyName(),
application.getPosition(),
application.getSpecificPosition(),
application.isCompleted(),
ProcessResponse.from(process)
);
}
}
4 changes: 4 additions & 0 deletions core-domain/src/main/java/gohigher/common/ProcessType.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public static ProcessType from(String value) {
}

public static List<ProcessType> from(List<String> values) {
if (values == null) {
return null;
}

return values.stream()
.map(ProcessType::from)
.toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,37 +27,42 @@ public interface ApplicationRepository extends JpaRepository<ApplicationJpaEntit
+ "WHERE a.userId = :userId "
+ "AND a.deleted = false "
+ "AND FUNCTION('YEAR', p.schedule) = :year "
+ "AND FUNCTION('MONTH', p.schedule) = :month")
+ "AND FUNCTION('MONTH', p.schedule) = :month "
+ "AND a.isCompleted = false")
List<ApplicationJpaEntity> findByUserIdAndMonth(Long userId, int year, int month);

@Query("SELECT a FROM ApplicationJpaEntity a "
+ "JOIN FETCH a.processes p "
+ "WHERE a.userId = :userId "
+ "AND a.deleted = false "
+ "AND p.schedule >= :startOfDate "
+ "AND p.schedule < :endOfDate")
+ "AND p.schedule < :endOfDate "
+ "AND a.isCompleted = false")
List<ApplicationJpaEntity> findByUserIdAndDate(Long userId, LocalDateTime startOfDate, LocalDateTime endOfDate);

@Query("SELECT a FROM ApplicationJpaEntity a "
+ "JOIN FETCH a.processes p "
+ "WHERE a.userId = :userId "
+ "AND p.isCurrent = true "
+ "AND p.schedule = null "
+ "AND a.deleted = false")
+ "AND a.deleted = false "
+ "AND a.isCompleted = false")
Slice<ApplicationJpaEntity> findUnscheduledByUserId(Long userId, Pageable pageable);

@Query("SELECT a FROM ApplicationJpaEntity a "
+ "JOIN FETCH a.processes p "
+ "WHERE a.userId = :userId "
+ "AND p.isCurrent = true "
+ "AND a.deleted = false")
+ "AND a.deleted = false "
+ "AND a.isCompleted = false")
List<ApplicationJpaEntity> findOnlyWithCurrentProcessByUserId(Long userId);

@Query("SELECT a FROM ApplicationJpaEntity a "
+ "JOIN FETCH a.processes p "
+ "WHERE a.userId = :userId "
+ "AND p.isCurrent = true "
+ "AND p.type = :processType "
+ "AND a.deleted = false")
+ "AND a.deleted = false "
+ "AND a.isCompleted = false")
List<ApplicationJpaEntity> findOnlyCurrentProcessByUserIdAndProcessType(Long userId, ProcessType processType);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package gohigher.application.entity;

import static gohigher.application.entity.QApplicationJpaEntity.applicationJpaEntity;
import static gohigher.application.entity.QApplicationProcessJpaEntity.applicationProcessJpaEntity;
import static gohigher.application.entity.QApplicationJpaEntity.*;
import static gohigher.application.entity.QApplicationProcessJpaEntity.*;

import java.util.List;
import java.util.stream.Collectors;
Expand All @@ -24,96 +24,106 @@
@RequiredArgsConstructor
public class ApplicationRepositoryCustomImpl implements ApplicationRepositoryCustom {

private final JPAQueryFactory jpaQueryFactory;

@Override
public Slice<ApplicationJpaEntity> findAllByUserId(Long userId, Pageable pageable, ApplicationSortingType sortingType,
List<ProcessType> process, List<Boolean> completed, String companyName) {

JPAQuery<ProcessWithApplicationResponse> query = jpaQueryFactory
.select(Projections.bean(
ProcessWithApplicationResponse.class,
applicationJpaEntity.id.as("applicationId"),
applicationJpaEntity.companyName,
applicationJpaEntity.position,
applicationJpaEntity.specificPosition,
applicationProcessJpaEntity.id.as("processId"),
applicationProcessJpaEntity.type,
applicationProcessJpaEntity.description,
applicationProcessJpaEntity.schedule
))
.from(applicationProcessJpaEntity)
.join(applicationProcessJpaEntity.application, applicationJpaEntity)
.where(
eqUserId(userId),
applicationProcessJpaEntity.application.deleted.eq(false),
applicationProcessJpaEntity.isCurrent.eq(true),
inProcessType(process),
containsCompanyName(companyName)
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize() + 1)
.orderBy(selectOrderSpecifierAboutFindAll(sortingType))
;

List<ApplicationJpaEntity> applications = convertToApplicationJpaEntity(query);

boolean hasNext = applications.size() > pageable.getPageSize();
if (hasNext) {
applications.remove(applications.size() - 1);
}

return new SliceImpl<>(applications, pageable, hasNext);
}

private BooleanExpression containsCompanyName(String companyName) {
if (companyName == null) {
companyName = "";
}
return applicationProcessJpaEntity.application.companyName.contains(companyName);
}

private BooleanExpression eqUserId(Long userId) {
if (userId == null) {
return applicationProcessJpaEntity.application.userId.isNull();
}
return applicationProcessJpaEntity.application.userId.eq(userId);
}

private BooleanExpression inProcessType(List<ProcessType> process) {
if (process.isEmpty()) {
return null;
}
return applicationProcessJpaEntity.type.in(process);
}

private OrderSpecifier<?> selectOrderSpecifierAboutFindAll(ApplicationSortingType sortingType) {
return switch (sortingType) {
case CREATED -> applicationProcessJpaEntity.id.desc();
case PROCESS_TYPE -> applicationProcessJpaEntity.schedule.asc();
case REVERSE_PROCESS_TYPE -> applicationProcessJpaEntity.schedule.desc();
case CLOSING -> applicationProcessJpaEntity.id.asc();
};
}

private List<ApplicationJpaEntity> convertToApplicationJpaEntity(JPAQuery<ProcessWithApplicationResponse> query) {
return query.fetch().stream()
.map(processResponse ->
ApplicationJpaEntity.builder()
.id(processResponse.getApplicationId())
.companyName(processResponse.getCompanyName())
.position(processResponse.getPosition())
.specificPosition(processResponse.getSpecificPosition())
.processes(
List.of(
ApplicationProcessJpaEntity.builder()
.id(processResponse.getProcessId())
.type(processResponse.getType())
.description(processResponse.getDescription())
.schedule(processResponse.getSchedule())
.build())
)
.build())
.collect(Collectors.toList());
}
private final JPAQueryFactory jpaQueryFactory;

@Override
public Slice<ApplicationJpaEntity> findAllByUserId(Long userId, Pageable pageable,
ApplicationSortingType sortingType,
List<ProcessType> process, List<Boolean> completed, String companyName) {

JPAQuery<ProcessWithApplicationResponse> query = jpaQueryFactory
.select(Projections.bean(
ProcessWithApplicationResponse.class,
applicationJpaEntity.id.as("applicationId"),
applicationJpaEntity.companyName,
applicationJpaEntity.position,
applicationJpaEntity.specificPosition,
applicationProcessJpaEntity.id.as("processId"),
applicationProcessJpaEntity.type,
applicationProcessJpaEntity.description,
applicationProcessJpaEntity.schedule,
applicationJpaEntity.isCompleted
))
.from(applicationProcessJpaEntity)
.join(applicationProcessJpaEntity.application, applicationJpaEntity)
.where(
eqUserId(userId),
applicationProcessJpaEntity.application.deleted.eq(false),
applicationProcessJpaEntity.isCurrent.eq(true),
inProcessType(process),
inCompleted(completed),
containsCompanyName(companyName)
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize() + 1)
.orderBy(selectOrderSpecifierAboutFindAll(sortingType));

List<ApplicationJpaEntity> applications = convertToApplicationJpaEntity(query);

boolean hasNext = applications.size() > pageable.getPageSize();
if (hasNext) {
applications.remove(applications.size() - 1);
}

return new SliceImpl<>(applications, pageable, hasNext);
}

private BooleanExpression inCompleted(List<Boolean> completed) {
if (completed == null || completed.isEmpty()) {
return null;
}
return applicationJpaEntity.isCompleted.in(completed);
}

private BooleanExpression containsCompanyName(String companyName) {
if (companyName == null) {
companyName = "";
}
return applicationProcessJpaEntity.application.companyName.contains(companyName);
}

private BooleanExpression eqUserId(Long userId) {
if (userId == null) {
return applicationProcessJpaEntity.application.userId.isNull();
}
return applicationProcessJpaEntity.application.userId.eq(userId);
}

private BooleanExpression inProcessType(List<ProcessType> process) {
if (process == null || process.isEmpty()) {
return null;
}
return applicationProcessJpaEntity.type.in(process);
}

private OrderSpecifier<?> selectOrderSpecifierAboutFindAll(ApplicationSortingType sortingType) {
return switch (sortingType) {
case CREATED -> applicationProcessJpaEntity.id.desc();
case PROCESS_TYPE -> applicationProcessJpaEntity.schedule.asc();
case REVERSE_PROCESS_TYPE -> applicationProcessJpaEntity.schedule.desc();
case CLOSING -> applicationProcessJpaEntity.id.asc();
};
}

private List<ApplicationJpaEntity> convertToApplicationJpaEntity(JPAQuery<ProcessWithApplicationResponse> query) {
return query.fetch().stream()
.map(processResponse ->
ApplicationJpaEntity.builder()
.id(processResponse.getApplicationId())
.companyName(processResponse.getCompanyName())
.position(processResponse.getPosition())
.specificPosition(processResponse.getSpecificPosition())
.isCompleted(processResponse.isCompleted())
.processes(
List.of(
ApplicationProcessJpaEntity.builder()
.id(processResponse.getProcessId())
.type(processResponse.getType())
.description(processResponse.getDescription())
.schedule(processResponse.getSchedule())
.build())
)
.build())
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
@NoArgsConstructor
public class ProcessWithApplicationResponse {

private long applicationId;
private String companyName;
private String position;
private String specificPosition;
private long processId;
private ProcessType type;
private String description;
private LocalDateTime schedule;
private long applicationId;
private String companyName;
private String position;
private String specificPosition;
private long processId;
private ProcessType type;
private String description;
private LocalDateTime schedule;
private boolean isCompleted;
}
Loading

0 comments on commit 663c555

Please sign in to comment.