新增普拉提训练系统的数据库结构和数据导入功能

- 创建普拉提分类和动作数据的SQL导入脚本,支持垫上普拉提和器械普拉提的分类管理
- 实现数据库结构迁移脚本,添加新字段以支持普拉提类型和器械名称
- 更新数据库升级总结文档,详细说明数据库结构变更和数据导入步骤
- 创建训练会话相关表,支持每日训练实例功能
- 引入训练会话管理模块,整合训练计划与实际训练会话的关系
This commit is contained in:
richarjiang
2025-08-15 15:34:11 +08:00
parent bea71af5d3
commit 0edcfdcae9
28 changed files with 2528 additions and 164 deletions

View File

@@ -24,31 +24,56 @@ export class AiCoachController {
): Promise<StreamableFile | AiChatResponseDto | void> {
const userId = user.sub;
const stream = body.stream !== false; // 默认流式
const userContent = body.messages?.[body.messages.length - 1]?.content || '';
// 创建或沿用会话ID并保存用户消息
const { conversationId } = await this.aiCoachService.createOrAppendMessages({
userId,
conversationId: body.conversationId,
userContent: body.messages?.[body.messages.length - 1]?.content || '',
userContent,
});
// 智能体重识别:若疑似“记体重”且传入图片,则优先识别并更新体重
let weightInfo: { weightKg?: number } = {};
let weightInfo: { weightKg?: number; systemNotice?: string } = {};
// 体重识别逻辑优化:
// 1. 如果有图片URL使用原有的图片识别逻辑
// 2. 如果没有图片URL但文本中包含体重信息使用新的文本识别逻辑
try {
weightInfo = await this.aiCoachService.maybeExtractAndUpdateWeight(
userId,
body.imageUrl,
body.messages?.[body.messages.length - 1]?.content,
);
} catch { }
if (body.imageUrl) {
// 原有逻辑:从图片识别体重
const imageWeightInfo = await this.aiCoachService.maybeExtractAndUpdateWeight(
userId,
body.imageUrl,
userContent,
);
if (imageWeightInfo.weightKg) {
weightInfo = {
weightKg: imageWeightInfo.weightKg,
systemNotice: `系统提示:已从图片识别体重为${imageWeightInfo.weightKg}kg并已为你更新到个人资料。`
};
}
} else {
// 新逻辑:从文本识别体重,并获取历史对比信息
const textWeightInfo = await this.aiCoachService.processWeightFromText(userId, userContent);
if (textWeightInfo.weightKg && textWeightInfo.systemNotice) {
weightInfo = {
weightKg: textWeightInfo.weightKg,
systemNotice: textWeightInfo.systemNotice
};
}
}
} catch (error) {
// 体重识别失败不影响正常对话
console.error('体重识别失败:', error);
}
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,
userContent,
systemNotice: weightInfo.systemNotice,
});
let text = '';
for await (const chunk of readable) {
@@ -67,13 +92,11 @@ export class AiCoachController {
const readable = await this.aiCoachService.streamChat({
userId,
conversationId,
userContent: body.messages?.[body.messages.length - 1]?.content || '',
systemNotice: weightInfo.weightKg ? `系统提示:已从图片识别体重为${weightInfo.weightKg}kg并已为你更新到个人资料。` : undefined,
userContent,
systemNotice: weightInfo.systemNotice,
});
readable.on('data', (chunk) => {
// 流水首段可提示体重已更新
// 简化处理:服务端不额外注入推送段,直接靠 systemNotice
res.write(chunk);
});
readable.on('end', () => {