diff --git a/src/main/java/com/ssafy/ssafsound/domain/post/dto/GetPostOffsetResDto.java b/src/main/java/com/ssafy/ssafsound/domain/post/dto/GetPostOffsetResDto.java index a37b74ab..f0a1dbf4 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/post/dto/GetPostOffsetResDto.java +++ b/src/main/java/com/ssafy/ssafsound/domain/post/dto/GetPostOffsetResDto.java @@ -22,7 +22,7 @@ public static GetPostOffsetResDto ofPosts(Page posts) { .posts(posts.getContent().stream() .map(GetPostElement::new) .collect(Collectors.toList())) - .currentPage(posts.getNumber()) + .currentPage(posts.getNumber()+1) .totalPageCount(posts.getTotalPages()) .build(); } @@ -32,7 +32,7 @@ public static GetPostOffsetResDto ofHotPosts(Page hotPosts) { .posts(hotPosts.getContent().stream() .map(hotPost -> new GetPostElement(hotPost.getPost())) .collect(Collectors.toList())) - .currentPage(hotPosts.getNumber()) + .currentPage(hotPosts.getNumber()+1) .totalPageCount(hotPosts.getTotalPages()) .build(); } @@ -42,7 +42,7 @@ public static GetPostOffsetResDto ofPostScraps(Page postScraps) { .posts(postScraps.getContent().stream() .map(postScrap -> new GetPostElement(postScrap.getPost())) .collect(Collectors.toList())) - .currentPage(postScraps.getNumber()) + .currentPage(postScraps.getNumber()+1) .totalPageCount(postScraps.getTotalPages()) .build(); } diff --git a/src/main/java/com/ssafy/ssafsound/domain/recruit/controller/RecruitController.java b/src/main/java/com/ssafy/ssafsound/domain/recruit/controller/RecruitController.java index 3f1ee7bb..1086a669 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/recruit/controller/RecruitController.java +++ b/src/main/java/com/ssafy/ssafsound/domain/recruit/controller/RecruitController.java @@ -6,6 +6,7 @@ import com.ssafy.ssafsound.domain.recruit.service.RecruitService; import com.ssafy.ssafsound.global.common.response.EnvelopeResponse; import lombok.RequiredArgsConstructor; + import org.springframework.data.domain.Pageable; import org.springframework.web.bind.annotation.*; @@ -57,23 +58,30 @@ public EnvelopeResponse deleteRecruit(@PathVariable Long recruitId, @Authe return EnvelopeResponse.builder().build(); } - @GetMapping - public EnvelopeResponse getRecruits(GetRecruitsReqDto getRecruitsReqDto, Pageable pageable, @Authentication AuthenticatedMember memberInfo) { - return EnvelopeResponse.builder() - .data(recruitService.getRecruits(getRecruitsReqDto, pageable, memberInfo.getMemberId())) + @GetMapping("/cursor") + public EnvelopeResponse getRecruitsByCursor(GetRecruitsReqDto getRecruitsReqDto, Pageable pageable, @Authentication AuthenticatedMember memberInfo) { + return EnvelopeResponse.builder() + .data(recruitService.getRecruitsByCursor(getRecruitsReqDto, pageable, memberInfo.getMemberId())) + .build(); + } + + @GetMapping("/offset") + public EnvelopeResponse getRecruitsByOffset(GetRecruitsReqDto getRecruitsReqDto, Pageable pageable, @Authentication AuthenticatedMember memberInfo) { + return EnvelopeResponse.builder() + .data(recruitService.getRecruitsByOffset(getRecruitsReqDto, pageable, memberInfo.getMemberId())) .build(); } @GetMapping("/my-scrap") - public EnvelopeResponse getScrapedRecruits(Long cursor, Pageable pageable, @Authentication AuthenticatedMember memberInfo) { - return EnvelopeResponse.builder() + public EnvelopeResponse getScrapedRecruits(Long cursor, Pageable pageable, @Authentication AuthenticatedMember memberInfo) { + return EnvelopeResponse.builder() .data(recruitService.getScrapedRecruits(memberInfo.getMemberId(), cursor, pageable)) .build(); } @GetMapping("/joined") - public EnvelopeResponse getMemberJoinedRecruits(GetMemberJoinRecruitsReqDto getMemberJoinRecruitsReqDto, @Authentication AuthenticatedMember memberInfo) { - return EnvelopeResponse.builder() + public EnvelopeResponse getMemberJoinedRecruits(GetMemberJoinRecruitsReqDto getMemberJoinRecruitsReqDto, @Authentication AuthenticatedMember memberInfo) { + return EnvelopeResponse.builder() .data(recruitService.getMemberJoinRecruits(getMemberJoinRecruitsReqDto, memberInfo.getMemberId())) .build(); } diff --git a/src/main/java/com/ssafy/ssafsound/domain/recruit/dto/GetRecruitOffsetResDto.java b/src/main/java/com/ssafy/ssafsound/domain/recruit/dto/GetRecruitOffsetResDto.java new file mode 100644 index 00000000..cac739df --- /dev/null +++ b/src/main/java/com/ssafy/ssafsound/domain/recruit/dto/GetRecruitOffsetResDto.java @@ -0,0 +1,46 @@ +package com.ssafy.ssafsound.domain.recruit.dto; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.ssafy.ssafsound.domain.recruit.domain.Recruit; +import lombok.Builder; +import lombok.Getter; +import org.springframework.data.domain.Page; + +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Collectors; + +@Getter +@Builder +public class GetRecruitOffsetResDto implements AddParticipantDto { + private List recruits; + private int currentPage; + private int totalPageCount; + @JsonIgnore + public List getRecruitsId() { + return recruits.stream().map(RecruitElement::getRecruitId).collect(Collectors.toList()); + } + + @JsonIgnore + public Map> getRecruitParticipantMapByRecruitIdAndRecruitType() { + Map> result = new TreeMap<>(); + for(RecruitElement recruitElement: recruits) { + result.put(recruitElement.getRecruitId(), recruitElement.getRecruitParticipantMap()); + } + return result; + } + + public static GetRecruitOffsetResDto fromPageAndMemberId(Page recruitPages, Long memberId) { + List recruits = recruitPages.getContent() + .stream() + .map((recruit -> RecruitElement.fromRecruitAndLoginMemberId(recruit, memberId))) + .collect(Collectors.toList()); + + return GetRecruitOffsetResDto.builder() + .recruits(recruits) + .currentPage(recruitPages.getNumber()+1) + .totalPageCount(recruitPages.getTotalPages()) + .build(); + } +} diff --git a/src/main/java/com/ssafy/ssafsound/domain/recruit/dto/GetRecruitsResDto.java b/src/main/java/com/ssafy/ssafsound/domain/recruit/dto/GetRecruitsCursorResDto.java similarity index 87% rename from src/main/java/com/ssafy/ssafsound/domain/recruit/dto/GetRecruitsResDto.java rename to src/main/java/com/ssafy/ssafsound/domain/recruit/dto/GetRecruitsCursorResDto.java index 2b5054be..1244e369 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/recruit/dto/GetRecruitsResDto.java +++ b/src/main/java/com/ssafy/ssafsound/domain/recruit/dto/GetRecruitsCursorResDto.java @@ -13,7 +13,7 @@ @Getter @Builder -public class GetRecruitsResDto implements AddParticipantDto { +public class GetRecruitsCursorResDto implements AddParticipantDto { private List recruits; private Long nextCursor; private Boolean isLast; @@ -32,14 +32,14 @@ public Map> getRecruitParticipantMapByRecr return result; } - public static GetRecruitsResDto fromPageAndMemberId(Slice sliceRecruit, Long memberId) { + public static GetRecruitsCursorResDto fromPageAndMemberId(Slice sliceRecruit, Long memberId) { List recruits = sliceRecruit.toList() .stream() .map((recruit -> RecruitElement.fromRecruitAndLoginMemberId(recruit, memberId))) .collect(Collectors.toList()); Long nextCursor = recruits.isEmpty() ? -1L : recruits.get(recruits.size()-1).getRecruitId(); - return GetRecruitsResDto.builder() + return GetRecruitsCursorResDto.builder() .recruits(recruits) .nextCursor(nextCursor) .isLast(sliceRecruit.isLast()) diff --git a/src/main/java/com/ssafy/ssafsound/domain/recruit/dto/GetRecruitsReqDto.java b/src/main/java/com/ssafy/ssafsound/domain/recruit/dto/GetRecruitsReqDto.java index 9373f53a..5f42a605 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/recruit/dto/GetRecruitsReqDto.java +++ b/src/main/java/com/ssafy/ssafsound/domain/recruit/dto/GetRecruitsReqDto.java @@ -10,7 +10,7 @@ @Getter @Builder public class GetRecruitsReqDto { - private Long cursor; + private Integer next; @NotBlank private String category; diff --git a/src/main/java/com/ssafy/ssafsound/domain/recruit/repository/RecruitDynamicQueryRepository.java b/src/main/java/com/ssafy/ssafsound/domain/recruit/repository/RecruitDynamicQueryRepository.java index 5e7b7c1d..a938042a 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/recruit/repository/RecruitDynamicQueryRepository.java +++ b/src/main/java/com/ssafy/ssafsound/domain/recruit/repository/RecruitDynamicQueryRepository.java @@ -3,13 +3,14 @@ import com.ssafy.ssafsound.domain.recruit.domain.Recruit; import com.ssafy.ssafsound.domain.recruit.dto.AppliedRecruit; import com.ssafy.ssafsound.domain.recruit.dto.GetRecruitsReqDto; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; public interface RecruitDynamicQueryRepository { - Slice findRecruitByGetRecruitsReqDto(GetRecruitsReqDto dto, Pageable pageable); + Slice findRecruitSliceByGetRecruitsReqDto(GetRecruitsReqDto dto, Pageable pageable); + Page findRecruitPageByGetRecruitsReqDto(GetRecruitsReqDto dto, Pageable pageable); Slice findMemberJoinRecruitWithCursorAndPageable(Long memberId, String category, Long cursorId, Pageable pageable); Slice findMemberScrapRecruits(Long memberId, Long cursorId, Pageable pageable); - Slice findMemberAppliedRecruits(Long memberId, Long cursor, String category, String matchStatus, Pageable pageable); } diff --git a/src/main/java/com/ssafy/ssafsound/domain/recruit/repository/RecruitDynamicQueryRepositoryImpl.java b/src/main/java/com/ssafy/ssafsound/domain/recruit/repository/RecruitDynamicQueryRepositoryImpl.java index 84c2e441..cc1b1590 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/recruit/repository/RecruitDynamicQueryRepositoryImpl.java +++ b/src/main/java/com/ssafy/ssafsound/domain/recruit/repository/RecruitDynamicQueryRepositoryImpl.java @@ -17,9 +17,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.SliceImpl; +import org.springframework.data.domain.*; +import org.springframework.data.support.PageableExecutionUtils; import org.springframework.stereotype.Repository; import org.springframework.util.StringUtils; @@ -44,56 +43,11 @@ public class RecruitDynamicQueryRepositoryImpl implements RecruitDynamicQueryRep private final JPAQueryFactory jpaQueryFactory; @Override - public Slice findRecruitByGetRecruitsReqDto(GetRecruitsReqDto dto, Pageable pageable) { - // cursor base pagination (value -1 or null ignore search condition) - Long cursor = dto.getCursor(); - - // recruit category (STUDY | PROJECT) - BooleanExpression categoryEq = dto.getCategory() == null ? null : recruit.category.eq(Category.valueOf(dto.getCategory().toUpperCase())); - - // recruit title contains search keyword - String keyword = dto.getKeyword(); - BooleanExpression titleEq = StringUtils.hasText(keyword) ? recruit.title.contains(keyword) : null; - - JPAQuery recruitDynamicQuery = jpaQueryFactory.selectFrom(recruit) - .where(recruitIdLtThanCursor(cursor), categoryEq, titleEq); - - // recruit skill - List skills = dto.getSkills(); - if(skills!=null && skills.size() > 0) { - String metaDataType = MetaDataType.SKILL.name(); - List containSkills = skills.stream() - .map(skillName->metaDataConsumer.getMetaData(metaDataType, skillName)) - .collect(Collectors.toList()); - - JPQLQuery recruitSkillContainRecruitIds = JPAExpressions - .select(recruitSkill.recruit.id) - .from(recruitSkill) - .innerJoin(recruitSkill.recruit, recruit) - .where(recruitSkill.skill.in(containSkills)); - - recruitDynamicQuery.where(recruit.id.in(recruitSkillContainRecruitIds)); - } - - // recruit types limitation - List recruitTypes = dto.getRecruitTypes(); - if(dto.getCategory() != null && dto.getCategory().toUpperCase().equals(Category.PROJECT.name()) && recruitTypes!=null && !recruitTypes.isEmpty()) { - String metaDataType = MetaDataType.RECRUIT_TYPE.name(); - List containRecruitTypes = recruitTypes.stream() - .map(recruitType->metaDataConsumer.getMetaData(metaDataType, recruitType)) - .collect(Collectors.toList()); - - JPQLQuery limitationContainRecruitIds = JPAExpressions - .select(recruitLimitation.recruit.id) - .from(recruitLimitation) - .innerJoin(recruitLimitation.recruit, recruit) - .where(recruitLimitation.type.in(containRecruitTypes)); - - recruitDynamicQuery.where(recruit.id.in(limitationContainRecruitIds)); - } - + public Slice findRecruitSliceByGetRecruitsReqDto(GetRecruitsReqDto dto, Pageable pageable) { + JPAQuery recruitDynamicQuery = findRecruitByGetRecruitsReqDto(dto); + Integer cursor = dto.getNext(); List recruits = recruitDynamicQuery - .where(recruit.finishedRecruit.eq(dto.isFinished())) + .where(recruitIdLtThanCursor(Long.valueOf(cursor))) .limit(pageable.getPageSize()+1) .orderBy(recruit.id.desc()) .fetch(); @@ -101,6 +55,88 @@ public Slice findRecruitByGetRecruitsReqDto(GetRecruitsReqDto dto, Page return new SliceImpl<>(hasNext ? recruits.subList(0, pageable.getPageSize()) : recruits, pageable, hasNext); } + @Override + public Page findRecruitPageByGetRecruitsReqDto(GetRecruitsReqDto dto, Pageable pageable) { + JPAQuery recruitDynamicQuery = findRecruitByGetRecruitsReqDto(dto); + List recruits = recruitDynamicQuery + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .orderBy(recruit.id.desc()) + .fetch(); + + JPAQuery countQuery = jpaQueryFactory + .select(recruit.count()) + .from(recruit) + .where( + recruitCategoryEq(dto.getCategory()), + recruitTitleEq(dto.getKeyword()), + recruit.finishedRecruit.eq(dto.isFinished()), + recruitTypeContains(dto.getCategory(), dto.getRecruitTypes()), + recruitSkillContains(dto.getSkills()) + ); + + return PageableExecutionUtils.getPage(recruits, pageable, countQuery::fetchOne); + } + + private JPAQuery findRecruitByGetRecruitsReqDto(GetRecruitsReqDto dto) { + JPAQuery recruitDynamicQuery = jpaQueryFactory.selectFrom(recruit); + recruitDynamicQuery + .where( + recruitCategoryEq(dto.getCategory()), + recruitTitleEq(dto.getKeyword()), + recruit.finishedRecruit.eq(dto.isFinished()), + recruitTypeContains(dto.getCategory(), dto.getRecruitTypes()), + recruitSkillContains(dto.getSkills()) + ); + return recruitDynamicQuery; + } + + private BooleanExpression recruitCategoryEq(String category) { + return category == null ? null : recruit.category.eq(Category.valueOf(category.toUpperCase())); + } + private BooleanExpression recruitTitleEq(String keyword) { + return StringUtils.hasText(keyword) ? recruit.title.contains(keyword) : null; + } + private BooleanExpression recruitTypeContains(String category, List recruitTypes) { + boolean isNotProject = category == null || category.toUpperCase().equals(Category.PROJECT.name()); + boolean isEmpty = recruitTypes == null || recruitTypes.isEmpty(); + if(isNotProject || isEmpty) { + return null; + } + + String metaDataType = MetaDataType.RECRUIT_TYPE.name(); + List containRecruitTypes = recruitTypes.stream() + .map(recruitType->metaDataConsumer.getMetaData(metaDataType, recruitType)) + .collect(Collectors.toList()); + + JPQLQuery limitationContainRecruitIds = JPAExpressions + .select(recruitLimitation.recruit.id) + .from(recruitLimitation) + .innerJoin(recruitLimitation.recruit, recruit) + .where(recruitLimitation.type.in(containRecruitTypes)); + + return recruit.id.in(limitationContainRecruitIds); + } + + private BooleanExpression recruitSkillContains(List skills) { + boolean isEmpty = skills==null || skills.isEmpty(); + if(isEmpty) { + return null; + } + + String metaDataType = MetaDataType.SKILL.name(); + List containSkills = skills.stream() + .map(skillName->metaDataConsumer.getMetaData(metaDataType, skillName)) + .collect(Collectors.toList()); + + JPQLQuery recruitSkillContainRecruitIds = JPAExpressions + .select(recruitSkill.recruit.id) + .from(recruitSkill) + .innerJoin(recruitSkill.recruit, recruit) + .where(recruitSkill.skill.in(containSkills)); + return recruit.id.in(recruitSkillContainRecruitIds); + } + @Override public Slice findMemberJoinRecruitWithCursorAndPageable(Long memberId, String category, Long cursor, Pageable pageable) { BooleanExpression applicationRecruitCategoryEq = diff --git a/src/main/java/com/ssafy/ssafsound/domain/recruit/service/RecruitService.java b/src/main/java/com/ssafy/ssafsound/domain/recruit/service/RecruitService.java index 1cb4baa5..d429f6cf 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/recruit/service/RecruitService.java +++ b/src/main/java/com/ssafy/ssafsound/domain/recruit/service/RecruitService.java @@ -21,6 +21,7 @@ import com.ssafy.ssafsound.global.common.exception.GlobalErrorInfo; import com.ssafy.ssafsound.global.common.exception.ResourceNotFoundException; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -129,10 +130,9 @@ public void deleteRecruit(Long recruitId, Long memberId) { } @Transactional(readOnly = true) - public GetRecruitsResDto getRecruits(GetRecruitsReqDto getRecruitsReqDto, Pageable pageable, Long loginMemberId) { - // 페이지네이션 조건에 따라 프로젝트/스터디 글 목록을 조회한다. - Slice recruitPages = recruitRepository.findRecruitByGetRecruitsReqDto(getRecruitsReqDto, pageable); - GetRecruitsResDto recruitsResDto = GetRecruitsResDto.fromPageAndMemberId(recruitPages, loginMemberId); + public GetRecruitsCursorResDto getRecruitsByCursor(GetRecruitsReqDto getRecruitsReqDto, Pageable pageable, Long loginMemberId) { + Slice recruitPages = recruitRepository.findRecruitSliceByGetRecruitsReqDto(getRecruitsReqDto, pageable); + GetRecruitsCursorResDto recruitsResDto = GetRecruitsCursorResDto.fromPageAndMemberId(recruitPages, loginMemberId); if(!recruitsResDto.getRecruits().isEmpty()) { addRecruitParticipants(recruitsResDto); } @@ -140,9 +140,22 @@ public GetRecruitsResDto getRecruits(GetRecruitsReqDto getRecruitsReqDto, Pageab } @Transactional(readOnly = true) - public GetRecruitsResDto getScrapedRecruits(Long memberId, Long cursor, Pageable pageable) { + public GetRecruitOffsetResDto getRecruitsByOffset(GetRecruitsReqDto getRecruitsReqDto, Pageable pageable, Long loginMemberId) { + Integer pageOffset = getRecruitsReqDto.getNext(); + + PageRequest pageRequest = PageRequest.of(pageOffset== null || pageOffset <= 0 ? 0 : pageOffset, pageable.getPageSize()-1); + Page recruitPages = recruitRepository.findRecruitPageByGetRecruitsReqDto(getRecruitsReqDto, pageRequest); + GetRecruitOffsetResDto recruitsResDto = GetRecruitOffsetResDto.fromPageAndMemberId(recruitPages, loginMemberId); + if(!recruitsResDto.getRecruits().isEmpty()) { + addRecruitParticipants(recruitsResDto); + } + return recruitsResDto; + } + + @Transactional(readOnly = true) + public GetRecruitsCursorResDto getScrapedRecruits(Long memberId, Long cursor, Pageable pageable) { Slice recruitPages = recruitRepository.findMemberScrapRecruits(memberId, cursor, pageable); - GetRecruitsResDto recruitsResDto = GetRecruitsResDto.fromPageAndMemberId(recruitPages, memberId); + GetRecruitsCursorResDto recruitsResDto = GetRecruitsCursorResDto.fromPageAndMemberId(recruitPages, memberId); return recruitsResDto; } @@ -158,7 +171,7 @@ public void expiredRecruit(Long recruitId, Long memberId) { } @Transactional(readOnly = true) - public GetRecruitsResDto getMemberJoinRecruits(GetMemberJoinRecruitsReqDto recruitsReqDto, Long loginMemberId) { + public GetRecruitsCursorResDto getMemberJoinRecruits(GetMemberJoinRecruitsReqDto recruitsReqDto, Long loginMemberId) { Long memberId = recruitsReqDto.getMemberId(); Member member = memberRepository.findById(memberId).orElseThrow(()->new ResourceNotFoundException(GlobalErrorInfo.NOT_FOUND)); @@ -167,7 +180,7 @@ public GetRecruitsResDto getMemberJoinRecruits(GetMemberJoinRecruitsReqDto recru } Slice recruitPages = recruitRepository.findMemberJoinRecruitWithCursorAndPageable(memberId, recruitsReqDto.getCategory(), recruitsReqDto.getCursor(), PageRequest.ofSize(recruitsReqDto.getSize())); - GetRecruitsResDto recruitsResDto = GetRecruitsResDto.fromPageAndMemberId(recruitPages, loginMemberId); + GetRecruitsCursorResDto recruitsResDto = GetRecruitsCursorResDto.fromPageAndMemberId(recruitPages, loginMemberId); if(!recruitsResDto.getRecruits().isEmpty()) { addRecruitParticipants(recruitsResDto); } diff --git a/src/main/resources/static/docs/index.html b/src/main/resources/static/docs/index.html index 9f69014d..3d944db6 100644 --- a/src/main/resources/static/docs/index.html +++ b/src/main/resources/static/docs/index.html @@ -854,7 +854,7 @@

11.7.1. HTTP request

-
GET /recruits?size=20&category=project&keyword=%EC%82%AC%EC%9D%B4%EB%93%9C&isFinished=false&recruitTypes=%EB%B0%B1%EC%97%94%EB%93%9C&recruitTypes=%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C&skills=Spring&skills=React HTTP/1.1
+
GET /recruits/offset?size=20&category=project&keyword=%EC%82%AC%EC%9D%B4%EB%93%9C&isFinished=false&recruitTypes=%EB%B0%B1%EC%97%94%EB%93%9C&recruitTypes=%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C&skills=Spring&skills=React HTTP/1.1
 Host: api.ssafsound.com
 Cookie: accessToken=accessTokenValue
@@ -10466,8 +10468,8 @@

cursor

-

다음 조회 커서 default(초기화면)에서는 미포함

+

next

+

조회할 page 번호

size

@@ -10505,7 +10507,7 @@

@@ -10578,14 +10580,14 @@

응답 데이터

-

data.nextCursor

+

data.currentPage

Number

-

다음 조회할 커서 번호

+

현재 조회한 페이지 번호

-

data.isLast

-

Boolean

-

마지막 페이지 여부

+

data.totalPageCount

+

Number

+

total 페이지 갯수

data.recruits[].recruitId

@@ -10736,7 +10738,7 @@

@@ -12707,7 +12709,7 @@

@@ -12923,7 +12925,7 @@

@@ -13656,7 +13658,7 @@

diff --git a/src/test/java/com/ssafy/ssafsound/domain/recruit/controller/RecruitControllerTest.java b/src/test/java/com/ssafy/ssafsound/domain/recruit/controller/RecruitControllerTest.java index 6db6f046..de95d33b 100644 --- a/src/test/java/com/ssafy/ssafsound/domain/recruit/controller/RecruitControllerTest.java +++ b/src/test/java/com/ssafy/ssafsound/domain/recruit/controller/RecruitControllerTest.java @@ -200,23 +200,23 @@ void deleteRecruit() { getEnvelopPatternWithNoContent())); } - @DisplayName("리크루트 목록 조회") + @DisplayName("커서 기반 리크루트 목록 조회") @Test - void getRecruits() { - doReturn(RecruitFixture.GET_RECRUITS_RES_DTO) + void getRecruitsByCursor() { + doReturn(RecruitFixture.GET_RECRUITS_CURSOR_RES_DTO) .when(recruitService) - .getRecruits(any(), any(), any()); + .getRecruitsByCursor(any(), any(), any()); restDocs .cookie(ACCESS_TOKEN) - .when().get("/recruits?size=20&category=project&keyword=사이드&isFinished=false&recruitTypes=백엔드&recruitTypes=프론트엔드&skills=Spring&skills=React") + .when().get("/recruits/cursor?size=20&category=project&keyword=사이드&isFinished=false&recruitTypes=백엔드&recruitTypes=프론트엔드&skills=Spring&skills=React") .then().log().all() .assertThat() .statusCode(HttpStatus.OK.value()) .apply(document("recruit/recruits", requestCookieAccessTokenOptional(), requestParameters( - parameterWithName("cursor").optional().description("다음 조회 커서 default(초기화면)에서는 미포함"), + parameterWithName("next").optional().description("조회할 커서 번호 default(초기화면)에서는 미포함"), parameterWithName("size").description("페이징 사이즈"), parameterWithName("category").description("카테고리 project|study"), parameterWithName("keyword").description("리크루트 게시글 제목 검색 키워드"), @@ -250,10 +250,60 @@ void getRecruits() { ); } + @DisplayName("offset 기반 리크루트 목록 조회") + @Test + void getRecruitsByOffset() { + doReturn(RecruitFixture.GET_RECRUITS_OFFSET_RES_DTO) + .when(recruitService) + .getRecruitsByOffset(any(), any(), any()); + + restDocs + .cookie(ACCESS_TOKEN) + .when().get("/recruits/offset?size=20&category=project&keyword=사이드&isFinished=false&recruitTypes=백엔드&recruitTypes=프론트엔드&skills=Spring&skills=React") + .then().log().all() + .assertThat() + .statusCode(HttpStatus.OK.value()) + .apply(document("recruit/recruits", + requestCookieAccessTokenOptional(), + requestParameters( + parameterWithName("next").optional().description("조회할 page 번호"), + parameterWithName("size").description("페이징 사이즈"), + parameterWithName("category").description("카테고리 project|study"), + parameterWithName("keyword").description("리크루트 게시글 제목 검색 키워드"), + parameterWithName("isFinished").description("리크루트 종료 여부"), + parameterWithName("recruitTypes").description("리크루트 모집파트, 메타데이터-리크루트 목록 조회 참고"), + parameterWithName("skills").description("리크루트와 연관된 기술 스택, 메타데이터-스킬 목록 조회 참고") + ), + getEnvelopPatternWithData().andWithPrefix("data.", + fieldWithPath("currentPage").type(JsonFieldType.NUMBER).description("현재 조회한 페이지 번호"), + fieldWithPath("totalPageCount").type(JsonFieldType.NUMBER).description("total 페이지 갯수") + ).andWithPrefix("data.recruits[].", + fieldWithPath("recruitId").type(JsonFieldType.NUMBER).description("리크루트 id"), + fieldWithPath("category").type(JsonFieldType.STRING).description("카테고리 project|study"), + fieldWithPath("mine").type(JsonFieldType.BOOLEAN).description("내가 쓴 글 여부(토큰 기준)"), + fieldWithPath("title").type(JsonFieldType.STRING).description("리크루트 모집글 제목, 글자 수 제한 50자"), + fieldWithPath("content").type(JsonFieldType.STRING).description("리크루트 본문 요약본 최대 50자"), + fieldWithPath("recruitEnd").type(JsonFieldType.STRING).description("yyyy-MM-dd 모집 종료 일자"), + fieldWithPath("finishedRecruit").type(JsonFieldType.BOOLEAN).description("리크루트 종료 여부"), + fieldWithPath("participants[].members[]").type(JsonFieldType.ARRAY).description("리크루트 참여 멤버 ").optional() + ).andWithPrefix("data.recruits[].skills[].", + fieldWithPath("skillId").type(JsonFieldType.NUMBER).description("스킬 id 미사용"), + fieldWithPath("name").type(JsonFieldType.STRING).description("리크루트와 연관된 기술 스택명, 메타데이터-스킬 목록 조회 참고") + ).andWithPrefix("data.recruits[].participants[].", + fieldWithPath("recruitType").type(JsonFieldType.STRING).description("리크루트 모집파트, 메타데이터-리크루트 목록 조회 참고"), + fieldWithPath("limit").type(JsonFieldType.NUMBER).description("리크루트 모집 인원 제한 1명이상 10명 이하") + ).andWithPrefix("data.recruits[].participants[].members[].", + fieldWithPath("nickname").type(JsonFieldType.STRING).description("리크루트 참여자 닉네임"), + fieldWithPath("isMajor").type(JsonFieldType.BOOLEAN).description("리크루트 참여자 전공 여부") + ) + ) + ); + } + @DisplayName("스크랩 된 리크루트 목록 조회") @Test void getScrapedRecruits() { - doReturn(RecruitFixture.GET_RECRUITS_RES_DTO) + doReturn(RecruitFixture.GET_RECRUITS_CURSOR_RES_DTO) .when(recruitService) .getScrapedRecruits(any(), any(), any()); @@ -298,7 +348,7 @@ void getScrapedRecruits() { @DisplayName("사용자 참여 확정 리크루트 목록 조회") @Test void getMemberJoinedRecruits() { - doReturn(RecruitFixture.GET_RECRUITS_RES_DTO) + doReturn(RecruitFixture.GET_RECRUITS_CURSOR_RES_DTO) .when(recruitService) .getMemberJoinRecruits(any(), any()); diff --git a/src/test/java/com/ssafy/ssafsound/domain/recruit/service/RecruitServiceTest.java b/src/test/java/com/ssafy/ssafsound/domain/recruit/service/RecruitServiceTest.java index 4f9a8108..ad47986c 100644 --- a/src/test/java/com/ssafy/ssafsound/domain/recruit/service/RecruitServiceTest.java +++ b/src/test/java/com/ssafy/ssafsound/domain/recruit/service/RecruitServiceTest.java @@ -177,13 +177,13 @@ void setStub() { lenient().when(recruitRepository.findById(2L)).thenReturn(java.util.Optional.ofNullable(savedRecruit)); lenient().when(recruitRepository - .findRecruitByGetRecruitsReqDto(emptyKeywordDto, pageInfo)) + .findRecruitSliceByGetRecruitsReqDto(emptyKeywordDto, pageInfo)) .thenReturn(new SliceImpl<>(List.of(savedRecruit))); - lenient().when(recruitRepository.findRecruitByGetRecruitsReqDto(findTitleDto, pageInfo)) + lenient().when(recruitRepository.findRecruitSliceByGetRecruitsReqDto(findTitleDto, pageInfo)) .thenReturn(new SliceImpl<>(List.of(savedRecruit))); - lenient().when(recruitRepository.findRecruitByGetRecruitsReqDto(notFindKeywordDto, pageInfo)) + lenient().when(recruitRepository.findRecruitSliceByGetRecruitsReqDto(notFindKeywordDto, pageInfo)) .thenReturn(new SliceImpl<>(List.of())); @@ -348,15 +348,15 @@ void Given_NotIncludePrevExistLimitAndPatchRecruitDto_When_DeleteRecruit_Then_Fa @DisplayName("키워드를 입력하지 않은 리크루트 목록 검색") @Test void Given_NotIncludeKeyword_When_GetPagingRecruits_Then_Success() { - GetRecruitsResDto getRecruitsResDto = recruitService.getRecruits(emptyKeywordDto, pageInfo, null); - assertEquals(1, getRecruitsResDto.getRecruits().size()); + GetRecruitsCursorResDto getRecruitsCursorResDto = recruitService.getRecruitsByCursor(emptyKeywordDto, pageInfo, null); + assertEquals(1, getRecruitsCursorResDto.getRecruits().size()); } @DisplayName("키워드를 입력한 리크루트 목록 검색 (검색 결과 O)") @Test void Given_IncludeKeyword_When_GetPagingRecruits_Then_Success() { - GetRecruitsResDto getRecruitsResDto = recruitService.getRecruits(findTitleDto, pageInfo, null); - assertEquals(1, getRecruitsResDto.getRecruits().size()); + GetRecruitsCursorResDto getRecruitsCursorResDto = recruitService.getRecruitsByCursor(findTitleDto, pageInfo, null); + assertEquals(1, getRecruitsCursorResDto.getRecruits().size()); } @DisplayName("인원제한이 설정된 모집군과 등록자의 모집군이 일치하는 리크루트 목록 검색") @@ -364,19 +364,19 @@ void Given_IncludeKeyword_When_GetPagingRecruits_Then_Success() { void Given_IncludeKeywordAndIncludeRegisterRecruitType_When_GetPagingRecruits_Then_Success() { savedRecruit.setRegisterRecruitType(new MetaData(RecruitType.DESIGN)); - GetRecruitsResDto getRecruitsResDto = recruitService.getRecruits(findTitleDto, pageInfo, null); - assertEquals(1, getRecruitsResDto.getRecruits().size()); - assertEquals(1, getRecruitsResDto.getRecruits().get(0).getParticipants().size()); - assertEquals(3, getRecruitsResDto.getRecruits().get(0).getParticipants().get(0).getLimit()); - assertEquals(2, getRecruitsResDto.getRecruits().get(0).getParticipants().get(0).getMembers().size()); + GetRecruitsCursorResDto getRecruitsCursorResDto = recruitService.getRecruitsByCursor(findTitleDto, pageInfo, null); + assertEquals(1, getRecruitsCursorResDto.getRecruits().size()); + assertEquals(1, getRecruitsCursorResDto.getRecruits().get(0).getParticipants().size()); + assertEquals(3, getRecruitsCursorResDto.getRecruits().get(0).getParticipants().get(0).getLimit()); + assertEquals(2, getRecruitsCursorResDto.getRecruits().get(0).getParticipants().get(0).getMembers().size()); } @DisplayName("키워드를 입력한 리크루트 목록 검색 (검색 결과 X)") @Test void Given_IncludeKeyword_When_GetPagingRecruits_Then_EmptySet() { - GetRecruitsResDto getRecruitsResDto = recruitService.getRecruits(notFindKeywordDto, pageInfo, null); - assertEquals(0, getRecruitsResDto.getRecruits().size()); + GetRecruitsCursorResDto getRecruitsCursorResDto = recruitService.getRecruitsByCursor(notFindKeywordDto, pageInfo, null); + assertEquals(0, getRecruitsCursorResDto.getRecruits().size()); } @DisplayName("등록자 리크루트 완료 성공") diff --git a/src/test/java/com/ssafy/ssafsound/global/util/fixture/RecruitFixture.java b/src/test/java/com/ssafy/ssafsound/global/util/fixture/RecruitFixture.java index 4b6b315f..2e3a1ec2 100644 --- a/src/test/java/com/ssafy/ssafsound/global/util/fixture/RecruitFixture.java +++ b/src/test/java/com/ssafy/ssafsound/global/util/fixture/RecruitFixture.java @@ -149,12 +149,18 @@ public class RecruitFixture { private static final RecruitElement RECRUIT_1_ELEMENT = RecruitElement.fromRecruitAndLoginMemberId(RECRUIT_1, 1L); - public static final GetRecruitsResDto GET_RECRUITS_RES_DTO = GetRecruitsResDto.builder() + public static final GetRecruitsCursorResDto GET_RECRUITS_CURSOR_RES_DTO = GetRecruitsCursorResDto.builder() .recruits(List.of(RECRUIT_1_ELEMENT)) .nextCursor(1L) .isLast(true) .build(); + public static final GetRecruitOffsetResDto GET_RECRUITS_OFFSET_RES_DTO = GetRecruitOffsetResDto.builder() + .recruits(List.of(RECRUIT_1_ELEMENT)) + .currentPage(0) + .totalPageCount(1) + .build(); + private static final RecruitApplicationElement APPLICATION_ELEMENT = RecruitApplicationFixture.APPLICATION1_ELEMENT(); private static final AppliedRecruit APPLIED_RECRUIT = AppliedRecruit.builder() .recruit(RECRUIT_1)