feat(chat): implement ChatViewModel for state management
This commit is contained in:
parent
fcf63c6175
commit
62ef1e2c38
|
|
@ -0,0 +1,98 @@
|
|||
package com.stand.standapp.ui.chat
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.stand.standapp.ui.chat.model.ChatMessage
|
||||
import com.stand.standapp.ui.chat.model.ChatSession
|
||||
import com.stand.standapp.ui.chat.repo.AiChatRepository
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.UUID
|
||||
|
||||
class ChatViewModel : ViewModel() {
|
||||
private val repository = AiChatRepository()
|
||||
|
||||
private val _sessions = MutableStateFlow<List<ChatSession>>(emptyList())
|
||||
val sessions: StateFlow<List<ChatSession>> = _sessions.asStateFlow()
|
||||
|
||||
private val _currentSessionId = MutableStateFlow<String?>(null)
|
||||
val currentSessionId: StateFlow<String?> = _currentSessionId.asStateFlow()
|
||||
|
||||
private val _messages = MutableStateFlow<List<ChatMessage>>(emptyList())
|
||||
val messages: StateFlow<List<ChatMessage>> = _messages.asStateFlow()
|
||||
|
||||
init {
|
||||
createNewSession()
|
||||
}
|
||||
|
||||
fun createNewSession() {
|
||||
val newSession = ChatSession()
|
||||
_sessions.value = listOf(newSession) + _sessions.value
|
||||
_currentSessionId.value = newSession.id
|
||||
}
|
||||
|
||||
fun switchSession(sessionId: String) {
|
||||
_currentSessionId.value = sessionId
|
||||
}
|
||||
|
||||
fun sendMessage(content: String) {
|
||||
val sessionId = _currentSessionId.value ?: return
|
||||
|
||||
// 1. 如果是新会话,更新标题
|
||||
updateSessionTitleIfFirstMessage(sessionId, content)
|
||||
|
||||
// 2. 添加用户消息
|
||||
val userMsg = ChatMessage(sessionId = sessionId, role = ChatMessage.Role.USER, content = content)
|
||||
|
||||
// 3. 准备 AI 空消息,处于 streaming 状态
|
||||
val aiMsgId = UUID.randomUUID().toString()
|
||||
val aiMsg = ChatMessage(id = aiMsgId, sessionId = sessionId, role = ChatMessage.Role.AI, content = "", isStreaming = true)
|
||||
|
||||
_messages.value = _messages.value + userMsg + aiMsg
|
||||
|
||||
// 4. 发起网络请求,流式追加内容
|
||||
viewModelScope.launch {
|
||||
repository.streamChat(content)
|
||||
.catch { e ->
|
||||
appendAiMessageChunk(aiMsgId, "\n[请求异常: ${e.message}]")
|
||||
finalizeAiMessage(aiMsgId)
|
||||
}
|
||||
.onCompletion {
|
||||
finalizeAiMessage(aiMsgId)
|
||||
}
|
||||
.collect { chunk ->
|
||||
appendAiMessageChunk(aiMsgId, chunk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun appendAiMessageChunk(messageId: String, chunk: String) {
|
||||
_messages.value = _messages.value.map { msg ->
|
||||
if (msg.id == messageId) {
|
||||
msg.copy(content = msg.content + chunk)
|
||||
} else msg
|
||||
}
|
||||
}
|
||||
|
||||
private fun finalizeAiMessage(messageId: String) {
|
||||
_messages.value = _messages.value.map { msg ->
|
||||
if (msg.id == messageId) {
|
||||
msg.copy(isStreaming = false)
|
||||
} else msg
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSessionTitleIfFirstMessage(sessionId: String, content: String) {
|
||||
val isFirst = _messages.value.none { it.sessionId == sessionId }
|
||||
if (isFirst) {
|
||||
val title = if (content.length > 10) content.take(10) + "..." else content
|
||||
_sessions.value = _sessions.value.map {
|
||||
if (it.id == sessionId) it.copy(title = title) else it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue