feat(diet-records): 新增营养成分表图片分析功能

- 添加营养成分表图片识别API接口,支持通过AI模型分析食物营养成分
- 新增NutritionAnalysisService服务,集成GLM-4.5V和Qwen VL视觉模型
- 实现营养成分提取和健康建议生成功能
- 添加完整的API文档和TypeScript类型定义
- 支持多种营养素类型识别,包括热量、蛋白质、脂肪等20+种营养素
This commit is contained in:
richarjiang
2025-10-16 10:03:22 +08:00
parent cc83b84c80
commit 5c2c9dfae8
7 changed files with 684 additions and 3 deletions

View File

@@ -0,0 +1,295 @@
# 营养成分表分析 API 文档
## 接口概述
本接口用于分析食物营养成分表图片通过AI大模型智能识别图片中的营养成分信息并为每个营养素提供详细的健康建议。
## 接口信息
- **接口地址**: `POST /diet-records/analyze-nutrition-image`
- **请求方式**: POST
- **内容类型**: `application/json`
- **认证方式**: Bearer Token (JWT)
## 请求参数
### Headers
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| Authorization | string | 是 | JWT认证令牌格式`Bearer {token}` |
| Content-Type | string | 是 | 固定值:`application/json` |
### Body 参数
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|--------|------|------|------|------|
| imageUrl | string | 是 | 营养成分表图片的URL地址 | `https://example.com/nutrition-label.jpg` |
#### 请求示例
```json
{
"imageUrl": "https://example.com/nutrition-label.jpg"
}
```
## 响应格式
### 成功响应
```json
{
"success": true,
"data": [
{
"key": "energy_kcal",
"name": "热量",
"value": "840千焦",
"analysis": "840千焦约等于201卡路里占成人每日推荐摄入总热量的10%,属于中等热量水平。"
},
{
"key": "protein",
"name": "蛋白质",
"value": "12.5g",
"analysis": "12.5克蛋白质占成人每日推荐摄入量的21%,是良好的蛋白质来源,有助于肌肉修复和生长。"
},
{
"key": "fat",
"name": "脂肪",
"value": "6.8g",
"analysis": "6.8克脂肪含量适中,主要包含不饱和脂肪酸,有助于维持正常的生理功能。"
},
{
"key": "carbohydrate",
"name": "碳水化合物",
"value": "28.5g",
"analysis": "28.5克碳水化合物提供主要能量来源,建议搭配运动以充分利用能量。"
},
{
"key": "sodium",
"name": "钠",
"value": "480mg",
"analysis": "480毫克钠含量适中约占成人每日推荐摄入量的20%,高血压患者需注意控制总钠摄入。"
}
]
}
```
### 错误响应
```json
{
"success": false,
"data": [],
"message": "错误描述信息"
}
```
## 响应字段说明
### 通用字段
| 字段名 | 类型 | 说明 |
|--------|------|------|
| success | boolean | 操作是否成功 |
| data | array | 营养成分分析结果数组 |
| message | string | 错误信息(仅在失败时返回) |
### 营养成分项字段 (data数组中的对象)
| 字段名 | 类型 | 说明 | 示例 |
|--------|------|------|------|
| key | string | 营养素的唯一标识符 | `energy_kcal` |
| name | string | 营养素的中文名称 | `热量` |
| value | string | 从图片中识别的原始值和单位 | `840千焦` |
| analysis | string | 针对该营养素的详细健康建议 | `840千焦约等于201卡路里...` |
## 支持的营养素类型
| 营养素 | key值 | 中文名称 |
|--------|-------|----------|
| 热量/能量 | energy_kcal | 热量 |
| 蛋白质 | protein | 蛋白质 |
| 脂肪 | fat | 脂肪 |
| 碳水化合物 | carbohydrate | 碳水化合物 |
| 膳食纤维 | fiber | 膳食纤维 |
| 钠 | sodium | 钠 |
| 钙 | calcium | 钙 |
| 铁 | iron | 铁 |
| 锌 | zinc | 锌 |
| 维生素C | vitamin_c | 维生素C |
| 维生素A | vitamin_a | 维生素A |
| 维生素D | vitamin_d | 维生素D |
| 维生素E | vitamin_e | 维生素E |
| 维生素B1 | vitamin_b1 | 维生素B1 |
| 维生素B2 | vitamin_b2 | 维生素B2 |
| 维生素B6 | vitamin_b6 | 维生素B6 |
| 维生素B12 | vitamin_b12 | 维生素B12 |
| 叶酸 | folic_acid | 叶酸 |
| 胆固醇 | cholesterol | 胆固醇 |
| 饱和脂肪 | saturated_fat | 饱和脂肪 |
| 反式脂肪 | trans_fat | 反式脂肪 |
| 糖 | sugar | 糖 |
## 错误码说明
| HTTP状态码 | 错误信息 | 说明 |
|------------|----------|------|
| 400 | 请提供图片URL | 请求体中缺少imageUrl参数 |
| 400 | 图片URL格式不正确 | 提供的URL格式无效 |
| 401 | 未授权访问 | 缺少或无效的JWT令牌 |
| 500 | 营养成分表分析失败,请稍后重试 | AI模型调用失败或服务器内部错误 |
| 500 | 图片中未检测到有效的营养成分表信息 | 图片中未识别到营养成分表 |
## 使用注意事项
### 图片要求
1. **图片格式**: 支持 JPG、PNG、WebP 格式
2. **图片内容**: 必须包含清晰的营养成分表
3. **图片质量**: 建议使用高清、无模糊、光线充足的图片
4. **URL要求**: 图片URL必须是公网可访问的地址
### 最佳实践
1. **URL有效性**: 确保提供的图片URL在分析期间保持可访问
2. **图片预处理**: 建议在客户端对图片进行适当的裁剪,突出营养成分表部分
3. **错误处理**: 客户端应妥善处理各种错误情况,提供友好的用户提示
4. **重试机制**: 对于网络或服务器错误,建议实现适当的重试机制
### 限制说明
1. **调用频率**: 建议客户端控制调用频率,避免过于频繁的请求
2. **图片大小**: 虽然不直接限制图片大小,但过大的图片可能影响处理速度
3. **并发限制**: 服务端可能有并发请求限制,建议客户端实现队列机制
## 客户端集成示例
### JavaScript/TypeScript 示例
```typescript
interface NutritionAnalysisRequest {
imageUrl: string;
}
interface NutritionAnalysisItem {
key: string;
name: string;
value: string;
analysis: string;
}
interface NutritionAnalysisResponse {
success: boolean;
data: NutritionAnalysisItem[];
message?: string;
}
async function analyzeNutritionImage(
imageUrl: string,
token: string
): Promise<NutritionAnalysisResponse> {
try {
const response = await fetch('/diet-records/analyze-nutrition-image', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ imageUrl })
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.message || '请求失败');
}
return result;
} catch (error) {
console.error('营养成分分析失败:', error);
throw error;
}
}
// 使用示例
const token = 'your-jwt-token';
const imageUrl = 'https://example.com/nutrition-label.jpg';
analyzeNutritionImage(imageUrl, token)
.then(result => {
if (result.success) {
console.log('识别到营养素数量:', result.data.length);
result.data.forEach(item => {
console.log(`${item.name}: ${item.value}`);
console.log(`建议: ${item.analysis}`);
});
} else {
console.error('分析失败:', result.message);
}
})
.catch(error => {
console.error('请求异常:', error);
});
```
### Swift 示例
```swift
struct NutritionAnalysisRequest: Codable {
let imageUrl: String
}
struct NutritionAnalysisItem: Codable {
let key: String
let name: String
let value: String
let analysis: String
}
struct NutritionAnalysisResponse: Codable {
let success: Bool
let data: [NutritionAnalysisItem]
let message: String?
}
class NutritionAnalysisService {
func analyzeNutritionImage(imageUrl: String, token: String) async throws -> NutritionAnalysisResponse {
guard let url = URL(string: "/diet-records/analyze-nutrition-image") else {
throw URLError(.badURL)
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let requestBody = NutritionAnalysisRequest(imageUrl: imageUrl)
request.httpBody = try JSONEncoder().encode(requestBody)
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw URLError(.badServerResponse)
}
guard 200...299 ~= httpResponse.statusCode else {
throw NSError(domain: "APIError", code: httpResponse.statusCode, userInfo: [NSLocalizedDescriptionKey: "HTTP Error"])
}
let result = try JSONDecoder().decode(NutritionAnalysisResponse.self, from: data)
return result
}
}
```
## 更新日志
| 版本 | 日期 | 更新内容 |
|------|------|----------|
| 1.0.0 | 2024-10-16 | 初始版本,支持营养成分表图片分析功能 |
## 技术支持
如有技术问题或集成困难,请联系开发团队获取支持。