-
Notifications
You must be signed in to change notification settings - Fork 0
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
[채팅] redis pub/sub을 활용한 채팅 #93
Changes from 6 commits
509764b
f390f42
7e7cb36
dab2985
b221a06
373bea3
7ee962f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package yeonba.be.chatting.dto.request; | ||
|
||
import java.io.Serializable; | ||
import java.time.LocalDateTime; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
public class ChatPublishRequest implements Serializable { | ||
|
||
private long roomId; | ||
private long userId; | ||
private String userName; | ||
private String content; | ||
private LocalDateTime sentAt; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package yeonba.be.chatting.dto.request; | ||
|
||
import java.io.Serializable; | ||
import java.time.LocalDateTime; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
public class ChatSubscribeResponse implements Serializable { | ||
|
||
private long roomId; | ||
private long userId; | ||
private String userName; | ||
private String content; | ||
private LocalDateTime sentAt; | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package yeonba.be.chatting.dto.response; | ||
|
||
import java.time.LocalDateTime; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
public class ChatMessageResponse { | ||
|
||
private long userId; | ||
private String userName; | ||
private String content; | ||
private LocalDateTime sentAt; | ||
|
||
public ChatMessageResponse(long userId, String userName, String content, LocalDateTime sentAt) { | ||
|
||
this.userId = userId; | ||
this.userName = userName; | ||
this.content = content; | ||
this.sentAt = sentAt; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,17 @@ | ||
package yeonba.be.chatting.repository.chatmessage; | ||
|
||
import java.util.List; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.stereotype.Repository; | ||
import yeonba.be.chatting.entity.ChatMessage; | ||
import yeonba.be.chatting.entity.ChatRoom; | ||
|
||
@Repository | ||
public interface ChatMessageRepository extends JpaRepository<ChatMessage, Long> { | ||
|
||
ChatMessage findFirstByChatRoomIdOrderBySentAtDesc(long chatRoomId); | ||
|
||
int countByChatRoomIdAndReadIsFalse(long chatRoomId); | ||
|
||
List<ChatMessage> findAllByChatRoomOrderBySentAtDesc(ChatRoom chatRoom); | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -4,17 +4,24 @@ | |||||
import java.util.List; | ||||||
import java.util.Optional; | ||||||
import lombok.RequiredArgsConstructor; | ||||||
import lombok.extern.slf4j.Slf4j; | ||||||
import org.springframework.context.ApplicationEventPublisher; | ||||||
import org.springframework.data.redis.listener.ChannelTopic; | ||||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer; | ||||||
import org.springframework.stereotype.Service; | ||||||
import org.springframework.transaction.annotation.Transactional; | ||||||
import yeonba.be.chatting.dto.request.ChatPublishRequest; | ||||||
import yeonba.be.chatting.dto.response.ChatMessageResponse; | ||||||
import yeonba.be.chatting.dto.response.ChatRoomResponse; | ||||||
import yeonba.be.chatting.entity.ChatMessage; | ||||||
import yeonba.be.chatting.entity.ChatRoom; | ||||||
import yeonba.be.chatting.repository.chatmessage.ChatMessageCommand; | ||||||
import yeonba.be.chatting.repository.chatmessage.ChatMessageQuery; | ||||||
import yeonba.be.chatting.repository.chatroom.ChatRoomCommand; | ||||||
import yeonba.be.chatting.repository.chatroom.ChatRoomQuery; | ||||||
import yeonba.be.chatting.repository.chatroom.ChatRoomRepository; | ||||||
import yeonba.be.exception.BlockException; | ||||||
import yeonba.be.exception.ChatException; | ||||||
import yeonba.be.exception.GeneralException; | ||||||
import yeonba.be.exception.NotificationException; | ||||||
import yeonba.be.notification.entity.Notification; | ||||||
|
@@ -26,6 +33,7 @@ | |||||
import yeonba.be.user.repository.block.BlockQuery; | ||||||
import yeonba.be.user.repository.user.UserQuery; | ||||||
|
||||||
@Slf4j | ||||||
@Service | ||||||
@RequiredArgsConstructor | ||||||
public class ChatService { | ||||||
|
@@ -39,6 +47,44 @@ public class ChatService { | |||||
private final NotificationQuery notificationQuey; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
이전 PR에서 제대로 리뷰 되지 못한 것 같네요. 죄송합니다. 오타 수정 부탁드립니다. |
||||||
|
||||||
private final ApplicationEventPublisher eventPublisher; | ||||||
private final RedisChattingPublisher redisChattingPublisher; | ||||||
private final RedisChattingSubscriber adapter; | ||||||
private final RedisMessageListenerContainer container; | ||||||
private final ChatRoomRepository chatRoomRepository; | ||||||
|
||||||
@Transactional | ||||||
public void publish(ChatPublishRequest request) { | ||||||
|
||||||
ChatRoom chatRoom = chatRoomQuery.findById(request.getRoomId()); | ||||||
User sender = userQuery.findById(request.getUserId()); | ||||||
User receiver = chatRoom.getSender().equals(sender) ? chatRoom.getReceiver() | ||||||
: chatRoom.getSender(); | ||||||
|
||||||
// TODO: 메시지 Pub/Sub과 메시지 저장 로직 비동기 처리(id, user 등 request, response 변경 가능) | ||||||
redisChattingPublisher.publish(new ChannelTopic(String.valueOf(request.getRoomId())), | ||||||
request); | ||||||
chatMessageCommand.save( | ||||||
new ChatMessage(chatRoom, sender, receiver, request.getContent(), request.getSentAt())); | ||||||
} | ||||||
|
||||||
public List<ChatMessageResponse> getChatMessages(long userId, long roomId) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
|
||||||
User user = userQuery.findById(userId); | ||||||
|
||||||
ChatRoom chatRoom = chatRoomQuery.findById(roomId); | ||||||
|
||||||
if (!user.equals(chatRoom.getSender()) && !user.equals(chatRoom.getReceiver())) { | ||||||
throw new GeneralException(ChatException.NOT_YOUR_CHAT_ROOM); | ||||||
} | ||||||
|
||||||
List<ChatMessage> chatMessages = chatMessageQuery.findAllByChatRoom(chatRoom); | ||||||
|
||||||
return chatMessages.stream() | ||||||
.map(chatMessage -> new ChatMessageResponse(chatMessage.getSender().getId(), | ||||||
chatMessage.getSender().getNickname(), | ||||||
chatMessage.getContent(), chatMessage.getSentAt())) | ||||||
.toList(); | ||||||
} | ||||||
|
||||||
@Transactional(readOnly = true) | ||||||
public List<ChatRoomResponse> getChatRooms(long userId) { | ||||||
|
@@ -105,15 +151,22 @@ public void acceptRequestedChat(long userId, long notificationId) { | |||||
// 본인에게 온 채팅 요청인지 검증 | ||||||
if (receiver.equals(userQuery.findById(userId))) { | ||||||
|
||||||
throw new GeneralException(NotificationException.NOT_YOUR_CHATTING_REQUEST_NOTIFICATION); | ||||||
throw new GeneralException( | ||||||
NotificationException.NOT_YOUR_CHATTING_REQUEST_NOTIFICATION); | ||||||
} | ||||||
|
||||||
// 채팅방 활성화 | ||||||
ChatRoom chatRoom = chatRoomQuery.findBy(sender, receiver); | ||||||
chatRoom.activeRoom(); | ||||||
|
||||||
String activeRoom = "채팅방이 활성화되었습니다."; | ||||||
chatMessageCommand.createChatMessage(new ChatMessage(chatRoom, sender, receiver, activeRoom)); | ||||||
String activeRoom = "채팅방이 생성되었습니다."; | ||||||
|
||||||
chatMessageCommand.save( | ||||||
new ChatMessage(chatRoom, sender, receiver, activeRoom, LocalDateTime.now())); | ||||||
|
||||||
// 메시지 수신을 위한 Redis Pub/Sub 구독 | ||||||
container.addMessageListener(adapter, new ChannelTopic(String.valueOf(chatRoom.getId()))); | ||||||
log.info("channel topic 생성 {}", chatRoom.getId()); | ||||||
|
||||||
NotificationSendEvent notificationSendEvent = new NotificationSendEvent( | ||||||
NotificationType.CHATTING_REQUEST_ACCEPTED, receiver, sender, | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package yeonba.be.chatting.service; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.data.redis.core.RedisTemplate; | ||
import org.springframework.data.redis.listener.ChannelTopic; | ||
import org.springframework.stereotype.Service; | ||
import yeonba.be.chatting.dto.request.ChatPublishRequest; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class RedisChattingPublisher { | ||
|
||
private final RedisTemplate<String, Object> redisTemplate; | ||
|
||
public void publish(ChannelTopic topic, ChatPublishRequest request) { | ||
|
||
redisTemplate.convertAndSend(topic.getTopic(), request); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AllArgsConstructor
로 간단히 표현할 수 있을 듯 한데, 직접 생성자를 작성하신 이유가 있을까요?