mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2025-01-19 03:30:06 +08:00
仿钉钉流程设计-抄送节点实现
This commit is contained in:
parent
d88718071d
commit
d9758636b1
@ -20,8 +20,10 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable {
|
|||||||
// TODO @jaosn:-1、0、1、4、-2 是前端已经定义好的么?感觉未来可以考虑搞成和 BPMN 尽量一致的单词哈;类似 usertask 用户审批;
|
// TODO @jaosn:-1、0、1、4、-2 是前端已经定义好的么?感觉未来可以考虑搞成和 BPMN 尽量一致的单词哈;类似 usertask 用户审批;
|
||||||
START_EVENT_NODE(0, "开始节点"),
|
START_EVENT_NODE(0, "开始节点"),
|
||||||
APPROVE_USER_NODE (1, "审批人节点"),
|
APPROVE_USER_NODE (1, "审批人节点"),
|
||||||
|
// 抄送人节点、对应 BPMN 的 ScriptTask. 使用ScriptTask 原因。好像 ServiceTask 自定义属性不能写入 XML
|
||||||
|
SCRIPT_TASK_NODE(2, "抄送人节点"),
|
||||||
EXCLUSIVE_GATEWAY_NODE(4, "排他网关"),
|
EXCLUSIVE_GATEWAY_NODE(4, "排他网关"),
|
||||||
END_NODE(-2, "结束节点");
|
END_EVENT_NODE(-2, "结束节点");
|
||||||
|
|
||||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModelNodeType::getType).toArray();
|
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModelNodeType::getType).toArray();
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import org.flowable.bpmn.BpmnAutoLayout;
|
|||||||
import org.flowable.bpmn.converter.BpmnXMLConverter;
|
import org.flowable.bpmn.converter.BpmnXMLConverter;
|
||||||
import org.flowable.bpmn.model.Process;
|
import org.flowable.bpmn.model.Process;
|
||||||
import org.flowable.bpmn.model.*;
|
import org.flowable.bpmn.model.*;
|
||||||
|
import org.flowable.common.engine.impl.scripting.ScriptingEngines;
|
||||||
import org.flowable.common.engine.impl.util.io.BytesStreamSource;
|
import org.flowable.common.engine.impl.util.io.BytesStreamSource;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -20,11 +21,15 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.flowable.bpmn.constants.BpmnXMLConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程模型转操作工具类
|
* 流程模型转操作工具类
|
||||||
*/
|
*/
|
||||||
public class BpmnModelUtils {
|
public class BpmnModelUtils {
|
||||||
|
|
||||||
|
public static final String BPMN_SIMPLE_COPY_EXECUTION_SCRIPT = "#{bpmSimpleNodeService.copy(execution)}";
|
||||||
|
|
||||||
public static Integer parseCandidateStrategy(FlowElement userTask) {
|
public static Integer parseCandidateStrategy(FlowElement userTask) {
|
||||||
return NumberUtils.parseInt(userTask.getAttributeValue(
|
return NumberUtils.parseInt(userTask.getAttributeValue(
|
||||||
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
|
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
|
||||||
@ -379,26 +384,27 @@ public class BpmnModelUtils {
|
|||||||
Assert.notNull(nodeType, "模型节点类型不支持");
|
Assert.notNull(nodeType, "模型节点类型不支持");
|
||||||
switch (nodeType) {
|
switch (nodeType) {
|
||||||
case START_EVENT_NODE:
|
case START_EVENT_NODE:
|
||||||
case APPROVE_USER_NODE: {
|
case APPROVE_USER_NODE:
|
||||||
|
case SCRIPT_TASK_NODE: {
|
||||||
addBpmnSequenceFlowElement(mainProcess, node.getId(), childNode.getId(), null, null);
|
addBpmnSequenceFlowElement(mainProcess, node.getId(), childNode.getId(), null, null);
|
||||||
// 递归调用后续节点
|
// 递归调用后续节点
|
||||||
addBpmnSequenceFlow(mainProcess, childNode,endId);
|
addBpmnSequenceFlow(mainProcess, childNode, endId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EXCLUSIVE_GATEWAY_NODE: {
|
case EXCLUSIVE_GATEWAY_NODE: {
|
||||||
String gateWayEndId = ( childNode == null || childNode.getId() == null ) ? BpmnModelConstants.END_EVENT_ID : childNode.getId();
|
String gateWayEndId = (childNode == null || childNode.getId() == null) ? BpmnModelConstants.END_EVENT_ID : childNode.getId();
|
||||||
List<BpmSimpleModelNodeVO> conditionNodes = node.getConditionNodes();
|
List<BpmSimpleModelNodeVO> conditionNodes = node.getConditionNodes();
|
||||||
Assert.notEmpty(conditionNodes, "网关节点的条件节点不能为空");
|
Assert.notEmpty(conditionNodes, "网关节点的条件节点不能为空");
|
||||||
for (int i = 0; i < conditionNodes.size(); i++) {
|
for (int i = 0; i < conditionNodes.size(); i++) {
|
||||||
BpmSimpleModelNodeVO item = conditionNodes.get(i);
|
BpmSimpleModelNodeVO item = conditionNodes.get(i);
|
||||||
BpmSimpleModelNodeVO nextNodeOnCondition = getNextNodeOnCondition(item);
|
BpmSimpleModelNodeVO nextNodeOnCondition = item.getChildNode();
|
||||||
if (nextNodeOnCondition != null && nextNodeOnCondition.getId() != null) {
|
if (nextNodeOnCondition != null && nextNodeOnCondition.getId() != null) {
|
||||||
addBpmnSequenceFlowElement(mainProcess, node.getId(), nextNodeOnCondition.getId(),
|
addBpmnSequenceFlowElement(mainProcess, node.getId(), nextNodeOnCondition.getId(),
|
||||||
String.format("%s_SequenceFlow_%d", node.getId(), i+1), null);
|
String.format("%s_SequenceFlow_%d", node.getId(), i + 1), null);
|
||||||
addBpmnSequenceFlow(mainProcess, nextNodeOnCondition, gateWayEndId);
|
addBpmnSequenceFlow(mainProcess, nextNodeOnCondition, gateWayEndId);
|
||||||
} else {
|
} else {
|
||||||
addBpmnSequenceFlowElement(mainProcess, node.getId(), gateWayEndId,
|
addBpmnSequenceFlowElement(mainProcess, node.getId(), gateWayEndId,
|
||||||
String.format("%s_SequenceFlow_%d", node.getId(), i+1), null);
|
String.format("%s_SequenceFlow_%d", node.getId(), i + 1), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 递归调用后续节点
|
// 递归调用后续节点
|
||||||
@ -412,10 +418,6 @@ public class BpmnModelUtils {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BpmSimpleModelNodeVO getNextNodeOnCondition(BpmSimpleModelNodeVO conditionNode) {
|
|
||||||
return conditionNode.getChildNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addBpmnSequenceFlowElement(Process mainProcess, String sourceId, String targetId, String seqFlowId, String conditionExpression) {
|
private static void addBpmnSequenceFlowElement(Process mainProcess, String sourceId, String targetId, String seqFlowId, String conditionExpression) {
|
||||||
SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId);
|
SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId);
|
||||||
if (StrUtil.isNotEmpty(conditionExpression)) {
|
if (StrUtil.isNotEmpty(conditionExpression)) {
|
||||||
@ -439,7 +441,10 @@ public class BpmnModelUtils {
|
|||||||
addBpmnStartEventNode(mainProcess, simpleModelNode);
|
addBpmnStartEventNode(mainProcess, simpleModelNode);
|
||||||
break;
|
break;
|
||||||
case APPROVE_USER_NODE:
|
case APPROVE_USER_NODE:
|
||||||
addBpmnUserTaskEventNode(mainProcess, simpleModelNode);
|
addBpmnUserTaskNode(mainProcess, simpleModelNode);
|
||||||
|
break;
|
||||||
|
case SCRIPT_TASK_NODE:
|
||||||
|
addBpmnScriptTaSskNode(mainProcess, simpleModelNode);
|
||||||
break;
|
break;
|
||||||
case EXCLUSIVE_GATEWAY_NODE:
|
case EXCLUSIVE_GATEWAY_NODE:
|
||||||
addBpmnExclusiveGatewayNode(mainProcess, simpleModelNode);
|
addBpmnExclusiveGatewayNode(mainProcess, simpleModelNode);
|
||||||
@ -467,6 +472,25 @@ public class BpmnModelUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addBpmnScriptTaSskNode(Process mainProcess, BpmSimpleModelNodeVO node) {
|
||||||
|
ScriptTask scriptTask = new ScriptTask();
|
||||||
|
scriptTask.setId(node.getId());
|
||||||
|
scriptTask.setName(node.getName());
|
||||||
|
scriptTask.setScriptFormat(ScriptingEngines.DEFAULT_SCRIPTING_LANGUAGE);
|
||||||
|
scriptTask.setScript(BPMN_SIMPLE_COPY_EXECUTION_SCRIPT);
|
||||||
|
// 添加自定义属性
|
||||||
|
addExtensionAttributes(node, scriptTask);
|
||||||
|
mainProcess.addFlowElement(scriptTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addExtensionAttributes(BpmSimpleModelNodeVO node, FlowElement flowElement) {
|
||||||
|
Integer candidateStrategy = MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY);
|
||||||
|
addExtensionAttributes(flowElement, BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,
|
||||||
|
candidateStrategy == null ? null : String.valueOf(candidateStrategy));
|
||||||
|
addExtensionAttributes(flowElement, BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM,
|
||||||
|
MapUtil.getStr(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));
|
||||||
|
}
|
||||||
|
|
||||||
private static void addBpmnExclusiveGatewayNode(Process mainProcess, BpmSimpleModelNodeVO node) {
|
private static void addBpmnExclusiveGatewayNode(Process mainProcess, BpmSimpleModelNodeVO node) {
|
||||||
Assert.notEmpty(node.getConditionNodes(), "网关节点的条件节点不能为空");
|
Assert.notEmpty(node.getConditionNodes(), "网关节点的条件节点不能为空");
|
||||||
ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
|
ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
|
||||||
@ -483,25 +507,21 @@ public class BpmnModelUtils {
|
|||||||
mainProcess.addFlowElement(endEvent);
|
mainProcess.addFlowElement(endEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addBpmnUserTaskEventNode(Process mainProcess, BpmSimpleModelNodeVO node) {
|
private static void addBpmnUserTaskNode(Process mainProcess, BpmSimpleModelNodeVO node) {
|
||||||
UserTask userTask = new UserTask();
|
UserTask userTask = new UserTask();
|
||||||
userTask.setId(node.getId());
|
userTask.setId(node.getId());
|
||||||
userTask.setName(node.getName());
|
userTask.setName(node.getName());
|
||||||
Integer candidateStrategy = MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY);
|
addExtensionAttributes(node, userTask);
|
||||||
// 添加自定义属性
|
|
||||||
addExtensionAttribute(userTask, BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,
|
|
||||||
candidateStrategy == null ? null : String.valueOf(candidateStrategy));
|
|
||||||
addExtensionAttribute(userTask, BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM,
|
|
||||||
MapUtil.getStr(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));
|
|
||||||
mainProcess.addFlowElement(userTask);
|
mainProcess.addFlowElement(userTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addExtensionAttribute(FlowElement element, String namespace, String name, String value) {
|
private static void addExtensionAttributes(FlowElement element, String namespace, String name, String value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ExtensionAttribute extensionAttribute = new ExtensionAttribute(name, value);
|
ExtensionAttribute extensionAttribute = new ExtensionAttribute(name, value);
|
||||||
extensionAttribute.setNamespace(namespace);
|
extensionAttribute.setNamespace(namespace);
|
||||||
|
extensionAttribute.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
|
||||||
element.addAttribute(extensionAttribute);
|
element.addAttribute(extensionAttribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,9 +17,11 @@ public interface BpmProcessInstanceCopyService {
|
|||||||
* 流程实例的抄送
|
* 流程实例的抄送
|
||||||
*
|
*
|
||||||
* @param userIds 抄送的用户编号
|
* @param userIds 抄送的用户编号
|
||||||
* @param taskId 流程任务编号
|
* @param processInstanceId 流程编号
|
||||||
|
* @param taskId 任务编号
|
||||||
|
* @param taskName 任务名称
|
||||||
*/
|
*/
|
||||||
void createProcessInstanceCopy(Collection<Long> userIds, String taskId);
|
void createProcessInstanceCopy(Collection<Long> userIds, String processInstanceId, String taskId, String taskName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得抄送的流程的分页
|
* 获得抄送的流程的分页
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package cn.iocoder.yudao.module.bpm.service.task;
|
package cn.iocoder.yudao.module.bpm.service.task;
|
||||||
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
|
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
|
||||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
|
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
|
||||||
@ -11,7 +10,6 @@ import jakarta.annotation.Resource;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.flowable.engine.repository.ProcessDefinition;
|
import org.flowable.engine.repository.ProcessDefinition;
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
import org.flowable.task.api.Task;
|
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
@ -47,14 +45,14 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
|
|||||||
private BpmProcessDefinitionService processDefinitionService;
|
private BpmProcessDefinitionService processDefinitionService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createProcessInstanceCopy(Collection<Long> userIds, String taskId) {
|
public void createProcessInstanceCopy(Collection<Long> userIds, String processInstanceId, String taskId, String taskName) {
|
||||||
// 1.1 校验任务存在
|
// 1.1 校验任务存在 暂时去掉这个校验. 因为任务可能仿钉钉快搭的抄送节点(ScriptTask)
|
||||||
Task task = taskService.getTask(taskId);
|
// Task task = taskService.getTask(taskId);
|
||||||
if (ObjectUtil.isNull(task)) {
|
// if (ObjectUtil.isNull(task)) {
|
||||||
throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
|
// throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
|
||||||
}
|
// }
|
||||||
// 1.2 校验流程实例存在
|
// 1.2 校验流程实例存在
|
||||||
String processInstanceId = task.getProcessInstanceId();
|
// String processInstanceId = task.getProcessInstanceId();
|
||||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
|
ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
|
||||||
if (processInstance == null) {
|
if (processInstance == null) {
|
||||||
throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
|
throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
|
||||||
@ -70,7 +68,7 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
|
|||||||
List<BpmProcessInstanceCopyDO> copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO()
|
List<BpmProcessInstanceCopyDO> copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO()
|
||||||
.setUserId(userId).setStartUserId(Long.valueOf(processInstance.getStartUserId()))
|
.setUserId(userId).setStartUserId(Long.valueOf(processInstance.getStartUserId()))
|
||||||
.setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
|
.setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
|
||||||
.setCategory(processDefinition.getCategory()).setTaskId(taskId).setTaskName(task.getName()));
|
.setCategory(processDefinition.getCategory()).setTaskId(taskId).setTaskName(taskName));
|
||||||
processInstanceCopyMapper.insertBatch(copyList);
|
processInstanceCopyMapper.insertBatch(copyList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package cn.iocoder.yudao.module.bpm.service.task;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.flowable.bpmn.model.FlowElement;
|
||||||
|
import org.flowable.engine.delegate.DelegateExecution;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仿钉钉快搭各个节点 Service
|
||||||
|
* @author jason
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class BpmSimpleNodeService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||||
|
@Resource
|
||||||
|
private BpmProcessInstanceCopyService processInstanceCopyService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仿钉钉快搭抄送
|
||||||
|
* @param execution 执行的任务(ScriptTask)
|
||||||
|
*/
|
||||||
|
public Boolean copy(DelegateExecution execution) {
|
||||||
|
Set<Long> userIds = taskCandidateInvoker.calculateUsers(execution);
|
||||||
|
FlowElement currentFlowElement = execution.getCurrentFlowElement();
|
||||||
|
processInstanceCopyService.createProcessInstanceCopy(userIds, execution.getProcessInstanceId(),
|
||||||
|
currentFlowElement.getId(), currentFlowElement.getName());
|
||||||
|
return Boolean.TRUE;
|
||||||
|
}
|
||||||
|
}
|
@ -185,7 +185,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||||||
|
|
||||||
// 2. 抄送用户
|
// 2. 抄送用户
|
||||||
if (CollUtil.isNotEmpty(reqVO.getCopyUserIds())) {
|
if (CollUtil.isNotEmpty(reqVO.getCopyUserIds())) {
|
||||||
processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getId());
|
processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), instance.getProcessInstanceId(),
|
||||||
|
reqVO.getId(), task.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 情况一:被委派的任务,不调用 complete 去完成任务
|
// 情况一:被委派的任务,不调用 complete 去完成任务
|
||||||
|
Loading…
Reference in New Issue
Block a user