Commit 528a321f0aa178b4b36b764075371ca1d3ada7e0

Authored by YunaiV
1 parent acea73c9

BPM:增加「发起人自选」的任务审批人的分配策略

Showing 33 changed files with 305 additions and 189 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java
@@ -32,14 +32,13 @@ public class BpmProcessInstanceCreateReqDTO { @@ -32,14 +32,13 @@ public class BpmProcessInstanceCreateReqDTO {
32 @NotEmpty(message = "业务的唯一标识") 32 @NotEmpty(message = "业务的唯一标识")
33 private String businessKey; 33 private String businessKey;
34 34
35 - // TODO @hai:assignees 复数  
36 /** 35 /**
37 - * 提前指派的审批人 36 + * 发起人自选审批人 Map
38 * 37 *
39 * key:taskKey 任务编码 38 * key:taskKey 任务编码
40 * value:审批人的数组 39 * value:审批人的数组
41 - * 例如: { taskKey1 :[1, 2] },则表示 taskKey1 这个任务,提前设定了,由 userId 为 1,2 的用户进行审批 40 + * 例如:{ taskKey1 :[1, 2] },则表示 taskKey1 这个任务,提前设定了,由 userId 为 1,2 的用户进行审批
42 */ 41 */
43 - private Map<String, List<Long>> assignee; 42 + private Map<String, List<Long>> startUserSelectAssignees;
44 43
45 } 44 }
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
@@ -29,12 +29,13 @@ public interface ErrorCodeConstants { @@ -29,12 +29,13 @@ public interface ErrorCodeConstants {
29 ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1_009_003_001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图"); 29 ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1_009_003_001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图");
30 ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_009_003_002, "流程定义不存在"); 30 ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_009_003_002, "流程定义不存在");
31 ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1_009_003_003, "流程定义处于挂起状态"); 31 ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1_009_003_003, "流程定义处于挂起状态");
32 - ErrorCode PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS = new ErrorCode(1_009_003_004, "流程定义的模型不存在");  
33 32
34 // ========== 流程实例 1-009-004-000 ========== 33 // ========== 流程实例 1-009-004-000 ==========
35 ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1_009_004_000, "流程实例不存在"); 34 ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1_009_004_000, "流程实例不存在");
36 ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1_009_004_001, "流程取消失败,流程不处于运行中"); 35 ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1_009_004_001, "流程取消失败,流程不处于运行中");
37 ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1_009_004_002, "流程取消失败,该流程不是你发起的"); 36 ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1_009_004_002, "流程取消失败,该流程不是你发起的");
  37 + ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_003, "审批任务({})的审批人未配置");
  38 + ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "审批任务({})的审批人({})不存在");
38 39
39 // ========== 流程任务 1-009-005-000 ========== 40 // ========== 流程任务 1-009-005-000 ==========
40 ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); 41 ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你");
@@ -50,8 +51,6 @@ public interface ErrorCodeConstants { @@ -50,8 +51,6 @@ public interface ErrorCodeConstants {
50 ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务"); 51 ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务");
51 ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人"); 52 ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人");
52 ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在"); 53 ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在");
53 -  
54 - // ========== 流程任务分配规则 1-009-006-000 TODO 芋艿:这里要改下 ==========  
55 ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); 54 ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!");
56 55
57 // ========== 动态表单模块 1-009-010-000 ========== 56 // ========== 动态表单模块 1-009-010-000 ==========
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java
@@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConver @@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConver
9 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; 9 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
10 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; 10 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
11 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; 11 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
  12 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy;
12 import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService; 13 import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
13 import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService; 14 import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
14 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; 15 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
@@ -16,6 +17,8 @@ import io.swagger.v3.oas.annotations.Operation; @@ -16,6 +17,8 @@ import io.swagger.v3.oas.annotations.Operation;
16 import io.swagger.v3.oas.annotations.Parameter; 17 import io.swagger.v3.oas.annotations.Parameter;
17 import io.swagger.v3.oas.annotations.tags.Tag; 18 import io.swagger.v3.oas.annotations.tags.Tag;
18 import jakarta.annotation.Resource; 19 import jakarta.annotation.Resource;
  20 +import org.flowable.bpmn.model.BpmnModel;
  21 +import org.flowable.bpmn.model.UserTask;
19 import org.flowable.engine.repository.Deployment; 22 import org.flowable.engine.repository.Deployment;
20 import org.flowable.engine.repository.ProcessDefinition; 23 import org.flowable.engine.repository.ProcessDefinition;
21 import org.springframework.security.access.prepost.PreAuthorize; 24 import org.springframework.security.access.prepost.PreAuthorize;
@@ -89,13 +92,23 @@ public class BpmProcessDefinitionController { @@ -89,13 +92,23 @@ public class BpmProcessDefinitionController {
89 list, null, processDefinitionMap, null, null)); 92 list, null, processDefinitionMap, null, null));
90 } 93 }
91 94
92 - @GetMapping ("/get-bpmn-xml")  
93 - @Operation(summary = "获得流程定义的 BPMN XML")  
94 - @Parameter(name = "id", description = "编号", required = true, example = "1024") 95 + @GetMapping ("/get")
  96 + @Operation(summary = "获得流程定义")
  97 + @Parameter(name = "id", description = "流程编号", required = true, example = "1024")
  98 + @Parameter(name = "key", description = "流程定义标识", required = true, example = "1024")
95 @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')") 99 @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
96 - public CommonResult<String> getProcessDefinitionBpmnXML(@RequestParam("id") String id) {  
97 - String bpmnXML = processDefinitionService.getProcessDefinitionBpmnXML(id);  
98 - return success(bpmnXML); 100 + public CommonResult<BpmProcessDefinitionRespVO> getProcessDefinition(
  101 + @RequestParam(value = "id", required = false) String id,
  102 + @RequestParam(value = "key", required = false) String key) {
  103 + ProcessDefinition processDefinition = id != null ? processDefinitionService.getProcessDefinition(id)
  104 + : processDefinitionService.getActiveProcessDefinition(key);
  105 + if (processDefinition == null) {
  106 + return success(null);
  107 + }
  108 + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId());
  109 + List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel);
  110 + return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(
  111 + processDefinition, null, null, null, null, bpmnModel, userTaskList));
99 } 112 }
100 113
101 } 114 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java
@@ -18,6 +18,9 @@ public class BpmCategorySaveReqVO { @@ -18,6 +18,9 @@ public class BpmCategorySaveReqVO {
18 @NotEmpty(message = "分类名不能为空") 18 @NotEmpty(message = "分类名不能为空")
19 private String name; 19 private String name;
20 20
  21 + @Schema(description = "分类描述", example = "你猜")
  22 + private String description;
  23 +
21 @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA") 24 @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA")
22 @NotEmpty(message = "分类标志不能为空") 25 @NotEmpty(message = "分类标志不能为空")
23 private String code; 26 private String code;
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java
@@ -48,7 +48,7 @@ public class BpmProcessDefinitionRespVO { @@ -48,7 +48,7 @@ public class BpmProcessDefinitionRespVO {
48 private String formCustomViewPath; 48 private String formCustomViewPath;
49 49
50 @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 50 @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
51 - private Integer suspensionState; 51 + private Integer suspensionState; // 参见 SuspensionState 枚举
52 52
53 @Schema(description = "部署时间") 53 @Schema(description = "部署时间")
54 private LocalDateTime deploymentTime; // 需要从对应的 Deployment 读取,非必须返回 54 private LocalDateTime deploymentTime; // 需要从对应的 Deployment 读取,非必须返回
@@ -56,4 +56,19 @@ public class BpmProcessDefinitionRespVO { @@ -56,4 +56,19 @@ public class BpmProcessDefinitionRespVO {
56 @Schema(description = "BPMN XML") 56 @Schema(description = "BPMN XML")
57 private String bpmnXml; // 需要从对应的 BpmnModel 读取,非必须返回 57 private String bpmnXml; // 需要从对应的 BpmnModel 读取,非必须返回
58 58
  59 + @Schema(description = "发起用户需要选择审批人的任务数组")
  60 + private List<UserTask> startUserSelectTasks; // 需要从对应的 BpmnModel 读取,非必须返回
  61 +
  62 + @Schema(description = "BPMN UserTask 用户任务")
  63 + @Data
  64 + public static class UserTask {
  65 +
  66 + @Schema(description = "任务标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "sudo")
  67 + private String id;
  68 +
  69 + @Schema(description = "任务名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
  70 + private String name;
  71 +
  72 + }
  73 +
59 } 74 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java
@@ -7,6 +7,8 @@ import lombok.Data; @@ -7,6 +7,8 @@ import lombok.Data;
7 import org.springframework.format.annotation.DateTimeFormat; 7 import org.springframework.format.annotation.DateTimeFormat;
8 8
9 import java.time.LocalDateTime; 9 import java.time.LocalDateTime;
  10 +import java.util.List;
  11 +import java.util.Map;
10 12
11 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; 13 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
12 14
@@ -30,6 +32,9 @@ public class BpmOALeaveCreateReqVO { @@ -30,6 +32,9 @@ public class BpmOALeaveCreateReqVO {
30 @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码") 32 @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码")
31 private String reason; 33 private String reason;
32 34
  35 + @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}")
  36 + private Map<String, List<Long>> startUserSelectAssignees;
  37 +
33 @AssertTrue(message = "结束时间,需要在开始时间之后") 38 @AssertTrue(message = "结束时间,需要在开始时间之后")
34 public boolean isEndTimeValid() { 39 public boolean isEndTimeValid() {
35 return !getEndTime().isBefore(getStartTime()); 40 return !getEndTime().isBefore(getStartTime());
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
@@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessI @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessI
11 import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; 11 import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
12 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; 12 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
13 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; 13 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
  14 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
14 import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService; 15 import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
15 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; 16 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
16 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; 17 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
@@ -130,7 +131,8 @@ public class BpmProcessInstanceController { @@ -130,7 +131,8 @@ public class BpmProcessInstanceController {
130 processInstance.getProcessDefinitionId()); 131 processInstance.getProcessDefinitionId());
131 BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo( 132 BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(
132 processInstance.getProcessDefinitionId()); 133 processInstance.getProcessDefinitionId());
133 - String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); 134 + String bpmnXml = BpmnModelUtils.getBpmnXml(
  135 + processDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId()));
134 AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); 136 AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId()));
135 DeptRespDTO dept = null; 137 DeptRespDTO dept = null;
136 if (startUser != null) { 138 if (startUser != null) {
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java
@@ -18,8 +18,7 @@ public class BpmProcessInstanceCreateReqVO { @@ -18,8 +18,7 @@ public class BpmProcessInstanceCreateReqVO {
18 @Schema(description = "变量实例(动态表单)") 18 @Schema(description = "变量实例(动态表单)")
19 private Map<String, Object> variables; 19 private Map<String, Object> variables;
20 20
21 - // TODO @hai:assignees 复数  
22 - @Schema(description = "提前指派的审批人", requiredMode = Schema.RequiredMode.REQUIRED, example = "{taskKey1: [1, 2]}")  
23 - private Map<String, List<Long>> assignee; 21 + @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}")
  22 + private Map<String, List<Long>> startUserSelectAssignees;
24 23
25 } 24 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java
@@ -4,12 +4,14 @@ import cn.hutool.core.date.LocalDateTimeUtil; @@ -4,12 +4,14 @@ import cn.hutool.core.date.LocalDateTimeUtil;
4 import cn.hutool.core.map.MapUtil; 4 import cn.hutool.core.map.MapUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 6 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
7 -import cn.iocoder.yudao.framework.common.util.collection.MapUtils;  
8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; 8 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
10 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; 9 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
11 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; 10 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
12 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; 11 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
  12 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
  13 +import org.flowable.bpmn.model.BpmnModel;
  14 +import org.flowable.bpmn.model.UserTask;
13 import org.flowable.common.engine.impl.db.SuspensionState; 15 import org.flowable.common.engine.impl.db.SuspensionState;
14 import org.flowable.engine.repository.Deployment; 16 import org.flowable.engine.repository.Deployment;
15 import org.flowable.engine.repository.ProcessDefinition; 17 import org.flowable.engine.repository.ProcessDefinition;
@@ -47,27 +49,50 @@ public interface BpmProcessDefinitionConvert { @@ -47,27 +49,50 @@ public interface BpmProcessDefinitionConvert {
47 Map<Long, BpmFormDO> formMap, 49 Map<Long, BpmFormDO> formMap,
48 Map<String, BpmCategoryDO> categoryMap) { 50 Map<String, BpmCategoryDO> categoryMap) {
49 return CollectionUtils.convertList(list, definition -> { 51 return CollectionUtils.convertList(list, definition -> {
50 - BpmProcessDefinitionRespVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionRespVO.class);  
51 - respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());  
52 - // Deployment  
53 - MapUtils.findAndThen(deploymentMap, definition.getDeploymentId(),  
54 - deployment -> respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime())));  
55 - // BpmProcessDefinitionInfoDO 52 + Deployment deployment = MapUtil.get(deploymentMap, definition.getDeploymentId(), Deployment.class);
56 BpmProcessDefinitionInfoDO processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfoDO.class); 53 BpmProcessDefinitionInfoDO processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfoDO.class);
  54 + BpmFormDO form = null;
57 if (processDefinitionInfo != null) { 55 if (processDefinitionInfo != null) {
58 - copyTo(processDefinitionInfo, respVO);  
59 - // Form  
60 - BpmFormDO form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmFormDO.class);  
61 - if (form != null) {  
62 - respVO.setFormName(form.getName());  
63 - } 56 + form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmFormDO.class);
64 } 57 }
65 - // Category  
66 - MapUtils.findAndThen(categoryMap, definition.getCategory(), category -> respVO.setCategoryName(category.getName()));  
67 - return respVO; 58 + BpmCategoryDO category = MapUtil.get(categoryMap, definition.getCategory(), BpmCategoryDO.class);
  59 + return buildProcessDefinition(definition, deployment, processDefinitionInfo, form, category, null, null);
68 }); 60 });
69 } 61 }
70 62
  63 + default BpmProcessDefinitionRespVO buildProcessDefinition(ProcessDefinition definition,
  64 + Deployment deployment,
  65 + BpmProcessDefinitionInfoDO processDefinitionInfo,
  66 + BpmFormDO form,
  67 + BpmCategoryDO category,
  68 + BpmnModel bpmnModel,
  69 + List<UserTask> startUserSelectUserTaskList) {
  70 + BpmProcessDefinitionRespVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionRespVO.class);
  71 + respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
  72 + // Deployment
  73 + if (deployment != null) {
  74 + respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime()));
  75 + }
  76 + // BpmProcessDefinitionInfoDO
  77 + if (processDefinitionInfo != null) {
  78 + copyTo(processDefinitionInfo, respVO);
  79 + // Form
  80 + if (form != null) {
  81 + respVO.setFormName(form.getName());
  82 + }
  83 + }
  84 + // Category
  85 + if (category != null) {
  86 + respVO.setCategoryName(category.getName());
  87 + }
  88 + // BpmnModel
  89 + if (bpmnModel != null) {
  90 + respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));
  91 + respVO.setStartUserSelectTasks(BeanUtils.toBean(startUserSelectUserTaskList, BpmProcessDefinitionRespVO.UserTask.class));
  92 + }
  93 + return respVO;
  94 + }
  95 +
71 @Mapping(source = "from.id", target = "to.id", ignore = true) 96 @Mapping(source = "from.id", target = "to.id", ignore = true)
72 void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to); 97 void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to);
73 98
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
@@ -68,7 +68,7 @@ public interface BpmProcessInstanceConvert { @@ -68,7 +68,7 @@ public interface BpmProcessInstanceConvert {
68 DeptRespDTO dept) { 68 DeptRespDTO dept) {
69 BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class); 69 BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class);
70 respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance)); 70 respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance));
71 - respVO.setFormVariables(FlowableUtils.filterProcessInstanceFormVariable(processInstance.getProcessVariables())); 71 + respVO.setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance));
72 // definition 72 // definition
73 respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class)); 73 respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class));
74 copyTo(processDefinitionExt, respVO.getProcessDefinition()); 74 copyTo(processDefinitionExt, respVO.getProcessDefinition());
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmCategoryDO.java
1 package cn.iocoder.yudao.module.bpm.dal.dataobject.definition; 1 package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;
2 2
3 -import lombok.*;  
4 -import java.util.*;  
5 -import java.time.LocalDateTime;  
6 -import java.time.LocalDateTime;  
7 -import com.baomidou.mybatisplus.annotation.*;  
8 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import com.baomidou.mybatisplus.annotation.KeySequence;
  5 +import com.baomidou.mybatisplus.annotation.TableId;
  6 +import com.baomidou.mybatisplus.annotation.TableName;
  7 +import lombok.*;
9 8
10 /** 9 /**
11 * BPM 流程分类 DO 10 * BPM 流程分类 DO
@@ -42,7 +41,7 @@ public class BpmCategoryDO extends BaseDO { @@ -42,7 +41,7 @@ public class BpmCategoryDO extends BaseDO {
42 /** 41 /**
43 * 分类状态 42 * 分类状态
44 * 43 *
45 - * 枚举 {@link TODO common_status 对应的类} 44 + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
46 */ 45 */
47 private Integer status; 46 private Integer status;
48 /** 47 /**
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/category/BpmCategoryMapper.java
@@ -21,7 +21,7 @@ public interface BpmCategoryMapper extends BaseMapperX&lt;BpmCategoryDO&gt; { @@ -21,7 +21,7 @@ public interface BpmCategoryMapper extends BaseMapperX&lt;BpmCategoryDO&gt; {
21 default PageResult<BpmCategoryDO> selectPage(BpmCategoryPageReqVO reqVO) { 21 default PageResult<BpmCategoryDO> selectPage(BpmCategoryPageReqVO reqVO) {
22 return selectPage(reqVO, new LambdaQueryWrapperX<BpmCategoryDO>() 22 return selectPage(reqVO, new LambdaQueryWrapperX<BpmCategoryDO>()
23 .likeIfPresent(BpmCategoryDO::getName, reqVO.getName()) 23 .likeIfPresent(BpmCategoryDO::getName, reqVO.getName())
24 - .eqIfPresent(BpmCategoryDO::getCode, reqVO.getCode()) 24 + .likeIfPresent(BpmCategoryDO::getCode, reqVO.getCode())
25 .eqIfPresent(BpmCategoryDO::getStatus, reqVO.getStatus()) 25 .eqIfPresent(BpmCategoryDO::getStatus, reqVO.getStatus())
26 .betweenIfPresent(BpmCategoryDO::getCreateTime, reqVO.getCreateTime()) 26 .betweenIfPresent(BpmCategoryDO::getCreateTime, reqVO.getCreateTime())
27 .orderByAsc(BpmCategoryDO::getSort)); 27 .orderByAsc(BpmCategoryDO::getSort));
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/FlowableContextHolder.java deleted
1 -package cn.iocoder.yudao.module.bpm.framework;  
2 -  
3 -import cn.hutool.core.collection.CollUtil;  
4 -import com.alibaba.ttl.TransmittableThreadLocal;  
5 -  
6 -import java.util.Collections;  
7 -import java.util.List;  
8 -import java.util.Map;  
9 -  
10 -/**  
11 - * 工作流--用户用到的上下文相关信息  
12 - */  
13 -@Deprecated // TODO 芋艿:找个方式,去掉这个上下文  
14 -public class FlowableContextHolder {  
15 -  
16 - private static final ThreadLocal<Map<String, List<Long>>> ASSIGNEE = new TransmittableThreadLocal<>();  
17 -  
18 - /**  
19 - * 通过流程任务的定义 key ,拿到提前选好的审批人  
20 - * 此方法目的:首次创建流程实例时,数据库中还查询不到 assignee 字段,所以存入上下文中获取  
21 - *  
22 - * @param taskDefinitionKey 流程任务 key  
23 - * @return 审批人 ID 集合  
24 - */  
25 - public static List<Long> getAssigneeByTaskDefinitionKey(String taskDefinitionKey) {  
26 - if (CollUtil.isNotEmpty(ASSIGNEE.get())) {  
27 - return ASSIGNEE.get().get(taskDefinitionKey);  
28 - }  
29 - return Collections.emptyList();  
30 - }  
31 -  
32 - /**  
33 - * 存入提前选好的审批人到上下文线程变量中  
34 - *  
35 - * @param assignee 流程任务 key -> 审批人 ID 炅和  
36 - */  
37 - public static void setAssignee(Map<String, List<Long>> assignee) {  
38 - ASSIGNEE.set(assignee);  
39 - }  
40 -  
41 -}  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java
@@ -4,16 +4,13 @@ import cn.hutool.core.collection.CollUtil; @@ -4,16 +4,13 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 -import cn.iocoder.yudao.framework.common.util.number.NumberUtils;  
8 -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;  
9 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; 7 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
10 -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; 8 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
11 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 9 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
12 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 10 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
13 import com.google.common.annotations.VisibleForTesting; 11 import com.google.common.annotations.VisibleForTesting;
14 import lombok.extern.slf4j.Slf4j; 12 import lombok.extern.slf4j.Slf4j;
15 import org.flowable.bpmn.model.BpmnModel; 13 import org.flowable.bpmn.model.BpmnModel;
16 -import org.flowable.bpmn.model.FlowElement;  
17 import org.flowable.bpmn.model.UserTask; 14 import org.flowable.bpmn.model.UserTask;
18 import org.flowable.engine.delegate.DelegateExecution; 15 import org.flowable.engine.delegate.DelegateExecution;
19 16
@@ -60,9 +57,13 @@ public class BpmTaskCandidateInvoker { @@ -60,9 +57,13 @@ public class BpmTaskCandidateInvoker {
60 // 遍历所有的 UserTask,校验审批人配置 57 // 遍历所有的 UserTask,校验审批人配置
61 userTaskList.forEach(userTask -> { 58 userTaskList.forEach(userTask -> {
62 // 1. 非空校验 59 // 1. 非空校验
63 - Integer strategy = parseCandidateStrategy(userTask);  
64 - String param = parseCandidateParam(userTask);  
65 - if (strategy == null || StrUtil.isBlank(param)) { 60 + Integer strategy = BpmnModelUtils.parseCandidateStrategy(userTask);
  61 + String param = BpmnModelUtils.parseCandidateParam(userTask);
  62 + if (strategy == null) {
  63 + throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());
  64 + }
  65 + BpmTaskCandidateStrategy candidateStrategy = getCandidateStrategy(strategy);
  66 + if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) {
66 throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName()); 67 throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());
67 } 68 }
68 // 2. 具体策略校验 69 // 2. 具体策略校验
@@ -77,16 +78,8 @@ public class BpmTaskCandidateInvoker { @@ -77,16 +78,8 @@ public class BpmTaskCandidateInvoker {
77 * @return 用户编号集合 78 * @return 用户编号集合
78 */ 79 */
79 public Set<Long> calculateUsers(DelegateExecution execution) { 80 public Set<Long> calculateUsers(DelegateExecution execution) {
80 - // TODO 芋艿:这里需要重构  
81 -// // 1. 先从提前选好的审批人中获取  
82 -// List<Long> assignee = processInstanceService.getAssigneeByProcessInstanceIdAndTaskDefinitionKey(  
83 -// execution.getProcessInstanceId(), execution.getCurrentActivityId());  
84 -// if (CollUtil.isNotEmpty(assignee)) {  
85 -// // TODO @hai:new HashSet 即可  
86 -// return convertSet(assignee, Function.identity());  
87 -// }  
88 - Integer strategy = parseCandidateStrategy(execution.getCurrentFlowElement());  
89 - String param = parseCandidateParam(execution.getCurrentFlowElement()); 81 + Integer strategy = BpmnModelUtils.parseCandidateStrategy(execution.getCurrentFlowElement());
  82 + String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement());
90 // 1.1 计算任务的候选人 83 // 1.1 计算任务的候选人
91 Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param); 84 Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
92 // 1.2 移除被禁用的用户 85 // 1.2 移除被禁用的用户
@@ -113,16 +106,6 @@ public class BpmTaskCandidateInvoker { @@ -113,16 +106,6 @@ public class BpmTaskCandidateInvoker {
113 }); 106 });
114 } 107 }
115 108
116 - private static Integer parseCandidateStrategy(FlowElement userTask) {  
117 - return NumberUtils.parseInt(userTask.getAttributeValue(  
118 - BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));  
119 - }  
120 -  
121 - private static String parseCandidateParam(FlowElement userTask) {  
122 - return userTask.getAttributeValue(  
123 - BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);  
124 - }  
125 -  
126 private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) { 109 private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
127 BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy); 110 BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
128 Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy); 111 Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java
@@ -36,4 +36,13 @@ public interface BpmTaskCandidateStrategy { @@ -36,4 +36,13 @@ public interface BpmTaskCandidateStrategy {
36 */ 36 */
37 Set<Long> calculateUsers(DelegateExecution execution, String param); 37 Set<Long> calculateUsers(DelegateExecution execution, String param);
38 38
  39 + /**
  40 + * 是否一定要输入参数
  41 + *
  42 + * @return 是否
  43 + */
  44 + default boolean isParamRequired() {
  45 + return true;
  46 + }
  47 +
39 } 48 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java
@@ -32,12 +32,12 @@ public class BpmTaskAssignLeaderExpression { @@ -32,12 +32,12 @@ public class BpmTaskAssignLeaderExpression {
32 private DeptApi deptApi; 32 private DeptApi deptApi;
33 33
34 @Resource 34 @Resource
35 - private BpmProcessInstanceService bpmProcessInstanceService; 35 + private BpmProcessInstanceService processInstanceService;
36 36
37 protected Set<Long> calculateUsers(DelegateExecution execution, int level) { 37 protected Set<Long> calculateUsers(DelegateExecution execution, int level) {
38 Assert.isTrue(level > 0, "level 必须大于 0"); 38 Assert.isTrue(level > 0, "level 必须大于 0");
39 // 获得发起人 39 // 获得发起人
40 - ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId()); 40 + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
41 Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); 41 Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
42 // 获得对应 leve 的部门 42 // 获得对应 leve 的部门
43 DeptRespDTO dept = null; 43 DeptRespDTO dept = null;
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java
@@ -19,10 +19,10 @@ import java.util.Set; @@ -19,10 +19,10 @@ import java.util.Set;
19 public class BpmTaskAssignStartUserExpression { 19 public class BpmTaskAssignStartUserExpression {
20 20
21 @Resource 21 @Resource
22 - private BpmProcessInstanceService bpmProcessInstanceService; 22 + private BpmProcessInstanceService processInstanceService;
23 23
24 public Set<Long> calculateUsers(DelegateExecution execution) { 24 public Set<Long> calculateUsers(DelegateExecution execution) {
25 - ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId()); 25 + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
26 Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); 26 Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
27 return SetUtils.asSet(startUserId); 27 return SetUtils.asSet(startUserId);
28 } 28 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserSelectStrategy.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
  5 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
  6 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
  7 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
  8 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
  9 +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
  10 +import jakarta.annotation.Resource;
  11 +import org.flowable.bpmn.model.BpmnModel;
  12 +import org.flowable.bpmn.model.UserTask;
  13 +import org.flowable.engine.delegate.DelegateExecution;
  14 +import org.flowable.engine.runtime.ProcessInstance;
  15 +import org.springframework.context.annotation.Lazy;
  16 +import org.springframework.stereotype.Component;
  17 +
  18 +import java.util.*;
  19 +
  20 +/**
  21 + * 发起人自选 {@link BpmTaskCandidateUserStrategy} 实现类
  22 + *
  23 + * @author 芋道源码
  24 + */
  25 +@Component
  26 +public class BpmTaskCandidateStartUserSelectStrategy implements BpmTaskCandidateStrategy {
  27 +
  28 + @Resource
  29 + @Lazy // 延迟加载,避免循环依赖
  30 + private BpmProcessInstanceService processInstanceService;
  31 +
  32 + @Override
  33 + public BpmTaskCandidateStrategyEnum getStrategy() {
  34 + return BpmTaskCandidateStrategyEnum.START_USER_SELECT;
  35 + }
  36 +
  37 + @Override
  38 + public void validateParam(String param) {}
  39 +
  40 + @Override
  41 + public Set<Long> calculateUsers(DelegateExecution execution, String param) {
  42 + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
  43 + Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId());
  44 + Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance);
  45 + Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空",
  46 + execution.getProcessInstanceId());
  47 + // 获得审批人
  48 + List<Long> assignees = startUserSelectAssignees.get(execution.getCurrentActivityId());
  49 + return new LinkedHashSet<>(assignees);
  50 + }
  51 +
  52 + @Override
  53 + public boolean isParamRequired() {
  54 + return false;
  55 + }
  56 +
  57 + /**
  58 + * 获得发起人自选审批人的 UserTask 列表
  59 + *
  60 + * @param bpmnModel BPMN 模型
  61 + * @return UserTask 列表
  62 + */
  63 + public static List<UserTask> getStartUserSelectUserTaskList(BpmnModel bpmnModel) {
  64 + if (bpmnModel == null) {
  65 + return null;
  66 + }
  67 + List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
  68 + if (CollUtil.isEmpty(userTaskList)) {
  69 + return null;
  70 + }
  71 + userTaskList.removeIf(userTask -> !Objects.equals(BpmnModelUtils.parseCandidateStrategy(userTask),
  72 + BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()));
  73 + return userTaskList;
  74 + }
  75 +
  76 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmConstants.java
@@ -15,6 +15,12 @@ public class BpmConstants { @@ -15,6 +15,12 @@ public class BpmConstants {
15 * @see ProcessInstance#getProcessVariables() 15 * @see ProcessInstance#getProcessVariables()
16 */ 16 */
17 public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS"; 17 public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS";
  18 + /**
  19 + * 流程实例的变量 - 发起用户选择的审批人 Map
  20 + *
  21 + * @see ProcessInstance#getProcessVariables()
  22 + */
  23 + public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES";
18 24
19 /** 25 /**
20 * 任务的变量 - 状态 26 * 任务的变量 - 状态
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java
@@ -20,6 +20,7 @@ public enum BpmTaskCandidateStrategyEnum { @@ -20,6 +20,7 @@ public enum BpmTaskCandidateStrategyEnum {
20 DEPT_LEADER(21, "部门的负责人"), 20 DEPT_LEADER(21, "部门的负责人"),
21 POST(22, "岗位"), 21 POST(22, "岗位"),
22 USER(30, "用户"), 22 USER(30, "用户"),
  23 + START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人
23 USER_GROUP(40, "用户组"), 24 USER_GROUP(40, "用户组"),
24 EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager 25 EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager
25 ; 26 ;
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
@@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.util; @@ -2,6 +2,8 @@ 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.util.ArrayUtil; 4 import cn.hutool.core.util.ArrayUtil;
  5 +import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  6 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
5 import org.flowable.bpmn.converter.BpmnXMLConverter; 7 import org.flowable.bpmn.converter.BpmnXMLConverter;
6 import org.flowable.bpmn.model.Process; 8 import org.flowable.bpmn.model.Process;
7 import org.flowable.bpmn.model.*; 9 import org.flowable.bpmn.model.*;
@@ -14,6 +16,16 @@ import java.util.*; @@ -14,6 +16,16 @@ import java.util.*;
14 */ 16 */
15 public class BpmnModelUtils { 17 public class BpmnModelUtils {
16 18
  19 + public static Integer parseCandidateStrategy(FlowElement userTask) {
  20 + return NumberUtils.parseInt(userTask.getAttributeValue(
  21 + BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
  22 + }
  23 +
  24 + public static String parseCandidateParam(FlowElement userTask) {
  25 + return userTask.getAttributeValue(
  26 + BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
  27 + }
  28 +
17 /** 29 /**
18 * 根据节点,获取入口连线 30 * 根据节点,获取入口连线
19 * 31 *
@@ -91,6 +103,14 @@ public class BpmnModelUtils { @@ -91,6 +103,14 @@ public class BpmnModelUtils {
91 return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false); 103 return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false);
92 } 104 }
93 105
  106 + public static String getBpmnXml(BpmnModel model) {
  107 + if (model == null) {
  108 + return null;
  109 + }
  110 + BpmnXMLConverter converter = new BpmnXMLConverter();
  111 + return new String(converter.convertToXML(model));
  112 + }
  113 +
94 // ========== 遍历相关的方法 ========== 114 // ========== 遍历相关的方法 ==========
95 115
96 /** 116 /**
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java
@@ -12,6 +12,7 @@ import org.flowable.engine.runtime.ProcessInstance; @@ -12,6 +12,7 @@ import org.flowable.engine.runtime.ProcessInstance;
12 import org.flowable.task.api.TaskInfo; 12 import org.flowable.task.api.TaskInfo;
13 13
14 import java.util.HashMap; 14 import java.util.HashMap;
  15 +import java.util.List;
15 import java.util.Map; 16 import java.util.Map;
16 17
17 /** 18 /**
@@ -79,7 +80,7 @@ public class FlowableUtils { @@ -79,7 +80,7 @@ public class FlowableUtils {
79 * @param processInstance 流程实例 80 * @param processInstance 流程实例
80 * @return 表单 81 * @return 表单
81 */ 82 */
82 - public static Map<String, Object> getProcessInstanceFormVariable(ProcessInstance processInstance) { 83 + public static Map<String, Object> getProcessInstanceFormVariable(HistoricProcessInstance processInstance) {
83 Map<String, Object> formVariables = new HashMap<>(processInstance.getProcessVariables()); 84 Map<String, Object> formVariables = new HashMap<>(processInstance.getProcessVariables());
84 filterProcessInstanceFormVariable(formVariables); 85 filterProcessInstanceFormVariable(formVariables);
85 return formVariables; 86 return formVariables;
@@ -98,6 +99,18 @@ public class FlowableUtils { @@ -98,6 +99,18 @@ public class FlowableUtils {
98 return processVariables; 99 return processVariables;
99 } 100 }
100 101
  102 + /**
  103 + * 获得流程实例的发起用户选择的审批人 Map
  104 + *
  105 + * @param processInstance 流程实例
  106 + * @return 发起用户选择的审批人 Map
  107 + */
  108 + @SuppressWarnings("unchecked")
  109 + public static Map<String, List<Long>> getStartUserSelectAssignees(ProcessInstance processInstance) {
  110 + return (Map<String, List<Long>>) processInstance.getProcessVariables().get(
  111 + BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
  112 + }
  113 +
101 // ========== Task 相关的工具方法 ========== 114 // ========== Task 相关的工具方法 ==========
102 115
103 /** 116 /**
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormServiceImpl.java
@@ -33,7 +33,7 @@ public class BpmFormServiceImpl implements BpmFormService { @@ -33,7 +33,7 @@ public class BpmFormServiceImpl implements BpmFormService {
33 33
34 @Override 34 @Override
35 public Long createForm(BpmFormSaveReqVO createReqVO) { 35 public Long createForm(BpmFormSaveReqVO createReqVO) {
36 - this.vadateFields(createReqVO.getFields()); 36 + this.validateFields(createReqVO.getFields());
37 // 插入 37 // 插入
38 BpmFormDO form = BeanUtils.toBean(createReqVO, BpmFormDO.class); 38 BpmFormDO form = BeanUtils.toBean(createReqVO, BpmFormDO.class);
39 formMapper.insert(form); 39 formMapper.insert(form);
@@ -43,7 +43,7 @@ public class BpmFormServiceImpl implements BpmFormService { @@ -43,7 +43,7 @@ public class BpmFormServiceImpl implements BpmFormService {
43 43
44 @Override 44 @Override
45 public void updateForm(BpmFormSaveReqVO updateReqVO) { 45 public void updateForm(BpmFormSaveReqVO updateReqVO) {
46 - vadateFields(updateReqVO.getFields()); 46 + validateFields(updateReqVO.getFields());
47 // 校验存在 47 // 校验存在
48 validateFormExists(updateReqVO.getId()); 48 validateFormExists(updateReqVO.getId());
49 // 更新 49 // 更新
@@ -93,7 +93,7 @@ public class BpmFormServiceImpl implements BpmFormService { @@ -93,7 +93,7 @@ public class BpmFormServiceImpl implements BpmFormService {
93 * 93 *
94 * @param fields field 数组 94 * @param fields field 数组
95 */ 95 */
96 - private void vadateFields(List<String> fields) { 96 + private void validateFields(List<String> fields) {
97 if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验 97 if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验
98 return; 98 return;
99 } 99 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmPro @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmPro
5 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; 5 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
6 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; 6 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
7 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; 7 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
  8 +import org.flowable.bpmn.model.BpmnModel;
8 import org.flowable.engine.repository.Deployment; 9 import org.flowable.engine.repository.Deployment;
9 import org.flowable.engine.repository.Model; 10 import org.flowable.engine.repository.Model;
10 import org.flowable.engine.repository.ProcessDefinition; 11 import org.flowable.engine.repository.ProcessDefinition;
@@ -61,12 +62,12 @@ public interface BpmProcessDefinitionService { @@ -61,12 +62,12 @@ public interface BpmProcessDefinitionService {
61 void updateProcessDefinitionState(String id, Integer state); 62 void updateProcessDefinitionState(String id, Integer state);
62 63
63 /** 64 /**
64 - * 获得流程定义对应的 BPMN XML 65 + * 获得流程定义对应的 BPMN
65 * 66 *
66 * @param id 流程定义编号 67 * @param id 流程定义编号
67 - * @return BPMN XML 68 + * @return BPMN
68 */ 69 */
69 - String getProcessDefinitionBpmnXML(String id); 70 + BpmnModel getProcessDefinitionBpmnModel(String id);
70 71
71 /** 72 /**
72 * 获得流程定义的信息 73 * 获得流程定义的信息
@@ -89,9 +90,9 @@ public interface BpmProcessDefinitionService { @@ -89,9 +90,9 @@ public interface BpmProcessDefinitionService {
89 } 90 }
90 91
91 /** 92 /**
92 - * 获得编号对应的 ProcessDefinition 93 + * 获得流程定义编号对应的 ProcessDefinition
93 * 94 *
94 - * @param id 编号 95 + * @param id 流程定义编号
95 * @return 流程定义 96 * @return 流程定义
96 */ 97 */
97 ProcessDefinition getProcessDefinition(String id); 98 ProcessDefinition getProcessDefinition(String id);
@@ -139,7 +140,7 @@ public interface BpmProcessDefinitionService { @@ -139,7 +140,7 @@ public interface BpmProcessDefinitionService {
139 * @return 流程部署 Map 140 * @return 流程部署 Map
140 */ 141 */
141 default Map<String, Deployment> getDeploymentMap(Set<String> ids) { 142 default Map<String, Deployment> getDeploymentMap(Set<String> ids) {
142 - return convertMap(getDeployments(ids), Deployment::getId); 143 + return convertMap(getDeploymentList(ids), Deployment::getId);
143 } 144 }
144 145
145 /** 146 /**
@@ -148,7 +149,7 @@ public interface BpmProcessDefinitionService { @@ -148,7 +149,7 @@ public interface BpmProcessDefinitionService {
148 * @param ids 部署编号的数组 149 * @param ids 部署编号的数组
149 * @return 流程部署的数组 150 * @return 流程部署的数组
150 */ 151 */
151 - List<Deployment> getDeployments(Set<String> ids); 152 + List<Deployment> getDeploymentList(Set<String> ids);
152 153
153 /** 154 /**
154 * 获得 id 对应的 Deployment 155 * 获得 id 对应的 Deployment
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
@@ -14,7 +14,6 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConsta @@ -14,7 +14,6 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConsta
14 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; 14 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
15 import jakarta.annotation.Resource; 15 import jakarta.annotation.Resource;
16 import lombok.extern.slf4j.Slf4j; 16 import lombok.extern.slf4j.Slf4j;
17 -import org.flowable.bpmn.converter.BpmnXMLConverter;  
18 import org.flowable.bpmn.model.BpmnModel; 17 import org.flowable.bpmn.model.BpmnModel;
19 import org.flowable.common.engine.impl.db.SuspensionState; 18 import org.flowable.common.engine.impl.db.SuspensionState;
20 import org.flowable.engine.RepositoryService; 19 import org.flowable.engine.RepositoryService;
@@ -84,7 +83,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ @@ -84,7 +83,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
84 } 83 }
85 84
86 @Override 85 @Override
87 - public List<Deployment> getDeployments(Set<String> ids) { 86 + public List<Deployment> getDeploymentList(Set<String> ids) {
88 if (CollUtil.isEmpty(ids)) { 87 if (CollUtil.isEmpty(ids)) {
89 return emptyList(); 88 return emptyList();
90 } 89 }
@@ -156,13 +155,8 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ @@ -156,13 +155,8 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
156 } 155 }
157 156
158 @Override 157 @Override
159 - public String getProcessDefinitionBpmnXML(String id) {  
160 - BpmnModel bpmnModel = repositoryService.getBpmnModel(id);  
161 - if (bpmnModel == null) {  
162 - return null;  
163 - }  
164 - BpmnXMLConverter converter = new BpmnXMLConverter();  
165 - return StrUtil.utf8Str(converter.convertToXML(bpmnModel)); 158 + public BpmnModel getProcessDefinitionBpmnModel(String id) {
  159 + return repositoryService.getBpmnModel(id);
166 } 160 }
167 161
168 @Override 162 @Override
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/BpmOALeaveServiceImpl.java
@@ -56,7 +56,8 @@ public class BpmOALeaveServiceImpl implements BpmOALeaveService { @@ -56,7 +56,8 @@ public class BpmOALeaveServiceImpl implements BpmOALeaveService {
56 processInstanceVariables.put("day", day); 56 processInstanceVariables.put("day", day);
57 String processInstanceId = processInstanceApi.createProcessInstance(userId, 57 String processInstanceId = processInstanceApi.createProcessInstance(userId,
58 new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY) 58 new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY)
59 - .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(leave.getId()))); 59 + .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(leave.getId()))
  60 + .setStartUserSelectAssignees(createReqVO.getStartUserSelectAssignees()));
60 61
61 // 将工作流的编号,更新到 OA 请假单中 62 // 将工作流的编号,更新到 OA 请假单中
62 leaveMapper.updateById(new BpmOALeaveDO().setId(leave.getId()).setProcessInstanceId(processInstanceId)); 63 leaveMapper.updateById(new BpmOALeaveDO().setId(leave.getId()).setProcessInstanceId(processInstanceId));
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
@@ -50,17 +50,6 @@ public interface BpmProcessInstanceService { @@ -50,17 +50,6 @@ public interface BpmProcessInstanceService {
50 } 50 }
51 51
52 /** 52 /**
53 - * 获得流程实例名字 Map  
54 - *  
55 - * @param ids 流程实例的编号集合  
56 - * @return 对应的映射关系  
57 - */  
58 - default Map<String, String> getProcessInstanceNameMap(Set<String> ids) {  
59 - return convertMap(getProcessInstances(ids),  
60 - ProcessInstance::getProcessInstanceId, ProcessInstance::getName);  
61 - }  
62 -  
63 - /**  
64 * 获得历史的流程实例 53 * 获得历史的流程实例
65 * 54 *
66 * @param id 流程实例的编号 55 * @param id 流程实例的编号
@@ -152,14 +141,4 @@ public interface BpmProcessInstanceService { @@ -152,14 +141,4 @@ public interface BpmProcessInstanceService {
152 */ 141 */
153 void updateProcessInstanceReject(String id, String reason); 142 void updateProcessInstanceReject(String id, String reason);
154 143
155 - // TODO @hai:改成 getProcessInstanceAssigneesByTaskDefinitionKey(String id, String taskDefinitionKey)  
156 - /**  
157 - * 获取流程实例中,取出指定流程任务提前指定的审批人  
158 - *  
159 - * @param processInstanceId 流程实例的编号  
160 - * @param taskDefinitionKey 流程任务定义的 key  
161 - * @return 审批人集合  
162 - */  
163 - List<Long> getAssigneeByProcessInstanceIdAndTaskDefinitionKey(String processInstanceId, String taskDefinitionKey);  
164 -  
165 } 144 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
1 -package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.FlowableContextHolder; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private HistoryService historyService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private BpmMessageService messageService; @Resource private AdminUserApi adminUserApi; @Resource private BpmProcessInstanceEventPublisher processInstanceEventPublisher; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery() .includeProcessVariables() .processInstanceId(id) .singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId, BpmProcessInstancePageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery() .includeProcessVariables() .orderByProcessInstanceStartTime().desc(); if (userId != null) { // 【我的流程】菜单时,需要传递该字段 processInstanceQuery.startedBy(String.valueOf(userId)); } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段 processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId())); } if (StrUtil.isNotEmpty(pageReqVO.getName())) { processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionId())) { processInstanceQuery.processDefinitionId("%" + pageReqVO.getProcessDefinitionId() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); } if (pageReqVO.getStatus() != null) { processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus()); } if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); } // 查询数量 long processInstanceCount = processInstanceQuery.count(); if (processInstanceCount == 0) { return PageResult.empty(processInstanceCount); } // 查询列表 List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); return new PageResult<>(processInstanceList, processInstanceCount); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, createReqVO.getAssignee()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getAssignee()); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> assignee) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 设置上下文信息 // TODO @hai:要不往 variables 存到一个全局固定 key 里,减少对上下文的依赖 FlowableContextHolder.setAssignee(assignee); // 创建流程实例 FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); return instance.getId(); } @Override public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 管理员取消,不用校验是否为自己的 AdminUserRespDTO user = adminUserApi.getUser(userId); // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void updateProcessInstanceWhenCancel(FlowableCancelledEvent event) { // 1. 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceReject 方法(审批不通过),已经进行更新了 if (BpmDeleteReasonEnum.isRejectReason((String) event.getCause())) { return; } // 2. 更新流程实例 status runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.CANCEL.getStatus()); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.CANCEL.getStatus())); } @Override public void updateProcessInstanceWhenApprove(ProcessInstance instance) { // 1. 更新流程实例 status runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.APPROVE.getStatus()); // 2. 发送流程被【通过】的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.APPROVE.getStatus())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceReject(String id, String reason) { // 1. 更新流程实例 status runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); // 2. 删除流程实例,以实现驳回任务时,取消整个审批流程 ProcessInstance processInstance = getProcessInstance(id); deleteProcessInstance(id, StrUtil.format(BpmDeleteReasonEnum.REJECT_TASK.format(reason))); // 3. 发送流程被【不通过】的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(processInstance, reason)); // 4. 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } @Override public List<Long> getAssigneeByProcessInstanceIdAndTaskDefinitionKey(String processInstanceId, String taskDefinitionKey) { // 1. 先从上下文获取,首次提交数据库中查询不到 List<Long> result = FlowableContextHolder.getAssigneeByTaskDefinitionKey(taskDefinitionKey); if (CollUtil.isNotEmpty(result)) { return result; } // 2. 从数据库中获取 // TODO @芋艿:指定审批人,这里的存储方案有问题,后续优化下 // BpmProcessInstanceExtDO instance = processInstanceExtMapper.selectByProcessInstanceId(processInstanceId); // if (instance == null) { // throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); // } // if (CollUtil.isNotEmpty(instance.getAssignee())) { // return instance.getAssignee().get(taskDefinitionKey); // } return Collections.emptyList(); } }  
2 \ No newline at end of file 1 \ No newline at end of file
  2 +package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.UserTask; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private HistoryService historyService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private BpmMessageService messageService; @Resource private AdminUserApi adminUserApi; @Resource private BpmProcessInstanceEventPublisher processInstanceEventPublisher; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery() .includeProcessVariables() .processInstanceId(id) .singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId, BpmProcessInstancePageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery() .includeProcessVariables() .orderByProcessInstanceStartTime().desc(); if (userId != null) { // 【我的流程】菜单时,需要传递该字段 processInstanceQuery.startedBy(String.valueOf(userId)); } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段 processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId())); } if (StrUtil.isNotEmpty(pageReqVO.getName())) { processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionId())) { processInstanceQuery.processDefinitionId("%" + pageReqVO.getProcessDefinitionId() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); } if (pageReqVO.getStatus() != null) { processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus()); } if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); } // 查询数量 long processInstanceCount = processInstanceQuery.count(); if (processInstanceCount == 0) { return PageResult.empty(processInstanceCount); } // 查询列表 List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); return new PageResult<>(processInstanceList, processInstanceCount); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, createReqVO.getStartUserSelectAssignees()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getStartUserSelectAssignees()); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> startUserSelectAssignees) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 1.2 校验发起人自选审批人 validateStartUserSelectAssignees(definition, startUserSelectAssignees); // 2. 创建流程实例 FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); if (CollUtil.isNotEmpty(startUserSelectAssignees)) { variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); } ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); return instance.getId(); } private void validateStartUserSelectAssignees(ProcessDefinition definition, Map<String, List<Long>> startUserSelectAssignees) { // 1. 获得发起人自选审批人的 UserTask 列表 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel); if (CollUtil.isEmpty(userTaskList)) { return; } // 2. 校验发起人自选审批人的 UserTask 是否都配置了 userTaskList.forEach(userTask -> { List<Long> assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(userTask.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, userTask.getName()); } Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assignees); assignees.forEach(assignee -> { if (userMap.get(assignee) == null) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, userTask.getName(), assignee); } }); }); } @Override public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 管理员取消,不用校验是否为自己的 AdminUserRespDTO user = adminUserApi.getUser(userId); // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void updateProcessInstanceWhenCancel(FlowableCancelledEvent event) { // 1. 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceReject 方法(审批不通过),已经进行更新了 if (BpmDeleteReasonEnum.isRejectReason((String) event.getCause())) { return; } // 2. 更新流程实例 status runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.CANCEL.getStatus()); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.CANCEL.getStatus())); } @Override public void updateProcessInstanceWhenApprove(ProcessInstance instance) { // 1. 更新流程实例 status runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.APPROVE.getStatus()); // 2. 发送流程被【通过】的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.APPROVE.getStatus())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceReject(String id, String reason) { // 1. 更新流程实例 status runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); // 2. 删除流程实例,以实现驳回任务时,取消整个审批流程 ProcessInstance processInstance = getProcessInstance(id); deleteProcessInstance(id, StrUtil.format(BpmDeleteReasonEnum.REJECT_TASK.format(reason))); // 3. 发送流程被【不通过】的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(processInstance, reason)); // 4. 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } }
3 \ No newline at end of file 3 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java
@@ -30,8 +30,9 @@ public class BpmTaskAssignLeaderExpressionTest extends BaseMockitoUnitTest { @@ -30,8 +30,9 @@ public class BpmTaskAssignLeaderExpressionTest extends BaseMockitoUnitTest {
30 private AdminUserApi adminUserApi; 30 private AdminUserApi adminUserApi;
31 @Mock 31 @Mock
32 private DeptApi deptApi; 32 private DeptApi deptApi;
  33 +
33 @Mock 34 @Mock
34 - private BpmProcessInstanceService bpmProcessInstanceService; 35 + private BpmProcessInstanceService processInstanceService;
35 36
36 @Test 37 @Test
37 public void testCalculateUsers_noDept() { 38 public void testCalculateUsers_noDept() {
@@ -96,7 +97,7 @@ public class BpmTaskAssignLeaderExpressionTest extends BaseMockitoUnitTest { @@ -96,7 +97,7 @@ public class BpmTaskAssignLeaderExpressionTest extends BaseMockitoUnitTest {
96 // mock 返回 startUserId 97 // mock 返回 startUserId
97 ExecutionEntityImpl processInstance = new ExecutionEntityImpl(); 98 ExecutionEntityImpl processInstance = new ExecutionEntityImpl();
98 processInstance.setStartUserId(String.valueOf(startUserId)); 99 processInstance.setStartUserId(String.valueOf(startUserId));
99 - when(bpmProcessInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))) 100 + when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId())))
100 .thenReturn(processInstance); 101 .thenReturn(processInstance);
101 return execution; 102 return execution;
102 } 103 }
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/category/BpmCategoryServiceImplTest.java
1 package cn.iocoder.yudao.module.bpm.service.category; 1 package cn.iocoder.yudao.module.bpm.service.category;
2 2
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
3 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; 6 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO;
4 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; 7 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO;
5 -import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryServiceImpl;  
6 -import org.junit.jupiter.api.Disabled;  
7 -import org.junit.jupiter.api.Test;  
8 -  
9 -import jakarta.annotation.Resource;  
10 -  
11 -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;  
12 -  
13 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; 8 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
14 import cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper; 9 import cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper;
15 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
16 - 10 +import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryServiceImpl;
  11 +import jakarta.annotation.Resource;
  12 +import org.junit.jupiter.api.Test;
17 import org.springframework.context.annotation.Import; 13 import org.springframework.context.annotation.Import;
18 14
19 -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;  
20 -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; 15 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
  16 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
  17 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
  18 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  19 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
21 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; 20 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
22 -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;  
23 -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; 21 +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS;
24 import static org.junit.jupiter.api.Assertions.*; 22 import static org.junit.jupiter.api.Assertions.*;
25 23
26 /** 24 /**
@@ -40,7 +38,8 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest { @@ -40,7 +38,8 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest {
40 @Test 38 @Test
41 public void testCreateCategory_success() { 39 public void testCreateCategory_success() {
42 // 准备参数 40 // 准备参数
43 - BpmCategorySaveReqVO createReqVO = randomPojo(BpmCategorySaveReqVO.class).setId(null); 41 + BpmCategorySaveReqVO createReqVO = randomPojo(BpmCategorySaveReqVO.class).setId(null)
  42 + .setStatus(randomCommonStatus());
44 43
45 // 调用 44 // 调用
46 Long categoryId = categoryService.createCategory(createReqVO); 45 Long categoryId = categoryService.createCategory(createReqVO);
@@ -59,6 +58,7 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest { @@ -59,6 +58,7 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest {
59 // 准备参数 58 // 准备参数
60 BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class, o -> { 59 BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class, o -> {
61 o.setId(dbCategory.getId()); // 设置更新的 ID 60 o.setId(dbCategory.getId()); // 设置更新的 ID
  61 + o.setStatus(randomCommonStatus());
62 }); 62 });
63 63
64 // 调用 64 // 调用
@@ -101,29 +101,28 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest { @@ -101,29 +101,28 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest {
101 } 101 }
102 102
103 @Test 103 @Test
104 - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解  
105 public void testGetCategoryPage() { 104 public void testGetCategoryPage() {
106 // mock 数据 105 // mock 数据
107 BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class, o -> { // 等会查询到 106 BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class, o -> { // 等会查询到
108 - o.setName(null);  
109 - o.setCode(null);  
110 - o.setStatus(null);  
111 - o.setCreateTime(null); 107 + o.setName("芋头");
  108 + o.setCode("xiaodun");
  109 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  110 + o.setCreateTime(buildTime(2023, 2, 2));
112 }); 111 });
113 categoryMapper.insert(dbCategory); 112 categoryMapper.insert(dbCategory);
114 // 测试 name 不匹配 113 // 测试 name 不匹配
115 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null))); 114 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName("小盾")));
116 // 测试 code 不匹配 115 // 测试 code 不匹配
117 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode(null))); 116 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode("tudou")));
118 // 测试 status 不匹配 117 // 测试 status 不匹配
119 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(null))); 118 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
120 // 测试 createTime 不匹配 119 // 测试 createTime 不匹配
121 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(null))); 120 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(buildTime(2024, 2, 2))));
122 // 准备参数 121 // 准备参数
123 BpmCategoryPageReqVO reqVO = new BpmCategoryPageReqVO(); 122 BpmCategoryPageReqVO reqVO = new BpmCategoryPageReqVO();
124 - reqVO.setName(null);  
125 - reqVO.setCode(null);  
126 - reqVO.setStatus(null); 123 + reqVO.setName("芋");
  124 + reqVO.setCode("xiao");
  125 + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
127 reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); 126 reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
128 127
129 // 调用 128 // 调用
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormServiceTest.java
@@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
6 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; 6 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
7 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; 7 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO;
8 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; 8 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;
9 -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormUpdateReqVO;  
10 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; 9 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
11 import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmFormMapper; 10 import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmFormMapper;
12 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmFormFieldRespDTO; 11 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmFormFieldRespDTO;
@@ -66,7 +65,7 @@ public class BpmFormServiceTest extends BaseDbUnitTest { @@ -66,7 +65,7 @@ public class BpmFormServiceTest extends BaseDbUnitTest {
66 }); 65 });
67 formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据 66 formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据
68 // 准备参数 67 // 准备参数
69 - BpmFormUpdateReqVO reqVO = randomPojo(BpmFormUpdateReqVO.class, o -> { 68 + BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> {
70 o.setId(dbForm.getId()); // 设置更新的 ID 69 o.setId(dbForm.getId()); // 设置更新的 ID
71 o.setConf("{'yudao': 'yuanma'}"); 70 o.setConf("{'yudao': 'yuanma'}");
72 o.setFields(randomFields()); 71 o.setFields(randomFields());
@@ -82,7 +81,7 @@ public class BpmFormServiceTest extends BaseDbUnitTest { @@ -82,7 +81,7 @@ public class BpmFormServiceTest extends BaseDbUnitTest {
82 @Test 81 @Test
83 public void testUpdateForm_notExists() { 82 public void testUpdateForm_notExists() {
84 // 准备参数 83 // 准备参数
85 - BpmFormUpdateReqVO reqVO = randomPojo(BpmFormUpdateReqVO.class, o -> { 84 + BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> {
86 o.setConf("{'yudao': 'yuanma'}"); 85 o.setConf("{'yudao': 'yuanma'}");
87 o.setFields(randomFields()); 86 o.setFields(randomFields());
88 }); 87 });
yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/sql/clean.sql
1 DELETE FROM "bpm_form"; 1 DELETE FROM "bpm_form";
2 DELETE FROM "bpm_user_group"; 2 DELETE FROM "bpm_user_group";
  3 +DELETE FROM "bpm_category";
yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/sql/create_tables.sql
@@ -12,6 +12,21 @@ CREATE TABLE IF NOT EXISTS &quot;bpm_user_group&quot; ( @@ -12,6 +12,21 @@ CREATE TABLE IF NOT EXISTS &quot;bpm_user_group&quot; (
12 PRIMARY KEY ("id") 12 PRIMARY KEY ("id")
13 ) COMMENT '用户组'; 13 ) COMMENT '用户组';
14 14
  15 +CREATE TABLE IF NOT EXISTS "bpm_category" (
  16 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  17 + "name" varchar(63) NOT NULL,
  18 + "code" varchar(63) NOT NULL,
  19 + "description" varchar(255) NOT NULL,
  20 + "status" tinyint NOT NULL,
  21 + "sort" int NOT NULL,
  22 + "creator" varchar(64) DEFAULT '',
  23 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  24 + "updater" varchar(64) DEFAULT '',
  25 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  26 + "deleted" bit NOT NULL DEFAULT FALSE,
  27 + PRIMARY KEY ("id")
  28 +) COMMENT '分类';
  29 +
15 CREATE TABLE IF NOT EXISTS "bpm_form" ( 30 CREATE TABLE IF NOT EXISTS "bpm_form" (
16 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, 31 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
17 "name" varchar(63) NOT NULL, 32 "name" varchar(63) NOT NULL,