diff --git a/src/main/java/project/flipnote/cardset/controller/GroupCardSetController.java b/src/main/java/project/flipnote/cardset/controller/GroupCardSetController.java index 7e4c91f4..2af768d7 100644 --- a/src/main/java/project/flipnote/cardset/controller/GroupCardSetController.java +++ b/src/main/java/project/flipnote/cardset/controller/GroupCardSetController.java @@ -4,6 +4,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -15,10 +16,13 @@ import lombok.RequiredArgsConstructor; import project.flipnote.cardset.controller.docs.GroupCardSetControllerDocs; import project.flipnote.cardset.model.CardSetDetailResponse; +import project.flipnote.cardset.model.CardSetSearchRequest; +import project.flipnote.cardset.model.CardSetSummaryResponse; import project.flipnote.cardset.model.CardSetUpdateRequest; import project.flipnote.cardset.model.CreateCardSetRequest; import project.flipnote.cardset.model.CreateCardSetResponse; import project.flipnote.cardset.service.CardSetService; +import project.flipnote.common.model.response.PagingResponse; import project.flipnote.common.security.dto.AuthPrinciple; @RequiredArgsConstructor @@ -62,4 +66,13 @@ public ResponseEntity updateCardSet( return ResponseEntity.ok(res); } + @GetMapping + public ResponseEntity> getCardSets( + @PathVariable("groupId") Long groupId, + @Valid @ModelAttribute CardSetSearchRequest req + ) { + PagingResponse res = cardSetService.getCardSets(groupId, req); + + return ResponseEntity.ok(res); + } } diff --git a/src/main/java/project/flipnote/cardset/model/CardSetSummaryResponse.java b/src/main/java/project/flipnote/cardset/model/CardSetSummaryResponse.java index a3067c20..e5088567 100644 --- a/src/main/java/project/flipnote/cardset/model/CardSetSummaryResponse.java +++ b/src/main/java/project/flipnote/cardset/model/CardSetSummaryResponse.java @@ -1,7 +1,5 @@ package project.flipnote.cardset.model; -import project.flipnote.cardset.entity.CardSet; - public record CardSetSummaryResponse( Long cardSetId, Long groupId, diff --git a/src/main/java/project/flipnote/cardset/repository/CardSetRepositoryCustom.java b/src/main/java/project/flipnote/cardset/repository/CardSetRepositoryCustom.java index bc8dc81a..284b4c0b 100644 --- a/src/main/java/project/flipnote/cardset/repository/CardSetRepositoryCustom.java +++ b/src/main/java/project/flipnote/cardset/repository/CardSetRepositoryCustom.java @@ -6,7 +6,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import project.flipnote.cardset.entity.CardSet; import project.flipnote.cardset.model.CardSetInfo; import project.flipnote.group.entity.Category; @@ -19,4 +18,11 @@ Page searchByNameContainingAndCategory( ); List findAllByIdWithImageRefId(Set cardSets); + + Page searchByGroupIdAndNameContainingAndCategory( + Long groupId, + String name, + Category category, + Pageable pageable + ); } diff --git a/src/main/java/project/flipnote/cardset/repository/CardSetRepositoryCustomImpl.java b/src/main/java/project/flipnote/cardset/repository/CardSetRepositoryCustomImpl.java index 6e64f473..e5694b91 100644 --- a/src/main/java/project/flipnote/cardset/repository/CardSetRepositoryCustomImpl.java +++ b/src/main/java/project/flipnote/cardset/repository/CardSetRepositoryCustomImpl.java @@ -5,7 +5,6 @@ import java.util.List; import java.util.Set; -import org.checkerframework.checker.units.qual.C; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -22,7 +21,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import project.flipnote.cardset.entity.CardSet; import project.flipnote.cardset.entity.QCardSet; import project.flipnote.cardset.entity.QCardSetMetadata; import project.flipnote.cardset.model.CardSetInfo; @@ -47,6 +45,36 @@ public Page searchByNameContainingAndCategory( String name, Category category, Pageable pageable + ) { + return searchByGroupIdAndNameContainingAndCategory(null, name, category, pageable); + } + + public List findAllByIdWithImageRefId(Set cardSets) { + return queryFactory.select( + Projections.constructor( + CardSetInfo.class, + cardSet, + cardSet.group, + cardSet.name, + cardSet.category, + cardSet.hashtag, + cardSet.imageUrl, + imageRef.id + )) + .from(cardSet) + .where(cardSet.id.in(cardSets)) + .leftJoin(imageRef) + .on(imageRef.referenceType.eq(ReferenceType.CARD_SET) + .and(imageRef.referenceId.eq(cardSet.id))) + .fetch(); + } + + @Override + public Page searchByGroupIdAndNameContainingAndCategory( + Long groupId, + String name, + Category category, + Pageable pageable ) { List> orders = new ArrayList<>(); @@ -91,7 +119,7 @@ public Page searchByNameContainingAndCategory( imageRef.id )) .from(cardSet) - .where(buildCardSetSearchFilterConditions(name, category)) + .where(buildCardSetSearchFilterConditions(groupId, name, category)) .leftJoin(imageRef) .on(imageRef.referenceType.eq(ReferenceType.CARD_SET) .and(imageRef.referenceId.eq(cardSet.id))); @@ -109,32 +137,12 @@ public Page searchByNameContainingAndCategory( Long total = queryFactory .select(cardSet.count()) .from(cardSet) - .where(buildCardSetSearchFilterConditions(name, category)) + .where(buildCardSetSearchFilterConditions(groupId, name, category)) .fetchOne(); return new PageImpl<>(content, pageable, total != null ? total : 0L); } - public List findAllByIdWithImageRefId(Set cardSets) { - return queryFactory.select( - Projections.constructor( - CardSetInfo.class, - cardSet, - cardSet.group, - cardSet.name, - cardSet.category, - cardSet.hashtag, - cardSet.imageUrl, - imageRef.id - )) - .from(cardSet) - .where(cardSet.id.in(cardSets)) - .leftJoin(imageRef) - .on(imageRef.referenceType.eq(ReferenceType.CARD_SET) - .and(imageRef.referenceId.eq(cardSet.id))) - .fetch(); - } - private OrderSpecifier toOrderSpecifier( NumberPath path, Sort.Order order @@ -150,8 +158,13 @@ private BooleanExpression categoryEquals(Category category) { return category == null ? null : cardSet.category.eq(category); } - private BooleanExpression[] buildCardSetSearchFilterConditions(String name, Category category) { - return new BooleanExpression[]{ + private BooleanExpression groupIdEquals(Long groupId) { + return groupId == null ? null : cardSet.group.id.eq(groupId); + } + + private BooleanExpression[] buildCardSetSearchFilterConditions(Long groupId, String name, Category category) { + return new BooleanExpression[] { + groupIdEquals(groupId), nameContains(name), categoryEquals(category), cardSet.publicVisible.isTrue() diff --git a/src/main/java/project/flipnote/cardset/service/CardSetService.java b/src/main/java/project/flipnote/cardset/service/CardSetService.java index cff000c7..f01909e3 100644 --- a/src/main/java/project/flipnote/cardset/service/CardSetService.java +++ b/src/main/java/project/flipnote/cardset/service/CardSetService.java @@ -34,11 +34,10 @@ import project.flipnote.group.exception.GroupErrorCode; import project.flipnote.group.repository.GroupMemberRepository; import project.flipnote.group.repository.GroupRepository; -import project.flipnote.image.entity.Image; +import project.flipnote.group.service.GroupService; import project.flipnote.image.entity.ImageMeta; import project.flipnote.image.entity.ImageRef; import project.flipnote.image.entity.ReferenceType; -import project.flipnote.image.exception.ImageErrorCode; import project.flipnote.image.service.ImageRefService; import project.flipnote.image.service.ImageService; import project.flipnote.user.entity.UserProfile; @@ -61,6 +60,7 @@ public class CardSetService { private final CardSetMetadataRepository cardSetMetadataRepository; private final ImageService imageService; private final ImageRefService imageRefService; + private final GroupService groupService; @Value("${image.default.cardSet}") private String defaultCardSetImage; @@ -326,4 +326,25 @@ public void decrementBookmarkCount(Long cardSetId) { public void decrementBookmarkCount(List cardSetIds) { cardSetMetadataRepository.decrementBookmarkCount(cardSetIds); } + + /** + * 특정 그룹의 카드셋 목록을 페이지 단위로 조회 + * + * @param groupId 조회할 그룹의 ID + * @param req 조회 조건 및 페이징 정보를 포함한 요청 DTO + * @return 페이지 단위로 조회된 카드셋 목록 + * @author 윤정환 + */ + public PagingResponse getCardSets(long groupId, CardSetSearchRequest req) { + groupService.validateGroupExists(groupId); + + // TODO: Projection 튜닝 필요 + Page cardSetPage = cardSetRepository.searchByGroupIdAndNameContainingAndCategory( + groupId, req.getKeyword(), Category.from(req.getCategory()), req.getPageRequest() + ); + + Page res = cardSetPage.map(CardSetSummaryResponse::from); + + return PagingResponse.from(res); + } } diff --git a/src/main/java/project/flipnote/common/exception/GlobalExceptionHandler.java b/src/main/java/project/flipnote/common/exception/GlobalExceptionHandler.java index 009b970e..5eb4b02d 100644 --- a/src/main/java/project/flipnote/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/project/flipnote/common/exception/GlobalExceptionHandler.java @@ -49,7 +49,8 @@ public ResponseEntity>> handleValidatio @ExceptionHandler(MissingServletRequestParameterException.class) public ResponseEntity handleMissingServletRequestParameter( - MissingServletRequestParameterException exception) { + MissingServletRequestParameterException exception + ) { String missingParam = exception.getParameterName(); String message = String.format("필수 파라미터 '%s'가 없습니다.", missingParam); return ResponseEntity.badRequest().body(message); diff --git a/src/main/java/project/flipnote/group/service/GroupService.java b/src/main/java/project/flipnote/group/service/GroupService.java index 6479921a..97e0fe84 100644 --- a/src/main/java/project/flipnote/group/service/GroupService.java +++ b/src/main/java/project/flipnote/group/service/GroupService.java @@ -38,11 +38,9 @@ import project.flipnote.group.repository.GroupRepository; import project.flipnote.group.repository.GroupRolePermissionRepository; import project.flipnote.groupjoin.exception.GroupJoinErrorCode; -import project.flipnote.image.entity.Image; import project.flipnote.image.entity.ImageMeta; import project.flipnote.image.entity.ImageRef; import project.flipnote.image.entity.ReferenceType; -import project.flipnote.image.exception.ImageErrorCode; import project.flipnote.image.service.ImageRefService; import project.flipnote.image.service.ImageService; import project.flipnote.user.entity.UserProfile; @@ -448,6 +446,19 @@ public CursorPagingResponse findMyGroup(AuthPrinciple authPrinciple, return createGroupInfoCursorPagingResponse(req, groups); } + /** + * 지정된 그룹 ID가 존재하는지 검사합니다. + * + * @param groupId 존재 여부를 확인할 그룹의 ID + * @throws BizException 그룹이 존재하지 않을 경우 발생 + * @author 윤정환 + */ + public void validateGroupExists(Long groupId) { + if (!groupRepository.existsById(groupId)) { + throw new BizException(GroupErrorCode.GROUP_NOT_FOUND); + } + } + //리스트 조회시 response 생성 private CursorPagingResponse createGroupInfoCursorPagingResponse(GroupListRequest req, List groups) {