Commit b504d9841e37c23cfed48640d641d47529fb4314

Authored by jason
1 parent 210b4e54

仿钉钉流程设计-后端实现30%

yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
... ... @@ -75,4 +75,6 @@ public interface ErrorCodeConstants {
75 75 // ========== BPM 流程表达式 1-009-014-000 ==========
76 76 ErrorCode PROCESS_EXPRESSION_NOT_EXISTS = new ErrorCode(1_009_014_000, "流程表达式不存在");
77 77  
  78 + // ========== BPM 仿钉钉流程设计器 1-009-015-000 ==========
  79 + ErrorCode CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT = new ErrorCode(1_009_015_000, "该流程模型不支持仿钉钉设计流程");
78 80 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmSimpleModelController.java
1 1 package cn.iocoder.yudao.module.bpm.controller.admin.definition;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelNodeVO;
4 5 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelSaveReqVO;
5 6 import cn.iocoder.yudao.module.bpm.service.definition.BpmSimpleModelService;
6 7 import io.swagger.v3.oas.annotations.Operation;
  8 +import io.swagger.v3.oas.annotations.Parameter;
7 9 import io.swagger.v3.oas.annotations.tags.Tag;
8 10 import jakarta.annotation.Resource;
9 11 import jakarta.validation.Valid;
10 12 import org.springframework.security.access.prepost.PreAuthorize;
11   -import org.springframework.web.bind.annotation.PostMapping;
12   -import org.springframework.web.bind.annotation.RequestBody;
13   -import org.springframework.web.bind.annotation.RequestMapping;
14   -import org.springframework.web.bind.annotation.RestController;
  13 +import org.springframework.web.bind.annotation.*;
15 14  
16 15 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
17 16  
... ... @@ -28,4 +27,12 @@ public class BpmSimpleModelController {
28 27 public CommonResult<Boolean> saveSimpleModel(@Valid @RequestBody BpmSimpleModelSaveReqVO reqVO) {
29 28 return success(bpmSimpleModelService.saveSimpleModel(reqVO));
30 29 }
  30 +
  31 + @GetMapping("/get")
  32 + @Operation(summary = "获得仿钉钉流程设计模型")
  33 + @Parameter(name = "modelId", description = "流程模型编号", required = true, example = "a2c5eee0-eb6c-11ee-abf4-0c37967c420a")
  34 + public CommonResult<BpmSimpleModelNodeVO> getSimpleModel(@RequestParam("modelId") String modelId){
  35 + return success(bpmSimpleModelService.getSimpleModel(modelId));
  36 + }
  37 +
31 38 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
1 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums;
2 2  
  3 +import com.google.common.collect.ImmutableSet;
  4 +import org.flowable.bpmn.model.*;
  5 +
  6 +import java.util.Set;
  7 +
3 8 /**
4 9 * BPMN XML 常量信息
5 10 *
... ... @@ -24,13 +29,13 @@ public interface BpmnModelConstants {
24 29 String USER_TASK_CANDIDATE_PARAM = "candidateParam";
25 30  
26 31 /**
27   - * BPMN Start Event 节点 Id, 用于后端生成 Start Event 节点
  32 + * BPMN End Event 节点 Id, 用于后端生成 End Event 节点
28 33 */
29   - String START_EVENT_ID = "StartEvent_1";
  34 + String END_EVENT_ID = "EndEvent_1";
30 35  
31 36 /**
32   - * BPMN End Event 节点 Id, 用于后端生成 End Event 节点
  37 + * 支持转仿钉钉设计模型的 Bpmn 节点
33 38 */
34   - String END_EVENT_ID = "EndEvent_1";
  39 + Set<Class<? extends FlowNode>> SUPPORT_CONVERT_SIMPLE_FlOW_NODES = ImmutableSet.of(UserTask.class, EndEvent.class);
35 40  
36 41 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
... ... @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.lang.Assert;
5   -import cn.hutool.core.lang.TypeReference;
6 5 import cn.hutool.core.map.MapUtil;
7 6 import cn.hutool.core.util.ArrayUtil;
8 7 import cn.hutool.core.util.StrUtil;
... ... @@ -338,31 +337,26 @@ public class BpmnModelUtils {
338 337  
339 338 /**
340 339 * 仿钉钉流程设计模型数据结构(json) 转换成 Bpmn Model (待完善)
341   - * @param processId 流程标识
342   - * @param processName 流程名称
  340 + *
  341 + * @param processId 流程标识
  342 + * @param processName 流程名称
343 343 * @param simpleModelNode 仿钉钉流程设计模型数据结构
344 344 * @return Bpmn Model
345 345 */
346   - public static BpmnModel convertSimpleModelToBpmnModel(String processId, String processName,BpmSimpleModelNodeVO simpleModelNode) {
  346 + public static BpmnModel convertSimpleModelToBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) {
347 347 BpmnModel bpmnModel = new BpmnModel();
348 348 Process mainProcess = new Process();
349 349 mainProcess.setId(processId);
350 350 mainProcess.setName(processName);
351 351 mainProcess.setExecutable(Boolean.TRUE);
352 352 bpmnModel.addProcess(mainProcess);
353   - // 目前前端传进来的节点。 没有 start event 节点 和 end event 节点。
354   - // 在最前面加上 start event node
355   - BpmSimpleModelNodeVO startEventNode = new BpmSimpleModelNodeVO();
356   - startEventNode.setId(BpmnModelConstants.START_EVENT_ID);
357   - startEventNode.setName("开始");
358   - startEventNode.setType(BpmSimpleModelNodeType.START_NODE.getType());
359   - startEventNode.setChildNode(simpleModelNode);
  353 + // 前端模型数据结构。 有 start event 节点. 没有 end event 节点。
360 354 // 添加 FlowNode
361   - addBpmnFlowNode(mainProcess, startEventNode);
  355 + addBpmnFlowNode(mainProcess, simpleModelNode);
362 356 // 单独添加 end event 节点
363 357 addBpmnEndEventNode(mainProcess);
364 358 // 添加节点之间的连线 Sequence Flow
365   - addBpmnSequenceFlow(mainProcess, startEventNode);
  359 + addBpmnSequenceFlow(mainProcess, simpleModelNode);
366 360 // 自动布局
367 361 new BpmnAutoLayout(bpmnModel).execute();
368 362  
... ... @@ -371,24 +365,24 @@ public class BpmnModelUtils {
371 365  
372 366 private static void addBpmnSequenceFlow(Process mainProcess, BpmSimpleModelNodeVO node) {
373 367 // 节点为 null 退出
374   - if (node == null) {
  368 + if (node == null || node.getId() == null) {
375 369 return;
376 370 }
377 371 BpmSimpleModelNodeVO childNode = node.getChildNode();
378 372 // 如果后续节点为 null. 添加与结束节点的连线
379   - if (childNode == null) {
380   - addBpmnSequenceFlowElement(mainProcess, node.getId(),BpmnModelConstants.END_EVENT_ID, null);
  373 + if (childNode == null || childNode.getId() == null) {
  374 + addBpmnSequenceFlowElement(mainProcess, node.getId(), BpmnModelConstants.END_EVENT_ID, null);
381 375 return;
382 376 }
383 377 BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType());
384 378 Assert.notNull(nodeType, "模型节点类型不支持");
385   - switch(nodeType){
386   - case START_NODE :
387   - case START_USER_NODE :
388   - case APPROVE_USER_NODE :
  379 + switch (nodeType) {
  380 + case START_NODE:
  381 + case START_USER_NODE:
  382 + case APPROVE_USER_NODE:
389 383 addBpmnSequenceFlowElement(mainProcess, node.getId(), childNode.getId(), null);
390 384 break;
391   - default : {
  385 + default: {
392 386 // TODO 其它节点类型的实现
393 387 }
394 388 }
... ... @@ -396,7 +390,7 @@ public class BpmnModelUtils {
396 390 addBpmnSequenceFlow(mainProcess, childNode);
397 391 }
398 392  
399   - private static void addBpmnSequenceFlowElement(Process mainProcess, String sourceId, String targetId, String conditionExpression) {
  393 + private static void addBpmnSequenceFlowElement(Process mainProcess, String sourceId, String targetId, String conditionExpression) {
400 394 SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId);
401 395 if (StrUtil.isNotEmpty(conditionExpression)) {
402 396 sequenceFlow.setConditionExpression(conditionExpression);
... ... @@ -407,20 +401,20 @@ public class BpmnModelUtils {
407 401  
408 402 private static void addBpmnFlowNode(Process mainProcess, BpmSimpleModelNodeVO simpleModelNode) {
409 403 // 节点为 null 退出
410   - if (simpleModelNode == null) {
  404 + if (simpleModelNode == null || simpleModelNode.getId() == null) {
411 405 return;
412 406 }
413 407 BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(simpleModelNode.getType());
414 408 Assert.notNull(nodeType, "模型节点类型不支持");
415   - switch(nodeType){
416   - case START_NODE :
  409 + switch (nodeType) {
  410 + case START_NODE:
417 411 addBpmnStartEventNode(mainProcess, simpleModelNode);
418 412 break;
419   - case START_USER_NODE :
420   - case APPROVE_USER_NODE :
  413 + case START_USER_NODE:
  414 + case APPROVE_USER_NODE:
421 415 addBpmnUserTaskEventNode(mainProcess, simpleModelNode);
422 416 break;
423   - default : {
  417 + default: {
424 418 // TODO 其它节点类型的实现
425 419 }
426 420 }
... ... @@ -454,17 +448,16 @@ public class BpmnModelUtils {
454 448 UserTask userTask = new UserTask();
455 449 userTask.setId(node.getId());
456 450 userTask.setName(node.getName());
457   - Integer candidateStrategy = MapUtil.getInt(node.getAttributes(),BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY);
458   - List<Integer> candidateParam = MapUtil.get(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_PARAM, new TypeReference<>() {});
  451 + Integer candidateStrategy = MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY);
459 452 // 添加自定义属性
460 453 addExtensionAttribute(userTask, BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,
461 454 candidateStrategy == null ? null : String.valueOf(candidateStrategy));
462 455 addExtensionAttribute(userTask, BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM,
463   - CollUtil.join(candidateParam, ","));
  456 + MapUtil.getStr(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));
464 457 mainProcess.addFlowElement(userTask);
465 458 }
466 459  
467   - private static void addExtensionAttribute(FlowElement element, String namespace, String name, String value) {
  460 + private static void addExtensionAttribute(FlowElement element, String namespace, String name, String value) {
468 461 if (value == null) {
469 462 return;
470 463 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmSimpleModelService.java
1 1 package cn.iocoder.yudao.module.bpm.service.definition;
2 2  
  3 +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelNodeVO;
3 4 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelSaveReqVO;
4 5 import jakarta.validation.Valid;
5 6  
... ... @@ -15,4 +16,11 @@ public interface BpmSimpleModelService {
15 16 * @param reqVO 请求信息
16 17 */
17 18 Boolean saveSimpleModel(@Valid BpmSimpleModelSaveReqVO reqVO);
  19 +
  20 + /**
  21 + * 获取仿钉钉流程设计模型结构
  22 + * @param modelId 流程模型编号
  23 + * @return 仿钉钉流程设计模型结构
  24 + */
  25 + BpmSimpleModelNodeVO getSimpleModel(String modelId);
18 26 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmSimpleModelServiceImpl.java
1 1 package cn.iocoder.yudao.module.bpm.service.definition;
2 2  
3   -import cn.hutool.core.util.ArrayUtil;
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
  5 +import cn.hutool.core.map.MapUtil;
  6 +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelNodeVO;
4 7 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelSaveReqVO;
  8 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
  9 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
5 10 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
6 11 import jakarta.annotation.Resource;
7   -import org.flowable.bpmn.model.BpmnModel;
  12 +import org.flowable.bpmn.model.*;
8 13 import org.flowable.engine.repository.Model;
9 14 import org.springframework.stereotype.Service;
10 15 import org.springframework.validation.annotation.Validated;
11 16  
  17 +import java.util.List;
  18 +import java.util.Map;
  19 +
12 20 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  21 +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT;
13 22 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.MODEL_NOT_EXISTS;
  23 +import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.START_NODE;
  24 +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_CANDIDATE_PARAM;
  25 +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY;
14 26  
15 27 /**
16 28 * 仿钉钉流程设计 Service 实现类
... ... @@ -29,16 +41,124 @@ public class BpmSimpleModelServiceImpl implements BpmSimpleModelService {
29 41 if (model == null) {
30 42 throw exception(MODEL_NOT_EXISTS);
31 43 }
32   - byte[] bpmnBytes = bpmModelService.getModelBpmnXML(reqVO.getModelId());
  44 +// byte[] bpmnBytes = bpmModelService.getModelBpmnXML(reqVO.getModelId());
  45 +// if (ArrayUtil.isEmpty(bpmnBytes)) {
  46 +// // BPMN XML 不存在。新增
  47 +// BpmnModel bpmnModel = BpmnModelUtils.convertSimpleModelToBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModelBody());
  48 +// bpmModelService.saveModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel));
  49 +// return Boolean.TRUE;
  50 +// } else {
  51 +// // TODO BPMN XML 已经存在。如何修改 ??
  52 +// return Boolean.FALSE;
  53 +// }
  54 + // 暂时直接修改
  55 + BpmnModel bpmnModel = BpmnModelUtils.convertSimpleModelToBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModelBody());
  56 + bpmModelService.saveModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel));
  57 + return Boolean.TRUE;
  58 + }
  59 +
  60 + @Override
  61 + public BpmSimpleModelNodeVO getSimpleModel(String modelId) {
  62 + Model model = bpmModelService.getModel(modelId);
  63 + if (model == null) {
  64 + throw exception(MODEL_NOT_EXISTS);
  65 + }
  66 + byte[] bpmnBytes = bpmModelService.getModelBpmnXML(modelId);
  67 + BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);
  68 + return convertBpmnModelToSimpleModel(bpmnModel);
  69 +
  70 + }
  71 +
  72 + /**
  73 + * Bpmn Model 转换成 仿钉钉流程设计模型数据结构(json) 待完善
  74 + *
  75 + * @param bpmnModel Bpmn Model
  76 + * @return 仿钉钉流程设计模型数据结构
  77 + */
  78 + private BpmSimpleModelNodeVO convertBpmnModelToSimpleModel(BpmnModel bpmnModel) {
  79 + if (bpmnModel == null) {
  80 + return null;
  81 + }
  82 + StartEvent startEvent = BpmnModelUtils.getStartEvent(bpmnModel);
  83 + if (startEvent == null) {
  84 + return null;
  85 + }
  86 + BpmSimpleModelNodeVO rootNode = new BpmSimpleModelNodeVO();
  87 + rootNode.setType(START_NODE.getType());
  88 + rootNode.setId(startEvent.getId());
  89 + rootNode.setName(startEvent.getName());
  90 + recursiveBuildSimpleModelNode(startEvent, rootNode);
  91 + return rootNode;
  92 +
  93 + }
33 94  
34   - if (ArrayUtil.isEmpty(bpmnBytes)) {
35   - // BPMN XML 不存在。新增
36   - BpmnModel bpmnModel = BpmnModelUtils.convertSimpleModelToBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModelBody());
37   - bpmModelService.saveModelBpmnXml(model.getId(),BpmnModelUtils.getBpmnXml(bpmnModel));
38   - return Boolean.TRUE;
39   - } else {
40   - // TODO BPMN XML 已经存在。如何修改 ??
41   - return Boolean.FALSE;
  95 + private void recursiveBuildSimpleModelNode(FlowNode currentFlowNode, BpmSimpleModelNodeVO currentSimpleModeNode) {
  96 + BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(currentSimpleModeNode.getType());
  97 + Assert.notNull(nodeType, "节点类型不支持");
  98 + // 校验节点是否支持转仿钉钉的流程模型
  99 + List<SequenceFlow> outgoingFlows = validateCanConvertSimpleNode(nodeType, currentFlowNode);
  100 + if (CollUtil.isEmpty(outgoingFlows) || outgoingFlows.get(0).getTargetFlowElement() == null) {
  101 + return;
  102 + }
  103 + FlowElement targetElement = outgoingFlows.get(0).getTargetFlowElement();
  104 + // 如果是 EndEvent 直接退出
  105 + if (targetElement instanceof EndEvent) {
  106 + return;
  107 + }
  108 + if (targetElement instanceof UserTask) {
  109 + BpmSimpleModelNodeVO childNode = convertUserTaskToSimpleModelNode((UserTask) targetElement);
  110 + currentSimpleModeNode.setChildNode(childNode);
  111 + recursiveBuildSimpleModelNode((FlowNode) targetElement, childNode);
  112 + }
  113 + // TODO 其它节点类型待实现
  114 + }
  115 +
  116 +
  117 + private BpmSimpleModelNodeVO convertUserTaskToSimpleModelNode(UserTask userTask) {
  118 + BpmSimpleModelNodeVO simpleModelNodeVO = new BpmSimpleModelNodeVO();
  119 + simpleModelNodeVO.setType(BpmSimpleModelNodeType.APPROVE_USER_NODE.getType());
  120 + simpleModelNodeVO.setName(userTask.getName());
  121 + simpleModelNodeVO.setId(userTask.getId());
  122 + Map<String, Object> attributes = MapUtil.newHashMap();
  123 + // TODO 暂时是普通审批,需要加会签
  124 + attributes.put("approveMethod", 1);
  125 + attributes.computeIfAbsent(USER_TASK_CANDIDATE_STRATEGY, (key) -> BpmnModelUtils.parseCandidateStrategy(userTask));
  126 + attributes.computeIfAbsent(USER_TASK_CANDIDATE_PARAM, (key) -> BpmnModelUtils.parseCandidateParam(userTask));
  127 + simpleModelNodeVO.setAttributes(attributes);
  128 + return simpleModelNodeVO;
  129 + }
  130 +
  131 + private List<SequenceFlow> validateCanConvertSimpleNode(BpmSimpleModelNodeType nodeType, FlowNode currentFlowNode) {
  132 + switch (nodeType) {
  133 + case START_NODE:
  134 + case APPROVE_USER_NODE: {
  135 + List<SequenceFlow> outgoingFlows = currentFlowNode.getOutgoingFlows();
  136 + if (CollUtil.isNotEmpty(outgoingFlows) && outgoingFlows.size() > 1) {
  137 + throw exception(CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT);
  138 + }
  139 + validIsSupportFlowNode(outgoingFlows.get(0).getTargetFlowElement());
  140 + return outgoingFlows;
  141 + }
  142 + default: {
  143 + // TODO 其它节点类型待实现
  144 + throw exception(CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT);
  145 + }
  146 + }
  147 + }
  148 +
  149 + private void validIsSupportFlowNode(FlowElement targetElement) {
  150 + if (targetElement == null) {
  151 + return;
  152 + }
  153 + boolean isSupport = false;
  154 + for (Class<? extends FlowNode> item : BpmnModelConstants.SUPPORT_CONVERT_SIMPLE_FlOW_NODES) {
  155 + if (item.isInstance(targetElement)) {
  156 + isSupport = true;
  157 + break;
  158 + }
  159 + }
  160 + if (!isSupport) {
  161 + throw exception(CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT);
42 162 }
43 163 }
44 164 }
... ...