强宝健康模块

This commit is contained in:
2026-09-28 16:41:53 +08:00
parent f0056441f6
commit 479364b97f
32 changed files with 2374 additions and 1 deletions
@@ -0,0 +1,126 @@
package com.accounting.util;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.time.LocalDate;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* 减重计划计算测试。
*
* <p>这块是「设置目标 → 每周该减多少」的核心算术,也是最容易出现
* 边界问题的地方:同日、超期、目标比起始还重、除零。所以边界用例
* 写得比正常用例还多。</p>
*/
class WeightPlanCalculatorTest {
@Test
@DisplayName("三个月 65→58kg:总减 7kg 分到 13 周,每周 0.54kg")
void weeklyLossForThreeMonths() {
LocalDate start = LocalDate.of(2026, 1, 1);
LocalDate target = LocalDate.of(2026, 4, 1); // 90 天
int weeks = WeightPlanCalculator.totalWeeks(start, target);
assertEquals(13, weeks);
BigDecimal weekly = WeightPlanCalculator.weeklyLoss(
new BigDecimal("65"), new BigDecimal("58"), weeks);
assertEquals(new BigDecimal("0.54"), weekly);
}
@Test
@DisplayName("总天数除不尽 7 时向上取整 —— 少算一周会让每周目标偏轻")
void roundsUpPartialWeek() {
// 8 天 = 1 周 + 1 天,应该算 2 周而不是 1 周
assertEquals(2, WeightPlanCalculator.totalWeeks(
LocalDate.of(2026, 1, 1), LocalDate.of(2026, 1, 9)));
// 6 天算 1 周
assertEquals(1, WeightPlanCalculator.totalWeeks(
LocalDate.of(2026, 1, 1), LocalDate.of(2026, 1, 7)));
}
@Test
@DisplayName("同日或倒填日期都算 1 周,不除零")
void sameDayFallsBackToOneWeek() {
LocalDate day = LocalDate.of(2026, 1, 1);
assertEquals(1, WeightPlanCalculator.totalWeeks(day, day));
// 目标日期早于开始日期也不该崩
assertEquals(1, WeightPlanCalculator.totalWeeks(day, day.minusDays(5)));
// 1 周减 7kg 是个荒谬的值,但必须是个数、不能是异常
assertEquals(new BigDecimal("7.00"), WeightPlanCalculator.weeklyLoss(
new BigDecimal("65"), new BigDecimal("58"), 1));
}
@Test
@DisplayName("目标比起始还重时每周应减为 0,不能是负数")
void gainingWeightGivesZeroNotNegative() {
// 负数会让进度条宽度算反、也会让「每周应减」显示成 -0.5kg
assertEquals(BigDecimal.ZERO, WeightPlanCalculator.weeklyLoss(
new BigDecimal("58"), new BigDecimal("65"), 13));
assertEquals(BigDecimal.ZERO, WeightPlanCalculator.weeklyLoss(
new BigDecimal("60"), new BigDecimal("60"), 13));
}
@Test
@DisplayName("理想体重曲线:第 0 周是起始体重,最后一周落在目标上")
void idealCurveEndsAtTarget() {
BigDecimal start = new BigDecimal("65");
BigDecimal target = new BigDecimal("58");
BigDecimal weekly = new BigDecimal("0.54");
assertEquals(new BigDecimal("65.0"),
WeightPlanCalculator.idealWeightAt(start, target, weekly, 0));
// 第 13 周:65 - 0.54*13 = 57.98,低于目标 58 → 应该钉在 58
assertEquals(new BigDecimal("58"),
WeightPlanCalculator.idealWeightAt(start, target, weekly, 13));
}
@Test
@DisplayName("理想体重不会跌破目标 —— 否则参照线会穿到目标下方")
void idealWeightNeverGoesBelowTarget() {
BigDecimal target = new BigDecimal("58");
// 刻意给一个过大的每周减重
BigDecimal ideal = WeightPlanCalculator.idealWeightAt(
new BigDecimal("65"), target, new BigDecimal("2.0"), 10);
assertEquals(target, ideal);
}
@Test
@DisplayName("时间进度:未开始 0、进行中在 0~1、超期封顶 1.0")
void timeProgressIsClamped() {
LocalDate start = LocalDate.of(2026, 1, 1);
LocalDate target = LocalDate.of(2026, 1, 11); // 10 天
// 还没开始
assertEquals(0.0, WeightPlanCalculator.timeProgress(start, target,
LocalDate.of(2025, 12, 20)));
// 正中间
assertEquals(0.5, WeightPlanCalculator.timeProgress(start, target,
LocalDate.of(2026, 1, 6)), 0.001);
// 已超期:封顶 1.0,不是 1.5 —— 进度条宽度不能溢出
assertEquals(1.0, WeightPlanCalculator.timeProgress(start, target,
LocalDate.of(2026, 3, 1)));
}
@Test
@DisplayName("每周超过 1kg 会被标记成不健康")
void flagsUnhealthyPace() {
// 65→58 只用 3 周 = 每周 2.33kg
BigDecimal weekly = WeightPlanCalculator.weeklyLoss(
new BigDecimal("65"), new BigDecimal("58"), 3);
assertTrue(weekly.compareTo(WeightPlanCalculator.HEALTHY_WEEKLY_LOSS_KG) > 0);
// 正常节奏不该被标
BigDecimal healthy = WeightPlanCalculator.weeklyLoss(
new BigDecimal("65"), new BigDecimal("58"), 13);
assertFalse(healthy.compareTo(WeightPlanCalculator.HEALTHY_WEEKLY_LOSS_KG) > 0);
}
}