Skip to content

Commit 3d37120

Browse files
authored
Merge pull request #51 from FlipNoteTeam/feat/cardset-search
Feat: 특정 그룹의 카드셋 조회 API 구현
2 parents 3ef1c8d + 1c2654a commit 3d37120

7 files changed

Lines changed: 97 additions & 34 deletions

File tree

src/main/java/project/flipnote/cardset/controller/GroupCardSetController.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.springframework.http.ResponseEntity;
55
import org.springframework.security.core.annotation.AuthenticationPrincipal;
66
import org.springframework.web.bind.annotation.GetMapping;
7+
import org.springframework.web.bind.annotation.ModelAttribute;
78
import org.springframework.web.bind.annotation.PathVariable;
89
import org.springframework.web.bind.annotation.PostMapping;
910
import org.springframework.web.bind.annotation.PutMapping;
@@ -15,10 +16,13 @@
1516
import lombok.RequiredArgsConstructor;
1617
import project.flipnote.cardset.controller.docs.GroupCardSetControllerDocs;
1718
import project.flipnote.cardset.model.CardSetDetailResponse;
19+
import project.flipnote.cardset.model.CardSetSearchRequest;
20+
import project.flipnote.cardset.model.CardSetSummaryResponse;
1821
import project.flipnote.cardset.model.CardSetUpdateRequest;
1922
import project.flipnote.cardset.model.CreateCardSetRequest;
2023
import project.flipnote.cardset.model.CreateCardSetResponse;
2124
import project.flipnote.cardset.service.CardSetService;
25+
import project.flipnote.common.model.response.PagingResponse;
2226
import project.flipnote.common.security.dto.AuthPrinciple;
2327

2428
@RequiredArgsConstructor
@@ -62,4 +66,13 @@ public ResponseEntity<CardSetDetailResponse> updateCardSet(
6266
return ResponseEntity.ok(res);
6367
}
6468

69+
@GetMapping
70+
public ResponseEntity<PagingResponse<CardSetSummaryResponse>> getCardSets(
71+
@PathVariable("groupId") Long groupId,
72+
@Valid @ModelAttribute CardSetSearchRequest req
73+
) {
74+
PagingResponse<CardSetSummaryResponse> res = cardSetService.getCardSets(groupId, req);
75+
76+
return ResponseEntity.ok(res);
77+
}
6578
}

src/main/java/project/flipnote/cardset/model/CardSetSummaryResponse.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package project.flipnote.cardset.model;
22

3-
import project.flipnote.cardset.entity.CardSet;
4-
53
public record CardSetSummaryResponse(
64
Long cardSetId,
75
Long groupId,

src/main/java/project/flipnote/cardset/repository/CardSetRepositoryCustom.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import org.springframework.data.domain.Page;
77
import org.springframework.data.domain.Pageable;
88

9-
import project.flipnote.cardset.entity.CardSet;
109
import project.flipnote.cardset.model.CardSetInfo;
1110
import project.flipnote.group.entity.Category;
1211

@@ -19,4 +18,11 @@ Page<CardSetInfo> searchByNameContainingAndCategory(
1918
);
2019

2120
List<CardSetInfo> findAllByIdWithImageRefId(Set<Long> cardSets);
21+
22+
Page<CardSetInfo> searchByGroupIdAndNameContainingAndCategory(
23+
Long groupId,
24+
String name,
25+
Category category,
26+
Pageable pageable
27+
);
2228
}

src/main/java/project/flipnote/cardset/repository/CardSetRepositoryCustomImpl.java

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import java.util.List;
66
import java.util.Set;
77

8-
import org.checkerframework.checker.units.qual.C;
98
import org.springframework.data.domain.Page;
109
import org.springframework.data.domain.PageImpl;
1110
import org.springframework.data.domain.Pageable;
@@ -22,7 +21,6 @@
2221

2322
import lombok.RequiredArgsConstructor;
2423
import lombok.extern.slf4j.Slf4j;
25-
import project.flipnote.cardset.entity.CardSet;
2624
import project.flipnote.cardset.entity.QCardSet;
2725
import project.flipnote.cardset.entity.QCardSetMetadata;
2826
import project.flipnote.cardset.model.CardSetInfo;
@@ -47,6 +45,36 @@ public Page<CardSetInfo> searchByNameContainingAndCategory(
4745
String name,
4846
Category category,
4947
Pageable pageable
48+
) {
49+
return searchByGroupIdAndNameContainingAndCategory(null, name, category, pageable);
50+
}
51+
52+
public List<CardSetInfo> findAllByIdWithImageRefId(Set<Long> cardSets) {
53+
return queryFactory.select(
54+
Projections.constructor(
55+
CardSetInfo.class,
56+
cardSet,
57+
cardSet.group,
58+
cardSet.name,
59+
cardSet.category,
60+
cardSet.hashtag,
61+
cardSet.imageUrl,
62+
imageRef.id
63+
))
64+
.from(cardSet)
65+
.where(cardSet.id.in(cardSets))
66+
.leftJoin(imageRef)
67+
.on(imageRef.referenceType.eq(ReferenceType.CARD_SET)
68+
.and(imageRef.referenceId.eq(cardSet.id)))
69+
.fetch();
70+
}
71+
72+
@Override
73+
public Page<CardSetInfo> searchByGroupIdAndNameContainingAndCategory(
74+
Long groupId,
75+
String name,
76+
Category category,
77+
Pageable pageable
5078
) {
5179
List<OrderSpecifier<?>> orders = new ArrayList<>();
5280

@@ -91,7 +119,7 @@ public Page<CardSetInfo> searchByNameContainingAndCategory(
91119
imageRef.id
92120
))
93121
.from(cardSet)
94-
.where(buildCardSetSearchFilterConditions(name, category))
122+
.where(buildCardSetSearchFilterConditions(groupId, name, category))
95123
.leftJoin(imageRef)
96124
.on(imageRef.referenceType.eq(ReferenceType.CARD_SET)
97125
.and(imageRef.referenceId.eq(cardSet.id)));
@@ -109,32 +137,12 @@ public Page<CardSetInfo> searchByNameContainingAndCategory(
109137
Long total = queryFactory
110138
.select(cardSet.count())
111139
.from(cardSet)
112-
.where(buildCardSetSearchFilterConditions(name, category))
140+
.where(buildCardSetSearchFilterConditions(groupId, name, category))
113141
.fetchOne();
114142

115143
return new PageImpl<>(content, pageable, total != null ? total : 0L);
116144
}
117145

118-
public List<CardSetInfo> findAllByIdWithImageRefId(Set<Long> cardSets) {
119-
return queryFactory.select(
120-
Projections.constructor(
121-
CardSetInfo.class,
122-
cardSet,
123-
cardSet.group,
124-
cardSet.name,
125-
cardSet.category,
126-
cardSet.hashtag,
127-
cardSet.imageUrl,
128-
imageRef.id
129-
))
130-
.from(cardSet)
131-
.where(cardSet.id.in(cardSets))
132-
.leftJoin(imageRef)
133-
.on(imageRef.referenceType.eq(ReferenceType.CARD_SET)
134-
.and(imageRef.referenceId.eq(cardSet.id)))
135-
.fetch();
136-
}
137-
138146
private OrderSpecifier<?> toOrderSpecifier(
139147
NumberPath<?> path,
140148
Sort.Order order
@@ -150,8 +158,13 @@ private BooleanExpression categoryEquals(Category category) {
150158
return category == null ? null : cardSet.category.eq(category);
151159
}
152160

153-
private BooleanExpression[] buildCardSetSearchFilterConditions(String name, Category category) {
154-
return new BooleanExpression[]{
161+
private BooleanExpression groupIdEquals(Long groupId) {
162+
return groupId == null ? null : cardSet.group.id.eq(groupId);
163+
}
164+
165+
private BooleanExpression[] buildCardSetSearchFilterConditions(Long groupId, String name, Category category) {
166+
return new BooleanExpression[] {
167+
groupIdEquals(groupId),
155168
nameContains(name),
156169
categoryEquals(category),
157170
cardSet.publicVisible.isTrue()

src/main/java/project/flipnote/cardset/service/CardSetService.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,10 @@
3434
import project.flipnote.group.exception.GroupErrorCode;
3535
import project.flipnote.group.repository.GroupMemberRepository;
3636
import project.flipnote.group.repository.GroupRepository;
37-
import project.flipnote.image.entity.Image;
37+
import project.flipnote.group.service.GroupService;
3838
import project.flipnote.image.entity.ImageMeta;
3939
import project.flipnote.image.entity.ImageRef;
4040
import project.flipnote.image.entity.ReferenceType;
41-
import project.flipnote.image.exception.ImageErrorCode;
4241
import project.flipnote.image.service.ImageRefService;
4342
import project.flipnote.image.service.ImageService;
4443
import project.flipnote.user.entity.UserProfile;
@@ -61,6 +60,7 @@ public class CardSetService {
6160
private final CardSetMetadataRepository cardSetMetadataRepository;
6261
private final ImageService imageService;
6362
private final ImageRefService imageRefService;
63+
private final GroupService groupService;
6464

6565
@Value("${image.default.cardSet}")
6666
private String defaultCardSetImage;
@@ -326,4 +326,25 @@ public void decrementBookmarkCount(Long cardSetId) {
326326
public void decrementBookmarkCount(List<Long> cardSetIds) {
327327
cardSetMetadataRepository.decrementBookmarkCount(cardSetIds);
328328
}
329+
330+
/**
331+
* 특정 그룹의 카드셋 목록을 페이지 단위로 조회
332+
*
333+
* @param groupId 조회할 그룹의 ID
334+
* @param req 조회 조건 및 페이징 정보를 포함한 요청 DTO
335+
* @return 페이지 단위로 조회된 카드셋 목록
336+
* @author 윤정환
337+
*/
338+
public PagingResponse<CardSetSummaryResponse> getCardSets(long groupId, CardSetSearchRequest req) {
339+
groupService.validateGroupExists(groupId);
340+
341+
// TODO: Projection 튜닝 필요
342+
Page<CardSetInfo> cardSetPage = cardSetRepository.searchByGroupIdAndNameContainingAndCategory(
343+
groupId, req.getKeyword(), Category.from(req.getCategory()), req.getPageRequest()
344+
);
345+
346+
Page<CardSetSummaryResponse> res = cardSetPage.map(CardSetSummaryResponse::from);
347+
348+
return PagingResponse.from(res);
349+
}
329350
}

src/main/java/project/flipnote/common/exception/GlobalExceptionHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ public ResponseEntity<ApiResponse<List<ApiResponse.FieldError>>> handleValidatio
4949

5050
@ExceptionHandler(MissingServletRequestParameterException.class)
5151
public ResponseEntity<String> handleMissingServletRequestParameter(
52-
MissingServletRequestParameterException exception) {
52+
MissingServletRequestParameterException exception
53+
) {
5354
String missingParam = exception.getParameterName();
5455
String message = String.format("필수 파라미터 '%s'가 없습니다.", missingParam);
5556
return ResponseEntity.badRequest().body(message);

src/main/java/project/flipnote/group/service/GroupService.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,9 @@
3838
import project.flipnote.group.repository.GroupRepository;
3939
import project.flipnote.group.repository.GroupRolePermissionRepository;
4040
import project.flipnote.groupjoin.exception.GroupJoinErrorCode;
41-
import project.flipnote.image.entity.Image;
4241
import project.flipnote.image.entity.ImageMeta;
4342
import project.flipnote.image.entity.ImageRef;
4443
import project.flipnote.image.entity.ReferenceType;
45-
import project.flipnote.image.exception.ImageErrorCode;
4644
import project.flipnote.image.service.ImageRefService;
4745
import project.flipnote.image.service.ImageService;
4846
import project.flipnote.user.entity.UserProfile;
@@ -448,6 +446,19 @@ public CursorPagingResponse<GroupInfo> findMyGroup(AuthPrinciple authPrinciple,
448446
return createGroupInfoCursorPagingResponse(req, groups);
449447
}
450448

449+
/**
450+
* 지정된 그룹 ID가 존재하는지 검사합니다.
451+
*
452+
* @param groupId 존재 여부를 확인할 그룹의 ID
453+
* @throws BizException 그룹이 존재하지 않을 경우 발생
454+
* @author 윤정환
455+
*/
456+
public void validateGroupExists(Long groupId) {
457+
if (!groupRepository.existsById(groupId)) {
458+
throw new BizException(GroupErrorCode.GROUP_NOT_FOUND);
459+
}
460+
}
461+
451462
//리스트 조회시 response 생성
452463
private CursorPagingResponse<GroupInfo> createGroupInfoCursorPagingResponse(GroupListRequest req,
453464
List<GroupInfo> groups) {

0 commit comments

Comments
 (0)