# TODO: 语音实时分段上传的取消逻辑 ## 背景 当前 `cancelRecordingVoice()` 只做了:停止录音、删除当前临时文件、重置会话状态,**没有**处理: - 已入队的待上传分段(`pendingVoiceSegmentStore`) - 重试任务(`pendingSegmentRetryJob`) - 正在执行的 `dispatchPendingVoiceSegment` 协程 用户取消后,已切好的分段仍可能被发送或重试。 ## 目标 用户点击「取消」后:当前语音会话的所有分段不再上传、不再重试,并释放相关资源。 --- ## TODO 列表 ### 1. ViewModel 增加「已取消会话」集合 - [ ] 在 `CreateMemoryViewModel` 中增加:`cancelledVoiceSessionIds: MutableSet` - [ ] 在开始新一轮录音时(如 `startRecordingVoice()` 或 `ensureVoiceSessionStarted()`)清理该集合,避免无限增长(可选:只保留最近 N 个或按会话生命周期清理) ### 2. 完善 `cancelRecordingVoice()` - [ ] 在调用 `resetVoiceSessionState()` **之前** 保存 `currentVoiceSessionId` 到局部变量 `sessionId` - [ ] 若 `sessionId != null`: - [ ] `cancelledVoiceSessionIds.add(sessionId)` - [ ] `pendingSegmentRetryJob?.cancel()` - [ ] 列出该 session 的所有待发分段并逐个从 store 删除: - 若 Store 无按 `voiceSessionId` 查询:用 `pendingVoiceSegmentStore.listAll().filter { it.voiceSessionId == sessionId }`,对每个调用 `pendingVoiceSegmentStore.remove(segment.clientSegmentId)` - [ ] 再调用 `resetVoiceSessionState()`(保持现有逻辑) ### 3. `dispatchPendingVoiceSegment` 开头做取消判断 - [ ] 在 `tryAcquirePendingDispatch` 之后、`ensureConversationReadyForSegmentUpload()` 之前增加: - 若 `pendingSegment.voiceSessionId in cancelledVoiceSessionIds`: - `pendingVoiceSegmentStore.remove(pendingSegment.clientSegmentId)` - `releasePendingDispatch(pendingSegment.clientSegmentId)` - `return` - [ ] 确保 `finally` 中 `releasePendingDispatch` 仍对正常路径执行(现有逻辑保持不变) ### 4. (可选)Store 按 session 列举 - [ ] 在 `PendingVoiceSegmentStore` 增加 `listByVoiceSession(voiceSessionId: String): List`(例如基于 `listAll().filter { it.voiceSessionId == voiceSessionId }`) - [ ] 在 `cancelRecordingVoice()` 中用 `listByVoiceSession(sessionId)` 替代 `listAll().filter { ... }`,减少全量列举 --- ## 涉及文件 - `app-android/app/src/main/java/com/huaga/life_echo/ui/viewmodel/CreateMemoryViewModel.kt` - `app-android/app/src/main/java/com/huaga/life_echo/feature/voice/PendingVoiceSegmentStore.kt`(仅当做第 4 项时) ## 验收 - 长按录音并触发至少一次自动切片(如 30s)后,点击取消:本地待发分段被清除,不再上传,重试任务不执行。 - 取消后再次录音:新会话正常录音与分段上传,无旧 session 分段被发送。