Browse Source

BPM:重构审批人的分配规则实现,移除 bpm_task_assign_rule 表,存储在 bpmn 的 userTask 中

YunaiV 1 year ago
parent
commit
cdbcd4d673
19 changed files with 110 additions and 624 deletions
  1. 4 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java
  2. 6 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java
  3. 1 1
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
  4. 0 3
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/candidate/vo/BpmTaskCandidateRuleVO.java
  5. 0 58
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmTaskAssignRuleController.java
  6. 0 24
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/rule/BpmTaskAssignRuleBaseVO.java
  7. 0 24
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/rule/BpmTaskAssignRuleCreateReqVO.java
  8. 0 28
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/rule/BpmTaskAssignRuleRespVO.java
  9. 0 20
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/rule/BpmTaskAssignRuleUpdateReqVO.java
  10. 0 40
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmTaskAssignRuleConvert.java
  11. 0 83
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmTaskAssignRuleDO.java
  12. 0 37
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/definition/BpmTaskAssignRuleMapper.java
  13. 6 18
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
  14. 2 14
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
  15. 2 69
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleService.java
  16. 64 192
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java
  17. 6 5
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupService.java
  18. 8 8
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupServiceImpl.java
  19. 11 0
      yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/util/BpmnModelUtils.java

+ 4 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java

@@ -16,6 +16,10 @@ public class NumberUtils {
         return StrUtil.isNotEmpty(str) ? Long.valueOf(str) : null;
     }
 
+    public static Integer parseInt(String str) {
+        return StrUtil.isNotEmpty(str) ? Integer.valueOf(str) : null;
+    }
+
     /**
      * 通过经纬度获取地球上两点之间的距离
      *

+ 6 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java

@@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -45,6 +46,11 @@ public class StrUtils {
         return Arrays.stream(longs).boxed().collect(Collectors.toList());
     }
 
+    public static Set<Long> splitToLongSet(String value, CharSequence separator) {
+        long[] longs = StrUtil.splitToLong(value, separator);
+        return Arrays.stream(longs).boxed().collect(Collectors.toSet());
+    }
+
     public static List<Integer> splitToInteger(String value, CharSequence separator) {
         int[] integers = StrUtil.splitToInt(value, separator);
         return Arrays.stream(integers).boxed().collect(Collectors.toList());

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java

@@ -27,7 +27,7 @@ public interface ErrorCodeConstants {
     ErrorCode MODEL_KEY_VALID = new ErrorCode(1_009_002_002, "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!");
     ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1_009_002_003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置");
     ErrorCode MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG = new ErrorCode(1_009_002_004, "部署流程失败," +
-            "原因:用户任务({})未配置分配规则,请点击【修改流程】按钮进行配置");
+            "原因:用户任务({})未配置审批人,请点击【流程设计】按钮,选择该它的【任务(审批人)】进行配置");
     ErrorCode MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS = new ErrorCode(1_009_003_005, "流程定义部署失败,原因:信息未发生变化");
 
     // ========== 流程定义 1-009-003-000 ==========

+ 0 - 3
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/candidate/vo/BpmTaskCandidateRuleVO.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo;
 
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleBaseVO;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotNull;
 import lombok.Data;
@@ -10,8 +9,6 @@ import java.util.Set;
 /**
  * 流程任务分配规则 Base VO,提供给添加、修改、详细的子 VO 使用
  * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
- *
- * @see BpmTaskAssignRuleBaseVO
  */
 @Data
 public class BpmTaskCandidateRuleVO {

+ 0 - 58
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmTaskAssignRuleController.java

@@ -1,58 +0,0 @@
-package cn.iocoder.yudao.module.bpm.controller.admin.definition;
-
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
-import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.Parameters;
-import io.swagger.v3.oas.annotations.Operation;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
-
-import jakarta.annotation.Resource;
-import jakarta.validation.Valid;
-import java.util.List;
-
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-
-@Tag(name = "管理后台 - 任务分配规则")
-@RestController
-@RequestMapping("/bpm/task-assign-rule")
-@Validated
-public class BpmTaskAssignRuleController {
-
-    @Resource
-    private BpmTaskAssignRuleService taskAssignRuleService;
-
-    @GetMapping("/list")
-    @Operation(summary = "获得任务分配规则列表")
-    @Parameters({
-            @Parameter(name = "modelId", description = "模型编号", example = "1024"),
-            @Parameter(name = "processDefinitionId", description = "流程定义的编号", example = "2048")
-    })
-    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:query')")
-    public CommonResult<List<BpmTaskAssignRuleRespVO>> getTaskAssignRuleList(
-            @RequestParam(value = "modelId", required = false) String modelId,
-            @RequestParam(value = "processDefinitionId", required = false) String processDefinitionId) {
-        return success(taskAssignRuleService.getTaskAssignRuleList(modelId, processDefinitionId));
-    }
-
-    @PostMapping("/create")
-    @Operation(summary = "创建任务分配规则")
-    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:create')")
-    public CommonResult<Long> createTaskAssignRule(@Valid @RequestBody BpmTaskAssignRuleCreateReqVO reqVO) {
-        return success(taskAssignRuleService.createTaskAssignRule(reqVO));
-    }
-
-    @PutMapping("/update")
-    @Operation(summary = "更新任务分配规则")
-    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:update')")
-    public CommonResult<Boolean> updateTaskAssignRule(@Valid @RequestBody BpmTaskAssignRuleUpdateReqVO reqVO) {
-        taskAssignRuleService.updateTaskAssignRule(reqVO);
-        return success(true);
-    }
-}

+ 0 - 24
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/rule/BpmTaskAssignRuleBaseVO.java

@@ -1,24 +0,0 @@
-package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import jakarta.validation.constraints.NotNull;
-import java.util.Set;
-
-/**
- * 流程任务分配规则 Base VO,提供给添加、修改、详细的子 VO 使用
- * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
- */
-@Data
-public class BpmTaskAssignRuleBaseVO {
-
-    @Schema(description = "规则类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "bpm_task_assign_rule_type")
-    @NotNull(message = "规则类型不能为空")
-    private Integer type;
-
-    @Schema(description = "规则值数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
-    @NotNull(message = "规则值数组不能为空")
-    private Set<Long> options;
-
-}

+ 0 - 24
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/rule/BpmTaskAssignRuleCreateReqVO.java

@@ -1,24 +0,0 @@
-package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
-
-import jakarta.validation.constraints.NotEmpty;
-
-@Schema(description = "管理后台 - 流程任务分配规则的创建 Request VO")
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class BpmTaskAssignRuleCreateReqVO extends BpmTaskAssignRuleBaseVO {
-
-    @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
-    @NotEmpty(message = "流程模型的编号不能为空")
-    private String modelId;
-
-    @Schema(description = "流程任务定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
-    @NotEmpty(message = "流程任务定义的编号不能为空")
-    private String taskDefinitionKey;
-
-}

+ 0 - 28
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/rule/BpmTaskAssignRuleRespVO.java

@@ -1,28 +0,0 @@
-package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
-
-@Schema(description = "管理后台 - 流程任务分配规则的 Response VO")
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class BpmTaskAssignRuleRespVO extends BpmTaskAssignRuleBaseVO {
-
-    @Schema(description = "任务分配规则的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
-    private Long id;
-
-    @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
-    private String modelId;
-
-    @Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
-    private String processDefinitionId;
-
-    @Schema(description = "流程任务定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
-    private String taskDefinitionKey;
-    @Schema(description = "流程任务定义的名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "关注芋道")
-    private String taskDefinitionName;
-
-}

+ 0 - 20
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/rule/BpmTaskAssignRuleUpdateReqVO.java

@@ -1,20 +0,0 @@
-package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
-
-import jakarta.validation.constraints.NotNull;
-
-@Schema(description = "管理后台 - 流程任务分配规则的更新 Request VO")
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class BpmTaskAssignRuleUpdateReqVO extends BpmTaskAssignRuleBaseVO {
-
-    @Schema(description = "任务分配规则的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
-    @NotNull(message = "任务分配规则的编号不能为空")
-    private Long id;
-
-}

+ 0 - 40
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmTaskAssignRuleConvert.java

@@ -1,40 +0,0 @@
-package cn.iocoder.yudao.module.bpm.convert.definition;
-
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
-import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
-import org.flowable.bpmn.model.UserTask;
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
-
-import java.util.List;
-import java.util.Map;
-
-@Mapper
-public interface BpmTaskAssignRuleConvert {
-    BpmTaskAssignRuleConvert INSTANCE = Mappers.getMapper(BpmTaskAssignRuleConvert.class);
-
-    default List<BpmTaskAssignRuleRespVO> convertList(List<UserTask> tasks, List<BpmTaskAssignRuleDO> rules) {
-        Map<String, BpmTaskAssignRuleDO> ruleMap = CollectionUtils.convertMap(rules, BpmTaskAssignRuleDO::getTaskDefinitionKey);
-        // 以 UserTask 为主维度,原因是:流程图编辑后,一些规则实际就没用了。
-        return CollectionUtils.convertList(tasks, task -> {
-            BpmTaskAssignRuleRespVO respVO = convert(ruleMap.get(task.getId()));
-            if (respVO == null) {
-                respVO = new BpmTaskAssignRuleRespVO();
-                respVO.setTaskDefinitionKey(task.getId());
-            }
-            respVO.setTaskDefinitionName(task.getName());
-            return respVO;
-        });
-    }
-
-    BpmTaskAssignRuleRespVO convert(BpmTaskAssignRuleDO bean);
-
-    BpmTaskAssignRuleDO convert(BpmTaskAssignRuleCreateReqVO bean);
-
-    BpmTaskAssignRuleDO convert(BpmTaskAssignRuleUpdateReqVO bean);
-
-    List<BpmTaskAssignRuleDO> convertList2(List<BpmTaskAssignRuleRespVO> list);
-}

+ 0 - 83
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmTaskAssignRuleDO.java

@@ -1,83 +0,0 @@
-package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;
-
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import cn.iocoder.yudao.framework.mybatis.core.type.JsonLongSetTypeHandler;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.*;
-
-import java.util.Set;
-
-/**
- * Bpm 任务分配的规则表,用于自定义配置每个任务的负责人、候选人的分配规则。
- * 也就是说,废弃 BPMN 原本的 UserTask 设置的 assignee、candidateUsers 等配置,而是通过使用该规则进行计算对应的负责人。
- *
- * 1. 默认情况下,{@link #processDefinitionId} 为 {@link #PROCESS_DEFINITION_ID_NULL} 值,表示贵改则与流程模型关联
- * 2. 在流程模型部署后,会将他的所有规则记录,复制出一份新部署出来的流程定义,通过设置 {@link #processDefinitionId} 为新的流程定义的编号进行关联
- *
- * @author 芋道源码
- */
-@TableName(value = "bpm_task_assign_rule", autoResultMap = true)
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class BpmTaskAssignRuleDO extends BaseDO {
-
-    /**
-     * {@link #processDefinitionId} 空串,用于标识属于流程模型,而不属于流程定义
-     */
-    public static final String PROCESS_DEFINITION_ID_NULL = "";
-
-    /**
-     * 编号
-     */
-    @TableId
-    private Long id;
-
-    /**
-     * 流程模型编号
-     *
-     * 关联 Model 的 id 属性
-     */
-    private String modelId;
-    /**
-     * 流程定义编号
-     *
-     * 关联 ProcessDefinition 的 id 属性
-     */
-    private String processDefinitionId;
-    /**
-     * 流程任务的定义 Key
-     *
-     * 关联 Task 的 taskDefinitionKey 属性
-     */
-    private String taskDefinitionKey;
-
-    /**
-     * 规则类型
-     *
-     * 枚举 {@link BpmTaskAssignRuleTypeEnum}
-     */
-    @TableField("`type`")
-    private Integer type;
-    /**
-     * 规则值数组,一般关联指定表的编号
-     * 根据 type 不同,对应的值是不同的:
-     *
-     * 1. {@link BpmTaskAssignRuleTypeEnum#ROLE} 时:角色编号
-     * 2. {@link BpmTaskAssignRuleTypeEnum#DEPT_MEMBER} 时:部门编号
-     * 3. {@link BpmTaskAssignRuleTypeEnum#DEPT_LEADER} 时:部门编号
-     * 4. {@link BpmTaskAssignRuleTypeEnum#USER} 时:用户编号
-     * 5. {@link BpmTaskAssignRuleTypeEnum#USER_GROUP} 时:用户组编号
-     * 6. {@link BpmTaskAssignRuleTypeEnum#SCRIPT} 时:脚本编号,目前通过 {@link BpmTaskRuleScriptEnum#getId()} 标识
-     */
-    @TableField(typeHandler = JsonLongSetTypeHandler.class)
-    private Set<Long> options;
-
-}

+ 0 - 37
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/definition/BpmTaskAssignRuleMapper.java

@@ -1,37 +0,0 @@
-package cn.iocoder.yudao.module.bpm.dal.mysql.definition;
-
-import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
-import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
-import org.apache.ibatis.annotations.Mapper;
-import org.springframework.lang.Nullable;
-
-import java.util.List;
-
-@Mapper
-public interface BpmTaskAssignRuleMapper extends BaseMapperX<BpmTaskAssignRuleDO> {
-
-    default List<BpmTaskAssignRuleDO> selectListByProcessDefinitionId(String processDefinitionId,
-                                                                      @Nullable String taskDefinitionKey) {
-        return selectList(new QueryWrapperX<BpmTaskAssignRuleDO>()
-                .eq("process_definition_id", processDefinitionId)
-                .eqIfPresent("task_definition_key", taskDefinitionKey));
-    }
-
-    default List<BpmTaskAssignRuleDO> selectListByModelId(String modelId) {
-        return selectList(new QueryWrapperX<BpmTaskAssignRuleDO>()
-                .eq("model_id", modelId)
-                .eq("process_definition_id", BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL));
-    }
-
-    default BpmTaskAssignRuleDO selectListByModelIdAndTaskDefinitionKey(String modelId,
-                                                                        String taskDefinitionKey) {
-        return selectOne(new QueryWrapperX<BpmTaskAssignRuleDO>()
-                .eq("model_id", modelId)
-                .eq("process_definition_id", BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL)
-                .eq("task_definition_key", taskDefinitionKey));
-    }
-
-
-
-}

+ 6 - 18
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java

@@ -1,12 +1,12 @@
 package cn.iocoder.yudao.module.bpm.service.definition;
 
-import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
+import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
 import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
@@ -17,10 +17,8 @@ import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCr
 import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
 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.common.engine.impl.util.io.BytesStreamSource;
 import org.flowable.engine.RepositoryService;
 import org.flowable.engine.repository.Deployment;
 import org.flowable.engine.repository.Model;
@@ -168,15 +166,13 @@ public class BpmModelServiceImpl implements BpmModelService {
         // 1.3 校验表单已配
         BpmFormDO form = checkFormConfig(model.getMetaInfo());
         // 1.4 校验任务分配规则已配置
-        taskAssignRuleService.checkTaskAssignRuleAllConfig(id);
+        taskAssignRuleService.checkTaskAssignRuleAllConfig(bpmnBytes);
 
         // 1.5 校验模型是否发生修改。如果未修改,则不允许创建
-        BpmProcessDefinitionCreateReqDTO definitionCreateReqDTO = BpmModelConvert.INSTANCE.convert2(model, form).setBpmnBytes(bpmnBytes);
+        BpmProcessDefinitionCreateReqDTO definitionCreateReqDTO = BpmModelConvert.INSTANCE.convert2(model, form)
+                .setBpmnBytes(bpmnBytes);
         if (processDefinitionService.isProcessDefinitionEquals(definitionCreateReqDTO)) { // 流程定义的信息相等
-            ProcessDefinition oldProcessDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(model.getDeploymentId());
-            if (oldProcessDefinition != null && taskAssignRuleService.isTaskAssignRulesEquals(model.getId(), oldProcessDefinition.getId())) {
-                throw exception(MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS);
-            }
+            throw exception(MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS);
         }
 
         // 2.1 创建流程定义
@@ -189,12 +185,8 @@ public class BpmModelServiceImpl implements BpmModelService {
         ProcessDefinition definition = processDefinitionService.getProcessDefinition(definitionId);
         model.setDeploymentId(definition.getDeploymentId());
         repositoryService.saveModel(model);
-
-        // 2.4 复制任务分配规则
-        taskAssignRuleService.copyTaskAssignRules(id, definition.getId());
     }
 
-
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void deleteModel(String id) {
@@ -229,11 +221,7 @@ public class BpmModelServiceImpl implements BpmModelService {
     @Override
     public BpmnModel getBpmnModel(String id) {
         byte[] bpmnBytes = repositoryService.getModelEditorSource(id);
-        if (ArrayUtil.isEmpty(bpmnBytes)) {
-            return null;
-        }
-        BpmnXMLConverter converter = new BpmnXMLConverter();
-        return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), true, true);
+        return BpmnModelUtils.getBpmnModel(bpmnBytes);
     }
 
     @Override

+ 2 - 14
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java

@@ -23,7 +23,6 @@ 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.common.engine.impl.util.io.BytesStreamSource;
 import org.flowable.engine.RepositoryService;
 import org.flowable.engine.repository.Deployment;
 import org.flowable.engine.repository.ProcessDefinition;
@@ -124,6 +123,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
                 .key(createReqDTO.getKey()).name(createReqDTO.getName()).category(createReqDTO.getCategory())
                 .addBytes(createReqDTO.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, createReqDTO.getBpmnBytes())
                 .tenantId(TenantContextHolder.getTenantIdStr())
+                .disableSchemaValidation() // 禁用 XML Schema 验证,因为有自定义的属性
                 .deploy();
 
         // 设置 ProcessDefinition 的 category 分类
@@ -197,7 +197,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
             return false;
         }
         // 校验 BPMN XML 信息
-        BpmnModel newModel = buildBpmnModel(createReqDTO.getBpmnBytes());
+        BpmnModel newModel = BpmnModelUtils.getBpmnModel(createReqDTO.getBpmnBytes());
         BpmnModel oldModel = getBpmnModel(oldProcessDefinition.getId());
         // 对比字节变化
         if (!BpmnModelUtils.equals(oldModel, newModel)) {
@@ -207,18 +207,6 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
         return true;
     }
 
-    /**
-     * 构建对应的 BPMN Model
-     *
-     * @param bpmnBytes 原始的 BPMN XML 字节数组
-     * @return BPMN Model
-     */
-    private  BpmnModel buildBpmnModel(byte[] bpmnBytes) {
-        // 转换成 BpmnModel 对象
-        BpmnXMLConverter converter = new BpmnXMLConverter();
-        return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), true, true);
-    }
-
     @Override
     public BpmProcessDefinitionExtDO getProcessDefinitionExt(String id) {
         return processDefinitionMapper.selectByProcessDefinitionId(id);

+ 2 - 69
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleService.java

@@ -1,14 +1,7 @@
 package cn.iocoder.yudao.module.bpm.service.definition;
 
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
-import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
 import org.flowable.engine.delegate.DelegateExecution;
-import org.springframework.lang.Nullable;
 
-import jakarta.validation.Valid;
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -18,73 +11,13 @@ import java.util.Set;
  */
 public interface BpmTaskAssignRuleService {
 
-    /**
-     * 获得流程定义的任务分配规则数组
-     *
-     * @param processDefinitionId 流程定义的编号
-     * @param taskDefinitionKey 流程任务定义的 Key。允许空
-     * @return 任务规则数组
-     */
-    List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId,
-                                                                         @Nullable String taskDefinitionKey);
-
-    /**
-     * 获得流程模型的任务规则数组
-     *
-     * @param modelId 流程模型的编号
-     * @return 任务规则数组
-     */
-    List<BpmTaskAssignRuleDO> getTaskAssignRuleListByModelId(String modelId);
-
-    /**
-     * 获得流程定义的任务分配规则数组
-     *
-     * @param modelId 流程模型的编号
-     * @param processDefinitionId 流程定义的编号
-     * @return 任务规则数组
-     */
-    List<BpmTaskAssignRuleRespVO> getTaskAssignRuleList(String modelId, String processDefinitionId);
-
-    /**
-     * 创建任务分配规则
-     *
-     * @param reqVO 创建信息
-     * @return 规则编号
-     */
-    Long createTaskAssignRule(@Valid BpmTaskAssignRuleCreateReqVO reqVO);
-
-    /**
-     * 更新任务分配规则
-     *
-     * @param reqVO 创建信息
-     */
-    void updateTaskAssignRule(@Valid BpmTaskAssignRuleUpdateReqVO reqVO);
-
-    /**
-     * 判断指定流程模型和流程定义的分配规则是否相等
-     *
-     * @param modelId 流程模型编号
-     * @param processDefinitionId 流程定义编号
-     * @return 是否相等
-     */
-    boolean isTaskAssignRulesEquals(String modelId, String processDefinitionId);
-
-    /**
-     * 将流程流程模型的任务分配规则,复制一份给流程定义
-     * 目的:每次流程模型部署时,都会生成一个新的流程定义,此时考虑到每次部署的流程不可变性,所以需要复制一份给该流程定义
-     *
-     * @param fromModelId 流程模型编号
-     * @param toProcessDefinitionId 流程定义编号
-     */
-    void copyTaskAssignRules(String fromModelId, String toProcessDefinitionId);
-
     /**
      * 校验流程模型的任务分配规则全部都配置了
      * 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去!
      *
-     * @param id 流程模型编号
+     * @param bpmnBytes BPMN XML
      */
-    void checkTaskAssignRuleAllConfig(String id);
+    void checkTaskAssignRuleAllConfig(byte[] bpmnBytes);
 
     /**
      * 计算当前执行任务的处理人

+ 64 - 192
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java

@@ -1,20 +1,16 @@
 package cn.iocoder.yudao.module.bpm.service.definition;
 
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
+import cn.iocoder.yudao.framework.common.util.string.StrUtils;
 import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
+import cn.iocoder.yudao.framework.flowable.core.enums.BpmnModelConstants;
 import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
-import cn.iocoder.yudao.module.bpm.convert.definition.BpmTaskAssignRuleConvert;
-import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
-import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper;
 import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants;
 import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
@@ -28,17 +24,16 @@ import cn.iocoder.yudao.module.system.api.permission.RoleApi;
 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 jakarta.annotation.Resource;
 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.common.engine.api.FlowableException;
 import org.flowable.engine.delegate.DelegateExecution;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
-import jakarta.annotation.Resource;
-import jakarta.validation.Valid;
 import java.util.*;
 import java.util.function.Function;
 
@@ -46,7 +41,6 @@ import static cn.hutool.core.text.CharSequenceUtil.format;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
-import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
 
 /**
@@ -58,15 +52,11 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
 public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
 
     @Resource
-    private BpmTaskAssignRuleMapper taskRuleMapper;
-    @Resource
-    @Lazy // 解决循环依赖
-    private BpmModelService modelService;
+    private BpmUserGroupService userGroupService;
     @Resource
     @Lazy // 解决循环依赖
-    private BpmProcessDefinitionService processDefinitionService;
-    @Resource
-    private BpmUserGroupService userGroupService;
+    private BpmProcessInstanceService processInstanceService;
+
     @Resource
     private RoleApi roleApi;
     @Resource
@@ -79,9 +69,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
     private DictDataApi dictDataApi;
     @Resource
     private PermissionApi permissionApi;
-    @Resource
-    @Lazy // 解决循环依赖
-    private BpmProcessInstanceService processInstanceService;
+
     /**
      * 任务分配脚本
      */
@@ -93,130 +81,23 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
     }
 
     @Override
-    public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId,
-                                                                                String taskDefinitionKey) {
-        return taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey);
-    }
-
-    @Override
-    public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByModelId(String modelId) {
-        return taskRuleMapper.selectListByModelId(modelId);
-    }
-
-    @Override
-    public List<BpmTaskAssignRuleRespVO> getTaskAssignRuleList(String modelId, String processDefinitionId) {
-        // 获得规则
-        List<BpmTaskAssignRuleDO> rules = Collections.emptyList();
-        BpmnModel model = null;
-        if (StrUtil.isNotEmpty(modelId)) {
-            rules = getTaskAssignRuleListByModelId(modelId);
-            model = modelService.getBpmnModel(modelId);
-        } else if (StrUtil.isNotEmpty(processDefinitionId)) {
-            rules = getTaskAssignRuleListByProcessDefinitionId(processDefinitionId, null);
-            model = processDefinitionService.getBpmnModel(processDefinitionId);
-        }
-        if (model == null) {
-            return Collections.emptyList();
-        }
-        // 获得用户任务,只有用户任务才可以设置分配规则
-        List<UserTask> userTasks = BpmnModelUtils.getBpmnModelElements(model, UserTask.class);
-        if (CollUtil.isEmpty(userTasks)) {
-            return Collections.emptyList();
-        }
-        // 转换数据
-        return BpmTaskAssignRuleConvert.INSTANCE.convertList(userTasks, rules);
-    }
-
-    @Override
-    public Long createTaskAssignRule(@Valid BpmTaskAssignRuleCreateReqVO reqVO) {
-        // 校验参数
-        validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions());
-        // 校验是否已经配置
-        BpmTaskAssignRuleDO existRule =
-                taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(reqVO.getModelId(), reqVO.getTaskDefinitionKey());
-        if (existRule != null) {
-            throw exception(TASK_ASSIGN_RULE_EXISTS, reqVO.getModelId(), reqVO.getTaskDefinitionKey());
-        }
-
-        // 存储
-        BpmTaskAssignRuleDO rule = BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO)
-                .setProcessDefinitionId(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL); // 只有流程模型,才允许新建
-        taskRuleMapper.insert(rule);
-        return rule.getId();
-    }
-
-    @Override
-    public void updateTaskAssignRule(@Valid BpmTaskAssignRuleUpdateReqVO reqVO) {
-        // 校验参数
-        validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions());
-        // 校验是否存在
-        BpmTaskAssignRuleDO existRule = taskRuleMapper.selectById(reqVO.getId());
-        if (existRule == null) {
-            throw exception(TASK_ASSIGN_RULE_NOT_EXISTS);
-        }
-        // 只允许修改流程模型的规则
-        if (!Objects.equals(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL, existRule.getProcessDefinitionId())) {
-            throw exception(TASK_UPDATE_FAIL_NOT_MODEL);
-        }
-
-        // 执行更新
-        taskRuleMapper.updateById(BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO));
-    }
-
-    @Override
-    public boolean isTaskAssignRulesEquals(String modelId, String processDefinitionId) {
-        // 调用 VO 接口的原因是,过滤掉流程模型不需要的规则,保持和 copyTaskAssignRules 方法的一致性
-        List<BpmTaskAssignRuleRespVO> modelRules = getTaskAssignRuleList(modelId, null);
-        List<BpmTaskAssignRuleRespVO> processInstanceRules = getTaskAssignRuleList(null, processDefinitionId);
-        if (modelRules.size() != processInstanceRules.size()) {
-            return false;
-        }
-
-        // 遍历,匹配对应的规则
-        Map<String, BpmTaskAssignRuleRespVO> processInstanceRuleMap =
-                CollectionUtils.convertMap(processInstanceRules, BpmTaskAssignRuleRespVO::getTaskDefinitionKey);
-        for (BpmTaskAssignRuleRespVO modelRule : modelRules) {
-            BpmTaskAssignRuleRespVO processInstanceRule = processInstanceRuleMap.get(modelRule.getTaskDefinitionKey());
-            if (processInstanceRule == null) {
-                return false;
-            }
-            if (!ObjectUtil.equals(modelRule.getType(), processInstanceRule.getType()) || !ObjectUtil.equal(
-                    modelRule.getOptions(), processInstanceRule.getOptions())) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public void copyTaskAssignRules(String fromModelId, String toProcessDefinitionId) {
-        List<BpmTaskAssignRuleRespVO> rules = getTaskAssignRuleList(fromModelId, null);
-        if (CollUtil.isEmpty(rules)) {
-            return;
-        }
-        // 开始复制
-        List<BpmTaskAssignRuleDO> newRules = BpmTaskAssignRuleConvert.INSTANCE.convertList2(rules);
-        newRules.forEach(rule -> rule.setProcessDefinitionId(toProcessDefinitionId).setId(null).setCreateTime(null)
-                .setUpdateTime(null));
-        taskRuleMapper.insertBatch(newRules);
-    }
-
-    @Override
-    public void checkTaskAssignRuleAllConfig(String id) {
-        // 一个用户任务都没配置,所以无需配置规则
-        List<BpmTaskAssignRuleRespVO> taskAssignRules = getTaskAssignRuleList(id, null);
-        if (CollUtil.isEmpty(taskAssignRules)) {
-            return;
-        }
-        // 校验未配置规则的任务
-        taskAssignRules.forEach(rule -> {
-            if (CollUtil.isEmpty(rule.getOptions())) {
-                throw exception(MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG, rule.getTaskDefinitionName());
+    public void checkTaskAssignRuleAllConfig(byte[] bpmnBytes) {
+        BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);
+        assert bpmnModel != null;
+        List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
+        // 遍历所有的 UserTask,校验审批人配置
+        userTaskList.forEach(userTask -> {
+            // TODO 芋艿:assignType/assignOptions/, 枚举
+            Integer type = NumberUtils.parseInt(userTask.getAttributeValue(BpmnModelConstants.NAMESPACE, "assignType"));
+            String options = userTask.getAttributeValue(BpmnModelConstants.NAMESPACE, "assignOptions");
+            if (type == null || StrUtil.isBlank(options)) {
+                throw exception(MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG, userTask.getName());
             }
+            validTaskAssignRuleOptions(type, StrUtils.splitToLong(options, ","));
         });
     }
 
-    private void validTaskAssignRuleOptions(Integer type, Set<Long> options) {
+    private void validTaskAssignRuleOptions(Integer type, Collection<Long> options) {
         if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.ROLE.getType())) {
             roleApi.validRoleList(options);
         } else if (ObjectUtils.equalsAny(type, BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(),
@@ -247,92 +128,83 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
             return convertSet(assignee, Function.identity());
         }
         // 2. 通过分配规则,计算审批人
-        BpmTaskAssignRuleDO rule = getTaskRule(execution);
-        return calculateTaskCandidateUsers(execution, rule);
+        return calculateTaskCandidateUsers0(execution);
     }
 
     @VisibleForTesting
-    BpmTaskAssignRuleDO getTaskRule(DelegateExecution execution) {
-        List<BpmTaskAssignRuleDO> taskRules = getTaskAssignRuleListByProcessDefinitionId(
-                execution.getProcessDefinitionId(), execution.getCurrentActivityId());
-        if (CollUtil.isEmpty(taskRules)) {
-            throw new FlowableException(format("流程任务({}/{}/{}) 找不到符合的任务规则",
-                    execution.getId(), execution.getProcessDefinitionId(), execution.getCurrentActivityId()));
-        }
-        if (taskRules.size() > 1) {
-            throw new FlowableException(format("流程任务({}/{}/{}) 找到过多任务规则({})",
-                    execution.getId(), execution.getProcessDefinitionId(), execution.getCurrentActivityId()));
-        }
-        return taskRules.get(0);
-    }
-
-    @VisibleForTesting
-    Set<Long> calculateTaskCandidateUsers(DelegateExecution execution, BpmTaskAssignRuleDO rule) {
+    Set<Long> calculateTaskCandidateUsers0(DelegateExecution execution) {
+        // 获得审批人配置
+        // TODO 芋艿:assignType/assignOptions/, 枚举
+        FlowElement flowElement = execution.getCurrentFlowElement();
+        Integer type = NumberUtils.parseInt(flowElement.getAttributeValue(BpmnModelConstants.NAMESPACE, "assignType"));
+        Set<Long> options = StrUtils.splitToLongSet(flowElement.getAttributeValue(BpmnModelConstants.NAMESPACE, "assignOptions"), ",");
+
+        // 计算审批人
         Set<Long> assigneeUserIds = null;
-        if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) {
-            assigneeUserIds = calculateTaskCandidateUsersByRole(rule);
-        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) {
-            assigneeUserIds = calculateTaskCandidateUsersByDeptMember(rule);
-        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) {
-            assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(rule);
-        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.POST.getType(), rule.getType())) {
-            assigneeUserIds = calculateTaskCandidateUsersByPost(rule);
-        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) {
-            assigneeUserIds = calculateTaskCandidateUsersByUser(rule);
-        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType(), rule.getType())) {
-            assigneeUserIds = calculateTaskCandidateUsersByUserGroup(rule);
-        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), rule.getType())) {
-            assigneeUserIds = calculateTaskCandidateUsersByScript(execution, rule);
+        if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), type)) {
+            assigneeUserIds = calculateTaskCandidateUsersByRole(options);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), type)) {
+            assigneeUserIds = calculateTaskCandidateUsersByDeptMember(options);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), type)) {
+            assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(options);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.POST.getType(), type)) {
+            assigneeUserIds = calculateTaskCandidateUsersByPost(options);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), type)) {
+            assigneeUserIds = calculateTaskCandidateUsersByUser(options);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType(), type)) {
+            assigneeUserIds = calculateTaskCandidateUsersByUserGroup(options);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), type)) {
+            assigneeUserIds = calculateTaskCandidateUsersByScript(execution, options);
         }
 
         // 移除被禁用的用户
         removeDisableUsers(assigneeUserIds);
         // 如果候选人为空,抛出异常
         if (CollUtil.isEmpty(assigneeUserIds)) {
-            log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]", execution.getId(),
-                    execution.getProcessDefinitionId(), execution.getCurrentActivityId(), toJsonString(rule));
+            log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}/{}) 找不到候选人]", execution.getId(),
+                    execution.getProcessDefinitionId(), execution.getCurrentActivityId(), type, options);
             throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER);
         }
         return assigneeUserIds;
     }
 
-    private Set<Long> calculateTaskCandidateUsersByRole(BpmTaskAssignRuleDO rule) {
-        return permissionApi.getUserRoleIdListByRoleIds(rule.getOptions());
+    private Set<Long> calculateTaskCandidateUsersByRole(Set<Long> roleIds) {
+        return permissionApi.getUserRoleIdListByRoleIds(roleIds);
     }
 
-    private Set<Long> calculateTaskCandidateUsersByDeptMember(BpmTaskAssignRuleDO rule) {
-        List<AdminUserRespDTO> users = adminUserApi.getUserListByDeptIds(rule.getOptions());
+    private Set<Long> calculateTaskCandidateUsersByDeptMember(Set<Long> deptIds) {
+        List<AdminUserRespDTO> users = adminUserApi.getUserListByDeptIds(deptIds);
         return convertSet(users, AdminUserRespDTO::getId);
     }
 
-    private Set<Long> calculateTaskCandidateUsersByDeptLeader(BpmTaskAssignRuleDO rule) {
-        List<DeptRespDTO> depts = deptApi.getDeptList(rule.getOptions());
+    private Set<Long> calculateTaskCandidateUsersByDeptLeader(Set<Long> deptIds) {
+        List<DeptRespDTO> depts = deptApi.getDeptList(deptIds);
         return convertSet(depts, DeptRespDTO::getLeaderUserId);
     }
 
-    private Set<Long> calculateTaskCandidateUsersByPost(BpmTaskAssignRuleDO rule) {
-        List<AdminUserRespDTO> users = adminUserApi.getUserListByPostIds(rule.getOptions());
+    private Set<Long> calculateTaskCandidateUsersByPost(Set<Long> postIds) {
+        List<AdminUserRespDTO> users = adminUserApi.getUserListByPostIds(postIds);
         return convertSet(users, AdminUserRespDTO::getId);
     }
 
-    private Set<Long> calculateTaskCandidateUsersByUser(BpmTaskAssignRuleDO rule) {
-        return rule.getOptions();
+    private Set<Long> calculateTaskCandidateUsersByUser(Set<Long> userIds) {
+        return userIds;
     }
 
-    private Set<Long> calculateTaskCandidateUsersByUserGroup(BpmTaskAssignRuleDO rule) {
-        List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(rule.getOptions());
+    private Set<Long> calculateTaskCandidateUsersByUserGroup(Set<Long> groupIds) {
+        List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(groupIds);
         Set<Long> userIds = new HashSet<>();
         userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds()));
         return userIds;
     }
 
-    private Set<Long> calculateTaskCandidateUsersByScript(DelegateExecution execution, BpmTaskAssignRuleDO rule) {
+    private Set<Long> calculateTaskCandidateUsersByScript(DelegateExecution execution, Set<Long> scriptIds) {
         // 获得对应的脚本
-        List<BpmTaskAssignScript> scripts = new ArrayList<>(rule.getOptions().size());
-        rule.getOptions().forEach(id -> {
-            BpmTaskAssignScript script = scriptMap.get(id);
+        List<BpmTaskAssignScript> scripts = new ArrayList<>(scriptIds.size());
+        scriptIds.forEach(scriptId -> {
+            BpmTaskAssignScript script = scriptMap.get(scriptId);
             if (script == null) {
-                throw exception(TASK_ASSIGN_SCRIPT_NOT_EXISTS, id);
+                throw exception(TASK_ASSIGN_SCRIPT_NOT_EXISTS, scriptId);
             }
             scripts.add(script);
         });

+ 6 - 5
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupService.java

@@ -1,13 +1,14 @@
 package cn.iocoder.yudao.module.bpm.service.definition;
 
-import java.util.*;
-import jakarta.validation.*;
-
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupCreateReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupUpdateReqVO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import jakarta.validation.Valid;
+
+import java.util.Collection;
+import java.util.List;
 
 /**
  * 用户组 Service 接口
@@ -77,6 +78,6 @@ public interface BpmUserGroupService {
      *
      * @param ids 用户组编号数组
      */
-    void validUserGroups(Set<Long> ids);
+    void validUserGroups(Collection<Long> ids);
 
 }

+ 8 - 8
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupServiceImpl.java

@@ -1,27 +1,27 @@
 package cn.iocoder.yudao.module.bpm.service.definition;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupCreateReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupUpdateReqVO;
 import cn.iocoder.yudao.module.bpm.convert.definition.BpmUserGroupConvert;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
 import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmUserGroupMapper;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import jakarta.annotation.Resource;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
-import jakarta.annotation.Resource;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.USER_GROUP_IS_DISABLE;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS;
 
 /**
  * 用户组 Service 实现类
@@ -89,7 +89,7 @@ public class BpmUserGroupServiceImpl implements BpmUserGroupService {
     }
 
     @Override
-    public void validUserGroups(Set<Long> ids) {
+    public void validUserGroups(Collection<Long> ids) {
         if (CollUtil.isEmpty(ids)) {
             return;
         }

+ 11 - 0
yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/util/BpmnModelUtils.java

@@ -1,9 +1,11 @@
 package cn.iocoder.yudao.framework.flowable.core.util;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ArrayUtil;
 import org.flowable.bpmn.converter.BpmnXMLConverter;
 import org.flowable.bpmn.model.Process;
 import org.flowable.bpmn.model.*;
+import org.flowable.common.engine.impl.util.io.BytesStreamSource;
 
 import java.util.*;
 
@@ -91,6 +93,15 @@ public class BpmnModelUtils {
         return converter.convertToXML(model);
     }
 
+    public static BpmnModel getBpmnModel(byte[] bpmnBytes) {
+        if (ArrayUtil.isEmpty(bpmnBytes)) {
+            return null;
+        }
+        BpmnXMLConverter converter = new BpmnXMLConverter();
+        // 补充说明:由于在 Flowable 中自定义了属性,所以 validateSchema 传递 false
+        return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false);
+    }
+
     // ========== 遍历相关的方法 ==========
 
     /**