update 工作流消息推送增加前端路由跳转

This commit is contained in:
疯狂的狮子Li
2026-03-27 15:20:39 +08:00
parent 60b6862c9e
commit 5f53681b95
7 changed files with 152 additions and 10 deletions

View File

@@ -53,6 +53,14 @@ public interface UserService {
*/ */
String selectEmailById(Long userId); String selectEmailById(Long userId);
/**
* 通过用户ID查询用户
*
* @param userId 用户id
* @return 用户列表
*/
UserDTO selectById(Long userId);
/** /**
* 通过用户ID查询用户列表 * 通过用户ID查询用户列表
* *

View File

@@ -641,6 +641,24 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
return ObjectUtils.notNullGetter(sysUser, SysUser::getEmail); return ObjectUtils.notNullGetter(sysUser, SysUser::getEmail);
} }
/**
* 通过用户ID查询用户
*
* @param userId 用户id
* @return 用户列表
*/
@Override
public UserDTO selectById(Long userId) {
SysUserVo vo = baseMapper.selectVoOne(new LambdaQueryWrapper<SysUser>()
.select(SysUser::getUserId, SysUser::getDeptId, SysUser::getUserName,
SysUser::getNickName, SysUser::getUserType, SysUser::getEmail,
SysUser::getPhoneNumber, SysUser::getGender, SysUser::getStatus,
SysUser::getCreateTime)
.eq(SysUser::getStatus, SystemConstants.NORMAL)
.eq(SysUser::getUserId, userId));
return BeanUtil.copyProperties(vo, UserDTO.class);
}
/** /**
* 通过用户ID查询用户列表 * 通过用户ID查询用户列表
* *

View File

@@ -58,6 +58,26 @@ public interface FlowConstant {
*/ */
String MESSAGE_NOTICE = "messageNotice"; String MESSAGE_NOTICE = "messageNotice";
/**
* 我的发起页面路径。
*/
String PATH_MY_DOCUMENT = "/task/myDocument";
/**
* 我的待办页面路径。
*/
String PATH_TASK_WAITING = "/task/taskWaiting";
/**
* 我的已办页面路径。
*/
String PATH_TASK_FINISH = "/task/taskFinish";
/**
* 我的抄送页面路径。
*/
String PATH_TASK_COPY = "/task/taskCopyList";
/** /**
* 任务状态字典类型编码。 * 任务状态字典类型编码。
*/ */

View File

@@ -8,6 +8,7 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.dto.UserDTO;
import org.dromara.common.core.enums.BusinessStatusEnum; import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.service.UserService; import org.dromara.common.core.service.UserService;
import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StreamUtils;
@@ -21,6 +22,7 @@ import org.dromara.warm.flow.core.listener.GlobalListener;
import org.dromara.warm.flow.core.listener.ListenerVariable; import org.dromara.warm.flow.core.listener.ListenerVariable;
import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.ConditionalOnEnable;
import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.workflow.common.enums.MessageTypeEnum;
import org.dromara.workflow.common.enums.TaskStatusEnum; import org.dromara.workflow.common.enums.TaskStatusEnum;
import org.dromara.workflow.domain.bo.FlowCopyBo; import org.dromara.workflow.domain.bo.FlowCopyBo;
import org.dromara.workflow.domain.vo.NodeExtVo; import org.dromara.workflow.domain.vo.NodeExtVo;
@@ -31,10 +33,7 @@ import org.dromara.workflow.service.IFlwNodeExtService;
import org.dromara.workflow.service.IFlwTaskService; import org.dromara.workflow.service.IFlwTaskService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.HashMap; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* 工作流全局监听器,处理任务流转中的扩展变量、消息和事件发布。 * 工作流全局监听器,处理任务流转中的扩展变量、消息和事件发布。
@@ -198,12 +197,14 @@ public class WorkflowGlobalListener implements GlobalListener {
String status = determineFlowStatus(instance); String status = determineFlowStatus(instance);
if (StringUtils.isNotBlank(status)) { if (StringUtils.isNotBlank(status)) {
flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, status, params, false); flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, status, params, false);
notifyInitiatorIfNeeded(definition, instance, status, variable);
} }
if (!BusinessStatusEnum.initialState(instance.getFlowStatus())) { if (!BusinessStatusEnum.initialState(instance.getFlowStatus())) {
if (task != null && CollUtil.isNotEmpty(nextTasks) && nextTasks.size() == 1 if (task != null && CollUtil.isNotEmpty(nextTasks) && nextTasks.size() == 1
&& flwCommonService.applyNodeCode(definition.getId()).equals(nextTasks.get(0).getNodeCode())) { && flwCommonService.applyNodeCode(definition.getId()).equals(nextTasks.get(0).getNodeCode())) {
// 如果为画线指定驳回 线条指定为驳回 驳回得节点为申请人节点 则修改流程状态为退回 // 如果为画线指定驳回 线条指定为驳回 驳回得节点为申请人节点 则修改流程状态为退回
flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, BusinessStatusEnum.BACK.getStatus(), params, false); flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, BusinessStatusEnum.BACK.getStatus(), params, false);
notifyInitiatorIfNeeded(definition, instance, BusinessStatusEnum.BACK.getStatus(), variable);
// 修改流程实例状态 // 修改流程实例状态
instance.setFlowStatus(BusinessStatusEnum.BACK.getStatus()); instance.setFlowStatus(BusinessStatusEnum.BACK.getStatus());
FlowEngine.insService().updateById(instance); FlowEngine.insService().updateById(instance);
@@ -237,7 +238,10 @@ public class WorkflowGlobalListener implements GlobalListener {
List<String> messageType = MapUtil.get(variable, FlowConstant.MESSAGE_TYPE, new TypeReference<>() { List<String> messageType = MapUtil.get(variable, FlowConstant.MESSAGE_TYPE, new TypeReference<>() {
}); });
String notice = MapUtil.getStr(variable, FlowConstant.MESSAGE_NOTICE); String notice = MapUtil.getStr(variable, FlowConstant.MESSAGE_NOTICE);
flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice); // 退回到申请人时只保留“已退回”结果消息,避免再追加一条“新的待办”形成重复提醒。
if (shouldSendTaskMessage(flowParams, definition, nextTasks)) {
flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice);
}
} }
FlowEngine.insService().removeVariables(instance.getId(), FlowEngine.insService().removeVariables(instance.getId(),
FlowConstant.FLOW_COPY_LIST, FlowConstant.FLOW_COPY_LIST,
@@ -247,6 +251,46 @@ public class WorkflowGlobalListener implements GlobalListener {
); );
} }
private boolean shouldSendTaskMessage(FlowParams flowParams, Definition definition, List<Task> nextTasks) {
if (flowParams == null || !TaskStatusEnum.BACK.getStatus().equals(flowParams.getHisStatus())) {
return true;
}
if (CollUtil.isEmpty(nextTasks) || nextTasks.size() != 1) {
return true;
}
// 只有“退回到申请人”场景需要拦截待办提醒,其余退回/流转仍然保留待办消息。
String applyNodeCode = flwCommonService.applyNodeCode(definition.getId());
return !StringUtils.equals(applyNodeCode, nextTasks.get(0).getNodeCode());
}
private void notifyInitiatorIfNeeded(Definition definition, Instance instance, String status, Map<String, Object> variable) {
if (!StringUtils.equalsAny(status, BusinessStatusEnum.FINISH.getStatus(), BusinessStatusEnum.BACK.getStatus())) {
return;
}
if (StringUtils.isBlank(instance.getCreateBy())) {
return;
}
BusinessStatusEnum statusEnum = BusinessStatusEnum.getByStatus(status);
if (statusEnum == null) {
return;
}
Long createBy = Convert.toLong(instance.getCreateBy(), null);
if (createBy == null) {
return;
}
UserDTO initiator = userService.selectById(createBy);
if (initiator == null || initiator.getUserId() == null) {
return;
}
// 已完成、已退回这类结果消息只发给发起人,不再混入处理人待办消息。
List<String> messageType = Collections.singletonList(MessageTypeEnum.SYSTEM_MESSAGE.getCode());
if (MapUtil.isNotEmpty(variable) && variable.containsKey(FlowConstant.MESSAGE_TYPE)) {
messageType = MapUtil.get(variable, FlowConstant.MESSAGE_TYPE, new TypeReference<>() {
});
}
flwCommonService.sendResultMessage(definition.getFlowName(), statusEnum, messageType, Collections.singletonList(initiator));
}
/** /**
* 根据流程实例确定最终状态 * 根据流程实例确定最终状态
* *

View File

@@ -1,5 +1,6 @@
package org.dromara.workflow.service; package org.dromara.workflow.service;
import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.core.domain.dto.UserDTO;
import java.util.List; import java.util.List;
@@ -31,6 +32,27 @@ public interface IFlwCommonService {
*/ */
void sendMessage(List<String> messageType, String message, String subject, List<UserDTO> userList); void sendMessage(List<String> messageType, String message, String subject, List<UserDTO> userList);
/**
* 发送带跳转路径的消息。
*
* @param messageType 消息类型
* @param message 消息内容
* @param subject 邮件标题
* @param userList 接收用户
* @param path 前端跳转路径
*/
void sendMessage(List<String> messageType, String message, String subject, List<UserDTO> userList, String path);
/**
* 发送工作流结果消息给指定用户。
*
* @param flowName 流程名称
* @param status 流程状态
* @param messageType 消息类型
* @param userList 接收用户
*/
void sendResultMessage(String flowName, BusinessStatusEnum status, List<String> messageType, List<UserDTO> userList);
/** /**
* 申请人节点编码 * 申请人节点编码
* *

View File

@@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.dto.PushPayloadDTO; import org.dromara.common.core.domain.dto.PushPayloadDTO;
import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.core.domain.dto.UserDTO;
import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.enums.PushSourceEnum; import org.dromara.common.core.enums.PushSourceEnum;
import org.dromara.common.core.enums.PushTypeEnum; import org.dromara.common.core.enums.PushTypeEnum;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
@@ -28,6 +29,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import static org.dromara.workflow.common.constant.FlowConstant.PATH_MY_DOCUMENT;
import static org.dromara.workflow.common.constant.FlowConstant.PATH_TASK_WAITING;
/** /**
* 工作流工具 * 工作流工具
@@ -68,7 +71,8 @@ public class FlwCommonServiceImpl implements IFlwCommonService {
if (CollUtil.isEmpty(userList)) { if (CollUtil.isEmpty(userList)) {
return; return;
} }
sendMessage(messageType, message, DEFAULT_SUBJECT, userList); // 发给当前处理人的工作流消息统一进入“我的待办”。
sendMessage(messageType, message, DEFAULT_SUBJECT, userList, PATH_TASK_WAITING);
} }
/** /**
@@ -81,11 +85,27 @@ public class FlwCommonServiceImpl implements IFlwCommonService {
*/ */
@Override @Override
public void sendMessage(List<String> messageType, String message, String subject, List<UserDTO> userList) { public void sendMessage(List<String> messageType, String message, String subject, List<UserDTO> userList) {
sendMessage(messageType, message, subject, userList, null);
}
@Override
public void sendResultMessage(String flowName, BusinessStatusEnum status, List<String> messageType, List<UserDTO> userList) {
if (status == null || CollUtil.isEmpty(messageType) || CollUtil.isEmpty(userList)) {
return;
}
// 审批结果类消息面向发起人查看,统一跳转到“我发起的”。
String message = "您发起的【" + flowName + "】单据审批已" + status.getDesc() + "";
sendMessage(messageType, message, DEFAULT_SUBJECT, userList, PATH_MY_DOCUMENT);
}
@Override
public void sendMessage(List<String> messageType, String message, String subject, List<UserDTO> userList, String path) {
if (CollUtil.isEmpty(messageType) || CollUtil.isEmpty(userList)) { if (CollUtil.isEmpty(messageType) || CollUtil.isEmpty(userList)) {
return; return;
} }
List<Long> userIds = new ArrayList<>(StreamUtils.toSet(userList, UserDTO::getUserId)); List<Long> userIds = new ArrayList<>(StreamUtils.toSet(userList, UserDTO::getUserId));
Set<String> emails = StreamUtils.toSet(userList, UserDTO::getEmail); Set<String> emails = StreamUtils.toSet(userList, UserDTO::getEmail);
emails.removeIf(StringUtils::isBlank);
for (String code : messageType) { for (String code : messageType) {
MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(code); MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(code);
@@ -95,11 +115,11 @@ public class FlwCommonServiceImpl implements IFlwCommonService {
try { try {
switch (messageTypeEnum) { switch (messageTypeEnum) {
case SYSTEM_MESSAGE -> { case SYSTEM_MESSAGE -> {
// 站内消息直接携带前端路由,消息盒子点击后可按路径分流。
messageService.publishMessage(userIds, PushPayloadDTO.of( messageService.publishMessage(userIds, PushPayloadDTO.of(
PushTypeEnum.MESSAGE, PushTypeEnum.MESSAGE,
PushSourceEnum.WORKFLOW, PushSourceEnum.WORKFLOW,
message, message, null, path
null
)); ));
} }
case EMAIL_MESSAGE -> MailUtils.sendText(emails, subject, message); case EMAIL_MESSAGE -> MailUtils.sendText(emails, subject, message);

View File

@@ -363,6 +363,14 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
.setAssociated(taskId)); .setAssociated(taskId));
// 批量保存抄送人员 // 批量保存抄送人员
FlowEngine.userService().saveBatch(userList); FlowEngine.userService().saveBatch(userList);
// 抄送消息进入“我的抄送”,不和待办列表混用。
flwCommonService.sendMessage(
List.of(org.dromara.workflow.common.enums.MessageTypeEnum.SYSTEM_MESSAGE.getCode()),
"您收到一条新的流程抄送,请及时查看。",
"单据抄送提醒",
userService.selectListByIds(StreamUtils.toList(flowCopyList, FlowCopyBo::getUserId)),
PATH_TASK_COPY
);
} }
/** /**
@@ -824,11 +832,13 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
userIdList.addAll(StreamUtils.toList(bo.getUserIds(), Convert::toLong)); userIdList.addAll(StreamUtils.toList(bo.getUserIds(), Convert::toLong));
} }
if (CollUtil.isNotEmpty(userIdList)) { if (CollUtil.isNotEmpty(userIdList)) {
// 转办、委托、加减签等运行时操作,消息接收人统一从“我的待办”进入处理。
flwCommonService.sendMessage( flwCommonService.sendMessage(
bo.getMessageType(), bo.getMessageType(),
StringUtils.isNotBlank(bo.getMessage()) ? bo.getMessage() : "单据「" + op.getDesc() + "」通知", StringUtils.isNotBlank(bo.getMessage()) ? bo.getMessage() : "单据「" + op.getDesc() + "」通知",
"单据「" + op.getDesc() + "」提醒", "单据「" + op.getDesc() + "」提醒",
userService.selectListByIds(userIdList) userService.selectListByIds(userIdList),
PATH_TASK_WAITING
); );
} }
} }
@@ -907,7 +917,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
} }
List<String> messageType = bo.getMessageType(); List<String> messageType = bo.getMessageType();
String message = bo.getMessage(); String message = bo.getMessage();
flwCommonService.sendMessage(messageType, message, "单据审批提醒", userList); flwCommonService.sendMessage(messageType, message, "单据审批提醒", userList, PATH_TASK_WAITING);
return true; return true;
} }
} }