From 62ef1e2c385b0063e81843a4c8c2ff618b826ccc Mon Sep 17 00:00:00 2001 From: "844143714@qq,com" <844143714@qq,com> Date: Tue, 19 May 2026 21:18:41 +0800 Subject: [PATCH] feat(chat): implement ChatViewModel for state management --- .../stand/standapp/ui/chat/ChatViewModel.kt | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 app/src/main/java/com/stand/standapp/ui/chat/ChatViewModel.kt diff --git a/app/src/main/java/com/stand/standapp/ui/chat/ChatViewModel.kt b/app/src/main/java/com/stand/standapp/ui/chat/ChatViewModel.kt new file mode 100644 index 0000000..9cb0a77 --- /dev/null +++ b/app/src/main/java/com/stand/standapp/ui/chat/ChatViewModel.kt @@ -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>(emptyList()) + val sessions: StateFlow> = _sessions.asStateFlow() + + private val _currentSessionId = MutableStateFlow(null) + val currentSessionId: StateFlow = _currentSessionId.asStateFlow() + + private val _messages = MutableStateFlow>(emptyList()) + val messages: StateFlow> = _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 + } + } + } +}