浏览代码

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

YunaiV 11 月之前
父节点
当前提交
528a321f0a
共有 33 个文件被更改,包括 304 次插入188 次删除
  1. 3 4
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java
  2. 2 3
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
  3. 19 6
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java
  4. 3 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java
  5. 16 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java
  6. 5 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java
  7. 3 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
  8. 2 3
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java
  9. 41 16
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java
  10. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
  11. 5 6
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmCategoryDO.java
  12. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/category/BpmCategoryMapper.java
  13. 0 41
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/FlowableContextHolder.java
  14. 10 27
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java
  15. 9 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java
  16. 2 2
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java
  17. 2 2
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java
  18. 76 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserSelectStrategy.java
  19. 6 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmConstants.java
  20. 1 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java
  21. 20 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
  22. 14 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java
  23. 3 3
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormServiceImpl.java
  24. 8 7
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java
  25. 3 9
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
  26. 2 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/BpmOALeaveServiceImpl.java
  27. 0 21
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
  28. 0 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
  29. 3 2
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java
  30. 26 27
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/category/BpmCategoryServiceImplTest.java
  31. 2 3
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormServiceTest.java
  32. 1 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/sql/clean.sql
  33. 15 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/sql/create_tables.sql

+ 3 - 4
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 {
     @NotEmpty(message = "业务的唯一标识")
     private String businessKey;
 
-    // TODO @hai:assignees 复数
     /**
-     * 提前指派的审批人
+     * 发起人自选审批人 Map
      *
      * key:taskKey 任务编码
      * value:审批人的数组
-     * 例如: { taskKey1 :[1, 2] },则表示 taskKey1 这个任务,提前设定了,由 userId 为 1,2 的用户进行审批
+     * 例如:{ taskKey1 :[1, 2] },则表示 taskKey1 这个任务,提前设定了,由 userId 为 1,2 的用户进行审批
      */
-    private Map<String, List<Long>> assignee;
+    private Map<String, List<Long>> startUserSelectAssignees;
 
 }

+ 2 - 3
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 {
     ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1_009_003_001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图");
     ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_009_003_002, "流程定义不存在");
     ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1_009_003_003, "流程定义处于挂起状态");
-    ErrorCode PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS = new ErrorCode(1_009_003_004, "流程定义的模型不存在");
 
     // ========== 流程实例 1-009-004-000 ==========
     ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1_009_004_000, "流程实例不存在");
     ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1_009_004_001, "流程取消失败,流程不处于运行中");
     ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1_009_004_002, "流程取消失败,该流程不是你发起的");
+    ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_003, "审批任务({})的审批人未配置");
+    ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "审批任务({})的审批人({})不存在");
 
     // ========== 流程任务 1-009-005-000 ==========
     ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你");
@@ -50,8 +51,6 @@ public interface ErrorCodeConstants {
     ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务");
     ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人");
     ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在");
-
-    // ========== 流程任务分配规则 1-009-006-000 TODO 芋艿:这里要改下 ==========
     ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!");
 
     // ========== 动态表单模块 1-009-010-000 ==========

+ 19 - 6
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
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
@@ -16,6 +17,8 @@ import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.UserTask;
 import org.flowable.engine.repository.Deployment;
 import org.flowable.engine.repository.ProcessDefinition;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -89,13 +92,23 @@ public class BpmProcessDefinitionController {
                 list, null, processDefinitionMap, null, null));
     }
 
-    @GetMapping ("/get-bpmn-xml")
-    @Operation(summary = "获得流程定义的 BPMN XML")
-    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @GetMapping ("/get")
+    @Operation(summary = "获得流程定义")
+    @Parameter(name = "id", description = "流程编号", required = true, example = "1024")
+    @Parameter(name = "key", description = "流程定义标识", required = true, example = "1024")
     @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
-    public CommonResult<String> getProcessDefinitionBpmnXML(@RequestParam("id") String id) {
-        String bpmnXML = processDefinitionService.getProcessDefinitionBpmnXML(id);
-        return success(bpmnXML);
+    public CommonResult<BpmProcessDefinitionRespVO> getProcessDefinition(
+            @RequestParam(value = "id", required = false) String id,
+            @RequestParam(value = "key", required = false) String key) {
+        ProcessDefinition processDefinition = id != null ? processDefinitionService.getProcessDefinition(id)
+                : processDefinitionService.getActiveProcessDefinition(key);
+        if (processDefinition == null) {
+            return success(null);
+        }
+        BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId());
+        List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel);
+        return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(
+                processDefinition, null, null, null, null, bpmnModel, userTaskList));
     }
 
 }

+ 3 - 0
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 {
     @NotEmpty(message = "分类名不能为空")
     private String name;
 
+    @Schema(description = "分类描述", example = "你猜")
+    private String description;
+
     @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA")
     @NotEmpty(message = "分类标志不能为空")
     private String code;

+ 16 - 1
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 {
     private String formCustomViewPath;
 
     @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
-    private Integer suspensionState;
+    private Integer suspensionState; // 参见 SuspensionState 枚举
 
     @Schema(description = "部署时间")
     private LocalDateTime deploymentTime; // 需要从对应的 Deployment 读取,非必须返回
@@ -56,4 +56,19 @@ public class BpmProcessDefinitionRespVO {
     @Schema(description = "BPMN XML")
     private String bpmnXml; // 需要从对应的 BpmnModel 读取,非必须返回
 
+    @Schema(description = "发起用户需要选择审批人的任务数组")
+    private List<UserTask> startUserSelectTasks; // 需要从对应的 BpmnModel 读取,非必须返回
+
+    @Schema(description = "BPMN UserTask 用户任务")
+    @Data
+    public static class UserTask {
+
+        @Schema(description = "任务标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "sudo")
+        private String id;
+
+        @Schema(description = "任务名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
+        private String name;
+
+    }
+
 }

+ 5 - 0
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;
 import org.springframework.format.annotation.DateTimeFormat;
 
 import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
 
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
 
@@ -30,6 +32,9 @@ public class BpmOALeaveCreateReqVO {
     @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码")
     private String reason;
 
+    @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}")
+    private Map<String, List<Long>> startUserSelectAssignees;
+
     @AssertTrue(message = "结束时间,需要在开始时间之后")
     public boolean isEndTimeValid() {
         return !getEndTime().isBefore(getStartTime());

+ 3 - 1
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
 import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
@@ -130,7 +131,8 @@ public class BpmProcessInstanceController {
                 processInstance.getProcessDefinitionId());
         BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(
                 processInstance.getProcessDefinitionId());
-        String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId());
+        String bpmnXml = BpmnModelUtils.getBpmnXml(
+                processDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId()));
         AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId()));
         DeptRespDTO dept = null;
         if (startUser != null) {

+ 2 - 3
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 {
     @Schema(description = "变量实例(动态表单)")
     private Map<String, Object> variables;
 
-    // TODO @hai:assignees 复数
-    @Schema(description = "提前指派的审批人", requiredMode = Schema.RequiredMode.REQUIRED, example = "{taskKey1: [1, 2]}")
-    private Map<String, List<Long>> assignee;
+    @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}")
+    private Map<String, List<Long>> startUserSelectAssignees;
 
 }

+ 41 - 16
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;
 import cn.hutool.core.map.MapUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
-import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.UserTask;
 import org.flowable.common.engine.impl.db.SuspensionState;
 import org.flowable.engine.repository.Deployment;
 import org.flowable.engine.repository.ProcessDefinition;
@@ -47,27 +49,50 @@ public interface BpmProcessDefinitionConvert {
                                                                         Map<Long, BpmFormDO> formMap,
                                                                         Map<String, BpmCategoryDO> categoryMap) {
         return CollectionUtils.convertList(list, definition -> {
-            BpmProcessDefinitionRespVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionRespVO.class);
-            respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
-            // Deployment
-            MapUtils.findAndThen(deploymentMap, definition.getDeploymentId(),
-                    deployment -> respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime())));
-            // BpmProcessDefinitionInfoDO
+            Deployment deployment = MapUtil.get(deploymentMap, definition.getDeploymentId(), Deployment.class);
             BpmProcessDefinitionInfoDO processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfoDO.class);
+            BpmFormDO form = null;
             if (processDefinitionInfo != null) {
-                copyTo(processDefinitionInfo, respVO);
-                // Form
-                BpmFormDO form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmFormDO.class);
-                if (form != null) {
-                    respVO.setFormName(form.getName());
-                }
+                form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmFormDO.class);
             }
-            // Category
-            MapUtils.findAndThen(categoryMap, definition.getCategory(), category -> respVO.setCategoryName(category.getName()));
-            return respVO;
+            BpmCategoryDO category = MapUtil.get(categoryMap, definition.getCategory(), BpmCategoryDO.class);
+            return buildProcessDefinition(definition, deployment, processDefinitionInfo, form, category, null, null);
         });
     }
 
+    default BpmProcessDefinitionRespVO buildProcessDefinition(ProcessDefinition definition,
+                                                              Deployment deployment,
+                                                              BpmProcessDefinitionInfoDO processDefinitionInfo,
+                                                              BpmFormDO form,
+                                                              BpmCategoryDO category,
+                                                              BpmnModel bpmnModel,
+                                                              List<UserTask> startUserSelectUserTaskList) {
+        BpmProcessDefinitionRespVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionRespVO.class);
+        respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
+        // Deployment
+        if (deployment != null) {
+            respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime()));
+        }
+        // BpmProcessDefinitionInfoDO
+        if (processDefinitionInfo != null) {
+            copyTo(processDefinitionInfo, respVO);
+            // Form
+            if (form != null) {
+                respVO.setFormName(form.getName());
+            }
+        }
+        // Category
+        if (category != null) {
+            respVO.setCategoryName(category.getName());
+        }
+        // BpmnModel
+        if (bpmnModel != null) {
+            respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));
+            respVO.setStartUserSelectTasks(BeanUtils.toBean(startUserSelectUserTaskList, BpmProcessDefinitionRespVO.UserTask.class));
+        }
+        return respVO;
+    }
+
     @Mapping(source = "from.id", target = "to.id", ignore = true)
     void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to);
 

+ 1 - 1
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 {
                                                           DeptRespDTO dept) {
         BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class);
         respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance));
-        respVO.setFormVariables(FlowableUtils.filterProcessInstanceFormVariable(processInstance.getProcessVariables()));
+        respVO.setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance));
         // definition
         respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class));
         copyTo(processDefinitionExt, respVO.getProcessDefinition());

+ 5 - 6
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmCategoryDO.java

@@ -1,11 +1,10 @@
 package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;
 
-import lombok.*;
-import java.util.*;
-import java.time.LocalDateTime;
-import java.time.LocalDateTime;
-import com.baomidou.mybatisplus.annotation.*;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
 
 /**
  * BPM 流程分类 DO
@@ -42,7 +41,7 @@ public class BpmCategoryDO extends BaseDO {
     /**
      * 分类状态
      *
-     * 枚举 {@link TODO common_status 对应的类}
+     * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
      */
     private Integer status;
     /**

+ 1 - 1
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<BpmCategoryDO> {
     default PageResult<BpmCategoryDO> selectPage(BpmCategoryPageReqVO reqVO) {
         return selectPage(reqVO, new LambdaQueryWrapperX<BpmCategoryDO>()
                 .likeIfPresent(BpmCategoryDO::getName, reqVO.getName())
-                .eqIfPresent(BpmCategoryDO::getCode, reqVO.getCode())
+                .likeIfPresent(BpmCategoryDO::getCode, reqVO.getCode())
                 .eqIfPresent(BpmCategoryDO::getStatus, reqVO.getStatus())
                 .betweenIfPresent(BpmCategoryDO::getCreateTime, reqVO.getCreateTime())
                 .orderByAsc(BpmCategoryDO::getSort));

+ 0 - 41
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/FlowableContextHolder.java

@@ -1,41 +0,0 @@
-package cn.iocoder.yudao.module.bpm.framework;
-
-import cn.hutool.core.collection.CollUtil;
-import com.alibaba.ttl.TransmittableThreadLocal;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 工作流--用户用到的上下文相关信息
- */
-@Deprecated // TODO 芋艿:找个方式,去掉这个上下文
-public class FlowableContextHolder {
-
-    private static final ThreadLocal<Map<String, List<Long>>> ASSIGNEE = new TransmittableThreadLocal<>();
-
-    /**
-     * 通过流程任务的定义 key ,拿到提前选好的审批人
-     * 此方法目的:首次创建流程实例时,数据库中还查询不到 assignee 字段,所以存入上下文中获取
-     *
-     * @param taskDefinitionKey 流程任务 key
-     * @return 审批人 ID 集合
-     */
-    public static List<Long> getAssigneeByTaskDefinitionKey(String taskDefinitionKey) {
-        if (CollUtil.isNotEmpty(ASSIGNEE.get())) {
-            return ASSIGNEE.get().get(taskDefinitionKey);
-        }
-        return Collections.emptyList();
-    }
-
-    /**
-     * 存入提前选好的审批人到上下文线程变量中
-     *
-     * @param assignee 流程任务 key -> 审批人 ID 炅和
-     */
-    public static void setAssignee(Map<String, List<Long>> assignee) {
-        ASSIGNEE.set(assignee);
-    }
-
-}

+ 10 - 27
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;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import com.google.common.annotations.VisibleForTesting;
 import lombok.extern.slf4j.Slf4j;
 import org.flowable.bpmn.model.BpmnModel;
-import org.flowable.bpmn.model.FlowElement;
 import org.flowable.bpmn.model.UserTask;
 import org.flowable.engine.delegate.DelegateExecution;
 
@@ -60,9 +57,13 @@ public class BpmTaskCandidateInvoker {
         // 遍历所有的 UserTask,校验审批人配置
         userTaskList.forEach(userTask -> {
             // 1. 非空校验
-            Integer strategy = parseCandidateStrategy(userTask);
-            String param = parseCandidateParam(userTask);
-            if (strategy == null || StrUtil.isBlank(param)) {
+            Integer strategy = BpmnModelUtils.parseCandidateStrategy(userTask);
+            String param = BpmnModelUtils.parseCandidateParam(userTask);
+            if (strategy == null) {
+                throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());
+            }
+            BpmTaskCandidateStrategy candidateStrategy = getCandidateStrategy(strategy);
+            if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) {
                 throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());
             }
             // 2. 具体策略校验
@@ -77,16 +78,8 @@ public class BpmTaskCandidateInvoker {
      * @return 用户编号集合
      */
     public Set<Long> calculateUsers(DelegateExecution execution) {
-        // TODO 芋艿:这里需要重构
-//        // 1. 先从提前选好的审批人中获取
-//        List<Long> assignee = processInstanceService.getAssigneeByProcessInstanceIdAndTaskDefinitionKey(
-//                execution.getProcessInstanceId(), execution.getCurrentActivityId());
-//        if (CollUtil.isNotEmpty(assignee)) {
-//            // TODO @hai:new HashSet 即可
-//            return convertSet(assignee, Function.identity());
-//        }
-        Integer strategy = parseCandidateStrategy(execution.getCurrentFlowElement());
-        String param = parseCandidateParam(execution.getCurrentFlowElement());
+        Integer strategy = BpmnModelUtils.parseCandidateStrategy(execution.getCurrentFlowElement());
+        String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement());
         // 1.1 计算任务的候选人
         Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
         // 1.2 移除被禁用的用户
@@ -113,16 +106,6 @@ public class BpmTaskCandidateInvoker {
         });
     }
 
-    private static Integer parseCandidateStrategy(FlowElement userTask) {
-        return NumberUtils.parseInt(userTask.getAttributeValue(
-                BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
-    }
-
-    private static String parseCandidateParam(FlowElement userTask) {
-        return userTask.getAttributeValue(
-                BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
-    }
-
     private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
         BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
         Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);

+ 9 - 0
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 {
      */
     Set<Long> calculateUsers(DelegateExecution execution, String param);
 
+    /**
+     * 是否一定要输入参数
+     *
+     * @return 是否
+     */
+    default boolean isParamRequired() {
+        return true;
+    }
+
 }

+ 2 - 2
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 {
     private DeptApi deptApi;
 
     @Resource
-    private BpmProcessInstanceService bpmProcessInstanceService;
+    private BpmProcessInstanceService processInstanceService;
 
     protected Set<Long> calculateUsers(DelegateExecution execution, int level) {
         Assert.isTrue(level > 0, "level 必须大于 0");
         // 获得发起人
-        ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId());
+        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
         Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
         // 获得对应 leve 的部门
         DeptRespDTO dept = null;

+ 2 - 2
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;
 public class BpmTaskAssignStartUserExpression {
 
     @Resource
-    private BpmProcessInstanceService bpmProcessInstanceService;
+    private BpmProcessInstanceService processInstanceService;
 
     public Set<Long> calculateUsers(DelegateExecution execution) {
-        ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId());
+        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
         Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
         return SetUtils.asSet(startUserId);
     }

+ 76 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserSelectStrategy.java

@@ -0,0 +1,76 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
+import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
+import jakarta.annotation.Resource;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.UserTask;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * 发起人自选 {@link BpmTaskCandidateUserStrategy} 实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskCandidateStartUserSelectStrategy implements BpmTaskCandidateStrategy {
+
+    @Resource
+    @Lazy // 延迟加载,避免循环依赖
+    private BpmProcessInstanceService processInstanceService;
+
+    @Override
+    public BpmTaskCandidateStrategyEnum getStrategy() {
+        return BpmTaskCandidateStrategyEnum.START_USER_SELECT;
+    }
+
+    @Override
+    public void validateParam(String param) {}
+
+    @Override
+    public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
+        Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId());
+        Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance);
+        Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空",
+                execution.getProcessInstanceId());
+        // 获得审批人
+        List<Long> assignees = startUserSelectAssignees.get(execution.getCurrentActivityId());
+        return new LinkedHashSet<>(assignees);
+    }
+
+    @Override
+    public boolean isParamRequired() {
+        return false;
+    }
+
+    /**
+     * 获得发起人自选审批人的 UserTask 列表
+     *
+     * @param bpmnModel BPMN 模型
+     * @return UserTask 列表
+     */
+    public static List<UserTask> getStartUserSelectUserTaskList(BpmnModel bpmnModel) {
+        if (bpmnModel == null) {
+            return null;
+        }
+        List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
+        if (CollUtil.isEmpty(userTaskList)) {
+            return null;
+        }
+        userTaskList.removeIf(userTask -> !Objects.equals(BpmnModelUtils.parseCandidateStrategy(userTask),
+                BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()));
+        return userTaskList;
+    }
+
+}

+ 6 - 0
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 {
      * @see ProcessInstance#getProcessVariables()
      */
     public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS";
+    /**
+     * 流程实例的变量 - 发起用户选择的审批人 Map
+     *
+     * @see ProcessInstance#getProcessVariables()
+     */
+    public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES";
 
     /**
      * 任务的变量 - 状态

+ 1 - 0
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 {
     DEPT_LEADER(21, "部门的负责人"),
     POST(22, "岗位"),
     USER(30, "用户"),
+    START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人
     USER_GROUP(40, "用户组"),
     EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager
     ;

+ 20 - 0
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;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ArrayUtil;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
 import org.flowable.bpmn.converter.BpmnXMLConverter;
 import org.flowable.bpmn.model.Process;
 import org.flowable.bpmn.model.*;
@@ -14,6 +16,16 @@ import java.util.*;
  */
 public class BpmnModelUtils {
 
+    public static Integer parseCandidateStrategy(FlowElement userTask) {
+        return NumberUtils.parseInt(userTask.getAttributeValue(
+                BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
+    }
+
+    public static String parseCandidateParam(FlowElement userTask) {
+        return userTask.getAttributeValue(
+                BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
+    }
+
     /**
      * 根据节点,获取入口连线
      *
@@ -91,6 +103,14 @@ public class BpmnModelUtils {
         return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false);
     }
 
+    public static String getBpmnXml(BpmnModel model) {
+        if (model == null) {
+            return null;
+        }
+        BpmnXMLConverter converter = new BpmnXMLConverter();
+        return new String(converter.convertToXML(model));
+    }
+
     // ========== 遍历相关的方法 ==========
 
     /**

+ 14 - 1
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;
 import org.flowable.task.api.TaskInfo;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -79,7 +80,7 @@ public class FlowableUtils {
      * @param processInstance 流程实例
      * @return 表单
      */
-    public static Map<String, Object> getProcessInstanceFormVariable(ProcessInstance processInstance) {
+    public static Map<String, Object> getProcessInstanceFormVariable(HistoricProcessInstance processInstance) {
         Map<String, Object> formVariables = new HashMap<>(processInstance.getProcessVariables());
         filterProcessInstanceFormVariable(formVariables);
         return formVariables;
@@ -98,6 +99,18 @@ public class FlowableUtils {
         return processVariables;
     }
 
+    /**
+     * 获得流程实例的发起用户选择的审批人 Map
+     *
+     * @param processInstance 流程实例
+     * @return 发起用户选择的审批人 Map
+     */
+    @SuppressWarnings("unchecked")
+    public static Map<String, List<Long>> getStartUserSelectAssignees(ProcessInstance processInstance) {
+        return (Map<String, List<Long>>) processInstance.getProcessVariables().get(
+                BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
+    }
+
     // ========== Task 相关的工具方法 ==========
 
     /**

+ 3 - 3
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 {
 
     @Override
     public Long createForm(BpmFormSaveReqVO createReqVO) {
-        this.vadateFields(createReqVO.getFields());
+        this.validateFields(createReqVO.getFields());
         // 插入
         BpmFormDO form = BeanUtils.toBean(createReqVO, BpmFormDO.class);
         formMapper.insert(form);
@@ -43,7 +43,7 @@ public class BpmFormServiceImpl implements BpmFormService {
 
     @Override
     public void updateForm(BpmFormSaveReqVO updateReqVO) {
-        vadateFields(updateReqVO.getFields());
+        validateFields(updateReqVO.getFields());
         // 校验存在
         validateFormExists(updateReqVO.getId());
         // 更新
@@ -93,7 +93,7 @@ public class BpmFormServiceImpl implements BpmFormService {
      *
      * @param fields field 数组
      */
-    private void vadateFields(List<String> fields) {
+    private void validateFields(List<String> fields) {
         if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验
             return;
         }

+ 8 - 7
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
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
+import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.engine.repository.Deployment;
 import org.flowable.engine.repository.Model;
 import org.flowable.engine.repository.ProcessDefinition;
@@ -61,12 +62,12 @@ public interface BpmProcessDefinitionService {
     void updateProcessDefinitionState(String id, Integer state);
 
     /**
-     * 获得流程定义对应的 BPMN XML
+     * 获得流程定义对应的 BPMN
      *
      * @param id 流程定义编号
-     * @return BPMN XML
+     * @return BPMN
      */
-    String getProcessDefinitionBpmnXML(String id);
+    BpmnModel getProcessDefinitionBpmnModel(String id);
 
     /**
      * 获得流程定义的信息
@@ -89,9 +90,9 @@ public interface BpmProcessDefinitionService {
     }
 
     /**
-     * 获得编号对应的 ProcessDefinition
+     * 获得流程定义编号对应的 ProcessDefinition
      *
-     * @param id 编号
+     * @param id 流程定义编号
      * @return 流程定义
      */
     ProcessDefinition getProcessDefinition(String id);
@@ -139,7 +140,7 @@ public interface BpmProcessDefinitionService {
      * @return 流程部署 Map
      */
     default Map<String, Deployment> getDeploymentMap(Set<String> ids) {
-        return convertMap(getDeployments(ids), Deployment::getId);
+        return convertMap(getDeploymentList(ids), Deployment::getId);
     }
 
     /**
@@ -148,7 +149,7 @@ public interface BpmProcessDefinitionService {
      * @param ids 部署编号的数组
      * @return 流程部署的数组
      */
-    List<Deployment> getDeployments(Set<String> ids);
+    List<Deployment> getDeploymentList(Set<String> ids);
 
     /**
      * 获得 id 对应的 Deployment

+ 3 - 9
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
 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
-import org.flowable.bpmn.converter.BpmnXMLConverter;
 import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.common.engine.impl.db.SuspensionState;
 import org.flowable.engine.RepositoryService;
@@ -84,7 +83,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
     }
 
     @Override
-    public List<Deployment> getDeployments(Set<String> ids) {
+    public List<Deployment> getDeploymentList(Set<String> ids) {
         if (CollUtil.isEmpty(ids)) {
             return emptyList();
         }
@@ -156,13 +155,8 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
     }
 
     @Override
-    public String getProcessDefinitionBpmnXML(String id) {
-        BpmnModel bpmnModel = repositoryService.getBpmnModel(id);
-        if (bpmnModel == null) {
-            return null;
-        }
-        BpmnXMLConverter converter = new BpmnXMLConverter();
-        return StrUtil.utf8Str(converter.convertToXML(bpmnModel));
+    public BpmnModel getProcessDefinitionBpmnModel(String id) {
+        return repositoryService.getBpmnModel(id);
     }
 
     @Override

+ 2 - 1
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 {
         processInstanceVariables.put("day", day);
         String processInstanceId = processInstanceApi.createProcessInstance(userId,
                 new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY)
-                        .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(leave.getId())));
+                        .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(leave.getId()))
+                        .setStartUserSelectAssignees(createReqVO.getStartUserSelectAssignees()));
 
         // 将工作流的编号,更新到 OA 请假单中
         leaveMapper.updateById(new BpmOALeaveDO().setId(leave.getId()).setProcessInstanceId(processInstanceId));

+ 0 - 21
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java

@@ -49,17 +49,6 @@ public interface BpmProcessInstanceService {
         return convertMap(getProcessInstances(ids), ProcessInstance::getProcessInstanceId);
     }
 
-    /**
-     * 获得流程实例名字 Map
-     *
-     * @param ids 流程实例的编号集合
-     * @return 对应的映射关系
-     */
-    default Map<String, String> getProcessInstanceNameMap(Set<String> ids) {
-        return convertMap(getProcessInstances(ids),
-                ProcessInstance::getProcessInstanceId, ProcessInstance::getName);
-    }
-
     /**
      * 获得历史的流程实例
      *
@@ -152,14 +141,4 @@ public interface BpmProcessInstanceService {
      */
     void updateProcessInstanceReject(String id, String reason);
 
-    // TODO @hai:改成 getProcessInstanceAssigneesByTaskDefinitionKey(String id, String taskDefinitionKey)
-    /**
-     * 获取流程实例中,取出指定流程任务提前指定的审批人
-     *
-     * @param processInstanceId 流程实例的编号
-     * @param taskDefinitionKey 流程任务定义的 key
-     * @return 审批人集合
-     */
-    List<Long> getAssigneeByProcessInstanceIdAndTaskDefinitionKey(String processInstanceId, String taskDefinitionKey);
-
 }

文件差异内容过多而无法显示
+ 0 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java


+ 3 - 2
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 {
     private AdminUserApi adminUserApi;
     @Mock
     private DeptApi deptApi;
+
     @Mock
-    private BpmProcessInstanceService bpmProcessInstanceService;
+    private BpmProcessInstanceService processInstanceService;
 
     @Test
     public void testCalculateUsers_noDept() {
@@ -96,7 +97,7 @@ public class BpmTaskAssignLeaderExpressionTest extends BaseMockitoUnitTest {
         // mock 返回 startUserId
         ExecutionEntityImpl processInstance = new ExecutionEntityImpl();
         processInstance.setStartUserId(String.valueOf(startUserId));
-        when(bpmProcessInstanceService.getProcessInstance(eq(execution.getProcessInstanceId())))
+        when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId())))
                 .thenReturn(processInstance);
         return execution;
     }

+ 26 - 27
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/category/BpmCategoryServiceImplTest.java

@@ -1,26 +1,24 @@
 package cn.iocoder.yudao.module.bpm.service.category;
 
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO;
-import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryServiceImpl;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-import jakarta.annotation.Resource;
-
-import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
-
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
 import cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-
+import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryServiceImpl;
+import jakarta.annotation.Resource;
+import org.junit.jupiter.api.Test;
 import org.springframework.context.annotation.Import;
 
-import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
-import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
+import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
-import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
-import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
@@ -40,7 +38,8 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest {
     @Test
     public void testCreateCategory_success() {
         // 准备参数
-        BpmCategorySaveReqVO createReqVO = randomPojo(BpmCategorySaveReqVO.class).setId(null);
+        BpmCategorySaveReqVO createReqVO = randomPojo(BpmCategorySaveReqVO.class).setId(null)
+                .setStatus(randomCommonStatus());
 
         // 调用
         Long categoryId = categoryService.createCategory(createReqVO);
@@ -59,6 +58,7 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest {
         // 准备参数
         BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class, o -> {
             o.setId(dbCategory.getId()); // 设置更新的 ID
+            o.setStatus(randomCommonStatus());
         });
 
         // 调用
@@ -101,29 +101,28 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest {
     }
 
     @Test
-    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
     public void testGetCategoryPage() {
        // mock 数据
        BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class, o -> { // 等会查询到
-           o.setName(null);
-           o.setCode(null);
-           o.setStatus(null);
-           o.setCreateTime(null);
+           o.setName("芋头");
+           o.setCode("xiaodun");
+           o.setStatus(CommonStatusEnum.ENABLE.getStatus());
+           o.setCreateTime(buildTime(2023, 2, 2));
        });
        categoryMapper.insert(dbCategory);
        // 测试 name 不匹配
-       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null)));
+       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName("小盾")));
        // 测试 code 不匹配
-       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode(null)));
+       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode("tudou")));
        // 测试 status 不匹配
-       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(null)));
+       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
        // 测试 createTime 不匹配
-       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(null)));
+       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(buildTime(2024, 2, 2))));
        // 准备参数
        BpmCategoryPageReqVO reqVO = new BpmCategoryPageReqVO();
-       reqVO.setName(null);
-       reqVO.setCode(null);
-       reqVO.setStatus(null);
+       reqVO.setName("芋");
+       reqVO.setCode("xiao");
+       reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
 
        // 调用

+ 2 - 3
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;
 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormUpdateReqVO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmFormMapper;
 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmFormFieldRespDTO;
@@ -66,7 +65,7 @@ public class BpmFormServiceTest extends BaseDbUnitTest {
         });
         formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据
         // 准备参数
-        BpmFormUpdateReqVO reqVO = randomPojo(BpmFormUpdateReqVO.class, o -> {
+        BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> {
             o.setId(dbForm.getId()); // 设置更新的 ID
             o.setConf("{'yudao': 'yuanma'}");
             o.setFields(randomFields());
@@ -82,7 +81,7 @@ public class BpmFormServiceTest extends BaseDbUnitTest {
     @Test
     public void testUpdateForm_notExists() {
         // 准备参数
-        BpmFormUpdateReqVO reqVO = randomPojo(BpmFormUpdateReqVO.class, o -> {
+        BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> {
             o.setConf("{'yudao': 'yuanma'}");
             o.setFields(randomFields());
         });

+ 1 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/sql/clean.sql

@@ -1,2 +1,3 @@
 DELETE FROM "bpm_form";
 DELETE FROM "bpm_user_group";
+DELETE FROM "bpm_category";

+ 15 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/sql/create_tables.sql

@@ -12,6 +12,21 @@ CREATE TABLE IF NOT EXISTS "bpm_user_group" (
     PRIMARY KEY ("id")
 ) COMMENT '用户组';
 
+CREATE TABLE IF NOT EXISTS "bpm_category" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "name" varchar(63) NOT NULL,
+    "code" varchar(63) NOT NULL,
+    "description" varchar(255) NOT NULL,
+    "status" tinyint NOT NULL,
+    "sort" int NOT NULL,
+    "creator" varchar(64) DEFAULT '',
+    "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar(64) DEFAULT '',
+    "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    PRIMARY KEY ("id")
+) COMMENT '分类';
+
 CREATE TABLE IF NOT EXISTS "bpm_form" (
     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
     "name" varchar(63) NOT NULL,

部分文件因为文件数量过多而无法显示