-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* [chore] : MongoDB 의존성 추가 * [chore] : docker-compose에 mongo 이미지 추가 * [feat] : messageType 변환 예외 구체화 * [feat] : chatMessage 엔티티 -> 몽고 DB collection으로 변경 * [feat] : 채팅 메시지 요청 dto 추가 * [feat] : 채팅 메시지 응답 dto 추가 * [feat] : 채팅 메시지<-> dto mapper 추가 * [feat] : 채팅 메시지 mongoRepository 추가 * [feat] : 채팅 메시지 저장, 조회 비즈니스 로직 추가 * [feat] : 채팅 메시지 저장, 조회 API 로직 추가 * [feat] : 채팅 메시지 조회 비즈니스 로직 테스트 * [test] : 테스트 컨테이너에 MongoDB 컨테이너 추가 * [test] : 채팅방 메시지 조회 통합 테스트 작성 * [rename] : chatMessage-> chatRoom으로 파일명 수정 * [chore] : PR시 배포 되도록 임시 수정 * [style] : 함수명 복수형으로 변경 * [feat] : dto validation 추가 * [chore] : cd 스크립트 원복
- Loading branch information
Showing
15 changed files
with
360 additions
and
43 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
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
52 changes: 52 additions & 0 deletions
52
src/main/java/com/dnd/gongmuin/chat/controller/ChatRoomController.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,52 @@ | ||
package com.dnd.gongmuin.chat.controller; | ||
|
||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.security.core.annotation.AuthenticationPrincipal; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import com.dnd.gongmuin.chat.dto.ChatMessageRequest; | ||
import com.dnd.gongmuin.chat.dto.ChatMessageResponse; | ||
import com.dnd.gongmuin.chat.service.ChatRoomService; | ||
import com.dnd.gongmuin.common.dto.PageResponse; | ||
import com.dnd.gongmuin.member.domain.Member; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import jakarta.validation.Valid; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Tag(name = "채팅방 API") | ||
@RestController | ||
@RequiredArgsConstructor | ||
public class ChatRoomController { | ||
|
||
private final ChatRoomService chatRoomService; | ||
|
||
@Operation(summary = "채팅방 메시지 등록 임시 API", description = "웹소켓 연결 전 임시 API") | ||
@PostMapping("/api/chat-messages/{chatRoomId}") | ||
public ResponseEntity<Void> registerChatMessage( | ||
@PathVariable Long chatRoomId, | ||
@Valid @RequestBody ChatMessageRequest request, | ||
@AuthenticationPrincipal Member member | ||
) { | ||
chatRoomService.saveChatMessage(request, chatRoomId, member.getId()); | ||
return new ResponseEntity<>(HttpStatus.CREATED); | ||
} | ||
|
||
@Operation(summary = "채팅방 메시지 조회 API", description = "채팅방 메시지를 최신순으로 페이징한다.") | ||
@GetMapping("/api/chat-messages/{chatRoomId}") | ||
public ResponseEntity<PageResponse<ChatMessageResponse>> getChatMessages( | ||
@PathVariable("chatRoomId") Long chatRoomId, | ||
Pageable pageable | ||
) { | ||
PageResponse<ChatMessageResponse> response = | ||
chatRoomService.getChatMessages(chatRoomId, pageable); | ||
return ResponseEntity.ok(response); | ||
} | ||
} |
73 changes: 32 additions & 41 deletions
73
src/main/java/com/dnd/gongmuin/chat/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 |
---|---|---|
@@ -1,69 +1,60 @@ | ||
package com.dnd.gongmuin.chat.domain; | ||
|
||
import static jakarta.persistence.ConstraintMode.*; | ||
import static jakarta.persistence.EnumType.*; | ||
import static jakarta.persistence.FetchType.*; | ||
import static jakarta.persistence.GenerationType.*; | ||
import static lombok.AccessLevel.*; | ||
|
||
import com.dnd.gongmuin.common.entity.TimeBaseEntity; | ||
import com.dnd.gongmuin.member.domain.Member; | ||
import java.time.LocalDateTime; | ||
|
||
import org.springframework.data.mongodb.core.mapping.Document; | ||
|
||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.Enumerated; | ||
import jakarta.persistence.ForeignKey; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.JoinColumn; | ||
import jakarta.persistence.ManyToOne; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Entity | ||
@Document(collection = "chat_messages") | ||
@Getter | ||
@NoArgsConstructor(access = PROTECTED) | ||
public class ChatMessage extends TimeBaseEntity { | ||
public class ChatMessage { | ||
|
||
@Id | ||
@GeneratedValue(strategy = IDENTITY) | ||
@Column(name = "chat_message_id") | ||
private Long id; | ||
private String id; | ||
|
||
@Column(name = "content", nullable = false) | ||
private String content; | ||
|
||
@Column(name = "is_read", nullable = false) | ||
private long chatRoomId; | ||
|
||
private long memberId; | ||
|
||
private Boolean isRead; | ||
|
||
@Column(name = "media_url", nullable = false) | ||
private String mediaUrl; | ||
|
||
@Enumerated(STRING) | ||
@Column(name = "type", nullable = false) | ||
private MessageType type; | ||
|
||
@ManyToOne(fetch = LAZY) | ||
@JoinColumn(name = "member_id", | ||
nullable = false, | ||
foreignKey = @ForeignKey(NO_CONSTRAINT)) | ||
private Member member; | ||
|
||
@ManyToOne(fetch = LAZY) | ||
@JoinColumn(name = "chat_room_id", | ||
nullable = false, | ||
foreignKey = @ForeignKey(NO_CONSTRAINT)) | ||
private ChatRoom chatRoom; | ||
private LocalDateTime createdAt; | ||
|
||
@Builder | ||
public ChatMessage(String content, String mediaUrl, MessageType type, Member member, | ||
ChatRoom chatRoom) { | ||
this.isRead = false; | ||
private ChatMessage( | ||
String content, | ||
long chatRoomId, | ||
long memberId, | ||
String mediaUrl, | ||
MessageType type | ||
) { | ||
this.content = content; | ||
this.chatRoomId = chatRoomId; | ||
this.memberId = memberId; | ||
this.mediaUrl = mediaUrl; | ||
this.type = type; | ||
this.member = member; | ||
this.chatRoom = chatRoom; | ||
this.isRead = false; | ||
this.createdAt = LocalDateTime.now(); | ||
} | ||
|
||
public static ChatMessage of( | ||
String content, | ||
long chatRoomId, | ||
long memberId, | ||
String mediaUrl, | ||
MessageType type | ||
) { | ||
return new ChatMessage(content, chatRoomId, memberId, mediaUrl, type); | ||
} | ||
} |
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
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,37 @@ | ||
package com.dnd.gongmuin.chat.dto; | ||
|
||
import com.dnd.gongmuin.chat.domain.ChatMessage; | ||
import com.dnd.gongmuin.chat.domain.MessageType; | ||
|
||
import lombok.AccessLevel; | ||
import lombok.NoArgsConstructor; | ||
|
||
@NoArgsConstructor(access = AccessLevel.PRIVATE) | ||
public class ChatMapper { | ||
|
||
public static ChatMessageResponse toChatMessageResponse( | ||
ChatMessage chatMessage | ||
) { | ||
return new ChatMessageResponse( | ||
chatMessage.getMemberId(), | ||
chatMessage.getChatRoomId(), | ||
chatMessage.getContent(), | ||
chatMessage.getType().getLabel(), | ||
chatMessage.getMediaUrl() | ||
); | ||
} | ||
|
||
public static ChatMessage toChatMessage( | ||
ChatMessageRequest request, | ||
long chatRoomId, | ||
long memberId | ||
) { | ||
return ChatMessage.of( | ||
request.content(), | ||
chatRoomId, | ||
memberId, | ||
request.mediaUrl(), | ||
MessageType.of(request.type()) | ||
); | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
src/main/java/com/dnd/gongmuin/chat/dto/ChatMessageRequest.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,12 @@ | ||
package com.dnd.gongmuin.chat.dto; | ||
|
||
import jakarta.validation.constraints.NotBlank; | ||
|
||
public record ChatMessageRequest( | ||
@NotBlank(message = "채팅 내용을 입력해주세요.") | ||
String content, | ||
@NotBlank(message = "채팅 타입을 입력해주세요.") | ||
String type, | ||
String mediaUrl | ||
) { | ||
} |
10 changes: 10 additions & 0 deletions
10
src/main/java/com/dnd/gongmuin/chat/dto/ChatMessageResponse.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,10 @@ | ||
package com.dnd.gongmuin.chat.dto; | ||
|
||
public record ChatMessageResponse( | ||
Long memberId, | ||
Long chatRoomId, | ||
String content, | ||
String type, | ||
String mediaUrl | ||
) { | ||
} |
16 changes: 16 additions & 0 deletions
16
src/main/java/com/dnd/gongmuin/chat/exception/ChatErrorCode.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,16 @@ | ||
package com.dnd.gongmuin.chat.exception; | ||
|
||
import com.dnd.gongmuin.common.exception.ErrorCode; | ||
|
||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Getter | ||
@RequiredArgsConstructor | ||
public enum ChatErrorCode implements ErrorCode { | ||
|
||
INVALID_MESSAGE_TYPE("메시지 타입을 올바르게 입력해주세요.", "CH_001"); | ||
|
||
private final String message; | ||
private final String code; | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/java/com/dnd/gongmuin/chat/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,13 @@ | ||
package com.dnd.gongmuin.chat.repository; | ||
|
||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.domain.Slice; | ||
import org.springframework.data.mongodb.repository.MongoRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
import com.dnd.gongmuin.chat.domain.ChatMessage; | ||
|
||
@Repository | ||
public interface ChatMessageRepository extends MongoRepository<ChatMessage, String> { | ||
Slice<ChatMessage> findByChatRoomIdOrderByCreatedAtDesc(long chatRoomId, Pageable pageable); | ||
} |
41 changes: 41 additions & 0 deletions
41
src/main/java/com/dnd/gongmuin/chat/service/ChatRoomService.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,41 @@ | ||
package com.dnd.gongmuin.chat.service; | ||
|
||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.domain.Slice; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import com.dnd.gongmuin.chat.dto.ChatMapper; | ||
import com.dnd.gongmuin.chat.dto.ChatMessageRequest; | ||
import com.dnd.gongmuin.chat.dto.ChatMessageResponse; | ||
import com.dnd.gongmuin.chat.repository.ChatMessageRepository; | ||
import com.dnd.gongmuin.common.dto.PageMapper; | ||
import com.dnd.gongmuin.common.dto.PageResponse; | ||
|
||
import jakarta.validation.Valid; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class ChatRoomService { | ||
|
||
private final ChatMessageRepository chatMessageRepository; | ||
|
||
@Transactional | ||
public void saveChatMessage( | ||
@Valid ChatMessageRequest request, | ||
Long chatRoomId, | ||
Long memberId | ||
) { | ||
chatMessageRepository.save(ChatMapper.toChatMessage(request, chatRoomId, memberId)); | ||
} | ||
|
||
@Transactional(readOnly = true) | ||
public PageResponse<ChatMessageResponse> getChatMessages(Long chatRoomId, Pageable pageable) { | ||
Slice<ChatMessageResponse> responsePage = chatMessageRepository | ||
.findByChatRoomIdOrderByCreatedAtDesc(chatRoomId, pageable) | ||
.map(ChatMapper::toChatMessageResponse); | ||
return PageMapper.toPageResponse(responsePage); | ||
} | ||
|
||
} |
40 changes: 40 additions & 0 deletions
40
src/test/java/com/dnd/gongmuin/chat/controller/ChatRoomControllerTest.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,40 @@ | ||
package com.dnd.gongmuin.chat.controller; | ||
|
||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; | ||
|
||
import java.util.List; | ||
|
||
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.ChatMessage; | ||
import com.dnd.gongmuin.chat.repository.ChatMessageRepository; | ||
import com.dnd.gongmuin.common.fixture.ChatMessageFixture; | ||
import com.dnd.gongmuin.common.support.ApiTestSupport; | ||
|
||
@DisplayName("[ChatMessage 통합 테스트]") | ||
class ChatRoomControllerTest extends ApiTestSupport { | ||
|
||
@Autowired | ||
private ChatMessageRepository chatMessageRepository; | ||
|
||
@DisplayName("[채팅방 아이디로 메시지를 조회할 수 있다.]") | ||
@Test | ||
void getChatMessages() throws Exception { | ||
List<ChatMessage> chatMessages = chatMessageRepository.saveAll(List.of( | ||
ChatMessageFixture.chatMessage(), | ||
ChatMessageFixture.chatMessage() | ||
)); | ||
mockMvc.perform(get("/api/chat-messages/{chatRoomId}", 1L) | ||
.cookie(accessToken)) | ||
.andExpect(status().isOk()) | ||
.andExpect(jsonPath("$.size").value(2)) | ||
.andExpect(jsonPath("$.content[0].memberId").value(chatMessages.get(0).getMemberId())) | ||
.andExpect(jsonPath("$.content[0].chatRoomId").value(chatMessages.get(0).getChatRoomId())) | ||
.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].mediaUrl").value(chatMessages.get(0).getMediaUrl())); | ||
} | ||
} |
Oops, something went wrong.