Commit d9758636b191a4b7d0201b8d3943c15c5b7b220a

Authored by jason
1 parent d8871807

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

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 {
20 20 // TODO @jaosn:-1、0、1、4、-2 是前端已经定义好的么?感觉未来可以考虑搞成和 BPMN 尽量一致的单词哈;类似 usertask 用户审批;
21 21 START_EVENT_NODE(0, "开始节点"),
22 22 APPROVE_USER_NODE (1, "审批人节点"),
  23 + // 抄送人节点、对应 BPMN 的 ScriptTask. 使用ScriptTask 原因。好像 ServiceTask 自定义属性不能写入 XML
  24 + SCRIPT_TASK_NODE(2, "抄送人节点"),
23 25 EXCLUSIVE_GATEWAY_NODE(4, "排他网关"),
24   - END_NODE(-2, "结束节点");
  26 + END_EVENT_NODE(-2, "结束节点");
25 27  
26 28 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModelNodeType::getType).toArray();
27 29  
... ...
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;
13 13 import org.flowable.bpmn.converter.BpmnXMLConverter;
14 14 import org.flowable.bpmn.model.Process;
15 15 import org.flowable.bpmn.model.*;
  16 +import org.flowable.common.engine.impl.scripting.ScriptingEngines;
16 17 import org.flowable.common.engine.impl.util.io.BytesStreamSource;
17 18  
18 19 import java.util.ArrayList;
... ... @@ -20,11 +21,15 @@ import java.util.HashSet;
20 21 import java.util.List;
21 22 import java.util.Set;
22 23  
  24 +import static org.flowable.bpmn.constants.BpmnXMLConstants.*;
  25 +
23 26 /**
24 27 * 流程模型转操作工具类
25 28 */
26 29 public class BpmnModelUtils {
27 30  
  31 + public static final String BPMN_SIMPLE_COPY_EXECUTION_SCRIPT = "#{bpmSimpleNodeService.copy(execution)}";
  32 +
28 33 public static Integer parseCandidateStrategy(FlowElement userTask) {
29 34 return NumberUtils.parseInt(userTask.getAttributeValue(
30 35 BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
... ... @@ -379,26 +384,27 @@ public class BpmnModelUtils {
379 384 Assert.notNull(nodeType, "模型节点类型不支持");
380 385 switch (nodeType) {
381 386 case START_EVENT_NODE:
382   - case APPROVE_USER_NODE: {
  387 + case APPROVE_USER_NODE:
  388 + case SCRIPT_TASK_NODE: {
383 389 addBpmnSequenceFlowElement(mainProcess, node.getId(), childNode.getId(), null, null);
384 390 // 递归调用后续节点
385   - addBpmnSequenceFlow(mainProcess, childNode,endId);
  391 + addBpmnSequenceFlow(mainProcess, childNode, endId);
386 392 break;
387 393 }
388 394 case EXCLUSIVE_GATEWAY_NODE: {
389   - String gateWayEndId = ( childNode == null || childNode.getId() == null ) ? BpmnModelConstants.END_EVENT_ID : childNode.getId();
  395 + String gateWayEndId = (childNode == null || childNode.getId() == null) ? BpmnModelConstants.END_EVENT_ID : childNode.getId();
390 396 List<BpmSimpleModelNodeVO> conditionNodes = node.getConditionNodes();
391 397 Assert.notEmpty(conditionNodes, "网关节点的条件节点不能为空");
392 398 for (int i = 0; i < conditionNodes.size(); i++) {
393 399 BpmSimpleModelNodeVO item = conditionNodes.get(i);
394   - BpmSimpleModelNodeVO nextNodeOnCondition = getNextNodeOnCondition(item);
  400 + BpmSimpleModelNodeVO nextNodeOnCondition = item.getChildNode();
395 401 if (nextNodeOnCondition != null && nextNodeOnCondition.getId() != null) {
396 402 addBpmnSequenceFlowElement(mainProcess, node.getId(), nextNodeOnCondition.getId(),
397   - String.format("%s_SequenceFlow_%d", node.getId(), i+1), null);
  403 + String.format("%s_SequenceFlow_%d", node.getId(), i + 1), null);
398 404 addBpmnSequenceFlow(mainProcess, nextNodeOnCondition, gateWayEndId);
399 405 } else {
400 406 addBpmnSequenceFlowElement(mainProcess, node.getId(), gateWayEndId,
401   - String.format("%s_SequenceFlow_%d", node.getId(), i+1), null);
  407 + String.format("%s_SequenceFlow_%d", node.getId(), i + 1), null);
402 408 }
403 409 }
404 410 // 递归调用后续节点
... ... @@ -412,10 +418,6 @@ public class BpmnModelUtils {
412 418  
413 419 }
414 420  
415   - private static BpmSimpleModelNodeVO getNextNodeOnCondition(BpmSimpleModelNodeVO conditionNode) {
416   - return conditionNode.getChildNode();
417   - }
418   -
419 421 private static void addBpmnSequenceFlowElement(Process mainProcess, String sourceId, String targetId, String seqFlowId, String conditionExpression) {
420 422 SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId);
421 423 if (StrUtil.isNotEmpty(conditionExpression)) {
... ... @@ -439,7 +441,10 @@ public class BpmnModelUtils {
439 441 addBpmnStartEventNode(mainProcess, simpleModelNode);
440 442 break;
441 443 case APPROVE_USER_NODE:
442   - addBpmnUserTaskEventNode(mainProcess, simpleModelNode);
  444 + addBpmnUserTaskNode(mainProcess, simpleModelNode);
  445 + break;
  446 + case SCRIPT_TASK_NODE:
  447 + addBpmnScriptTaSskNode(mainProcess, simpleModelNode);
443 448 break;
444 449 case EXCLUSIVE_GATEWAY_NODE:
445 450 addBpmnExclusiveGatewayNode(mainProcess, simpleModelNode);
... ... @@ -467,6 +472,25 @@ public class BpmnModelUtils {
467 472 }
468 473 }
469 474  
  475 + private static void addBpmnScriptTaSskNode(Process mainProcess, BpmSimpleModelNodeVO node) {
  476 + ScriptTask scriptTask = new ScriptTask();
  477 + scriptTask.setId(node.getId());
  478 + scriptTask.setName(node.getName());
  479 + scriptTask.setScriptFormat(ScriptingEngines.DEFAULT_SCRIPTING_LANGUAGE);
  480 + scriptTask.setScript(BPMN_SIMPLE_COPY_EXECUTION_SCRIPT);
  481 + // 添加自定义属性
  482 + addExtensionAttributes(node, scriptTask);
  483 + mainProcess.addFlowElement(scriptTask);
  484 + }
  485 +
  486 + private static void addExtensionAttributes(BpmSimpleModelNodeVO node, FlowElement flowElement) {
  487 + Integer candidateStrategy = MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY);
  488 + addExtensionAttributes(flowElement, BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,
  489 + candidateStrategy == null ? null : String.valueOf(candidateStrategy));
  490 + addExtensionAttributes(flowElement, BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM,
  491 + MapUtil.getStr(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));
  492 + }
  493 +
470 494 private static void addBpmnExclusiveGatewayNode(Process mainProcess, BpmSimpleModelNodeVO node) {
471 495 Assert.notEmpty(node.getConditionNodes(), "网关节点的条件节点不能为空");
472 496 ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
... ... @@ -483,25 +507,21 @@ public class BpmnModelUtils {
483 507 mainProcess.addFlowElement(endEvent);
484 508 }
485 509  
486   - private static void addBpmnUserTaskEventNode(Process mainProcess, BpmSimpleModelNodeVO node) {
  510 + private static void addBpmnUserTaskNode(Process mainProcess, BpmSimpleModelNodeVO node) {
487 511 UserTask userTask = new UserTask();
488 512 userTask.setId(node.getId());
489 513 userTask.setName(node.getName());
490   - Integer candidateStrategy = MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY);
491   - // 添加自定义属性
492   - addExtensionAttribute(userTask, BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,
493   - candidateStrategy == null ? null : String.valueOf(candidateStrategy));
494   - addExtensionAttribute(userTask, BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM,
495   - MapUtil.getStr(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));
  514 + addExtensionAttributes(node, userTask);
496 515 mainProcess.addFlowElement(userTask);
497 516 }
498 517  
499   - private static void addExtensionAttribute(FlowElement element, String namespace, String name, String value) {
  518 + private static void addExtensionAttributes(FlowElement element, String namespace, String name, String value) {
500 519 if (value == null) {
501 520 return;
502 521 }
503 522 ExtensionAttribute extensionAttribute = new ExtensionAttribute(name, value);
504 523 extensionAttribute.setNamespace(namespace);
  524 + extensionAttribute.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
505 525 element.addAttribute(extensionAttribute);
506 526 }
507 527  
... ...
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 {
17 17 * 流程实例的抄送
18 18 *
19 19 * @param userIds 抄送的用户编号
20   - * @param taskId 流程任务编号
  20 + * @param processInstanceId 流程编号
  21 + * @param taskId 任务编号
  22 + * @param taskName 任务名称
21 23 */
22   - void createProcessInstanceCopy(Collection<Long> userIds, String taskId);
  24 + void createProcessInstanceCopy(Collection<Long> userIds, String processInstanceId, String taskId, String taskName);
23 25  
24 26 /**
25 27 * 获得抄送的流程的分页
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java
1 1 package cn.iocoder.yudao.module.bpm.service.task;
2 2  
3   -import cn.hutool.core.util.ObjectUtil;
4 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 4 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
6 5 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
... ... @@ -11,7 +10,6 @@ import jakarta.annotation.Resource;
11 10 import lombok.extern.slf4j.Slf4j;
12 11 import org.flowable.engine.repository.ProcessDefinition;
13 12 import org.flowable.engine.runtime.ProcessInstance;
14   -import org.flowable.task.api.Task;
15 13 import org.springframework.context.annotation.Lazy;
16 14 import org.springframework.stereotype.Service;
17 15 import org.springframework.validation.annotation.Validated;
... ... @@ -47,14 +45,14 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
47 45 private BpmProcessDefinitionService processDefinitionService;
48 46  
49 47 @Override
50   - public void createProcessInstanceCopy(Collection<Long> userIds, String taskId) {
51   - // 1.1 校验任务存在
52   - Task task = taskService.getTask(taskId);
53   - if (ObjectUtil.isNull(task)) {
54   - throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
55   - }
  48 + public void createProcessInstanceCopy(Collection<Long> userIds, String processInstanceId, String taskId, String taskName) {
  49 + // 1.1 校验任务存在 暂时去掉这个校验. 因为任务可能仿钉钉快搭的抄送节点(ScriptTask)
  50 +// Task task = taskService.getTask(taskId);
  51 +// if (ObjectUtil.isNull(task)) {
  52 +// throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
  53 +// }
56 54 // 1.2 校验流程实例存在
57   - String processInstanceId = task.getProcessInstanceId();
  55 +// String processInstanceId = task.getProcessInstanceId();
58 56 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
59 57 if (processInstance == null) {
60 58 throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
... ... @@ -70,7 +68,7 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
70 68 List<BpmProcessInstanceCopyDO> copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO()
71 69 .setUserId(userId).setStartUserId(Long.valueOf(processInstance.getStartUserId()))
72 70 .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
73   - .setCategory(processDefinition.getCategory()).setTaskId(taskId).setTaskName(task.getName()));
  71 + .setCategory(processDefinition.getCategory()).setTaskId(taskId).setTaskName(taskName));
74 72 processInstanceCopyMapper.insertBatch(copyList);
75 73 }
76 74  
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmSimpleNodeService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.task;
  2 +
  3 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
  4 +import jakarta.annotation.Resource;
  5 +import org.flowable.bpmn.model.FlowElement;
  6 +import org.flowable.engine.delegate.DelegateExecution;
  7 +import org.springframework.stereotype.Service;
  8 +
  9 +import java.util.Set;
  10 +
  11 +/**
  12 + * 仿钉钉快搭各个节点 Service
  13 + * @author jason
  14 + */
  15 +@Service
  16 +public class BpmSimpleNodeService {
  17 +
  18 + @Resource
  19 + private BpmTaskCandidateInvoker taskCandidateInvoker;
  20 + @Resource
  21 + private BpmProcessInstanceCopyService processInstanceCopyService;
  22 +
  23 + /**
  24 + * 仿钉钉快搭抄送
  25 + * @param execution 执行的任务(ScriptTask)
  26 + */
  27 + public Boolean copy(DelegateExecution execution) {
  28 + Set<Long> userIds = taskCandidateInvoker.calculateUsers(execution);
  29 + FlowElement currentFlowElement = execution.getCurrentFlowElement();
  30 + processInstanceCopyService.createProcessInstanceCopy(userIds, execution.getProcessInstanceId(),
  31 + currentFlowElement.getId(), currentFlowElement.getName());
  32 + return Boolean.TRUE;
  33 + }
  34 +}
... ...
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 {
185 185  
186 186 // 2. 抄送用户
187 187 if (CollUtil.isNotEmpty(reqVO.getCopyUserIds())) {
188   - processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getId());
  188 + processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), instance.getProcessInstanceId(),
  189 + reqVO.getId(), task.getName());
189 190 }
190 191  
191 192 // 情况一:被委派的任务,不调用 complete 去完成任务
... ...