浏览代码

初步实现 BpmUserTaskActivitiBehavior 的规则

YunaiV 3 年之前
父节点
当前提交
e863b60300
共有 11 个文件被更改,包括 234 次插入14 次删除
  1. 1 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/model/BpmModelConvert.java
  2. 7 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/definition/BpmProcessDefinitionExtDO.java
  3. 15 3
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/definition/BpmTaskRuleDO.java
  4. 21 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/definition/BpmTaskRuleMapper.java
  5. 5 2
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/config/BpmActivitiConfiguration.java
  6. 8 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmActivityBehaviorFactory.java
  7. 95 2
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehavior.java
  8. 36 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/BpmTaskRuleService.java
  9. 5 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/dto/BpmDefinitionCreateReqDTO.java
  10. 6 6
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/impl/BpmModelServiceImpl.java
  11. 35 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/impl/BpmTaskRuleServiceImpl.java

+ 1 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/model/BpmModelConvert.java

@@ -80,6 +80,7 @@ public interface BpmModelConvert {
 
     default BpmDefinitionCreateReqDTO convert2(Model model) {
         BpmDefinitionCreateReqDTO createReqDTO = new BpmDefinitionCreateReqDTO();
+        createReqDTO.setModelId(model.getId());
         createReqDTO.setName(model.getName());
         createReqDTO.setKey(model.getKey());
         createReqDTO.setCategory(model.getCategory());

+ 7 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/definition/BpmProcessDefinitionExtDO.java

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.*;
+import org.activiti.engine.repository.Model;
 import org.activiti.engine.repository.ProcessDefinition;
 
 /**
@@ -33,6 +34,12 @@ public class BpmProcessDefinitionExtDO extends BaseDO {
      * 关联 {@link ProcessDefinition#getId()}
      */
     private String processDefinitionId;
+    /**
+     * 流程模型的编号
+     *
+     * 关联 {@link Model#getId()}
+     */
+    private String modelId;
     /**
      * 描述
      */

+ 15 - 3
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/definition/BpmTaskRuleDO.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition;
 import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleScriptEnum;
 import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleTypeEnum;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.framework.mybatis.core.type.JsonLongSetTypeHandler;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -13,9 +14,14 @@ import org.activiti.engine.repository.ProcessDefinition;
 import org.activiti.engine.task.Task;
 
 import java.util.List;
+import java.util.Set;
 
 /**
- * Bpm 任务规则表
+ * Bpm 任务规则表,用于自定义配置每个任务的负责人的分配规则。
+ * 也就是说,废弃 BPMN 原本的 UserTask 设置的 assignee、candidateUsers 等配置,而是通过使用该规则进行计算对应的负责人。
+ *
+ * 1. 默认情况下,{@link #processDefinitionId} 为 {@link #PROCESS_DEFINITION_ID_NULL} 值,表示贵改则与流程模型关联
+ * 2. 在流程模型部署后,会将他的所有规则记录,复制出一份新部署出来的流程定义,通过设置 {@link #processDefinitionId} 为新的流程定义的编号进行关联
  *
  * @author 芋道源码
  */
@@ -28,6 +34,11 @@ import java.util.List;
 @AllArgsConstructor
 public class BpmTaskRuleDO extends BaseDO {
 
+    /**
+     * {@link #processDefinitionId} 空串,用于标识属于流程模型,而不属于流程定义
+     */
+    private static final String PROCESS_DEFINITION_ID_NULL = "";
+
     /**
      * 编号
      */
@@ -58,6 +69,7 @@ public class BpmTaskRuleDO extends BaseDO {
      *
      * 枚举 {@link BpmTaskRuleTypeEnum}
      */
+    @TableField("`type`")
     private Integer type;
     /**
      * 规则值数组,一般关联指定表的编号
@@ -70,7 +82,7 @@ public class BpmTaskRuleDO extends BaseDO {
      * 5. {@link BpmTaskRuleTypeEnum#USER_GROUP} 时:用户组编号
      * 6. {@link BpmTaskRuleTypeEnum#SCRIPT} 时:脚本编号,目前通过 {@link BpmTaskRuleScriptEnum#getId()} 标识
      */
-    @TableField(typeHandler = JacksonTypeHandler.class)
-    private List<Long> values;
+    @TableField(typeHandler = JsonLongSetTypeHandler.class)
+    private Set<Long> options;
 
 }

+ 21 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/definition/BpmTaskRuleMapper.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.definition;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmTaskRuleDO;
+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 BpmTaskRuleMapper extends BaseMapperX<BpmTaskRuleDO> {
+
+    default List<BpmTaskRuleDO> selectListByProcessDefinitionId(String processDefinitionId,
+                                                                @Nullable String taskDefinitionKey) {
+        return selectList(new QueryWrapperX<BpmTaskRuleDO>()
+                .eq("process_definition_id", processDefinitionId)
+                .eqIfPresent("task_definition_key", taskDefinitionKey));
+    }
+
+}

+ 5 - 2
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/config/BpmActivitiConfiguration.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.config;
 
 import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.BpmActivityBehaviorFactory;
 import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.listener.BpmTackActivitiEventListener;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskRuleService;
 import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -44,8 +45,10 @@ public class BpmActivitiConfiguration {
     }
 
     @Bean
-    public BpmActivityBehaviorFactory bpmActivityBehaviorFactory() {
-        return new BpmActivityBehaviorFactory();
+    public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskRuleService taskRuleService) {
+        BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory();
+        bpmActivityBehaviorFactory.setBpmTaskRuleService(taskRuleService);
+        return bpmActivityBehaviorFactory;
     }
 
 }

+ 8 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmActivityBehaviorFactory.java

@@ -1,8 +1,10 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior;
 
 import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.BpmUserTaskActivitiBehavior;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskRuleService;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import lombok.Setter;
 import lombok.ToString;
 import org.activiti.bpmn.model.UserTask;
 import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
@@ -19,9 +21,14 @@ import org.activiti.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFacto
 @ToString(callSuper = true)
 public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory {
 
+    @Setter
+    private BpmTaskRuleService bpmTaskRuleService;
+
     @Override
     public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) {
-        return new BpmUserTaskActivitiBehavior(userTask);
+        BpmUserTaskActivitiBehavior userTaskActivityBehavior = new BpmUserTaskActivitiBehavior(userTask);
+        userTaskActivityBehavior.setBpmTaskRuleService(bpmTaskRuleService);
+        return userTaskActivityBehavior;
     }
 
     // TODO 芋艿:并行任务 ParallelMultiInstanceBehavior

+ 95 - 2
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehavior.java

@@ -1,6 +1,16 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmTaskRuleDO;
+import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleTypeEnum;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskRuleService;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
+import lombok.Setter;
 import org.activiti.bpmn.model.UserTask;
+import org.activiti.engine.ActivitiException;
 import org.activiti.engine.delegate.DelegateExecution;
 import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
 import org.activiti.engine.impl.el.ExpressionManager;
@@ -8,6 +18,8 @@ import org.activiti.engine.impl.persistence.entity.TaskEntity;
 import org.activiti.engine.impl.persistence.entity.TaskEntityManager;
 
 import java.util.List;
+import java.util.Objects;
+import java.util.Set;
 
 /**
  * 自定义的流程任务的 assignee 负责人的分配
@@ -16,6 +28,9 @@ import java.util.List;
  */
 public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
 
+    @Setter
+    private BpmTaskRuleService bpmTaskRuleService;
+
     public BpmUserTaskActivitiBehavior(UserTask userTask) {
         super(userTask);
     }
@@ -24,8 +39,86 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
     protected void handleAssignments(TaskEntityManager taskEntityManager,
                                      String assignee, String owner, List<String> candidateUsers, List<String> candidateGroups,
                                      TaskEntity task, ExpressionManager expressionManager, DelegateExecution execution) {
-        System.out.println("");
-        taskEntityManager.changeTaskAssignee(task, "1");
+        // 获得任务的规则
+        BpmTaskRuleDO rule = getTaskRule(task);
+        // 获得任务的候选用户们
+        Set<Long> candidateUserIds = calculateTaskCandidateUsers(task, rule);
+        // 设置负责人
+        Long assigneeUserId = chooseTaskAssignee(candidateUserIds);
+        taskEntityManager.changeTaskAssignee(task, String.valueOf(assigneeUserId));
+        // 设置候选人们
+        candidateUserIds.remove(assigneeUserId); // 已经成为负责人了,就不要在扮演候选人了
+        if (CollUtil.isNotEmpty(candidateUserIds)) {
+            task.addCandidateUsers(CollectionUtils.convertSet(candidateUserIds, String::valueOf));
+        }
+    }
+
+    private BpmTaskRuleDO getTaskRule(TaskEntity task) {
+        List<BpmTaskRuleDO> taskRules = bpmTaskRuleService.getTaskRulesByProcessDefinitionId(task.getProcessDefinitionId(),
+                task.getTaskDefinitionKey());
+        if (CollUtil.isEmpty(taskRules)) {
+            throw new ActivitiException(StrUtil.format("流程任务({}/{}/{}) 找不到符合的任务规则",
+                    task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey()));
+        }
+        if (taskRules.size() > 1) {
+            throw new ActivitiException(StrUtil.format("流程任务({}/{}/{}) 找到过多任务规则({})",
+                    task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), taskRules.size()));
+        }
+        return taskRules.get(0);
+    }
+
+    private Long chooseTaskAssignee(Set<Long> candidateUserIds) {
+        // TODO 芋艿:未来可以优化下,改成轮询的策略
+        int index = RandomUtil.randomInt(candidateUserIds.size());
+        return CollUtil.get(candidateUserIds, index);
+    }
+
+    private Set<Long> calculateTaskCandidateUsers(TaskEntity task, BpmTaskRuleDO rule) {
+        Set<Long> assigneeUserIds = null;
+        if (Objects.equals(BpmTaskRuleTypeEnum.ROLE.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule);
+        } else if (Objects.equals(BpmTaskRuleTypeEnum.DEPT.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByDept(task, rule);
+        } else if (Objects.equals(BpmTaskRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByDept(task, rule);
+        } else if (Objects.equals(BpmTaskRuleTypeEnum.USER.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule);
+        } else if (Objects.equals(BpmTaskRuleTypeEnum.USER_GROUP.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule);
+        }
+
+        // TODO 芋艿:统一过滤掉被禁用的用户
+        // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人
+        if (CollUtil.isEmpty(assigneeUserIds)) {
+            throw new ActivitiException(StrUtil.format("流程任务({}/{}/{}) 任务规则({}) 找不到候选人",
+                    task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey()));
+        }
+        return assigneeUserIds;
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByRole(TaskEntity task, BpmTaskRuleDO rule) {
+        throw new UnsupportedOperationException("暂不支持该任务规则");
+    }
+
+
+    private Set<Long> calculateTaskCandidateUsersByDept(TaskEntity task, BpmTaskRuleDO rule) {
+        throw new UnsupportedOperationException("暂不支持该任务规则");
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByDeptLeader(TaskEntity task, BpmTaskRuleDO rule) {
+        throw new UnsupportedOperationException("暂不支持该任务规则");
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByUser(TaskEntity task, BpmTaskRuleDO rule) {
+        return rule.getOptions();
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByUserGroup(TaskEntity task, BpmTaskRuleDO rule) {
+        throw new UnsupportedOperationException("暂不支持该任务规则");
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByScript(TaskEntity task, BpmTaskRuleDO rule) {
+        throw new UnsupportedOperationException("暂不支持该任务规则");
     }
 
 }

+ 36 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/BpmTaskRuleService.java

@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.service.definition;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmTaskRuleDO;
+import org.springframework.lang.Nullable;
+
+import java.util.List;
+
+/**
+ * BPM 任务规则 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface BpmTaskRuleService {
+
+    /**
+     * 获得流程定义的任务规则数组
+     *
+     * @param processDefinitionId 流程定义的编号
+     * @param taskDefinitionKey 流程任务定义的 Key。允许空
+     * @return 任务规则数组
+     */
+    List<BpmTaskRuleDO> getTaskRulesByProcessDefinitionId(String processDefinitionId,
+                                                          @Nullable String taskDefinitionKey);
+
+    /**
+     * 获得流程模型的任务规则数组
+     *
+     * @param modelId 流程模型的编号
+     * @return 任务规则数组
+     */
+    List<BpmTaskRuleDO> getTaskRulesByModelId(Long modelId);
+
+    // TODO 芋艿:创建任务规则
+    // TODO 芋艿:复制任务规则
+
+}

+ 5 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/dto/BpmDefinitionCreateReqDTO.java

@@ -10,6 +10,11 @@ import javax.validation.constraints.NotEmpty;
 @Data
 public class BpmDefinitionCreateReqDTO {
 
+    /**
+     * 流程模型的编号
+     */
+    @NotEmpty(message = "流程模型编号不能为空")
+    private String modelId;
     /**
      * 流程标识
      */

+ 6 - 6
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/impl/BpmModelServiceImpl.java

@@ -214,11 +214,11 @@ public class BpmModelServiceImpl implements BpmModelService {
         }
     }
 
-    public static void main(String[] args) {
-        // 创建转换对象
-        BpmnXMLConverter converter = new BpmnXMLConverter();
-        BpmnModel bpmnModel = converter.convertToBpmnModel(new StringStreamSource(""), true, true);
-        bpmnModel.getProcesses().get(0).getId()
-    }
+//    public static void main(String[] args) {
+//        // 创建转换对象
+//        BpmnXMLConverter converter = new BpmnXMLConverter();
+//        BpmnModel bpmnModel = converter.convertToBpmnModel(new StringStreamSource(""), true, true);
+//        bpmnModel.getProcesses().get(0).getId()
+//    }
 
 }

+ 35 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/impl/BpmTaskRuleServiceImpl.java

@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.service.definition.impl;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmTaskRuleDO;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.definition.BpmTaskRuleMapper;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskRuleService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * BPM 任务规则 Service 实现类
+ */
+@Service
+@Validated
+@Slf4j
+public class BpmTaskRuleServiceImpl implements BpmTaskRuleService {
+
+    @Resource
+    private BpmTaskRuleMapper taskRuleMapper;
+
+    @Override
+    public List<BpmTaskRuleDO> getTaskRulesByProcessDefinitionId(String processDefinitionId,
+                                                                 String taskDefinitionKey) {
+        return taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey);
+    }
+
+    @Override
+    public List<BpmTaskRuleDO> getTaskRulesByModelId(Long modelId) {
+        return null;
+    }
+
+}