Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[REFACTOR] 지원서 조회하기와 관련된 API에 대한 리팩토링 #208

Merged
merged 6 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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