feat: 实现饮食记录确认流程

- 新增饮食记录确认流程,将自动记录模式升级为用户确认模式,提升用户交互体验。
- 实现两阶段饮食记录流程,支持AI识别食物并生成确认选项,用户选择后记录到数据库并提供营养分析。
- 扩展DTO层,新增相关数据结构以支持确认流程。
- 更新服务层,新增处理确认逻辑的方法,优化饮食记录的创建流程。
- 增强API文档,详细说明新流程及使用建议,确保开发者理解和使用新功能。
This commit is contained in:
richarjiang
2025-08-18 18:59:36 +08:00
parent 485ba1f67c
commit ede5730647
7 changed files with 903 additions and 30 deletions

View File

@@ -0,0 +1,239 @@
# 饮食记录确认流程 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 // 第二阶段可以使用流式
};
```