캐릭터 챗봇 #338
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue