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 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   - // 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 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 4 * BPMN XML 常量信息
10 5 *
... ... @@ -28,15 +23,4 @@ public interface BpmnModelConstants {
28 23 */
29 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 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4   -import cn.hutool.core.lang.Assert;
5   -import cn.hutool.core.map.MapUtil;
6 4 import cn.hutool.core.util.ArrayUtil;
7   -import cn.hutool.core.util.StrUtil;
8 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 6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
12   -import org.flowable.bpmn.BpmnAutoLayout;
13 7 import org.flowable.bpmn.converter.BpmnXMLConverter;
14 8 import org.flowable.bpmn.model.Process;
15 9 import org.flowable.bpmn.model.*;
16   -import org.flowable.common.engine.impl.scripting.ScriptingEngines;
17 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 17 public class BpmnModelUtils {
30 18  
31   - public static final String BPMN_SIMPLE_COPY_EXECUTION_SCRIPT = "#{bpmSimpleNodeService.copy(execution)}";
32   -
33 19 public static Integer parseCandidateStrategy(FlowElement userTask) {
34 20 return NumberUtils.parseInt(userTask.getAttributeValue(
35 21 BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
... ... @@ -340,196 +326,4 @@ public class BpmnModelUtils {
340 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 46 */
47 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 1 package cn.iocoder.yudao.module.bpm.service.definition;
2 2  
3   -import cn.hutool.core.util.ArrayUtil;
4 3 import cn.hutool.core.util.StrUtil;
5 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
... ... @@ -104,7 +103,7 @@ public class BpmModelServiceImpl implements BpmModelService {
104 103 // 保存流程定义
105 104 repositoryService.saveModel(model);
106 105 // 保存 BPMN XML
107   - saveModelBpmnXml(model.getId(), StrUtil.utf8Bytes(bpmnXml));
  106 + saveModelBpmnXml(model, bpmnXml);
108 107 return model.getId();
109 108 }
110 109  
... ... @@ -122,7 +121,7 @@ public class BpmModelServiceImpl implements BpmModelService {
122 121 // 更新模型
123 122 repositoryService.saveModel(model);
124 123 // 更新 BPMN XML
125   - saveModelBpmnXml(model.getId(), StrUtil.utf8Bytes(updateReqVO.getBpmnXml()));
  124 + saveModelBpmnXml(model, updateReqVO.getBpmnXml());
126 125 }
127 126  
128 127 @Override
... ... @@ -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 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 17 * 流程实例的抄送
18 18 *
19 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 1 package cn.iocoder.yudao.module.bpm.service.task;
2 2  
  3 +import cn.hutool.core.util.ObjectUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 5 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
5 6 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
... ... @@ -10,6 +11,7 @@ import jakarta.annotation.Resource;
10 11 import lombok.extern.slf4j.Slf4j;
11 12 import org.flowable.engine.repository.ProcessDefinition;
12 13 import org.flowable.engine.runtime.ProcessInstance;
  14 +import org.flowable.task.api.Task;
13 15 import org.springframework.context.annotation.Lazy;
14 16 import org.springframework.stereotype.Service;
15 17 import org.springframework.validation.annotation.Validated;
... ... @@ -45,14 +47,14 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
45 47 private BpmProcessDefinitionService processDefinitionService;
46 48  
47 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 56 // 1.2 校验流程实例存在
55   -// String processInstanceId = task.getProcessInstanceId();
  57 + String processInstanceId = task.getProcessInstanceId();
56 58 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
57 59 if (processInstance == null) {
58 60 throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
... ... @@ -68,7 +70,7 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
68 70 List<BpmProcessInstanceCopyDO> copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO()
69 71 .setUserId(userId).setStartUserId(Long.valueOf(processInstance.getStartUserId()))
70 72 .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
71   - .setCategory(processDefinition.getCategory()).setTaskId(taskId).setTaskName(taskName));
  73 + .setCategory(processDefinition.getCategory()).setTaskId(taskId).setTaskName(task.getName()));
72 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 186  
187 187 // 2. 抄送用户
188 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 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 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 54 Authorization: Bearer {{token}}
55 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 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 53  
54 54 /**
55 55 * 合同总金额(按用户)
56   - *
57 56 * @return 统计数据@return 统计数据@param reqVO 请求参数
58 57 * @return 统计数据
59 58 */
... ... @@ -192,20 +191,4 @@ public interface CrmStatisticsCustomerMapper {
192 191 */
193 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 77  
78 78 /**
79 79 * 客户成交周期(按日期)
80   - * <p>
  80 + *
81 81 * 成交周期的定义:客户 customer 在创建出来,到合同 contract 第一次成交的时间差
82 82 *
83 83 * @param reqVO 请求参数
... ... @@ -93,20 +93,4 @@ public interface CrmStatisticsCustomerService {
93 93 */
94 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 4 import cn.hutool.core.util.ObjUtil;
5 5 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
6 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 7 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
11 8 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper;
12 9 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
... ... @@ -22,7 +19,6 @@ import java.time.LocalDateTime;
22 19 import java.util.Collections;
23 20 import java.util.List;
24 21 import java.util.Map;
25   -import java.util.function.Function;
26 22 import java.util.stream.Stream;
27 23  
28 24 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
... ... @@ -294,51 +290,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
294 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 19 <!-- TODO 芋艿:应该不用过滤时间 -->
20 20 <select id="selectCustomerDealCountGroupByDate"
21 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 24 FROM crm_customer AS customer
25 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 27 AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
29 28 AND customer.owner_user_id IN
30 29 <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
31 30 #{userId}
32 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 33 GROUP BY time
35 34 </select>
36 35  
... ... @@ -54,14 +53,13 @@
54 53 COUNT(DISTINCT customer.id) AS customer_deal_count
55 54 FROM crm_customer AS customer
56 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 57 AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
60 58 AND customer.owner_user_id IN
61 59 <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
62 60 #{userId}
63 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 63 GROUP BY customer.owner_user_id
66 64 </select>
67 65  
... ... @@ -223,42 +221,4 @@
223 221 GROUP BY customer.owner_user_id
224 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 224 </mapper>
... ...