Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature/29 chat list] 채팅 목록 #19

Merged
merged 5 commits into from
Nov 7, 2023
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
63 changes: 60 additions & 3 deletions src/main/java/com/tomato/market/controller/ChatController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.text.ParseException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
Expand All @@ -19,8 +20,11 @@

import com.tomato.market.data.dto.ChatDto;
import com.tomato.market.data.dto.ChatListResponseDto;
import com.tomato.market.data.dto.ImageDto;
import com.tomato.market.data.dto.RoomDto;
import com.tomato.market.data.dto.RoomListResponseDto;
import com.tomato.market.data.dto.RoomResponseDto;
import com.tomato.market.service.BoardService;
import com.tomato.market.service.ChatService;

import jakarta.validation.Valid;
Expand Down Expand Up @@ -57,11 +61,16 @@ public class ChatController {

private Logger logger = LoggerFactory.getLogger(ChatController.class);
private ChatService chatService;

// PostImage를 불러오기 위해 BoardService의 코드를 가져다 씀, 옳은 방식인지는 모르겠다
private BoardService boardService;
private final SimpMessagingTemplate simpMessagingTemplate;

@Autowired
public ChatController(ChatService chatService, SimpMessagingTemplate simpMessagingTemplate) {
public ChatController(
ChatService chatService, BoardService boardService, SimpMessagingTemplate simpMessagingTemplate) {
this.chatService = chatService;
this.boardService = boardService;
this.simpMessagingTemplate = simpMessagingTemplate;
}

Expand All @@ -86,10 +95,10 @@ public RoomResponseDto createRoom(@RequestBody @Valid RoomDto roomDto) { // 채
// 채팅방 입장 시 채팅 내역 불러오기
@GetMapping("/api/chat/room")
public ChatListResponseDto getChatList(@RequestParam String roomId) {
logger.info("ChatController.getChatLIst() is called");
logger.info("ChatController.getChatList() is called");

// RoomId로 MongoDB에서 채팅 내력 리스트 반환
logger.info("ChatController.getChatLIst() : ChatList 호출");
logger.info("ChatController.getChatList() : ChatList 호출");
List<ChatDto> chatList = chatService.getChatList(roomId);

logger.info(chatList.toString());
Expand Down Expand Up @@ -123,4 +132,52 @@ public void sendMessage(ChatDto chatDto, String roomId) throws ParseException {
logger.info("ChatController.sendMessage() : 채팅 전송");
simpMessagingTemplate.convertAndSend("/topic/chat/" + chatDto.getRoomId(), chatDto);
}

// 채팅방 내역 불러오기
@GetMapping("/api/chat/list")
public RoomListResponseDto getRoomList(@RequestParam String userId) {
logger.info("ChatController.getRoomList() is called");
// 내가 판매중인, 구매중인 채팅 내역을 모두 가져와야 함
// RoomEntity의 SellerId, UserId를 모두 검색 -> JOIN?, 최신(역순)으로 출력
// 최신 채팅을 기준으로 출력해야 하지만, 시간 상 그냥 출력하기로 결정
// FRONT에서 SellerId와 UserId가 같으면 Disabled 처리 해뒀음

// 채팅 목록 조회
logger.info("ChatController.getRoomList() : " + userId + "의 정보를 탐색");
List<RoomDto> roomList = chatService.getRoomList(userId);
logger.info("ChatController.getRoomList() : 채팅 목록 조회 성공");

// 각 채팅의 썸네일 이미지 조회
// BoardService 가져다 쓰기?
// 찾은 RoomList에서 각 Post의 ID로 Image를 찾음
List<ImageDto> imageList = new ArrayList<>();
for (RoomDto roomDto : roomList) {
// 썸네일로 사용할 Image 1개만 필요
imageList.add(boardService.getPostImage(roomDto.getPostNum()));
}
logger.info("BoardController.getPostList() : 게시글의 이미지 정보를 찾음");

// 마지막 채팅 정보 조회
// Message, CreatedAt
// 리스트들의 개수가 일치해야 함, 채팅 내역이 없어도 리스트에는 추가
// 어떻게 최신 내역 1개만 가져오는가
// 내림차순 조회 + 0번 선택?
// 최신순 정렬은 어떻게? front에서 처리?
List<ChatDto> chatList = new ArrayList<>();
for (RoomDto roomDto : roomList) {
List<ChatDto> chats = chatService.getChatList(roomDto.getRoomId());
if (chats.size() == 0) {
chats.add(ChatDto.builder().message("No Data").build());
}
chatList.add(chats.get(chats.size() - 1)); // size or size-1
}

return RoomListResponseDto.builder()
.status(HttpStatus.OK)
.message("채팅 목록 조회 성공")
.roomList(roomList)
.imageList(imageList)
.chatList(chatList)
.build();
}
}
2 changes: 2 additions & 0 deletions src/main/java/com/tomato/market/dao/ChatDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public interface ChatDao {
ChatCollection save(ChatCollection chatCollection);

List<ChatCollection> findByRoomId(String roomId);

List<RoomEntity> findBySellerIdOrUserId(String sellerId, String userId);
}
14 changes: 14 additions & 0 deletions src/main/java/com/tomato/market/dao/impl/ChatDaoImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,18 @@ public List<ChatCollection> findByRoomId(String roomId) {
return result;
}
}

@Override
public List<RoomEntity> findBySellerIdOrUserId(String sellerId, String userId) {
logger.info("ChatDaoImpl.findBySellerIdOrUserId() is called");

List<RoomEntity> roomEntities = roomRepository.findBySellerIdOrUserId(sellerId, userId);
if (roomEntities == null) {
logger.warn("ChatDaoImpl.findBySellerIdOrUserId() : 채팅 목록 조회 실패");
return null;
} else {
logger.info("ChatDaoImpl.findBySellerIdOrUserId() : 채팅 목록 조회 성공");
return roomEntities;
}
}
}
22 changes: 22 additions & 0 deletions src/main/java/com/tomato/market/data/dto/RoomListResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.tomato.market.data.dto;

import org.springframework.http.HttpStatus;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class RoomListResponseDto {
HttpStatus status;
String message;
Object roomList;
Object imageList;
Object chatList;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@

public interface ChatRepository extends MongoRepository<ChatCollection, String> {
List<ChatCollection> findByRoomId(String roomId);

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.tomato.market.data.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import com.tomato.market.data.entity.RoomEntity;

public interface RoomRepository extends JpaRepository<RoomEntity, Integer> {
RoomEntity findByUserIdAndPostNum(String userId, Integer postNum);

List<RoomEntity> findBySellerIdOrUserId(String sellerId, String userId);
}
2 changes: 2 additions & 0 deletions src/main/java/com/tomato/market/service/ChatService.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ public interface ChatService {
public void saveChat(ChatDto chatDto);

public List<ChatDto> getChatList(String roomId);

public List<RoomDto> getRoomList(String userId);
}
22 changes: 22 additions & 0 deletions src/main/java/com/tomato/market/service/impl/ChatServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,26 @@ public List<ChatDto> getChatList(String roomId) {

return chatDtoList;
}

public List<RoomDto> getRoomList(String userId) {
logger.info("ChatServiceImpl.getRoomList() is called");

List<RoomEntity> roomEntities = chatDao.findBySellerIdOrUserId(userId, userId);
if (roomEntities == null) {
logger.warn("ChatServiceImpl.getRoomList() : 채팅 목록 조회 실패");
throw new ChatException("채팅 목록 조회에 실패했습니다.");
}

// roomEntities.size() == 0일 수 있음
logger.warn("ChatServiceImpl.getRoomList() : 채팅 목록 조회 성공");
// Entity -> DTO 변환
List<RoomDto> roomDtoList = new ArrayList<>();
for (RoomEntity roomEntity : roomEntities) {
roomDtoList.add(
RoomDto.toRoomDto(roomEntity)
);
}

return roomDtoList;
}
}
67 changes: 65 additions & 2 deletions src/test/java/com/tomato/market/controller/ChatControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
Expand Down Expand Up @@ -32,8 +33,10 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import com.tomato.market.data.dto.ChatDto;
import com.tomato.market.data.dto.ImageDto;
import com.tomato.market.data.dto.RoomDto;
import com.tomato.market.handler.exception.ChatException;
import com.tomato.market.service.impl.BoardServiceImpl;
import com.tomato.market.service.impl.ChatServiceImpl;

@WebMvcTest(ChatController.class)
Expand All @@ -43,6 +46,8 @@ public class ChatControllerTest {
@MockBean
private ChatServiceImpl chatService;
@MockBean
private BoardServiceImpl boardService;
@MockBean
private SimpMessagingTemplate simpMessagingTemplate;
@Autowired
private WebApplicationContext ctx;
Expand All @@ -66,6 +71,17 @@ public class ChatControllerTest {
// Content
private String content;

// RoomList
private List<RoomDto> roomList;

// Image
private ImageDto imageDto;
private String imageName = "original.png";
private String uuid = "uuidoriginal.png";

// ImageList
private List<ImageDto> imageList;

@BeforeEach
void setUp() {
mockmvc = MockMvcBuilders.webAppContextSetup(ctx)
Expand Down Expand Up @@ -102,6 +118,21 @@ void setUp() {
.sender(sender)
.message(message)
.build();


roomList = new ArrayList<>();
roomList.add(roomDto);
roomList.add(roomDto);

imageDto = ImageDto.builder()
.postNum(postNum)
.imageName(imageName)
.uuid(uuid)
.build();

imageList = new ArrayList<>();
imageList.add(imageDto);
imageList.add(imageDto);
}

@Test
Expand Down Expand Up @@ -183,7 +214,7 @@ void sendMessageSuccess() throws ParseException {
// 전송 실패는 없음, 원하지 않은 곳으로 전송될 뿐임
doNothing().when(simpMessagingTemplate).convertAndSend(any(String.class), any(ChatDto.class));

ChatController chatController = new ChatController(chatService, simpMessagingTemplate);
ChatController chatController = new ChatController(chatService, boardService, simpMessagingTemplate);
chatController.sendMessage(chatDto, roomId);

verify(chatService).saveChat(chatDto);
Expand All @@ -195,12 +226,44 @@ void sendMessageSuccess() throws ParseException {
void saveMessageFailure() throws ParseException {
doThrow(new ChatException("채팅을 저장하지 못했습니다.")).when(chatService).saveChat(chatDto);

ChatController chatController = new ChatController(chatService, simpMessagingTemplate);
ChatController chatController = new ChatController(chatService, boardService, simpMessagingTemplate);
ChatException exception = Assertions.assertThrows(ChatException.class, () -> {
chatController.sendMessage(chatDto, roomId);
});
Assertions.assertEquals(exception.getMessage(), "채팅을 저장하지 못했습니다.");

verify(chatService).saveChat(chatDto);
}

@Test
@DisplayName("채팅_방_목록_조회_성공")
void getRoomListSuccess() throws Exception {
given(chatService.getRoomList(any(String.class))).willReturn(roomList);
given(boardService.getPostImage(any(Integer.class))).willReturn(imageDto);
given(chatService.getChatList(any(String.class))).willReturn(chatList);

mockmvc.perform(get("/api/chat/list").param("userId", userId))
.andExpect(status().isOk())
.andExpect(jsonPath("$.message", is("채팅 목록 조회 성공")))
.andExpect(jsonPath("$.roomList").exists())
.andExpect(jsonPath("$.imageList").exists())
.andDo(print());

verify(chatService).getRoomList(any(String.class));
verify(boardService, times(2)).getPostImage(any(Integer.class));
verify(chatService, times(2)).getChatList(any(String.class));
}

@Test
@DisplayName("채팅_방_목록_조회_실패")
void getRoomListFailure() throws Exception {
given(chatService.getRoomList(any(String.class))).willThrow(new ChatException("채팅 목록 조회에 실패했습니다."));

mockmvc.perform(get("/api/chat/list").param("userId", userId))
.andExpect(status().isOk())
.andExpect(jsonPath("$.message", is("채팅 목록 조회에 실패했습니다.")))
.andDo(print());

verify(chatService).getRoomList(any(String.class));
}
}
32 changes: 32 additions & 0 deletions src/test/java/com/tomato/market/service/ChatServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public class ChatServiceTest {
private ChatCollection chatCollection;
private List<ChatCollection> chatCollectionList;

// RoomList
private List<RoomEntity> roomEntities;

@BeforeEach
void setUp() {
roomDto = RoomDto.builder()
Expand Down Expand Up @@ -91,6 +94,10 @@ void setUp() {
chatCollectionList = new ArrayList<>();
chatCollectionList.add(chatCollection);
chatCollectionList.add(chatCollection);

roomEntities = new ArrayList<>();
roomEntities.add(roomEntity);
roomEntities.add(roomEntity);
}

@Test
Expand Down Expand Up @@ -182,4 +189,29 @@ void getChatListFailure() {

verify(chatDao).findByRoomId(roomId);
}

@Test
@DisplayName("채팅_목록_조회_성공")
void getRoomListSuccess() {
given(chatDao.findBySellerIdOrUserId(userId, userId)).willReturn(roomEntities);

ChatServiceImpl chatService = new ChatServiceImpl(chatDao);
Assertions.assertEquals(chatService.getRoomList(userId).size(), 2);

verify(chatDao).findBySellerIdOrUserId(userId, userId);
}

@Test
@DisplayName("채팅_목록_조회_실패")
void getRoomListFailure() {
given(chatDao.findBySellerIdOrUserId(userId, userId)).willReturn(null);

ChatServiceImpl chatService = new ChatServiceImpl(chatDao);
ChatException exception = Assertions.assertThrows(ChatException.class, () -> {
chatService.getRoomList(userId);
});
Assertions.assertEquals(exception.getMessage(), "채팅 목록 조회에 실패했습니다.");

verify(chatDao).findBySellerIdOrUserId(userId, userId);
}
}
Loading