新增训练计划模块,包括控制器、服务、模型及数据传输对象,更新应用模块以引入新模块,同时在AI教练模块中添加体态评估功能,支持体重识别与更新,优化用户体重历史记录管理。

This commit is contained in:
richarjiang
2025-08-14 12:57:03 +08:00
parent 8c358a21f7
commit 24924e5d81
26 changed files with 935 additions and 5 deletions

View File

@@ -6,6 +6,7 @@ import { CurrentUser } from '../common/decorators/current-user.decorator';
import { AccessTokenPayload } from '../users/services/apple-auth.service';
import { AiCoachService } from './ai-coach.service';
import { AiChatRequestDto, AiChatResponseDto } from './dto/ai-chat.dto';
import { PostureAssessmentRequestDto, PostureAssessmentResponseDto } from './dto/posture-assessment.dto';
@ApiTags('ai-coach')
@Controller('ai-coach')
@@ -31,19 +32,30 @@ export class AiCoachController {
userContent: body.messages?.[body.messages.length - 1]?.content || '',
});
// 智能体重识别:若疑似“记体重”且传入图片,则优先识别并更新体重
let weightInfo: { weightKg?: number } = {};
try {
weightInfo = await this.aiCoachService.maybeExtractAndUpdateWeight(
userId,
body.imageUrl,
body.messages?.[body.messages.length - 1]?.content,
);
} catch { }
if (!stream) {
// 非流式:聚合后一次性返回文本
const readable = await this.aiCoachService.streamChat({
userId,
conversationId,
userContent: body.messages?.[body.messages.length - 1]?.content || '',
systemNotice: weightInfo.weightKg ? `系统提示:已从图片识别体重为${weightInfo.weightKg}kg并已为你更新到个人资料。` : undefined,
});
let text = '';
for await (const chunk of readable) {
text += chunk.toString();
}
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.send({ conversationId, text });
res.send({ conversationId, text, weightKg: weightInfo.weightKg });
return;
}
@@ -56,9 +68,12 @@ export class AiCoachController {
userId,
conversationId,
userContent: body.messages?.[body.messages.length - 1]?.content || '',
systemNotice: weightInfo.weightKg ? `系统提示:已从图片识别体重为${weightInfo.weightKg}kg并已为你更新到个人资料。` : undefined,
});
readable.on('data', (chunk) => {
// 流水首段可提示体重已更新
// 简化处理:服务端不额外注入推送段,直接靠 systemNotice
res.write(chunk);
});
readable.on('end', () => {
@@ -101,6 +116,24 @@ export class AiCoachController {
const ok = await this.aiCoachService.deleteConversation(user.sub, conversationId);
return { success: ok };
}
@Post('posture-assessment')
@ApiOperation({ summary: 'AI体态评估' })
@ApiBody({ type: PostureAssessmentRequestDto })
async postureAssessment(
@Body() body: PostureAssessmentRequestDto,
@CurrentUser() user: AccessTokenPayload,
): Promise<PostureAssessmentResponseDto> {
const res = await this.aiCoachService.assessPosture({
userId: user.sub,
frontImageUrl: body.frontImageUrl,
sideImageUrl: body.sideImageUrl,
backImageUrl: body.backImageUrl,
heightCm: body.heightCm,
weightKg: body.weightKg,
});
return res as any;
}
}