- 在教练页面中新增AI选择选项和食物确认选项的数据结构 - 扩展消息结构以支持选择选项和交互类型 - 实现非流式JSON响应的处理逻辑,支持用户确认选择 - 添加选择选项的UI组件,提升用户交互体验 - 更新样式以适应新功能,确保视觉一致性
6.9 KiB
6.9 KiB
饮食记录确认流程客户端实现
概述
已完成对客户端的改动,支持饮食记录确认流程的新API结构。实现了对非流式JSON响应的处理,能够正确显示食物选择选项,并支持用户确认选择。
主要改动
1. 数据结构扩展
在 app/(tabs)/coach.tsx 中添加了新的类型定义:
// AI选择选项数据结构
type AiChoiceOption = {
id: string;
label: string;
value: any;
recommended?: boolean;
};
// 餐次类型
type MealType = 'breakfast' | 'lunch' | 'dinner' | 'snack';
// 食物确认选项数据结构
type FoodConfirmationOption = {
id: string;
label: string;
foodName: string;
portion: string;
calories: number;
mealType: MealType;
nutritionData: {
proteinGrams?: number;
carbohydrateGrams?: number;
fatGrams?: number;
fiberGrams?: number;
};
};
// AI响应数据结构
type AiResponseData = {
content: string;
choices?: AiChoiceOption[];
interactionType?: 'text' | 'food_confirmation' | 'selection';
pendingData?: any;
context?: any;
};
2. 消息结构扩展
扩展了 ChatMessage 类型以支持新的字段:
type ChatMessage = {
id: string;
role: Role;
content: string;
attachments?: MessageAttachment[];
choices?: AiChoiceOption[]; // 新增:选择选项
interactionType?: string; // 新增:交互类型
pendingData?: any; // 新增:待确认数据
context?: any; // 新增:上下文信息
};
3. 响应处理逻辑
支持非流式JSON响应
修改了 sendRequestInternal 函数以检测和处理非流式JSON响应:
// 如果是非流式请求,直接调用API并处理响应
if (!body.stream) {
try {
const response = await api.post<{ conversationId?: string; data?: AiResponseData; text?: string }>('/api/ai-coach/chat', body);
// 处理响应
if (response.data) {
// 结构化响应(可能包含选择选项)
const assistantMsg: ChatMessage = {
id: assistantId,
role: 'assistant',
content: response.data.content,
choices: response.data.choices,
interactionType: response.data.interactionType,
pendingData: response.data.pendingData,
context: response.data.context,
};
// ... 更新消息状态
}
// ...
}
}
流式响应中的JSON检测
在流式响应处理中添加了JSON检测逻辑:
const onChunk = (chunk: string) => {
// 尝试解析是否为JSON结构化数据(可能是确认选项)
try {
const parsed = JSON.parse(chunk);
if (parsed && parsed.data && parsed.data.choices) {
// 处理结构化响应(包含选择选项)
// ... 创建带选择选项的消息
return;
}
} catch {
// 不是JSON,继续作为普通文本处理
}
// ... 正常的文本流处理
};
4. 选择选项UI组件
在 renderBubbleContent 函数中添加了选择选项的渲染逻辑:
// 检查是否有选择选项需要显示
if (item.choices && item.choices.length > 0 && item.interactionType === 'food_confirmation') {
return (
<View style={{ gap: 12 }}>
<Markdown style={markdownStyles} mergeStyle>
{item.content || ''}
</Markdown>
<View style={styles.choicesContainer}>
{item.choices.map((choice) => (
<TouchableOpacity
key={choice.id}
style={[
styles.choiceButton,
choice.recommended && styles.choiceButtonRecommended
]}
onPress={() => handleChoiceSelection(choice, item)}
>
<View style={styles.choiceContent}>
<Text style={[
styles.choiceLabel,
choice.recommended && styles.choiceLabelRecommended
]}>
{choice.label}
</Text>
{choice.recommended && (
<View style={styles.recommendedBadge}>
<Text style={styles.recommendedText}>推荐</Text>
</View>
)}
</View>
</TouchableOpacity>
))}
</View>
</View>
);
}
5. 选择确认逻辑
实现了 handleChoiceSelection 函数处理用户选择:
async function handleChoiceSelection(choice: AiChoiceOption, message: ChatMessage) {
try {
// 构建确认请求
const confirmationText = `我选择记录${choice.label}`;
// 发送确认消息,包含选择的数据
await sendStreamWithConfirmation(confirmationText, choice.id, {
selectedOption: choice.value,
imageUrl: message.pendingData?.imageUrl
});
} catch (e: any) {
Alert.alert('选择失败', e?.message || '选择失败,请重试');
}
}
6. 确认数据发送
添加了 sendStreamWithConfirmation 函数:
async function sendStreamWithConfirmation(text: string, selectedChoiceId: string, confirmationData: any) {
const historyForServer = convertToServerMessages(messages);
const cid = ensureConversationId();
const body = {
conversationId: cid,
messages: [...historyForServer, { role: 'user' as const, content: text }],
selectedChoiceId,
confirmationData,
stream: false, // 确认阶段使用非流式
};
await sendRequestInternal(body, text);
}
7. UI样式
添加了选择选项相关的样式:
choicesContainer: {
gap: 8,
},
choiceButton: {
backgroundColor: 'rgba(255,255,255,0.9)',
borderWidth: 1,
borderColor: 'rgba(187,242,70,0.3)',
borderRadius: 12,
padding: 12,
},
choiceButtonRecommended: {
borderColor: 'rgba(187,242,70,0.6)',
backgroundColor: 'rgba(187,242,70,0.1)',
},
// ... 其他相关样式
使用流程
- 用户发送饮食图片:用户点击"#记饮食"并上传食物图片
- AI识别返回选项:服务器返回食物识别结果和确认选项(非流式JSON)
- 显示选择选项:客户端渲染食物选择选项,推荐项目带有特殊标识
- 用户选择确认:用户点击某个选项
- 发送确认数据:客户端发送确认请求,包含选择的食物数据
- 记录完成:服务器记录饮食数据并返回确认消息
兼容性
- 保持对现有流式文本响应的完全兼容
- 自动检测响应类型(JSON vs 文本流)
- 旧的饮食记录方式(文字输入)继续正常工作
- 缓存和会话管理支持新的消息结构
技术要点
- 响应类型检测:客户端能够自动识别JSON结构化响应和普通文本流
- 状态管理:新增状态正确地保存到本地缓存
- 用户体验:选择选项有清晰的视觉反馈,推荐选项突出显示
- 错误处理:完整的错误处理机制,确保用户体验流畅
- 性能优化:避免不必要的重渲染,保持界面响应性
这个实现完全符合API文档的要求,支持饮食记录的两阶段确认流程,并保持了与现有功能的兼容性。