| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- import { Repository } from 'typeorm';
- import { AppDataSource } from '../config/database';
- import { WeightRecord } from '../entities/WeightRecord';
- import { WeightGoal, GoalStatus } from '../entities/WeightGoal';
- export class StatsService {
- private weightRecordRepository: Repository<WeightRecord>;
- private weightGoalRepository: Repository<WeightGoal>;
- constructor() {
- this.weightRecordRepository = AppDataSource.getRepository(WeightRecord);
- this.weightGoalRepository = AppDataSource.getRepository(WeightGoal);
- }
- /**
- * 获取用户统计数据
- */
- async getStats(userId: string, from?: string, to?: string): Promise<any> {
- // 获取记录
- const queryBuilder = this.weightRecordRepository
- .createQueryBuilder('wr')
- .where('wr.userId = :userId', { userId })
- .andWhere('wr.deletedAt IS NULL');
- if (from) {
- queryBuilder.andWhere('wr.recordDate >= :from', { from });
- }
- if (to) {
- queryBuilder.andWhere('wr.recordDate <= :to', { to });
- }
- const records = await queryBuilder
- .orderBy('wr.recordDate', 'ASC')
- .getMany();
- if (records.length === 0) {
- return {
- currentWeight: null,
- weightChange: null,
- daysTracked: 0,
- avgWeeklyChange: null,
- bodyFatChange: null,
- goalProgress: null,
- goalETA: null
- };
- }
- // 基础统计
- const firstRecord = records[0];
- const latestRecord = records[records.length - 1];
- const currentWeight = Number(latestRecord.weight);
- const initialWeight = Number(firstRecord.weight);
- const weightChange = Number((currentWeight - initialWeight).toFixed(2));
- const bodyFatChange = Number((Number(latestRecord.bodyFat) - Number(firstRecord.bodyFat)).toFixed(2));
- // 天数统计
- const firstDate = new Date(firstRecord.recordDate);
- const latestDate = new Date(latestRecord.recordDate);
- const daysTracked = Math.ceil((latestDate.getTime() - firstDate.getTime()) / (1000 * 60 * 60 * 24)) + 1;
- // 平均每周变化
- const weeksTracked = daysTracked / 7;
- const avgWeeklyChange = weeksTracked > 0 ? Number((weightChange / weeksTracked).toFixed(2)) : 0;
- // 获取活跃目标
- const goal = await this.weightGoalRepository.findOne({
- where: { userId, status: GoalStatus.ACTIVE }
- });
- let goalProgress: number | null = null;
- let goalETA: string | null = null;
- if (goal) {
- const totalWeightDiff = Math.abs(Number(goal.targetWeight) - Number(goal.startWeight));
- const currentWeightDiff = Math.abs(Number(goal.targetWeight) - currentWeight);
- goalProgress = totalWeightDiff > 0
- ? Number((((totalWeightDiff - currentWeightDiff) / totalWeightDiff) * 100).toFixed(1))
- : 100;
- // 计算ETA
- if (avgWeeklyChange !== 0 && currentWeightDiff > 0) {
- const weeksNeeded = currentWeightDiff / Math.abs(avgWeeklyChange);
- const etaDate = new Date();
- etaDate.setDate(etaDate.getDate() + Math.ceil(weeksNeeded * 7));
- goalETA = etaDate.toISOString().split('T')[0];
- }
- }
- return {
- currentWeight,
- weightChange,
- daysTracked,
- avgWeeklyChange,
- bodyFatChange,
- goalProgress,
- goalETA
- };
- }
- }
|