仿钉钉流程设计-抄送节点实现

This commit is contained in:
jason 2024-04-05 12:59:37 +08:00
parent d88718071d
commit d9758636b1
6 changed files with 90 additions and 33 deletions

View File

@ -20,8 +20,10 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable {
// TODO @jaosn-1014-2 是前端已经定义好的么感觉未来可以考虑搞成和 BPMN 尽量一致的单词哈类似 usertask 用户审批
START_EVENT_NODE(0, "开始节点"),
APPROVE_USER_NODE (1, "审批人节点"),
// 抄送人节点对应 BPMN ScriptTask. 使用ScriptTask 原因好像 ServiceTask 自定义属性不能写入 XML
SCRIPT_TASK_NODE(2, "抄送人节点"),
EXCLUSIVE_GATEWAY_NODE(4, "排他网关"),
END_NODE(-2, "结束节点");
END_EVENT_NODE(-2, "结束节点");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModelNodeType::getType).toArray();

View File

@ -13,6 +13,7 @@ import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.impl.scripting.ScriptingEngines;
import org.flowable.common.engine.impl.util.io.BytesStreamSource;
import java.util.ArrayList;
@ -20,11 +21,15 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.flowable.bpmn.constants.BpmnXMLConstants.*;
/**
* 流程模型转操作工具类
*/
public class BpmnModelUtils {
public static final String BPMN_SIMPLE_COPY_EXECUTION_SCRIPT = "#{bpmSimpleNodeService.copy(execution)}";
public static Integer parseCandidateStrategy(FlowElement userTask) {
return NumberUtils.parseInt(userTask.getAttributeValue(
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
@ -379,26 +384,27 @@ public class BpmnModelUtils {
Assert.notNull(nodeType, "模型节点类型不支持");
switch (nodeType) {
case START_EVENT_NODE:
case APPROVE_USER_NODE: {
case APPROVE_USER_NODE:
case SCRIPT_TASK_NODE: {
addBpmnSequenceFlowElement(mainProcess, node.getId(), childNode.getId(), null, null);
// 递归调用后续节点
addBpmnSequenceFlow(mainProcess, childNode,endId);
addBpmnSequenceFlow(mainProcess, childNode, endId);
break;
}
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();
Assert.notEmpty(conditionNodes, "网关节点的条件节点不能为空");
for (int i = 0; i < conditionNodes.size(); i++) {
BpmSimpleModelNodeVO item = conditionNodes.get(i);
BpmSimpleModelNodeVO nextNodeOnCondition = getNextNodeOnCondition(item);
BpmSimpleModelNodeVO nextNodeOnCondition = item.getChildNode();
if (nextNodeOnCondition != null && nextNodeOnCondition.getId() != null) {
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);
} else {
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) {
SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId);
if (StrUtil.isNotEmpty(conditionExpression)) {
@ -439,7 +441,10 @@ public class BpmnModelUtils {
addBpmnStartEventNode(mainProcess, simpleModelNode);
break;
case APPROVE_USER_NODE:
addBpmnUserTaskEventNode(mainProcess, simpleModelNode);
addBpmnUserTaskNode(mainProcess, simpleModelNode);
break;
case SCRIPT_TASK_NODE:
addBpmnScriptTaSskNode(mainProcess, simpleModelNode);
break;
case EXCLUSIVE_GATEWAY_NODE:
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) {
Assert.notEmpty(node.getConditionNodes(), "网关节点的条件节点不能为空");
ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
@ -483,25 +507,21 @@ public class BpmnModelUtils {
mainProcess.addFlowElement(endEvent);
}
private static void addBpmnUserTaskEventNode(Process mainProcess, BpmSimpleModelNodeVO node) {
private static void addBpmnUserTaskNode(Process mainProcess, BpmSimpleModelNodeVO node) {
UserTask userTask = new UserTask();
userTask.setId(node.getId());
userTask.setName(node.getName());
Integer candidateStrategy = MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY);
// 添加自定义属性
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));
addExtensionAttributes(node, 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) {
return;
}
ExtensionAttribute extensionAttribute = new ExtensionAttribute(name, value);
extensionAttribute.setNamespace(namespace);
extensionAttribute.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
element.addAttribute(extensionAttribute);
}

View File

@ -17,9 +17,11 @@ public interface BpmProcessInstanceCopyService {
* 流程实例的抄送
*
* @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);
/**
* 获得抄送的流程的分页

View File

@ -1,6 +1,5 @@
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.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
@ -11,7 +10,6 @@ import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@ -47,14 +45,14 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
private BpmProcessDefinitionService processDefinitionService;
@Override
public void createProcessInstanceCopy(Collection<Long> userIds, String taskId) {
// 1.1 校验任务存在
Task task = taskService.getTask(taskId);
if (ObjectUtil.isNull(task)) {
throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
}
public void createProcessInstanceCopy(Collection<Long> userIds, String processInstanceId, String taskId, String taskName) {
// 1.1 校验任务存在 暂时去掉这个校验. 因为任务可能仿钉钉快搭的抄送节点(ScriptTask)
// Task task = taskService.getTask(taskId);
// if (ObjectUtil.isNull(task)) {
// throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
// }
// 1.2 校验流程实例存在
String processInstanceId = task.getProcessInstanceId();
// String processInstanceId = task.getProcessInstanceId();
ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
if (processInstance == null) {
throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
@ -70,7 +68,7 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
List<BpmProcessInstanceCopyDO> copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO()
.setUserId(userId).setStartUserId(Long.valueOf(processInstance.getStartUserId()))
.setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
.setCategory(processDefinition.getCategory()).setTaskId(taskId).setTaskName(task.getName()));
.setCategory(processDefinition.getCategory()).setTaskId(taskId).setTaskName(taskName));
processInstanceCopyMapper.insertBatch(copyList);
}

View File

@ -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;
}
}

View File

@ -185,7 +185,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
// 2. 抄送用户
if (CollUtil.isNotEmpty(reqVO.getCopyUserIds())) {
processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getId());
processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), instance.getProcessInstanceId(),
reqVO.getId(), task.getName());
}
// 情况一被委派的任务不调用 complete 去完成任务