Эх сурвалжийг харах

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

jason 1 жил өмнө
parent
commit
d9758636b1

+ 3 - 1
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();
 

+ 39 - 19
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<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);
     }
 

+ 4 - 2
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<Long> userIds, String taskId);
+    void createProcessInstanceCopy(Collection<Long> userIds, String processInstanceId, String taskId, String taskName);
 
     /**
      * 获得抄送的流程的分页

+ 8 - 10
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<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);
     }
 

+ 34 - 0
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<Long> userIds = taskCandidateInvoker.calculateUsers(execution);
+        FlowElement currentFlowElement = execution.getCurrentFlowElement();
+        processInstanceCopyService.createProcessInstanceCopy(userIds, execution.getProcessInstanceId(),
+                currentFlowElement.getId(), currentFlowElement.getName());
+        return Boolean.TRUE;
+    }
+}

+ 2 - 1
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 去完成任务