Commit d9758636b191a4b7d0201b8d3943c15c5b7b220a
1 parent
d8871807
仿钉钉流程设计-抄送节点实现
Showing
6 changed files
with
90 additions
and
33 deletions
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 去完成任务 | ... | ... |