Files
plates-server/docs/diet-confirmation-flow-api.md
richarjiang ede5730647 feat: 实现饮食记录确认流程
- 新增饮食记录确认流程,将自动记录模式升级为用户确认模式,提升用户交互体验。
- 实现两阶段饮食记录流程,支持AI识别食物并生成确认选项,用户选择后记录到数据库并提供营养分析。
- 扩展DTO层,新增相关数据结构以支持确认流程。
- 更新服务层,新增处理确认逻辑的方法,优化饮食记录的创建流程。
- 增强API文档,详细说明新流程及使用建议,确保开发者理解和使用新功能。
2025-08-18 18:59:36 +08:00

240 lines
6.6 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.

# 饮食记录确认流程 API 文档
## 概述
新的饮食记录流程分为两个阶段:
1. **图片识别阶段**AI识别食物并返回确认选项
2. **用户确认阶段**:用户选择确认选项后记录到数据库
## 重要说明
⚠️ **流式响应兼容性**当系统需要返回确认选项时会自动使用非流式模式返回JSON结构即使客户端请求了 `stream: true`。这确保了确认选项的正确显示。
## API 流程
### 第一阶段:图片识别(返回确认选项)
**请求示例:**
```json
POST /ai-coach/chat
{
"conversationId": "user123-1234567890",
"messages": [
{
"role": "user",
"content": "#记饮食"
}
],
"imageUrls": ["https://example.com/food-image.jpg"],
"stream": false
}
```
**响应示例:**
```json
{
"conversationId": "user123-1234567890",
"data": {
"content": "我识别到了以下食物,请选择要记录的内容:\n\n图片中识别到烤鱼和米饭看起来是一份营养均衡的晚餐。",
"choices": [
{
"id": "food_0",
"label": "一条烤鱼 220卡",
"value": {
"id": "food_0",
"foodName": "烤鱼",
"portion": "1条",
"calories": 220,
"mealType": "dinner",
"nutritionData": {
"proteinGrams": 35,
"carbohydrateGrams": 2,
"fatGrams": 8,
"fiberGrams": 0
}
},
"recommended": true
},
{
"id": "food_1",
"label": "一碗米饭 150卡",
"value": {
"id": "food_1",
"foodName": "米饭",
"portion": "1碗",
"calories": 150,
"mealType": "dinner",
"nutritionData": {
"proteinGrams": 3,
"carbohydrateGrams": 32,
"fatGrams": 0.5,
"fiberGrams": 1
}
},
"recommended": false
}
],
"interactionType": "food_confirmation",
"pendingData": {
"imageUrl": "https://example.com/food-image.jpg",
"recognitionResult": {
"recognizedItems": [...],
"analysisText": "图片中识别到烤鱼和米饭...",
"confidence": 85
}
},
"context": {
"command": "diet",
"step": "confirmation"
}
}
}
```
### 第二阶段:用户确认选择
**请求示例:**
```json
POST /ai-coach/chat
{
"conversationId": "user123-1234567890",
"messages": [
{
"role": "user",
"content": "我选择记录烤鱼"
}
],
"selectedChoiceId": "food_0",
"confirmationData": {
"selectedOption": {
"id": "food_0",
"foodName": "烤鱼",
"portion": "1条",
"calories": 220,
"mealType": "dinner",
"nutritionData": {
"proteinGrams": 35,
"carbohydrateGrams": 2,
"fatGrams": 8,
"fiberGrams": 0
}
},
"imageUrl": "https://example.com/food-image.jpg"
},
"stream": false
}
```
**响应示例:**
```json
{
"conversationId": "user123-1234567890",
"text": "很好我已经为您记录了这份烤鱼1条约220卡路里。\n\n根据您的饮食记录这是一份优质的蛋白质来源包含35克蛋白质脂肪含量适中。建议搭配一些蔬菜来增加膳食纤维的摄入。\n\n您今天的饮食营养搭配看起来不错记得保持均衡的饮食习惯"
}
```
## 数据结构说明
### AiChoiceOptionDto
```typescript
{
id: string; // 选项唯一标识符
label: string; // 显示给用户的文本(如"一条鱼 200卡"
value: any; // 选项对应的数据
recommended?: boolean; // 是否为推荐选项
}
```
### AiResponseDataDto
```typescript
{
content: string; // AI回复的文本内容
choices?: AiChoiceOptionDto[]; // 选择选项(可选)
interactionType?: string; // 交互类型:'text' | 'food_confirmation' | 'selection'
pendingData?: any; // 需要用户确认的数据(可选)
context?: any; // 上下文信息(可选)
}
```
### FoodConfirmationOption
```typescript
{
id: string; // 唯一标识符
label: string; // 显示文本
foodName: string; // 食物名称
portion: string; // 份量描述
calories: number; // 估算热量
mealType: MealType; // 餐次类型
nutritionData: { // 营养数据
proteinGrams?: number; // 蛋白质(克)
carbohydrateGrams?: number; // 碳水化合物(克)
fatGrams?: number; // 脂肪(克)
fiberGrams?: number; // 膳食纤维(克)
};
}
```
## 错误处理
### 图片识别失败
如果图片模糊或无法识别食物API会返回正常的文本响应
```json
{
"conversationId": "user123-1234567890",
"text": "抱歉,我无法清晰地识别图片中的食物。请确保图片清晰,光线充足,食物在画面中清晰可见,然后重新上传。"
}
```
### 无效的确认数据
如果第二阶段的确认数据无效,系统会返回错误提示:
```json
{
"conversationId": "user123-1234567890",
"text": "确认数据无效,请重新选择要记录的食物。"
}
```
## 使用建议
1. **图片质量**:确保上传的图片清晰,光线充足,食物在画面中清晰可见
2. **选择确认**:用户可以选择多个食物选项,每次确认记录一种食物
3. **营养分析**:系统会基于用户的历史饮食记录提供个性化的营养分析和建议
4. **流式响应处理**
- 客户端应该检查响应的 `Content-Type`
- `application/json`:结构化数据(确认选项)
- `text/plain`:流式文本
- 当返回确认选项时,系统会忽略 `stream` 参数并返回JSON
## 客户端适配指南
### 响应类型检测
```javascript
// 检查响应类型
if (response.headers['content-type'].includes('application/json')) {
// 处理结构化数据(确认选项)
const data = await response.json();
if (data.data && data.data.choices) {
// 显示选择选项
showFoodConfirmationOptions(data.data.choices);
}
} else {
// 处理流式文本
handleStreamResponse(response);
}
```
### 确认选择发送
```javascript
// 用户选择后发送确认
const confirmationRequest = {
conversationId: "user123-1234567890",
messages: [{ role: "user", content: "我选择记录烤鱼" }],
selectedChoiceId: "food_0",
confirmationData: {
selectedOption: selectedFoodOption,
imageUrl: originalImageUrl
},
stream: true // 第二阶段可以使用流式
};
```