Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 7 additions & 14 deletions src/main/java/com/dokdok/meeting/api/MeetingListApi.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
package com.dokdok.meeting.api;

import com.dokdok.global.response.ApiResponse;
import com.dokdok.global.response.CursorResponse;
import com.dokdok.global.response.PageResponse;
import com.dokdok.meeting.dto.MeetingListFilter;
import com.dokdok.meeting.dto.MeetingListItemCursorResponse;
import com.dokdok.meeting.dto.MeetingListItemPageResponse;
import com.dokdok.meeting.dto.MeetingListItemResponse;
import com.dokdok.meeting.dto.MeetingListCursor;
import com.dokdok.meeting.dto.MeetingListCursorRequest;
import com.dokdok.meeting.entity.MeetingStatus;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand Down Expand Up @@ -51,7 +47,7 @@ public interface MeetingListApi {
responseCode = "200",
description = "약속 리스트 조회 성공",
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(implementation = MeetingListItemCursorResponse.class),
schema = @Schema(implementation = MeetingListItemPageResponse.class),
examples = @ExampleObject(value = """
{
"code": "SUCCESS",
Expand All @@ -69,12 +65,9 @@ public interface MeetingListApi {
}
],
"totalCount": 12,
"pageSize": 4,
"hasNext": true,
"nextCursor": {
"meetingId": 2,
"startDateTime": "2025-02-03T14:00:00"
}
"currentPage": 0,
"pageSize": 5,
"totalPages": 3
}
}
"""))
Expand All @@ -100,11 +93,11 @@ public interface MeetingListApi {
{"code": "E000", "message": "서버 에러가 발생했습니다. 담당자에게 문의 바랍니다.", "data": null}
""")))
})
ResponseEntity<ApiResponse<CursorResponse<MeetingListItemResponse, MeetingListCursor>>> getMeetingList(
ResponseEntity<ApiResponse<PageResponse<MeetingListItemResponse>>> getMeetingList(
@PathVariable Long gatheringId,
@RequestParam MeetingListFilter filter,
@ParameterObject MeetingListCursorRequest cursor,
@RequestParam(defaultValue = "4") int size
@ParameterObject
@PageableDefault(size = 5, sort = {"meetingStartDate", "id"}) Pageable pageable
);

@Operation(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package com.dokdok.meeting.controller;

import com.dokdok.global.response.ApiResponse;
import com.dokdok.global.response.CursorResponse;
import com.dokdok.global.response.PageResponse;
import com.dokdok.meeting.api.MeetingListApi;
import com.dokdok.meeting.dto.MeetingListCursor;
import com.dokdok.meeting.dto.MeetingListCursorRequest;
import com.dokdok.meeting.dto.MeetingListFilter;
import com.dokdok.meeting.dto.MeetingListItemResponse;
import com.dokdok.meeting.entity.MeetingStatus;
Expand All @@ -31,15 +28,14 @@ public class MeetingListController implements MeetingListApi {

@Override
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ApiResponse<CursorResponse<MeetingListItemResponse, MeetingListCursor>>> getMeetingList(
public ResponseEntity<ApiResponse<PageResponse<MeetingListItemResponse>>> getMeetingList(
@PathVariable Long gatheringId,
@RequestParam MeetingListFilter filter,
@ParameterObject MeetingListCursorRequest cursor,
@RequestParam(defaultValue = "4") int size
@ParameterObject
@PageableDefault(size = 5, sort = {"meetingStartDate", "id"}) Pageable pageable
) {
MeetingListCursor cursorValue = cursor == null ? null : cursor.toCursorOrNull();
CursorResponse<MeetingListItemResponse, MeetingListCursor> response =
meetingService.meetingList(gatheringId, filter, size, cursorValue);
PageResponse<MeetingListItemResponse> response =
meetingService.meetingList(gatheringId, filter, pageable);
return ApiResponse.success(response, "약속 리스트 조회에 성공했습니다.");
}

Expand Down
142 changes: 45 additions & 97 deletions src/main/java/com/dokdok/meeting/service/MeetingService.java
Original file line number Diff line number Diff line change
Expand Up @@ -483,24 +483,22 @@ private void validateMeetingDates(MeetingUpdateRequest request, Meeting meeting)
* 내가 참여한 약속 : 완전히 끝난 약속 중 내가 참여한 약속
* @param gatheringId 모임 식별자
* @param filter 약속 리스트 필터
* @param size 페이지 크기
* @param cursor 커서
* @return CursorResponse
* @param pageable 페이지 정보
* @return PageResponse
*/
public CursorResponse<MeetingListItemResponse, MeetingListCursor> meetingList(
public PageResponse<MeetingListItemResponse> meetingList(
Long gatheringId,
MeetingListFilter filter,
int size,
MeetingListCursor cursor
Pageable pageable
) {
Long userId = SecurityUtil.getCurrentUserId();
gatheringValidator.validateMembership(gatheringId, userId);

return switch (filter) {
case ALL -> getAllMeetings(gatheringId, size, cursor, userId);
case UPCOMING -> getUpcomingMeetings(gatheringId, size, cursor, userId);
case DONE -> getDoneMeetings(gatheringId, size, cursor, userId);
case JOINED -> getJoinedMeetings(gatheringId, size, cursor, userId);
case ALL -> getAllMeetingsPage(gatheringId, pageable, userId);
case UPCOMING -> getUpcomingMeetingsPage(gatheringId, pageable, userId);
case DONE -> getDoneMeetingsPage(gatheringId, pageable, userId);
case JOINED -> getJoinedMeetingsPage(gatheringId, pageable, userId);
};

}
Expand Down Expand Up @@ -674,138 +672,88 @@ public MyMeetingTabCountsResponse getMyMeetingTabCounts() {
}

/**
* 모임의 약속 중 확정된 리스트를 전부 반환한다.
* 모임의 약속 중 확정된 리스트를 페이지로 반환한다.
*/
private CursorResponse<MeetingListItemResponse, MeetingListCursor> getAllMeetings(
private PageResponse<MeetingListItemResponse> getAllMeetingsPage(
Long gatheringId,
int size,
MeetingListCursor cursor,
Pageable pageable,
Long userId
) {
Pageable pageable = cursorPageable(size);
List<Meeting> meetings = meetingRepository.findByGatheringIdAndMeetingStatusAfterCursor(
Page<Meeting> meetingPage = meetingRepository.findByGatheringIdAndMeetingStatus(
gatheringId,
MeetingStatus.CONFIRMED,
cursorStartDateTime(cursor),
cursorMeetingId(cursor),
pageable
);
Integer totalCount = cursor == null
? meetingRepository.countByGatheringIdAndMeetingStatus(gatheringId, MeetingStatus.CONFIRMED)
: null;
return buildMeetingListResponse(meetings, size, userId, gatheringId, totalCount);
return buildMeetingPageResponse(meetingPage, userId, gatheringId);
}

/**
* 다가오는 약속 리스트를 반환한다.
* 다가오는 약속 리스트를 페이지로 반환한다.
*/
private CursorResponse<MeetingListItemResponse, MeetingListCursor> getUpcomingMeetings(
private PageResponse<MeetingListItemResponse> getUpcomingMeetingsPage(
Long gatheringId,
int size,
MeetingListCursor cursor,
Pageable pageable,
Long userId
) {
LocalDateTime now = LocalDateTime.now();
Pageable pageable = cursorPageable(size);

List<Meeting> meetings = meetingRepository
.findByGatheringIdAndMeetingStatusAndMeetingStartDateBetweenAfterCursor(
gatheringId,
MeetingStatus.CONFIRMED,
now,
now.plusDays(3),
cursorStartDateTime(cursor),
cursorMeetingId(cursor),
pageable
);

Integer totalCount = cursor == null
? meetingRepository.countUpcomingMeetings(
gatheringId,
MeetingStatus.CONFIRMED,
now,
now.plusDays(3)
)
: null;
return buildMeetingListResponse(meetings, size, userId, gatheringId, totalCount);
Page<Meeting> meetingPage = meetingRepository.findByGatheringIdAndMeetingStatusAndMeetingStartDateBetween(
gatheringId,
MeetingStatus.CONFIRMED,
now,
now.plusDays(3),
pageable
);
return buildMeetingPageResponse(meetingPage, userId, gatheringId);
}

/**
* 완료된 약속 리스트를 반환한다.
* 완료된 약속 리스트를 페이지로 반환한다.
*/
private CursorResponse<MeetingListItemResponse, MeetingListCursor> getDoneMeetings(
private PageResponse<MeetingListItemResponse> getDoneMeetingsPage(
Long gatheringId,
int size,
MeetingListCursor cursor,
Pageable pageable,
Long userId
) {
Pageable pageable = cursorPageable(size);
List<Meeting> meetings = meetingRepository.findByGatheringIdAndMeetingStatusAfterCursor(
Page<Meeting> meetingPage = meetingRepository.findByGatheringIdAndMeetingStatus(
gatheringId,
MeetingStatus.DONE,
cursorStartDateTime(cursor),
cursorMeetingId(cursor),
pageable
);
Integer totalCount = cursor == null
? meetingRepository.countByGatheringIdAndMeetingStatus(gatheringId, MeetingStatus.DONE)
: null;
return buildMeetingListResponse(meetings, size, userId, gatheringId, totalCount);
return buildMeetingPageResponse(meetingPage, userId, gatheringId);
}

/**
* 완료된 약속 중 내가 참여했던 약속 리스트를 반환한다.
* 완료된 약속 중 내가 참여했던 약속 리스트를 페이지로 반환한다.
*/
private CursorResponse<MeetingListItemResponse, MeetingListCursor> getJoinedMeetings(
private PageResponse<MeetingListItemResponse> getJoinedMeetingsPage(
Long gatheringId,
int size,
MeetingListCursor cursor,
Pageable pageable,
Long userId
) {
Pageable pageable = cursorPageable(size);
List<Meeting> meetings = meetingMemberRepository.findMeetingsByUserIdAndStatusAfterCursor(
Page<Meeting> meetingPage = meetingMemberRepository.findMeetingsByUserIdAndStatus(
userId,
gatheringId,
MeetingStatus.DONE,
cursorStartDateTime(cursor),
cursorMeetingId(cursor),
pageable
);
Integer totalCount = cursor == null
? meetingMemberRepository.countMeetingsByUserIdAndStatus(
userId,
gatheringId,
MeetingStatus.DONE
)
: null;
return buildMeetingListResponse(meetings, size, userId, gatheringId, totalCount);
return buildMeetingPageResponse(meetingPage, userId, gatheringId);
}

/**
* 약속 리스트 커서 응답을 구성한다.
* 약속 리스트 페이지 응답을 구성한다.
*/
private CursorResponse<MeetingListItemResponse, MeetingListCursor> buildMeetingListResponse(
List<Meeting> meetingCandidates,
int size,
private PageResponse<MeetingListItemResponse> buildMeetingPageResponse(
Page<Meeting> meetingPage,
Long userId,
Long gatheringId,
Integer totalCount
Long gatheringId
) {
boolean hasNext = meetingCandidates.size() > size;
List<Meeting> meetings = hasNext ? meetingCandidates.subList(0, size) : meetingCandidates;
if (meetings.isEmpty()) {
return CursorResponse.of(List.of(), size, false, null, totalCount);
}

List<MeetingListItemResponse> items = buildMeetingItems(meetings, userId, gatheringId);

MeetingListCursor nextCursor = null;
if (hasNext) {
Meeting last = meetings.get(meetings.size() - 1);
nextCursor = new MeetingListCursor(last.getMeetingStartDate(), last.getId());
}

return CursorResponse.of(items, size, hasNext, nextCursor, totalCount);
List<MeetingListItemResponse> items = buildMeetingItems(meetingPage.getContent(), userId, gatheringId);
return PageResponse.of(
items,
meetingPage.getTotalElements(),
meetingPage.getNumber(),
meetingPage.getSize()
);
}

/**
Expand Down
18 changes: 8 additions & 10 deletions src/test/java/com/dokdok/meeting/service/MeetingServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Page;
Expand Down Expand Up @@ -1083,7 +1082,7 @@ void givenGatheringIdAndAllFilter_whenGetMeetingList_thenReturnItems() {
// given
Long gatheringId = 100L;
Long userId = 55L;
int size = 10;
Pageable pageable = org.springframework.data.domain.PageRequest.of(0, 10);
Book book1 = Book.builder().id(1L).bookName("book1").build();
Book book2 = Book.builder().id(2L).bookName("book2").build();
Meeting meeting1 = Meeting.builder()
Expand All @@ -1106,13 +1105,12 @@ void givenGatheringIdAndAllFilter_whenGetMeetingList_thenReturnItems() {
.build();

List<Meeting> meetings = List.of(meeting1, meeting2);
given(meetingRepository.findByGatheringIdAndMeetingStatusAfterCursor(
Page<Meeting> meetingPage = new PageImpl<>(meetings, pageable, meetings.size());
given(meetingRepository.findByGatheringIdAndMeetingStatus(
eq(gatheringId),
eq(MeetingStatus.CONFIRMED),
any(),
any(),
any()
)).willReturn(meetings);
)).willReturn(meetingPage);
given(topicRepository.findTopicTypesByMeetingIds(List.of(1L, 2L)))
.willReturn(List.of(
new Object[]{1L, TopicType.FREE},
Expand All @@ -1126,14 +1124,14 @@ void givenGatheringIdAndAllFilter_whenGetMeetingList_thenReturnItems() {
mock.when(SecurityUtil::getCurrentUserId).thenReturn(userId);

// when
CursorResponse<MeetingListItemResponse, MeetingListCursor> response =
meetingService.meetingList(gatheringId, MeetingListFilter.ALL, size, null);
PageResponse<MeetingListItemResponse> response =
meetingService.meetingList(gatheringId, MeetingListFilter.ALL, pageable);

// then
assertThat(response.items()).hasSize(2);
assertThat(response.pageSize()).isEqualTo(10);
assertThat(response.hasNext()).isFalse();
assertThat(response.nextCursor()).isNull();
assertThat(response.currentPage()).isEqualTo(0);
assertThat(response.totalPages()).isEqualTo(1);
MeetingListItemResponse item1 = response.items().stream()
.filter(item -> item.meetingId().equals(1L))
.findFirst()
Expand Down