Skip to content

Commit

Permalink
GETP-177 feat: 지원한 프로젝트 목록 조회 기능 구현 (#117)
Browse files Browse the repository at this point in the history
* GETP-177 feat: 내 프로젝트 지원 목록 기능 구현

* GETP-177 test: 내 프로젝트 지원 목록 기능 테스트 작성

* GETP-177 fix: 순환 의존성 해결 및 엔드포인트 수정

* GETP-177 test: 순환 의존성 해결 및 엔드 포인트 수정 후 테스트 작성

---------

Co-authored-by: 신찬규(Shin Changyu) <[email protected]>
Co-authored-by: Changyu Shin <[email protected]>
  • Loading branch information
3 people authored Aug 19, 2024
1 parent 841b7de commit 790c677
Show file tree
Hide file tree
Showing 12 changed files with 394 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package es.princip.getp.domain.project.query.dao;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import es.princip.getp.domain.project.query.dto.AppliedProjectCardResponse;

public interface AppliedProjectDao {

Page<AppliedProjectCardResponse> findPagedMyAppliedProjects(Pageable pageable, Long memberId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package es.princip.getp.domain.project.query.dao;

import com.querydsl.jpa.impl.JPAQuery;
import es.princip.getp.domain.project.command.domain.Project;
import es.princip.getp.domain.project.query.dto.AppliedProjectCardResponse;
import es.princip.getp.infra.support.QueryDslSupport;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Map;

import static es.princip.getp.domain.people.command.domain.QPeople.people;
import static es.princip.getp.domain.project.command.domain.QProject.project;
import static es.princip.getp.domain.project.command.domain.QProjectApplication.projectApplication;
import static es.princip.getp.domain.project.query.dao.ProjectDaoUtil.toProjectIds;

@Repository
@RequiredArgsConstructor
public class AppliedProjectQueryDslDao extends QueryDslSupport implements AppliedProjectDao{

private final ProjectApplicationDao projectApplicationDao;

@Override
public Page<AppliedProjectCardResponse> findPagedMyAppliedProjects(
final Pageable pageable,
final Long memberId
) {
final List<Project> projects = getAppliedProjects(pageable, memberId);
final Long[] projectIds = toProjectIds(projects);
final Map<Long, Long> projectApplicationCounts = projectApplicationDao.countByProjectIds(projectIds);
final List<AppliedProjectCardResponse> content = assembleAppliedProjectCardResponse(projects, projectApplicationCounts);

return applyPagination(
pageable,
content,
countQuery -> getAppliedProjectsCountQuery(memberId)
);
}

private List<Project> getAppliedProjects(
final Pageable pageable,
final Long memberId
) {
return queryFactory.selectFrom(project)
.join(projectApplication).on(projectApplication.projectId.eq(project.projectId))
.join(people).on(people.peopleId.eq(projectApplication.applicantId))
.where(people.memberId.eq(memberId))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
}

private JPAQuery<Long> getAppliedProjectsCountQuery(final Long memberId) {
return queryFactory.select(project.count())
.from(project)
.join(projectApplication).on(projectApplication.projectId.eq(project.projectId))
.join(people).on(people.peopleId.eq(projectApplication.applicantId))
.where(people.memberId.eq(memberId));
}

private List<AppliedProjectCardResponse> assembleAppliedProjectCardResponse(
final List<Project> projects,
final Map<Long, Long> projectApplicationCounts
) {
return projects.stream()
.map(project -> AppliedProjectCardResponse.of(
project,
projectApplicationCounts.get(project.getProjectId())
))
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package es.princip.getp.domain.project.query.dao;

import es.princip.getp.infra.support.QueryDslSupport;
import lombok.RequiredArgsConstructor;

import org.springframework.stereotype.Repository;

import java.util.Map;
Expand All @@ -10,6 +12,7 @@
import static es.princip.getp.domain.project.command.domain.QProjectApplication.projectApplication;

@Repository
@RequiredArgsConstructor
public class ProjectApplicationQueryDslDao extends QueryDslSupport implements ProjectApplicationDao {

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package es.princip.getp.domain.project.query.dto;

import es.princip.getp.domain.common.domain.Duration;
import es.princip.getp.domain.common.dto.HashtagsResponse;
import es.princip.getp.domain.project.command.domain.Project;
import es.princip.getp.domain.project.command.domain.ProjectStatus;

public record AppliedProjectCardResponse(
Long projectId,
String title,
Long payment,
Long applicantsCount,
Long estimatedDays,
Duration applicationDuration,
HashtagsResponse hashtags,
String description,
ProjectStatus status
) {

public static AppliedProjectCardResponse of(final Project project, final Long applicantsCount) {
return new AppliedProjectCardResponse(
project.getProjectId(),
project.getTitle(),
project.getPayment(),
applicantsCount,
project.getEstimatedDuration().days(),
project.getApplicationDuration(),
HashtagsResponse.from(project.getHashtags()),
project.getDescription(),
project.getStatus()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package es.princip.getp.domain.project.query.presentation;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import es.princip.getp.domain.project.query.dao.AppliedProjectDao;
import es.princip.getp.domain.project.query.dto.AppliedProjectCardResponse;
import es.princip.getp.infra.dto.response.ApiResponse;
import es.princip.getp.infra.dto.response.ApiResponse.ApiSuccessResult;
import es.princip.getp.infra.dto.response.PageResponse;
import es.princip.getp.infra.security.details.PrincipalDetails;
import lombok.RequiredArgsConstructor;

import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Page;
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.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;

@RestController
@RequestMapping("/people")
@RequiredArgsConstructor
public class AppliedProjectQueryController {

private final AppliedProjectDao appliedProjectDao;

@GetMapping("/me/projects")
@PreAuthorize("hasRole('PEOPLE') and isAuthenticated()")
public ResponseEntity<ApiSuccessResult<PageResponse<AppliedProjectCardResponse>>> getMyAppliedProjects(
@PageableDefault(sort = "projectId", direction = Sort.Direction.DESC) final Pageable pageable,
@AuthenticationPrincipal final PrincipalDetails principalDetails
) {
final Long memberId = principalDetails.getMember().getMemberId();
final Page<AppliedProjectCardResponse> page = appliedProjectDao.findPagedMyAppliedProjects(pageable, memberId);
final PageResponse<AppliedProjectCardResponse> response = PageResponse.from(page);
return ApiResponse.success(HttpStatus.OK, response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package es.princip.getp.domain.common.description;


import org.springframework.restdocs.request.ParameterDescriptor;

import static es.princip.getp.infra.util.ParameterDescriptorHelper.getDescriptor;

public class PaginationDescription {

public static ParameterDescriptor[] description(final int page, final int size, final String sort) {
return new ParameterDescriptor[] {
getDescriptor("page", "페이지 번호", "default", String.valueOf(page)),
getDescriptor("size", "페이지 크기", "default", String.valueOf(size)),
getDescriptor("sort", "정렬 방식", "default", sort)
};
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package es.princip.getp.domain.people.query.presentation;

import es.princip.getp.domain.common.description.PaginationDescription;
import es.princip.getp.domain.people.command.domain.PeopleType;
import es.princip.getp.domain.people.query.dao.PeopleDao;
import es.princip.getp.domain.people.query.dto.people.CardPeopleResponse;
Expand Down Expand Up @@ -64,8 +65,8 @@ private ResultActions perform() throws Exception {

@Test
public void getCardPeoplePage() throws Exception {
Pageable pageable = PageRequest.of(page, size, sort);
List<CardPeopleResponse> content = List.of(
final Pageable pageable = PageRequest.of(page, size, sort);
final List<CardPeopleResponse> content = List.of(
new CardPeopleResponse(
1L,
NICKNAME,
Expand All @@ -80,21 +81,14 @@ public void getCardPeoplePage() throws Exception {
)
)
);
Page<CardPeopleResponse> page = new PageImpl<>(content, pageable, content.size());
final Page<CardPeopleResponse> page = new PageImpl<>(content, pageable, content.size());
given(peopleDao.findCardPeoplePage(any(Pageable.class))).willReturn(page);

perform()
.andExpect(status().isOk())
.andDo(
restDocs.document(
queryParameters(
parameterWithName("page").description("페이지 번호")
.optional().attributes(key("default").value("0")),
parameterWithName("size").description("페이지 크기")
.optional().attributes(key("default").value("10")),
parameterWithName("sort").description("정렬 방식")
.optional().attributes(key("default").value("peopleId,desc"))
),
queryParameters(PaginationDescription.description(this.page, size, "peopleId,desc")),
responseFields(
getDescriptor("content[].peopleId", "피플 ID"),
getDescriptor("content[].peopleType", "피플 유형")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package es.princip.getp.domain.project.query.dao;

import es.princip.getp.domain.people.query.infra.PeopleDataLoader;
import es.princip.getp.domain.project.query.dto.AppliedProjectCardResponse;
import es.princip.getp.domain.project.query.infra.ProjectApplicationDataLoader;
import es.princip.getp.domain.project.query.infra.ProjectDataLoader;
import es.princip.getp.infra.DataLoader;
import es.princip.getp.infra.support.DaoTest;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
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 java.util.List;

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

public class AppliedProjectDaoTest extends DaoTest {

private static final int TEST_SIZE = 20;
private static final int PAGE_SIZE = 10;

@PersistenceContext
private EntityManager entityManager;

@Autowired
private AppliedProjectDao appliedProjectDao;

private List<DataLoader> dataLoaders;

@BeforeEach
void setUp() {
dataLoaders = List.of(
new PeopleDataLoader(entityManager),
new ProjectDataLoader(entityManager),
new ProjectApplicationDataLoader(entityManager)
);
dataLoaders.forEach(dataLoader -> dataLoader.load(TEST_SIZE));
}

@AfterEach
void teardown() {
dataLoaders.forEach(DataLoader::teardown);
}

@Nested
class 지원한_프로젝트_목록_조회 {

final Pageable pageable = PageRequest.of(0, PAGE_SIZE);

@Test
void 프로젝트_목록_조회() {
final Page<AppliedProjectCardResponse> response = appliedProjectDao.findPagedMyAppliedProjects(
pageable,
1L
);

assertThat(response.getContent()).allSatisfy(content -> {
assertThat(content).usingRecursiveComparison().isNotNull();
});
assertThat(response.getNumberOfElements()).isGreaterThan(0);
}
}
}
Loading

0 comments on commit 790c677

Please sign in to comment.