mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-30 09:48:43 +08:00 
			
		
		
		
	1. 调整 activiti 配置,使用 SQL 初始化 activi 表;
2. 实现内置的几个 Bpm 自定义分配 Script
This commit is contained in:
		| @@ -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, "流程发起人的二级领导"); | ||||
|  | ||||
|     /** | ||||
|      * 脚本编号 | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
|     /** | ||||
|      * 获得枚举值 | ||||
|   | ||||
| @@ -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()); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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 配置类 | ||||
|   | ||||
| @@ -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 配置类 | ||||
|   | ||||
| @@ -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:看看有没其它解决方案 | ||||
|   | ||||
| @@ -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)); | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV