- 新增饮食记录确认流程,将自动记录模式升级为用户确认模式,提升用户交互体验。 - 实现两阶段饮食记录流程,支持AI识别食物并生成确认选项,用户选择后记录到数据库并提供营养分析。 - 扩展DTO层,新增相关数据结构以支持确认流程。 - 更新服务层,新增处理确认逻辑的方法,优化饮食记录的创建流程。 - 增强API文档,详细说明新流程及使用建议,确保开发者理解和使用新功能。
240 lines
6.6 KiB
Markdown
240 lines
6.6 KiB
Markdown
# 饮食记录确认流程 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 // 第二阶段可以使用流式
|
||
};
|
||
```
|