diff --git a/src/main/java/com/swyp/picke/domain/user/service/MypageService.java b/src/main/java/com/swyp/picke/domain/user/service/MypageService.java index 3347e33..d148977 100644 --- a/src/main/java/com/swyp/picke/domain/user/service/MypageService.java +++ b/src/main/java/com/swyp/picke/domain/user/service/MypageService.java @@ -203,19 +203,27 @@ private ContentActivityListResponse buildCommentActivities(User user, int pageOf lookupTargets.addAll(myPerspectives); Map battleMap = loadBattles(lookupTargets); Map optionMap = loadOptions(lookupTargets); + List commentedBattleIds = comments.stream() + .map(comment -> comment.getPerspective().getBattle().getId()) + .distinct() + .toList(); + Map commentOptionMap = voteQueryService.findPostVoteOptionsByBattleIds( + user.getId(), commentedBattleIds); Stream commentItems = comments.stream() .map(comment -> { Perspective p = comment.getPerspective(); return toActivityItem(comment.getId().toString(), ActivityType.COMMENT, p, - battleMap.get(p.getBattle().getId()), optionMap.get(p.getOption().getId()), - comment.getContent(), comment.getCreatedAt(), myCharacterImageUrl); + comment.getUser().getId(), + battleMap.get(p.getBattle().getId()), commentOptionMap.get(p.getBattle().getId()), + comment.getContent(), comment.getLikeCount(), comment.getCreatedAt(), myCharacterImageUrl); }); Stream perspectiveItems = myPerspectives.stream() .map(p -> toActivityItem(p.getId().toString(), ActivityType.PERSPECTIVE, p, + p.getUser().getId(), battleMap.get(p.getBattle().getId()), optionMap.get(p.getOption().getId()), - p.getContent(), p.getCreatedAt(), myCharacterImageUrl)); + p.getContent(), p.getLikeCount(), p.getCreatedAt(), myCharacterImageUrl)); List items = Stream.concat(commentItems, perspectiveItems) .sorted(Comparator.comparing(ContentActivityListResponse.ContentActivityItem::createdAt).reversed()) @@ -244,8 +252,9 @@ private ContentActivityListResponse buildLikeActivities(User user, int pageOffse UserSummary perspectiveAuthor = userService.findSummaryById(p.getUser().getId()); String authorCharacterImageUrl = resolveCharacterImageUrl(perspectiveAuthor.characterType()); return toActivityItem(like.getId().toString(), ActivityType.LIKE, p, + p.getUser().getId(), battleMap.get(p.getBattle().getId()), optionMap.get(p.getOption().getId()), - p.getContent(), like.getCreatedAt(), authorCharacterImageUrl); + p.getContent(), p.getLikeCount(), like.getCreatedAt(), authorCharacterImageUrl); }) .toList(); @@ -256,10 +265,11 @@ private ContentActivityListResponse buildLikeActivities(User user, int pageOffse private ContentActivityListResponse.ContentActivityItem toActivityItem( String activityId, ActivityType activityType, Perspective perspective, - Battle battle, BattleOption option, String content, LocalDateTime createdAt, + Long authorUserId, + Battle battle, BattleOption option, String content, int likeCount, LocalDateTime createdAt, String characterImageUrl) { - UserSummary author = userService.findSummaryById(perspective.getUser().getId()); + UserSummary author = userService.findSummaryById(authorUserId); ContentActivityListResponse.AuthorInfo authorInfo = new ContentActivityListResponse.AuthorInfo( author.userTag(), author.nickname(), @@ -279,7 +289,7 @@ private ContentActivityListResponse.ContentActivityItem toActivityItem( voteSide, optionTitle, content, - perspective.getLikeCount(), + likeCount, createdAt ); } diff --git a/src/main/java/com/swyp/picke/domain/vote/repository/BattleVoteRepository.java b/src/main/java/com/swyp/picke/domain/vote/repository/BattleVoteRepository.java index 9e19caf..f527369 100644 --- a/src/main/java/com/swyp/picke/domain/vote/repository/BattleVoteRepository.java +++ b/src/main/java/com/swyp/picke/domain/vote/repository/BattleVoteRepository.java @@ -22,6 +22,13 @@ public interface BattleVoteRepository extends JpaRepository { @Query("SELECT v FROM BattleVote v LEFT JOIN FETCH v.postVoteOption WHERE v.battle.id = :battleId AND v.user.id = :userId") Optional findByBattleIdAndUserIdWithOption(@Param("battleId") Long battleId, @Param("userId") Long userId); + @Query("SELECT v FROM BattleVote v JOIN FETCH v.battle LEFT JOIN FETCH v.postVoteOption " + + "WHERE v.user.id = :userId AND v.battle.id IN :battleIds") + List findByUserIdAndBattleIdInWithPostVoteOption( + @Param("userId") Long userId, + @Param("battleIds") List battleIds + ); + Optional findByBattleAndUser(Battle battle, User user); long countByBattle(Battle battle); diff --git a/src/main/java/com/swyp/picke/domain/vote/service/VoteQueryService.java b/src/main/java/com/swyp/picke/domain/vote/service/VoteQueryService.java index 210027a..1b96099 100644 --- a/src/main/java/com/swyp/picke/domain/vote/service/VoteQueryService.java +++ b/src/main/java/com/swyp/picke/domain/vote/service/VoteQueryService.java @@ -5,7 +5,9 @@ import com.swyp.picke.domain.vote.entity.BattleVote; import com.swyp.picke.domain.vote.repository.BattleVoteRepository; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; @@ -39,6 +41,20 @@ public long countUserVotes(Long userId, VoteSide voteSide) { return battleVoteRepository.countByUserIdAndPostVoteOptionIsNotNull(userId); } + public Map findPostVoteOptionsByBattleIds(Long userId, List battleIds) { + if (battleIds.isEmpty()) { + return Map.of(); + } + + return battleVoteRepository.findByUserIdAndBattleIdInWithPostVoteOption(userId, battleIds).stream() + .filter(vote -> vote.getPostVoteOption() != null) + .collect(Collectors.toMap( + vote -> vote.getBattle().getId(), + BattleVote::getPostVoteOption, + (first, second) -> second + )); + } + public long countTotalParticipation(Long userId) { return battleVoteRepository.countByUserId(userId); } diff --git a/src/test/java/com/swyp/picke/domain/user/service/MypageServiceTest.java b/src/test/java/com/swyp/picke/domain/user/service/MypageServiceTest.java index 5ac2999..275a235 100644 --- a/src/test/java/com/swyp/picke/domain/user/service/MypageServiceTest.java +++ b/src/test/java/com/swyp/picke/domain/user/service/MypageServiceTest.java @@ -52,6 +52,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -223,22 +224,25 @@ void getBattleRecords_applies_vote_side_filter() { } @Test - @DisplayName("COMMENT 타입으로 댓글활동을 반환한다") - void getContentActivities_returns_comments() { + @DisplayName("COMMENT 활동은 댓글의 작성자, 좋아요 수, 선택지, 작성 시간을 반환한다") + void getContentActivities_returns_comment_data() { User user = createUser(1L, "tag"); + User perspectiveAuthor = createUser(2L, "perspective-author-tag"); UserProfile profile = createProfile(user, "nick", CharacterType.OWL); Battle battle = createBattle("배틀"); Long battleId = battle.getId(); BattleOption option = createOption(battle, BattleOptionLabel.A); Long optionId = option.getId(); + BattleOption commentOption = createOption(battle, BattleOptionLabel.B); Perspective perspective = Perspective.builder() .battle(battle) - .user(user) + .user(perspectiveAuthor) .option(option) .content("관점 내용") .build(); ReflectionTestUtils.setField(perspective, "id", generateId()); + ReflectionTestUtils.setField(perspective, "likeCount", 7); PerspectiveComment comment = PerspectiveComment.builder() .perspective(perspective) @@ -246,7 +250,9 @@ void getContentActivities_returns_comments() { .content("댓글") .build(); ReflectionTestUtils.setField(comment, "id", generateId()); - ReflectionTestUtils.setField(comment, "createdAt", LocalDateTime.now()); + ReflectionTestUtils.setField(comment, "likeCount", 2); + LocalDateTime commentCreatedAt = LocalDateTime.now(); + ReflectionTestUtils.setField(comment, "createdAt", commentCreatedAt); when(userService.findCurrentUser()).thenReturn(user); when(userService.findUserProfile(1L)).thenReturn(profile); @@ -256,6 +262,8 @@ void getContentActivities_returns_comments() { when(perspectiveQueryService.countUserPerspectives(1L)).thenReturn(0L); when(battleQueryService.findBattlesByIds(List.of(battleId))).thenReturn(Map.of(battleId, battle)); when(battleQueryService.findOptionsByIds(List.of(optionId))).thenReturn(Map.of(optionId, option)); + when(voteQueryService.findPostVoteOptionsByBattleIds(1L, List.of(battleId))) + .thenReturn(Map.of(battleId, commentOption)); when(userService.findSummaryById(1L)).thenReturn(new UserSummary("tag", "nick", "OWL")); ContentActivityListResponse response = mypageService.getContentActivities(null, null, ActivityType.COMMENT); @@ -263,6 +271,14 @@ void getContentActivities_returns_comments() { assertThat(response.items()).hasSize(1); assertThat(response.items().get(0).activityType()).isEqualTo(ActivityType.COMMENT); assertThat(response.items().get(0).content()).isEqualTo("댓글"); + assertThat(response.items().get(0).author().userTag()).isEqualTo("tag"); + assertThat(response.items().get(0).author().nickname()).isEqualTo("nick"); + assertThat(response.items().get(0).likeCount()).isEqualTo(2); + assertThat(response.items().get(0).voteSide()).isEqualTo(VoteSide.CON); + assertThat(response.items().get(0).optionTitle()).isEqualTo("B"); + assertThat(response.items().get(0).createdAt()).isEqualTo(commentCreatedAt); + verify(userService).findSummaryById(1L); + verify(userService, never()).findSummaryById(2L); } @Test diff --git a/src/test/java/com/swyp/picke/domain/vote/service/VoteQueryServiceTest.java b/src/test/java/com/swyp/picke/domain/vote/service/VoteQueryServiceTest.java index d34caf1..763e5c7 100644 --- a/src/test/java/com/swyp/picke/domain/vote/service/VoteQueryServiceTest.java +++ b/src/test/java/com/swyp/picke/domain/vote/service/VoteQueryServiceTest.java @@ -1,6 +1,9 @@ package com.swyp.picke.domain.vote.service; +import com.swyp.picke.domain.battle.entity.Battle; +import com.swyp.picke.domain.battle.entity.BattleOption; import com.swyp.picke.domain.user.enums.VoteSide; +import com.swyp.picke.domain.vote.entity.BattleVote; import com.swyp.picke.domain.vote.repository.BattleVoteRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -10,12 +13,14 @@ import org.mockito.junit.jupiter.MockitoExtension; import java.util.List; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.mock; @ExtendWith(MockitoExtension.class) class VoteQueryServiceTest { @@ -80,4 +85,22 @@ void countTotalParticipation_includes_pre_vote_only_records() { assertThat(count).isEqualTo(5L); verify(battleVoteRepository).countByUserId(1L); } + + @Test + @DisplayName("배틀별 사후 투표 선택지를 조회한다") + void findPostVoteOptionsByBattleIds_returns_post_vote_options() { + Battle battle = mock(Battle.class); + BattleOption postVoteOption = mock(BattleOption.class); + BattleVote vote = mock(BattleVote.class); + + when(battle.getId()).thenReturn(10L); + when(vote.getBattle()).thenReturn(battle); + when(vote.getPostVoteOption()).thenReturn(postVoteOption); + when(battleVoteRepository.findByUserIdAndBattleIdInWithPostVoteOption(1L, List.of(10L))) + .thenReturn(List.of(vote)); + + Map result = voteQueryService.findPostVoteOptionsByBattleIds(1L, List.of(10L)); + + assertThat(result).containsEntry(10L, postVoteOption); + } }