- 已办去重: 同一办件一行 + doneCount 累计次数(FlwTaskRepository 查询去重)
- 发起/撤回/重提留痕事件行(状态码 6/7, 无需 DDL); /oapi/flow/progress steps 含撤回/重提步骤
- 管理端新增启用回路: POST /admin/map/enable/{id}、POST /admin/def/enable/{id}; GET /api/flow/def/source 源码查询
- 修复 findEnabledByOrdId 未过滤 status 的隐性缺陷; 新增 DeptLabelSupport 科室标签支撑
- 测试: FlowEngineIT 撤回留痕/已办去重断言、OapiFlowContractIT 撤回步骤断言(94 tests 全过)
- 本地验签修复: 本地 UserBase 无 context-path, yh.open.services 显式覆盖服务目录枚举兜底的 /userbase basePath, 否则出站签名与服务端剥离不对齐 → GetUserInfo 等全部「验签失败」; 生产经 Caddy 挂 /userbase 时删除 services 覆盖
This commit is contained in:
@@ -41,6 +41,8 @@ class FlowEngineIT extends FlowTestSupport {
|
||||
private FlowInstanceService instanceService;
|
||||
@Autowired
|
||||
private FlowTaskService taskService;
|
||||
@Autowired
|
||||
private com.yunhai.flowcenter.service.FlowQueryService queryService;
|
||||
|
||||
@TestConfiguration
|
||||
static class StubConfig {
|
||||
@@ -107,7 +109,7 @@ class FlowEngineIT extends FlowTestSupport {
|
||||
assertEquals(FlwEnums.InstanceStatus.REJECTED.getCode(), instance.getStatus());
|
||||
assertNotNull(instance.getFinishTime());
|
||||
// 同批另一待办已失效
|
||||
List<FlwTask> all = taskRepository.findByInstanceIdOrderByIdAsc(instance.getId());
|
||||
List<FlwTask> all = allTasks(instance.getId());
|
||||
assertEquals(FlwEnums.TaskStatus.REJECTED.getCode(), all.get(0).getStatus());
|
||||
assertEquals(FlwEnums.TaskStatus.INVALIDATED.getCode(), all.get(1).getStatus());
|
||||
}
|
||||
@@ -129,6 +131,15 @@ class FlowEngineIT extends FlowTestSupport {
|
||||
instanceService.withdraw(SCH, outcome.getInstanceId());
|
||||
assertEquals(FlwEnums.InstanceStatus.WITHDRAWN.getCode(),
|
||||
instanceRepository.findById(outcome.getInstanceId()).get().getStatus());
|
||||
// 撤回留痕(2026-09-18):轨迹里必须有"撤回申请"行,否则撤回在时间线上是空白
|
||||
List<FlwTask> afterWithdraw = traceRows(outcome.getInstanceId());
|
||||
FlwTask withdrawRow = afterWithdraw.stream()
|
||||
.filter(t -> FlwEnums.TaskStatus.NODE_WITHDRAW.equals(t.getNodeId())).findFirst().orElse(null);
|
||||
assertNotNull(withdrawRow, "撤回必须留痕");
|
||||
assertEquals(FlwEnums.TaskStatus.WITHDRAWN.getCode(), withdrawRow.getStatus());
|
||||
assertEquals(201, withdrawRow.getAssigneeId()); // 操作人=提交人
|
||||
// 留痕行不算"已被办理":重提前的撤回守卫口径不能被自己写的事件行挡住
|
||||
assertFalse(FlwEnums.TaskStatus.handledCodes().contains(withdrawRow.getStatus()));
|
||||
|
||||
// 重提重启后再办理,随后撤回必须被拒(已有任务被办理)
|
||||
FlowInstanceService.ApplyOutcome reapplied = apply(def.getFlowGuid(), "biz-4", 201, "张三");
|
||||
@@ -137,6 +148,60 @@ class FlowEngineIT extends FlowTestSupport {
|
||||
assertThrows(BizException.class, () -> instanceService.withdraw(SCH, reapplied.getInstanceId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 轨迹时间流 + 已办去重(2026-09-18):
|
||||
* ① 发起/重新提交留痕成事件行,detail.trace 按发生时间升序且带 kind/round/科室富化;
|
||||
* ② 同一办件被本人办理两次(驳回 + 重提后再同意)时,已办列表只出一行、doneCount=2、
|
||||
* 结果取最后一次(同意),并保留"共办理 N 次"的提示依据。
|
||||
*/
|
||||
@Test
|
||||
void trace_timeline_kindsRounds_andDoneDedup() {
|
||||
mapDept("ybb", 101, "甲");
|
||||
FlwDef def = saveAndPublishDef("t20", "轨迹时间流", linearDef("ybb", "审核"));
|
||||
FlowInstanceService.ApplyOutcome first = apply(def.getFlowGuid(), "biz-20", 201, "张三");
|
||||
Long instanceId = first.getInstanceId();
|
||||
|
||||
// 一轮:办理人驳回;提交人重提;二轮:同一个人同意 → 同一办件上本人两条已办
|
||||
taskService.handle(SCH, taskRepository.findByInstanceIdAndStatus(instanceId, 0).get(0).getId(),
|
||||
false, "材料不全", 101);
|
||||
apply(def.getFlowGuid(), "biz-20", 201, "张三");
|
||||
taskService.handle(SCH, taskRepository.findByInstanceIdAndStatus(instanceId, 0).get(0).getId(),
|
||||
true, "补齐了", 101);
|
||||
assertEquals(FlwEnums.InstanceStatus.COMPLETED.getCode(),
|
||||
instanceRepository.findById(instanceId).get().getStatus());
|
||||
|
||||
// ① 轨迹时间流:发起 → 驳回 → 重新提交 → 同意
|
||||
java.util.List<java.util.Map<String, Object>> trace =
|
||||
queryService.detail(SCH, instanceId, null).getTrace();
|
||||
java.util.List<String> kinds = new java.util.ArrayList<>();
|
||||
for (java.util.Map<String, Object> row : trace) {
|
||||
kinds.add((String) row.get("kind"));
|
||||
}
|
||||
assertEquals(java.util.Arrays.asList("SUBMIT", "REJECT", "RESUBMIT", "APPROVE"), kinds);
|
||||
// 时间为升序(前端直接按后端顺序渲染)
|
||||
for (int i = 1; i < trace.size(); i++) {
|
||||
assertTrue(String.valueOf(trace.get(i).get("time"))
|
||||
.compareTo(String.valueOf(trace.get(i - 1).get("time"))) >= 0,
|
||||
"轨迹必须按发生时间升序:" + trace);
|
||||
}
|
||||
// 轮次:驳回属于第 1 轮,重新提交与二轮同意属于第 2 轮
|
||||
assertEquals(1, trace.get(1).get("round"));
|
||||
assertEquals(2, trace.get(2).get("round"));
|
||||
assertEquals(2, trace.get(3).get("round"));
|
||||
// 科室口径富化(DEPT_KEY 节点):deptName 取映射里的科室名,前端"按科室"据此换掉人名
|
||||
assertEquals("DEPT_KEY", trace.get(3).get("assigneeType"));
|
||||
assertEquals("ybb", trace.get(3).get("deptKey"));
|
||||
assertEquals("ybb科", trace.get(3).get("deptName"));
|
||||
|
||||
// ② 已办去重:同一办件一行,结果是最后一次(同意),次数=2
|
||||
com.yunhai.platform.utils.model.BasePageResult<com.yunhai.flowcenter.model.FlowModels.TaskItem>
|
||||
done = queryService.pageDone(SCH, 101, 1, 10);
|
||||
assertEquals(1, done.getContent().size(), "一个办件只能有一条已办记录");
|
||||
assertEquals(2, done.getContent().get(0).getDoneCount());
|
||||
assertEquals(FlwEnums.TaskStatus.APPROVED.getCode(), done.getContent().get(0).getStatus());
|
||||
assertEquals("审核", done.getContent().get(0).getNodeName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void reapply_inFlight_duplicated_noNewInstance() {
|
||||
mapDept("ybb", 101, "甲");
|
||||
@@ -171,7 +236,7 @@ class FlowEngineIT extends FlowTestSupport {
|
||||
assertEquals(FlwEnums.InstanceStatus.APPROVING.getCode(), after.getStatus());
|
||||
assertTrue(after.getVersion() > versionBefore, "重启后 instanceVersion 必须单调递增");
|
||||
// 轨迹连续:历史拒绝任务保留 + 新一轮任务待办
|
||||
List<FlwTask> all = taskRepository.findByInstanceIdOrderByIdAsc(first.getInstanceId());
|
||||
List<FlwTask> all = allTasks(first.getInstanceId());
|
||||
assertEquals(FlwEnums.TaskStatus.REJECTED.getCode(), all.get(0).getStatus());
|
||||
assertEquals(1, taskRepository.findByInstanceIdAndStatus(first.getInstanceId(), 0).size());
|
||||
assertNull(after.getFinishTime());
|
||||
@@ -240,7 +305,7 @@ class FlowEngineIT extends FlowTestSupport {
|
||||
assertEquals(FlwEnums.InstanceStatus.COMPLETED.getCode(),
|
||||
instanceRepository.findById(outcome.getInstanceId()).get().getStatus());
|
||||
// 只应有一轮 END 完成(无重复推进导致的重复任务创建:总任务数=2)
|
||||
assertEquals(2, taskRepository.findByInstanceIdOrderByIdAsc(outcome.getInstanceId()).size());
|
||||
assertEquals(2, allTasks(outcome.getInstanceId()).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -306,7 +371,7 @@ class FlowEngineIT extends FlowTestSupport {
|
||||
taskService.handle(SCH, pending.get(0).getId(), true, "我先批", 101);
|
||||
FlwInstance instance = instanceRepository.findById(instanceId).get();
|
||||
assertEquals(FlwEnums.InstanceStatus.COMPLETED.getCode(), instance.getStatus());
|
||||
List<FlwTask> all = taskRepository.findByInstanceIdOrderByIdAsc(instanceId);
|
||||
List<FlwTask> all = allTasks(instanceId);
|
||||
assertEquals(FlwEnums.TaskStatus.APPROVED.getCode(), all.get(0).getStatus());
|
||||
assertEquals(FlwEnums.TaskStatus.INVALIDATED.getCode(), all.get(1).getStatus());
|
||||
}
|
||||
@@ -375,7 +440,18 @@ class FlowEngineIT extends FlowTestSupport {
|
||||
@Autowired
|
||||
private com.yunhai.flowcenter.repository.FlwBranchRepository branchRepository;
|
||||
|
||||
/**
|
||||
* 审批任务行(剔除 6/7 留痕事件行,2026-09-18 起"发起/重新提交/撤回"也落 flw_task 行):
|
||||
* 只关心审批动作的断言(按序号取值、按数量计数)必须走这个口,否则会取到事件行。
|
||||
*/
|
||||
private List<FlwTask> allTasks(Long instanceId) {
|
||||
return taskRepository.findByInstanceIdOrderByIdAsc(instanceId).stream()
|
||||
.filter(t -> !FlwEnums.TaskStatus.eventCodes().contains(t.getStatus()))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** 全量轨迹行(含 6/7 留痕事件行):留痕类断言用 */
|
||||
private List<FlwTask> traceRows(Long instanceId) {
|
||||
return taskRepository.findByInstanceIdOrderByIdAsc(instanceId);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user