feat(chat-room): 1.1 데이터 모델 생성 및 채팅 메시지 모델 서버-로컬 분리
왜: 서버 스키마와 클라이언트 전용 필드가 혼재되어 혼란을 야기하던 문제를 해결하고, 유지보수성과 확장성을 높이기 위함. 무엇: - tasks 1.1 수행 (데이터 모델 클래스 생성) - ChatMessage 데이터 클래스 생성 (로컬/UI/도메인용) - MessageStatus enum 생성 (SENDING, SENT, FAILED) - MessageType enum 생성 (USER_MESSAGE, AI_MESSAGE, NOTICE, TYPING_INDICATOR) - CharacterType 기존 enum 재사용 (chat/character/detail/CharacterDetailResponse.kt) - ChatRoomEnterResponse, ChatMessagesResponse 데이터 클래스 생성 - 채팅 메시지 모델 서버-로컬 분리 및 응답 모델 정리 - ServerChatMessage DTO 추가 (서버 응답 전용: messageId, message, profileImageUrl, mine, createdAt) - ChatMessageMappers 추가: ServerChatMessage.toLocal(isGrouped: Boolean = false) - ChatRoomEnterResponse, ChatMessagesResponse에서 messages 타입을 List<ServerChatMessage>로 정리 - 문서 - docs/chat-room-data-models.md 갱신 (서버/로컬 분리 사항 반영) - docs/chat-room-message-model-separation.md 신설 (분리 배경/가이드) 추가 참고: - 시간 포맷 유틸은 후속 태스크(8.1)에서 테스트와 함께 구현 예정
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 보이스온 - 채팅방 캐릭터 정보 모델
|
||||
*/
|
||||
package kr.co.vividnext.sodalive.chat.room
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kr.co.vividnext.sodalive.chat.character.detail.CharacterType
|
||||
|
||||
@Keep
|
||||
data class CharacterInfo(
|
||||
@SerializedName("characterId") val characterId: Long,
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("profileImageUrl") val profileImageUrl: String,
|
||||
@SerializedName("characterType") val characterType: CharacterType
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 보이스온 - 채팅 메시지 모델 (서버 응답 기반)
|
||||
*/
|
||||
package kr.co.vividnext.sodalive.chat.room
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
/**
|
||||
* 서버/로컬 공통으로 사용하는 채팅 메시지 모델
|
||||
* - createdAt: UTC timestamp(ms)
|
||||
* - status: 기본값 SENT (서버에서 내려오는 메시지)
|
||||
* - localId: 서버 전송 전 임시 식별자 (전송 중 로컬 식별용)
|
||||
* - isGrouped: UI 그룹화 처리용 클라이언트 플래그
|
||||
*/
|
||||
@Keep
|
||||
data class ChatMessage(
|
||||
@SerializedName("messageId") val messageId: Long,
|
||||
@SerializedName("message") val message: String,
|
||||
@SerializedName("profileImageUrl") val profileImageUrl: String,
|
||||
@SerializedName("mine") val mine: Boolean,
|
||||
@SerializedName("createdAt") val createdAt: Long,
|
||||
@SerializedName("status") val status: MessageStatus = MessageStatus.SENT,
|
||||
@SerializedName("localId") val localId: String? = null,
|
||||
// 서버 필드는 아니지만 UI 로직 편의를 위해 포함 (직렬화 제외 가능)
|
||||
val isGrouped: Boolean = false
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 보이스온 - 채팅 메시지 매핑 유틸리티
|
||||
*/
|
||||
package kr.co.vividnext.sodalive.chat.room
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
/**
|
||||
* 서버 DTO -> 로컬 표시/도메인 모델 변환
|
||||
*/
|
||||
@Keep
|
||||
fun ServerChatMessage.toLocal(isGrouped: Boolean = false): ChatMessage {
|
||||
return ChatMessage(
|
||||
messageId = messageId,
|
||||
message = message,
|
||||
profileImageUrl = profileImageUrl,
|
||||
mine = mine,
|
||||
createdAt = createdAt,
|
||||
status = MessageStatus.SENT,
|
||||
localId = null,
|
||||
isGrouped = isGrouped
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 보이스온 - 점진적 메시지 로딩 응답 모델
|
||||
*/
|
||||
package kr.co.vividnext.sodalive.chat.room
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
@Keep
|
||||
data class ChatMessagesResponse(
|
||||
@SerializedName("messages") val messages: List<ServerChatMessage>,
|
||||
@SerializedName("hasMore") val hasMore: Boolean,
|
||||
@SerializedName("nextCursor") val nextCursor: Long?
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 보이스온 - 통합 채팅방 입장 응답 모델
|
||||
*/
|
||||
package kr.co.vividnext.sodalive.chat.room
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
@Keep
|
||||
data class ChatRoomEnterResponse(
|
||||
@SerializedName("roomId") val roomId: Long,
|
||||
@SerializedName("character") val character: CharacterInfo,
|
||||
@SerializedName("messages") val messages: List<ServerChatMessage>,
|
||||
@SerializedName("hasMoreMessages") val hasMoreMessages: Boolean
|
||||
)
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 보이스온 - 채팅방 메시지 전송 상태 Enum
|
||||
*/
|
||||
package kr.co.vividnext.sodalive.chat.room
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
/**
|
||||
* 메시지 전송 상태
|
||||
*/
|
||||
@Keep
|
||||
enum class MessageStatus {
|
||||
/** 전송 중 */
|
||||
SENDING,
|
||||
/** 전송 완료 */
|
||||
SENT,
|
||||
/** 전송 실패 */
|
||||
FAILED
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 보이스온 - 채팅방 메시지 타입 Enum
|
||||
*/
|
||||
package kr.co.vividnext.sodalive.chat.room
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
/**
|
||||
* 메시지 표시 타입 (RecyclerView ViewType과 연계)
|
||||
*/
|
||||
@Keep
|
||||
enum class MessageType {
|
||||
USER_MESSAGE,
|
||||
AI_MESSAGE,
|
||||
NOTICE,
|
||||
TYPING_INDICATOR
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 보이스온 - 채팅 메시지 모델 (서버 응답 전용 DTO)
|
||||
*/
|
||||
package kr.co.vividnext.sodalive.chat.room
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
/**
|
||||
* 서버에서 내려오는 순수 메시지 데이터
|
||||
* - 클라이언트 전용 필드(status, localId, isGrouped 등)를 포함하지 않음
|
||||
*/
|
||||
@Keep
|
||||
data class ServerChatMessage(
|
||||
@SerializedName("messageId") val messageId: Long,
|
||||
@SerializedName("message") val message: String,
|
||||
@SerializedName("profileImageUrl") val profileImageUrl: String,
|
||||
@SerializedName("mine") val mine: Boolean,
|
||||
@SerializedName("createdAt") val createdAt: Long
|
||||
)
|
||||
Reference in New Issue
Block a user