From c02bdc5eca12b2b18a58e65294c47819107c4013 Mon Sep 17 00:00:00 2001 From: chenxing Date: Mon, 12 Oct 2026 20:45:29 +0800 Subject: [PATCH] =?UTF-8?q?fix(trace):=20=E7=A7=91=E5=AE=A4=E8=A7=86?= =?UTF-8?q?=E5=9B=BE=E6=8A=84=E9=80=81=E8=A1=8C=E8=A1=A5=E9=81=AE=E8=94=BD?= =?UTF-8?q?=E2=80=94=E2=80=94ccTraceRow=20=E8=A1=A5=20fillCcScope=20?= =?UTF-8?q?=E5=8F=96=20node.receivers=20=E8=90=BD=20assigneeType/deptKey/d?= =?UTF-8?q?eptName=EF=BC=88NOTIFY=20=E7=94=A8=20receivers=20=E8=80=8C?= =?UTF-8?q?=E9=9D=9E=20assignee=EF=BC=8C=E7=9B=B4=E6=8E=A5=E5=A4=8D?= =?UTF-8?q?=E7=94=A8=20fillAssigneeScope=20=E4=BC=9A=E7=A9=BA=E5=A1=AB?= =?UTF-8?q?=EF=BC=89=EF=BC=8CapplyAssigneeScope=20=E6=8A=BD=E5=85=AC?= =?UTF-8?q?=E5=85=B1=E8=90=BD=E5=80=BC=E6=AE=B5=EF=BC=9BFlowEngineIT=20?= =?UTF-8?q?=E5=A2=9E=20ccTraceRows=5FcarryDeptScope=5FforDeptView=20+=20?= =?UTF-8?q?=E5=8E=9F=20notify=20=E7=94=A8=E4=BE=8B=E8=A1=A5=20USERS=20?= =?UTF-8?q?=E6=96=AD=E8=A8=80=EF=BC=9BFlowTestSupport=20=E5=A2=9E=20notify?= =?UTF-8?q?DeptDef=E3=80=82mvn=20test=20209=20=E5=85=A8=E7=BB=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowcenter/service/FlowQueryService.java | 30 +++++++++++++++++-- .../flowcenter/engine/FlowEngineIT.java | 25 ++++++++++++++++ .../flowcenter/support/FlowTestSupport.java | 15 ++++++++++ 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/yunhai/flowcenter/service/FlowQueryService.java b/src/main/java/com/yunhai/flowcenter/service/FlowQueryService.java index cee4437..2e4051e 100644 --- a/src/main/java/com/yunhai/flowcenter/service/FlowQueryService.java +++ b/src/main/java/com/yunhai/flowcenter/service/FlowQueryService.java @@ -304,7 +304,8 @@ public class FlowQueryService { rows.add(new TimedRow(r.getDecideTime(), 1, r.getId(), routeTraceRow(r))); } for (FlwCc c : ccs) { - rows.add(new TimedRow(c.getSentTime(), 2, c.getId(), ccTraceRow(c))); + rows.add(new TimedRow(c.getSentTime(), 2, c.getId(), + ccTraceRow(c, model, instance, deptNames))); } rows.sort(Comparator.comparing((TimedRow r) -> r.time, Comparator.nullsLast(Comparator.naturalOrder())) @@ -355,8 +356,11 @@ public class FlowQueryService { return row; } - /** 抄送轨迹行(kind=CC):no taskId/opinion,接收人记在 assignee* 字段(前端复用人列) */ - private static Map ccTraceRow(FlwCc c) { + /** 抄送轨迹行(kind=CC):no taskId/opinion,接收人记在 assignee* 字段(前端复用人列)。 + * 2026-10-12 补科室口径:抄送节点用 receivers,走 {@link #fillCcScope} 富化 assigneeType, + * 前端 dept 档据此显科室名 / "抄送对象",不再回落接收人真实姓名。 */ + private Map ccTraceRow(FlwCc c, FlowDefModel model, FlwInstance instance, + Map deptNames) { Map row = new HashMap<>(); row.put("rowKey", "cc" + c.getId()); row.put("ccId", c.getId()); @@ -367,6 +371,7 @@ public class FlowQueryService { row.put("assigneeName", c.getUserName()); row.put("status", null); row.put("time", str(c.getSentTime())); + fillCcScope(row, model, c.getNodeId(), instance.getSchId(), deptNames); return row; } @@ -469,6 +474,25 @@ public class FlowQueryService { Long schId, Map deptNames) { FlowDefModel.Node node = model == null ? null : model.node(nodeId); FlowDefModel.Assignee assignee = node == null ? null : node.assignee; + applyAssigneeScope(row, assignee, schId, deptNames); + } + + /** + * 抄送行(kind=CC)科室口径富化(2026-10-12):NOTIFY 节点的接收人存在 {@code receivers} + * 字段而非 {@code assignee},故不能直接复用 {@link #fillAssigneeScope}——否则取到的 assignee + * 恒为 null,dept 档下抄送行只能回落接收人真实姓名(原先的泄露点)。 + * 节点在定义里找不到(重提后的老节点、已删节点)不填,前端统一显示"抄送对象"。 + */ + private void fillCcScope(Map row, FlowDefModel model, String nodeId, + Long schId, Map deptNames) { + FlowDefModel.Node node = model == null ? null : model.node(nodeId); + FlowDefModel.Assignee receivers = node == null ? null : node.receivers; + applyAssigneeScope(row, receivers, schId, deptNames); + } + + /** 两种口径的公共落值:assigneeType 必填;DEPT_KEY 另填 deptKey + 映射里的科室名 */ + private void applyAssigneeScope(Map row, FlowDefModel.Assignee assignee, + Long schId, Map deptNames) { if (assignee == null || assignee.type == null) { return; } diff --git a/src/test/java/com/yunhai/flowcenter/engine/FlowEngineIT.java b/src/test/java/com/yunhai/flowcenter/engine/FlowEngineIT.java index d73d802..fa1b435 100644 --- a/src/test/java/com/yunhai/flowcenter/engine/FlowEngineIT.java +++ b/src/test/java/com/yunhai/flowcenter/engine/FlowEngineIT.java @@ -507,6 +507,10 @@ class FlowEngineIT extends FlowTestSupport { kinds.add((String) row.get("kind")); } assertEquals(java.util.Arrays.asList("SUBMIT", "CC", "CC", "APPROVE"), kinds); + // 科室口径富化(2026-10-12):抄送行取节点的 receivers(不是 assignee,见 fillCcScope)—— + // 否则 dept 档下抄送行拿不到 assigneeType,前端只能回落接收人真实姓名(原先的泄露点) + assertEquals("USERS", trace.get(1).get("assigneeType")); + assertEquals("USERS", trace.get(2).get("assigneeType")); // 抄送我的:301 视角一行,回填办件信息 com.yunhai.platform.utils.model.BasePageResult ccPage = queryService.pageCc(SCH, 301, null, 1, 10); @@ -515,6 +519,27 @@ class FlowEngineIT extends FlowTestSupport { assertEquals("张三的测试办件", ccPage.getContent().get(0).getBizTitle()); } + /** + * 抄送轨迹行的科室口径富化(2026-10-12):NOTIFY 节点的接收人存在 {@code receivers} 字段, + * 与审批节点的 {@code assignee} 不同名——富化若只认 assignee 就恒为空,dept 档下抄送行 + * 拿不到科室名/类型,前端只能回落接收人真实姓名。本例锁死"科室抄送 → 科室名"这一路。 + */ + @Test + void ccTraceRows_carryDeptScope_forDeptView() { + mapDept("km", 101, "甲"); + FlwDef def = saveAndPublishDef("t16b", "抄送科室", notifyDeptDef("km")); + Long instanceId = apply(def.getFlowGuid(), "biz-16b", 201, "张三").getInstanceId(); + + java.util.List> trace = + queryService.detail(SCH, instanceId, null).getTrace(); + java.util.Map cc = trace.stream() + .filter(r -> "CC".equals(r.get("kind"))) + .findFirst().orElseThrow(() -> new AssertionError("轨迹缺 kind=CC 行")); + assertEquals("DEPT_KEY", cc.get("assigneeType")); + assertEquals(dk("km"), cc.get("deptKey")); + assertEquals("km科", cc.get("deptName")); + } + /** NOTIFY 在并行分支内:瞬时到达 JOIN 不占等待(审批支到齐即放行),落库与轨迹同主线口径 */ @Test void notify_inParallelBranch_reachesJoin_withoutBlocking() { diff --git a/src/test/java/com/yunhai/flowcenter/support/FlowTestSupport.java b/src/test/java/com/yunhai/flowcenter/support/FlowTestSupport.java index f160714..5e764f4 100644 --- a/src/test/java/com/yunhai/flowcenter/support/FlowTestSupport.java +++ b/src/test/java/com/yunhai/flowcenter/support/FlowTestSupport.java @@ -167,6 +167,21 @@ public abstract class FlowTestSupport { + "\"settings\":{\"rejectPolicy\":\"END_REJECTED\"}}"; } + /** 抄送主线流程(科室口径):START → cc(DEPT_KEY deptA) → n1(deptA) → END */ + protected String notifyDeptDef(String deptA) { + return "{" + + "\"code\":\"" + codeOf() + "\",\"name\":\"抄送科室测试流程\",\"start\":\"start\"," + + "\"nodes\":[" + + "{\"id\":\"start\",\"type\":\"START\",\"name\":\"开始\"}," + + "{\"id\":\"cc1\",\"type\":\"NOTIFY\",\"name\":\"抄送科室\",\"receivers\":{\"type\":\"DEPT_KEY\",\"deptKey\":\"" + dk(deptA) + "\"}}," + + "{\"id\":\"n1\",\"type\":\"APPROVAL\",\"name\":\"审核\",\"assignee\":{\"type\":\"DEPT_KEY\",\"deptKey\":\"" + dk(deptA) + "\"}}," + + "{\"id\":\"end\",\"type\":\"END\"}" + + "]," + + "\"edges\":[{\"from\":\"start\",\"to\":\"cc1\"},{\"from\":\"cc1\",\"to\":\"n1\"}," + + "{\"from\":\"n1\",\"to\":\"end\"}]," + + "\"settings\":{\"rejectPolicy\":\"END_REJECTED\"}}"; + } + /** 抄送并行流程:START → split → {n1(deptA), cc(USERS [301])} → join → END(抄送支瞬时到达汇聚) */ protected String notifyParallelDef(String deptA) { return "{"