feat(chat-room): 채팅방 리스트 응답 개선(타입/미리보기/상대 이미지/시간)
- ChatRoomListQueryDto: characterType, lastActivityAt 필드 추가 - ChatRoomListItemDto: opponentType, lastMessagePreview, lastMessageTimeLabel 제공 - 레포지토리 정렬 기준을 최근 메시지 또는 생성일로 일원화(COALESCE)
This commit is contained in:
		| @@ -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? | ||||
| ) | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -40,7 +40,9 @@ interface ChatRoomRepository : JpaRepository<ChatRoom, Long> { | ||||
|             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 | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user