feat: 扩展ViewModel支持新功能
- 扩展CreateMemoryViewModel创建回忆功能 - 扩展MyMemoirViewModel支持回忆录状态和任务状态 - 优化数据同步和错误处理
This commit is contained in:
@@ -9,9 +9,13 @@ import com.huaga.life_echo.data.repository.ChapterRepository
|
||||
import com.huaga.life_echo.network.WebSocketClient
|
||||
import com.huaga.life_echo.network.WebSocketMessage
|
||||
import com.huaga.life_echo.network.MessageType
|
||||
import com.huaga.life_echo.network.ApiService
|
||||
import com.huaga.life_echo.network.AuthService
|
||||
import com.huaga.life_echo.data.database.Chapter
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
class CreateMemoryViewModel(
|
||||
private val conversationRepository: ConversationRepository,
|
||||
@@ -20,6 +24,7 @@ class CreateMemoryViewModel(
|
||||
) : ViewModel() {
|
||||
|
||||
private val webSocketClient = WebSocketClient()
|
||||
private val apiService = ApiService(TokenManager, AuthService())
|
||||
|
||||
val isRecording = MutableStateFlow(false)
|
||||
val transcript = MutableStateFlow("")
|
||||
@@ -33,6 +38,10 @@ class CreateMemoryViewModel(
|
||||
val streamingText = MutableStateFlow("") // 流式文本内容
|
||||
val isTyping = MutableStateFlow(false) // AI是否正在输入
|
||||
|
||||
// 后台处理状态
|
||||
val isProcessing = MutableStateFlow(false) // 是否正在处理回忆录
|
||||
val processingStatus = MutableStateFlow("") // 处理状态文本
|
||||
|
||||
init {
|
||||
TokenManager.initialize(context)
|
||||
}
|
||||
@@ -45,6 +54,9 @@ class CreateMemoryViewModel(
|
||||
connectionStatus.value = "连接中..."
|
||||
|
||||
try {
|
||||
// 清除旧的任务记录(与测试脚本一致)
|
||||
apiService.clearTasks()
|
||||
|
||||
// 获取访问令牌
|
||||
val token = TokenManager.getAccessToken()
|
||||
webSocketClient.connect(convId, token) { message ->
|
||||
@@ -102,13 +114,28 @@ class CreateMemoryViewModel(
|
||||
private fun handleWebSocketMessage(message: WebSocketMessage) {
|
||||
when (message.type) {
|
||||
MessageType.transcript -> {
|
||||
transcript.value = message.data["text"] ?: ""
|
||||
transcript.value = message.getString("text") ?: ""
|
||||
}
|
||||
MessageType.agent_response -> {
|
||||
// 完整回复
|
||||
agentResponse.value = message.data["text"] ?: ""
|
||||
isStreaming.value = false
|
||||
streamingText.value = ""
|
||||
// 处理Agent回复(可能有多条消息)
|
||||
val text = message.getString("text") ?: ""
|
||||
val index = message.getInt("index") ?: 0
|
||||
val total = message.getInt("total") ?: 1
|
||||
|
||||
// 如果是第一条消息,重置内容
|
||||
if (index == 0) {
|
||||
agentResponse.value = text
|
||||
} else {
|
||||
// 追加后续消息
|
||||
agentResponse.value += "\n\n$text"
|
||||
}
|
||||
|
||||
// 如果是最后一条消息,结束流式状态
|
||||
if (index >= total - 1) {
|
||||
isStreaming.value = false
|
||||
streamingText.value = ""
|
||||
webSocketClient.setGenerating(false)
|
||||
}
|
||||
}
|
||||
MessageType.agent_response_start -> {
|
||||
// 流式回复开始
|
||||
@@ -119,7 +146,7 @@ class CreateMemoryViewModel(
|
||||
}
|
||||
MessageType.agent_response_chunk -> {
|
||||
// 流式回复片段
|
||||
val chunk = message.data["text"] ?: ""
|
||||
val chunk = message.getString("text") ?: ""
|
||||
streamingText.value += chunk
|
||||
}
|
||||
MessageType.agent_response_end -> {
|
||||
@@ -144,9 +171,11 @@ class CreateMemoryViewModel(
|
||||
isRecording.value = false
|
||||
isStreaming.value = false
|
||||
webSocketClient.setGenerating(false)
|
||||
// 触发对话结束后的处理
|
||||
handleConversationEnded()
|
||||
}
|
||||
MessageType.error -> {
|
||||
connectionStatus.value = "错误: ${message.data["message"]}"
|
||||
connectionStatus.value = "错误: ${message.getString("message")}"
|
||||
isStreaming.value = false
|
||||
webSocketClient.setGenerating(false)
|
||||
}
|
||||
@@ -168,6 +197,109 @@ class CreateMemoryViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理对话结束后的逻辑
|
||||
* 等待后台任务完成,然后刷新章节列表
|
||||
*/
|
||||
private fun handleConversationEnded() {
|
||||
viewModelScope.launch {
|
||||
isProcessing.value = true
|
||||
processingStatus.value = "正在处理回忆录..."
|
||||
|
||||
// 等待后台处理完成(最多等待3分钟)
|
||||
val maxWaitSeconds = 180
|
||||
val checkInterval = 5 // 每5秒检查一次
|
||||
var elapsed = 0
|
||||
|
||||
while (elapsed < maxWaitSeconds) {
|
||||
delay(checkInterval * 1000L)
|
||||
elapsed += checkInterval
|
||||
|
||||
// 检查任务状态
|
||||
val tasksStatus = apiService.getTasksStatus()
|
||||
tasksStatus.fold(
|
||||
onSuccess = { status ->
|
||||
val total = status.total
|
||||
val pending = status.pending
|
||||
val running = status.running
|
||||
val success = status.success
|
||||
val failure = status.failure
|
||||
val allCompleted = status.all_completed
|
||||
|
||||
processingStatus.value = "处理中: 总任务$total, 等待$pending, 运行$running, 成功$success, 失败$failure"
|
||||
|
||||
// 如果所有任务完成,刷新章节列表
|
||||
if (total > 0 && allCompleted) {
|
||||
processingStatus.value = "处理完成!"
|
||||
refreshChapters()
|
||||
isProcessing.value = false
|
||||
return@launch
|
||||
}
|
||||
|
||||
// 如果没有任务但有章节内容,也认为完成
|
||||
if (total == 0 && elapsed > 30) {
|
||||
// 检查是否有章节
|
||||
val chaptersResult = apiService.getChapters()
|
||||
chaptersResult.fold(
|
||||
onSuccess = { chapters ->
|
||||
if (chapters.isNotEmpty()) {
|
||||
processingStatus.value = "处理完成!"
|
||||
refreshChapters()
|
||||
isProcessing.value = false
|
||||
return@launch
|
||||
}
|
||||
},
|
||||
onFailure = { }
|
||||
)
|
||||
}
|
||||
},
|
||||
onFailure = { exception ->
|
||||
processingStatus.value = "检查任务状态失败: ${exception.message}"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 超时后也刷新章节列表
|
||||
processingStatus.value = "处理超时,正在刷新章节..."
|
||||
refreshChapters()
|
||||
isProcessing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新章节列表
|
||||
*/
|
||||
private suspend fun refreshChapters() {
|
||||
val chaptersResult = apiService.getChapters()
|
||||
chaptersResult.fold(
|
||||
onSuccess = { chapterDtos ->
|
||||
// 转换为本地Chapter实体并保存
|
||||
val chapters = chapterDtos.map { dto ->
|
||||
Chapter(
|
||||
id = dto.id,
|
||||
title = dto.title,
|
||||
content = dto.content,
|
||||
orderIndex = dto.order_index,
|
||||
status = dto.status,
|
||||
category = dto.category,
|
||||
updatedAt = dto.updated_at?.let {
|
||||
try {
|
||||
java.time.Instant.parse(it).toEpochMilli()
|
||||
} catch (e: Exception) {
|
||||
System.currentTimeMillis()
|
||||
}
|
||||
} ?: System.currentTimeMillis(),
|
||||
isNew = dto.is_new
|
||||
)
|
||||
}
|
||||
chapterRepository.insertChapters(chapters)
|
||||
},
|
||||
onFailure = { exception ->
|
||||
processingStatus.value = "刷新章节失败: ${exception.message}"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
viewModelScope.launch {
|
||||
|
||||
@@ -5,6 +5,8 @@ import androidx.lifecycle.viewModelScope
|
||||
import com.huaga.life_echo.data.database.Chapter
|
||||
import com.huaga.life_echo.data.repository.ChapterRepository
|
||||
import com.huaga.life_echo.network.ApiService
|
||||
import com.huaga.life_echo.network.models.MemoirStateDto
|
||||
import com.huaga.life_echo.network.models.TasksStatusDto
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@@ -21,6 +23,8 @@ class MyMemoirViewModel(
|
||||
val error = MutableStateFlow<String?>(null)
|
||||
val bookInfo = MutableStateFlow<com.huaga.life_echo.network.models.BookDto?>(null)
|
||||
val showFullTextReading = MutableStateFlow(false)
|
||||
val memoirState = MutableStateFlow<MemoirStateDto?>(null)
|
||||
val tasksStatus = MutableStateFlow<TasksStatusDto?>(null)
|
||||
|
||||
fun selectChapter(chapter: Chapter) {
|
||||
selectedChapter.value = chapter
|
||||
@@ -52,11 +56,36 @@ class MyMemoirViewModel(
|
||||
fun refreshChapters() {
|
||||
viewModelScope.launch {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
val result = apiService.getChapters()
|
||||
result.getOrNull()?.let { chapters ->
|
||||
// TODO: 更新本地数据库
|
||||
}
|
||||
result.fold(
|
||||
onSuccess = { chapterDtos ->
|
||||
// 转换为本地Chapter实体并保存
|
||||
val chapters = chapterDtos.map { dto ->
|
||||
Chapter(
|
||||
id = dto.id,
|
||||
title = dto.title,
|
||||
content = dto.content,
|
||||
orderIndex = dto.order_index,
|
||||
status = dto.status,
|
||||
category = dto.category,
|
||||
updatedAt = dto.updated_at?.let {
|
||||
try {
|
||||
java.time.Instant.parse(it).toEpochMilli()
|
||||
} catch (e: Exception) {
|
||||
System.currentTimeMillis()
|
||||
}
|
||||
} ?: System.currentTimeMillis(),
|
||||
isNew = dto.is_new
|
||||
)
|
||||
}
|
||||
chapterRepository.insertChapters(chapters)
|
||||
},
|
||||
onFailure = { exception ->
|
||||
error.value = "同步失败: ${exception.message}"
|
||||
}
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
error.value = "同步失败: ${e.message}"
|
||||
} finally {
|
||||
@@ -65,6 +94,52 @@ class MyMemoirViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载回忆录状态
|
||||
*/
|
||||
fun loadMemoirState() {
|
||||
viewModelScope.launch {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
val result = apiService.getMemoirState()
|
||||
result.fold(
|
||||
onSuccess = { state ->
|
||||
memoirState.value = state
|
||||
},
|
||||
onFailure = { exception ->
|
||||
error.value = "获取回忆录状态失败: ${exception.message}"
|
||||
}
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
error.value = "获取回忆录状态失败: ${e.message}"
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查任务状态
|
||||
*/
|
||||
fun checkTasksStatus() {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val result = apiService.getTasksStatus()
|
||||
result.fold(
|
||||
onSuccess = { status ->
|
||||
tasksStatus.value = status
|
||||
},
|
||||
onFailure = { exception ->
|
||||
error.value = "获取任务状态失败: ${exception.message}"
|
||||
}
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
error.value = "获取任务状态失败: ${e.message}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadBookInfo() {
|
||||
viewModelScope.launch {
|
||||
apiService.getBookInfo().fold(
|
||||
|
||||
Reference in New Issue
Block a user