diff --git a/src/main/java/com/dnd/gongmuin/chat/controller/ChatRoomController.java b/src/main/java/com/dnd/gongmuin/chat/controller/ChatRoomController.java index a80fad5c..1bf58690 100644 --- a/src/main/java/com/dnd/gongmuin/chat/controller/ChatRoomController.java +++ b/src/main/java/com/dnd/gongmuin/chat/controller/ChatRoomController.java @@ -1,5 +1,7 @@ package com.dnd.gongmuin.chat.controller; +import java.util.List; + import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -14,6 +16,7 @@ import com.dnd.gongmuin.chat.dto.response.AcceptChatResponse; import com.dnd.gongmuin.chat.dto.response.ChatMessageResponse; import com.dnd.gongmuin.chat.dto.response.ChatRoomDetailResponse; +import com.dnd.gongmuin.chat.dto.response.ChatRoomSimpleResponse; import com.dnd.gongmuin.chat.dto.response.RejectChatResponse; import com.dnd.gongmuin.chat.service.ChatRoomService; import com.dnd.gongmuin.common.dto.PageResponse; @@ -52,6 +55,15 @@ public ResponseEntity createChatRoom( return ResponseEntity.ok(response); } + @Operation(summary = "채팅방 목록 조회 API", description = "회원의 채팅방 목록을 조회한다.") + @GetMapping("/api/chat-rooms") + public ResponseEntity> getChatRoomsByMember( + @AuthenticationPrincipal Member member + ) { + List response = chatRoomService.getChatRoomsByMember(member); + return ResponseEntity.ok(response); + } + @Operation(summary = "채팅방 조회 API", description = "채팅방 아이디로 채팅방을 조회한다.") @GetMapping("/api/chat-rooms/{chatRoomId}") public ResponseEntity createChatRoom( diff --git a/src/main/java/com/dnd/gongmuin/chat/dto/ChatMessageMapper.java b/src/main/java/com/dnd/gongmuin/chat/dto/ChatMessageMapper.java index 6ef1bf27..e2852f4a 100644 --- a/src/main/java/com/dnd/gongmuin/chat/dto/ChatMessageMapper.java +++ b/src/main/java/com/dnd/gongmuin/chat/dto/ChatMessageMapper.java @@ -18,6 +18,7 @@ public static ChatMessageResponse toChatMessageResponse( chatMessage.getMemberId(), chatMessage.getContent(), chatMessage.getType().getLabel(), + chatMessage.getIsRead(), chatMessage.getCreatedAt().toString() ); } diff --git a/src/main/java/com/dnd/gongmuin/chat/dto/request/ChatMessageRequest.java b/src/main/java/com/dnd/gongmuin/chat/dto/request/ChatMessageRequest.java index 90654923..3fe88baf 100644 --- a/src/main/java/com/dnd/gongmuin/chat/dto/request/ChatMessageRequest.java +++ b/src/main/java/com/dnd/gongmuin/chat/dto/request/ChatMessageRequest.java @@ -10,6 +10,6 @@ public record ChatMessageRequest( String type, @NotNull(message = "회원 아이디를 입력해주세요.") - Long memberId + Long senderId ) { } diff --git a/src/main/java/com/dnd/gongmuin/chat/dto/request/CreateChatRoomRequest.java b/src/main/java/com/dnd/gongmuin/chat/dto/request/CreateChatRoomRequest.java index 40f3e7bd..d6ae8686 100644 --- a/src/main/java/com/dnd/gongmuin/chat/dto/request/CreateChatRoomRequest.java +++ b/src/main/java/com/dnd/gongmuin/chat/dto/request/CreateChatRoomRequest.java @@ -6,7 +6,7 @@ public record CreateChatRoomRequest( @NotNull(message = "질문 게시글 아이디는 필수 입력 항목입니다.") Long questionPostId, - @NotNull(message = "답변 아이디는 필수 입력 항목입니다.") + @NotNull(message = "답변자 아이디는 필수 입력 항목입니다.") Long answererId ) { } diff --git a/src/main/java/com/dnd/gongmuin/chat/dto/response/ChatMessageResponse.java b/src/main/java/com/dnd/gongmuin/chat/dto/response/ChatMessageResponse.java index c436ce89..368fb1c7 100644 --- a/src/main/java/com/dnd/gongmuin/chat/dto/response/ChatMessageResponse.java +++ b/src/main/java/com/dnd/gongmuin/chat/dto/response/ChatMessageResponse.java @@ -4,6 +4,7 @@ public record ChatMessageResponse( Long senderId, String content, String type, + boolean isRead, String createdAt ) { } diff --git a/src/main/java/com/dnd/gongmuin/chat/dto/response/ChatRoomInfo.java b/src/main/java/com/dnd/gongmuin/chat/dto/response/ChatRoomInfo.java new file mode 100644 index 00000000..52bc26f3 --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/chat/dto/response/ChatRoomInfo.java @@ -0,0 +1,29 @@ +package com.dnd.gongmuin.chat.dto.response; + +import com.dnd.gongmuin.member.domain.JobGroup; +import com.querydsl.core.annotations.QueryProjection; + +public record ChatRoomInfo( + Long chatRoomId, + Long partnerId, + String partnerNickname, + String partnerJobGroup, + int partnerProfileImageNo +) { + @QueryProjection + public ChatRoomInfo( + Long chatRoomId, + Long partnerId, + String partnerNickname, + JobGroup partnerJobGroup, + int partnerProfileImageNo + ) { + this( + chatRoomId, + partnerId, + partnerNickname, + partnerJobGroup.getLabel(), + partnerProfileImageNo + ); + } +} diff --git a/src/main/java/com/dnd/gongmuin/chat/dto/response/ChatRoomSimpleResponse.java b/src/main/java/com/dnd/gongmuin/chat/dto/response/ChatRoomSimpleResponse.java new file mode 100644 index 00000000..136b893c --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/chat/dto/response/ChatRoomSimpleResponse.java @@ -0,0 +1,12 @@ +package com.dnd.gongmuin.chat.dto.response; + +import com.dnd.gongmuin.question_post.dto.response.MemberInfo; + +public record ChatRoomSimpleResponse( + Long chatRoomId, + MemberInfo chatPartner, + String latestMessage, + String messageType, + String messageCreatedAt +) { +} diff --git a/src/main/java/com/dnd/gongmuin/chat/dto/response/LatestChatMessage.java b/src/main/java/com/dnd/gongmuin/chat/dto/response/LatestChatMessage.java new file mode 100644 index 00000000..57934805 --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/chat/dto/response/LatestChatMessage.java @@ -0,0 +1,11 @@ +package com.dnd.gongmuin.chat.dto.response; + +import java.time.LocalDateTime; + +public record LatestChatMessage( + Long chatRoomId, + String content, + String type, + LocalDateTime createdAt +) { +} diff --git a/src/main/java/com/dnd/gongmuin/chat/repository/ChatMessageQueryRepository.java b/src/main/java/com/dnd/gongmuin/chat/repository/ChatMessageQueryRepository.java new file mode 100644 index 00000000..50c7f193 --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/chat/repository/ChatMessageQueryRepository.java @@ -0,0 +1,40 @@ +package com.dnd.gongmuin.chat.repository; + +import java.util.List; + +import org.springframework.data.domain.Sort; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.aggregation.Aggregation; +import org.springframework.data.mongodb.core.aggregation.AggregationOperation; +import org.springframework.data.mongodb.core.aggregation.AggregationResults; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.stereotype.Component; + +import com.dnd.gongmuin.chat.dto.response.LatestChatMessage; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Component +public class ChatMessageQueryRepository { + + private final MongoTemplate mongoTemplate; + + public List findLatestChatByChatRoomIds(List chatRoomIds) { + AggregationOperation sort = Aggregation.sort(Sort.Direction.DESC, "createdAt"); + AggregationOperation match = Aggregation.match(Criteria.where("chatRoomId").in(chatRoomIds)); + AggregationOperation group = Aggregation.group("chatRoomId") + .first("createdAt").as("createdAt") + .first("content").as("content") + .first("type").as("type"); + AggregationOperation project = Aggregation.project() + .and("_id").as("chatRoomId") + .and("createdAt").as("createdAt") + .and("content").as("content") + .and("type").as("type"); + Aggregation aggregation = Aggregation.newAggregation(sort, match, group, project); + AggregationResults results = mongoTemplate.aggregate(aggregation, "chat_messages", + LatestChatMessage.class); + return results.getMappedResults(); + } +} diff --git a/src/main/java/com/dnd/gongmuin/chat/repository/ChatRoomQueryRepository.java b/src/main/java/com/dnd/gongmuin/chat/repository/ChatRoomQueryRepository.java new file mode 100644 index 00000000..e4224266 --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/chat/repository/ChatRoomQueryRepository.java @@ -0,0 +1,10 @@ +package com.dnd.gongmuin.chat.repository; + +import java.util.List; + +import com.dnd.gongmuin.chat.dto.response.ChatRoomInfo; +import com.dnd.gongmuin.member.domain.Member; + +public interface ChatRoomQueryRepository { + List getChatRoomsByMember(Member member); +} diff --git a/src/main/java/com/dnd/gongmuin/chat/repository/ChatRoomQueryRepositoryImpl.java b/src/main/java/com/dnd/gongmuin/chat/repository/ChatRoomQueryRepositoryImpl.java new file mode 100644 index 00000000..80aa490f --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/chat/repository/ChatRoomQueryRepositoryImpl.java @@ -0,0 +1,45 @@ +package com.dnd.gongmuin.chat.repository; + +import static com.dnd.gongmuin.chat.domain.QChatRoom.*; + +import java.util.List; + +import com.dnd.gongmuin.chat.dto.response.ChatRoomInfo; +import com.dnd.gongmuin.chat.dto.response.QChatRoomInfo; +import com.dnd.gongmuin.member.domain.Member; +import com.querydsl.core.types.dsl.CaseBuilder; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class ChatRoomQueryRepositoryImpl implements ChatRoomQueryRepository { + private final JPAQueryFactory queryFactory; + + public List getChatRoomsByMember(Member member) { + return queryFactory + .select(new QChatRoomInfo( + chatRoom.id, + new CaseBuilder() + .when(chatRoom.inquirer.id.eq(member.getId())) + .then(chatRoom.answerer.id) + .otherwise(chatRoom.inquirer.id), + new CaseBuilder() + .when(chatRoom.inquirer.id.eq(member.getId())) + .then(chatRoom.answerer.nickname) + .otherwise(chatRoom.inquirer.nickname), + new CaseBuilder() + .when(chatRoom.inquirer.id.eq(member.getId())) + .then(chatRoom.answerer.jobGroup) + .otherwise(chatRoom.inquirer.jobGroup), + new CaseBuilder() + .when(chatRoom.inquirer.id.eq(member.getId())) + .then(chatRoom.answerer.profileImageNo) + .otherwise(chatRoom.inquirer.profileImageNo) + )) + .from(chatRoom) + .where(chatRoom.inquirer.id.eq(member.getId()) + .or(chatRoom.answerer.id.eq(member.getId()))) + .fetch(); + } +} diff --git a/src/main/java/com/dnd/gongmuin/chat/repository/ChatRoomRepository.java b/src/main/java/com/dnd/gongmuin/chat/repository/ChatRoomRepository.java index 7ea09775..8499787d 100644 --- a/src/main/java/com/dnd/gongmuin/chat/repository/ChatRoomRepository.java +++ b/src/main/java/com/dnd/gongmuin/chat/repository/ChatRoomRepository.java @@ -4,5 +4,5 @@ import com.dnd.gongmuin.chat.domain.ChatRoom; -public interface ChatRoomRepository extends JpaRepository { +public interface ChatRoomRepository extends JpaRepository, ChatRoomQueryRepository { } diff --git a/src/main/java/com/dnd/gongmuin/chat/service/ChatMessageService.java b/src/main/java/com/dnd/gongmuin/chat/service/ChatMessageService.java index f001e5db..5089b666 100644 --- a/src/main/java/com/dnd/gongmuin/chat/service/ChatMessageService.java +++ b/src/main/java/com/dnd/gongmuin/chat/service/ChatMessageService.java @@ -24,10 +24,10 @@ public ChatMessageResponse saveChatMessage( ChatMessageRequest request, Long chatRoomId ) { - Long memberId = request.memberId(); + Long senderId = request.senderId(); ChatMessage chatMessage = chatMessageRepository.save( - ChatMessageMapper.toChatMessage(request, chatRoomId, memberId)); - log.info("chatRoomId = {}, memberId= {}, chatMessageId= {}", chatRoomId, memberId, chatMessage.getId()); + ChatMessageMapper.toChatMessage(request, chatRoomId, senderId)); + log.info("chatRoomId = {}, senderId= {}, chatMessageId= {}", chatRoomId, senderId, chatMessage.getId()); return ChatMessageMapper.toChatMessageResponse(chatMessage); } } diff --git a/src/main/java/com/dnd/gongmuin/chat/service/ChatRoomService.java b/src/main/java/com/dnd/gongmuin/chat/service/ChatRoomService.java index 1008ed00..69dced06 100644 --- a/src/main/java/com/dnd/gongmuin/chat/service/ChatRoomService.java +++ b/src/main/java/com/dnd/gongmuin/chat/service/ChatRoomService.java @@ -1,6 +1,10 @@ package com.dnd.gongmuin.chat.service; +import java.util.Comparator; +import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -14,8 +18,12 @@ import com.dnd.gongmuin.chat.dto.response.AcceptChatResponse; import com.dnd.gongmuin.chat.dto.response.ChatMessageResponse; import com.dnd.gongmuin.chat.dto.response.ChatRoomDetailResponse; +import com.dnd.gongmuin.chat.dto.response.ChatRoomInfo; +import com.dnd.gongmuin.chat.dto.response.ChatRoomSimpleResponse; +import com.dnd.gongmuin.chat.dto.response.LatestChatMessage; import com.dnd.gongmuin.chat.dto.response.RejectChatResponse; import com.dnd.gongmuin.chat.exception.ChatErrorCode; +import com.dnd.gongmuin.chat.repository.ChatMessageQueryRepository; import com.dnd.gongmuin.chat.repository.ChatMessageRepository; import com.dnd.gongmuin.chat.repository.ChatRoomRepository; import com.dnd.gongmuin.common.dto.PageMapper; @@ -26,6 +34,7 @@ import com.dnd.gongmuin.member.exception.MemberErrorCode; import com.dnd.gongmuin.member.repository.MemberRepository; import com.dnd.gongmuin.question_post.domain.QuestionPost; +import com.dnd.gongmuin.question_post.dto.response.MemberInfo; import com.dnd.gongmuin.question_post.exception.QuestionPostErrorCode; import com.dnd.gongmuin.question_post.repository.QuestionPostRepository; @@ -36,6 +45,7 @@ public class ChatRoomService { private final ChatMessageRepository chatMessageRepository; + private final ChatMessageQueryRepository chatMessageQueryRepository; private final ChatRoomRepository chatRoomRepository; private final MemberRepository memberRepository; private final QuestionPostRepository questionPostRepository; @@ -66,6 +76,37 @@ public ChatRoomDetailResponse createChatRoom(CreateChatRoomRequest request, Memb ); } + @Transactional(readOnly = true) + public List getChatRoomsByMember(Member member) { + // 회원 채팅방 정보 가져오기 + List chatRoomInfos = chatRoomRepository.getChatRoomsByMember(member); + // chatRoomId 리스트 추출 + List chatRoomIds = chatRoomInfos.stream() + .map(ChatRoomInfo::chatRoomId) + .toList(); + // 각 채팅방 최근 메시지 정보 + List latestChatMessages = chatMessageQueryRepository.findLatestChatByChatRoomIds( + chatRoomIds); + // -> 순서 보장 x + Map messageMap = latestChatMessages.stream() + .collect(Collectors.toMap(LatestChatMessage::chatRoomId, message -> message)); + // 최신순 정렬 + return chatRoomInfos.stream() + .sorted( + Comparator.comparing((ChatRoomInfo info) -> messageMap.get(info.chatRoomId()).createdAt()).reversed()) + .map(chatRoomInfo -> { + LatestChatMessage latestMessage = messageMap.get(chatRoomInfo.chatRoomId()); + return new ChatRoomSimpleResponse( + chatRoomInfo.chatRoomId(), + new MemberInfo(chatRoomInfo.partnerId(), chatRoomInfo.partnerNickname(), + chatRoomInfo.partnerJobGroup(), chatRoomInfo.partnerProfileImageNo()), + latestMessage.content(), + latestMessage.type(), + latestMessage.createdAt().toString() + ); + }).toList(); + } + @Transactional(readOnly = true) public ChatRoomDetailResponse getChatRoomById(Long chatRoomId, Member member) { ChatRoom chatRoom = getChatRoomById(chatRoomId); diff --git a/src/main/java/com/dnd/gongmuin/member/domain/Member.java b/src/main/java/com/dnd/gongmuin/member/domain/Member.java index 37e77af5..87b1ce57 100644 --- a/src/main/java/com/dnd/gongmuin/member/domain/Member.java +++ b/src/main/java/com/dnd/gongmuin/member/domain/Member.java @@ -62,16 +62,6 @@ private Member(String nickname, String socialName, JobGroup jobGroup, JobCategor this.role = role; } - @Override - public String toString() { - return "MEMBER INFO{" + - "id=" + id + - ", role='" + role + '\'' + - ", jobGroup=" + jobGroup + - ", socialEmail='" + socialEmail + '\'' + - '}'; - } - public static Member of(String socialName, String socialEmail, int credit) { return Member.builder() .socialName(socialName) @@ -103,6 +93,16 @@ public static Member of(String nickname, String socialName, JobGroup jobGroup, J .build(); } + @Override + public String toString() { + return "MEMBER INFO{" + + "id=" + id + + ", role='" + role + '\'' + + ", jobGroup=" + jobGroup + + ", socialEmail='" + socialEmail + '\'' + + '}'; + } + public void updateSocialEmail(String socialEmail) { this.socialEmail = socialEmail; } diff --git a/src/test/java/com/dnd/gongmuin/chat/controller/ChatRoomControllerTest.java b/src/test/java/com/dnd/gongmuin/chat/controller/ChatRoomControllerTest.java index de61ba91..68fcfd5e 100644 --- a/src/test/java/com/dnd/gongmuin/chat/controller/ChatRoomControllerTest.java +++ b/src/test/java/com/dnd/gongmuin/chat/controller/ChatRoomControllerTest.java @@ -4,12 +4,14 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import java.time.LocalDateTime; import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import com.dnd.gongmuin.chat.domain.ChatMessage; import com.dnd.gongmuin.chat.domain.ChatRoom; @@ -64,7 +66,8 @@ void getChatMessages() throws Exception { .andExpect(jsonPath("$.size").value(2)) .andExpect(jsonPath("$.content[0].senderId").value(chatMessages.get(0).getMemberId())) .andExpect(jsonPath("$.content[0].content").value(chatMessages.get(0).getContent())) - .andExpect(jsonPath("$.content[0].type").value(chatMessages.get(0).getType().getLabel())); + .andExpect(jsonPath("$.content[0].type").value(chatMessages.get(0).getType().getLabel())) + .andExpect(jsonPath("$.content[0].isRead").value(chatMessages.get(0).getIsRead())); } @DisplayName("[채팅방을 생성할 수 있다.]") @@ -81,6 +84,50 @@ void createChatRoom() throws Exception { .andExpect(status().isOk()); } + @DisplayName("[회원의 채팅방 목록을 조회할 수 있다.]") + @Test + void getChatRoomsByMember() throws Exception { + //given + Member member1 = memberRepository.save(MemberFixture.member4()); + Member member2 = memberRepository.save(MemberFixture.member5()); + List questionPosts = questionPostRepository.saveAll( + List.of( + questionPostRepository.save(QuestionPostFixture.questionPost(member1)), + questionPostRepository.save(QuestionPostFixture.questionPost(member2)) + ) + ); + ChatRoom chatRoom1 = chatRoomRepository.save( + ChatRoomFixture.chatRoom(questionPosts.get(0), member1, loginMember)); + ChatRoom chatRoom2 = chatRoomRepository.save( + ChatRoomFixture.chatRoom(questionPosts.get(0), member2, loginMember)); + ChatRoom chatRoom3 = chatRoomRepository.save( + ChatRoomFixture.chatRoom(questionPosts.get(1), loginMember, member1)); + ChatRoom chatRoom4 = chatRoomRepository.save(ChatRoomFixture.chatRoom(questionPosts.get(1), member2, member1)); + chatMessageRepository.saveAll( + List.of( + chatMessageRepository.save( + ChatMessageFixture.chatMessage(chatRoom1.getId(), "11", LocalDateTime.now())), + chatMessageRepository.save( + ChatMessageFixture.chatMessage(chatRoom1.getId(), "12", LocalDateTime.now())), + chatMessageRepository.save( + ChatMessageFixture.chatMessage(chatRoom2.getId(), "21", LocalDateTime.now())), + chatMessageRepository.save( + ChatMessageFixture.chatMessage(chatRoom2.getId(), "22", LocalDateTime.now())), + chatMessageRepository.save( + ChatMessageFixture.chatMessage(chatRoom3.getId(), "31", LocalDateTime.now())), + chatMessageRepository.save( + ChatMessageFixture.chatMessage(chatRoom3.getId(), "32", LocalDateTime.now())), + chatMessageRepository.save( + ChatMessageFixture.chatMessage(chatRoom4.getId(), "41", LocalDateTime.now())), + chatMessageRepository.save(ChatMessageFixture.chatMessage(chatRoom4.getId(), "42", LocalDateTime.now())) + ) + ); + mockMvc.perform(get("/api/chat-rooms") + .cookie(accessToken)) + .andExpect(status().isOk()) + .andDo(MockMvcResultHandlers.print()); + } + @DisplayName("[채팅방 아이디로 채팅방을 상세조회할 수 있다.]") @Test void getChatRoomById() throws Exception { diff --git a/src/test/java/com/dnd/gongmuin/chat/repository/ChatMessageQueryRepositoryTest.java b/src/test/java/com/dnd/gongmuin/chat/repository/ChatMessageQueryRepositoryTest.java new file mode 100644 index 00000000..085ccf7a --- /dev/null +++ b/src/test/java/com/dnd/gongmuin/chat/repository/ChatMessageQueryRepositoryTest.java @@ -0,0 +1,50 @@ +package com.dnd.gongmuin.chat.repository; + +import static org.assertj.core.api.Assertions.*; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import com.dnd.gongmuin.chat.dto.response.LatestChatMessage; +import com.dnd.gongmuin.common.fixture.ChatMessageFixture; +import com.dnd.gongmuin.common.support.TestContainerSupport; + +@SpringBootTest +class ChatMessageQueryRepositoryTest extends TestContainerSupport { + + @Autowired + ChatMessageQueryRepository chatMessageQueryRepository; + @Autowired + ChatMessageRepository chatMessageRepository; + + @DisplayName("채팅방 아이디 리스트로 각 채팅방의 최근 채팅 메시지를 가져올 수 있다.") + @Test + void findLatestChatByChatRoomIds() { + //given + chatMessageRepository.saveAll(List.of( + ChatMessageFixture.chatMessage(1L, "첫번째 채팅방 첫번째 메시지", LocalDateTime.now()), + ChatMessageFixture.chatMessage(1L, "첫번째 채팅방 두번째 메시지", LocalDateTime.now().plusHours(11)), + ChatMessageFixture.chatMessage(2L, "두번째 채팅방 첫번째 메시지", LocalDateTime.now()), + ChatMessageFixture.chatMessage(2L, "두번째 채팅방 두번째 메시지", LocalDateTime.now().plusHours(22)), + ChatMessageFixture.chatMessage(3L, "세번째 채팅방 첫번째 메시지", LocalDateTime.now()) + )); + + //when + List responses = chatMessageQueryRepository.findLatestChatByChatRoomIds( + List.of(1L, 2L)); + + //then + Assertions.assertAll( + () -> assertThat(responses).hasSize(2), + () -> assertThat(responses.stream().map(LatestChatMessage::content).collect(Collectors.toList())) + .containsExactlyInAnyOrder("두번째 채팅방 두번째 메시지", "첫번째 채팅방 두번째 메시지") // 순서 보장x + ); + } +} \ No newline at end of file diff --git a/src/test/java/com/dnd/gongmuin/chat/repository/ChatRoomRepositoryTest.java b/src/test/java/com/dnd/gongmuin/chat/repository/ChatRoomRepositoryTest.java new file mode 100644 index 00000000..8367a211 --- /dev/null +++ b/src/test/java/com/dnd/gongmuin/chat/repository/ChatRoomRepositoryTest.java @@ -0,0 +1,56 @@ +package com.dnd.gongmuin.chat.repository; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import com.dnd.gongmuin.chat.domain.ChatRoom; +import com.dnd.gongmuin.chat.dto.response.ChatRoomInfo; +import com.dnd.gongmuin.common.fixture.ChatRoomFixture; +import com.dnd.gongmuin.common.fixture.MemberFixture; +import com.dnd.gongmuin.common.fixture.QuestionPostFixture; +import com.dnd.gongmuin.common.support.DataJpaTestSupport; +import com.dnd.gongmuin.member.domain.Member; +import com.dnd.gongmuin.member.repository.MemberRepository; +import com.dnd.gongmuin.question_post.domain.QuestionPost; +import com.dnd.gongmuin.question_post.repository.QuestionPostRepository; + +@DisplayName("[ChatRoomRepository 테스트]") +class ChatRoomRepositoryTest extends DataJpaTestSupport { + @Autowired + ChatRoomRepository chatRoomRepository; + @Autowired + MemberRepository memberRepository; + @Autowired + QuestionPostRepository questionPostRepository; + + @DisplayName("회원이 속한 채팅방을 모두 조회할 수 있다.") + @Test + void getChatRoomsByMember() { + //given + Member questioner = memberRepository.save(MemberFixture.member()); + Member target = memberRepository.save(MemberFixture.member()); + Member answerer = memberRepository.save(MemberFixture.member()); + QuestionPost questionPost = questionPostRepository.save(QuestionPostFixture.questionPost(questioner)); + List chatRooms = chatRoomRepository.saveAll(List.of( + chatRoomRepository.save(ChatRoomFixture.chatRoom(questionPost, questioner, answerer)), + chatRoomRepository.save(ChatRoomFixture.chatRoom(questionPost, questioner, target)), + chatRoomRepository.save(ChatRoomFixture.chatRoom(questionPost, target, answerer)) + )); + //when + List chatRoomInfos = chatRoomRepository.getChatRoomsByMember(target); + //then + Assertions.assertAll( + () -> assertThat(chatRoomInfos).hasSize(2), + () -> assertThat(chatRoomInfos.get(0).chatRoomId()).isEqualTo(chatRooms.get(1).getId()), + () -> assertThat(chatRoomInfos.get(0).partnerId()).isEqualTo(questioner.getId()), + () -> assertThat(chatRoomInfos.get(1).chatRoomId()).isEqualTo(chatRooms.get(2).getId()), + () -> assertThat(chatRoomInfos.get(1).partnerId()).isEqualTo(answerer.getId()) + ); + } +} \ No newline at end of file diff --git a/src/test/java/com/dnd/gongmuin/common/fixture/ChatMessageFixture.java b/src/test/java/com/dnd/gongmuin/common/fixture/ChatMessageFixture.java index e3f9e909..f907d048 100644 --- a/src/test/java/com/dnd/gongmuin/common/fixture/ChatMessageFixture.java +++ b/src/test/java/com/dnd/gongmuin/common/fixture/ChatMessageFixture.java @@ -1,5 +1,9 @@ package com.dnd.gongmuin.common.fixture; +import java.time.LocalDateTime; + +import org.springframework.test.util.ReflectionTestUtils; + import com.dnd.gongmuin.chat.domain.ChatMessage; import com.dnd.gongmuin.chat.domain.MessageType; @@ -17,4 +21,15 @@ public static ChatMessage chatMessage() { MessageType.TEXT ); } + + public static ChatMessage chatMessage(Long chatRoomId, String content, LocalDateTime createdAt) { + ChatMessage chatmessage = ChatMessage.of( + content, + chatRoomId, + 1L, + MessageType.TEXT + ); + ReflectionTestUtils.setField(chatmessage, "createdAt", createdAt); + return chatmessage; + } } diff --git a/src/test/java/com/dnd/gongmuin/common/fixture/MemberFixture.java b/src/test/java/com/dnd/gongmuin/common/fixture/MemberFixture.java index bf8dad0f..bc0bc2d5 100644 --- a/src/test/java/com/dnd/gongmuin/common/fixture/MemberFixture.java +++ b/src/test/java/com/dnd/gongmuin/common/fixture/MemberFixture.java @@ -62,6 +62,19 @@ public static Member member4() { ); } + public static Member member5() { + return Member.of( + "회원", + "소셜회원", + JobGroup.AD, + JobCategory.ME, + "KAKAO12345/member2@daum.net", + "member@korea.kr", + 20000, + "ROLE_USER" + ); + } + public static Member member(Long memberId) { Member member = Member.of( "김회원",