WeightGoalService.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import { Repository } from 'typeorm';
  2. import { v4 as uuidv4 } from 'uuid';
  3. import { AppDataSource } from '../config/database';
  4. import { WeightGoal, GoalStatus } from '../entities/WeightGoal';
  5. import { WeightRecord } from '../entities/WeightRecord';
  6. import { CreateWeightGoalDto, UpdateWeightGoalDto } from '../dto/weight-goal.dto';
  7. export class WeightGoalService {
  8. private weightGoalRepository: Repository<WeightGoal>;
  9. private weightRecordRepository: Repository<WeightRecord>;
  10. constructor() {
  11. this.weightGoalRepository = AppDataSource.getRepository(WeightGoal);
  12. this.weightRecordRepository = AppDataSource.getRepository(WeightRecord);
  13. }
  14. /**
  15. * 创建体重目标
  16. */
  17. async createGoal(userId: string, dto: CreateWeightGoalDto): Promise<WeightGoal> {
  18. // 获取用户最新体重记录
  19. const latestRecord = await this.weightRecordRepository.findOne({
  20. where: { userId },
  21. order: { recordDate: 'DESC', createdAt: 'DESC' }
  22. });
  23. if (!latestRecord) {
  24. throw new Error('请先添加体重记录');
  25. }
  26. // 将之前的活跃目标标记为已放弃
  27. await this.weightGoalRepository.update(
  28. { userId, status: GoalStatus.ACTIVE },
  29. { status: GoalStatus.ABANDONED }
  30. );
  31. // 计算每周目标
  32. const startDate = new Date(latestRecord.recordDate);
  33. const targetDate = new Date(dto.targetDate);
  34. const daysDiff = Math.ceil((targetDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));
  35. const weeksDiff = daysDiff / 7;
  36. const weightDiff = Math.abs(Number(latestRecord.weight) - dto.targetWeight);
  37. const weeklyTarget = weeksDiff > 0 ? Number((weightDiff / weeksDiff).toFixed(2)) : 0;
  38. // 创建新目标
  39. const goal = this.weightGoalRepository.create({
  40. id: uuidv4(),
  41. userId,
  42. targetWeight: dto.targetWeight,
  43. targetBodyFat: dto.targetBodyFat || null,
  44. targetDate: dto.targetDate,
  45. startWeight: Number(latestRecord.weight),
  46. startBodyFat: Number(latestRecord.bodyFat),
  47. startDate: latestRecord.recordDate,
  48. weeklyTarget,
  49. status: GoalStatus.ACTIVE
  50. });
  51. await this.weightGoalRepository.save(goal);
  52. return goal;
  53. }
  54. /**
  55. * 更新体重目标
  56. */
  57. async updateGoal(userId: string, dto: UpdateWeightGoalDto): Promise<WeightGoal> {
  58. const goal = await this.weightGoalRepository.findOne({
  59. where: { userId, status: GoalStatus.ACTIVE }
  60. });
  61. if (!goal) {
  62. throw new Error('未找到活跃的目标');
  63. }
  64. // 更新目标
  65. if (dto.targetWeight !== undefined) goal.targetWeight = dto.targetWeight;
  66. if (dto.targetBodyFat !== undefined) goal.targetBodyFat = dto.targetBodyFat;
  67. if (dto.targetDate !== undefined) goal.targetDate = dto.targetDate;
  68. // 重新计算每周目标
  69. if (dto.targetWeight !== undefined || dto.targetDate !== undefined) {
  70. const startDate = new Date(goal.startDate);
  71. const targetDate = new Date(goal.targetDate);
  72. const daysDiff = Math.ceil((targetDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));
  73. const weeksDiff = daysDiff / 7;
  74. const weightDiff = Math.abs(Number(goal.startWeight) - goal.targetWeight);
  75. goal.weeklyTarget = weeksDiff > 0 ? Number((weightDiff / weeksDiff).toFixed(2)) : 0;
  76. }
  77. await this.weightGoalRepository.save(goal);
  78. return goal;
  79. }
  80. /**
  81. * 获取当前活跃目标
  82. */
  83. async getCurrentGoal(userId: string): Promise<WeightGoal | null> {
  84. const goal = await this.weightGoalRepository.findOne({
  85. where: { userId, status: GoalStatus.ACTIVE },
  86. order: { createdAt: 'DESC' }
  87. });
  88. return goal;
  89. }
  90. /**
  91. * 格式化目标输出
  92. */
  93. formatGoal(goal: WeightGoal): any {
  94. return {
  95. id: goal.id,
  96. targetWeight: Number(goal.targetWeight),
  97. targetBodyFat: goal.targetBodyFat ? Number(goal.targetBodyFat) : null,
  98. targetDate: goal.targetDate,
  99. startWeight: Number(goal.startWeight),
  100. startBodyFat: goal.startBodyFat ? Number(goal.startBodyFat) : null,
  101. startDate: goal.startDate,
  102. weeklyTarget: goal.weeklyTarget ? Number(goal.weeklyTarget) : null,
  103. status: goal.status,
  104. createdAt: goal.createdAt.getTime(),
  105. updatedAt: goal.updatedAt?.getTime() || goal.createdAt.getTime()
  106. };
  107. }
  108. }