新增普拉提训练系统的数据库结构和数据导入功能
- 创建普拉提分类和动作数据的SQL导入脚本,支持垫上普拉提和器械普拉提的分类管理 - 实现数据库结构迁移脚本,添加新字段以支持普拉提类型和器械名称 - 更新数据库升级总结文档,详细说明数据库结构变更和数据导入步骤 - 创建训练会话相关表,支持每日训练实例功能 - 引入训练会话管理模块,整合训练计划与实际训练会话的关系
This commit is contained in:
@@ -306,6 +306,168 @@ export class AiCoachService {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从用户文本中识别体重信息
|
||||
* 支持多种格式:65kg、65公斤、65.5kg、体重65等
|
||||
*/
|
||||
private extractWeightFromText(text: string | undefined): number | null {
|
||||
if (!text) return null;
|
||||
|
||||
const t = text.toLowerCase();
|
||||
|
||||
// 匹配各种体重格式的正则表达式
|
||||
const patterns = [
|
||||
/(?:体重|称重|秤|重量|weight).*?(\d+(?:\.\d+)?)\s*(?:kg|公斤|千克)/i,
|
||||
/(\d+(?:\.\d+)?)\s*(?:kg|公斤|千克)/i,
|
||||
/(?:体重|称重|秤|重量|weight).*?(\d+(?:\.\d+)?)/i,
|
||||
/我(?:现在|今天)?(?:体重|重量|称重)?(?:是|为|有)?(\d+(?:\.\d+)?)/i,
|
||||
];
|
||||
|
||||
for (const pattern of patterns) {
|
||||
const match = t.match(pattern);
|
||||
if (match) {
|
||||
const weight = parseFloat(match[1]);
|
||||
// 合理的体重范围检查 (20-400kg)
|
||||
if (weight >= 20 && weight <= 400) {
|
||||
return weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户体重历史记录
|
||||
*/
|
||||
async getUserWeightHistory(userId: string, limit: number = 10): Promise<{
|
||||
currentWeight?: number;
|
||||
history: Array<{ weight: number; source: string; createdAt: Date }>;
|
||||
}> {
|
||||
try {
|
||||
// 获取当前体重
|
||||
const profile = await UserProfile.findOne({ where: { userId } });
|
||||
const currentWeight = profile?.weight;
|
||||
|
||||
// 获取体重历史
|
||||
const history = await this.usersService.getWeightHistory(userId, { limit });
|
||||
|
||||
return {
|
||||
currentWeight: currentWeight || undefined,
|
||||
history
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(`获取用户体重历史失败: ${error instanceof Error ? error.message : String(error)}`);
|
||||
return { history: [] };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建体重更新的系统提示信息
|
||||
*/
|
||||
private buildWeightUpdateSystemNotice(
|
||||
newWeight: number,
|
||||
currentWeight?: number,
|
||||
history: Array<{ weight: number; source: string; createdAt: Date }> = []
|
||||
): string {
|
||||
let notice = `系统提示:已为你更新体重为${newWeight}kg。`;
|
||||
|
||||
if (currentWeight && currentWeight !== newWeight) {
|
||||
const diff = newWeight - currentWeight;
|
||||
const diffText = diff > 0 ? `增加了${diff.toFixed(1)}kg` : `减少了${Math.abs(diff).toFixed(1)}kg`;
|
||||
notice += `相比之前的${currentWeight}kg,你${diffText}。`;
|
||||
}
|
||||
|
||||
// 添加历史对比信息
|
||||
if (history.length > 0) {
|
||||
const recentWeights = history.slice(0, 3);
|
||||
if (recentWeights.length > 1) {
|
||||
const trend = this.analyzeWeightTrend(recentWeights, newWeight);
|
||||
notice += trend;
|
||||
}
|
||||
}
|
||||
|
||||
return notice;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析体重趋势
|
||||
*/
|
||||
private analyzeWeightTrend(
|
||||
recentWeights: Array<{ weight: number; createdAt: Date }>,
|
||||
newWeight: number
|
||||
): string {
|
||||
if (recentWeights.length < 2) return '';
|
||||
|
||||
const weights = [newWeight, ...recentWeights.map(w => w.weight)];
|
||||
let trend = '';
|
||||
|
||||
// 计算最近几次的平均变化
|
||||
let totalChange = 0;
|
||||
for (let i = 0; i < weights.length - 1; i++) {
|
||||
totalChange += weights[i] - weights[i + 1];
|
||||
}
|
||||
const avgChange = totalChange / (weights.length - 1);
|
||||
|
||||
if (Math.abs(avgChange) < 0.5) {
|
||||
trend = '你的体重保持相对稳定,继续保持良好的生活习惯!';
|
||||
} else if (avgChange > 0) {
|
||||
trend = `最近体重呈上升趋势,建议加强运动和注意饮食控制。`;
|
||||
} else {
|
||||
trend = `最近体重呈下降趋势,很棒的进步!继续坚持健康的生活方式。`;
|
||||
}
|
||||
|
||||
return trend;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理体重记录和更新(无图片版本)
|
||||
* 从用户文本中识别体重,更新记录,并返回相关信息
|
||||
*/
|
||||
async processWeightFromText(userId: string, userText?: string): Promise<{
|
||||
weightKg?: number;
|
||||
systemNotice?: string;
|
||||
shouldSkipChat?: boolean;
|
||||
}> {
|
||||
if (!userText) return {};
|
||||
|
||||
// 检查是否是体重记录意图
|
||||
if (!this.isLikelyWeightLogIntent(userText)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
// 从文本中提取体重
|
||||
const extractedWeight = this.extractWeightFromText(userText);
|
||||
if (!extractedWeight) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// 获取用户体重历史
|
||||
const { currentWeight, history } = await this.getUserWeightHistory(userId);
|
||||
|
||||
// 更新体重到数据库
|
||||
await this.usersService.addWeightByVision(userId, extractedWeight);
|
||||
|
||||
// 构建系统提示
|
||||
const systemNotice = this.buildWeightUpdateSystemNotice(
|
||||
extractedWeight,
|
||||
currentWeight || undefined,
|
||||
history
|
||||
);
|
||||
|
||||
return {
|
||||
weightKg: extractedWeight,
|
||||
systemNotice,
|
||||
shouldSkipChat: false // 仍然需要与AI聊天,让AI给出激励回复
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error(`处理文本体重记录失败: ${error instanceof Error ? error.message : String(error)}`);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user