Commit 48aef10d2dc2663587373779a633320001fb902c

Authored by 芋道源码
Committed by Gitee
1 parent decdfb2a

回退 'Pull Request !937 : feat: 客户成交周期分析(按区域、按产品)'

Showing 23 changed files with 27 additions and 862 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
@@ -75,8 +75,4 @@ public interface ErrorCodeConstants { @@ -75,8 +75,4 @@ public interface ErrorCodeConstants {
75 // ========== BPM 流程表达式 1-009-014-000 ========== 75 // ========== BPM 流程表达式 1-009-014-000 ==========
76 ErrorCode PROCESS_EXPRESSION_NOT_EXISTS = new ErrorCode(1_009_014_000, "流程表达式不存在"); 76 ErrorCode PROCESS_EXPRESSION_NOT_EXISTS = new ErrorCode(1_009_014_000, "流程表达式不存在");
77 77
78 - // ========== BPM 仿钉钉流程设计器 1-009-015-000 ==========  
79 - // TODO @芋艿:这个错误码,需要关注下  
80 - ErrorCode CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT = new ErrorCode(1_009_015_000, "该流程模型不支持仿钉钉设计流程");  
81 -  
82 } 78 }
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java deleted
1 -package cn.iocoder.yudao.module.bpm.enums.definition;  
2 -  
3 -import cn.hutool.core.util.ArrayUtil;  
4 -import cn.iocoder.yudao.framework.common.core.IntArrayValuable;  
5 -import lombok.AllArgsConstructor;  
6 -import lombok.Getter;  
7 -  
8 -import java.util.Arrays;  
9 -import java.util.Objects;  
10 -  
11 -/**  
12 - * 仿钉钉的流程器设计器的模型节点类型  
13 - *  
14 - * @author jason  
15 - */  
16 -@Getter  
17 -@AllArgsConstructor  
18 -public enum BpmSimpleModelNodeType implements IntArrayValuable {  
19 -  
20 - // TODO @jaosn:-1、0、1、4、-2 是前端已经定义好的么?感觉未来可以考虑搞成和 BPMN 尽量一致的单词哈;类似 usertask 用户审批;  
21 - START_EVENT_NODE(0, "开始节点"),  
22 - APPROVE_USER_NODE (1, "审批人节点"),  
23 - // 抄送人节点、对应 BPMN 的 ScriptTask. 使用ScriptTask 原因。好像 ServiceTask 自定义属性不能写入 XML  
24 - SCRIPT_TASK_NODE(2, "抄送人节点"),  
25 - EXCLUSIVE_GATEWAY_NODE(4, "排他网关"),  
26 - END_EVENT_NODE(-2, "结束节点");  
27 -  
28 - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModelNodeType::getType).toArray();  
29 -  
30 - private final Integer type;  
31 - private final String name;  
32 -  
33 - public static boolean isGatewayNode(Integer type) {  
34 - // TODO 后续增加并行网关的支持  
35 - return Objects.equals(EXCLUSIVE_GATEWAY_NODE.getType(), type);  
36 - }  
37 -  
38 - public static BpmSimpleModelNodeType valueOf(Integer type) {  
39 - return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values());  
40 - }  
41 -  
42 - @Override  
43 - public int[] array() {  
44 - return ARRAYS;  
45 - }  
46 -  
47 -}  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmSimpleModelController.java deleted
1 -package cn.iocoder.yudao.module.bpm.controller.admin.definition;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.CommonResult;  
4 -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelNodeVO;  
5 -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelSaveReqVO;  
6 -import cn.iocoder.yudao.module.bpm.service.definition.BpmSimpleModelService;  
7 -import io.swagger.v3.oas.annotations.Operation;  
8 -import io.swagger.v3.oas.annotations.Parameter;  
9 -import io.swagger.v3.oas.annotations.tags.Tag;  
10 -import jakarta.annotation.Resource;  
11 -import jakarta.validation.Valid;  
12 -import org.springframework.security.access.prepost.PreAuthorize;  
13 -import org.springframework.web.bind.annotation.*;  
14 -  
15 -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;  
16 -  
17 -// TODO @芋艿:后续考虑下,怎么放这个 Controller  
18 -@Tag(name = "管理后台 - BPM 仿钉钉流程设计器")  
19 -@RestController  
20 -@RequestMapping("/bpm/simple")  
21 -public class BpmSimpleModelController {  
22 - @Resource  
23 - private BpmSimpleModelService bpmSimpleModelService;  
24 -  
25 - @PostMapping("/save")  
26 - @Operation(summary = "保存仿钉钉流程设计模型")  
27 - @PreAuthorize("@ss.hasPermission('bpm:model:update')")  
28 - public CommonResult<Boolean> saveSimpleModel(@Valid @RequestBody BpmSimpleModelSaveReqVO reqVO) {  
29 - return success(bpmSimpleModelService.saveSimpleModel(reqVO));  
30 - }  
31 -  
32 - @GetMapping("/get")  
33 - @Operation(summary = "获得仿钉钉流程设计模型")  
34 - @Parameter(name = "modelId", description = "流程模型编号", required = true, example = "a2c5eee0-eb6c-11ee-abf4-0c37967c420a")  
35 - public CommonResult<BpmSimpleModelNodeVO> getSimpleModel(@RequestParam("modelId") String modelId){  
36 - return success(bpmSimpleModelService.getSimpleModel(modelId));  
37 - }  
38 -  
39 -}  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/simple/BpmSimpleModelNodeVO.java deleted
1 -package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple;  
2 -  
3 -import cn.iocoder.yudao.framework.common.validation.InEnum;  
4 -import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;  
5 -import com.fasterxml.jackson.annotation.JsonInclude;  
6 -import io.swagger.v3.oas.annotations.media.Schema;  
7 -import jakarta.validation.constraints.NotEmpty;  
8 -import jakarta.validation.constraints.NotNull;  
9 -import lombok.Data;  
10 -  
11 -import java.util.List;  
12 -import java.util.Map;  
13 -  
14 -@Schema(description = "管理后台 - 仿钉钉流程设计模型节点 VO")  
15 -@Data  
16 -@JsonInclude(JsonInclude.Include.NON_NULL)  
17 -public class BpmSimpleModelNodeVO {  
18 -  
19 - @Schema(description = "模型节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartEvent_1")  
20 - @NotEmpty(message = "模型节点编号不能为空")  
21 - private String id;  
22 -  
23 - @Schema(description = "模型节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
24 - @NotNull(message = "模型节点类型不能为空")  
25 - @InEnum(BpmSimpleModelNodeType.class)  
26 - private Integer type;  
27 -  
28 - @Schema(description = "模型节点名称", example = "领导审批")  
29 - private String name;  
30 -  
31 - @Schema(description = "孩子节点")  
32 - private BpmSimpleModelNodeVO childNode;  
33 -  
34 - @Schema(description = "网关节点的条件节点")  
35 - private List<BpmSimpleModelNodeVO> conditionNodes;  
36 -  
37 - @Schema(description = "节点的属性")  
38 - private Map<String, Object> attributes;  
39 -  
40 -}  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/simple/BpmSimpleModelSaveReqVO.java deleted
1 -package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import jakarta.validation.Valid;  
5 -import jakarta.validation.constraints.NotEmpty;  
6 -import jakarta.validation.constraints.NotNull;  
7 -import lombok.Data;  
8 -  
9 -// TODO @芋艿:或许挪到 model 里的 simple 包  
10 -@Schema(description = "管理后台 - 仿钉钉流程设计模型的新增/修改 Request VO")  
11 -@Data  
12 -public class BpmSimpleModelSaveReqVO {  
13 -  
14 - @Schema(description = "流程模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
15 - @NotEmpty(message = "流程模型编号不能为空")  
16 - private String modelId; // 对应 Flowable act_re_model 表 ID_ 字段  
17 -  
18 - @Schema(description = "仿钉钉流程设计模型对象", requiredMode = Schema.RequiredMode.REQUIRED)  
19 - @NotNull(message = "仿钉钉流程设计模型对象不能为空")  
20 - @Valid  
21 - private BpmSimpleModelNodeVO simpleModelBody;  
22 -  
23 -}  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums; 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 -  
8 /** 3 /**
9 * BPMN XML 常量信息 4 * BPMN XML 常量信息
10 * 5 *
@@ -28,15 +23,4 @@ public interface BpmnModelConstants { @@ -28,15 +23,4 @@ public interface BpmnModelConstants {
28 */ 23 */
29 String USER_TASK_CANDIDATE_PARAM = "candidateParam"; 24 String USER_TASK_CANDIDATE_PARAM = "candidateParam";
30 25
31 - // TODO @芋艿:这里后面得关注下;  
32 - /**  
33 - * BPMN End Event 节点 Id, 用于后端生成 End Event 节点  
34 - */  
35 - String END_EVENT_ID = "EndEvent_1";  
36 -  
37 - /**  
38 - * 支持转仿钉钉设计模型的 Bpmn 节点  
39 - */  
40 - Set<Class<? extends FlowNode>> SUPPORT_CONVERT_SIMPLE_FlOW_NODES = ImmutableSet.of(UserTask.class, EndEvent.class);  
41 -  
42 } 26 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.util; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 -import cn.hutool.core.lang.Assert;  
5 -import cn.hutool.core.map.MapUtil;  
6 import cn.hutool.core.util.ArrayUtil; 4 import cn.hutool.core.util.ArrayUtil;
7 -import cn.hutool.core.util.StrUtil;  
8 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 5 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
9 -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelNodeVO;  
10 -import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;  
11 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; 6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
12 -import org.flowable.bpmn.BpmnAutoLayout;  
13 import org.flowable.bpmn.converter.BpmnXMLConverter; 7 import org.flowable.bpmn.converter.BpmnXMLConverter;
14 import org.flowable.bpmn.model.Process; 8 import org.flowable.bpmn.model.Process;
15 import org.flowable.bpmn.model.*; 9 import org.flowable.bpmn.model.*;
16 -import org.flowable.common.engine.impl.scripting.ScriptingEngines;  
17 import org.flowable.common.engine.impl.util.io.BytesStreamSource; 10 import org.flowable.common.engine.impl.util.io.BytesStreamSource;
18 11
19 -import java.util.ArrayList;  
20 -import java.util.HashSet;  
21 -import java.util.List;  
22 -import java.util.Set;  
23 -  
24 -import static org.flowable.bpmn.constants.BpmnXMLConstants.*; 12 +import java.util.*;
25 13
26 /** 14 /**
27 * 流程模型转操作工具类 15 * 流程模型转操作工具类
28 */ 16 */
29 public class BpmnModelUtils { 17 public class BpmnModelUtils {
30 18
31 - public static final String BPMN_SIMPLE_COPY_EXECUTION_SCRIPT = "#{bpmSimpleNodeService.copy(execution)}";  
32 -  
33 public static Integer parseCandidateStrategy(FlowElement userTask) { 19 public static Integer parseCandidateStrategy(FlowElement userTask) {
34 return NumberUtils.parseInt(userTask.getAttributeValue( 20 return NumberUtils.parseInt(userTask.getAttributeValue(
35 BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); 21 BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
@@ -340,196 +326,4 @@ public class BpmnModelUtils { @@ -340,196 +326,4 @@ public class BpmnModelUtils {
340 return userTaskList; 326 return userTaskList;
341 } 327 }
342 328
343 - // ========== TODO 芋艿:这里得捉摸下; ==========  
344 -  
345 - /**  
346 - * 仿钉钉流程设计模型数据结构(json) 转换成 Bpmn Model (待完善)  
347 - *  
348 - * @param processId 流程标识  
349 - * @param processName 流程名称  
350 - * @param simpleModelNode 仿钉钉流程设计模型数据结构  
351 - * @return Bpmn Model  
352 - */  
353 - public static BpmnModel convertSimpleModelToBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) {  
354 - BpmnModel bpmnModel = new BpmnModel();  
355 - Process mainProcess = new Process();  
356 - mainProcess.setId(processId);  
357 - mainProcess.setName(processName);  
358 - mainProcess.setExecutable(Boolean.TRUE);  
359 - bpmnModel.addProcess(mainProcess);  
360 - // 前端模型数据结构。 有 start event 节点. 没有 end event 节点。  
361 - // 添加 FlowNode  
362 - addBpmnFlowNode(mainProcess, simpleModelNode);  
363 - // 单独添加 end event 节点  
364 - addBpmnEndEventNode(mainProcess);  
365 - // 添加节点之间的连线 Sequence Flow  
366 - addBpmnSequenceFlow(mainProcess, simpleModelNode, BpmnModelConstants.END_EVENT_ID);  
367 - // 自动布局  
368 - new BpmnAutoLayout(bpmnModel).execute();  
369 - return bpmnModel;  
370 - }  
371 -  
372 - private static void addBpmnSequenceFlow(Process mainProcess, BpmSimpleModelNodeVO node, String endId) {  
373 - // 节点为 null 退出  
374 - if (node == null || node.getId() == null) {  
375 - return;  
376 - }  
377 - BpmSimpleModelNodeVO childNode = node.getChildNode();  
378 - // 如果不是网关节点、且后续节点为 null. 添加与结束节点的连线  
379 - if (!BpmSimpleModelNodeType.isGatewayNode(node.getType()) && (childNode == null || childNode.getId() == null)) {  
380 - addBpmnSequenceFlowElement(mainProcess, node.getId(), endId, null, null);  
381 - return;  
382 - }  
383 - BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType());  
384 - Assert.notNull(nodeType, "模型节点类型不支持");  
385 - switch (nodeType) {  
386 - case START_EVENT_NODE:  
387 - case APPROVE_USER_NODE:  
388 - case SCRIPT_TASK_NODE: {  
389 - addBpmnSequenceFlowElement(mainProcess, node.getId(), childNode.getId(), null, null);  
390 - // 递归调用后续节点  
391 - addBpmnSequenceFlow(mainProcess, childNode, endId);  
392 - break;  
393 - }  
394 - case EXCLUSIVE_GATEWAY_NODE: {  
395 - String gateWayEndId = (childNode == null || childNode.getId() == null) ? BpmnModelConstants.END_EVENT_ID : childNode.getId();  
396 - List<BpmSimpleModelNodeVO> conditionNodes = node.getConditionNodes();  
397 - Assert.notEmpty(conditionNodes, "网关节点的条件节点不能为空");  
398 - for (int i = 0; i < conditionNodes.size(); i++) {  
399 - BpmSimpleModelNodeVO item = conditionNodes.get(i);  
400 - BpmSimpleModelNodeVO nextNodeOnCondition = item.getChildNode();  
401 - if (nextNodeOnCondition != null && nextNodeOnCondition.getId() != null) {  
402 - addBpmnSequenceFlowElement(mainProcess, node.getId(), nextNodeOnCondition.getId(),  
403 - String.format("%s_SequenceFlow_%d", node.getId(), i + 1), null);  
404 - addBpmnSequenceFlow(mainProcess, nextNodeOnCondition, gateWayEndId);  
405 - } else {  
406 - addBpmnSequenceFlowElement(mainProcess, node.getId(), gateWayEndId,  
407 - String.format("%s_SequenceFlow_%d", node.getId(), i + 1), null);  
408 - }  
409 - }  
410 - // 递归调用后续节点  
411 - addBpmnSequenceFlow(mainProcess, childNode, endId);  
412 - break;  
413 - }  
414 - default: {  
415 - // TODO 其它节点类型的实现  
416 - }  
417 - }  
418 -  
419 - }  
420 -  
421 - private static void addBpmnSequenceFlowElement(Process mainProcess, String sourceId, String targetId, String seqFlowId, String conditionExpression) {  
422 - SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId);  
423 - if (StrUtil.isNotEmpty(conditionExpression)) {  
424 - sequenceFlow.setConditionExpression(conditionExpression);  
425 - }  
426 - if (StrUtil.isNotEmpty(seqFlowId)) {  
427 - sequenceFlow.setId(seqFlowId);  
428 - }  
429 - mainProcess.addFlowElement(sequenceFlow);  
430 - }  
431 -  
432 - private static void addBpmnFlowNode(Process mainProcess, BpmSimpleModelNodeVO simpleModelNode) {  
433 - // 节点为 null 退出  
434 - if (simpleModelNode == null || simpleModelNode.getId() == null) {  
435 - return;  
436 - }  
437 - BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(simpleModelNode.getType());  
438 - Assert.notNull(nodeType, "模型节点类型不支持");  
439 - switch (nodeType) {  
440 - case START_EVENT_NODE:  
441 - addBpmnStartEventNode(mainProcess, simpleModelNode);  
442 - break;  
443 - case APPROVE_USER_NODE:  
444 - addBpmnUserTaskNode(mainProcess, simpleModelNode);  
445 - break;  
446 - case SCRIPT_TASK_NODE:  
447 - addBpmnScriptTaSskNode(mainProcess, simpleModelNode);  
448 - break;  
449 - case EXCLUSIVE_GATEWAY_NODE:  
450 - addBpmnExclusiveGatewayNode(mainProcess, simpleModelNode);  
451 - break;  
452 - default: {  
453 - // TODO 其它节点类型的实现  
454 - }  
455 - }  
456 -  
457 - // 如果不是网关类型的接口, 并且chileNode为空退出  
458 - if (!BpmSimpleModelNodeType.isGatewayNode(simpleModelNode.getType()) && simpleModelNode.getChildNode() == null) {  
459 - return;  
460 - }  
461 -  
462 - // 如果是网关类型接口. 递归添加条件节点  
463 - if (BpmSimpleModelNodeType.isGatewayNode(simpleModelNode.getType()) && ArrayUtil.isNotEmpty(simpleModelNode.getConditionNodes())) {  
464 - for (BpmSimpleModelNodeVO node : simpleModelNode.getConditionNodes()) {  
465 - addBpmnFlowNode(mainProcess, node.getChildNode());  
466 - }  
467 - }  
468 -  
469 - // chileNode不为空,递归添加子节点  
470 - if (simpleModelNode.getChildNode() != null) {  
471 - addBpmnFlowNode(mainProcess, simpleModelNode.getChildNode());  
472 - }  
473 - }  
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 -  
494 - private static void addBpmnExclusiveGatewayNode(Process mainProcess, BpmSimpleModelNodeVO node) {  
495 - Assert.notEmpty(node.getConditionNodes(), "网关节点的条件节点不能为空");  
496 - ExclusiveGateway exclusiveGateway = new ExclusiveGateway();  
497 - exclusiveGateway.setId(node.getId());  
498 - // 条件节点的最后一个条件为 网关的 default sequence flow  
499 - exclusiveGateway.setDefaultFlow(String.format("%s_SequenceFlow_%d", node.getId(), node.getConditionNodes().size()));  
500 - mainProcess.addFlowElement(exclusiveGateway);  
501 - }  
502 -  
503 - private static void addBpmnEndEventNode(Process mainProcess) {  
504 - EndEvent endEvent = new EndEvent();  
505 - endEvent.setId(BpmnModelConstants.END_EVENT_ID);  
506 - endEvent.setName("结束");  
507 - mainProcess.addFlowElement(endEvent);  
508 - }  
509 -  
510 - private static void addBpmnUserTaskNode(Process mainProcess, BpmSimpleModelNodeVO node) {  
511 - UserTask userTask = new UserTask();  
512 - userTask.setId(node.getId());  
513 - userTask.setName(node.getName());  
514 - addExtensionAttributes(node, userTask);  
515 - mainProcess.addFlowElement(userTask);  
516 - }  
517 -  
518 - private static void addExtensionAttributes(FlowElement element, String namespace, String name, String value) {  
519 - if (value == null) {  
520 - return;  
521 - }  
522 - ExtensionAttribute extensionAttribute = new ExtensionAttribute(name, value);  
523 - extensionAttribute.setNamespace(namespace);  
524 - extensionAttribute.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);  
525 - element.addAttribute(extensionAttribute);  
526 - }  
527 -  
528 - private static void addBpmnStartEventNode(Process mainProcess, BpmSimpleModelNodeVO node) {  
529 - StartEvent startEvent = new StartEvent();  
530 - startEvent.setId(node.getId());  
531 - startEvent.setName(node.getName());  
532 - mainProcess.addFlowElement(startEvent);  
533 - }  
534 -  
535 } 329 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
@@ -46,30 +46,6 @@ public interface BpmModelService { @@ -46,30 +46,6 @@ public interface BpmModelService {
46 */ 46 */
47 byte[] getModelBpmnXML(String id); 47 byte[] getModelBpmnXML(String id);
48 48
49 -  
50 - /**  
51 - * 保存流程模型的 BPMN XML  
52 - *  
53 - * @param id 编号  
54 - * @param xmlBytes BPMN XML bytes  
55 - */  
56 - // TODO @芋艿:可能要关注下;  
57 - void saveModelBpmnXml(String id, byte[] xmlBytes);  
58 -  
59 - /**  
60 - * 获得仿钉钉快搭模型的 JSON 数据  
61 - * @param id 编号  
62 - * @return JSON bytes  
63 - */  
64 - byte[] getModelSimpleJson(String id);  
65 -  
66 - /**  
67 - * 保存仿钉钉快搭模型的 JSON 数据  
68 - * @param id 编号  
69 - * @param jsonBytes JSON bytes  
70 - */  
71 - void saveModelSimpleJson(String id, byte[] jsonBytes);  
72 -  
73 /** 49 /**
74 * 修改流程模型 50 * 修改流程模型
75 * 51 *
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
1 package cn.iocoder.yudao.module.bpm.service.definition; 1 package cn.iocoder.yudao.module.bpm.service.definition;
2 2
3 -import cn.hutool.core.util.ArrayUtil;  
4 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
@@ -104,7 +103,7 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -104,7 +103,7 @@ public class BpmModelServiceImpl implements BpmModelService {
104 // 保存流程定义 103 // 保存流程定义
105 repositoryService.saveModel(model); 104 repositoryService.saveModel(model);
106 // 保存 BPMN XML 105 // 保存 BPMN XML
107 - saveModelBpmnXml(model.getId(), StrUtil.utf8Bytes(bpmnXml)); 106 + saveModelBpmnXml(model, bpmnXml);
108 return model.getId(); 107 return model.getId();
109 } 108 }
110 109
@@ -122,7 +121,7 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -122,7 +121,7 @@ public class BpmModelServiceImpl implements BpmModelService {
122 // 更新模型 121 // 更新模型
123 repositoryService.saveModel(model); 122 repositoryService.saveModel(model);
124 // 更新 BPMN XML 123 // 更新 BPMN XML
125 - saveModelBpmnXml(model.getId(), StrUtil.utf8Bytes(updateReqVO.getBpmnXml())); 124 + saveModelBpmnXml(model, updateReqVO.getBpmnXml());
126 } 125 }
127 126
128 @Override 127 @Override
@@ -237,25 +236,11 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -237,25 +236,11 @@ public class BpmModelServiceImpl implements BpmModelService {
237 } 236 }
238 } 237 }
239 238
240 - @Override  
241 - public void saveModelBpmnXml(String id, byte[] xmlBytes) {  
242 - if (ArrayUtil.isEmpty(xmlBytes)) {  
243 - return;  
244 - }  
245 - repositoryService.addModelEditorSource(id, xmlBytes);  
246 - }  
247 -  
248 - @Override  
249 - public byte[] getModelSimpleJson(String id) {  
250 - return repositoryService.getModelEditorSourceExtra(id);  
251 - }  
252 -  
253 - @Override  
254 - public void saveModelSimpleJson(String id, byte[] jsonBytes) {  
255 - if (ArrayUtil.isEmpty(jsonBytes)) { 239 + private void saveModelBpmnXml(Model model, String bpmnXml) {
  240 + if (StrUtil.isEmpty(bpmnXml)) {
256 return; 241 return;
257 } 242 }
258 - repositoryService.addModelEditorSourceExtra(id, jsonBytes); 243 + repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(bpmnXml));
259 } 244 }
260 245
261 /** 246 /**
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmSimpleModelService.java deleted
1 -package cn.iocoder.yudao.module.bpm.service.definition;  
2 -  
3 -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelNodeVO;  
4 -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelSaveReqVO;  
5 -import jakarta.validation.Valid;  
6 -  
7 -/**  
8 - * 仿钉钉流程设计 Service 接口  
9 - *  
10 - * @author jason  
11 - */  
12 -public interface BpmSimpleModelService {  
13 -  
14 - /**  
15 - * 保存仿钉钉流程设计模型  
16 - *  
17 - * @param reqVO 请求信息  
18 - */  
19 - Boolean saveSimpleModel(@Valid BpmSimpleModelSaveReqVO reqVO);  
20 -  
21 - /**  
22 - * 获取仿钉钉流程设计模型结构  
23 - *  
24 - * @param modelId 流程模型编号  
25 - * @return 仿钉钉流程设计模型结构  
26 - */  
27 - BpmSimpleModelNodeVO getSimpleModel(String modelId);  
28 -  
29 -}  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmSimpleModelServiceImpl.java deleted
1 -package cn.iocoder.yudao.module.bpm.service.definition;  
2 -  
3 -import cn.hutool.core.collection.CollUtil;  
4 -import cn.hutool.core.lang.Assert;  
5 -import cn.hutool.core.map.MapUtil;  
6 -import cn.hutool.core.util.StrUtil;  
7 -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;  
8 -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelNodeVO;  
9 -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelSaveReqVO;  
10 -import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;  
11 -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;  
12 -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;  
13 -import jakarta.annotation.Resource;  
14 -import org.flowable.bpmn.model.*;  
15 -import org.flowable.engine.repository.Model;  
16 -import org.springframework.stereotype.Service;  
17 -import org.springframework.validation.annotation.Validated;  
18 -  
19 -import java.util.List;  
20 -import java.util.Map;  
21 -  
22 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;  
23 -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT;  
24 -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.MODEL_NOT_EXISTS;  
25 -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.START_EVENT_NODE;  
26 -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_CANDIDATE_PARAM;  
27 -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY;  
28 -  
29 -/**  
30 - * 仿钉钉流程设计 Service 实现类  
31 - *  
32 - * @author jason  
33 - */  
34 -@Service  
35 -@Validated  
36 -public class BpmSimpleModelServiceImpl implements BpmSimpleModelService {  
37 -  
38 - @Resource  
39 - private BpmModelService bpmModelService;  
40 -  
41 - @Override  
42 - public Boolean saveSimpleModel(BpmSimpleModelSaveReqVO reqVO) {  
43 - Model model = bpmModelService.getModel(reqVO.getModelId());  
44 - if (model == null) {  
45 - throw exception(MODEL_NOT_EXISTS);  
46 - }  
47 -// byte[] bpmnBytes = bpmModelService.getModelBpmnXML(reqVO.getModelId());  
48 -// if (ArrayUtil.isEmpty(bpmnBytes)) {  
49 -// // BPMN XML 不存在。新增  
50 -// BpmnModel bpmnModel = BpmnModelUtils.convertSimpleModelToBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModelBody());  
51 -// bpmModelService.saveModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel));  
52 -// return Boolean.TRUE;  
53 -// } else {  
54 -// // TODO BPMN XML 已经存在。如何修改 ?? TODO add by 芋艿:感觉一个流程,只能二选一,要么 bpmn、要么 simple  
55 -// return Boolean.FALSE;  
56 -// }  
57 - // 1. JSON 转换成 bpmnModel  
58 - BpmnModel bpmnModel = BpmnModelUtils.convertSimpleModelToBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModelBody());  
59 - // 2.1 保存 Bpmn XML  
60 - bpmModelService.saveModelBpmnXml(model.getId(), StrUtil.utf8Bytes(BpmnModelUtils.getBpmnXml(bpmnModel)));  
61 - // 2.2 保存 JSON 数据  
62 - bpmModelService.saveModelSimpleJson(model.getId(), JsonUtils.toJsonByte(reqVO.getSimpleModelBody()));  
63 - return Boolean.TRUE;  
64 - }  
65 -  
66 - @Override  
67 - public BpmSimpleModelNodeVO getSimpleModel(String modelId) {  
68 - Model model = bpmModelService.getModel(modelId);  
69 - if (model == null) {  
70 - throw exception(MODEL_NOT_EXISTS);  
71 - }  
72 - // 暂时不用 bpmn 转 json, 有点复杂,  
73 - // 通过 ACT_RE_MODEL 表 EDITOR_SOURCE_EXTRA_VALUE_ID_ 获取 仿钉钉快搭模型的JSON 数据  
74 - byte[] jsonBytes = bpmModelService.getModelSimpleJson(model.getId());  
75 - return JsonUtils.parseObject(jsonBytes, BpmSimpleModelNodeVO.class);  
76 - }  
77 -  
78 - // TODO @jason:一般要支持这个么?感觉 bpmn 转 json 支持会不会太复杂。可以优先级低一点,做下调研~  
79 -  
80 - /**  
81 - * Bpmn Model 转换成 仿钉钉流程设计模型数据结构(json) 待完善  
82 - *  
83 - * @param bpmnModel Bpmn Model  
84 - * @return 仿钉钉流程设计模型数据结构  
85 - */  
86 - private BpmSimpleModelNodeVO convertBpmnModelToSimpleModel(BpmnModel bpmnModel) {  
87 - if (bpmnModel == null) {  
88 - return null;  
89 - }  
90 - StartEvent startEvent = BpmnModelUtils.getStartEvent(bpmnModel);  
91 - if (startEvent == null) {  
92 - return null;  
93 - }  
94 - BpmSimpleModelNodeVO rootNode = new BpmSimpleModelNodeVO();  
95 - rootNode.setType(START_EVENT_NODE.getType());  
96 - rootNode.setId(startEvent.getId());  
97 - rootNode.setName(startEvent.getName());  
98 - recursiveBuildSimpleModelNode(startEvent, rootNode);  
99 - return rootNode;  
100 - }  
101 -  
102 - private void recursiveBuildSimpleModelNode(FlowNode currentFlowNode, BpmSimpleModelNodeVO currentSimpleModeNode) {  
103 - BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(currentSimpleModeNode.getType());  
104 - Assert.notNull(nodeType, "节点类型不支持");  
105 - // 校验节点是否支持转仿钉钉的流程模型  
106 - List<SequenceFlow> outgoingFlows = validateCanConvertSimpleNode(nodeType, currentFlowNode);  
107 - if (CollUtil.isEmpty(outgoingFlows) || outgoingFlows.get(0).getTargetFlowElement() == null) {  
108 - return;  
109 - }  
110 - FlowElement targetElement = outgoingFlows.get(0).getTargetFlowElement();  
111 - // 如果是 EndEvent 直接退出  
112 - if (targetElement instanceof EndEvent) {  
113 - return;  
114 - }  
115 - if (targetElement instanceof UserTask) {  
116 - BpmSimpleModelNodeVO childNode = convertUserTaskToSimpleModelNode((UserTask) targetElement);  
117 - currentSimpleModeNode.setChildNode(childNode);  
118 - recursiveBuildSimpleModelNode((FlowNode) targetElement, childNode);  
119 - }  
120 - // TODO 其它节点类型待实现  
121 - }  
122 -  
123 - private BpmSimpleModelNodeVO convertUserTaskToSimpleModelNode(UserTask userTask) {  
124 - BpmSimpleModelNodeVO simpleModelNodeVO = new BpmSimpleModelNodeVO();  
125 - simpleModelNodeVO.setType(BpmSimpleModelNodeType.APPROVE_USER_NODE.getType());  
126 - simpleModelNodeVO.setName(userTask.getName());  
127 - simpleModelNodeVO.setId(userTask.getId());  
128 - Map<String, Object> attributes = MapUtil.newHashMap();  
129 - // TODO 暂时是普通审批,需要加会签  
130 - attributes.put("approveMethod", 1);  
131 - attributes.computeIfAbsent(USER_TASK_CANDIDATE_STRATEGY, (key) -> BpmnModelUtils.parseCandidateStrategy(userTask));  
132 - attributes.computeIfAbsent(USER_TASK_CANDIDATE_PARAM, (key) -> BpmnModelUtils.parseCandidateParam(userTask));  
133 - simpleModelNodeVO.setAttributes(attributes);  
134 - return simpleModelNodeVO;  
135 - }  
136 -  
137 - private List<SequenceFlow> validateCanConvertSimpleNode(BpmSimpleModelNodeType nodeType, FlowNode currentFlowNode) {  
138 - switch (nodeType) {  
139 - case START_EVENT_NODE:  
140 - case APPROVE_USER_NODE: {  
141 - List<SequenceFlow> outgoingFlows = currentFlowNode.getOutgoingFlows();  
142 - if (CollUtil.isNotEmpty(outgoingFlows) && outgoingFlows.size() > 1) {  
143 - throw exception(CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT);  
144 - }  
145 - validIsSupportFlowNode(outgoingFlows.get(0).getTargetFlowElement());  
146 - return outgoingFlows;  
147 - }  
148 - default: {  
149 - // TODO 其它节点类型待实现  
150 - throw exception(CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT);  
151 - }  
152 - }  
153 - }  
154 -  
155 - private void validIsSupportFlowNode(FlowElement targetElement) {  
156 - if (targetElement == null) {  
157 - return;  
158 - }  
159 - boolean isSupport = false;  
160 - for (Class<? extends FlowNode> item : BpmnModelConstants.SUPPORT_CONVERT_SIMPLE_FlOW_NODES) {  
161 - if (item.isInstance(targetElement)) {  
162 - isSupport = true;  
163 - break;  
164 - }  
165 - }  
166 - if (!isSupport) {  
167 - throw exception(CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT);  
168 - }  
169 - }  
170 -}  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java
@@ -17,11 +17,9 @@ public interface BpmProcessInstanceCopyService { @@ -17,11 +17,9 @@ public interface BpmProcessInstanceCopyService {
17 * 流程实例的抄送 17 * 流程实例的抄送
18 * 18 *
19 * @param userIds 抄送的用户编号 19 * @param userIds 抄送的用户编号
20 - * @param processInstanceId 流程编号  
21 - * @param taskId 任务编号  
22 - * @param taskName 任务名称 20 + * @param taskId 流程任务编号
23 */ 21 */
24 - void createProcessInstanceCopy(Collection<Long> userIds, String processInstanceId, String taskId, String taskName); 22 + void createProcessInstanceCopy(Collection<Long> userIds, String taskId);
25 23
26 /** 24 /**
27 * 获得抄送的流程的分页 25 * 获得抄送的流程的分页
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java
1 package cn.iocoder.yudao.module.bpm.service.task; 1 package cn.iocoder.yudao.module.bpm.service.task;
2 2
  3 +import cn.hutool.core.util.ObjectUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; 5 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
5 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; 6 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
@@ -10,6 +11,7 @@ import jakarta.annotation.Resource; @@ -10,6 +11,7 @@ import jakarta.annotation.Resource;
10 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
11 import org.flowable.engine.repository.ProcessDefinition; 12 import org.flowable.engine.repository.ProcessDefinition;
12 import org.flowable.engine.runtime.ProcessInstance; 13 import org.flowable.engine.runtime.ProcessInstance;
  14 +import org.flowable.task.api.Task;
13 import org.springframework.context.annotation.Lazy; 15 import org.springframework.context.annotation.Lazy;
14 import org.springframework.stereotype.Service; 16 import org.springframework.stereotype.Service;
15 import org.springframework.validation.annotation.Validated; 17 import org.springframework.validation.annotation.Validated;
@@ -45,14 +47,14 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy @@ -45,14 +47,14 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
45 private BpmProcessDefinitionService processDefinitionService; 47 private BpmProcessDefinitionService processDefinitionService;
46 48
47 @Override 49 @Override
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 -// } 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 + }
54 // 1.2 校验流程实例存在 56 // 1.2 校验流程实例存在
55 -// String processInstanceId = task.getProcessInstanceId(); 57 + String processInstanceId = task.getProcessInstanceId();
56 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); 58 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
57 if (processInstance == null) { 59 if (processInstance == null) {
58 throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); 60 throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
@@ -68,7 +70,7 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy @@ -68,7 +70,7 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
68 List<BpmProcessInstanceCopyDO> copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO() 70 List<BpmProcessInstanceCopyDO> copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO()
69 .setUserId(userId).setStartUserId(Long.valueOf(processInstance.getStartUserId())) 71 .setUserId(userId).setStartUserId(Long.valueOf(processInstance.getStartUserId()))
70 .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName()) 72 .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
71 - .setCategory(processDefinition.getCategory()).setTaskId(taskId).setTaskName(taskName)); 73 + .setCategory(processDefinition.getCategory()).setTaskId(taskId).setTaskName(task.getName()));
72 processInstanceCopyMapper.insertBatch(copyList); 74 processInstanceCopyMapper.insertBatch(copyList);
73 } 75 }
74 76
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmSimpleNodeService.java deleted
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
@@ -186,8 +186,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -186,8 +186,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
186 186
187 // 2. 抄送用户 187 // 2. 抄送用户
188 if (CollUtil.isNotEmpty(reqVO.getCopyUserIds())) { 188 if (CollUtil.isNotEmpty(reqVO.getCopyUserIds())) {
189 - processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), instance.getProcessInstanceId(),  
190 - reqVO.getId(), task.getName()); 189 + processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getId());
191 } 190 }
192 191
193 // 情况一:被委派的任务,不调用 complete 去完成任务 192 // 情况一:被委派的任务,不调用 complete 去完成任务
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http
@@ -53,13 +53,3 @@ tenant-id: {{adminTenentId}} @@ -53,13 +53,3 @@ tenant-id: {{adminTenentId}}
53 GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-user?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59 53 GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-user?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59
54 Authorization: Bearer {{token}} 54 Authorization: Bearer {{token}}
55 tenant-id: {{adminTenentId}} 55 tenant-id: {{adminTenentId}}
56 -  
57 -### 6.3 获取客户成交周期(按区域)  
58 -GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-area?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59  
59 -Authorization: Bearer {{token}}  
60 -tenant-id: {{adminTenentId}}  
61 -  
62 -### 6.4 获取客户成交周期(按产品)  
63 -GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-product?deptId=100&times[0]=2023-01-01 00:00:00&times[1]=2024-12-12 23:59:59  
64 -Authorization: Bearer {{token}}  
65 -tenant-id: {{adminTenentId}}  
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java
@@ -96,18 +96,6 @@ public class CrmStatisticsCustomerController { @@ -96,18 +96,6 @@ public class CrmStatisticsCustomerController {
96 return success(customerService.getCustomerDealCycleByUser(reqVO)); 96 return success(customerService.getCustomerDealCycleByUser(reqVO));
97 } 97 }
98 98
99 - @GetMapping("/get-customer-deal-cycle-by-area")  
100 - @Operation(summary = "获取客户成交周期(按用户)")  
101 - @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")  
102 - public CommonResult<List<CrmStatisticsCustomerDealCycleByAreaRespVO>> getCustomerDealCycleByArea(@Valid CrmStatisticsCustomerReqVO reqVO) {  
103 - return success(customerService.getCustomerDealCycleByArea(reqVO));  
104 - }  
105 -  
106 - @GetMapping("/get-customer-deal-cycle-by-product")  
107 - @Operation(summary = "获取客户成交周期(按用户)")  
108 - @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")  
109 - public CommonResult<List<CrmStatisticsCustomerDealCycleByProductRespVO>> getCustomerDealCycleByProduct(@Valid CrmStatisticsCustomerReqVO reqVO) {  
110 - return success(customerService.getCustomerDealCycleByProduct(reqVO));  
111 - } 99 + // TODO dhb52:【成交周期分析】里,有按照员工(已实现)、地区(未实现)、产品(未实现),需要在看看哈;可以把 CustomerDealCycle 拆成 3 个 tab,员工客户成交周期分析、地区客户成交周期分析、产品客户成交周期分析;
112 100
113 } 101 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByAreaRespVO.java deleted
1 -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;  
2 -  
3 -import com.fasterxml.jackson.annotation.JsonIgnore;  
4 -import io.swagger.v3.oas.annotations.media.Schema;  
5 -import lombok.Data;  
6 -  
7 -@Schema(description = "管理后台 - CRM 客户成交周期分析(按区域) VO")  
8 -@Data  
9 -public class CrmStatisticsCustomerDealCycleByAreaRespVO {  
10 -  
11 - @Schema(description = "省份编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
12 - @JsonIgnore  
13 - private Integer areaId;  
14 -  
15 - @Schema(description = "省份名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "浙江省")  
16 - private String areaName;  
17 -  
18 - @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0")  
19 - private Double customerDealCycle;  
20 -  
21 - @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
22 - private Integer customerDealCount;  
23 -  
24 -}  
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByProductRespVO.java deleted
1 -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import lombok.Data;  
5 -  
6 -@Schema(description = "管理后台 - CRM 客户成交周期分析(按产品) VO")  
7 -@Data  
8 -public class CrmStatisticsCustomerDealCycleByProductRespVO {  
9 -  
10 - @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "演示产品")  
11 - private String productName;  
12 -  
13 - @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0")  
14 - private Double customerDealCycle;  
15 -  
16 - @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
17 - private Integer customerDealCount;  
18 -  
19 -}  
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java
@@ -53,7 +53,6 @@ public interface CrmStatisticsCustomerMapper { @@ -53,7 +53,6 @@ public interface CrmStatisticsCustomerMapper {
53 53
54 /** 54 /**
55 * 合同总金额(按用户) 55 * 合同总金额(按用户)
56 - *  
57 * @return 统计数据@return 统计数据@param reqVO 请求参数 56 * @return 统计数据@return 统计数据@param reqVO 请求参数
58 * @return 统计数据 57 * @return 统计数据
59 */ 58 */
@@ -192,20 +191,4 @@ public interface CrmStatisticsCustomerMapper { @@ -192,20 +191,4 @@ public interface CrmStatisticsCustomerMapper {
192 */ 191 */
193 List<CrmStatisticsCustomerDealCycleByUserRespVO> selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO); 192 List<CrmStatisticsCustomerDealCycleByUserRespVO> selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO);
194 193
195 - /**  
196 - * 客户成交周期(按区域)  
197 - *  
198 - * @param reqVO 请求参数  
199 - * @return 统计数据  
200 - */  
201 - List<CrmStatisticsCustomerDealCycleByAreaRespVO> selectCustomerDealCycleGroupByAreaId(CrmStatisticsCustomerReqVO reqVO);  
202 -  
203 - /**  
204 - * 客户成交周期(按产品)  
205 - *  
206 - * @param reqVO 请求参数  
207 - * @return 统计数据  
208 - */  
209 - List<CrmStatisticsCustomerDealCycleByProductRespVO> selectCustomerDealCycleGroupByProductId(CrmStatisticsCustomerReqVO reqVO);  
210 -  
211 } 194 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java
@@ -77,7 +77,7 @@ public interface CrmStatisticsCustomerService { @@ -77,7 +77,7 @@ public interface CrmStatisticsCustomerService {
77 77
78 /** 78 /**
79 * 客户成交周期(按日期) 79 * 客户成交周期(按日期)
80 - * <p> 80 + *
81 * 成交周期的定义:客户 customer 在创建出来,到合同 contract 第一次成交的时间差 81 * 成交周期的定义:客户 customer 在创建出来,到合同 contract 第一次成交的时间差
82 * 82 *
83 * @param reqVO 请求参数 83 * @param reqVO 请求参数
@@ -93,20 +93,4 @@ public interface CrmStatisticsCustomerService { @@ -93,20 +93,4 @@ public interface CrmStatisticsCustomerService {
93 */ 93 */
94 List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO); 94 List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO);
95 95
96 - /**  
97 - * 客户成交周期(按区域)  
98 - *  
99 - * @param reqVO 请求参数  
100 - * @return 统计数据  
101 - */  
102 - List<CrmStatisticsCustomerDealCycleByAreaRespVO> getCustomerDealCycleByArea(CrmStatisticsCustomerReqVO reqVO);  
103 -  
104 - /**  
105 - * 客户成交周期(按产品)  
106 - *  
107 - * @param reqVO 请求参数  
108 - * @return 统计数据  
109 - */  
110 - List<CrmStatisticsCustomerDealCycleByProductRespVO> getCustomerDealCycleByProduct(CrmStatisticsCustomerReqVO reqVO);  
111 -  
112 } 96 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java
@@ -4,9 +4,6 @@ import cn.hutool.core.collection.CollUtil; @@ -4,9 +4,6 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
5 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; 5 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
6 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 6 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
7 -import cn.iocoder.yudao.framework.ip.core.Area;  
8 -import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;  
9 -import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;  
10 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; 7 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
11 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; 8 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper;
12 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 9 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
@@ -22,7 +19,6 @@ import java.time.LocalDateTime; @@ -22,7 +19,6 @@ import java.time.LocalDateTime;
22 import java.util.Collections; 19 import java.util.Collections;
23 import java.util.List; 20 import java.util.List;
24 import java.util.Map; 21 import java.util.Map;
25 -import java.util.function.Function;  
26 import java.util.stream.Stream; 22 import java.util.stream.Stream;
27 23
28 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 24 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
@@ -294,51 +290,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @@ -294,51 +290,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
294 return summaryList; 290 return summaryList;
295 } 291 }
296 292
297 - @Override  
298 - public List<CrmStatisticsCustomerDealCycleByAreaRespVO> getCustomerDealCycleByArea(CrmStatisticsCustomerReqVO reqVO) {  
299 - // 1. 获得用户编号数组  
300 - List<Long> userIds = getUserIds(reqVO);  
301 - if (CollUtil.isEmpty(userIds)) {  
302 - return Collections.emptyList();  
303 - }  
304 - reqVO.setUserIds(userIds);  
305 -  
306 - // 2. 获取客户地区统计数据  
307 - List<CrmStatisticsCustomerDealCycleByAreaRespVO> dealCycleByAreaList = customerMapper.selectCustomerDealCycleGroupByAreaId(reqVO);  
308 - if (CollUtil.isEmpty(dealCycleByAreaList)) {  
309 - return Collections.emptyList();  
310 - }  
311 -  
312 - // 3. 拼接数据  
313 - Map<Integer, Area> areaMap = convertMap(AreaUtils.getByType(AreaTypeEnum.PROVINCE, Function.identity()),  
314 - Area::getId);  
315 - return convertList(dealCycleByAreaList, vo -> {  
316 - if (vo.getAreaId() != null) {  
317 - Integer parentId = AreaUtils.getParentIdByType(vo.getAreaId(), AreaTypeEnum.PROVINCE);  
318 - findAndThen(areaMap, parentId, area -> vo.setAreaId(parentId).setAreaName(area.getName()));  
319 - }  
320 - return vo;  
321 - });  
322 - }  
323 -  
324 - @Override  
325 - public List<CrmStatisticsCustomerDealCycleByProductRespVO> getCustomerDealCycleByProduct(CrmStatisticsCustomerReqVO reqVO) {  
326 - // 1. 获得用户编号数组  
327 - List<Long> userIds = getUserIds(reqVO);  
328 - if (CollUtil.isEmpty(userIds)) {  
329 - return Collections.emptyList();  
330 - }  
331 - reqVO.setUserIds(userIds);  
332 -  
333 - // 2. 获取客户产品统计数据  
334 - List<CrmStatisticsCustomerDealCycleByProductRespVO> dealCycleByProductList = customerMapper.selectCustomerDealCycleGroupByProductId(reqVO);  
335 - if (CollUtil.isEmpty(dealCycleByProductList)) {  
336 - return Collections.emptyList();  
337 - }  
338 -  
339 - return dealCycleByProductList;  
340 - }  
341 -  
342 /** 293 /**
343 * 拼接用户信息(昵称) 294 * 拼接用户信息(昵称)
344 * 295 *
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml
@@ -19,18 +19,17 @@ @@ -19,18 +19,17 @@
19 <!-- TODO 芋艿:应该不用过滤时间 --> 19 <!-- TODO 芋艿:应该不用过滤时间 -->
20 <select id="selectCustomerDealCountGroupByDate" 20 <select id="selectCustomerDealCountGroupByDate"
21 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO"> 21 resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO">
22 - SELECT DATE_FORMAT(customer.create_time, '%Y-%m-%d') AS time,  
23 - COUNT(DISTINCT customer.id) AS customer_deal_count 22 + SELECT DATE_FORMAT(customer.create_time, '%Y-%m-%d') AS time,
  23 + COUNT(DISTINCT customer.id) AS customer_deal_count
24 FROM crm_customer AS customer 24 FROM crm_customer AS customer
25 LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id 25 LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
26 - WHERE customer.deleted = 0  
27 - AND contract.deleted = 0 26 + WHERE customer.deleted = 0 AND contract.deleted = 0
28 AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} 27 AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
29 AND customer.owner_user_id IN 28 AND customer.owner_user_id IN
30 <foreach collection="userIds" item="userId" open="(" close=")" separator=","> 29 <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
31 #{userId} 30 #{userId}
32 </foreach> 31 </foreach>
33 - AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} 32 + AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
34 GROUP BY time 33 GROUP BY time
35 </select> 34 </select>
36 35
@@ -54,14 +53,13 @@ @@ -54,14 +53,13 @@
54 COUNT(DISTINCT customer.id) AS customer_deal_count 53 COUNT(DISTINCT customer.id) AS customer_deal_count
55 FROM crm_customer AS customer 54 FROM crm_customer AS customer
56 LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id 55 LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id
57 - WHERE customer.deleted = 0  
58 - AND contract.deleted = 0 56 + WHERE customer.deleted = 0 AND contract.deleted = 0
59 AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} 57 AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
60 AND customer.owner_user_id IN 58 AND customer.owner_user_id IN
61 <foreach collection="userIds" item="userId" open="(" close=")" separator=","> 59 <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
62 #{userId} 60 #{userId}
63 </foreach> 61 </foreach>
64 - AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} 62 + AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
65 GROUP BY customer.owner_user_id 63 GROUP BY customer.owner_user_id
66 </select> 64 </select>
67 65
@@ -223,42 +221,4 @@ @@ -223,42 +221,4 @@
223 GROUP BY customer.owner_user_id 221 GROUP BY customer.owner_user_id
224 </select> 222 </select>
225 223
226 - <select id="selectCustomerDealCycleGroupByAreaId"  
227 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByAreaRespVO">  
228 - SELECT customer.area_id AS area_id,  
229 - IFNULL(TRUNCATE(AVG(TIMESTAMPDIFF(DAY, customer.create_time, contract.order_date)), 1), 0) AS customer_deal_cycle,  
230 - COUNT(DISTINCT customer.id) AS customer_deal_count  
231 - FROM crm_customer AS customer  
232 - LEFT JOIN crm_contract AS contract ON customer.id = contract.customer_id  
233 - WHERE customer.deleted = 0  
234 - AND contract.deleted = 0  
235 - AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}  
236 - AND customer.owner_user_id IN  
237 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
238 - #{userId}  
239 - </foreach>  
240 - AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}  
241 - GROUP BY  
242 - customer.area_id  
243 - </select>  
244 -  
245 - <select id="selectCustomerDealCycleGroupByProductId"  
246 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByProductRespVO">  
247 - SELECT (SELECT name FROM crm_product WHERE id = product.id) AS product_name,  
248 - IFNULL(TRUNCATE(AVG(TIMESTAMPDIFF(DAY, customer.create_time, contract.order_date)), 1), 0) AS customer_deal_cycle,  
249 - COUNT(DISTINCT customer.id) AS customer_deal_count  
250 - FROM crm_customer AS customer  
251 - LEFT JOIN crm_contract AS contract ON customer.id = contract.customer_id  
252 - LEFT JOIN crm_contract_product AS product ON product.contract_id = contract.id  
253 - WHERE customer.deleted = 0  
254 - AND contract.deleted = 0  
255 - AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}  
256 - AND customer.owner_user_id IN  
257 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
258 - #{userId}  
259 - </foreach>  
260 - AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}  
261 - GROUP BY product.id  
262 - </select>  
263 -  
264 </mapper> 224 </mapper>