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

258 mongo entity 리팩토링 #259

Open
wants to merge 34 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0891f32
Refactor #258 : 사용하지 않는 클래스 삭제
eugene225 Oct 13, 2024
4ce8dc6
refactor #258 : 기존 document 형태변경 및 레포지토리 메서드 구현
eugene225 Oct 13, 2024
8274547
refactor #258 : 기존 collection 생성 관련 메서드 수정 및 새 레포지토리 메서드 적용
eugene225 Oct 14, 2024
f5f35af
refactor #258 : collection 이름 사용하는 메서드 수정
eugene225 Oct 14, 2024
91fe154
refactor #258 : chatRoom 조회 시에는 updatedAt 필드 기준 정렬 및 커서값 수정
eugene225 Oct 15, 2024
93684f2
test #258 : chatRoom 조회 시에는 updatedAt 필드 기준 정렬 및 커서값 수정
eugene225 Oct 15, 2024
da2bf54
refactor #258 : chatMessage 조회 시 objectId 기준 정렬 및 커서 값 수정
eugene225 Oct 15, 2024
bbe8c1d
test #258 : chatMessage 조회 시 objectId 기준 정렬 및 커서값 수정 적용
eugene225 Oct 15, 2024
15c22d9
chore #258 : 정리
eugene225 Oct 15, 2024
fb12724
feat #258 : mongoTemplate에서 사용할 데이터 객체 생성
eugene225 Oct 18, 2024
50dd23d
feat #258 : chatRoom 목록 조회 시 마지막 메시지 정보 필요한 필드만 구성한 dto 생성
eugene225 Oct 18, 2024
d381eed
feat #258 : userId 목록으로 한번에 조회하는 메서드 추가
eugene225 Oct 18, 2024
bfdb9dd
refactor #258 : ChatRoomDto 반환 형태 메시지 정보와 유저 정보 객체 분리
eugene225 Oct 18, 2024
7862625
feat #258 : chatRoomId 목록으로 마지막 메시지 조회 한번에 가져오는 쿼리 구현 및 unReadMsg 개수 …
eugene225 Oct 18, 2024
7dae2fb
chore #258 : 디버깅 코드 삭제
eugene225 Oct 18, 2024
174d3f8
refactor #258 : 리팩토링된 레포지토리 메서드 적용
eugene225 Oct 18, 2024
61f8987
test #258 : ChatRoomDto 변형된 형태 코드 수정
eugene225 Oct 18, 2024
f3f3f66
refactor #258 : service 단 map 키 데이터 타입 통일
eugene225 Oct 18, 2024
0880bf5
refactor #258 : image fetchJoin 추가한 메서드 구현
eugene225 Oct 18, 2024
8e7bac4
refactor #258 : fetchJoin 적용된 레포지토리 메서드 적용
eugene225 Oct 18, 2024
a9135a4
chore #258 : 사용하지 않는 메서드 삭제
eugene225 Oct 24, 2024
44b6ae6
refactor #258 : chatActivity 관련 데이터 MongoDB 이전 및 복합인덱스 설정
eugene225 Oct 25, 2024
ff226f4
refactor #258 : unReadMsgCnt 따로 관리하도록 메서드 수정
eugene225 Oct 25, 2024
7f24bbf
feat #258 : chatActivityDto 생성
eugene225 Oct 25, 2024
d2f541d
feat #258 : unReadMsg count 증가 api 추가
eugene225 Oct 25, 2024
d33a6f3
refactor #258 : 파라미터 수정
eugene225 Oct 25, 2024
a630d87
chore #258 : 사용하지 않는 메서드 삭제
eugene225 Oct 26, 2024
67d2612
refactor #258 : unReadMsgCnt 필드 삭제
eugene225 Oct 26, 2024
4dc1276
refactor #258 : chatActivity 정보 저장 코드 추가
eugene225 Oct 26, 2024
64df0ae
chore #258 : 기존 코드 가독성 향상 시키기
eugene225 Oct 26, 2024
d6d7f2a
chore #258 : 코드 정리
eugene225 Oct 26, 2024
7e62e51
refactor #258 : Aggregation pipeline 활용해 읽지 않은 메시지 수 집계 쿼리 최적화
eugene225 Oct 30, 2024
a6faec1
refactor #258 : chatActivity 관련 데이터 mongoDB로 이전하면서 메서드 파라미터 수정
eugene225 Oct 30, 2024
833da76
test #258 : 채팅방이 2개 이상일 때에도 의도에 맞게 쿼리가 발생하는지 확인
eugene225 Oct 30, 2024
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
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package org.prgms.locomocoserver.chat.application;

import lombok.RequiredArgsConstructor;
import org.prgms.locomocoserver.chat.domain.ChatParticipant;
import org.prgms.locomocoserver.chat.domain.mongo.ChatMessageMongoCustomRepository;
import org.prgms.locomocoserver.chat.domain.querydsl.ChatParticipantCustomRepository;
import org.bson.types.ObjectId;
import org.prgms.locomocoserver.chat.domain.mongo.ChatActivity;
import org.prgms.locomocoserver.chat.domain.mongo.ChatActivityRepository;
import org.prgms.locomocoserver.chat.dto.request.ChatActivityRequestDto;
import org.prgms.locomocoserver.chat.exception.ChatErrorType;
import org.prgms.locomocoserver.chat.exception.ChatException;
Expand All @@ -14,21 +14,13 @@
@RequiredArgsConstructor
public class ChatActivityService {

private final ChatParticipantCustomRepository chatParticipantRepository;
private final ChatMessageMongoCustomRepository chatMessageMongoCustomRepository;
private final ChatActivityRepository chatActivityRepository;

@Transactional
public void updateLastReadMessage(Long chatRoomId, ChatActivityRequestDto requestDto) {
ChatParticipant chatParticipant = chatParticipantRepository.findByUserIdAndChatRoomId(requestDto.userId(), chatRoomId)
ChatActivity chatActivity = chatActivityRepository.findByUserIdAndChatRoomId(requestDto.userId().toString(), String.valueOf(chatRoomId))
.orElseThrow(() -> new ChatException(ChatErrorType.CHAT_PARTICIPANT_NOT_FOUND));
chatParticipant.updateLastReadMessageId(requestDto.lastReadMessageId());
chatActivity.updateLastReadMessage(requestDto.userId().toString(), new ObjectId(requestDto.lastReadMessageId()));
}

@Transactional(readOnly = true)
public int unReadMessageCount(Long roomId, String lastReadMsgId) {
if (lastReadMsgId == null) {
return 0;
}
return chatMessageMongoCustomRepository.unReadMessageCount(roomId, lastReadMsgId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,46 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.prgms.locomocoserver.chat.dao.ChatActivityDao;
import org.prgms.locomocoserver.chat.domain.ChatParticipant;
import org.prgms.locomocoserver.chat.domain.ChatParticipantRepository;
import org.prgms.locomocoserver.chat.domain.ChatRoom;
import org.prgms.locomocoserver.chat.domain.ChatRoomRepository;
import org.prgms.locomocoserver.chat.domain.mongo.ChatActivity;
import org.prgms.locomocoserver.chat.domain.mongo.ChatActivityRepository;
import org.prgms.locomocoserver.chat.domain.mongo.ChatMessageMongoCustomRepository;
import org.prgms.locomocoserver.chat.domain.querydsl.ChatParticipantCustomRepository;
import org.prgms.locomocoserver.chat.domain.querydsl.ChatRoomCustomRepository;
import org.prgms.locomocoserver.chat.dto.ChatMessageBriefDto;
import org.prgms.locomocoserver.chat.dto.ChatMessageDto;
import org.prgms.locomocoserver.chat.dto.ChatRoomDto;
import org.prgms.locomocoserver.chat.dto.ChatUserInfo;
import org.prgms.locomocoserver.chat.dto.request.ChatCreateRequestDto;
import org.prgms.locomocoserver.chat.dto.request.ChatEnterRequestDto;
import org.prgms.locomocoserver.chat.dto.request.ChatMessageRequestDto;
import org.prgms.locomocoserver.chat.exception.ChatErrorType;
import org.prgms.locomocoserver.chat.exception.ChatException;
import org.prgms.locomocoserver.user.domain.User;
import org.prgms.locomocoserver.user.domain.querydsl.UserCustomRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class ChatRoomService {

private final MongoChatMessageService mongoChatMessageService;
private final ChatRoomRepository chatRoomRepository;
private final ChatRoomCustomRepository chatRoomCustomRepository;
private final ChatParticipantCustomRepository chatParticipantCustomRepository;
private final ChatActivityService chatActivityService;
private final UserCustomRepository userCustomRepository;
private final ChatMessageMongoCustomRepository chatMessageMongoCustomRepository;
private final ChatActivityRepository chatActivityRepository;

private final StompChatService stompChatService;
private final ChatMessagePolicy chatMessagePolicy;
Expand All @@ -46,6 +56,9 @@ public void enterChatRoom(ChatEnterRequestDto requestDto) {
ChatParticipant chatParticipant = chatParticipantCustomRepository.save(ChatParticipant.builder().user(requestDto.participant())
.chatRoom(chatRoom).build()).orElseThrow(() -> new RuntimeException("채팅방 참여에 실패했습니다."));

chatActivityRepository.save(ChatActivity.builder().chatRoomId(requestDto.chatRoomId().toString())
.userId(requestDto.participant().getId().toString()).build());

chatRoom.addChatParticipant(chatParticipant);
stompChatService.sendToSubscribers(chatMessageDto);
}
Expand All @@ -59,7 +72,8 @@ public ChatRoom createChatRoom(ChatCreateRequestDto requestDto) {

chatRoom.addChatParticipant(chatParticipant);
chatRoomRepository.save(chatRoom); // mysql chat room create
mongoChatMessageService.createChatRoom(chatRoom.getId()); // mongo chat room create
chatActivityRepository.save(ChatActivity.builder()
.chatRoomId(chatRoom.getId().toString()).userId(requestDto.creator().getId().toString()).build());

chatMessagePolicy.saveEnterMessage(chatRoom.getId(), chatParticipant.getUser());

Expand All @@ -68,7 +82,9 @@ public ChatRoom createChatRoom(ChatCreateRequestDto requestDto) {

@Transactional
public ChatMessageDto saveChatMessage(ChatMessageRequestDto requestDto) {
return chatMessagePolicy.saveChatMessage(requestDto.chatRoomId(), requestDto);
ChatMessageDto chatMessageDto = chatMessagePolicy.saveChatMessage(requestDto.chatRoomId(), requestDto);

return chatMessageDto;
}

@Transactional
Expand All @@ -82,21 +98,42 @@ public ChatMessageDto saveEnterMessage(ChatEnterRequestDto requestDto) {
}

@Transactional(readOnly = true)
public List<ChatRoomDto> getAllChatRoom(Long userId, Long cursor, int pageSize) {
if (cursor == null) cursor = Long.MAX_VALUE;
List<ChatRoom> chatRooms = chatRoomCustomRepository.findByParticipantsId(userId, cursor, pageSize);
public List<ChatRoomDto> getAllChatRoom(Long userId, String cursor, int pageSize) {
if (cursor == null) cursor = LocalDateTime.now().toString();

List<ChatRoomDto> chatRoomDtos = chatRooms.stream()
log.info("START findByParticipantsId");
List<ChatRoom> chatRooms = chatRoomCustomRepository.findByParticipantsId(userId, cursor, pageSize);
log.info("END findByParticipantsId");

List<String> chatRoomIds = createChatRoomIds(chatRooms);
log.info("START findLastMessagesAndUnReadMsgCount");
List<ChatActivityDao> lastMessages = chatMessageMongoCustomRepository.findLastMessagesAndUnReadMsgCount(userId.toString(), chatRoomIds);
log.info("END findLastMessagesAndUnreadMsgCount");

Map<Long, ChatActivityDao> lastMsgMongoMap = lastMessages.stream()
.collect(Collectors.toMap(
dao -> Long.parseLong(dao.chatRoomId()),
dao -> dao
));

List<Long> userIds = createUserIds(lastMessages);
log.info("START findByIdIn");
Map<Long, User> userMap = userCustomRepository.findAllWithImageByIdIn(userIds).stream()
.collect(Collectors.toMap(User::getId, user -> user));
log.info("END findByIdIn");

log.info("START dto Change");
return chatRooms.stream()
.map(chatRoom -> {
ChatMessageDto lastMessageDto = chatMessagePolicy.getLastChatMessage(chatRoom.getId());
ChatParticipant chatParticipant = getChatParticipant(chatRoom, userId);
ChatActivityDao dao = lastMsgMongoMap.get(chatRoom.getId());
ChatMessageBriefDto lastMessageDto = ChatMessageBriefDto.of(dao.chatRoomId(), dao.chatMessageId().toString(), dao.senderId(), dao.message(), dao.createdAt());
User user = userMap.get(Long.parseLong(dao.senderId()));
ChatUserInfo chatUserInfo = user.getDeletedAt() == null ? ChatUserInfo.of(user) : ChatUserInfo.deletedUser(user.getId());

int unReadMsgCnt = chatActivityService.unReadMessageCount(chatRoom.getId(), chatParticipant.getLastReadMessageId());
return ChatRoomDto.of(chatRoom, unReadMsgCnt, lastMessageDto);
return ChatRoomDto.of(chatRoom, Integer.parseInt(dao.unReadMsgCnt()), lastMessageDto, chatUserInfo);
})
.filter(Objects::nonNull) // null인 경우 제외
.filter(Objects::nonNull)
.toList();
return chatRoomDtos;
}

@Transactional(readOnly = true)
Expand Down Expand Up @@ -136,10 +173,15 @@ private boolean isParticipantExist(ChatRoom chatRoom, User user) {
.anyMatch(chatParticipant -> chatParticipant.getUser().getId().equals(user.getId()));
}

private ChatParticipant getChatParticipant(ChatRoom chatRoom, Long userId) {
return chatRoom.getChatParticipants().stream()
.filter(p -> p.getUser().getId().equals(userId))
.findFirst()
.orElse(null);
private List<String> createChatRoomIds(List<ChatRoom> chatRooms) {
return chatRooms.stream()
.map(chatRoom -> chatRoom.getId().toString()).collect(Collectors.toList());
}

private List<Long> createUserIds(List<ChatActivityDao> lastMessages) {
return lastMessages.stream()
.map(dao -> Long.valueOf(dao.senderId()))
.distinct()
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,115 +5,94 @@
import org.prgms.locomocoserver.chat.domain.ChatRoomRepository;
import org.prgms.locomocoserver.chat.domain.mongo.ChatMessageMongo;
import org.prgms.locomocoserver.chat.domain.mongo.ChatMessageMongoCustomRepository;
import org.prgms.locomocoserver.chat.domain.mongo.ChatMessageMongoRepository;
import org.prgms.locomocoserver.chat.domain.querydsl.ChatRoomCustomRepository;
import org.prgms.locomocoserver.chat.dto.ChatMessageDto;
import org.prgms.locomocoserver.chat.dto.ChatUserInfo;
import org.prgms.locomocoserver.chat.dto.request.ChatMessageRequestDto;
import org.prgms.locomocoserver.chat.exception.ChatErrorType;
import org.prgms.locomocoserver.chat.exception.ChatException;
import org.prgms.locomocoserver.chat.domain.querydsl.ChatRoomCustomRepository;
import org.prgms.locomocoserver.user.application.UserService;
import org.prgms.locomocoserver.user.domain.User;
import org.prgms.locomocoserver.user.exception.UserException;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Primary
@Service
@RequiredArgsConstructor
public class MongoChatMessageService implements ChatMessagePolicy {

private static final String BASE_CHATROOM_NAME = "chat_messages_";
private final MongoTemplate mongoTemplate;
private final ChatMessageMongoRepository chatMessageMongoRepository;
private final ChatMessageMongoCustomRepository chatMessageMongoCustomRepository;
private final UserService userService;
private final ChatRoomRepository chatRoomRepository;
private final ChatRoomCustomRepository chatRoomCustomRepository;

@Transactional
public ChatRoom createChatRoom(Long roomId) {
String collectionName = BASE_CHATROOM_NAME + roomId;

if (!mongoTemplate.collectionExists(collectionName)) {
mongoTemplate.createCollection(collectionName);
}

return chatRoomRepository.findById(roomId).orElseThrow(() -> new ChatException(ChatErrorType.CHATROOM_NOT_FOUND));
}

@Transactional
public ChatMessageDto saveEnterMessage(Long roomId, User sender) {
String collectionName = BASE_CHATROOM_NAME + roomId;
ChatMessageMongo chatMessageMongo = mongoTemplate.save(toEnterMessage(sender), collectionName);
ChatMessageMongo chatMessageMongo = chatMessageMongoRepository.save(toEnterMessage(roomId, sender));

return ChatMessageDto.of(roomId, chatMessageMongo, ChatUserInfo.of(sender));
}

@Transactional
public ChatMessageDto saveChatMessage(Long roomId, ChatMessageRequestDto message) {
String collectionName = BASE_CHATROOM_NAME + roomId;
User participant = userService.getById(message.senderId());

ChatRoom chatRoom = chatRoomRepository.findByIdAndDeletedAtIsNull(roomId)
.orElseThrow(() -> new ChatException(ChatErrorType.CHATROOM_NOT_FOUND));

ChatMessageMongo chatMessageMongo = mongoTemplate.save(message.toChatMessageMongo(false, null), collectionName);
ChatMessageMongo chatMessageMongo = chatMessageMongoRepository.save(message.toChatMessageMongo(roomId, false, null));
chatRoom.updateUpdatedAt();

return ChatMessageDto.of(roomId, chatMessageMongo, ChatUserInfo.of(participant));
}

@Override
@Transactional
public ChatMessageDto saveChatMessageWithImage(Long roomId, List<String> imageUrls, ChatMessageRequestDto request) {
String collectionName = BASE_CHATROOM_NAME + roomId;
User participant = userService.getById(request.senderId());

ChatRoom chatRoom = chatRoomRepository.findByIdAndDeletedAtIsNull(roomId)
.orElseThrow(() -> new ChatException(ChatErrorType.CHATROOM_NOT_FOUND));

ChatMessageMongo chatMessageMongo = mongoTemplate.save(request.toChatMessageMongo(false, imageUrls), collectionName);
ChatMessageMongo chatMessageMongo = chatMessageMongoRepository.save(request.toChatMessageMongo(roomId, false, imageUrls));
chatRoom.updateUpdatedAt();

return ChatMessageDto.of(roomId, imageUrls, chatMessageMongo, ChatUserInfo.of(participant));
}

@Transactional(readOnly = true)
public List<ChatMessageDto> getAllChatMessages(Long roomId, String cursorValue, int pageSize) {
List<ChatMessageMongo> chatMessages = chatMessageMongoCustomRepository.findAllChatMessagesByRoomId(roomId, cursorValue, pageSize);
List<ChatMessageMongo> chatMessages = chatMessageMongoCustomRepository.findAllChatMessages(roomId, cursorValue, pageSize);
Map<Long, ChatUserInfo> userMap = getUserMap(roomId);

return createChatMessageDtos(roomId, chatMessages, userMap);
}

@Transactional
public void deleteChatMessages(ChatRoom chatRoom) {
chatMessageMongoCustomRepository.deleteAllChatMessages(chatRoom.getId());
chatMessageMongoRepository.deleteByChatRoomId(chatRoom.getId().toString());
}

@Transactional(readOnly = true)
public ChatMessageDto getLastChatMessage(Long roomId) {
ChatMessageMongo lastMessage = chatMessageMongoCustomRepository.findLatestMessageByRoomId(roomId)
ChatMessageMongo lastMessage = chatMessageMongoRepository.findTopByChatRoomIdOrderByCreatedAtDesc(roomId.toString())
.orElseThrow(() -> new ChatException(ChatErrorType.CHAT_MESSAGE_NOT_FOUND));

ChatUserInfo chatUserInfo = getChatUserInfo(lastMessage.getSenderId());
return ChatMessageDto.of(roomId, lastMessage, chatUserInfo);
}

public String getChatRoomName(Long roomId) {
return BASE_CHATROOM_NAME + roomId;
}

private ChatMessageMongo toEnterMessage(User participant) {
return ChatMessageMongo.builder().senderId(participant.getId().toString()).createdAt(LocalDateTime.now())
private ChatMessageMongo toEnterMessage(Long roomId, User participant) {
return ChatMessageMongo.builder().chatRoomId(roomId.toString()).senderId(participant.getId().toString()).createdAt(LocalDateTime.now())
.message(participant.getNickname() + "님이 입장하셨습니다.").isNotice(true).build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.prgms.locomocoserver.chat.dao;

import org.bson.types.ObjectId;

import java.time.LocalDateTime;

public record ChatActivityDao(
String unReadMsgCnt,
String chatRoomId,
ObjectId chatMessageId,
String senderId,
String message,
LocalDateTime createdAt
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.prgms.locomocoserver.chat.dao;

import org.bson.types.ObjectId;

public record ChatActivityRequestDao(
String chatRoomId,
ObjectId lastReadMsgId
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.prgms.locomocoserver.chat.domain.mongo;

import lombok.Builder;
import lombok.Getter;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.mapping.Document;

@Getter
@Document(collection = "chat_activity")
@CompoundIndex(def = "{'userId': 1, 'chatRoomId': 1}", name = "user_chatRoom_idx", unique = true)
public class ChatActivity {
@Id
private String id;
private String userId;
private String chatRoomId;
private ObjectId lastReadMsgId;

@Builder
public ChatActivity(String userId, String chatRoomId) {
this.userId = userId;
this.chatRoomId = chatRoomId;
this.lastReadMsgId = null;
}

public void updateLastReadMessage(String userId, ObjectId lastReadMsgId) {
if (!this.userId.equals(userId)) {
throw new IllegalArgumentException("Invalid User Id : " + userId);
}
this.lastReadMsgId = lastReadMsgId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.prgms.locomocoserver.chat.domain.mongo;

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface ChatActivityRepository extends MongoRepository<ChatActivity, String> {
Optional<ChatActivity> findByUserIdAndChatRoomId(String userId, String chatRoomId);
}
Loading
Loading