|
@@ -57,11 +57,13 @@ import com.etotem.cfc.mapper.TaskMapper;
|
|
|
import com.etotem.cfc.service.impl.HealthPlanServiceImpl;
|
|
import com.etotem.cfc.service.impl.HealthPlanServiceImpl;
|
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
|
import org.junit.jupiter.api.Test;
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
|
+import org.mockito.ArgumentCaptor;
|
|
|
import org.mockito.InOrder;
|
|
import org.mockito.InOrder;
|
|
|
|
|
|
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.Field;
|
|
|
import java.util.Arrays;
|
|
import java.util.Arrays;
|
|
|
import java.util.Collections;
|
|
import java.util.Collections;
|
|
|
|
|
+import java.util.List;
|
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
@@ -113,10 +115,18 @@ class HealthPlanDeleteCascadeTest {
|
|
|
Map<String, Object> r = service.deletePlanCascade(5L, 10L);
|
|
Map<String, Object> r = service.deletePlanCascade(5L, 10L);
|
|
|
|
|
|
|
|
assertEquals(5, ((Number) r.get("taskCount")).intValue(), "taskCount = 实例3 + 直接任务2");
|
|
assertEquals(5, ((Number) r.get("taskCount")).intValue(), "taskCount = 实例3 + 直接任务2");
|
|
|
- // 顺序必须:先删实例 → 再删直接任务 → 最后删方案
|
|
|
|
|
|
|
+ // 两次 delete 的语义断言:第一次按 template_id 删每日实例,第二次按 source_type+source_id 删直接任务
|
|
|
|
|
+ ArgumentCaptor<Wrapper> captor = ArgumentCaptor.forClass(Wrapper.class);
|
|
|
|
|
+ verify(taskMapper, times(2)).delete(captor.capture());
|
|
|
|
|
+ List<Wrapper> wrappers = captor.getAllValues();
|
|
|
|
|
+ assertTrue(wrappers.get(0).getTargetSql().contains("template_id"),
|
|
|
|
|
+ "第一次删除必须按 template_id(每日实例),实际: " + wrappers.get(0).getTargetSql());
|
|
|
|
|
+ String secondSql = wrappers.get(1).getTargetSql();
|
|
|
|
|
+ assertTrue(secondSql.contains("source_type") && secondSql.contains("source_id"),
|
|
|
|
|
+ "第二次删除必须按 source_type+source_id(直接任务),实际: " + secondSql);
|
|
|
|
|
+ // 任务删除必须先于方案删除
|
|
|
InOrder inOrder = inOrder(taskMapper, mapper);
|
|
InOrder inOrder = inOrder(taskMapper, mapper);
|
|
|
- inOrder.verify(taskMapper).delete(any(Wrapper.class));
|
|
|
|
|
- inOrder.verify(taskMapper).delete(any(Wrapper.class));
|
|
|
|
|
|
|
+ inOrder.verify(taskMapper, atLeastOnce()).delete(any(Wrapper.class));
|
|
|
inOrder.verify(mapper).deleteById(5L);
|
|
inOrder.verify(mapper).deleteById(5L);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -169,9 +179,11 @@ class HealthPlanDeleteCascadeTest {
|
|
|
|
|
|
|
|
- [ ] **步骤 2:运行测试确认失败**
|
|
- [ ] **步骤 2:运行测试确认失败**
|
|
|
|
|
|
|
|
-运行:`cd cfc-backend && mvn test -Dtest=HealthPlanDeleteCascadeTest -q`
|
|
|
|
|
|
|
+运行:`cd cfc-backend && /tmp/apache-maven-3.8.6/bin/mvn test -DskipTests=false -Dtest=HealthPlanDeleteCascadeTest -q`
|
|
|
预期:**编译失败**(`deletePlanCascade` 方法不存在),错误 `cannot find symbol: method deletePlanCascade`。失败原因正确(接口未声明)。
|
|
预期:**编译失败**(`deletePlanCascade` 方法不存在),错误 `cannot find symbol: method deletePlanCascade`。失败原因正确(接口未声明)。
|
|
|
|
|
|
|
|
|
|
+> ⚠️ 环境注意:项目 pom 默认 `skipTests=true`,`mvn test` 只编译不执行;必须显式 `-DskipTests=false` 才真正运行测试。本机 mvn 不在 PATH,用 `/tmp/apache-maven-3.8.6/bin/mvn`。
|
|
|
|
|
+
|
|
|
- [ ] **步骤 3:在 Service 接口声明方法**
|
|
- [ ] **步骤 3:在 Service 接口声明方法**
|
|
|
|
|
|
|
|
在 `cfc-backend/src/main/java/com/etotem/cfc/service/HealthPlanService.java` 末尾(`generateFromSelfCheck` 声明之后、类闭合括号之前)追加:
|
|
在 `cfc-backend/src/main/java/com/etotem/cfc/service/HealthPlanService.java` 末尾(`generateFromSelfCheck` 声明之后、类闭合括号之前)追加:
|
|
@@ -253,8 +265,8 @@ import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
|
- [ ] **步骤 5:运行测试确认通过**
|
|
- [ ] **步骤 5:运行测试确认通过**
|
|
|
|
|
|
|
|
-运行:`cd cfc-backend && mvn test -Dtest=HealthPlanDeleteCascadeTest -q`
|
|
|
|
|
-预期:**BUILD SUCCESS**,5 个测试全部 PASS(级联删除含顺序验证、无任务方案、IDOR 拒绝、方案不存在、管理端跳过校验)。
|
|
|
|
|
|
|
+运行:`cd cfc-backend && /tmp/apache-maven-3.8.6/bin/mvn test -DskipTests=false -Dtest=HealthPlanDeleteCascadeTest -q`
|
|
|
|
|
+预期:**BUILD SUCCESS**,5 个测试全部 PASS(级联删除含 wrapper 语义顺序验证、无任务方案、IDOR 拒绝、方案不存在、管理端跳过校验)。
|
|
|
|
|
|
|
|
- [ ] **步骤 6:Commit**
|
|
- [ ] **步骤 6:Commit**
|
|
|
|
|
|
|
@@ -323,7 +335,7 @@ git commit -m "feat(health-plan): 方案级联删除 Service 层(含每日实
|
|
|
|
|
|
|
|
- [ ] **步骤 3:编译验证**
|
|
- [ ] **步骤 3:编译验证**
|
|
|
|
|
|
|
|
-运行:`cd cfc-backend && mvn clean compile -q`
|
|
|
|
|
|
|
+运行:`cd cfc-backend && /tmp/apache-maven-3.8.6/bin/mvn clean compile -q`
|
|
|
预期:**BUILD SUCCESS**,无编译错误。
|
|
预期:**BUILD SUCCESS**,无编译错误。
|
|
|
|
|
|
|
|
- [ ] **步骤 4:Commit**
|
|
- [ ] **步骤 4:Commit**
|
|
@@ -597,7 +609,7 @@ git commit -m "feat(admin): 健康方案管理支持删除(确认弹窗 + 级
|
|
|
- [ ] **步骤 2:全量回归验证(后端)**
|
|
- [ ] **步骤 2:全量回归验证(后端)**
|
|
|
|
|
|
|
|
```bash
|
|
```bash
|
|
|
-cd cfc-backend && mvn clean compile -q && mvn test -Dtest=HealthPlanDeleteCascadeTest -q
|
|
|
|
|
|
|
+cd cfc-backend && /tmp/apache-maven-3.8.6/bin/mvn clean compile -q && /tmp/apache-maven-3.8.6/bin/mvn test -DskipTests=false -Dtest=HealthPlanDeleteCascadeTest -q
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
预期:`BUILD SUCCESS`,5 个测试 PASS。
|
|
预期:`BUILD SUCCESS`,5 个测试 PASS。
|
|
@@ -625,7 +637,7 @@ git commit -m "docs(overview): 健康方案级联删除完成(v2.18)"
|
|
|
|
|
|
|
|
| # | 验收项 | 验证方式 | 状态 |
|
|
| # | 验收项 | 验证方式 | 状态 |
|
|
|
|---|---|---|---|
|
|
|---|---|---|---|
|
|
|
-| 1 | `deletePlanCascade` 一次性事务内完成「实例→直接任务→方案」三级删除 | `HealthPlanDeleteCascadeTest` 5 用例(InOrder 验证顺序 + taskCount 汇总) | [ ] |
|
|
|
|
|
|
|
+| 1 | `deletePlanCascade` 一次性事务内完成「实例→直接任务→方案」三级删除 | `HealthPlanDeleteCascadeTest` 5 用例(ArgumentCaptor 断言第 1 次 delete 按 template_id、第 2 次按 source_type+source_id,且任务删除先于方案删除 + taskCount 汇总) | [ ] |
|
|
|
| 2 | `familyId` 不匹配拒绝删除且零数据变动(IDOR) | 测试用例「方案不属于该家庭返回null且不删任何数据」 | [ ] |
|
|
| 2 | `familyId` 不匹配拒绝删除且零数据变动(IDOR) | 测试用例「方案不属于该家庭返回null且不删任何数据」 | [ ] |
|
|
|
| 3 | 用户端 `POST /api/health/plan/delete` 返回 `{planId, taskCount}` | 编译 + 联调(HBuilderX 重打包后小程序点删除) | [ ] |
|
|
| 3 | 用户端 `POST /api/health/plan/delete` 返回 `{planId, taskCount}` | 编译 + 联调(HBuilderX 重打包后小程序点删除) | [ ] |
|
|
|
| 4 | 管理端 `POST /api/admin/health-plan/delete` 五角色权限校验 | 编译 + 管理端联调 | [ ] |
|
|
| 4 | 管理端 `POST /api/admin/health-plan/delete` 五角色权限校验 | 编译 + 管理端联调 | [ ] |
|