From f2ca013b9612e17095930605699b4f2f31f202b8 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 14 Aug 2025 13:39:59 +0900 Subject: [PATCH] =?UTF-8?q?feat(chat-room):=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=9D=91=EB=8B=B5=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0(=ED=83=80=EC=9E=85/=EB=AF=B8=EB=A6=AC=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0/=EC=83=81=EB=8C=80=20=EC=9D=B4=EB=AF=B8=EC=A7=80/?= =?UTF-8?q?=EC=8B=9C=EA=B0=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ChatRoomListQueryDto: characterType, lastActivityAt 필드 추가 - ChatRoomListItemDto: opponentType, lastMessagePreview, lastMessageTimeLabel 제공 - 레포지토리 정렬 기준을 최근 메시지 또는 생성일로 일원화(COALESCE) --- .../sodalive/chat/room/dto/ChatRoomDto.kt | 11 ++++++-- .../room/repository/ChatRoomRepository.kt | 4 ++- .../chat/room/service/ChatRoomService.kt | 27 ++++++++++++++++--- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/dto/ChatRoomDto.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/dto/ChatRoomDto.kt index 3849ff5..aa2f930 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/dto/ChatRoomDto.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/dto/ChatRoomDto.kt @@ -2,6 +2,8 @@ package kr.co.vividnext.sodalive.chat.room.dto import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonProperty +import kr.co.vividnext.sodalive.chat.character.CharacterType +import java.time.LocalDateTime /** * 채팅방 생성 요청 DTO @@ -24,7 +26,9 @@ data class ChatRoomListItemDto( val chatRoomId: Long, val title: String, val imageUrl: String, - val lastMessagePreview: String? + val opponentType: String, + val lastMessagePreview: String?, + val lastMessageTimeLabel: String ) /** @@ -40,10 +44,13 @@ data class ChatMessageItemDto( /** * 채팅방 목록 쿼리 DTO (레포지토리 투영용) */ + data class ChatRoomListQueryDto( val chatRoomId: Long, val title: String, - val imagePath: String? + val imagePath: String?, + val characterType: CharacterType, + val lastActivityAt: LocalDateTime? ) /** diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/repository/ChatRoomRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/repository/ChatRoomRepository.kt index 0b719a0..a01fe62 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/repository/ChatRoomRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/repository/ChatRoomRepository.kt @@ -40,7 +40,9 @@ interface ChatRoomRepository : JpaRepository { SELECT new kr.co.vividnext.sodalive.chat.room.dto.ChatRoomListQueryDto( r.id, r.title, - pc.character.imagePath + pc.character.imagePath, + pc.character.characterType, + COALESCE(MAX(m.createdAt), r.createdAt) ) FROM ChatRoom r JOIN r.participants p diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/service/ChatRoomService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/service/ChatRoomService.kt index e539ae2..9ecedeb 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/service/ChatRoomService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/room/service/ChatRoomService.kt @@ -199,18 +199,39 @@ class ChatRoomService( val latest = messageRepository.findTopByChatRoomAndIsActiveTrueOrderByCreatedAtDesc(room) val preview = latest?.message?.let { msg -> - if (msg.length <= 25) msg else msg.take(25) + "..." + if (msg.length <= 30) msg else msg.take(30) + "..." } + val imageUrl = "$imageHost/${q.imagePath ?: "profile/default-profile.png"}" + val opponentType = q.characterType.name // Clone or Character + val time = latest?.createdAt ?: q.lastActivityAt + val timeLabel = formatRelativeTime(time) + ChatRoomListItemDto( chatRoomId = q.chatRoomId, title = q.title, - imageUrl = "$imageHost/${q.imagePath ?: "profile/default-profile.png"}", - lastMessagePreview = preview + imageUrl = imageUrl, + opponentType = opponentType, + lastMessagePreview = preview, + lastMessageTimeLabel = timeLabel ) } } + private fun formatRelativeTime(time: java.time.LocalDateTime?): String { + if (time == null) return "" + val now = java.time.LocalDateTime.now() + val duration = java.time.Duration.between(time, now) + val seconds = duration.seconds + if (seconds <= 60) return "방금" + val minutes = duration.toMinutes() + if (minutes < 60) return "${'$'}minutes분 전" + val hours = duration.toHours() + if (hours < 24) return "${'$'}hours시간 전" + // 그 외: 날짜 (yyyy-MM-dd) + return time.toLocalDate().toString() + } + @Transactional(readOnly = true) fun isMyRoomSessionActive(member: Member, chatRoomId: Long): Boolean { val room = chatRoomRepository.findById(chatRoomId).orElseThrow {