Bläddra i källkod

1. 调整 activiti 配置,使用 SQL 初始化 activi 表;
2. 实现内置的几个 Bpm 自定义分配 Script

YunaiV 3 år sedan
förälder
incheckning
9922e2fcf2
12 ändrade filer med 270 tillägg och 44 borttagningar
  1. 5 3
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/definition/BpmTaskRuleScriptEnum.java
  2. 2 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/BpmTaskAssignScript.java
  3. 62 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.java
  4. 27 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java
  5. 27 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java
  6. 30 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignStartUserScript.java
  7. 0 13
      yudao-admin-server/src/main/resources/application-dev.yaml
  8. 0 13
      yudao-admin-server/src/main/resources/application-local.yaml
  9. 13 0
      yudao-admin-server/src/main/resources/application.yaml
  10. 0 0
      yudao-admin-server/src/main/resources/processes/leave-update.bpmn_bak
  11. 8 14
      yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehaviorTest.java
  12. 96 0
      yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX2ScriptTest.java

+ 5 - 3
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/definition/BpmTaskRuleScriptEnum.java

@@ -5,7 +5,7 @@ import lombok.Getter;
 
 /**
  * BPM 任务规则的脚本枚举
- * 目前暂时通过 TODO 硬编码,未来可以考虑 Groovy 动态脚本的方式
+ * 目前暂时通过 TODO 芋艿:硬编码,未来可以考虑 Groovy 动态脚本的方式
  *
  * @author 芋道源码
  */
@@ -13,8 +13,10 @@ import lombok.Getter;
 @AllArgsConstructor
 public enum BpmTaskRuleScriptEnum {
 
-    ONE(1L, ""),
-    TWO(2L, "");
+    START_USER(10L, "流程发起人"),
+
+    LEADER_X1(20L, "流程发起人的一级领导"),
+    LEADER_X2(21L, "流程发起人的二级领导");
 
     /**
      * 脚本编号

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

@@ -4,6 +4,7 @@ import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleScri
 import org.activiti.engine.impl.persistence.entity.TaskEntity;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * Bpm 任务分配的自定义 Script 脚本
@@ -22,7 +23,7 @@ public interface BpmTaskAssignScript {
      * @param task 任务
      * @return 候选人用户的编号数组
      */
-    List<Long> calculateTaskCandidateUsers(TaskEntity task);
+    Set<Long> calculateTaskCandidateUsers(TaskEntity task);
 
     /**
      * 获得枚举值

+ 62 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.java

@@ -0,0 +1,62 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.impl;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.BpmTaskAssignScript;
+import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.dept.SysDeptDO;
+import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysDeptService;
+import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
+import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
+import org.activiti.engine.impl.persistence.entity.TaskEntity;
+import org.springframework.util.Assert;
+
+import javax.annotation.Resource;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
+import static java.util.Collections.emptySet;
+
+/**
+ * 分配给发起人的 Leader 审批的 Script 实现类
+ * 目前 Leader 的定义是,
+ *
+ * @author 芋道源码
+ */
+public abstract class BpmTaskAssignLeaderAbstractScript implements BpmTaskAssignScript {
+
+    @Resource
+    private SysUserService userService;
+    @Resource
+    private SysDeptService deptService;
+
+    protected Set<Long> calculateTaskCandidateUsers(TaskEntity task, int level) {
+        Assert.isTrue(level > 0, "level 必须大于 0");
+        // 获得发起人
+        Long startUserId = Long.parseLong(task.getProcessInstance().getStartUserId());
+        // 获得对应 leve 的部门
+        SysDeptDO dept = null;
+        for (int i = 0; i < level; i++) {
+            // 获得 level 对应的部门
+            if (dept == null) {
+                dept = getStartUserDept(startUserId);
+                if (dept == null) { // 找不到发起人的部门,所以无法使用该规则
+                    return emptySet();
+                }
+            } else {
+                SysDeptDO parentDept = deptService.getDept(dept.getParentId());
+                if (parentDept == null) { // 找不到父级部门,所以只好结束寻找。原因是:例如说,级别比较高的人,所在部门层级比较少
+                    break;
+                }
+                dept = parentDept;
+            }
+        }
+        return dept.getLeaderUserId() != null ? asSet(dept.getLeaderUserId()) : emptySet();
+    }
+
+    private SysDeptDO getStartUserDept(Long startUserId) {
+        SysUserDO startUser = userService.getUser(startUserId);
+        if (startUser.getDeptId() == null) { // 找不到部门,所以无法使用该规则
+            return null;
+        }
+        return deptService.getDept(startUser.getDeptId());
+    }
+
+}

+ 27 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.impl;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleScriptEnum;
+import org.activiti.engine.impl.persistence.entity.TaskEntity;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 分配给发起人的一级 Leader 审批的 Script 实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskAssignLeaderX1Script extends BpmTaskAssignLeaderAbstractScript {
+
+    @Override
+    public Set<Long> calculateTaskCandidateUsers(TaskEntity task) {
+        return calculateTaskCandidateUsers(task, 1);
+    }
+
+    @Override
+    public BpmTaskRuleScriptEnum getEnum() {
+        return BpmTaskRuleScriptEnum.LEADER_X1;
+    }
+
+}

+ 27 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.impl;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleScriptEnum;
+import org.activiti.engine.impl.persistence.entity.TaskEntity;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 分配给发起人的二级 Leader 审批的 Script 实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskAssignLeaderX2Script extends BpmTaskAssignLeaderAbstractScript {
+
+    @Override
+    public Set<Long> calculateTaskCandidateUsers(TaskEntity task) {
+        return calculateTaskCandidateUsers(task, 2);
+    }
+
+    @Override
+    public BpmTaskRuleScriptEnum getEnum() {
+        return BpmTaskRuleScriptEnum.LEADER_X2;
+    }
+
+}

+ 30 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignStartUserScript.java

@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.impl;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleScriptEnum;
+import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.BpmTaskAssignScript;
+import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
+import org.activiti.engine.impl.persistence.entity.TaskEntity;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 分配给发起人审批的 Script 实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskAssignStartUserScript implements BpmTaskAssignScript {
+
+    @Override
+    public Set<Long> calculateTaskCandidateUsers(TaskEntity task) {
+        Long userId = Long.parseLong(task.getProcessInstance().getStartUserId());
+        return SetUtils.asSet(userId);
+    }
+
+    @Override
+    public BpmTaskRuleScriptEnum getEnum() {
+        return BpmTaskRuleScriptEnum.START_USER;
+    }
+
+}

+ 0 - 13
yudao-admin-server/src/main/resources/application-dev.yaml

@@ -61,19 +61,6 @@ spring:
     port: 6379 # 端口
     database: 1 # 数据库索引
 
-  # 工作流 Activiti 配置
-  activiti:
-    # 1. false:默认值,activiti启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常
-    # 2. true:启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表
-    # 3. create_drop:启动时自动创建表,关闭时自动删除表
-    # 4. drop_create:启动时,删除旧表,再创建新表
-    database-schema-update: true
-    # activiti7 默认不生成历史信息表,需手动设置开启
-    db-history-used: true
-    check-process-definitions: true
-    #full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数
-    history-level: full
-
 --- #################### 定时任务相关配置 ####################
 
 # Quartz 配置项,对应 QuartzProperties 配置类

+ 0 - 13
yudao-admin-server/src/main/resources/application-local.yaml

@@ -61,19 +61,6 @@ spring:
     port: 6379 # 端口
     database: 0 # 数据库索引
 
-  # 工作流 Activiti 配置
-  activiti:
-    #1.false:默认值,activiti启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常
-    #2.true:启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表
-    #3.create_drop:启动时自动创建表,关闭时自动删除表
-    #4.drop_create:启动时,删除旧表,再创建新表
-    database-schema-update: true
-    #activiti7默认不生成历史信息表,需手动设置开启
-    db-history-used: true
-    check-process-definitions: true
-    #full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数
-    history-level: full
-
 --- #################### 定时任务相关配置 ####################
 
 # Quartz 配置项,对应 QuartzProperties 配置类

+ 13 - 0
yudao-admin-server/src/main/resources/application.yaml

@@ -20,6 +20,19 @@ spring:
       write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳
       fail-on-empty-beans: false # 允许序列化无属性的 Bean
 
+  # 工作流 Activiti 配置
+  activiti:
+    # 1.false: 默认值,activiti启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常
+    # 2.true: 启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表
+    # 3.create_drop: 启动时自动创建表,关闭时自动删除表
+    # 4.drop_create: 启动时,删除旧表,再创建新表
+    database-schema-update: false # 设置为 false,可通过 sql/activiti.sql 初始化
+    # activiti7 默认不生成历史信息表,需手动设置开启
+    db-history-used: true
+    check-process-definitions: true
+    # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数
+    history-level: full
+
 # MyBatis Plus 的配置项
 mybatis-plus:
 #  在 mybatis-config/mybatis-config.xml 中设置 TODO jason:看看有没其它解决方案

+ 0 - 0
yudao-admin-server/src/main/resources/processes/leave-update.bpmn → yudao-admin-server/src/main/resources/processes/leave-update.bpmn_bak


+ 8 - 14
yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehaviorTest.java

@@ -15,25 +15,19 @@ import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
 import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
-import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
-import cn.iocoder.yudao.framework.test.core.util.RandomUtils;
 import org.activiti.engine.impl.persistence.entity.TaskEntity;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
 
 import java.util.*;
-import java.util.function.Consumer;
-import java.util.function.Function;
 
 import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.*;
 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
+import static java.util.Collections.singleton;
 import static java.util.Collections.singletonList;
 import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.ArgumentMatchers.anySet;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
@@ -153,31 +147,31 @@ class BpmUserTaskActivitiBehaviorTest extends BaseMockitoUnitTest {
     @Test
     public void testCalculateTaskCandidateUsers_Script() {
         // 准备参数
-        BpmTaskAssignRuleDO rule = new BpmTaskAssignRuleDO().setOptions(asSet(1L, 2L))
+        BpmTaskAssignRuleDO rule = new BpmTaskAssignRuleDO().setOptions(asSet(20L, 21L))
                 .setType(BpmTaskAssignRuleTypeEnum.SCRIPT.getType());
         // mock 方法
         BpmTaskAssignScript script1 = new BpmTaskAssignScript() {
 
             @Override
-            public List<Long> calculateTaskCandidateUsers(TaskEntity task) {
-                return singletonList(11L);
+            public Set<Long> calculateTaskCandidateUsers(TaskEntity task) {
+                return singleton(11L);
             }
 
             @Override
             public BpmTaskRuleScriptEnum getEnum() {
-                return BpmTaskRuleScriptEnum.ONE;
+                return BpmTaskRuleScriptEnum.LEADER_X1;
             }
         };
         BpmTaskAssignScript script2 = new BpmTaskAssignScript() {
 
             @Override
-            public List<Long> calculateTaskCandidateUsers(TaskEntity task) {
-                return singletonList(22L);
+            public Set<Long> calculateTaskCandidateUsers(TaskEntity task) {
+                return singleton(22L);
             }
 
             @Override
             public BpmTaskRuleScriptEnum getEnum() {
-                return BpmTaskRuleScriptEnum.TWO;
+                return BpmTaskRuleScriptEnum.LEADER_X2;
             }
         };
         behavior.setScripts(Arrays.asList(script1, script2));

+ 96 - 0
yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX2ScriptTest.java

@@ -0,0 +1,96 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.impl;
+
+import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.dept.SysDeptDO;
+import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysDeptService;
+import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
+import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import org.activiti.engine.impl.persistence.entity.ExecutionEntityImpl;
+import org.activiti.engine.impl.persistence.entity.TaskEntity;
+import org.activiti.engine.impl.persistence.entity.TaskEntityImpl;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+import javax.annotation.Resource;
+
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskAssignLeaderX2Script script;
+
+    @Mock
+    private SysUserService userService;
+    @Mock
+    private SysDeptService deptService;
+
+    @Test
+    public void testCalculateTaskCandidateUsers_noDept() {
+        // 准备参数
+        TaskEntity task = buildTaskEntity(1L);
+        // mock 方法(startUser)
+        SysUserDO startUser = randomPojo(SysUserDO.class, o -> o.setDeptId(10L));
+        when(userService.getUser(eq(1L))).thenReturn(startUser);
+
+        // 调用
+        Set<Long> result = script.calculateTaskCandidateUsers(task);
+        // 断言
+        assertEquals(0, result.size());
+    }
+
+    @Test
+    public void testCalculateTaskCandidateUsers_noParentDept() {
+        // 准备参数
+        TaskEntity task = buildTaskEntity(1L);
+        // mock 方法(startUser)
+        SysUserDO startUser = randomPojo(SysUserDO.class, o -> o.setDeptId(10L));
+        when(userService.getUser(eq(1L))).thenReturn(startUser);
+        SysDeptDO startUserDept = randomPojo(SysDeptDO.class, o -> o.setId(10L).setParentId(100L)
+                .setLeaderUserId(20L));
+        when(deptService.getDept(eq(10L))).thenReturn(startUserDept);
+
+        // 调用
+        Set<Long> result = script.calculateTaskCandidateUsers(task);
+        // 断言
+        assertEquals(asSet(20L), result);
+    }
+
+    @Test
+    public void testCalculateTaskCandidateUsers_existParentDept() {
+        // 准备参数
+        TaskEntity task = buildTaskEntity(1L);
+        // mock 方法(startUser)
+        SysUserDO startUser = randomPojo(SysUserDO.class, o -> o.setDeptId(10L));
+        when(userService.getUser(eq(1L))).thenReturn(startUser);
+        SysDeptDO startUserDept = randomPojo(SysDeptDO.class, o -> o.setId(10L).setParentId(100L)
+                .setLeaderUserId(20L));
+        when(deptService.getDept(eq(10L))).thenReturn(startUserDept);
+        // mock 方法(父 dept)
+        SysDeptDO parentDept = randomPojo(SysDeptDO.class, o -> o.setId(100L).setParentId(1000L)
+                .setLeaderUserId(200L));
+        when(deptService.getDept(eq(100L))).thenReturn(parentDept);
+
+        // 调用
+        Set<Long> result = script.calculateTaskCandidateUsers(task);
+        // 断言
+        assertEquals(asSet(200L), result);
+    }
+
+    @SuppressWarnings("SameParameterValue")
+    private TaskEntity buildTaskEntity(Long startUserId) {
+        TaskEntityImpl task = new TaskEntityImpl();
+        task.setProcessInstance(new ExecutionEntityImpl());
+        task.getProcessInstance().setStartUserId(String.valueOf(startUserId));
+        return task;
+    }
+
+}