Files
digital-pilates/docs/complete-cancel-solution.md
richarjiang d52981ab29 feat: 实现聊天取消功能,提升用户交互体验
- 在教练页面中添加用户取消发送或终止回复的能力
- 更新发送按钮状态,支持发送和取消状态切换
- 在流式回复中显示取消按钮,允许用户中断助手的生成
- 增强请求管理,添加请求序列号和有效性验证,防止延迟响应影响用户体验
- 优化错误处理,区分用户主动取消和网络错误,提升系统稳定性
- 更新相关文档,详细描述取消功能的实现和用户体验设计
2025-08-18 18:59:23 +08:00

235 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 完整取消功能解决方案
## 问题诊断
您遇到的问题确实需要前后端协作解决:
1. **客户端问题**: XMLHttpRequest.abort() 只能终止网络传输,无法停止服务端处理
2. **会话持久化问题**: 服务端可能已经开始处理并保存会话记录
3. **延迟响应问题**: 服务端处理完成后,响应仍会到达客户端
## 已实现的客户端增强方案
### 1. 请求序列号机制
```typescript
// 防止延迟响应影响当前状态
const requestSequenceRef = useRef<number>(0);
const activeRequestIdRef = useRef<string | null>(null);
function cancelCurrentRequest() {
// 增加序列号,使后续响应失效
requestSequenceRef.current += 1;
activeRequestIdRef.current = null;
// 中断网络请求
streamAbortRef.current?.abort();
// 清理状态和UI
setIsSending(false);
setIsStreaming(false);
// ...
}
```
### 2. 请求有效性验证
```typescript
const isRequestValid = () => {
return activeRequestIdRef.current === requestId &&
requestSequenceRef.current === currentSequence;
};
// 在所有回调中验证
const onChunk = (chunk: string) => {
if (!isRequestValid()) {
console.log('Ignoring chunk from invalidated request');
return;
}
// 处理chunk...
};
```
### 3. 延迟会话ID生成
```typescript
// 修改前立即生成会话ID
function ensureConversationId(): string {
const cid = `mobile-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
setConversationId(cid); // 立即设置
return cid;
}
// 修改后:延迟生成,只在请求成功时设置
function ensureConversationId(): string {
if (conversationId && conversationId.trim()) return conversationId;
return ''; // 返回空,让服务端生成
}
```
### 4. 请求追踪机制
```typescript
// 在 api.ts 中添加请求ID
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
const requestHeaders = {
'Content-Type': 'application/json',
'X-Request-Id': requestId, // 用于服务端追踪
// ...
};
```
## 需要服务端配合的方案
### 方案1: 连接断开检测(推荐)
**原理**: 服务端检测客户端连接状态,断开时停止处理
```python
# 服务端伪代码
async def ai_chat_stream(request):
conversation_id = request.json.get('conversationId')
is_new_conversation = not conversation_id
try:
# 检查客户端连接
if await request.is_disconnected():
return
# 延迟创建会话记录
temp_conv_id = None
if is_new_conversation:
temp_conv_id = create_temp_conversation()
async for chunk in ai_generate_stream():
# 每次生成前检查连接
if await request.is_disconnected():
if temp_conv_id:
delete_temp_conversation(temp_conv_id)
return
yield chunk
# 只有成功完成才保存会话
if temp_conv_id:
save_permanent_conversation(temp_conv_id)
except ClientDisconnectedError:
if temp_conv_id:
delete_temp_conversation(temp_conv_id)
```
**优点**:
- 自动检测无需额外API
- 资源清理及时
- 实现相对简单
### 方案2: 主动取消端点(备选)
**原理**: 提供专门的取消API客户端主动通知
```python
@app.post("/api/ai-coach/cancel")
async def cancel_request(request):
request_id = request.json.get('requestId')
conversation_id = request.json.get('conversationId')
# 标记请求取消
cancel_request_processing(request_id)
# 清理临时会话
if is_temp_conversation(conversation_id):
delete_conversation(conversation_id)
return {"status": "cancelled"}
```
**客户端调用**:
```typescript
function cancelCurrentRequest() {
// 现有逻辑...
// 主动通知服务端
if (activeRequestIdRef.current) {
notifyServerCancel(conversationId, activeRequestIdRef.current);
}
}
async function notifyServerCancel(conversationId?: string, requestId?: string) {
try {
await api.post('/api/ai-coach/cancel', {
requestId,
conversationId
});
} catch (error) {
console.warn('Failed to notify server:', error);
}
}
```
## 数据库设计建议
```sql
-- 会话状态管理
CREATE TABLE conversations (
id VARCHAR(50) PRIMARY KEY,
user_id INT,
status ENUM('temp', 'active', 'cancelled') DEFAULT 'temp',
created_at TIMESTAMP,
confirmed_at TIMESTAMP NULL,
messages JSON,
INDEX idx_status_created (status, created_at)
);
-- 定期清理临时会话(防止内存泄漏)
-- 可以用定时任务或在新请求时清理
DELETE FROM conversations
WHERE status = 'temp'
AND created_at < NOW() - INTERVAL 10 MINUTE;
```
## 立即可用的解决方案
### 当前已实现(无需服务端修改)
客户端增强已经可以解决大部分问题:
1.**防止延迟响应**: 请求序列号机制
2.**严格状态管理**: 请求有效性验证
3.**延迟会话创建**: 避免空会话记录
4.**即时UI反馈**: 取消后立即清理界面
### 效果评估
**改进前**:
- 点击取消 → 网络中断 → 但服务端继续处理 → 稍后响应仍然到达
- 会话记录已创建且保留
**改进后**:
- 点击取消 → 序列号增加 → 界面立即清理 → 延迟响应被忽略
- 会话ID延迟生成减少空记录
### 推荐实施步骤
**第一阶段(立即可用)**:
1. ✅ 使用当前客户端增强方案
2. 测试取消功能的改善效果
**第二阶段(服务端配合)**:
1. 实现连接断开检测
2. 临时会话管理机制
3. 完善资源清理
**第三阶段(可选优化)**:
1. 主动取消通知端点
2. 数据库优化和定期清理
3. 监控和日志完善
## 测试验证
建议测试以下场景:
1. **快速取消**: 发送后立即点击取消
2. **生成中取消**: 在AI回复过程中取消
3. **网络异常**: 弱网环境下的取消行为
4. **连续操作**: 快速发送多条消息并取消
5. **状态恢复**: 取消后是否能正常发送新消息
通过这个完整方案,可以显著改善取消功能的用户体验,即使在服务端暂未配合的情况下,客户端增强也能解决大部分问题。