diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/definition/BpmTaskRuleScriptEnum.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/definition/BpmTaskRuleScriptEnum.java index ebc3f50de..571d37f87 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/definition/BpmTaskRuleScriptEnum.java +++ b/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, "流程发起人的二级领导"); /** * 脚本编号 diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/BpmTaskAssignScript.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/BpmTaskAssignScript.java index 0e881e703..b70b1ce72 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/BpmTaskAssignScript.java +++ b/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 calculateTaskCandidateUsers(TaskEntity task); + Set calculateTaskCandidateUsers(TaskEntity task); /** * 获得枚举值 diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.java new file mode 100644 index 000000000..cfb3b2a22 --- /dev/null +++ b/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 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()); + } + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java new file mode 100644 index 000000000..d29029bb3 --- /dev/null +++ b/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 calculateTaskCandidateUsers(TaskEntity task) { + return calculateTaskCandidateUsers(task, 1); + } + + @Override + public BpmTaskRuleScriptEnum getEnum() { + return BpmTaskRuleScriptEnum.LEADER_X1; + } + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java new file mode 100644 index 000000000..35d7f7356 --- /dev/null +++ b/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 calculateTaskCandidateUsers(TaskEntity task) { + return calculateTaskCandidateUsers(task, 2); + } + + @Override + public BpmTaskRuleScriptEnum getEnum() { + return BpmTaskRuleScriptEnum.LEADER_X2; + } + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignStartUserScript.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignStartUserScript.java new file mode 100644 index 000000000..3f7617fdb --- /dev/null +++ b/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 calculateTaskCandidateUsers(TaskEntity task) { + Long userId = Long.parseLong(task.getProcessInstance().getStartUserId()); + return SetUtils.asSet(userId); + } + + @Override + public BpmTaskRuleScriptEnum getEnum() { + return BpmTaskRuleScriptEnum.START_USER; + } + +} diff --git a/yudao-admin-server/src/main/resources/application-dev.yaml b/yudao-admin-server/src/main/resources/application-dev.yaml index 57026afec..32d261b33 100644 --- a/yudao-admin-server/src/main/resources/application-dev.yaml +++ b/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 配置类 diff --git a/yudao-admin-server/src/main/resources/application-local.yaml b/yudao-admin-server/src/main/resources/application-local.yaml index 10d12e95a..5fc3e9eca 100644 --- a/yudao-admin-server/src/main/resources/application-local.yaml +++ b/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 配置类 diff --git a/yudao-admin-server/src/main/resources/application.yaml b/yudao-admin-server/src/main/resources/application.yaml index b6866ffe7..159ceb56b 100644 --- a/yudao-admin-server/src/main/resources/application.yaml +++ b/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:看看有没其它解决方案 diff --git a/yudao-admin-server/src/main/resources/processes/leave-update.bpmn b/yudao-admin-server/src/main/resources/processes/leave-update.bpmn_bak similarity index 100% rename from yudao-admin-server/src/main/resources/processes/leave-update.bpmn rename to yudao-admin-server/src/main/resources/processes/leave-update.bpmn_bak diff --git a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehaviorTest.java b/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehaviorTest.java index 39b88e436..21fe1fb88 100644 --- a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehaviorTest.java +++ b/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 calculateTaskCandidateUsers(TaskEntity task) { - return singletonList(11L); + public Set calculateTaskCandidateUsers(TaskEntity task) { + return singleton(11L); } @Override public BpmTaskRuleScriptEnum getEnum() { - return BpmTaskRuleScriptEnum.ONE; + return BpmTaskRuleScriptEnum.LEADER_X1; } }; BpmTaskAssignScript script2 = new BpmTaskAssignScript() { @Override - public List calculateTaskCandidateUsers(TaskEntity task) { - return singletonList(22L); + public Set calculateTaskCandidateUsers(TaskEntity task) { + return singleton(22L); } @Override public BpmTaskRuleScriptEnum getEnum() { - return BpmTaskRuleScriptEnum.TWO; + return BpmTaskRuleScriptEnum.LEADER_X2; } }; behavior.setScripts(Arrays.asList(script1, script2)); diff --git a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX2ScriptTest.java b/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX2ScriptTest.java new file mode 100644 index 000000000..ec12b5467 --- /dev/null +++ b/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 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 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 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; + } + +}