diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java index 75889985e..4e26d02d9 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java @@ -20,8 +20,10 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable { // TODO @jaosn:-1、0、1、4、-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(); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 17c36623b..03745adce 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -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 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); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java index bd84490e8..94df76d4d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java @@ -17,9 +17,11 @@ public interface BpmProcessInstanceCopyService { * 流程实例的抄送 * * @param userIds 抄送的用户编号 - * @param taskId 流程任务编号 + * @param processInstanceId 流程编号 + * @param taskId 任务编号 + * @param taskName 任务名称 */ - void createProcessInstanceCopy(Collection userIds, String taskId); + void createProcessInstanceCopy(Collection userIds, String processInstanceId, String taskId, String taskName); /** * 获得抄送的流程的分页 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java index aba8bd9f1..ce1ddd7fd 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java @@ -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 userIds, String taskId) { - // 1.1 校验任务存在 - Task task = taskService.getTask(taskId); - if (ObjectUtil.isNull(task)) { - throw exception(ErrorCodeConstants.TASK_NOT_EXISTS); - } + public void createProcessInstanceCopy(Collection 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 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); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmSimpleNodeService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmSimpleNodeService.java new file mode 100644 index 000000000..89ba0ee84 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmSimpleNodeService.java @@ -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 userIds = taskCandidateInvoker.calculateUsers(execution); + FlowElement currentFlowElement = execution.getCurrentFlowElement(); + processInstanceCopyService.createProcessInstanceCopy(userIds, execution.getProcessInstanceId(), + currentFlowElement.getId(), currentFlowElement.getName()); + return Boolean.TRUE; + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 7afed756b..5c2de9f48 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -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 去完成任务