Skip to content

Commit

Permalink
feat: 리스트 상세 조회 시 가장 최신 댓글 1개를 응답에 포함하도록 구현 (#300)
Browse files Browse the repository at this point in the history
* feat: 리스트 상세 조회 시 가장 최신 댓글 1개를 응답에 포함하도록 구현 (#295)

* feat: 리스트 상세 조회 시에 가장 최신 댓글에 달린 답글의 총 개수도 포함 (#295)
  • Loading branch information
kdkdhoho authored Oct 12, 2024
1 parent 56a278b commit c583deb
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ public boolean isDeletedUser() {
return user.isDelete();
}

public void validateOwnerIsNotDelete() {
public void validateOwnerIsNotDeleted() {
if (this.user.isDelete()) {
throw new CustomException(DELETED_USER_EXCEPTION, "탈퇴한 회원의 리스트입니다.");
}
Expand All @@ -263,7 +263,7 @@ public void validateUpdateAuthority(User loginUser, Collaborators beforeCollabor
throw new CustomException(INVALID_ACCESS);
}

public void increaseUpdateCount(){
public void increaseUpdateCount() {
this.updateCount++;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.listywave.list.application.dto.response;

import com.listywave.collaborator.application.domain.Collaborator;
import com.listywave.list.application.domain.comment.Comment;
import com.listywave.list.application.domain.item.Item;
import com.listywave.list.application.domain.label.Label;
import com.listywave.list.application.domain.list.ListEntity;
import com.listywave.user.application.domain.User;
import jakarta.annotation.Nullable;
import java.time.LocalDateTime;
import java.util.List;
import lombok.Builder;
Expand All @@ -28,14 +30,19 @@ public record ListDetailResponse(
String backgroundPalette,
String backgroundColor,
int collectCount,
int viewCount
int viewCount,
long totalCommentCount,
@Nullable NewestComment newestComment
) {

public static ListDetailResponse of(
ListEntity list,
User owner,
boolean isCollected,
List<Collaborator> collaborators
List<Collaborator> collaborators,
long totalCommentCount,
Comment newestComment,
Long totalReplyCount
) {
return ListDetailResponse.builder()
.categoryEngName(list.getCategory().name().toLowerCase())
Expand All @@ -56,6 +63,8 @@ public static ListDetailResponse of(
.backgroundPalette(list.getBackgroundPalette().name())
.collectCount(list.getCollectCount())
.viewCount(list.getViewCount())
.totalCommentCount(totalCommentCount)
.newestComment(NewestComment.of(newestComment, totalReplyCount))
.build();
}

Expand Down Expand Up @@ -123,4 +132,29 @@ public static ItemResponse of(Item item) {
.build();
}
}

@Builder
public record NewestComment(
Long userId,
String userNickname,
String userProfileImageUrl,
LocalDateTime createdDate,
String content,
Long totalReplyCount
) {

public static NewestComment of(@Nullable Comment comment, Long totalReplyCount) {
if (comment == null) {
return null;
}
return NewestComment.builder()
.userId(comment.getUserId())
.userNickname(comment.getUserNickname())
.userProfileImageUrl(comment.getUserProfileImageUrl())
.createdDate(comment.getCreatedDate())
.content(comment.getCommentContent())
.totalReplyCount(totalReplyCount)
.build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,15 +137,19 @@ private Items createItems(List<ItemCreateRequest> itemCreateRequests) {

public ListDetailResponse getListDetail(Long listId, Long loginUserId) {
ListEntity list = listRepository.getById(listId);
list.validateOwnerIsNotDelete();
list.validateOwnerIsNotDeleted();
List<Collaborator> collaborators = collaboratorService.findAllByList(list).collaborators();

boolean isCollected = false;
if (loginUserId != null) {
User user = userRepository.getById(loginUserId);
isCollected = collectionRepository.existsByListAndUserId(list, user.getId());
}
return ListDetailResponse.of(list, list.getUser(), isCollected, collaborators);

long totalCommentCount = commentRepository.countCommentsByList(list);
Comment newestComment = commentRepository.findFirstByListOrderByCreatedDateDesc(list);
Long totalReplyCount = replyRepository.countByComment(newestComment);
return ListDetailResponse.of(list, list.getUser(), isCollected, collaborators, totalCommentCount, newestComment, totalReplyCount);
}

@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.listywave.common.exception.CustomException;
import com.listywave.list.application.domain.comment.Comment;
import com.listywave.list.application.domain.list.ListEntity;
import jakarta.annotation.Nullable;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
Expand All @@ -20,4 +21,9 @@ default Comment getById(Long id) {

@Query("select c from Comment c where c.list in :lists")
List<Comment> findAllByListIn(@Param("lists") List<ListEntity> lists);

long countCommentsByList(ListEntity list);

@Nullable
Comment findFirstByListOrderByCreatedDateDesc(ListEntity list);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ default Reply getById(Long id) {
@Modifying
@Query("delete from Reply r where r.comment in :comments")
void deleteAllByCommentIn(@Param("comments") List<Comment> comments);

Long countByComment(Comment comment);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,33 @@
import static com.listywave.acceptance.comment.CommentAcceptanceTestHelper.댓글_저장_API_호출;
import static com.listywave.acceptance.common.CommonAcceptanceHelper.HTTP_상태_코드를_검증한다;
import static com.listywave.acceptance.follow.FollowAcceptanceTestHelper.팔로우_요청_API;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.*;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.가장_좋아하는_견종_TOP3_생성_요청_데이터;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.검색_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.리스트_공개_여부_변경_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.리스트_삭제_요청_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.리스트_상세_조회를_검증한다;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.리스트_수정_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.리스트_저장_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.리스트의_아이템_순위와_히스토리의_아이템_순위를_검증한다;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.비회원_리스트_상세_조회_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.비회원_피드_리스트_조회_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.비회원_히스토리_조회_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.비회원이_피드_리스트_조회_카테고리_콜라보레이터_필터링_요청;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.비회원이_피드_리스트_조회_카테고리_필터링_요청;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.비회원이_피드_리스트_조회_콜라보레이터_필터링_요청;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.아이템_순위와_라벨을_바꾼_좋아하는_견종_TOP3_요청_데이터;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.정렬기준을_포함한_검색_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.좋아하는_라면_TOP3_생성_요청_데이터;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.최신_리스트_10개_조회_카테고리_필터링_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.카테고리로_검색_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.카테고리와_키워드로_검색_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.콜렉트_요청_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.키워드로_검색_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.키워드와_정렬기준을_포함한_검색_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.트랜딩_리스트_조회_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.팔로우한_사용자의_최신_리스트_10개_조회_API_호출;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.회원_피드_리스트_조회;
import static com.listywave.acceptance.list.ListAcceptanceTestHelper.회원용_리스트_상세_조회_API_호출;
import static com.listywave.acceptance.reply.ReplyAcceptanceTestHelper.답글_등록_API_호출;
import static com.listywave.list.fixture.ListFixture.가장_좋아하는_견종_TOP3;
import static com.listywave.list.fixture.ListFixture.가장_좋아하는_견종_TOP3_순위_변경;
Expand All @@ -20,8 +46,12 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.within;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.springframework.http.HttpStatus.*;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.NO_CONTENT;
import static org.springframework.http.HttpStatus.UNAUTHORIZED;

import com.listywave.acceptance.common.AcceptanceTest;
import com.listywave.auth.infra.kakao.response.KakaoLogoutResponse;
Expand All @@ -30,6 +60,7 @@
import com.listywave.list.application.domain.list.BackgroundColor;
import com.listywave.list.application.domain.list.BackgroundPalette;
import com.listywave.list.application.domain.list.ListEntity;
import com.listywave.list.application.dto.response.CommentCreateResponse;
import com.listywave.list.application.dto.response.ListCreateResponse;
import com.listywave.list.application.dto.response.ListDetailResponse;
import com.listywave.list.application.dto.response.ListRecentResponse;
Expand All @@ -38,6 +69,7 @@
import com.listywave.list.presentation.dto.request.ItemCreateRequest;
import com.listywave.list.presentation.dto.request.ListUpdateRequest;
import com.listywave.list.presentation.dto.request.ReplyCreateRequest;
import com.listywave.list.presentation.dto.request.comment.CommentCreateRequest;
import com.listywave.user.application.dto.FindFeedListResponse;
import com.listywave.user.application.dto.FindFeedListResponse.FeedListInfo;
import com.listywave.user.application.dto.FindFeedListResponse.ListItemsResponse;
Expand Down Expand Up @@ -210,7 +242,7 @@ class 리스트_상세_조회 {
var 결과 = 비회원_리스트_상세_조회_API_호출(동호_리스트_ID).as(ListDetailResponse.class);

// then
var 기대값 = ListDetailResponse.of(가장_좋아하는_견종_TOP3_순위_변경(동호, List.of()), 동호, false, List.of());
var 기대값 = ListDetailResponse.of(가장_좋아하는_견종_TOP3_순위_변경(동호, List.of()), 동호, false, List.of(), 0, null, 0L);
리스트_상세_조회를_검증한다(결과, 기대값);
}

Expand All @@ -230,6 +262,52 @@ class 리스트_상세_조회 {
// then
HTTP_상태_코드를_검증한다(응답, BAD_REQUEST);
}

@Test
void 댓글이_하나도_없는_리스트라면_가장_최신_댓글_정보에_값이_담기지_않는다() {
// given
var 동호 = 회원을_저장한다(동호());
var 리스트_생성_요청_데이터 = 가장_좋아하는_견종_TOP3_생성_요청_데이터(List.of());
리스트_저장_API_호출(리스트_생성_요청_데이터, 액세스_토큰을_발급한다(동호));

// when
var 동호_리스트 = 비회원_리스트_상세_조회_API_호출(1L).as(ListDetailResponse.class);

// then
assertThat(동호_리스트.totalCommentCount()).isZero();
assertThat(동호_리스트.newestComment()).isNull();
}

@Test
void 댓글이_있다면_가장_최신_댓글과_해당_댓글에_달린_답글_개수도_응답한다() {
// given
var 동호 = 회원을_저장한다(동호());
var 리스트_생성_요청_데이터 = 가장_좋아하는_견종_TOP3_생성_요청_데이터(List.of());
var 리스트_ID = 리스트_저장_API_호출(리스트_생성_요청_데이터, 액세스_토큰을_발급한다(동호))
.as(ListCreateResponse.class)
.listId();

var 정수 = 회원을_저장한다(정수());
댓글_저장_API_호출(액세스_토큰을_발급한다(정수), 리스트_ID, new CommentCreateRequest("댓글 1빠!"));
var 댓글_ID = 댓글_저장_API_호출(액세스_토큰을_발급한다(정수), 리스트_ID, new CommentCreateRequest("댓글 2빠!"))
.as(CommentCreateResponse.class)
.id();

var 유진 = 회원을_저장한다(유진());
답글_등록_API_호출(액세스_토큰을_발급한다(유진), new ReplyCreateRequest("답글 1빠!"), 리스트_ID, 댓글_ID);
답글_등록_API_호출(액세스_토큰을_발급한다(유진), new ReplyCreateRequest("답글 2빠!"), 리스트_ID, 댓글_ID);
답글_등록_API_호출(액세스_토큰을_발급한다(유진), new ReplyCreateRequest("답글 3빠!"), 리스트_ID, 댓글_ID);

// when
var 동호_리스트 = 비회원_리스트_상세_조회_API_호출(1L).as(ListDetailResponse.class);

// then
assertAll(
() -> assertThat(동호_리스트.newestComment()).isNotNull(),
() -> assertThat(동호_리스트.newestComment().content()).isEqualTo("댓글 2빠!"),
() -> assertThat(동호_리스트.newestComment().totalReplyCount()).isEqualTo(3)
);
}
}

@Nested
Expand All @@ -253,7 +331,7 @@ class 리스트_수정 {
// then
var 리스트_상세_조회_결과 = 회원용_리스트_상세_조회_API_호출(동호_액세스_토큰, 동호_리스트_ID);
ListEntity 수정된_리스트 = 가장_좋아하는_견종_TOP3_순위_변경(동호, List.of());
ListDetailResponse 기대값 = ListDetailResponse.of(수정된_리스트, 동호, false, List.of(Collaborator.init(유진, 수정된_리스트)));
ListDetailResponse 기대값 = ListDetailResponse.of(수정된_리스트, 동호, false, List.of(Collaborator.init(유진, 수정된_리스트)), 0, null, 0L);
리스트_상세_조회를_검증한다(리스트_상세_조회_결과, 기대값);
}

Expand Down Expand Up @@ -624,7 +702,7 @@ class 리스트_팀섹 {
.as(ListRecentResponse.class);

// then
var 동호_리스트 = 비회원_피드_리스트_조회_API_호출(동호 ).as(FindFeedListResponse.class).feedLists();
var 동호_리스트 = 비회원_피드_리스트_조회_API_호출(동호).as(FindFeedListResponse.class).feedLists();
var 정수_리스트 = 비회원_피드_리스트_조회_API_호출(정수).as(FindFeedListResponse.class).feedLists();
var 모든_리스트 = new ArrayList<>(동호_리스트);
모든_리스트.addAll(정수_리스트);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,10 @@ class ListEntityTest {

@Test
void 작성자가_삭제_처리됐는지_검증한다() {
assertThatNoException().isThrownBy(list::validateOwnerIsNotDelete);
assertThatNoException().isThrownBy(list::validateOwnerIsNotDeleted);

user.softDelete();
CustomException exception = assertThrows(CustomException.class, list::validateOwnerIsNotDelete);
CustomException exception = assertThrows(CustomException.class, list::validateOwnerIsNotDeleted);
assertThat(exception.getErrorCode()).isEqualTo(DELETED_USER_EXCEPTION);
}

Expand Down

0 comments on commit c583deb

Please sign in to comment.