-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ✨ Chatroom Join Event Relay (#185)
* feat: add message_content_type enum class * feat: add message_category_type enum class * feat: two types of converter impl * feat: chat_message entity with step builder * rename: type package path transfer * feat: chat_message repository & domain service * fix: chat_message redis entity @id package spring to jakarta * test: dao layer test * chore: configuring the rabbit listener container factory & socket application fix * chore: add rabbitmq dependency into the socket module * feat: impl chat_join_event_listener * refactor: seperate business logic from listener * rename: contants -> constants * feat: add system message enum classes * refactor: using send_message_command for dto * chore: when integration tests run in the external-api, amqp connect is blocked
- Loading branch information
1 parent
afb5d86
commit 444ad64
Showing
20 changed files
with
802 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
...in/src/main/java/kr/co/pennyway/domain/common/converter/MessageCategoryTypeConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package kr.co.pennyway.domain.common.converter; | ||
|
||
import jakarta.persistence.Converter; | ||
import kr.co.pennyway.domain.common.redis.message.type.MessageCategoryType; | ||
|
||
@Converter | ||
public class MessageCategoryTypeConverter extends AbstractLegacyEnumAttributeConverter<MessageCategoryType> { | ||
private static final String ENUM_NAME = "메시지 카테고리 타입"; | ||
|
||
public MessageCategoryTypeConverter() { | ||
super(MessageCategoryType.class, false, ENUM_NAME); | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
...ain/src/main/java/kr/co/pennyway/domain/common/converter/MessageContentTypeConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package kr.co.pennyway.domain.common.converter; | ||
|
||
import jakarta.persistence.Converter; | ||
import kr.co.pennyway.domain.common.redis.message.type.MessageContentType; | ||
|
||
@Converter | ||
public class MessageContentTypeConverter extends AbstractLegacyEnumAttributeConverter<MessageContentType> { | ||
private static final String ENUM_NAME = "메시지 컨텐츠 타입"; | ||
|
||
public MessageContentTypeConverter() { | ||
super(MessageContentType.class, false, ENUM_NAME); | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
...y-domain/src/main/java/kr/co/pennyway/domain/common/redis/message/domain/ChatMessage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package kr.co.pennyway.domain.common.redis.message.domain; | ||
|
||
import jakarta.persistence.Convert; | ||
import jakarta.persistence.Id; | ||
import kr.co.pennyway.domain.common.converter.MessageCategoryTypeConverter; | ||
import kr.co.pennyway.domain.common.converter.MessageContentTypeConverter; | ||
import kr.co.pennyway.domain.common.redis.message.type.MessageCategoryType; | ||
import kr.co.pennyway.domain.common.redis.message.type.MessageContentType; | ||
import lombok.AccessLevel; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import org.springframework.data.redis.core.RedisHash; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
/** | ||
* 채팅 메시지를 표현하는 클래스입니다. | ||
* Redis에 저장되는 채팅 메시지의 기본 단위입니다. | ||
*/ | ||
@Getter | ||
@RedisHash | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class ChatMessage { | ||
/** | ||
* 채팅 메시지 ID는 "chatroom:{roomId}:message:{messageId}" 형태로 생성한다. | ||
*/ | ||
@Id | ||
private String id; | ||
private String content; | ||
@Convert(converter = MessageContentTypeConverter.class) | ||
private MessageContentType contentType; | ||
@Convert(converter = MessageCategoryTypeConverter.class) | ||
private MessageCategoryType categoryType; | ||
private LocalDateTime createdAt; | ||
private LocalDateTime deletedAt; | ||
private Long sender; | ||
|
||
protected ChatMessage(ChatMessageBuilder builder) { | ||
this.id = "chatroom:" + builder.getChatRoomId() + ":message:" + builder.getChatId(); | ||
this.content = builder.getContent(); | ||
this.contentType = builder.getContentType(); | ||
this.categoryType = builder.getCategoryType(); | ||
this.createdAt = LocalDateTime.now(); | ||
this.deletedAt = null; | ||
this.sender = builder.getSender(); | ||
} | ||
|
||
public Long getChatRoomId() { | ||
return Long.parseLong(id.split(":")[1]); | ||
} | ||
|
||
public Long getChatId() { | ||
return Long.parseLong(id.split(":")[3]); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "ChatMessage{" + | ||
"id='" + id + '\'' + | ||
", content='" + content + '\'' + | ||
", contentType=" + contentType + | ||
", categoryType=" + categoryType + | ||
", createdAt=" + createdAt + | ||
", deletedAt=" + deletedAt + | ||
", sender=" + sender + | ||
'}'; | ||
} | ||
} |
229 changes: 229 additions & 0 deletions
229
...n/src/main/java/kr/co/pennyway/domain/common/redis/message/domain/ChatMessageBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
package kr.co.pennyway.domain.common.redis.message.domain; | ||
|
||
import kr.co.pennyway.domain.common.redis.message.type.MessageCategoryType; | ||
import kr.co.pennyway.domain.common.redis.message.type.MessageContentType; | ||
import org.springframework.lang.NonNull; | ||
|
||
import java.util.Objects; | ||
|
||
/** | ||
* 채팅 메시지 생성을 위한 Step Builder입니다. | ||
* 필수 필드들을 순차적으로 설정하도록 강제하여 객체 생성의 안정성을 보장합니다. | ||
* | ||
* <p>사용 예시: | ||
* <pre> | ||
* ChatMessage message = ChatMessage.builder() | ||
* .chatRoomId(123L) | ||
* .chatId(456L) | ||
* .content("Hello") | ||
* .contentType(MessageContentType.TEXT) | ||
* .categoryType(MessageCategoryType.NORMAL) | ||
* .sender(789L) | ||
* .build(); | ||
* </pre> | ||
*/ | ||
public final class ChatMessageBuilder { | ||
private Long chatRoomId; | ||
private Long chatId; | ||
private String content; | ||
private MessageContentType contentType; | ||
private MessageCategoryType categoryType; | ||
private long sender; | ||
|
||
private ChatMessageBuilder() { | ||
} | ||
|
||
/** | ||
* ChatMessage 빌더의 시작점입니다. | ||
* | ||
* @return 채팅방 ID 설정 단계 | ||
*/ | ||
public static ChatRoomIdStep builder() { | ||
return new Steps(); | ||
} | ||
|
||
Long getChatRoomId() { | ||
return chatRoomId; | ||
} | ||
|
||
Long getChatId() { | ||
return chatId; | ||
} | ||
|
||
String getContent() { | ||
return content; | ||
} | ||
|
||
MessageContentType getContentType() { | ||
return contentType; | ||
} | ||
|
||
MessageCategoryType getCategoryType() { | ||
return categoryType; | ||
} | ||
|
||
long getSender() { | ||
return sender; | ||
} | ||
|
||
/** | ||
* 채팅방 ID 설정 단계입니다. | ||
* 채팅 메시지가 속한 채팅방의 ID를 지정합니다. | ||
*/ | ||
public interface ChatRoomIdStep { | ||
/** | ||
* 채팅방 ID를 설정합니다. | ||
* | ||
* @param chatRoomId 채팅방 ID | ||
* @return 채팅 메시지 ID 설정 단계 | ||
* @throws NullPointerException chatRoomId가 null인 경우 | ||
*/ | ||
ChatIdStep chatRoomId(Long chatRoomId); | ||
} | ||
|
||
/** | ||
* 채팅 메시지 ID 설정 단계입니다. | ||
* 개별 채팅 메시지를 식별하기 위한 ID를 지정합니다. | ||
*/ | ||
public interface ChatIdStep { | ||
/** | ||
* 채팅 메시지 ID를 설정합니다. | ||
* | ||
* @param chatId 채팅 메시지 ID | ||
* @return 메시지 내용 설정 단계 | ||
* @throws NullPointerException chatId가 null인 경우 | ||
*/ | ||
ContentStep chatId(Long chatId); | ||
} | ||
|
||
/** | ||
* 메시지 내용 설정 단계입니다. | ||
* 채팅 메시지의 실제 내용을 지정합니다. | ||
*/ | ||
public interface ContentStep { | ||
/** | ||
* 메시지 내용을 설정합니다. | ||
* | ||
* @param content 메시지 내용 | ||
* @return 메시지 타입 설정 단계 | ||
* @throws NullPointerException content가 null인 경우 | ||
* @throws IllegalArgumentException content가 5000자를 초과하는 경우 | ||
*/ | ||
ContentTypeStep content(String content); | ||
} | ||
|
||
/** | ||
* 메시지 타입 설정 단계입니다. | ||
* 메시지의 형식(텍스트, 이미지, 파일 등)을 지정합니다. | ||
*/ | ||
public interface ContentTypeStep { | ||
/** | ||
* 메시지 타입을 설정합니다. | ||
* | ||
* @param contentType 메시지 타입 | ||
* @return 메시지 카테고리 설정 단계 | ||
* @throws NullPointerException contentType이 null인 경우 | ||
*/ | ||
CategoryTypeStep contentType(MessageContentType contentType); | ||
} | ||
|
||
/** | ||
* 메시지 카테고리 설정 단계입니다. | ||
* 메시지의 종류(일반, 시스템 등)를 지정합니다. | ||
*/ | ||
public interface CategoryTypeStep { | ||
/** | ||
* 메시지 카테고리를 설정합니다. | ||
* | ||
* @param categoryType 메시지 카테고리 | ||
* @return 발신자 설정 단계 | ||
* @throws NullPointerException categoryType이 null인 경우 | ||
*/ | ||
SenderStep categoryType(MessageCategoryType categoryType); | ||
} | ||
|
||
/** | ||
* 발신자 설정 단계입니다. | ||
* 메시지를 보낸 사용자의 ID를 지정합니다. | ||
*/ | ||
public interface SenderStep { | ||
/** | ||
* 발신자 ID를 설정합니다. | ||
* | ||
* @param sender 발신자 ID | ||
* @return 빌드 단계 | ||
*/ | ||
BuildStep sender(Long sender); | ||
} | ||
|
||
/** | ||
* 최종 빌드 단계입니다. | ||
* 모든 필수 필드가 설정된 후 ChatMessage 객체를 생성합니다. | ||
*/ | ||
public interface BuildStep { | ||
/** | ||
* 설정된 값들을 사용하여 ChatMessage 객체를 생성합니다. | ||
* | ||
* @return 생성된 ChatMessage 객체 | ||
*/ | ||
ChatMessage build(); | ||
} | ||
|
||
private static class Steps implements | ||
ChatRoomIdStep, | ||
ChatIdStep, | ||
ContentStep, | ||
ContentTypeStep, | ||
CategoryTypeStep, | ||
SenderStep, | ||
BuildStep { | ||
|
||
private final ChatMessageBuilder builder = new ChatMessageBuilder(); | ||
|
||
@Override | ||
public ChatIdStep chatRoomId(@NonNull final Long chatRoomId) { | ||
builder.chatRoomId = Objects.requireNonNull(chatRoomId, "chatRoomId must not be null"); | ||
return this; | ||
} | ||
|
||
@Override | ||
public ContentStep chatId(@NonNull final Long chatId) { | ||
builder.chatId = Objects.requireNonNull(chatId, "chatId must not be null"); | ||
return this; | ||
} | ||
|
||
@Override | ||
public ContentTypeStep content(@NonNull final String content) { | ||
builder.content = Objects.requireNonNull(content, "content must not be null"); | ||
|
||
if (content.length() > 5000) { | ||
throw new IllegalArgumentException("content length must be less than or equal to 5000"); | ||
} | ||
|
||
return this; | ||
} | ||
|
||
@Override | ||
public CategoryTypeStep contentType(@NonNull final MessageContentType contentType) { | ||
builder.contentType = Objects.requireNonNull(contentType, "contentType must not be null"); | ||
return this; | ||
} | ||
|
||
@Override | ||
public SenderStep categoryType(@NonNull final MessageCategoryType categoryType) { | ||
builder.categoryType = Objects.requireNonNull(categoryType, "categoryType must not be null"); | ||
return this; | ||
} | ||
|
||
@Override | ||
public BuildStep sender(@NonNull final Long sender) { | ||
builder.sender = sender; | ||
return this; | ||
} | ||
|
||
@Override | ||
public ChatMessage build() { | ||
return new ChatMessage(builder); | ||
} | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
...ain/java/kr/co/pennyway/domain/common/redis/message/repository/ChatMessageRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package kr.co.pennyway.domain.common.redis.message.repository; | ||
|
||
import kr.co.pennyway.domain.common.redis.message.domain.ChatMessage; | ||
import org.springframework.data.repository.CrudRepository; | ||
|
||
public interface ChatMessageRepository extends CrudRepository<ChatMessage, String> { | ||
} |
22 changes: 22 additions & 0 deletions
22
.../src/main/java/kr/co/pennyway/domain/common/redis/message/service/ChatMessageService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package kr.co.pennyway.domain.common.redis.message.service; | ||
|
||
import kr.co.pennyway.common.annotation.DomainService; | ||
import kr.co.pennyway.domain.common.redis.message.domain.ChatMessage; | ||
import kr.co.pennyway.domain.common.redis.message.repository.ChatMessageRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
@Slf4j | ||
@DomainService | ||
@RequiredArgsConstructor | ||
public class ChatMessageService { | ||
private final ChatMessageRepository chatMessageRepository; | ||
|
||
public ChatMessage save(ChatMessage chatMessage) { | ||
return chatMessageRepository.save(chatMessage); | ||
} | ||
|
||
public void delete(final long chatRoomId, final long chatId) { | ||
chatMessageRepository.deleteById("chatroom:" + chatRoomId + ":message:" + chatId); | ||
} | ||
} |
Oops, something went wrong.