新增训练计划模块,包括控制器、服务、模型及数据传输对象,更新应用模块以引入新模块,同时在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

@@ -30,6 +30,7 @@ import { RevenueCatWebhookDto, RevenueCatEventType } from './dto/revenue-cat-web
import { RestorePurchaseDto, RestorePurchaseResponseDto, RestoredPurchaseInfo, ActiveEntitlement, NonSubscriptionTransaction } from './dto/restore-purchase.dto';
import { PurchaseRestoreLog, RestoreStatus, RestoreSource } from './models/purchase-restore-log.model';
import { BlockedTransaction, BlockReason } from './models/blocked-transaction.model';
import { UserWeightHistory, WeightUpdateSource } from './models/user-weight-history.model';
const DEFAULT_FREE_USAGE_COUNT = 10;
@@ -52,6 +53,8 @@ export class UsersService {
private blockedTransactionModel: typeof BlockedTransaction,
@InjectModel(UserProfile)
private userProfileModel: typeof UserProfile,
@InjectModel(UserWeightHistory)
private userWeightHistoryModel: typeof UserWeightHistory,
@InjectConnection()
private sequelize: Sequelize,
) { }
@@ -157,7 +160,14 @@ export class UsersService {
if (dailyStepsGoal !== undefined) profile.dailyStepsGoal = dailyStepsGoal as any;
if (dailyCaloriesGoal !== undefined) profile.dailyCaloriesGoal = dailyCaloriesGoal as any;
if (pilatesPurposes !== undefined) profile.pilatesPurposes = pilatesPurposes as any;
if (weight !== undefined) profile.weight = weight as any;
if (weight !== undefined) {
profile.weight = weight as any;
try {
await this.userWeightHistoryModel.create({ userId, weight, source: WeightUpdateSource.Manual });
} catch (e) {
this.logger.error(`记录体重历史失败: ${e instanceof Error ? e.message : String(e)}`);
}
}
if (height !== undefined) profile.height = height as any;
await profile.save();
}
@@ -172,6 +182,35 @@ export class UsersService {
};
}
async addWeightByVision(userId: string, weight: number): Promise<void> {
const t = await this.sequelize.transaction();
try {
const [profile] = await this.userProfileModel.findOrCreate({ where: { userId }, defaults: { userId }, transaction: t });
profile.weight = weight as any;
await profile.save({ transaction: t });
await this.userWeightHistoryModel.create({ userId, weight, source: WeightUpdateSource.Vision }, { transaction: t });
await t.commit();
} catch (e) {
await t.rollback();
this.logger.error(`addWeightByVision error: ${e instanceof Error ? e.message : String(e)}`);
}
}
async getWeightHistory(userId: string, params: { start?: Date; end?: Date; limit?: number } = {}) {
const where: any = { userId };
if (params.start || params.end) {
where.createdAt = {} as any;
if (params.start) where.createdAt.$gte = params.start as any;
if (params.end) where.createdAt.$lte = params.end as any;
}
const limit = params.limit && params.limit > 0 ? Math.min(1000, params.limit) : 200;
const rows = await this.userWeightHistoryModel.findAll({
where,
order: [['created_at', 'DESC']],
limit,
});
return rows.map(r => ({ weight: r.weight, source: r.source, createdAt: r.createdAt }));
}
/**
* Apple 登录