feat: 扩展网络层API和WebSocket消息处理
- 扩展ApiService添加回忆录状态和任务状态接口 - 优化WebSocketClient连接管理 - 增强WebSocketMessage消息类型支持 - 更新MemoirModels数据模型
This commit is contained in:
@@ -95,7 +95,6 @@ class ApiService(
|
||||
return try {
|
||||
val response = client.get("$BASE_URL/api/chapters") {
|
||||
contentType(ContentType.Application.Json)
|
||||
parameter("user_id", userId)
|
||||
}
|
||||
Result.success(response.body())
|
||||
} catch (e: Exception) {
|
||||
@@ -261,5 +260,46 @@ class ApiService(
|
||||
Result.success(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 回忆录状态相关API ====================
|
||||
|
||||
suspend fun getMemoirState(): Result<com.huaga.life_echo.network.models.MemoirStateDto> {
|
||||
return try {
|
||||
val response = client.get("$BASE_URL/api/memoir-state") {
|
||||
contentType(ContentType.Application.Json)
|
||||
}
|
||||
Result.success(response.body())
|
||||
} catch (e: Exception) {
|
||||
Result.failure(Exception("获取回忆录状态失败: ${e.message}", e))
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 任务状态相关API ====================
|
||||
|
||||
suspend fun getTasksStatus(): Result<com.huaga.life_echo.network.models.TasksStatusDto> {
|
||||
return try {
|
||||
val response = client.get("$BASE_URL/api/tasks/status") {
|
||||
contentType(ContentType.Application.Json)
|
||||
}
|
||||
Result.success(response.body())
|
||||
} catch (e: Exception) {
|
||||
Result.failure(Exception("获取任务状态失败: ${e.message}", e))
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun clearTasks(): Result<Unit> {
|
||||
return try {
|
||||
val response = client.delete("$BASE_URL/api/tasks/clear") {
|
||||
contentType(ContentType.Application.Json)
|
||||
}
|
||||
if (response.status.isSuccess()) {
|
||||
Result.success(Unit)
|
||||
} else {
|
||||
Result.failure(Exception("清除任务失败: ${response.status}"))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.failure(Exception("清除任务失败: ${e.message}", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ import io.ktor.http.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import kotlinx.serialization.json.putJsonObject
|
||||
|
||||
class WebSocketClient {
|
||||
private val client = HttpClient(Android) {
|
||||
@@ -74,7 +78,7 @@ class WebSocketClient {
|
||||
sendMessage(WebSocketMessage(
|
||||
type = MessageType.connect,
|
||||
conversation_id = conversationId,
|
||||
data = mapOf("status" to "connected")
|
||||
data = buildJsonObject { put("status", JsonPrimitive("connected")) }
|
||||
))
|
||||
|
||||
} catch (e: Exception) {
|
||||
@@ -118,7 +122,7 @@ class WebSocketClient {
|
||||
sendMessage(WebSocketMessage(
|
||||
type = MessageType.audio_chunk,
|
||||
conversation_id = conversationId,
|
||||
data = mapOf("audio_base64" to base64Audio)
|
||||
data = buildJsonObject { put("audio_base64", JsonPrimitive(base64Audio)) }
|
||||
))
|
||||
}
|
||||
|
||||
@@ -126,14 +130,15 @@ class WebSocketClient {
|
||||
sendMessage(WebSocketMessage(
|
||||
type = MessageType.text,
|
||||
conversation_id = conversationId,
|
||||
data = mapOf("text" to text)
|
||||
data = buildJsonObject { put("text", JsonPrimitive(text)) }
|
||||
))
|
||||
}
|
||||
|
||||
suspend fun sendEndConversation(conversationId: String) {
|
||||
sendMessage(WebSocketMessage(
|
||||
type = MessageType.end_conversation,
|
||||
conversation_id = conversationId
|
||||
conversation_id = conversationId,
|
||||
data = buildJsonObject { }
|
||||
))
|
||||
}
|
||||
|
||||
@@ -146,7 +151,7 @@ class WebSocketClient {
|
||||
sendMessage(WebSocketMessage(
|
||||
type = MessageType.cancel_generation,
|
||||
conversation_id = conversationId,
|
||||
data = mapOf("action" to "cancel")
|
||||
data = buildJsonObject { put("action", JsonPrimitive("cancel")) }
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.huaga.life_echo.network
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
|
||||
@Serializable
|
||||
enum class MessageType {
|
||||
@@ -23,7 +26,58 @@ enum class MessageType {
|
||||
data class WebSocketMessage(
|
||||
val type: MessageType,
|
||||
val conversation_id: String? = null,
|
||||
val data: Map<String, String> = emptyMap(),
|
||||
val data: JsonObject = buildJsonObject { },
|
||||
val timestamp: String? = null
|
||||
)
|
||||
) {
|
||||
/**
|
||||
* 获取data中的字符串值
|
||||
*/
|
||||
fun getString(key: String): String? {
|
||||
return data[key]?.let {
|
||||
if (it is JsonPrimitive) {
|
||||
try {
|
||||
it.content
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取data中的整数值
|
||||
*/
|
||||
fun getInt(key: String): Int? {
|
||||
return data[key]?.let {
|
||||
if (it is JsonPrimitive) {
|
||||
try {
|
||||
it.content.toIntOrNull()
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取data中的布尔值
|
||||
*/
|
||||
fun getBoolean(key: String): Boolean? {
|
||||
return data[key]?.let {
|
||||
if (it is JsonPrimitive) {
|
||||
try {
|
||||
it.content.toBooleanStrictOrNull()
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,30 +6,31 @@ import kotlinx.serialization.Serializable
|
||||
* 回忆录相关的数据模型
|
||||
*/
|
||||
|
||||
// 书籍信息DTO(扩展版)
|
||||
// 书籍信息DTO(匹配服务器格式)
|
||||
@Serializable
|
||||
data class BookDto(
|
||||
val id: String,
|
||||
val userId: String,
|
||||
val title: String,
|
||||
val subtitle: String? = null,
|
||||
val totalPages: Int,
|
||||
val totalWords: Int,
|
||||
val updatedAt: Long,
|
||||
val lastUpdatedAt: Long? = null
|
||||
val total_pages: Int? = null,
|
||||
val total_words: Int? = null,
|
||||
val cover_image_url: String? = null,
|
||||
val has_update: Boolean = false,
|
||||
val last_update_chapter_id: String? = null
|
||||
)
|
||||
|
||||
// 章节信息DTO(扩展版)
|
||||
// 章节信息DTO(匹配服务器格式)
|
||||
@Serializable
|
||||
data class ChapterDto(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val content: String,
|
||||
val orderIndex: Int,
|
||||
val order_index: Int,
|
||||
val status: String, // "draft", "partial", "completed"
|
||||
val category: String,
|
||||
val pageCount: Int? = null,
|
||||
val updatedAt: Long? = null
|
||||
val images: List<String> = emptyList(),
|
||||
val updated_at: String? = null,
|
||||
val is_new: Boolean = false,
|
||||
val source_segments: List<String> = emptyList()
|
||||
)
|
||||
|
||||
// 章节内容详情DTO
|
||||
@@ -45,3 +46,39 @@ data class ChapterContentDto(
|
||||
val updatedAt: Long,
|
||||
val quotes: List<String> = emptyList() // 引用内容列表
|
||||
)
|
||||
|
||||
// 回忆录状态DTO
|
||||
@Serializable
|
||||
data class MemoirStateDto(
|
||||
val current_stage: String,
|
||||
val covered_stages: List<String> = emptyList(),
|
||||
val slots: Map<String, Map<String, SlotInfo>> = emptyMap()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SlotInfo(
|
||||
val snippet: String? = null,
|
||||
val filled: Boolean = false
|
||||
)
|
||||
|
||||
// 任务状态DTO
|
||||
@Serializable
|
||||
data class TasksStatusDto(
|
||||
val total: Int,
|
||||
val pending: Int,
|
||||
val running: Int,
|
||||
val success: Int,
|
||||
val failure: Int,
|
||||
val all_completed: Boolean,
|
||||
val tasks: List<TaskInfoDto> = emptyList()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class TaskInfoDto(
|
||||
val task_id: String,
|
||||
val task_type: String = "memoir",
|
||||
val status: String, // "pending", "running", "success", "failure"
|
||||
val created_at: String? = null,
|
||||
val updated_at: String? = null,
|
||||
val result: Map<String, String>? = null
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user