Skip to content

Commit

Permalink
fix: ✏️ Append LastMessage Field in ChatRoomDetail Response (#196)
Browse files Browse the repository at this point in the history
* feat: add chat_room_res info dto for service return data mapping

* fix: chat_room_mapper use chat_room_res.info dto within parameter

* fix: when search my chatroom, add flow for read last message

* fix: usecase is fixed by dto spec

* test: fix unit test
  • Loading branch information
psychology50 authored Nov 14, 2024
1 parent af58ae7 commit 96ef7bb
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import kr.co.pennyway.domain.domains.chatroom.domain.ChatRoom;
import kr.co.pennyway.domain.domains.chatroom.dto.ChatRoomDetail;
import lombok.Builder;

import java.time.LocalDateTime;
Expand All @@ -13,6 +14,23 @@
import java.util.Set;

public final class ChatRoomRes {
/**
* μ±„νŒ…λ°© 정보λ₯Ό λ‹΄κΈ° μœ„ν•œ DTO
*
* @param chatRoom {@link ChatRoomDetail} : μ±„νŒ…λ°© 정보
* @param unreadMessageCount long : 읽지 μ•Šμ€ λ©”μ‹œμ§€ 수
* @param lastMessage {@link ChatRes.ChatDetail} : κ°€μž₯ 졜근 λ©”μ‹œμ§€. 없을 경우 null
*/
public record Info(
ChatRoomDetail chatRoom,
long unreadMessageCount,
ChatRes.ChatDetail lastMessage
) {
public static Info of(ChatRoomDetail chatRoom, long unreadMessageCount, ChatRes.ChatDetail recentMessage) {
return new Info(chatRoom, unreadMessageCount, recentMessage);
}
}

@Schema(description = "μ±„νŒ…λ°© 상세 정보")
public record Detail(
@Schema(description = "μ±„νŒ…λ°© ID", type = "long")
Expand All @@ -33,10 +51,12 @@ public record Detail(
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
LocalDateTime createdAt,
@Schema(description = "λ§ˆμ§€λ§‰ λ©”μ‹œμ§€ 정보. 없을 경우 null이 λ°˜ν™˜λœλ‹€.")
ChatRes.ChatDetail lastMessage,
@Schema(description = "읽지 μ•Šμ€ λ©”μ‹œμ§€ 수. 100 μ΄μƒμ˜ 값을 가지면, 100으둜 ν‘œμ‹œλœλ‹€.")
long unreadMessageCount
) {
public Detail(Long id, String title, String description, String backgroundImageUrl, boolean isPrivate, boolean isAdmin, int participantCount, LocalDateTime createdAt, long unreadMessageCount) {
public Detail(Long id, String title, String description, String backgroundImageUrl, boolean isPrivate, boolean isAdmin, int participantCount, LocalDateTime createdAt, ChatRes.ChatDetail lastMessage, long unreadMessageCount) {
this.id = id;
this.title = title;
this.description = Objects.toString(description, "");
Expand All @@ -45,10 +65,11 @@ public Detail(Long id, String title, String description, String backgroundImageU
this.isAdmin = isAdmin;
this.participantCount = participantCount;
this.createdAt = createdAt;
this.lastMessage = lastMessage;
this.unreadMessageCount = (unreadMessageCount > 100) ? 100 : unreadMessageCount;
}

public static Detail of(ChatRoom chatRoom, boolean isAdmin, int participantCount, long unreadMessageCount) {
public static Detail of(ChatRoom chatRoom, ChatRes.ChatDetail lastMessage, boolean isAdmin, int participantCount, long unreadMessageCount) {
return new Detail(
chatRoom.getId(),
chatRoom.getTitle(),
Expand All @@ -58,9 +79,25 @@ public static Detail of(ChatRoom chatRoom, boolean isAdmin, int participantCount
isAdmin,
participantCount,
chatRoom.getCreatedAt(),
lastMessage,
unreadMessageCount
);
}

public static Detail from(ChatRoomRes.Info info) {
return new Detail(
info.chatRoom().id(),
info.chatRoom().title(),
info.chatRoom().description(),
info.chatRoom().backgroundImageUrl(),
info.chatRoom().password() != null,
info.chatRoom().isAdmin(),
info.chatRoom().participantCount(),
info.chatRoom().createdAt(),
info.lastMessage(),
info.unreadMessageCount()
);
}
}

@Schema(description = "μ±„νŒ…λ°© μš”μ•½ 정보")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Mapper
public final class ChatRoomMapper {
/**
* μ±„νŒ…λ°© 상세 정보λ₯Ό SliceResponseTemplate ν˜•νƒœλ‘œ λ³€ν™˜ν•œλ‹€.
* ν•΄λ‹Ή λ©”μ„œλ“œλŠ” μ–Έμ œλ‚˜ μ±„νŒ…λ°© 검색 μ‘λ‹΅μœΌλ‘œ μ‚¬μš©λ˜λ©°, λ§ˆμ§€λ§‰ λ©”μ‹œμ§€ μ •λ³΄λŠ” null둜 μ„€μ •λœλ‹€.
*
* @param details
* @param pageable
* @return
*/
public static SliceResponseTemplate<ChatRoomRes.Detail> toChatRoomResDetails(Slice<ChatRoomDetail> details, Pageable pageable) {
List<ChatRoomRes.Detail> contents = new ArrayList<>();
for (ChatRoomDetail detail : details.getContent()) {
Expand All @@ -31,6 +38,7 @@ public static SliceResponseTemplate<ChatRoomRes.Detail> toChatRoomResDetails(Sli
detail.isAdmin(),
detail.participantCount(),
detail.createdAt(),
null,
0
)
);
Expand All @@ -39,31 +47,31 @@ public static SliceResponseTemplate<ChatRoomRes.Detail> toChatRoomResDetails(Sli
return SliceResponseTemplate.of(contents, pageable, contents.size(), details.hasNext());
}

public static List<ChatRoomRes.Detail> toChatRoomResDetails(Map<ChatRoomDetail, Long> details) {
public static List<ChatRoomRes.Detail> toChatRoomResDetails(List<ChatRoomRes.Info> details) {
List<ChatRoomRes.Detail> responses = new ArrayList<>();

for (Map.Entry<ChatRoomDetail, Long> entry : details.entrySet()) {
ChatRoomDetail detail = entry.getKey();
for (ChatRoomRes.Info info : details) {
responses.add(
new ChatRoomRes.Detail(
detail.id(),
detail.title(),
detail.description(),
detail.backgroundImageUrl(),
detail.password() != null,
detail.isAdmin(),
detail.participantCount(),
detail.createdAt(),
entry.getValue()
info.chatRoom().id(),
info.chatRoom().title(),
info.chatRoom().description(),
info.chatRoom().backgroundImageUrl(),
info.chatRoom().password() != null,
info.chatRoom().isAdmin(),
info.chatRoom().participantCount(),
info.chatRoom().createdAt(),
info.lastMessage(),
info.unreadMessageCount()
)
);
}

return responses;
}

public static ChatRoomRes.Detail toChatRoomResDetail(ChatRoom chatRoom, boolean isAdmin, int participantCount, long unreadMessageCount) {
return ChatRoomRes.Detail.of(chatRoom, isAdmin, participantCount, unreadMessageCount);
public static ChatRoomRes.Detail toChatRoomResDetail(ChatRoom chatRoom, ChatRes.ChatDetail lastMessage, boolean isAdmin, int participantCount, long unreadMessageCount) {
return ChatRoomRes.Detail.of(chatRoom, lastMessage, isAdmin, participantCount, unreadMessageCount);
}

public static ChatRoomRes.RoomWithParticipants toChatRoomResRoomWithParticipants(ChatMemberResult.Detail myInfo, List<ChatMemberResult.Detail> recentParticipants, List<ChatMemberResult.Summary> otherParticipants, List<ChatMessage> chatMessages) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package kr.co.pennyway.api.apis.chat.service;

import kr.co.pennyway.api.apis.chat.dto.ChatRes;
import kr.co.pennyway.api.apis.chat.dto.ChatRoomRes;
import kr.co.pennyway.domain.common.redis.message.domain.ChatMessage;
import kr.co.pennyway.domain.common.redis.message.service.ChatMessageService;
import kr.co.pennyway.domain.domains.chatroom.dto.ChatRoomDetail;
import kr.co.pennyway.domain.domains.chatroom.service.ChatRoomService;
Expand All @@ -11,9 +14,8 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
Expand All @@ -26,17 +28,19 @@ public class ChatRoomSearchService {
/**
* μ‚¬μš©μž IDκ°€ μ†ν•œ μ±„νŒ…λ°© λͺ©λ‘μ„ μ‘°νšŒν•œλ‹€.
*
* @return μ±„νŒ…λ°© λͺ©λ‘ (μ±„νŒ…λ°© 정보, 읽지 μ•Šμ€ λ©”μ‹œμ§€ 수)
* @return μ±„νŒ…λ°© λͺ©λ‘. {@link ChatRoomRes.Info} 리슀트 ν˜•νƒœλ‘œ λ°˜ν™˜
*/
@Transactional(readOnly = true)
public Map<ChatRoomDetail, Long> readChatRooms(Long userId) {
public List<ChatRoomRes.Info> readChatRooms(Long userId) {
List<ChatRoomDetail> chatRooms = chatRoomService.readChatRoomsByUserId(userId);
Map<ChatRoomDetail, Long> result = new HashMap<>();
List<ChatRoomRes.Info> result = new ArrayList<>();

for (ChatRoomDetail chatRoom : chatRooms) {
Long lastReadMessageId = chatMessageStatusService.readLastReadMessageId(userId, chatRoom.id());
ChatMessage lastMessage = chatMessageService.readRecentMessages(chatRoom.id(), 1).stream().findFirst().orElse(null);
Long unreadCount = chatMessageService.countUnreadMessages(chatRoom.id(), lastReadMessageId);
result.put(chatRoom, unreadCount);

result.add(ChatRoomRes.Info.of(chatRoom, unreadCount, lastMessage == null ? null : ChatRes.ChatDetail.from(lastMessage)));
}

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class ChatMemberUseCase {
public ChatRoomRes.Detail joinChatRoom(Long userId, Long chatRoomId, Integer password) {
Triple<ChatRoom, Integer, Long> chatRoom = chatMemberJoinService.execute(userId, chatRoomId, password);

return ChatRoomMapper.toChatRoomResDetail(chatRoom.getLeft(), false, chatRoom.getMiddle(), chatRoom.getRight());
return ChatRoomMapper.toChatRoomResDetail(chatRoom.getLeft(), null, false, chatRoom.getMiddle(), chatRoom.getRight());
}

public List<ChatMemberRes.MemberDetail> readChatMembers(Long chatRoomId, Set<Long> chatMemberIds) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import org.springframework.data.domain.Slice;

import java.util.List;
import java.util.Map;
import java.util.Set;

@UseCase
Expand All @@ -31,11 +30,11 @@ public class ChatRoomUseCase {
public ChatRoomRes.Detail createChatRoom(ChatRoomReq.Create request, Long userId) {
ChatRoom chatRoom = chatRoomSaveService.createChatRoom(request, userId);

return ChatRoomMapper.toChatRoomResDetail(chatRoom, true, 1, 0);
return ChatRoomMapper.toChatRoomResDetail(chatRoom, null, true, 1, 0);
}

public List<ChatRoomRes.Detail> getChatRooms(Long userId) {
Map<ChatRoomDetail, Long> chatRooms = chatRoomSearchService.readChatRooms(userId);
List<ChatRoomRes.Info> chatRooms = chatRoomSearchService.readChatRooms(userId);

return ChatRoomMapper.toChatRoomResDetails(chatRooms);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ void createChatRoomSuccess() throws Exception {
// given
ChatRoom fixture = ChatRoomFixture.PRIVATE_CHAT_ROOM.toEntity(1L);
ChatRoomReq.Create request = ChatRoomFixture.PRIVATE_CHAT_ROOM.toCreateRequest();
given(chatRoomUseCase.createChatRoom(request, 1L)).willReturn(ChatRoomRes.Detail.of(fixture, true, 1, 10));
given(chatRoomUseCase.createChatRoom(request, 1L)).willReturn(ChatRoomRes.Detail.of(fixture, null, true, 1, 10));

// when
ResultActions result = performPostChatRoom(request);
Expand All @@ -70,7 +70,7 @@ void createChatRoomSuccessWithNullBackgroundImageUrl() throws Exception {
ChatRoom fixture = ChatRoomFixture.PUBLIC_CHAT_ROOM.toEntity(1L);
ChatRoomReq.Create request = ChatRoomFixture.PUBLIC_CHAT_ROOM.toCreateRequest();

given(chatRoomUseCase.createChatRoom(request, 1L)).willReturn(ChatRoomRes.Detail.of(fixture, true, 1, 10));
given(chatRoomUseCase.createChatRoom(request, 1L)).willReturn(ChatRoomRes.Detail.of(fixture, null, true, 1, 10));

// when
ResultActions result = performPostChatRoom(request);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package kr.co.pennyway.api.apis.chat.service;

import kr.co.pennyway.api.apis.chat.dto.ChatRoomRes;
import kr.co.pennyway.domain.common.redis.message.domain.ChatMessage;
import kr.co.pennyway.domain.common.redis.message.domain.ChatMessageBuilder;
import kr.co.pennyway.domain.common.redis.message.service.ChatMessageService;
import kr.co.pennyway.domain.common.redis.message.type.MessageCategoryType;
import kr.co.pennyway.domain.common.redis.message.type.MessageContentType;
import kr.co.pennyway.domain.domains.chatroom.dto.ChatRoomDetail;
import kr.co.pennyway.domain.domains.chatroom.service.ChatRoomService;
import kr.co.pennyway.domain.domains.chatstatus.service.ChatMessageStatusService;
Expand All @@ -15,11 +20,12 @@
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class ChatRoomSearchServiceTest {
Expand All @@ -34,7 +40,7 @@ class ChatRoomSearchServiceTest {
private ChatMessageService chatMessageService;

@Test
@DisplayName("μ‚¬μš©μžμ˜ μ±„νŒ…λ°© λͺ©λ‘κ³Ό 각 방의 읽지 μ•Šμ€ λ©”μ‹œμ§€ 수λ₯Ό μ •μƒμ μœΌλ‘œ μ‘°νšŒν•œλ‹€")
@DisplayName("μ‚¬μš©μžμ˜ μ±„νŒ…λ°© λͺ©λ‘κ³Ό 각 방의 읽지 μ•Šμ€ λ©”μ‹œμ§€ 수, λ§ˆμ§€λ§‰ λ©”μ‹œμ§€λ₯Ό μ •μƒμ μœΌλ‘œ μ‘°νšŒν•œλ‹€")
void successReadChatRooms() {
// given
Long userId = 1L;
Expand All @@ -46,21 +52,29 @@ void successReadChatRooms() {
given(chatRoomService.readChatRoomsByUserId(userId)).willReturn(chatRooms);

// room1: λ§ˆμ§€λ§‰μœΌλ‘œ 읽은 λ©”μ‹œμ§€ ID 10, 읽지 μ•Šμ€ λ©”μ‹œμ§€ 5개
ChatMessage firstRoomLastMessage = ChatMessageBuilder.builder().chatRoomId(2L).chatId(1L).content("Hello").contentType(MessageContentType.TEXT).categoryType(MessageCategoryType.NORMAL).sender(userId).build();

given(chatMessageStatusService.readLastReadMessageId(userId, 1L)).willReturn(10L);
given(chatMessageService.countUnreadMessages(1L, 10L)).willReturn(5L);
given(chatMessageService.readRecentMessages(1L, 1)).willReturn(List.of(firstRoomLastMessage));

// room2: λ§ˆμ§€λ§‰μœΌλ‘œ 읽은 λ©”μ‹œμ§€ ID 20, 읽지 μ•Šμ€ λ©”μ‹œμ§€ 3개
ChatMessage secondRoomLastMessage = ChatMessageBuilder.builder().chatRoomId(2L).chatId(100L).content("jayangλ‹˜μ΄ μž…μž₯ν•˜μ…¨μŠ΅λ‹ˆλ‹€.").contentType(MessageContentType.TEXT).categoryType(MessageCategoryType.SYSTEM).sender(userId).build();

given(chatMessageStatusService.readLastReadMessageId(userId, 2L)).willReturn(20L);
given(chatMessageService.countUnreadMessages(2L, 20L)).willReturn(3L);
given(chatMessageService.readRecentMessages(2L, 1)).willReturn(List.of(secondRoomLastMessage));

// when
Map<ChatRoomDetail, Long> result = chatRoomSearchService.readChatRooms(userId);
List<ChatRoomRes.Info> result = chatRoomSearchService.readChatRooms(userId);

// then
assertAll(
() -> assertEquals(2, result.size()),
() -> assertEquals(5L, result.get(chatRooms.get(0))),
() -> assertEquals(3L, result.get(chatRooms.get(1)))
() -> assertEquals(2, result.size(), "쑰회된 μ±„νŒ…λ°© λͺ©λ‘μ€ 2κ°œμ—¬μ•Ό ν•œλ‹€."),
() -> assertEquals(5L, result.get(0).unreadMessageCount(), "Room1의 읽지 μ•Šμ€ λ©”μ‹œμ§€ μˆ˜λŠ” 5κ°œμ—¬μ•Ό ν•œλ‹€."),
() -> assertEquals(firstRoomLastMessage.getChatId(), result.get(0).lastMessage().chatId(), "Room1의 λ§ˆμ§€λ§‰ λ©”μ‹œμ§€λŠ” IDκ°€ μΌμΉ˜ν•΄μ•Ό ν•œλ‹€."),
() -> assertEquals(3L, result.get(1).unreadMessageCount(), "Room2의 읽지 μ•Šμ€ λ©”μ‹œμ§€ μˆ˜λŠ” 3κ°œμ—¬μ•Ό ν•œλ‹€."),
() -> assertEquals(secondRoomLastMessage.getChatId(), result.get(1).lastMessage().chatId(), "Room2의 λ§ˆμ§€λ§‰ λ©”μ‹œμ§€λŠ” IDκ°€ μΌμΉ˜ν•΄μ•Ό ν•œλ‹€.")
);
}

Expand All @@ -72,10 +86,13 @@ void returnEmptyMapWhenNoRooms() {
given(chatRoomService.readChatRoomsByUserId(userId)).willReturn(Collections.emptyList());

// when
Map<ChatRoomDetail, Long> result = chatRoomSearchService.readChatRooms(userId);
List<ChatRoomRes.Info> result = chatRoomSearchService.readChatRooms(userId);

// then
assertTrue(result.isEmpty());
verify(chatMessageStatusService, never()).readLastReadMessageId(eq(userId), anyLong());
verify(chatMessageService, never()).countUnreadMessages(anyLong(), anyLong());
verify(chatMessageService, never()).countUnreadMessages(anyLong(), anyLong());
}

@Test
Expand Down Expand Up @@ -110,10 +127,11 @@ void verifyServiceCallOrder() {
new ChatRoomDetail(1L, "Room1", "", "", 123456, LocalDateTime.now(), true, 2)
);

InOrder inOrder = inOrder(chatRoomService, chatMessageStatusService, chatMessageService);
InOrder inOrder = inOrder(chatRoomService, chatMessageStatusService, chatMessageService, chatMessageService);

given(chatRoomService.readChatRoomsByUserId(userId)).willReturn(chatRooms);
given(chatMessageStatusService.readLastReadMessageId(userId, 1L)).willReturn(10L);
given(chatMessageService.readRecentMessages(1L, 1)).willReturn(Collections.emptyList());
given(chatMessageService.countUnreadMessages(userId, 10L)).willReturn(5L);

// when
Expand Down

0 comments on commit 96ef7bb

Please sign in to comment.