Files
digital-pilates/services/nutritionLabelAnalysis.ts
richarjiang b27099c6d9 feat(nutrition): 优化营养成分表分析功能并移除流式显示
- 移除流式分析文本显示,简化用户界面
- 修复ImagePicker媒体类型配置,使用数组格式
- 简化API响应处理逻辑,直接使用服务端返回数据
- 移除旧格式转换函数,统一使用新的API响应格式
- 清理冗余状态变量和UI组件,提升代码可维护性
2025-10-16 12:46:43 +08:00

138 lines
4.1 KiB
TypeScript
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.

import { api, postTextStream, type TextStreamCallbacks } from '@/services/api';
import type { CreateDietRecordDto, MealType } from './dietRecords';
export interface NutritionLabelData {
energy: number; // 能量 (kJ/千卡)
protein: number; // 蛋白质 (g)
fat: number; // 脂肪 (g)
carbohydrate: number; // 碳水化合物 (g)
sodium: number; // 钠 (mg)
fiber?: number; // 膳食纤维 (g)
sugar?: number; // 糖 (g)
servingSize?: string; // 每份量
}
export interface NutritionLabelAnalysisResult {
id: string;
imageUri: string;
nutritionData: NutritionLabelData;
confidence: number;
analyzedAt: string;
foodName?: string; // 识别出的食物名称
brand?: string; // 品牌
}
export interface NutritionLabelAnalysisRequest {
imageUri: string;
}
// 新API返回的营养成分项
export interface NutritionItem {
key: string;
name: string;
value: string;
analysis: string;
}
// 新API返回的响应格式
export interface NutritionAnalysisResponse {
success: boolean;
data: NutritionItem[];
message?: string; // 仅在失败时返回
}
// 新API请求参数
export interface NutritionAnalysisRequest {
imageUrl: string;
}
/**
* 分析营养成分表(非流式)
*/
export async function analyzeNutritionLabel(request: NutritionLabelAnalysisRequest): Promise<NutritionLabelAnalysisResult> {
return api.post<NutritionLabelAnalysisResult>('/ai-coach/nutrition-label-analysis', request);
}
/**
* 流式分析营养成分表
*/
export async function analyzeNutritionLabelStream(
request: NutritionLabelAnalysisRequest,
callbacks: TextStreamCallbacks
) {
const body = {
imageUri: request.imageUri,
stream: true
};
return postTextStream('/ai-coach/nutrition-label-analysis', body, callbacks, { timeoutMs: 120000 });
}
/**
* 保存成分表分析结果到饮食记录
*/
export async function saveNutritionLabelToDietRecord(
analysisResult: NutritionLabelAnalysisResult,
mealType: MealType
): Promise<any> {
const dietRecordData: CreateDietRecordDto = {
mealType,
foodName: analysisResult.foodName || '成分表分析食物',
foodDescription: `品牌: ${analysisResult.brand || '未知'}`,
portionDescription: analysisResult.nutritionData.servingSize || '100g',
estimatedCalories: analysisResult.nutritionData.energy,
proteinGrams: analysisResult.nutritionData.protein,
carbohydrateGrams: analysisResult.nutritionData.carbohydrate,
fatGrams: analysisResult.nutritionData.fat,
fiberGrams: analysisResult.nutritionData.fiber,
sugarGrams: analysisResult.nutritionData.sugar,
sodiumMg: analysisResult.nutritionData.sodium,
source: 'vision',
mealTime: new Date().toISOString(),
imageUrl: analysisResult.imageUri,
aiAnalysisResult: analysisResult,
};
return api.post('/diet-records', dietRecordData);
}
/**
* 分析营养成分表图片新API
* 需要先上传图片到COS获取URL然后调用此接口
*/
export async function analyzeNutritionImage(request: NutritionAnalysisRequest): Promise<NutritionAnalysisResponse> {
try {
const response = await api.post<any>('/diet-records/analyze-nutrition-image', request);
// 处理不同的响应格式
if (Array.isArray(response)) {
// 如果直接返回数组,包装成标准格式
return {
success: true,
data: response as NutritionItem[]
};
} else if (response && typeof response === 'object') {
// 如果是对象,检查是否已经是标准格式
if (response.success !== undefined && response.data) {
return response as NutritionAnalysisResponse;
} else if (Array.isArray(response.data)) {
// 如果有data字段且是数组包装成标准格式
return {
success: true,
data: response.data as NutritionItem[]
};
}
}
// 如果都不匹配,返回错误
throw new Error('无法解析API返回结果');
} catch (error) {
console.error('[NUTRITION_ANALYSIS] API调用失败:', error);
return {
success: false,
data: [],
message: error instanceof Error ? error.message : '分析失败'
};
}
}