diff --git a/application/src/main/kotlin/com/studentcenter/weave/application/chat/port/inbound/GetChatMessage.kt b/application/src/main/kotlin/com/studentcenter/weave/application/chat/port/inbound/GetChatMessage.kt new file mode 100644 index 00000000..1711d1af --- /dev/null +++ b/application/src/main/kotlin/com/studentcenter/weave/application/chat/port/inbound/GetChatMessage.kt @@ -0,0 +1,30 @@ +package com.studentcenter.weave.application.chat.port.inbound + +import com.studentcenter.weave.domain.chat.entity.ChatMessage +import com.studentcenter.weave.support.common.dto.ScrollRequest +import com.studentcenter.weave.support.common.dto.ScrollResponse +import java.util.UUID + +interface GetChatMessage { + + fun getScrollList(query: ScrollListQuery): ScrollListResult + + data class ScrollListQuery( + val chatRoomId: UUID, + override val next: UUID?, + override val limit: Int + ) : ScrollRequest( + next = next, + limit = limit + ) + + data class ScrollListResult( + override val items: List, + override val next: UUID?, + ) : ScrollResponse( + items = items, + next = next, + total = items.size, + ) + +} diff --git a/application/src/main/kotlin/com/studentcenter/weave/application/chat/port/outbound/ChatMessageRepository.kt b/application/src/main/kotlin/com/studentcenter/weave/application/chat/port/outbound/ChatMessageRepository.kt index 94727be9..a8618656 100644 --- a/application/src/main/kotlin/com/studentcenter/weave/application/chat/port/outbound/ChatMessageRepository.kt +++ b/application/src/main/kotlin/com/studentcenter/weave/application/chat/port/outbound/ChatMessageRepository.kt @@ -1,9 +1,12 @@ package com.studentcenter.weave.application.chat.port.outbound +import com.studentcenter.weave.application.chat.port.inbound.GetChatMessage import com.studentcenter.weave.domain.chat.entity.ChatMessage interface ChatMessageRepository { fun save(chatMessage: ChatMessage) + fun getScrollList(query: GetChatMessage.ScrollListQuery): List + } diff --git a/application/src/main/kotlin/com/studentcenter/weave/application/chat/service/GetChatMessageService.kt b/application/src/main/kotlin/com/studentcenter/weave/application/chat/service/GetChatMessageService.kt new file mode 100644 index 00000000..563564e9 --- /dev/null +++ b/application/src/main/kotlin/com/studentcenter/weave/application/chat/service/GetChatMessageService.kt @@ -0,0 +1,26 @@ +package com.studentcenter.weave.application.chat.service + +import com.studentcenter.weave.application.chat.port.inbound.GetChatMessage +import com.studentcenter.weave.application.chat.port.outbound.ChatMessageRepository +import com.studentcenter.weave.domain.chat.entity.ChatMessage +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +@Transactional(readOnly = true) +class GetChatMessageService( + private val chatMessageRepository: ChatMessageRepository, +) : GetChatMessage { + + override fun getScrollList(query: GetChatMessage.ScrollListQuery): GetChatMessage.ScrollListResult { + val result: List = query + .copy(limit = query.limit + 1) + .let { chatMessageRepository.getScrollList(it) } + + return GetChatMessage.ScrollListResult( + items = result.take(query.limit), + next = if (result.size > query.limit) result.last().id else null + ) + } + +} diff --git a/bootstrap/http/src/main/kotlin/com/studentcenter/weave/bootstrap/chat/api/ChatMessageApi.kt b/bootstrap/http/src/main/kotlin/com/studentcenter/weave/bootstrap/chat/api/ChatMessageApi.kt new file mode 100644 index 00000000..855b8f27 --- /dev/null +++ b/bootstrap/http/src/main/kotlin/com/studentcenter/weave/bootstrap/chat/api/ChatMessageApi.kt @@ -0,0 +1,23 @@ +package com.studentcenter.weave.bootstrap.chat.api + +import com.studentcenter.weave.bootstrap.chat.dto.GetChatMessagesRequest +import com.studentcenter.weave.bootstrap.chat.dto.GetChatMessagesResponse +import com.studentcenter.weave.bootstrap.common.security.annotation.Secured +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.tags.Tag +import org.springframework.http.HttpStatus +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.ResponseStatus + +@Tag(name = "ChatMessage", description = "채팅 메시지 API") +@RequestMapping("/api/chat-messages") +interface ChatMessageApi { + + @Secured + @Operation(summary = "채팅 메시지 조회") + @GetMapping + @ResponseStatus(HttpStatus.OK) + fun getChatMessages(request: GetChatMessagesRequest): GetChatMessagesResponse + +} diff --git a/bootstrap/http/src/main/kotlin/com/studentcenter/weave/bootstrap/chat/controller/ChatMessageRestController.kt b/bootstrap/http/src/main/kotlin/com/studentcenter/weave/bootstrap/chat/controller/ChatMessageRestController.kt new file mode 100644 index 00000000..8062bd6c --- /dev/null +++ b/bootstrap/http/src/main/kotlin/com/studentcenter/weave/bootstrap/chat/controller/ChatMessageRestController.kt @@ -0,0 +1,23 @@ +package com.studentcenter.weave.bootstrap.chat.controller + +import com.studentcenter.weave.application.chat.port.inbound.GetChatMessage +import com.studentcenter.weave.bootstrap.chat.api.ChatMessageApi +import com.studentcenter.weave.bootstrap.chat.dto.GetChatMessagesRequest +import com.studentcenter.weave.bootstrap.chat.dto.GetChatMessagesResponse +import com.studentcenter.weave.bootstrap.chat.dto.GetChatMessagesResponse.Companion.toResponse +import org.springframework.web.bind.annotation.RestController + +@RestController +class ChatMessageRestController( + private val getChatMessage: GetChatMessage, +) : ChatMessageApi { + + override fun getChatMessages(request: GetChatMessagesRequest): GetChatMessagesResponse { + val (chatRoomId, next, limit) = request.validate() + return GetChatMessage + .ScrollListQuery(chatRoomId, next, limit) + .let { getChatMessage.getScrollList(it) } + .toResponse() + } + +} diff --git a/bootstrap/http/src/main/kotlin/com/studentcenter/weave/bootstrap/chat/dto/GetChatMessagesRequest.kt b/bootstrap/http/src/main/kotlin/com/studentcenter/weave/bootstrap/chat/dto/GetChatMessagesRequest.kt new file mode 100644 index 00000000..cec3596d --- /dev/null +++ b/bootstrap/http/src/main/kotlin/com/studentcenter/weave/bootstrap/chat/dto/GetChatMessagesRequest.kt @@ -0,0 +1,29 @@ +package com.studentcenter.weave.bootstrap.chat.dto + +import io.swagger.v3.oas.annotations.media.Schema +import java.util.* + +@Schema(description = "채팅 메시지 조회 요청") +data class GetChatMessagesRequest( + @Schema(description = "채팅방 ID", example = "123e4567-e89b-12d3-a456-426614174000") + val chatRoomId: UUID?, + @Schema(description = "이전 메시지 조회를 위한 cursor", example = "123e4567-e89b-12d3-a456-426614174000") + val next: UUID?, + @Schema(description = "한번에 조회할 메시지 수", defaultValue = "20", example = "20") + val limit: Int? = 20, +) { + + fun validate(): Validated { + requireNotNull(chatRoomId) { "유효하지 않은 채팅방 입니다" } + requireNotNull(limit) { "유효하지 않은 요청입니다"} + require(limit > 0) { "한번에 조회할 메시지 수는 0보다 커야 합니다" } + return Validated(chatRoomId, next, limit) + } + + data class Validated( + val chatRoomId: UUID, + val next: UUID?, + val limit: Int, + ) + +} diff --git a/bootstrap/http/src/main/kotlin/com/studentcenter/weave/bootstrap/chat/dto/GetChatMessagesResponse.kt b/bootstrap/http/src/main/kotlin/com/studentcenter/weave/bootstrap/chat/dto/GetChatMessagesResponse.kt new file mode 100644 index 00000000..128b8a9b --- /dev/null +++ b/bootstrap/http/src/main/kotlin/com/studentcenter/weave/bootstrap/chat/dto/GetChatMessagesResponse.kt @@ -0,0 +1,26 @@ +package com.studentcenter.weave.bootstrap.chat.dto + +import com.studentcenter.weave.application.chat.port.inbound.GetChatMessage +import com.studentcenter.weave.domain.chat.entity.ChatMessage +import io.swagger.v3.oas.annotations.media.Schema +import java.util.* + +@Schema(description = "채팅 메시지 목록 조회 응답") +data class GetChatMessagesResponse( + @Schema(description = "채팅 메시지 목록") + val items: List, + @Schema(description = "다음 메시지 ID") + val next: UUID?, + @Schema(description = "조회한 메시지 수") + val total: Int, +) { + companion object { + fun GetChatMessage.ScrollListResult.toResponse(): GetChatMessagesResponse { + return GetChatMessagesResponse( + items = this.items, + next = this.next, + total = this.total, + ) + } + } +} diff --git a/infrastructure/persistence/src/main/kotlin/com/studentcenter/weave/infrastructure/persistence/chat/adapter/ChatMessageJpaAdapter.kt b/infrastructure/persistence/src/main/kotlin/com/studentcenter/weave/infrastructure/persistence/chat/adapter/ChatMessageJpaAdapter.kt index fd24bc74..76faa12c 100644 --- a/infrastructure/persistence/src/main/kotlin/com/studentcenter/weave/infrastructure/persistence/chat/adapter/ChatMessageJpaAdapter.kt +++ b/infrastructure/persistence/src/main/kotlin/com/studentcenter/weave/infrastructure/persistence/chat/adapter/ChatMessageJpaAdapter.kt @@ -1,5 +1,6 @@ package com.studentcenter.weave.infrastructure.persistence.chat.adapter +import com.studentcenter.weave.application.chat.port.inbound.GetChatMessage import com.studentcenter.weave.application.chat.port.outbound.ChatMessageRepository import com.studentcenter.weave.domain.chat.entity.ChatMessage import com.studentcenter.weave.infrastructure.persistence.chat.enitty.ChatMessageJpaEntity.Companion.toJpaEntity @@ -17,4 +18,14 @@ class ChatMessageJpaAdapter( .also { chatMessageJpaRepository.save(it) } } + override fun getScrollList(query: GetChatMessage.ScrollListQuery): List { + return chatMessageJpaRepository + .getScrollList( + chatRoomId = query.chatRoomId, + next = query.next, + limit = query.limit, + ) + .map { it.toDomain() } + } + } diff --git a/infrastructure/persistence/src/main/kotlin/com/studentcenter/weave/infrastructure/persistence/chat/repository/ChatMessageJpaRepository.kt b/infrastructure/persistence/src/main/kotlin/com/studentcenter/weave/infrastructure/persistence/chat/repository/ChatMessageJpaRepository.kt index 92bfb5f6..d1311c47 100644 --- a/infrastructure/persistence/src/main/kotlin/com/studentcenter/weave/infrastructure/persistence/chat/repository/ChatMessageJpaRepository.kt +++ b/infrastructure/persistence/src/main/kotlin/com/studentcenter/weave/infrastructure/persistence/chat/repository/ChatMessageJpaRepository.kt @@ -2,10 +2,28 @@ package com.studentcenter.weave.infrastructure.persistence.chat.repository import com.studentcenter.weave.infrastructure.persistence.chat.enitty.ChatMessageJpaEntity import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Repository import java.util.* @Repository interface ChatMessageJpaRepository : JpaRepository { + @Query( + value = + """ + select * from chat_message cm + where cm.room_id = :chatRoomId + and (:next is null or cm.id <= :next) + order by cm.id desc + limit :limit + """, + nativeQuery = true + ) + fun getScrollList( + chatRoomId: UUID, + next: UUID?, + limit: Int, + ): List + }