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 { return api.post('/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 { 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 { try { const response = await api.post('/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 : '分析失败' }; } } // 营养成分分析记录的接口定义 export interface NutritionAnalysisRecord { id: number; userId: string; imageUrl: string; analysisResult: { data: NutritionItem[]; success: boolean; message?: string; }; status: 'success' | 'failed' | 'processing'; message: string; aiProvider: string; aiModel: string; nutritionCount: number; createdAt: string; updatedAt: string; } // 获取历史记录的请求参数 export interface GetNutritionRecordsParams { startDate?: string; endDate?: string; status?: string; page?: number; limit?: number; } // 获取历史记录的响应格式 export interface GetNutritionRecordsResponse { code: number; message: string; data: { records: NutritionAnalysisRecord[]; total: number; page: number; limit: number; totalPages: number; }; } /** * 获取营养成分分析记录列表 */ export async function getNutritionAnalysisRecords(params?: GetNutritionRecordsParams): Promise { try { const searchParams = new URLSearchParams(); if (params) { Object.entries(params).forEach(([key, value]) => { if (value !== undefined) { searchParams.append(key, String(value)); } }); } const queryString = searchParams.toString() ? `?${searchParams.toString()}` : ''; // 使用 api.get 方法,但需要特殊处理响应格式 const response = await api.get(`/diet-records/nutrition-analysis-records${queryString}`); // 检查响应是否已经是标准格式 if (response && typeof response === 'object' && 'code' in response) { return response as GetNutritionRecordsResponse; } // 如果不是标准格式,包装成标准格式 return { code: 0, message: '获取成功', data: { records: response.records || [], total: response.total || 0, page: response.page || 1, limit: response.limit || 20, totalPages: response.totalPages || Math.ceil((response.total || 0) / (response.limit || 20)) } }; } catch (error) { console.error('[NUTRITION_RECORDS] 获取历史记录失败:', error); // 返回错误格式的响应 return { code: 1, message: error instanceof Error ? error.message : '获取营养成分分析记录失败,请稍后重试', data: { records: [], total: 0, page: 1, limit: 20, totalPages: 0 } }; } } /** * 删除营养成分分析记录 */ export async function deleteNutritionAnalysisRecord(recordId: number): Promise { try { const response = await api.delete(`/diet-records/nutrition-analysis-records/${recordId}`); return response; } catch (error) { console.error('[NUTRITION_RECORDS] 删除记录失败:', error); throw error; } }