一、故事翻译及自动标注重点单词功能

1.我们项目的互动式情景、中英文翻译及解析功能需要与DeepSeek连续对话。我们团队通过对消息列表的构建维护DeepSeek的历史上下文,实现连续对话和重新回答功能,完整代码实现如下:
 

fun streamResponse(messages: List<Pair<String, String>>, prompt: String): Flow<String> = callbackFlow {
    val url = "$apiBase/chat/completions"
 
    val jsonMessages = JSONArray().apply {
        for ((userInput, assistantReply) in messages) {
            put(JSONObject().apply {
                put("role", "user")
                put("content", userInput) 
            })
            put(JSONObject().apply {
                put("role", "assistant")
                put("content", assistantReply)
            })
        }
 
        put(JSONObject().apply {
            put("role", "user")
            put("content", prompt) 
        })
    }
 
    val payload = JSONObject().apply {
        put("model", model)
        put("messages", jsonMessages) 
        put("temperature", 0.7)
        put("max_tokens", 1024)
        put("stream", true)
    }
 
    val request = withContext(Dispatchers.IO) {
        Request.Builder()
            .url(url)
            .header("Authorization", "Bearer $apiKey")
            .header("Content-Type", "application/json")
            .post(payload.toString().toRequestBody())
            .build()
    }
 
    val eventSourceFactory = EventSources.createFactory(client)
 
    val finalResponse = StringBuilder() 
 
    val eventSourceListener = object : EventSourceListener() {
        override fun onEvent(
            eventSource: EventSource,
            id: String?,
            type: String?,
            data: String
        ) {
            val trimmedData = data.trim()
            Log.d("DeepSeekDebug", "收到完整数据: $trimmedData")
 
            if (trimmedData == "[DONE]") {
                // 请求结束时打印最终结果
                Log.d("DeepSeekDebug", "请求完成,最终回复: $finalResponse")
                close()
                return
            }
 
            try {
                val jsonObject = JSONObject(trimmedData)
                val choices = jsonObject.getJSONArray("choices")
                if (choices.length() > 0) {
                    val delta = choices.getJSONObject(0).getJSONObject("delta")
                    if (delta.has("content")) {
                        val content = delta.getString("content")
                        finalResponse.append(content) 
                        trySend(content) 
                    }
                }
            } catch (e: Exception) {
                trySend("\n[解析错误] ${e.message}")
            }
        }
 
        override fun onFailure(
            eventSource: EventSource,
            t: Throwable?,
            response: Response?
        ) {
            trySend("\n[API请求错误] ${t?.message}")
            close(t)
        }
 
        override fun onClosed(eventSource: EventSource) {
            close()
        }
    }
 
    val eventSource = eventSourceFactory.newEventSource(request, eventSourceListener)
 
    awaitClose {
        eventSource.cancel()
    }
}

2.消息列表的构建与持久化 

构建历史消息列表,通过 uiState.fullResponse.split("\n\n\n") 将完整的对话历史分割成多个部分,并从中提取出用户的输入和助手的回答,然后使用 chunked(2) 将每一轮对话(用户输入和助手回答)配对成一个 Pair。

val history = uiState.fullResponse.split("\n\n\n").mapNotNull {
                val parts = it.split(": ", limit = 2)
                if (parts.size == 2) parts[1] else null
            }.chunked(2).map {
                it[0] to (it.getOrNull(1) ?: "")
            }

然后将该列表传递给 deepSeekService.streamResponse 方法:

  deepSeekService.streamResponse(history, prompt)

3.临时对话存储 ,使用单例模式来容纳一个线程安全的静态变量,并对其进行控制,避免受到 ViewModel 生命周期的影响,这里使用_globalContent维护全局历史消息,使用Mutex确保线程安全。

private const val TAG = "GlobalTracker"
    private var _globalContent: String = ""
    private val mutex = Mutex()
 
    val globalContent: String
        get() = _globalContent
 
    suspend fun appendContent(newContent: String) {
        mutex.withLock {
            _globalContent += newContent
            Log.d(TAG, "globalContent 发生变化: $_globalContent")
        }
    }

4.对话消息持久化,定义对话历史实体类: 

@Entity(tableName = "chat_history")
data class ChatHistoryEntity(
    @PrimaryKey(autoGenerate = true) val id: Int = 0, // 消息ID
    val timestamp: Long = System.currentTimeMillis(), // 时间戳
    val role: String, // 角色
    val content: String // 内容
)

5.实现界面如下:

 

二、四六级真题

1.数据模型类,这个类可以被序列化和反序列化,用于 JSON 格式的转换。data class:Kotlin 的数据类,自动生成 equals(), hashCode(), toString(), copy() 等方法。使用Level 枚举 ,定义了两种级别:CET4 和 CET6 ,均可以序列化。完整代码如下:


@kotlinx.serialization.Serializable
data class Exam(
    val title: String,
    val level: Level,
    val questions: List<Question>
) {
    @kotlinx.serialization.Serializable
    enum class Level {
        CET4, CET6
    }

    @kotlinx.serialization.Serializable
    data class Question(
        val id: Int,
        val type: QuestionType,
        val text: String,
        val options: List<String>,
        val correctAnswers: List<Int>, // 索引列表,对于单选题只有一个元素,多选题可以有多个
        val explanation: String
    )

    @kotlinx.serialization.Serializable
    enum class QuestionType {
        SINGLE_CHOICE, MULTIPLE_CHOICE
    }
} 

2. ExamViewModel文件,管理考试应用的状态和数据

UI 状态管理,使用 mutableStateOf 管理 UI 状态,对外暴露不可变的 State 对象;当前考试集管理,使用 StateFlow 管理当前显示的考试集索引,对外暴露为不可变的 StateFlow。

private val _uiState = mutableStateOf<ExamUiState>(ExamUiState.Loading)
val uiState: State<ExamUiState> = _uiState

private val _currentExamSet = MutableStateFlow(0)
val currentExamSet = _currentExamSet.asStateFlow()

在 viewModelScope 协程中执行加载试题,更新当前考试集索引,成功时更新 UI 状态为 Success 并传递对应考试数据,失败时更新 UI 状态为 Error。 

fun loadExam(setIndex: Int) {
    viewModelScope.launch {
        try {
            _currentExamSet.value = setIndex
            _uiState.value = ExamUiState.Success(_examSets[setIndex])
        } catch (e: Exception) {
            _uiState.value = ExamUiState.Error(DataResult.Error.Code.UNKNOWN)
        }
    }
}

3.功能界面设计,多组件组合 

 设计流程图如下:

4.实现界面如下:

三、每日阅读功能的开发与实现

1.功能描述:进行整篇文章的阅读,点击单词显示其释义或翻译,支持侧边栏的显示/隐藏,集成 DeepSeek 翻译服务获取单词翻译,本地缓存已查询过的单词。

2.实现思路:

3.实现效果展示:

四、部分前端页面的优化

1.帮助界面优化:

2.默写练习页面优化:

3.谐音梗界面优化:

4.故事界面优化:

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐