From 81d9a075e839c49832909ec81111bf1b213dfa11 Mon Sep 17 00:00:00 2001 From: chenxing Date: Sat, 17 Oct 2026 23:00:59 +0800 Subject: [PATCH] =?UTF-8?q?[ysch-flow]=20S2.2=20=E7=BB=93=E6=9E=9C/?= =?UTF-8?q?=E9=80=80=E5=9B=9E=E6=B6=88=E6=81=AF=E6=94=B9=E8=B5=B0=20bizUrl?= =?UTF-8?q?=20=E4=BC=98=E5=85=88=E7=AD=96=E7=95=A5=EF=BC=88F8=20=E7=BC=BA?= =?UTF-8?q?=E5=8F=A3=E2=91=A0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit notifyResult/notifyReturned 此前把 jumpPath 硬编码 /instance/{id}、jumpApp 硬编码 null, 消息归属因此落在流程中心,四类消息(待办/抄送/结果/退回)口径不一致。现与 task/cc 同策略并抽出 jumpPath/jumpApp 两个共用判定:bizUrl 非空→直达业务方详情页且归属申报 给 apply 调用方 applyAppCode;bizUrl 空(存量办件)→仍回落中心页且归属留空。 单测 8 例(新增 4 例:结果带/不带 bizUrl、退回、抄送)。 --- .../flowcenter/notify/UserMsgNotifier.java | 37 +++++++--- .../notify/UserMsgNotifierBizUrlTest.java | 71 +++++++++++++++++++ 2 files changed, 99 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/yunhai/flowcenter/notify/UserMsgNotifier.java b/src/main/java/com/yunhai/flowcenter/notify/UserMsgNotifier.java index f40a494..c12f9d4 100644 --- a/src/main/java/com/yunhai/flowcenter/notify/UserMsgNotifier.java +++ b/src/main/java/com/yunhai/flowcenter/notify/UserMsgNotifier.java @@ -20,6 +20,9 @@ import java.util.Collections; * - 任务被办理:todo -1(办理人) * - 完成/拒绝/撤回:msgType=1、bizType=flow.result、content 必填(结果+意见)、msgNo 带 version * (重提重启多轮终态互不幂等吞掉) + * - 退回:bizType=flow.returned、msgNo 带 version,通知被退回节点的新待办人 + * 结果/退回与待办/抄送同跳转策略(2026-10-17 F8 缺口①):bizUrl 优先 + jumpApp=applyAppCode, + * 空回落中心页且归属留空——四类消息的页面归属口径从此一致。 * - 催办:bizType=flow.urge、msgNo=urge:{taskId}:{第N次} * 全部在 AFTER_COMMIT 监听器里发送,失败不阻断(UserMsgClient 内部容错)。 */ @@ -77,9 +80,8 @@ public class UserMsgNotifier { // 按_by-key 换得的 app_code,接收方按它查基址拼绝对地址);applyAppCode 空(老数据/ // UserBase 不可达)或未传 bizUrl 回落流程中心页(/task/{taskId}、/instance/{id}), // 页面属自己(jumpApp 留空按发送方回落)。bizType 是消息分类,不是路由键,不再冒充。 - boolean useBizUrl = instance.getBizUrl() != null && !instance.getBizUrl().trim().isEmpty(); - String jumpPath = useBizUrl ? instance.getBizUrl() : "/task/" + task.getId(); - String jumpApp = useBizUrl ? instance.getApplyAppCode() : null; + String jumpPath = jumpPath(instance, "/task/" + task.getId()); + String jumpApp = jumpApp(instance); userMsgClient.sendUserMsg(instance.getSchId().intValue(), Collections.singletonList(task.getAssigneeId()), 2, "flow.task", String.valueOf(instance.getId()), title, content, @@ -124,7 +126,8 @@ public class UserMsgNotifier { userMsgClient.sendUserMsg(instance.getSchId().intValue(), targets, 1, "flow.returned", String.valueOf(instance.getId()), "办件被退回:" + safe(instance.getBizTitle()), content.toString(), - "/instance/" + instance.getId(), null, + // F8 缺口①:退回消息与待办/抄送同策略——业务方详情页优先、归属申报给 apply 调用方 + jumpPath(instance, "/instance/" + instance.getId()), jumpApp(instance), "returned:" + instance.getId() + ":" + instance.getVersion(), 30); } @@ -147,14 +150,11 @@ public class UserMsgNotifier { ? "" : "(" + nodeName + ")"); // 跳转与任务到达同策略:bizUrl(业务方详情页,页面归属=apply 调用方 applyAppCode)优先, // 空回落办件详情(属自己) - boolean useBizUrl = instance.getBizUrl() != null && !instance.getBizUrl().trim().isEmpty(); - String jumpPath = useBizUrl ? instance.getBizUrl() : "/instance/" + instance.getId(); - String jumpApp = useBizUrl ? instance.getApplyAppCode() : null; userMsgClient.sendUserMsg(instance.getSchId().intValue(), Collections.singletonList((Integer) ccUserId), 1, "flow.cc", String.valueOf(instance.getId()), "您有新的抄送:" + bizTitle, content, - jumpPath, jumpApp, "cc:" + ccId, 30); + jumpPath(instance, "/instance/" + instance.getId()), jumpApp(instance), "cc:" + ccId, 30); } private void notifyResult(FlowEvent event) { @@ -188,7 +188,9 @@ public class UserMsgNotifier { Collections.singletonList(instance.getSubmitterId()), 1, "flow.result", String.valueOf(instance.getId()), "办件状态更新:" + bizTitle, content.toString(), - "/instance/" + instance.getId(), null, + // F8 缺口①:结果消息与待办/抄送同策略——业务方详情页优先(子应用详情页 iframe + // 显终态进度),jumpApp 申报给 apply 调用方;bizUrl 空仍回落中心办件详情 + jumpPath(instance, "/instance/" + instance.getId()), jumpApp(instance), "result:" + instance.getId() + ":" + instance.getVersion(), 30); } @@ -209,6 +211,23 @@ public class UserMsgNotifier { "/task/" + task.getId(), null, "urge:" + task.getId() + ":" + count, 7); } + /** + * 四类消息(待办/抄送/结果/退回)共用的跳转路径策略:办件 bizUrl 优先,空/空白回落中心页。 + * bizUrl 自 2026-09-26 起是裸相对路径(不含域名/ctx/#),接收方按 jumpApp 查基址拼绝对地址。 + */ + private static String jumpPath(FlwInstance instance, String centerFallback) { + return hasBizUrl(instance) ? instance.getBizUrl() : centerFallback; + } + + /** 页面归属:走 bizUrl 时申报给 apply 调用方(applyAppCode),回落中心页时留空按发送方归属 */ + private static String jumpApp(FlwInstance instance) { + return hasBizUrl(instance) ? instance.getApplyAppCode() : null; + } + + private static boolean hasBizUrl(FlwInstance instance) { + return instance.getBizUrl() != null && !instance.getBizUrl().trim().isEmpty(); + } + private FlwInstance instanceOf(Long instanceId) { return instanceId == null ? null : instanceRepository.findById(instanceId).orElse(null); } diff --git a/src/test/java/com/yunhai/flowcenter/notify/UserMsgNotifierBizUrlTest.java b/src/test/java/com/yunhai/flowcenter/notify/UserMsgNotifierBizUrlTest.java index 7f4918f..51e65ab 100644 --- a/src/test/java/com/yunhai/flowcenter/notify/UserMsgNotifierBizUrlTest.java +++ b/src/test/java/com/yunhai/flowcenter/notify/UserMsgNotifierBizUrlTest.java @@ -151,4 +151,75 @@ class UserMsgNotifierBizUrlTest { assertEquals("/task/78", userMsg.lastJumpPath); assertNull(userMsg.lastJumpApp); } + + /** + * F8 缺口①:结果消息(completed/rejected/withdrawn)不再硬编码归流程中心, + * 与待办/抄送同策略——bizUrl 优先 + jumpApp=applyAppCode。 + */ + @Test + void resultMessage_followsBizUrlStrategy() { + FlwInstance instance = instance(11L, "/leave/detail?id=88", "notice"); + when(instanceRepository.findById(11L)).thenReturn(Optional.of(instance)); + + notifier.onFlowEvent(FlowEvent.of(FlowEvent.Type.COMPLETED, 11L, null, + java.util.Collections.singletonMap("result", "通过"))); + + assertEquals("/leave/detail?id=88", userMsg.lastJumpPath); + assertEquals("notice", userMsg.lastJumpApp); + } + + /** 存量兼容:老办件无 bizUrl 时结果消息仍回落中心办件详情、归属留空 */ + @Test + void resultMessageWithoutBizUrl_fallsBackToCenterInstancePage() { + FlwInstance instance = instance(12L, " ", "notice"); + when(instanceRepository.findById(12L)).thenReturn(Optional.of(instance)); + + notifier.onFlowEvent(FlowEvent.of(FlowEvent.Type.REJECTED, 12L)); + + assertEquals("/instance/12", userMsg.lastJumpPath); + assertNull(userMsg.lastJumpApp); + } + + /** F8 缺口①:退回消息同策略(四类消息归属口径从此一致) */ + @Test + void returnedMessage_followsBizUrlStrategy() { + FlwInstance instance = instance(13L, "/hall/detail?id=9", "svc-hall"); + when(instanceRepository.findById(13L)).thenReturn(Optional.of(instance)); + java.util.Map data = new java.util.HashMap<>(); + data.put("targetUserIds", java.util.Arrays.asList(201, 202)); + data.put("fromNodeName", "科室初审"); + data.put("opinion", "请补材料"); + + notifier.onFlowEvent(FlowEvent.of(FlowEvent.Type.RETURNED, 13L, null, data)); + + assertEquals("/hall/detail?id=9", userMsg.lastJumpPath); + assertEquals("svc-hall", userMsg.lastJumpApp); + } + + /** 抄送消息(原已同策略)纳入同一断言口径,防后续回归 */ + @Test + void ccMessage_followsBizUrlStrategy() { + FlwInstance instance = instance(14L, "/notice/detail?serid=3", "notice"); + when(instanceRepository.findById(14L)).thenReturn(Optional.of(instance)); + java.util.Map data = new java.util.HashMap<>(); + data.put("ccUserId", 301); + data.put("ccId", 55); + data.put("nodeName", "抄送分管领导"); + + notifier.onFlowEvent(FlowEvent.of(FlowEvent.Type.CC_SENT, 14L, null, data)); + + assertEquals("/notice/detail?serid=3", userMsg.lastJumpPath); + assertEquals("notice", userMsg.lastJumpApp); + } + + private FlwInstance instance(long id, String bizUrl, String applyAppCode) { + FlwInstance instance = new FlwInstance(); + instance.setId(id); + instance.setSchId(1L); + instance.setBizTitle("测试办件"); + instance.setSubmitterId(101); + instance.setBizUrl(bizUrl); + instance.setApplyAppCode(applyAppCode); + return instance; + } }