feat: 新增饮食记录和分析功能
- 创建饮食记录相关的数据库模型、DTO和API接口,支持用户手动添加和AI视觉识别记录饮食。 - 实现饮食分析服务,提供营养分析和健康建议,优化AI教练服务以集成饮食分析功能。 - 更新用户控制器,添加饮食记录的增删查改接口,增强用户饮食管理体验。 - 提供详细的API使用指南和数据库创建脚本,确保功能的完整性和可用性。
This commit is contained in:
@@ -7,6 +7,7 @@ import { AiConversation } from './models/ai-conversation.model';
|
||||
import { PostureAssessment } from './models/posture-assessment.model';
|
||||
import { UserProfile } from '../users/models/user-profile.model';
|
||||
import { UsersService } from '../users/users.service';
|
||||
import { DietAnalysisService, DietAnalysisResult } from './services/diet-analysis.service';
|
||||
|
||||
const SYSTEM_PROMPT = `作为一名资深的健康管家兼营养分析师(Nutrition Analyst)和健身教练,我拥有丰富的专业知识,包括但不限于:
|
||||
|
||||
@@ -86,6 +87,8 @@ interface CommandResult {
|
||||
cleanText: string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class AiCoachService {
|
||||
private readonly logger = new Logger(AiCoachService.name);
|
||||
@@ -93,7 +96,11 @@ export class AiCoachService {
|
||||
private readonly model: string;
|
||||
private readonly visionModel: string;
|
||||
|
||||
constructor(private readonly configService: ConfigService, private readonly usersService: UsersService) {
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
private readonly usersService: UsersService,
|
||||
private readonly dietAnalysisService: DietAnalysisService,
|
||||
) {
|
||||
const dashScopeApiKey = this.configService.get<string>('DASHSCOPE_API_KEY') || 'sk-e3ff4494c2f1463a8910d5b3d05d3143';
|
||||
const baseURL = this.configService.get<string>('DASHSCOPE_BASE_URL') || 'https://dashscope.aliyuncs.com/compatible-mode/v1';
|
||||
|
||||
@@ -195,6 +202,8 @@ export class AiCoachService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
async streamChat(params: {
|
||||
userId: string;
|
||||
conversationId: string;
|
||||
@@ -226,15 +235,33 @@ export class AiCoachService {
|
||||
messages.unshift({ role: 'system', content: weightContext });
|
||||
}
|
||||
} else if (commandResult.command === 'diet') {
|
||||
// 使用视觉模型分析饮食图片
|
||||
// 使用饮食分析服务处理图片
|
||||
if (params.imageUrls) {
|
||||
const dietAnalysis = await this.analyzeDietImage(params.imageUrls);
|
||||
const dietAnalysisResult = await this.dietAnalysisService.analyzeDietImageEnhanced(params.imageUrls);
|
||||
|
||||
// 如果AI确定应该记录饮食,则自动添加到数据库
|
||||
const createDto = await this.dietAnalysisService.processDietRecord(
|
||||
params.userId,
|
||||
dietAnalysisResult,
|
||||
params.imageUrls[0]
|
||||
);
|
||||
|
||||
if (createDto) {
|
||||
// 构建用户的最近饮食上下文用于营养分析
|
||||
const nutritionContext = await this.dietAnalysisService.buildUserNutritionContext(params.userId);
|
||||
if (nutritionContext) {
|
||||
messages.unshift({ role: 'system', content: nutritionContext });
|
||||
}
|
||||
|
||||
params.systemNotice = `系统提示:已成功为您记录了${createDto.foodName}的饮食信息(${createDto.portionDescription || ''},约${createDto.estimatedCalories || 0}卡路里)。`;
|
||||
}
|
||||
|
||||
messages.push({
|
||||
role: 'user',
|
||||
content: `用户通过拍照记录饮食,图片分析结果如下:\n${dietAnalysis}`
|
||||
content: `用户通过拍照记录饮食,AI分析结果:\n${dietAnalysisResult.analysisText}`
|
||||
});
|
||||
messages.unshift({ role: 'system', content: this.dietAnalysisService.buildEnhancedDietAnalysisPrompt() });
|
||||
}
|
||||
messages.unshift({ role: 'system', content: this.buildDietAnalysisPrompt() });
|
||||
}
|
||||
|
||||
// else if (this.isLikelyNutritionTopic(params.userContent, messages)) {
|
||||
@@ -386,8 +413,10 @@ export class AiCoachService {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 构建饮食分析提示
|
||||
* 构建饮食分析提示(保留原有方法用于兼容)
|
||||
* @returns 饮食分析提示文本
|
||||
*/
|
||||
private buildDietAnalysisPrompt(): string {
|
||||
@@ -413,53 +442,8 @@ export class AiCoachService {
|
||||
请以结构化、清晰的方式输出结果,使用亲切专业的语言风格。`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析饮食图片
|
||||
* @param imageUrl 图片URL
|
||||
* @returns 饮食分析结果
|
||||
*/
|
||||
private async analyzeDietImage(imageUrls: string[]): Promise<string> {
|
||||
try {
|
||||
const prompt = `请分析这张食物图片,识别其中的食物种类、分量,并提供以下信息:
|
||||
|
||||
1. 食物识别:
|
||||
- 主要食材名称
|
||||
- 烹饪方式
|
||||
- 食物类型(主食、蛋白质、蔬菜、水果等)
|
||||
|
||||
2. 分量估算:
|
||||
- 每种食物的大致重量或体积
|
||||
- 使用常见单位描述(如:100g、1碗、2片等)
|
||||
|
||||
3. 营养分析:
|
||||
- 估算总热量(kcal)
|
||||
- 三大营养素含量(蛋白质、碳水化合物、脂肪)
|
||||
- 主要维生素和矿物质
|
||||
|
||||
请以结构化、清晰的方式输出结果。`;
|
||||
|
||||
const completion = await this.client.chat.completions.create({
|
||||
model: this.visionModel,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [
|
||||
{ type: 'text', text: prompt },
|
||||
...imageUrls.map((imageUrl) => ({ type: 'image_url', image_url: { url: imageUrl } as any })),
|
||||
] as any,
|
||||
},
|
||||
],
|
||||
temperature: 1,
|
||||
});
|
||||
|
||||
this.logger.log(`diet image analysis result: ${completion.choices?.[0]?.message?.content}`);
|
||||
|
||||
return completion.choices?.[0]?.message?.content || '无法分析图片中的食物';
|
||||
} catch (error) {
|
||||
this.logger.error(`饮食图片分析失败: ${error instanceof Error ? error.message : String(error)}`);
|
||||
return '饮食图片分析失败';
|
||||
}
|
||||
}
|
||||
|
||||
private deriveTitleIfEmpty(assistantReply: string): string | null {
|
||||
if (!assistantReply) return null;
|
||||
|
||||
Reference in New Issue
Block a user