diff --git a/src/main/java/com/swyp/picke/domain/perspective/repository/PerspectiveRepository.java b/src/main/java/com/swyp/picke/domain/perspective/repository/PerspectiveRepository.java index c95f04f..fd7d3b3 100644 --- a/src/main/java/com/swyp/picke/domain/perspective/repository/PerspectiveRepository.java +++ b/src/main/java/com/swyp/picke/domain/perspective/repository/PerspectiveRepository.java @@ -12,6 +12,10 @@ public interface PerspectiveRepository extends JpaRepository List findByBattleIdAndUserIdOrderByCreatedAtDesc(Long battleId, Long userId); + List findByUserIdOrderByCreatedAtDesc(Long userId, Pageable pageable); + + long countByUserId(Long userId); + List findByBattleIdAndStatusOrderByCreatedAtDesc(Long battleId, PerspectiveStatus status, Pageable pageable); List findByBattleIdAndStatusAndCreatedAtBeforeOrderByCreatedAtDesc(Long battleId, PerspectiveStatus status, LocalDateTime cursor, Pageable pageable); diff --git a/src/main/java/com/swyp/picke/domain/perspective/service/PerspectiveQueryService.java b/src/main/java/com/swyp/picke/domain/perspective/service/PerspectiveQueryService.java index e1defc9..b6fec9c 100644 --- a/src/main/java/com/swyp/picke/domain/perspective/service/PerspectiveQueryService.java +++ b/src/main/java/com/swyp/picke/domain/perspective/service/PerspectiveQueryService.java @@ -1,9 +1,11 @@ package com.swyp.picke.domain.perspective.service; +import com.swyp.picke.domain.perspective.entity.Perspective; import com.swyp.picke.domain.perspective.entity.PerspectiveComment; import com.swyp.picke.domain.perspective.entity.PerspectiveLike; import com.swyp.picke.domain.perspective.repository.PerspectiveCommentRepository; import com.swyp.picke.domain.perspective.repository.PerspectiveLikeRepository; +import com.swyp.picke.domain.perspective.repository.PerspectiveRepository; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; @@ -18,9 +20,10 @@ public class PerspectiveQueryService { private final PerspectiveCommentRepository perspectiveCommentRepository; private final PerspectiveLikeRepository perspectiveLikeRepository; + private final PerspectiveRepository perspectiveRepository; - public List findUserComments(Long userId, int offset, int size) { - PageRequest pageable = PageRequest.of(offset / size, size); + public List findUserComments(Long userId, int limit) { + PageRequest pageable = PageRequest.of(0, limit); return perspectiveCommentRepository.findByUserIdOrderByCreatedAtDesc(userId, pageable); } @@ -28,6 +31,15 @@ public long countUserComments(Long userId) { return perspectiveCommentRepository.countByUserId(userId); } + public List findUserPerspectives(Long userId, int limit) { + PageRequest pageable = PageRequest.of(0, limit); + return perspectiveRepository.findByUserIdOrderByCreatedAtDesc(userId, pageable); + } + + public long countUserPerspectives(Long userId) { + return perspectiveRepository.countByUserId(userId); + } + public List findUserLikes(Long userId, int offset, int size) { PageRequest pageable = PageRequest.of(offset / size, size); return perspectiveLikeRepository.findByUserIdOrderByCreatedAtDesc(userId, pageable); diff --git a/src/main/java/com/swyp/picke/domain/user/enums/ActivityType.java b/src/main/java/com/swyp/picke/domain/user/enums/ActivityType.java index 2fb5219..e853d99 100644 --- a/src/main/java/com/swyp/picke/domain/user/enums/ActivityType.java +++ b/src/main/java/com/swyp/picke/domain/user/enums/ActivityType.java @@ -2,5 +2,6 @@ public enum ActivityType { COMMENT, - LIKE + LIKE, + PERSPECTIVE } 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 9f6d230..3ab60bd 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 @@ -33,7 +33,9 @@ import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.stream.Stream; import java.util.Map; @Service @@ -190,25 +192,40 @@ public ContentActivityListResponse getContentActivities(Integer offset, Integer } private ContentActivityListResponse buildCommentActivities(User user, int pageOffset, int pageSize) { - List comments = perspectiveQueryService.findUserComments(user.getId(), pageOffset, pageSize); - long totalCount = perspectiveQueryService.countUserComments(user.getId()); + int limit = pageOffset + pageSize; + + List comments = perspectiveQueryService.findUserComments(user.getId(), limit); + List myPerspectives = perspectiveQueryService.findUserPerspectives(user.getId(), limit); UserProfile profile = userService.findUserProfile(user.getId()); String myCharacterImageUrl = resolveCharacterImageUrl(profile.getCharacterType()); - List perspectives = comments.stream().map(PerspectiveComment::getPerspective).toList(); - Map battleMap = loadBattles(perspectives); - Map optionMap = loadOptions(perspectives); + List lookupTargets = new ArrayList<>(comments.stream().map(PerspectiveComment::getPerspective).toList()); + lookupTargets.addAll(myPerspectives); + Map battleMap = loadBattles(lookupTargets); + Map optionMap = loadOptions(lookupTargets); - List items = comments.stream() + 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); - }) + }); + + Stream perspectiveItems = myPerspectives.stream() + .map(p -> toActivityItem(p.getId().toString(), ActivityType.PERSPECTIVE, p, + battleMap.get(p.getBattle().getId()), optionMap.get(p.getOption().getId()), + p.getContent(), p.getCreatedAt(), myCharacterImageUrl)); + + List items = Stream.concat(commentItems, perspectiveItems) + .sorted(Comparator.comparing(ContentActivityListResponse.ContentActivityItem::createdAt).reversed()) + .skip(pageOffset) + .limit(pageSize) .toList(); + long totalCount = perspectiveQueryService.countUserComments(user.getId()) + + perspectiveQueryService.countUserPerspectives(user.getId()); int nextOffset = pageOffset + pageSize; boolean hasNext = nextOffset < totalCount; return new ContentActivityListResponse(items, hasNext ? nextOffset : null, hasNext); 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 0854dae..1956966 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 @@ -248,8 +248,10 @@ void getContentActivities_returns_comments() { when(userService.findCurrentUser()).thenReturn(user); when(userService.findUserProfile(1L)).thenReturn(profile); - when(perspectiveQueryService.findUserComments(1L, 0, 20)).thenReturn(List.of(comment)); + when(perspectiveQueryService.findUserComments(1L, 20)).thenReturn(List.of(comment)); + when(perspectiveQueryService.findUserPerspectives(1L, 20)).thenReturn(List.of()); when(perspectiveQueryService.countUserComments(1L)).thenReturn(1L); + 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(userService.findSummaryById(1L)).thenReturn(new UserSummary("tag", "nick", "OWL")); @@ -261,6 +263,60 @@ void getContentActivities_returns_comments() { assertThat(response.items().get(0).content()).isEqualTo("댓글"); } + @Test + @DisplayName("COMMENT 타입으로 조회하면 내가 작성한 관점과 댓글이 최신순으로 병합되어 반환된다") + void getContentActivities_merges_perspectives_and_comments_by_created_at() { + User user = createUser(1L, "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(); + + Perspective myPerspective = Perspective.builder() + .battle(battle) + .user(user) + .option(option) + .content("내가 쓴 관점") + .build(); + ReflectionTestUtils.setField(myPerspective, "id", generateId()); + ReflectionTestUtils.setField(myPerspective, "createdAt", LocalDateTime.now()); + + Perspective othersPerspective = Perspective.builder() + .battle(battle) + .user(user) + .option(option) + .content("다른 사람 관점") + .build(); + ReflectionTestUtils.setField(othersPerspective, "id", generateId()); + + PerspectiveComment comment = PerspectiveComment.builder() + .perspective(othersPerspective) + .user(user) + .content("내가 남긴 댓글") + .build(); + ReflectionTestUtils.setField(comment, "id", generateId()); + ReflectionTestUtils.setField(comment, "createdAt", LocalDateTime.now().minusMinutes(1)); + + when(userService.findCurrentUser()).thenReturn(user); + when(userService.findUserProfile(1L)).thenReturn(profile); + when(perspectiveQueryService.findUserComments(1L, 20)).thenReturn(List.of(comment)); + when(perspectiveQueryService.findUserPerspectives(1L, 20)).thenReturn(List.of(myPerspective)); + when(perspectiveQueryService.countUserComments(1L)).thenReturn(1L); + when(perspectiveQueryService.countUserPerspectives(1L)).thenReturn(1L); + when(battleQueryService.findBattlesByIds(List.of(battleId))).thenReturn(Map.of(battleId, battle)); + when(battleQueryService.findOptionsByIds(List.of(optionId))).thenReturn(Map.of(optionId, option)); + when(userService.findSummaryById(1L)).thenReturn(new UserSummary("tag", "nick", "OWL")); + + ContentActivityListResponse response = mypageService.getContentActivities(null, null, ActivityType.COMMENT); + + assertThat(response.items()).hasSize(2); + assertThat(response.items().get(0).activityType()).isEqualTo(ActivityType.PERSPECTIVE); + assertThat(response.items().get(0).content()).isEqualTo("내가 쓴 관점"); + assertThat(response.items().get(1).activityType()).isEqualTo(ActivityType.COMMENT); + assertThat(response.items().get(1).content()).isEqualTo("내가 남긴 댓글"); + } + @Test @DisplayName("LIKE 타입으로 좋아요활동을 반환한다") void getContentActivities_returns_likes() {