From 703ab02fc4278b83e5924429b40732957930925f Mon Sep 17 00:00:00 2001 From: onuyyy Date: Mon, 23 Feb 2026 11:38:13 +0900 Subject: [PATCH] =?UTF-8?q?refactor=20:=20=EB=A9=94=EC=9D=B8=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=95=BD=EC=86=8D=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=9D=91=EB=8B=B5=20=EA=B0=92=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dokdok/meeting/api/MyMeetingListApi.java | 3 +- .../dto/MyMeetingListItemResponse.java | 5 +- .../repository/MeetingMemberRepository.java | 46 +++++++++++++++++++ .../meeting/service/MeetingService.java | 17 +++++-- .../topic/repository/TopicRepository.java | 11 +++++ .../meeting/service/MeetingServiceTest.java | 10 +++- 6 files changed, 84 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/dokdok/meeting/api/MyMeetingListApi.java b/src/main/java/com/dokdok/meeting/api/MyMeetingListApi.java index 8efdc864..dcc6cbed 100644 --- a/src/main/java/com/dokdok/meeting/api/MyMeetingListApi.java +++ b/src/main/java/com/dokdok/meeting/api/MyMeetingListApi.java @@ -62,7 +62,8 @@ public interface MyMeetingListApi { "endDateTime": "2025-02-01T16:00:00", "meetingStatus": "CONFIRMED", "myRole": "LEADER", - "progressStatus": "UPCOMING" + "progressStatus": "UPCOMING", + "preOpinionTemplateConfirmed": true } ], "totalCount": 8, diff --git a/src/main/java/com/dokdok/meeting/dto/MyMeetingListItemResponse.java b/src/main/java/com/dokdok/meeting/dto/MyMeetingListItemResponse.java index 4aea3cb3..835454b0 100644 --- a/src/main/java/com/dokdok/meeting/dto/MyMeetingListItemResponse.java +++ b/src/main/java/com/dokdok/meeting/dto/MyMeetingListItemResponse.java @@ -38,6 +38,9 @@ public record MyMeetingListItemResponse( MeetingMyRole myRole, @Schema(description = "약속 진행 상태(시간 기준)", example = "UPCOMING") - MeetingProgressStatus progressStatus + MeetingProgressStatus progressStatus, + + @Schema(description = "사전 의견 템플릿 확정 여부", example = "true") + boolean preOpinionTemplateConfirmed ) { } diff --git a/src/main/java/com/dokdok/meeting/repository/MeetingMemberRepository.java b/src/main/java/com/dokdok/meeting/repository/MeetingMemberRepository.java index 95fb54c8..da6c4fee 100644 --- a/src/main/java/com/dokdok/meeting/repository/MeetingMemberRepository.java +++ b/src/main/java/com/dokdok/meeting/repository/MeetingMemberRepository.java @@ -3,6 +3,7 @@ import com.dokdok.meeting.entity.MeetingMember; import com.dokdok.meeting.entity.MeetingStatus; import com.dokdok.meeting.entity.Meeting; +import com.dokdok.retrospective.entity.PersonalMeetingRetrospective; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; @@ -105,6 +106,23 @@ int countMyMeetingsByStatus( @Param("meetingStatus") MeetingStatus meetingStatus ); + @Query(""" + SELECT count(mm) FROM MeetingMember mm + JOIN mm.meeting m + WHERE mm.user.id = :userId + AND mm.canceledAt IS NULL + AND m.meetingStatus = :meetingStatus + AND NOT EXISTS ( + SELECT 1 FROM PersonalMeetingRetrospective pmr + WHERE pmr.meeting = m + AND pmr.user.id = :userId + ) + """) + int countMyMeetingsByStatusWithoutPersonalRetrospective( + @Param("userId") Long userId, + @Param("meetingStatus") MeetingStatus meetingStatus + ); + @Query(""" SELECT count(mm) FROM MeetingMember mm JOIN mm.meeting m @@ -216,6 +234,34 @@ List findMyMeetingsByStatusAfterCursor( Pageable pageable ); + @Query( + value = """ + SELECT m FROM MeetingMember mm + JOIN mm.meeting m + JOIN FETCH m.book + JOIN FETCH m.gathering + WHERE mm.user.id = :userId + AND mm.canceledAt IS NULL + AND m.meetingStatus = :meetingStatus + AND NOT EXISTS ( + SELECT 1 FROM PersonalMeetingRetrospective pmr + WHERE pmr.meeting = m + AND pmr.user.id = :userId + ) + AND (CAST(:cursorStartDateTime AS timestamp) IS NULL + OR m.meetingStartDate > :cursorStartDateTime + OR (m.meetingStartDate = :cursorStartDateTime AND m.id > :cursorMeetingId)) + ORDER BY m.meetingStartDate ASC, m.id ASC + """ + ) + List findMyDoneMeetingsWithoutPersonalRetrospectiveAfterCursor( + @Param("userId") Long userId, + @Param("meetingStatus") MeetingStatus meetingStatus, + @Param("cursorStartDateTime") LocalDateTime cursorStartDateTime, + @Param("cursorMeetingId") Long cursorMeetingId, + Pageable pageable + ); + @Query( value = """ SELECT m FROM MeetingMember mm diff --git a/src/main/java/com/dokdok/meeting/service/MeetingService.java b/src/main/java/com/dokdok/meeting/service/MeetingService.java index 8b0d69f5..b3ffdac8 100644 --- a/src/main/java/com/dokdok/meeting/service/MeetingService.java +++ b/src/main/java/com/dokdok/meeting/service/MeetingService.java @@ -666,7 +666,7 @@ public CursorResponse getMyMeeting cursorMeetingId(cursor), pageable ); - case DONE -> meetingMemberRepository.findMyMeetingsByStatusAfterCursor( + case DONE -> meetingMemberRepository.findMyDoneMeetingsWithoutPersonalRetrospectiveAfterCursor( userId, MeetingStatus.DONE, cursorStartDateTime(cursor), @@ -691,7 +691,7 @@ public CursorResponse getMyMeeting now, now.plusDays(3) ); - case DONE -> meetingMemberRepository.countMyMeetingsByStatus( + case DONE -> meetingMemberRepository.countMyMeetingsByStatusWithoutPersonalRetrospective( userId, MeetingStatus.DONE ); @@ -724,7 +724,7 @@ public MyMeetingTabCountsResponse getMyMeetingTabCounts() { now, now.plusDays(3) ); - int doneCount = meetingMemberRepository.countMyMeetingsByStatus( + int doneCount = meetingMemberRepository.countMyMeetingsByStatusWithoutPersonalRetrospective( userId, MeetingStatus.DONE ); @@ -899,6 +899,13 @@ private List buildMyMeetingItems(List meetin LocalDateTime now = LocalDateTime.now(); List items = new ArrayList<>(); + List meetingIds = meetings.stream() + .map(Meeting::getId) + .toList(); + Set meetingIdsWithConfirmedTopics = new HashSet<>( + topicRepository.findMeetingIdsWithConfirmedTopics(meetingIds) + ); + for (Meeting meeting : meetings) { MeetingProgressStatus progressStatus = resolveProgressStatus( meeting.getMeetingStartDate(), @@ -906,6 +913,7 @@ private List buildMyMeetingItems(List meetin now ); MeetingMyRole myRole = resolveMyMeetingRole(meeting, userId); + boolean preOpinionTemplateConfirmed = meetingIdsWithConfirmedTopics.contains(meeting.getId()); items.add(new MyMeetingListItemResponse( meeting.getId(), @@ -918,7 +926,8 @@ private List buildMyMeetingItems(List meetin meeting.getMeetingEndDate(), meeting.getMeetingStatus(), myRole, - progressStatus + progressStatus, + preOpinionTemplateConfirmed )); } return items; diff --git a/src/main/java/com/dokdok/topic/repository/TopicRepository.java b/src/main/java/com/dokdok/topic/repository/TopicRepository.java index f5c8af8b..a8df5a5f 100644 --- a/src/main/java/com/dokdok/topic/repository/TopicRepository.java +++ b/src/main/java/com/dokdok/topic/repository/TopicRepository.java @@ -228,6 +228,17 @@ List findTopicsInfoByMeetingIds( @Param("meetingIds") List meetingIds ); + @Query(""" + SELECT DISTINCT t.meeting.id + FROM Topic t + WHERE t.meeting.id IN :meetingIds + AND t.topicStatus = com.dokdok.topic.entity.TopicStatus.CONFIRMED + AND t.deletedAt IS NULL + """) + List findMeetingIdsWithConfirmedTopics( + @Param("meetingIds") List meetingIds + ); + @Query(""" SELECT MAX(t.updatedAt) FROM Topic t diff --git a/src/test/java/com/dokdok/meeting/service/MeetingServiceTest.java b/src/test/java/com/dokdok/meeting/service/MeetingServiceTest.java index d04f792c..b827d639 100644 --- a/src/test/java/com/dokdok/meeting/service/MeetingServiceTest.java +++ b/src/test/java/com/dokdok/meeting/service/MeetingServiceTest.java @@ -1438,6 +1438,8 @@ void givenNullFilter_whenGetMyMeetingList_thenReturnItems() { any(), any() )).willReturn(List.of(myMeeting)); + given(topicRepository.findMeetingIdsWithConfirmedTopics(List.of(myMeeting.getId()))) + .willReturn(List.of(myMeeting.getId())); try (MockedStatic mock = mockStatic(SecurityUtil.class)) { mock.when(SecurityUtil::getCurrentUserId).thenReturn(userId); @@ -1482,6 +1484,8 @@ void givenUpcomingFilter_whenGetMyMeetingList_thenReturnUpcomingItems() { any(), any() )).willReturn(List.of(upcomingMeeting)); + given(topicRepository.findMeetingIdsWithConfirmedTopics(List.of(upcomingMeeting.getId()))) + .willReturn(List.of()); try (MockedStatic mock = mockStatic(SecurityUtil.class)) { mock.when(SecurityUtil::getCurrentUserId).thenReturn(userId); @@ -1512,8 +1516,10 @@ void givenUser_whenGetMyMeetingTabCounts_thenReturnCounts() { any(), any() )).willReturn(2); - given(meetingMemberRepository.countMyMeetingsByStatus(userId, MeetingStatus.DONE)) - .willReturn(3); + given(meetingMemberRepository.countMyMeetingsByStatusWithoutPersonalRetrospective( + userId, + MeetingStatus.DONE + )).willReturn(3); try (MockedStatic mock = mockStatic(SecurityUtil.class)) { mock.when(SecurityUtil::getCurrentUserId).thenReturn(userId);