28 changed files with 345 additions and 762 deletions
@ -1,206 +0,0 @@ |
|||
package org.dromara.workflow.utils; |
|||
|
|||
import cn.hutool.core.collection.CollUtil; |
|||
import cn.hutool.core.util.ObjectUtil; |
|||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|||
import lombok.AccessLevel; |
|||
import lombok.NoArgsConstructor; |
|||
import org.dromara.common.core.utils.SpringUtils; |
|||
import org.dromara.common.core.utils.StreamUtils; |
|||
import org.dromara.common.core.utils.StringUtils; |
|||
import org.dromara.common.mail.utils.MailUtils; |
|||
import org.dromara.common.sse.dto.SseMessageDto; |
|||
import org.dromara.common.sse.utils.SseMessageUtils; |
|||
import org.dromara.system.api.domain.vo.RemoteUserVo; |
|||
import org.dromara.warm.flow.core.constant.ExceptionCons; |
|||
import org.dromara.warm.flow.core.dto.FlowParams; |
|||
import org.dromara.warm.flow.core.entity.Node; |
|||
import org.dromara.warm.flow.core.entity.Task; |
|||
import org.dromara.warm.flow.core.entity.User; |
|||
import org.dromara.warm.flow.core.enums.NodeType; |
|||
import org.dromara.warm.flow.core.enums.SkipType; |
|||
import org.dromara.warm.flow.core.service.NodeService; |
|||
import org.dromara.warm.flow.core.service.TaskService; |
|||
import org.dromara.warm.flow.core.service.UserService; |
|||
import org.dromara.warm.flow.core.utils.AssertUtil; |
|||
import org.dromara.warm.flow.orm.entity.FlowNode; |
|||
import org.dromara.warm.flow.orm.entity.FlowTask; |
|||
import org.dromara.warm.flow.orm.entity.FlowUser; |
|||
import org.dromara.warm.flow.orm.mapper.FlowNodeMapper; |
|||
import org.dromara.warm.flow.orm.mapper.FlowTaskMapper; |
|||
import org.dromara.workflow.common.enums.MessageTypeEnum; |
|||
import org.dromara.workflow.service.IFlwTaskAssigneeService; |
|||
import org.dromara.workflow.service.IFlwTaskService; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.HashSet; |
|||
import java.util.List; |
|||
import java.util.Set; |
|||
import java.util.stream.Collectors; |
|||
|
|||
|
|||
/** |
|||
* 工作流工具 |
|||
* |
|||
* @author may |
|||
*/ |
|||
@NoArgsConstructor(access = AccessLevel.PRIVATE) |
|||
public class WorkflowUtils { |
|||
|
|||
private static final IFlwTaskAssigneeService TASK_ASSIGNEE_SERVICE = SpringUtils.getBean(IFlwTaskAssigneeService.class); |
|||
private static final IFlwTaskService FLW_TASK_SERVICE = SpringUtils.getBean(IFlwTaskService.class); |
|||
private static final FlowNodeMapper FLOW_NODE_MAPPER = SpringUtils.getBean(FlowNodeMapper.class); |
|||
private static final FlowTaskMapper FLOW_TASK_MAPPER = SpringUtils.getBean(FlowTaskMapper.class); |
|||
private static final UserService USER_SERVICE = SpringUtils.getBean(UserService.class); |
|||
private static final TaskService TASK_SERVICE = SpringUtils.getBean(TaskService.class); |
|||
private static final NodeService NODE_SERVICE = SpringUtils.getBean(NodeService.class); |
|||
|
|||
/** |
|||
* 获取工作流用户service |
|||
*/ |
|||
public static UserService getFlowUserService() { |
|||
return USER_SERVICE; |
|||
} |
|||
|
|||
/** |
|||
* 构建工作流用户 |
|||
* |
|||
* @param userList 办理用户 |
|||
* @param taskId 任务ID |
|||
* @return 用户 |
|||
*/ |
|||
public static Set<User> buildUser(List<User> userList, Long taskId) { |
|||
if (CollUtil.isEmpty(userList)) { |
|||
return Set.of(); |
|||
} |
|||
Set<User> list = new HashSet<>(); |
|||
Set<String> processedBySet = new HashSet<>(); |
|||
for (User user : userList) { |
|||
// 根据 processedBy 前缀判断处理人类型,分别获取用户列表
|
|||
List<RemoteUserVo> users = TASK_ASSIGNEE_SERVICE.fetchUsersByStorageId(user.getProcessedBy()); |
|||
// 转换为 FlowUser 并添加到结果集合
|
|||
if (CollUtil.isNotEmpty(users)) { |
|||
users.forEach(dto -> { |
|||
String processedBy = String.valueOf(dto.getUserId()); |
|||
if (!processedBySet.contains(processedBy)) { |
|||
FlowUser flowUser = new FlowUser(); |
|||
flowUser.setType(user.getType()); |
|||
flowUser.setProcessedBy(processedBy); |
|||
flowUser.setAssociated(taskId); |
|||
list.add(flowUser); |
|||
processedBySet.add(processedBy); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
return list; |
|||
} |
|||
|
|||
/** |
|||
* 发送消息 |
|||
* |
|||
* @param flowName 流程定义名称 |
|||
* @param messageType 消息类型 |
|||
* @param message 消息内容,为空则发送默认配置的消息内容 |
|||
*/ |
|||
public static void sendMessage(String flowName, Long instId, List<String> messageType, String message) { |
|||
List<RemoteUserVo> userList = new ArrayList<>(); |
|||
List<FlowTask> list = FLW_TASK_SERVICE.selectByInstId(instId); |
|||
if (StringUtils.isBlank(message)) { |
|||
message = "有新的【" + flowName + "】单据已经提交至您,请您及时处理。"; |
|||
} |
|||
for (Task task : list) { |
|||
List<RemoteUserVo> users = FLW_TASK_SERVICE.currentTaskAllUser(task.getId()); |
|||
if (CollUtil.isNotEmpty(users)) { |
|||
userList.addAll(users); |
|||
} |
|||
} |
|||
if (CollUtil.isNotEmpty(userList)) { |
|||
for (String code : messageType) { |
|||
MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(code); |
|||
if (ObjectUtil.isNotEmpty(messageTypeEnum)) { |
|||
switch (messageTypeEnum) { |
|||
case SYSTEM_MESSAGE: |
|||
SseMessageDto dto = new SseMessageDto(); |
|||
dto.setUserIds(StreamUtils.toList(userList, RemoteUserVo::getUserId).stream().distinct().collect(Collectors.toList())); |
|||
dto.setMessage(message); |
|||
SseMessageUtils.publishMessage(dto); |
|||
break; |
|||
case EMAIL_MESSAGE: |
|||
MailUtils.sendText(StreamUtils.join(userList, RemoteUserVo::getEmail), "单据审批提醒", message); |
|||
break; |
|||
case SMS_MESSAGE: |
|||
//todo 短信发送
|
|||
break; |
|||
default: |
|||
throw new IllegalStateException("Unexpected value: " + messageTypeEnum); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 驳回 |
|||
* |
|||
* @param message 审批意见 |
|||
* @param instanceId 流程实例id |
|||
* @param targetNodeCode 目标节点 |
|||
* @param flowStatus 流程状态 |
|||
* @param flowHisStatus 节点操作状态 |
|||
*/ |
|||
public static void backTask(String message, Long instanceId, String targetNodeCode, String flowStatus, String flowHisStatus) { |
|||
List<FlowTask> list = FLW_TASK_SERVICE.selectByInstId(instanceId); |
|||
if (CollUtil.isNotEmpty(list)) { |
|||
List<FlowTask> tasks = StreamUtils.filter(list, e -> e.getNodeCode().equals(targetNodeCode)); |
|||
if (list.size() == tasks.size()) { |
|||
return; |
|||
} |
|||
} |
|||
for (FlowTask task : list) { |
|||
List<RemoteUserVo> userList = FLW_TASK_SERVICE.currentTaskAllUser(task.getId()); |
|||
FlowParams flowParams = FlowParams.build(); |
|||
flowParams.nodeCode(targetNodeCode); |
|||
flowParams.message(message); |
|||
flowParams.skipType(SkipType.PASS.getKey()); |
|||
flowParams.flowStatus(flowStatus).hisStatus(flowHisStatus); |
|||
flowParams.ignore(true); |
|||
//解决会签没权限问题
|
|||
if (CollUtil.isNotEmpty(userList)) { |
|||
flowParams.handler(userList.get(0).getUserId().toString()); |
|||
} |
|||
TASK_SERVICE.skip(task.getId(), flowParams); |
|||
} |
|||
//解决会签多人审批问题
|
|||
backTask(message, instanceId, targetNodeCode, flowStatus, flowHisStatus); |
|||
} |
|||
|
|||
/** |
|||
* 申请人节点编码 |
|||
* |
|||
* @param definitionId 流程定义id |
|||
* @return 申请人节点编码 |
|||
*/ |
|||
public static String applyNodeCode(Long definitionId) { |
|||
//获取已发布的流程节点
|
|||
List<FlowNode> flowNodes = FLOW_NODE_MAPPER.selectList(new LambdaQueryWrapper<FlowNode>().eq(FlowNode::getDefinitionId, definitionId)); |
|||
AssertUtil.isTrue(CollUtil.isEmpty(flowNodes), ExceptionCons.NOT_PUBLISH_NODE); |
|||
Node startNode = flowNodes.stream().filter(t -> NodeType.isStart(t.getNodeType())).findFirst().orElse(null); |
|||
AssertUtil.isNull(startNode, ExceptionCons.LOST_START_NODE); |
|||
Node nextNode = NODE_SERVICE.getNextNode(definitionId, startNode.getNodeCode(), null, SkipType.PASS.getKey()); |
|||
return nextNode.getNodeCode(); |
|||
} |
|||
|
|||
/** |
|||
* 删除运行中的任务 |
|||
* |
|||
* @param taskIds 任务id |
|||
*/ |
|||
public static void deleteRunTask(List<Long> taskIds) { |
|||
if (CollUtil.isEmpty(taskIds)) { |
|||
return; |
|||
} |
|||
USER_SERVICE.deleteByTaskIds(taskIds); |
|||
FLOW_TASK_MAPPER.deleteByIds(taskIds); |
|||
} |
|||
} |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 5.4 KiB |
@ -1,216 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
|||
<meta name="renderer" content="webkit"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> |
|||
<link rel="icon" href="./ico/favicon-FqvijpIH.ico"> |
|||
<title>Warm-Flow设计器</title> |
|||
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]--> |
|||
<style> |
|||
html, |
|||
body, |
|||
#app { |
|||
height: 100%; |
|||
margin: 0px; |
|||
padding: 0px; |
|||
} |
|||
|
|||
.chromeframe { |
|||
margin: 0.2em 0; |
|||
background: #ccc; |
|||
color: #000; |
|||
padding: 0.2em 0; |
|||
} |
|||
|
|||
#loader-wrapper { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
z-index: 999999; |
|||
} |
|||
|
|||
#loader { |
|||
display: block; |
|||
position: relative; |
|||
left: 50%; |
|||
top: 50%; |
|||
width: 150px; |
|||
height: 150px; |
|||
margin: -75px 0 0 -75px; |
|||
border-radius: 50%; |
|||
border: 3px solid transparent; |
|||
border-top-color: #FFF; |
|||
-webkit-animation: spin 2s linear infinite; |
|||
-ms-animation: spin 2s linear infinite; |
|||
-moz-animation: spin 2s linear infinite; |
|||
-o-animation: spin 2s linear infinite; |
|||
animation: spin 2s linear infinite; |
|||
z-index: 1001; |
|||
} |
|||
|
|||
#loader:before { |
|||
content: ""; |
|||
position: absolute; |
|||
top: 5px; |
|||
left: 5px; |
|||
right: 5px; |
|||
bottom: 5px; |
|||
border-radius: 50%; |
|||
border: 3px solid transparent; |
|||
border-top-color: #FFF; |
|||
-webkit-animation: spin 3s linear infinite; |
|||
-moz-animation: spin 3s linear infinite; |
|||
-o-animation: spin 3s linear infinite; |
|||
-ms-animation: spin 3s linear infinite; |
|||
animation: spin 3s linear infinite; |
|||
} |
|||
|
|||
#loader:after { |
|||
content: ""; |
|||
position: absolute; |
|||
top: 15px; |
|||
left: 15px; |
|||
right: 15px; |
|||
bottom: 15px; |
|||
border-radius: 50%; |
|||
border: 3px solid transparent; |
|||
border-top-color: #FFF; |
|||
-moz-animation: spin 1.5s linear infinite; |
|||
-o-animation: spin 1.5s linear infinite; |
|||
-ms-animation: spin 1.5s linear infinite; |
|||
-webkit-animation: spin 1.5s linear infinite; |
|||
animation: spin 1.5s linear infinite; |
|||
} |
|||
|
|||
|
|||
@-webkit-keyframes spin { |
|||
0% { |
|||
-webkit-transform: rotate(0deg); |
|||
-ms-transform: rotate(0deg); |
|||
transform: rotate(0deg); |
|||
} |
|||
|
|||
100% { |
|||
-webkit-transform: rotate(360deg); |
|||
-ms-transform: rotate(360deg); |
|||
transform: rotate(360deg); |
|||
} |
|||
} |
|||
|
|||
@keyframes spin { |
|||
0% { |
|||
-webkit-transform: rotate(0deg); |
|||
-ms-transform: rotate(0deg); |
|||
transform: rotate(0deg); |
|||
} |
|||
|
|||
100% { |
|||
-webkit-transform: rotate(360deg); |
|||
-ms-transform: rotate(360deg); |
|||
transform: rotate(360deg); |
|||
} |
|||
} |
|||
|
|||
|
|||
#loader-wrapper .loader-section { |
|||
position: fixed; |
|||
top: 0; |
|||
width: 51%; |
|||
height: 100%; |
|||
background: #7171C6; |
|||
z-index: 1000; |
|||
-webkit-transform: translateX(0); |
|||
-ms-transform: translateX(0); |
|||
transform: translateX(0); |
|||
} |
|||
|
|||
#loader-wrapper .loader-section.section-left { |
|||
left: 0; |
|||
} |
|||
|
|||
#loader-wrapper .loader-section.section-right { |
|||
right: 0; |
|||
} |
|||
|
|||
|
|||
.loaded #loader-wrapper .loader-section.section-left { |
|||
-webkit-transform: translateX(-100%); |
|||
-ms-transform: translateX(-100%); |
|||
transform: translateX(-100%); |
|||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); |
|||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); |
|||
} |
|||
|
|||
.loaded #loader-wrapper .loader-section.section-right { |
|||
-webkit-transform: translateX(100%); |
|||
-ms-transform: translateX(100%); |
|||
transform: translateX(100%); |
|||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); |
|||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); |
|||
} |
|||
|
|||
.loaded #loader { |
|||
opacity: 0; |
|||
-webkit-transition: all 0.3s ease-out; |
|||
transition: all 0.3s ease-out; |
|||
} |
|||
|
|||
.loaded #loader-wrapper { |
|||
visibility: hidden; |
|||
-webkit-transform: translateY(-100%); |
|||
-ms-transform: translateY(-100%); |
|||
transform: translateY(-100%); |
|||
-webkit-transition: all 0.3s 1s ease-out; |
|||
transition: all 0.3s 1s ease-out; |
|||
} |
|||
|
|||
.no-js #loader-wrapper { |
|||
display: none; |
|||
} |
|||
|
|||
.no-js h1 { |
|||
color: #222222; |
|||
} |
|||
|
|||
#loader-wrapper .load_title { |
|||
font-family: 'Open Sans'; |
|||
color: #FFF; |
|||
font-size: 19px; |
|||
width: 100%; |
|||
text-align: center; |
|||
z-index: 9999999999999; |
|||
position: absolute; |
|||
top: 60%; |
|||
opacity: 1; |
|||
line-height: 30px; |
|||
} |
|||
|
|||
#loader-wrapper .load_title span { |
|||
font-weight: normal; |
|||
font-style: italic; |
|||
font-size: 13px; |
|||
color: #FFF; |
|||
opacity: 0.5; |
|||
} |
|||
</style> |
|||
<script type="module" crossorigin src="./js/index-iDzH0SQw.js"></script> |
|||
<link rel="stylesheet" crossorigin href="./css/index-Dcqqxojt.css"> |
|||
</head> |
|||
|
|||
<body> |
|||
<div id="app"> |
|||
<div id="loader-wrapper"> |
|||
<div id="loader"></div> |
|||
<div class="loader-section section-left"></div> |
|||
<div class="loader-section section-right"></div> |
|||
<div class="load_title">正在加载系统资源,请耐心等待</div> |
|||
</div> |
|||
</div> |
|||
</body> |
|||
|
|||
</html> |
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue