From 235dacc5f2cf1562fa2cfa50c50161918a4a33a4 Mon Sep 17 00:00:00 2001 From: YongHwan Kim Date: Thu, 5 Oct 2023 10:01:08 +0900 Subject: [PATCH] =?UTF-8?q?[feat]=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=EC=8B=9C=20=EA=B0=80?= =?UTF-8?q?=EC=9E=A5=20=EC=B5=9C=EA=B7=BC=EC=97=90=20=EC=A0=84=EC=86=A1?= =?UTF-8?q?=EB=90=9C=20=EC=B1=84=ED=8C=85=EB=B0=A9=EC=9D=B4=20=EC=9C=84?= =?UTF-8?q?=EC=AA=BD=EC=9C=BC=EB=A1=9C=20=EC=98=A4=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EA=B0=9C=EC=84=A0=20(#144)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * #141 feat: 가장 최근에 보낸 메시지를 받은 채팅방이 가장 위에 오도록 정렬 * #141 fix: 오타 수정 --- .../app/api/chat/ChatLogRestController.java | 24 ++--------- .../app/api/chat/ChatLogService.java | 2 +- .../app/api/chat/ChatRoomService.java | 3 ++ .../codesquard/app/api/chat/ChatService.java | 42 +++++++++++++++++++ .../chat/response/ChatLogMessageResponse.java | 2 +- .../chat/response/ChatRoomListResponse.java | 4 ++ .../app/api/member/MemberService.java | 5 +++ .../api/chat/ChatLogRestControllerTest.java | 13 ++++++ .../api/chat/ChatRoomRestControllerTest.java | 11 ++++- 9 files changed, 83 insertions(+), 23 deletions(-) create mode 100644 backend/src/main/java/codesquard/app/api/chat/ChatService.java diff --git a/backend/src/main/java/codesquard/app/api/chat/ChatLogRestController.java b/backend/src/main/java/codesquard/app/api/chat/ChatLogRestController.java index c37b21b03..52c4e6d4a 100644 --- a/backend/src/main/java/codesquard/app/api/chat/ChatLogRestController.java +++ b/backend/src/main/java/codesquard/app/api/chat/ChatLogRestController.java @@ -1,8 +1,6 @@ package codesquard.app.api.chat; import java.util.Collections; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; @@ -34,12 +32,8 @@ @RestController public class ChatLogRestController { - private static final int DEFAULT_READ_MESSAGE_SIZE = 10; - - private final Map>, Long> chatRequests = - new ConcurrentHashMap<>(); - private final ChatLogService chatLogService; + private final ChatService chatService; @ResponseStatus(HttpStatus.CREATED) @PostMapping("/chats/{chatRoomId}") @@ -48,20 +42,10 @@ public ApiResponse sendMessage( @RequestBody ChatLogSendRequest request, @AuthPrincipal Principal sender) { ChatLogSendResponse response = chatLogService.sendMessage(request, chatRoomId, sender); - - onMessage(chatRoomId, sender); + chatService.onMessage(chatRoomId, sender); return ApiResponse.created("메시지 전송이 완료되었습니다.", response); } - private void onMessage(Long chatRoomId, Principal sender) { - for (Map.Entry>, Long> entry : this.chatRequests.entrySet()) { - DeferredResult> key = entry.getKey(); - Long cursor = entry.getValue(); - key.setResult(ApiResponse.ok("채팅 메시지 목록 조회가 완료되었습니다.", - chatLogService.readMessages(chatRoomId, sender, cursor, Pageable.ofSize(DEFAULT_READ_MESSAGE_SIZE)))); - } - } - @GetMapping("/chats/{chatRoomId}") public DeferredResult> readMessages( @PathVariable Long chatRoomId, @@ -72,9 +56,9 @@ public DeferredResult> readMessages( principal.getLoginId()); DeferredResult> deferredResult = new DeferredResult<>(10000L); - this.chatRequests.put(deferredResult, messageIndex); + chatService.putMessageIndex(deferredResult, messageIndex); - deferredResult.onCompletion(() -> chatRequests.remove(deferredResult)); + deferredResult.onCompletion(() -> chatService.removeMessageIndex(deferredResult)); deferredResult.onTimeout(() -> deferredResult.setErrorResult( ApiResponse.of(HttpStatus.REQUEST_TIMEOUT, "새로운 채팅 메시지가 존재하지 않습니다.", Collections.emptyList()))); diff --git a/backend/src/main/java/codesquard/app/api/chat/ChatLogService.java b/backend/src/main/java/codesquard/app/api/chat/ChatLogService.java index c6fba055e..3945f9c70 100644 --- a/backend/src/main/java/codesquard/app/api/chat/ChatLogService.java +++ b/backend/src/main/java/codesquard/app/api/chat/ChatLogService.java @@ -77,7 +77,7 @@ public ChatLogListResponse readMessages(Long chatRoomId, Principal principal, Lo private Long getNextCursor(List contents, boolean hasNext) { Long nextCursor = null; if (hasNext) { - nextCursor = contents.get(contents.size() - 1).getChatLogId(); + nextCursor = contents.get(contents.size() - 1).getMessageIndex(); } return nextCursor; } diff --git a/backend/src/main/java/codesquard/app/api/chat/ChatRoomService.java b/backend/src/main/java/codesquard/app/api/chat/ChatRoomService.java index 2c77905ba..9a010f541 100644 --- a/backend/src/main/java/codesquard/app/api/chat/ChatRoomService.java +++ b/backend/src/main/java/codesquard/app/api/chat/ChatRoomService.java @@ -1,5 +1,6 @@ package codesquard.app.api.chat; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -89,6 +90,7 @@ public ChatRoomListResponse readAllChatRoom(Principal principal, Pageable pageab List contents = slice.getContent().stream() .map(getChatRoomItemResponseMapper(newMessageMap, principal)) + .sorted(Comparator.comparing(ChatRoomItemResponse::getLastSendTime).reversed()) .collect(Collectors.toUnmodifiableList()); boolean hasNext = slice.hasNext(); Long nextCursor = getNextCursor(contents, hasNext); @@ -136,6 +138,7 @@ public ChatRoomListResponse readAllChatRoomByItem(Long itemId, Principal princip List contents = slice.getContent().stream() .map(getChatRoomItemResponseMapper(newMessageMap, principal)) + .sorted(Comparator.comparing(ChatRoomItemResponse::getLastSendTime).reversed()) .collect(Collectors.toUnmodifiableList()); boolean hasNext = slice.hasNext(); Long nextCursor = getNextCursor(contents, hasNext); diff --git a/backend/src/main/java/codesquard/app/api/chat/ChatService.java b/backend/src/main/java/codesquard/app/api/chat/ChatService.java new file mode 100644 index 000000000..6670f1eb2 --- /dev/null +++ b/backend/src/main/java/codesquard/app/api/chat/ChatService.java @@ -0,0 +1,42 @@ +package codesquard.app.api.chat; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.web.context.request.async.DeferredResult; + +import codesquard.app.api.chat.response.ChatLogListResponse; +import codesquard.app.api.response.ApiResponse; +import codesquard.app.domain.oauth.support.Principal; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Service +public class ChatService { + + private static final int DEFAULT_READ_MESSAGE_SIZE = 10; + + private final Map>, Long> chatRequests = + new ConcurrentHashMap<>(); + + private final ChatLogService chatLogService; + + public void onMessage(Long chatRoomId, Principal sender) { + for (Map.Entry>, Long> entry : this.chatRequests.entrySet()) { + DeferredResult> key = entry.getKey(); + Long cursor = entry.getValue(); + key.setResult(ApiResponse.ok("채팅 메시지 목록 조회가 완료되었습니다.", + chatLogService.readMessages(chatRoomId, sender, cursor, Pageable.ofSize(DEFAULT_READ_MESSAGE_SIZE)))); + } + } + + public void putMessageIndex(DeferredResult> deferredResult, Long messageIndex) { + chatRequests.put(deferredResult, messageIndex); + } + + public void removeMessageIndex(DeferredResult> deferredResult) { + chatRequests.remove(deferredResult); + } +} diff --git a/backend/src/main/java/codesquard/app/api/chat/response/ChatLogMessageResponse.java b/backend/src/main/java/codesquard/app/api/chat/response/ChatLogMessageResponse.java index 5b4a8d568..0c56b5ebd 100644 --- a/backend/src/main/java/codesquard/app/api/chat/response/ChatLogMessageResponse.java +++ b/backend/src/main/java/codesquard/app/api/chat/response/ChatLogMessageResponse.java @@ -11,7 +11,7 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class ChatLogMessageResponse { - private Long chatLogId; + private Long messageIndex; private Boolean isMe; private String message; diff --git a/backend/src/main/java/codesquard/app/api/chat/response/ChatRoomListResponse.java b/backend/src/main/java/codesquard/app/api/chat/response/ChatRoomListResponse.java index dff22d0ce..66228d310 100644 --- a/backend/src/main/java/codesquard/app/api/chat/response/ChatRoomListResponse.java +++ b/backend/src/main/java/codesquard/app/api/chat/response/ChatRoomListResponse.java @@ -17,4 +17,8 @@ public ChatRoomListResponse(List contents, boolean hasNext this.contents = contents; this.paging = ItemResponses.Paging.create(nextCursor, hasNext); } + + public boolean isEmptyChatRooms() { + return contents.isEmpty(); + } } diff --git a/backend/src/main/java/codesquard/app/api/member/MemberService.java b/backend/src/main/java/codesquard/app/api/member/MemberService.java index c6f009988..64a65575a 100644 --- a/backend/src/main/java/codesquard/app/api/member/MemberService.java +++ b/backend/src/main/java/codesquard/app/api/member/MemberService.java @@ -28,4 +28,9 @@ public MemberProfileResponse modifyProfileImage(String loginId, MultipartFile up member.changeAvatarUrl(avatarUrl); return new MemberProfileResponse(avatarUrl); } + + public Member findMemberByLoginId(String loginId) { + return memberRepository.findMemberByLoginId(loginId) + .orElseThrow(() -> new NotFoundResourceException(ErrorCode.NOT_FOUND_MEMBER)); + } } diff --git a/backend/src/test/java/codesquard/app/api/chat/ChatLogRestControllerTest.java b/backend/src/test/java/codesquard/app/api/chat/ChatLogRestControllerTest.java index b95e0b36d..ca1a28bef 100644 --- a/backend/src/test/java/codesquard/app/api/chat/ChatLogRestControllerTest.java +++ b/backend/src/test/java/codesquard/app/api/chat/ChatLogRestControllerTest.java @@ -7,6 +7,7 @@ import static org.hamcrest.Matchers.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.anyLong; +import static org.mockito.BDDMockito.anyString; import static org.mockito.BDDMockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; @@ -41,6 +42,7 @@ import codesquard.app.api.chat.response.ChatLogListResponse; import codesquard.app.api.chat.response.ChatLogMessageResponse; import codesquard.app.api.chat.response.ChatLogSendResponse; +import codesquard.app.api.member.MemberService; import codesquard.app.domain.category.Category; import codesquard.app.domain.chat.ChatLog; import codesquard.app.domain.chat.ChatRoom; @@ -60,6 +62,12 @@ class ChatLogRestControllerTest extends ControllerTestSupport { @MockBean private ChatLogService chatLogService; + @MockBean + private ChatService chatService; + + @MockBean + private MemberService memberService; + @Autowired private PageableHandlerMethodArgumentResolver pageableHandlerMethodArgumentResolver; @@ -99,6 +107,11 @@ public void sendMessage() throws Exception { any(Principal.class))) .willReturn(response); + Member receiver = createMember("avatarUrl", "bruni@gmail.com", "bruni"); + given(memberService.findMemberByLoginId( + anyString() + )).willReturn(receiver); + // when & then mockMvc.perform(post("/api/chats/1") .content(objectMapper.writeValueAsString(requestBody)) diff --git a/backend/src/test/java/codesquard/app/api/chat/ChatRoomRestControllerTest.java b/backend/src/test/java/codesquard/app/api/chat/ChatRoomRestControllerTest.java index 0fb34fbb7..858e5992c 100644 --- a/backend/src/test/java/codesquard/app/api/chat/ChatRoomRestControllerTest.java +++ b/backend/src/test/java/codesquard/app/api/chat/ChatRoomRestControllerTest.java @@ -9,6 +9,7 @@ import static org.mockito.BDDMockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.util.HashMap; @@ -26,6 +27,7 @@ import org.springframework.data.web.PageableHandlerMethodArgumentResolver; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import codesquard.app.ControllerTestSupport; @@ -51,6 +53,9 @@ class ChatRoomRestControllerTest extends ControllerTestSupport { @MockBean private ChatRoomService chatRoomService; + @MockBean + private ChatService chatService; + @Autowired private PageableHandlerMethodArgumentResolver pageableHandlerMethodArgumentResolver; @@ -115,7 +120,11 @@ public void readAllChatRoom() throws Exception { .willReturn(response); // when & then - mockMvc.perform(get("/api/chats")) + MvcResult asyncListener = mockMvc.perform(get("/api/chats")) + .andExpect(request().asyncStarted()) + .andReturn(); + + mockMvc.perform(asyncDispatch(asyncListener)) .andExpect(status().isOk()) .andExpect(jsonPath("statusCode").value(equalTo(200))) .andExpect(jsonPath("message").value(equalTo("채팅방 목록 조회를 완료하였습니다.")))