Parcourir la source

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

YunaiV il y a 11 mois
Parent
commit
528a321f0a
33 fichiers modifiés avec 304 ajouts et 188 suppressions
  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 = "业务的唯一标识")
     @NotEmpty(message = "业务的唯一标识")
     private String businessKey;
     private String businessKey;
 
 
-    // TODO @hai:assignees 复数
     /**
     /**
-     * 提前指派的审批人
+     * 发起人自选审批人 Map
      *
      *
      * key:taskKey 任务编码
      * key:taskKey 任务编码
      * value:审批人的数组
      * 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_NAME_NOT_MATCH = new ErrorCode(1_009_003_001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图");
     ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_009_003_002, "流程定义不存在");
     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_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 ==========
     // ========== 流程实例 1-009-004-000 ==========
     ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(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_EXISTS = new ErrorCode(1_009_004_001, "流程取消失败,流程不处于运行中");
     ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1_009_004_002, "流程取消失败,该流程不是你发起的");
     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 ==========
     // ========== 流程任务 1-009-005-000 ==========
     ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你");
     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_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_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人");
     ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在");
     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, "操作失败,原因:找不到任务的审批人!");
     ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!");
 
 
     // ========== 动态表单模块 1-009-010-000 ==========
     // ========== 动态表单模块 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.BpmCategoryDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 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.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.BpmCategoryService;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
 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.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
 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.Deployment;
 import org.flowable.engine.repository.ProcessDefinition;
 import org.flowable.engine.repository.ProcessDefinition;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -89,13 +92,23 @@ public class BpmProcessDefinitionController {
                 list, null, processDefinitionMap, null, null));
                 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')")
     @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 = "分类名不能为空")
     @NotEmpty(message = "分类名不能为空")
     private String name;
     private String name;
 
 
+    @Schema(description = "分类描述", example = "你猜")
+    private String description;
+
     @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA")
     @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA")
     @NotEmpty(message = "分类标志不能为空")
     @NotEmpty(message = "分类标志不能为空")
     private String code;
     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;
     private String formCustomViewPath;
 
 
     @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
-    private Integer suspensionState;
+    private Integer suspensionState; // 参见 SuspensionState 枚举
 
 
     @Schema(description = "部署时间")
     @Schema(description = "部署时间")
     private LocalDateTime deploymentTime; // 需要从对应的 Deployment 读取,非必须返回
     private LocalDateTime deploymentTime; // 需要从对应的 Deployment 读取,非必须返回
@@ -56,4 +56,19 @@ public class BpmProcessDefinitionRespVO {
     @Schema(description = "BPMN XML")
     @Schema(description = "BPMN XML")
     private String bpmnXml; // 需要从对应的 BpmnModel 读取,非必须返回
     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 org.springframework.format.annotation.DateTimeFormat;
 
 
 import java.time.LocalDateTime;
 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;
 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 = "阅读芋道源码")
     @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码")
     private String reason;
     private String reason;
 
 
+    @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}")
+    private Map<String, List<Long>> startUserSelectAssignees;
+
     @AssertTrue(message = "结束时间,需要在开始时间之后")
     @AssertTrue(message = "结束时间,需要在开始时间之后")
     public boolean isEndTimeValid() {
     public boolean isEndTimeValid() {
         return !getEndTime().isBefore(getStartTime());
         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.convert.task.BpmProcessInstanceConvert;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
 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.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.BpmCategoryService;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
@@ -130,7 +131,8 @@ public class BpmProcessInstanceController {
                 processInstance.getProcessDefinitionId());
                 processInstance.getProcessDefinitionId());
         BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(
         BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(
                 processInstance.getProcessDefinitionId());
                 processInstance.getProcessDefinitionId());
-        String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId());
+        String bpmnXml = BpmnModelUtils.getBpmnXml(
+                processDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId()));
         AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId()));
         AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId()));
         DeptRespDTO dept = null;
         DeptRespDTO dept = null;
         if (startUser != 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 = "变量实例(动态表单)")
     @Schema(description = "变量实例(动态表单)")
     private Map<String, Object> variables;
     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.hutool.core.map.MapUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 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.CollectionUtils;
-import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 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.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.BpmCategoryDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 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.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.common.engine.impl.db.SuspensionState;
 import org.flowable.engine.repository.Deployment;
 import org.flowable.engine.repository.Deployment;
 import org.flowable.engine.repository.ProcessDefinition;
 import org.flowable.engine.repository.ProcessDefinition;
@@ -47,27 +49,50 @@ public interface BpmProcessDefinitionConvert {
                                                                         Map<Long, BpmFormDO> formMap,
                                                                         Map<Long, BpmFormDO> formMap,
                                                                         Map<String, BpmCategoryDO> categoryMap) {
                                                                         Map<String, BpmCategoryDO> categoryMap) {
         return CollectionUtils.convertList(list, definition -> {
         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);
             BpmProcessDefinitionInfoDO processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfoDO.class);
+            BpmFormDO form = null;
             if (processDefinitionInfo != 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)
     @Mapping(source = "from.id", target = "to.id", ignore = true)
     void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to);
     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) {
                                                           DeptRespDTO dept) {
         BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class);
         BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class);
         respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance));
         respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance));
-        respVO.setFormVariables(FlowableUtils.filterProcessInstanceFormVariable(processInstance.getProcessVariables()));
+        respVO.setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance));
         // definition
         // definition
         respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class));
         respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class));
         copyTo(processDefinitionExt, respVO.getProcessDefinition());
         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;
 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 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
  * 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;
     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) {
     default PageResult<BpmCategoryDO> selectPage(BpmCategoryPageReqVO reqVO) {
         return selectPage(reqVO, new LambdaQueryWrapperX<BpmCategoryDO>()
         return selectPage(reqVO, new LambdaQueryWrapperX<BpmCategoryDO>()
                 .likeIfPresent(BpmCategoryDO::getName, reqVO.getName())
                 .likeIfPresent(BpmCategoryDO::getName, reqVO.getName())
-                .eqIfPresent(BpmCategoryDO::getCode, reqVO.getCode())
+                .likeIfPresent(BpmCategoryDO::getCode, reqVO.getCode())
                 .eqIfPresent(BpmCategoryDO::getStatus, reqVO.getStatus())
                 .eqIfPresent(BpmCategoryDO::getStatus, reqVO.getStatus())
                 .betweenIfPresent(BpmCategoryDO::getCreateTime, reqVO.getCreateTime())
                 .betweenIfPresent(BpmCategoryDO::getCreateTime, reqVO.getCreateTime())
                 .orderByAsc(BpmCategoryDO::getSort));
                 .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.lang.Assert;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 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.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.AdminUserApi;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.annotations.VisibleForTesting;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.bpmn.model.BpmnModel;
-import org.flowable.bpmn.model.FlowElement;
 import org.flowable.bpmn.model.UserTask;
 import org.flowable.bpmn.model.UserTask;
 import org.flowable.engine.delegate.DelegateExecution;
 import org.flowable.engine.delegate.DelegateExecution;
 
 
@@ -60,9 +57,13 @@ public class BpmTaskCandidateInvoker {
         // 遍历所有的 UserTask,校验审批人配置
         // 遍历所有的 UserTask,校验审批人配置
         userTaskList.forEach(userTask -> {
         userTaskList.forEach(userTask -> {
             // 1. 非空校验
             // 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());
                 throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());
             }
             }
             // 2. 具体策略校验
             // 2. 具体策略校验
@@ -77,16 +78,8 @@ public class BpmTaskCandidateInvoker {
      * @return 用户编号集合
      * @return 用户编号集合
      */
      */
     public Set<Long> calculateUsers(DelegateExecution execution) {
     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 计算任务的候选人
         // 1.1 计算任务的候选人
         Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
         Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
         // 1.2 移除被禁用的用户
         // 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) {
     private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
         BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
         BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
         Assert.notNull(strategyEnum, "策略(%s) 不存在", 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);
     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;
     private DeptApi deptApi;
 
 
     @Resource
     @Resource
-    private BpmProcessInstanceService bpmProcessInstanceService;
+    private BpmProcessInstanceService processInstanceService;
 
 
     protected Set<Long> calculateUsers(DelegateExecution execution, int level) {
     protected Set<Long> calculateUsers(DelegateExecution execution, int level) {
         Assert.isTrue(level > 0, "level 必须大于 0");
         Assert.isTrue(level > 0, "level 必须大于 0");
         // 获得发起人
         // 获得发起人
-        ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId());
+        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
         Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
         Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
         // 获得对应 leve 的部门
         // 获得对应 leve 的部门
         DeptRespDTO dept = null;
         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 {
 public class BpmTaskAssignStartUserExpression {
 
 
     @Resource
     @Resource
-    private BpmProcessInstanceService bpmProcessInstanceService;
+    private BpmProcessInstanceService processInstanceService;
 
 
     public Set<Long> calculateUsers(DelegateExecution execution) {
     public Set<Long> calculateUsers(DelegateExecution execution) {
-        ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId());
+        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
         Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
         Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
         return SetUtils.asSet(startUserId);
         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()
      * @see ProcessInstance#getProcessVariables()
      */
      */
     public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS";
     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, "部门的负责人"),
     DEPT_LEADER(21, "部门的负责人"),
     POST(22, "岗位"),
     POST(22, "岗位"),
     USER(30, "用户"),
     USER(30, "用户"),
+    START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人
     USER_GROUP(40, "用户组"),
     USER_GROUP(40, "用户组"),
     EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager
     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.collection.CollUtil;
 import cn.hutool.core.util.ArrayUtil;
 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.converter.BpmnXMLConverter;
 import org.flowable.bpmn.model.Process;
 import org.flowable.bpmn.model.Process;
 import org.flowable.bpmn.model.*;
 import org.flowable.bpmn.model.*;
@@ -14,6 +16,16 @@ import java.util.*;
  */
  */
 public class BpmnModelUtils {
 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);
         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 org.flowable.task.api.TaskInfo;
 
 
 import java.util.HashMap;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Map;
 
 
 /**
 /**
@@ -79,7 +80,7 @@ public class FlowableUtils {
      * @param processInstance 流程实例
      * @param processInstance 流程实例
      * @return 表单
      * @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());
         Map<String, Object> formVariables = new HashMap<>(processInstance.getProcessVariables());
         filterProcessInstanceFormVariable(formVariables);
         filterProcessInstanceFormVariable(formVariables);
         return formVariables;
         return formVariables;
@@ -98,6 +99,18 @@ public class FlowableUtils {
         return processVariables;
         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 相关的工具方法 ==========
     // ========== 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
     @Override
     public Long createForm(BpmFormSaveReqVO createReqVO) {
     public Long createForm(BpmFormSaveReqVO createReqVO) {
-        this.vadateFields(createReqVO.getFields());
+        this.validateFields(createReqVO.getFields());
         // 插入
         // 插入
         BpmFormDO form = BeanUtils.toBean(createReqVO, BpmFormDO.class);
         BpmFormDO form = BeanUtils.toBean(createReqVO, BpmFormDO.class);
         formMapper.insert(form);
         formMapper.insert(form);
@@ -43,7 +43,7 @@ public class BpmFormServiceImpl implements BpmFormService {
 
 
     @Override
     @Override
     public void updateForm(BpmFormSaveReqVO updateReqVO) {
     public void updateForm(BpmFormSaveReqVO updateReqVO) {
-        vadateFields(updateReqVO.getFields());
+        validateFields(updateReqVO.getFields());
         // 校验存在
         // 校验存在
         validateFormExists(updateReqVO.getId());
         validateFormExists(updateReqVO.getId());
         // 更新
         // 更新
@@ -93,7 +93,7 @@ public class BpmFormServiceImpl implements BpmFormService {
      *
      *
      * @param fields field 数组
      * @param fields field 数组
      */
      */
-    private void vadateFields(List<String> fields) {
+    private void validateFields(List<String> fields) {
         if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验
         if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验
             return;
             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.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
 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.Deployment;
 import org.flowable.engine.repository.Model;
 import org.flowable.engine.repository.Model;
 import org.flowable.engine.repository.ProcessDefinition;
 import org.flowable.engine.repository.ProcessDefinition;
@@ -61,12 +62,12 @@ public interface BpmProcessDefinitionService {
     void updateProcessDefinitionState(String id, Integer state);
     void updateProcessDefinitionState(String id, Integer state);
 
 
     /**
     /**
-     * 获得流程定义对应的 BPMN XML
+     * 获得流程定义对应的 BPMN
      *
      *
      * @param id 流程定义编号
      * @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 流程定义
      * @return 流程定义
      */
      */
     ProcessDefinition getProcessDefinition(String id);
     ProcessDefinition getProcessDefinition(String id);
@@ -139,7 +140,7 @@ public interface BpmProcessDefinitionService {
      * @return 流程部署 Map
      * @return 流程部署 Map
      */
      */
     default Map<String, Deployment> getDeploymentMap(Set<String> ids) {
     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 部署编号的数组
      * @param ids 部署编号的数组
      * @return 流程部署的数组
      * @return 流程部署的数组
      */
      */
-    List<Deployment> getDeployments(Set<String> ids);
+    List<Deployment> getDeploymentList(Set<String> ids);
 
 
     /**
     /**
      * 获得 id 对应的 Deployment
      * 获得 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 cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
 import jakarta.annotation.Resource;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
-import org.flowable.bpmn.converter.BpmnXMLConverter;
 import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.common.engine.impl.db.SuspensionState;
 import org.flowable.common.engine.impl.db.SuspensionState;
 import org.flowable.engine.RepositoryService;
 import org.flowable.engine.RepositoryService;
@@ -84,7 +83,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
     }
     }
 
 
     @Override
     @Override
-    public List<Deployment> getDeployments(Set<String> ids) {
+    public List<Deployment> getDeploymentList(Set<String> ids) {
         if (CollUtil.isEmpty(ids)) {
         if (CollUtil.isEmpty(ids)) {
             return emptyList();
             return emptyList();
         }
         }
@@ -156,13 +155,8 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
     }
     }
 
 
     @Override
     @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
     @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);
         processInstanceVariables.put("day", day);
         String processInstanceId = processInstanceApi.createProcessInstance(userId,
         String processInstanceId = processInstanceApi.createProcessInstance(userId,
                 new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY)
                 new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY)
-                        .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(leave.getId())));
+                        .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(leave.getId()))
+                        .setStartUserSelectAssignees(createReqVO.getStartUserSelectAssignees()));
 
 
         // 将工作流的编号,更新到 OA 请假单中
         // 将工作流的编号,更新到 OA 请假单中
         leaveMapper.updateById(new BpmOALeaveDO().setId(leave.getId()).setProcessInstanceId(processInstanceId));
         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);
         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);
     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);
-
 }
 }

Fichier diff supprimé car celui-ci est trop grand
+ 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;
     private AdminUserApi adminUserApi;
     @Mock
     @Mock
     private DeptApi deptApi;
     private DeptApi deptApi;
+
     @Mock
     @Mock
-    private BpmProcessInstanceService bpmProcessInstanceService;
+    private BpmProcessInstanceService processInstanceService;
 
 
     @Test
     @Test
     public void testCalculateUsers_noDept() {
     public void testCalculateUsers_noDept() {
@@ -96,7 +97,7 @@ public class BpmTaskAssignLeaderExpressionTest extends BaseMockitoUnitTest {
         // mock 返回 startUserId
         // mock 返回 startUserId
         ExecutionEntityImpl processInstance = new ExecutionEntityImpl();
         ExecutionEntityImpl processInstance = new ExecutionEntityImpl();
         processInstance.setStartUserId(String.valueOf(startUserId));
         processInstance.setStartUserId(String.valueOf(startUserId));
-        when(bpmProcessInstanceService.getProcessInstance(eq(execution.getProcessInstanceId())))
+        when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId())))
                 .thenReturn(processInstance);
                 .thenReturn(processInstance);
         return execution;
         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;
 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.BpmCategoryPageReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO;
 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.dataobject.definition.BpmCategoryDO;
 import cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper;
 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 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.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.*;
 import static org.junit.jupiter.api.Assertions.*;
 
 
 /**
 /**
@@ -40,7 +38,8 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest {
     @Test
     @Test
     public void testCreateCategory_success() {
     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);
         Long categoryId = categoryService.createCategory(createReqVO);
@@ -59,6 +58,7 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest {
         // 准备参数
         // 准备参数
         BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class, o -> {
         BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class, o -> {
             o.setId(dbCategory.getId()); // 设置更新的 ID
             o.setId(dbCategory.getId()); // 设置更新的 ID
+            o.setStatus(randomCommonStatus());
         });
         });
 
 
         // 调用
         // 调用
@@ -101,29 +101,28 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest {
     }
     }
 
 
     @Test
     @Test
-    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
     public void testGetCategoryPage() {
     public void testGetCategoryPage() {
        // mock 数据
        // mock 数据
        BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class, o -> { // 等会查询到
        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);
        categoryMapper.insert(dbCategory);
        // 测试 name 不匹配
        // 测试 name 不匹配
-       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null)));
+       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName("小盾")));
        // 测试 code 不匹配
        // 测试 code 不匹配
-       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode(null)));
+       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode("tudou")));
        // 测试 status 不匹配
        // 测试 status 不匹配
-       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(null)));
+       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
        // 测试 createTime 不匹配
        // 测试 createTime 不匹配
-       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(null)));
+       categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(buildTime(2024, 2, 2))));
        // 准备参数
        // 准备参数
        BpmCategoryPageReqVO reqVO = new BpmCategoryPageReqVO();
        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));
        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.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.BpmFormSaveReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;
 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.dataobject.definition.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmFormMapper;
 import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmFormMapper;
 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmFormFieldRespDTO;
 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmFormFieldRespDTO;
@@ -66,7 +65,7 @@ public class BpmFormServiceTest extends BaseDbUnitTest {
         });
         });
         formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据
         formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据
         // 准备参数
         // 准备参数
-        BpmFormUpdateReqVO reqVO = randomPojo(BpmFormUpdateReqVO.class, o -> {
+        BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> {
             o.setId(dbForm.getId()); // 设置更新的 ID
             o.setId(dbForm.getId()); // 设置更新的 ID
             o.setConf("{'yudao': 'yuanma'}");
             o.setConf("{'yudao': 'yuanma'}");
             o.setFields(randomFields());
             o.setFields(randomFields());
@@ -82,7 +81,7 @@ public class BpmFormServiceTest extends BaseDbUnitTest {
     @Test
     @Test
     public void testUpdateForm_notExists() {
     public void testUpdateForm_notExists() {
         // 准备参数
         // 准备参数
-        BpmFormUpdateReqVO reqVO = randomPojo(BpmFormUpdateReqVO.class, o -> {
+        BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> {
             o.setConf("{'yudao': 'yuanma'}");
             o.setConf("{'yudao': 'yuanma'}");
             o.setFields(randomFields());
             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_form";
 DELETE FROM "bpm_user_group";
 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")
     PRIMARY KEY ("id")
 ) COMMENT '用户组';
 ) 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" (
 CREATE TABLE IF NOT EXISTS "bpm_form" (
     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
     "name" varchar(63) NOT NULL,
     "name" varchar(63) NOT NULL,

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff