摘要:当一个根级的结束时,就会进行上述的缓存对象统一的持久化。解决的办法也很简单,改为监听,判断是否时需要修改的任务实体即可。这样后面要进行驳回时,只要通过这样关系表,马上就可以定位到要驳回到的任务了。
1.前言
本文内容主要为以下两点,因为内容有交叉,所以会放在一起介绍。
1.以自由跳转为基础实现不改变原先任务id的驳回
关于Activiti6动态跳转可以查看我的另一篇文章Activiti6实现自由跳转
2.java类方式进行Activiti6配置、spring boot集成
因为有一些自定义的需求,如流程字体、自动部署、自定义监听器等,直接引入[activit-spring-boot]又没有必要,所以参考activit6源码中[activit-spring-boot]模块的代码完成。
关于自由跳转的内容我就不再多说,主要介绍如何修改Activiti生成的实体的id,以达到驳回时重新生成的任务id与原先的任务id一致。(某些业务场景下可能会用到,例如某流程中A环节提交的表单与task id绑定,当环节提交又被驳回时,为保证表单内容与任务关系不变,驳回后的任务id与原先任务id要一致)
2.1前提知识1.Activiti持久化实体的过程时先创建实体对象,记录到缓存中,在完成执行后统一进行缓存对象的持久化,并清空缓存。
2.Activiti采用命令模式执行操作,所有操作都时一个CMD。执行一个CMD的时候会创建一个上下文环境,包含待持久化的实体缓存等,如果在CMD中嵌套执行CMD,新的CMD默认会使用上级上下文环境。当一个根级的CMD结束时,Activiti就会进行上述的缓存对象统一的持久化。
3.Activiti有丰富的事件类型(具体可以查看事件枚举类ActivitiEventType)供我们实现相应监听器,进行特殊业务处理。例如ENTITY_CREATED——实体创建完成(task、activity、Execution等所有实体)、TASK_CREATED——任务创建完成(针对task)、TASK_COMPLETED——任务完成等等。
2.2关于修改任务id结合上述内容我们就可以知道,只要在TASK_CREATED进行监听,直接在监听器中将id改为需要的值即可。理论上是这样,但是需要注意,Activiti6中历史任务实体创建是在TASK_CREATED之前的,如果你在TASK_CREATED中修改任务id,实际上历史任务实体创建时是获取不到的,这样就会导致历史任务的id与运行时任务id不一致。解决的办法也很简单,改为监听ENTITY_CREATED,判断是否时需要修改id的任务实体即可。
实现代码 properties配置文件# 是否更新数据库表 spring.activiti.databaseSchemaUpdate=true # 是否激活异步执行器 spring.activiti.asyncExecutorActivate=false # 流程历史记录登录 spring.activiti.historyLevel=audit # 是否检查更新流程定义 spring.activiti.checkProcessDefinitions=false # 流程定义所在前缀 spring.activiti.processDefinitionLocationPrefix=classpath*:/procDef/ # 流程定义后缀 spring.activiti.processDefinitionLocationSuffixes=**.bpmn # 部署流程定义时是否生成图片 spring.activiti.createDiagramOnDeploy=false # 字体 下面内容为转成unicode的"宋体" spring.activiti.activityFontName=u5b8bu4f53 spring.activiti.labelFontName=u5b8bu4f53解析Properties类
@ConfigurationProperties("spring.activiti") public class ActivitiProperties { private boolean checkProcessDefinitions = true; private boolean asyncExecutorActivate = true; private boolean restApiEnabled; private String deploymentName; private String mailServerHost = "localhost"; private int mailServerPort = 1025; private String mailServerUserName; private String mailServerPassword; private String mailServerDefaultFrom; private boolean mailServerUseSsl; private boolean mailServerUseTls; private String databaseSchemaUpdate = "true"; private String databaseSchema; private boolean isDbIdentityUsed = true; private boolean isDbHistoryUsed = true; private HistoryLevel historyLevel = HistoryLevel.AUDIT; private String processDefinitionLocationPrefix = "classpath:/processes/"; private Listspring boot配置类processDefinitionLocationSuffixes = Arrays.asList("**.bpmn20.xml", "**.bpmn"); private String restApiMapping = "/api/*"; private String restApiServletName = "activitiRestApi"; private boolean jpaEnabled = true; // true by default private List customMybatisMappers; private List customMybatisXMLMappers; private boolean createDiagramOnDeploy; private String activityFontName; private String labelFontName; //省略getter、setter }
@Configuration @EnableConfigurationProperties(ActivitiProperties.class) public class ActivitiConfig { private static final Logger logger = LoggerFactory.getLogger(ActivitiConfig.class); @Autowired private ActivitiProperties activitiProperties; @Autowired private DataSource dataSource; @Autowired private PlatformTransactionManager transactionManager; @Autowired private TaskCreatedListener taskCreatedListener; @Autowired private TaskCompletedListener taskCompletedListener; @Autowired private EntityCreatedListener entityCreatedListener; @Autowired private ResourcePatternResolver resourceLoader; @Bean public SpringProcessEngineConfiguration processEngineConfiguration() throws IOException { SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration(); configuration.setDataSource(dataSource); configuration.setTransactionManager(transactionManager); configuration.setDatabaseSchemaUpdate(activitiProperties.getDatabaseSchemaUpdate()); configuration.setAsyncExecutorActivate(activitiProperties.isAsyncExecutorActivate()); configuration.setHistory(activitiProperties.getHistoryLevel().getKey()); configuration.setCreateDiagramOnDeploy(activitiProperties.isCreateDiagramOnDeploy()); configuration.setActivityFontName(activitiProperties.getActivityFontName()); configuration.setLabelFontName(activitiProperties.getLabelFontName()); //todo 修改自动部署,当前自动部署直接搬自[activit-spring-boot] //如果checkProcessDefinitions为true,则发布新版流程定义,后续可能根据流程定义文件MD5等判断是否真正变化而进行发布 List实体创建完成监听器procDefResources = discoverProcessDefinitionResources(activitiProperties.getProcessDefinitionLocationPrefix(), activitiProperties.getProcessDefinitionLocationSuffixes(),this.activitiProperties.isCheckProcessDefinitions()); configuration.setDeploymentResources(procDefResources.toArray(new Resource[procDefResources.size()])); Map > typedListeners = new HashMap<>(); typedListeners.put("ENTITY_CREATED", Collections.singletonList(entityCreatedListener)); typedListeners.put("TASK_CREATED", Collections.singletonList(taskCreatedListener)); typedListeners.put("TASK_COMPLETED", Collections.singletonList(taskCompletedListener)); configuration.setTypedEventListeners(typedListeners); return configuration; } private List discoverProcessDefinitionResources(String prefix, List suffixes, boolean checkPDs) throws IOException { if (checkPDs) { List result = new ArrayList<>(); for (String suffix : suffixes) { String path = prefix + suffix; Resource[] resources = resourceLoader.getResources(path); if (resources != null && resources.length > 0) { CollectionUtils.mergeArrayIntoCollection(resources, result); } } if (result.isEmpty()) { logger.info("No process definitions were found for autodeployment"); } return result; } return new ArrayList<>(); } @Bean public ProcessEngineFactoryBean processEngine() throws IOException { ProcessEngineFactoryBean factoryBean = new ProcessEngineFactoryBean(); factoryBean.setProcessEngineConfiguration(processEngineConfiguration()); return factoryBean; } @Bean public RuntimeService runtimeService(ProcessEngine processEngine) { return processEngine.getRuntimeService(); } @Bean public RepositoryService repositoryService(ProcessEngine processEngine) { return processEngine.getRepositoryService(); } @Bean public TaskService taskService(ProcessEngine processEngine) { return processEngine.getTaskService(); } @Bean public HistoryService historyService(ProcessEngine processEngine) { return processEngine.getHistoryService(); } @Bean public ManagementService managementService(ProcessEngine processEngine) { return processEngine.getManagementService(); } @Bean public IdentityService identityService(ProcessEngine processEngine) { return processEngine.getIdentityService(); }
@Component public class EntityCreatedListener implements ActivitiEventListener { public void onEvent(ActivitiEvent event){ Object entity = ((ActivitiEntityEvent)event).getEntity(); if(entity instanceof TaskEntity){ TaskEntity taskEntity = (TaskEntity)entity; // 这个要改变的id值,可以在上篇文章中的SetFLowNodeAndGoCmd中设置相应流程变量即可。 String changeTaskId = (String)taskEntity.getVariable("changeTaskIdVarKey"); if(!StringUtils.isEmpty(changeTaskId)){ taskEntity.setId(changeTaskId); taskEntity.setVariable("changeTaskIdKey",""); } } } public boolean isFailOnException(){ return true; } }2.3关于如何获取当前任务的来源任务,以进行驳回
我们知道Activiti中有TASK_CREATED和TASK_COMPLETED事件,在同一个流程实例中,一个任务A如果不是最后的结束任务,那么在它完成后,必定会有一个新的任务B创建,而我们简单理解为A为B的来源任务。(假设A是申请任务,B就时审批任务,B的处理人对当前审批不同意要驳回时,流程就要回退到任务A。)
这样一来,我们可以监听TASK_COMPLETED,在此时为流程设置一个变量fromTaskId,值为任务A的id,当任务A的TASK_COMPLETED结束后,就来到的了任务B的TASK_CREATED中,我们此时从流程变量中获取fromTaskId,并将次id作为任务B的来源id持久化到一张自己创建的任务关系表中。这样后面要进行驳回时,只要通过这样关系表,马上就可以定位到要驳回到的任务id了。
// 关于监听器的注册看上面配置类中typedListeners部分已有 @Component public class TaskCompletedListener implements ActivitiEventListener { public void onEvent(ActivitiEvent event){ TaskEntity taskEntity = (TaskEntity)((ActivitiEntityEvent)event).getEntity(); taskEntity.setVariable("fromTaskIdVarKey", taskEntity.getId()); } public boolean isFailOnException(){ return true; } }任务创建完成监听器
@Component public class TaskCreatedListener implements ActivitiEventListener { public void onEvent(ActivitiEvent event){ TaskEntity taskEntity = (TaskEntity)((ActivitiEntityEvent)event).getEntity(); String fromTaskId = (String)taskEntity.getVariable(WfVarKeyConstants.fromTaskId); if(StringUtils.isEmpty(fromTaskId)) return; xxxTaskInfo info = new xxxTaskInfo(); info.setId(taskEntity.getId()); info.setFromId(fromTaskId); //此处进行任务关系持久化,自行实现 xxxTaskInfoRepository.save(info); } public boolean isFailOnException(){ return true; } }3.最后
本来打算做一个Activiti小贴士列表,不过看篇幅已经很长了,小贴士好像也凑不齐一篇文章,而且还没人看:)
那就放到下次来说
todo
1.Activiti命令执行模式
2.持久化过程与会话缓存(CRUD)
3.BPMN流程执行计划
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/68932.html
摘要:跳转方法当前任务获取流程定义获取目标节点定义删除当前运行任务流程执行到来源节点删除当前运行时任务命令,并返回当前任务的执行对象这里继承了,主要时很多跳转业务场景下,要求不能时挂起任务。 前言 工作快2年的小白,如有错误,恳请大家批评指点,这也是开始写博客的一个初衷,能够在分享互动、知识梳理中进步。之前工作的项目使用activiti5进行企业流程系统开发,现在这份工作也开始需要流程开发,...
摘要:另外很容易构建风格的,简单优雅帅气,正如它的名字。配置一些基本的信息。三写生产文档的注解通过注解表明该接口会生成文档,包括接口名请求方法参数返回信息的等等。四参考资料中使用构建强大的文档 swagger,中文拽的意思。它是一个功能强大的api框架,它的集成非常简单,不仅提供了在线文档的查阅,而且还提供了在线文档的测试。另外swagger很容易构建restful风格的api,简单优雅帅气...
摘要:优化当我们在数据库中增加字段时,需要在对应的实体类中增加字段,中也需要去增加字段,去维护,会消耗大量的时间我们可以让接口去继承,删除接口中的所有方法,因为中都已经实现了。遇到这里问题不会报错,只要注意打印出来的语句即可。 SpringBoot集成Mybatis 自动生成实体类和Mapper 1.使用IDEA创建一个空的SpringBoot项目 2.在pom.xml中引入以下配置 ...
摘要:如图流程引擎创建完成后,只会对进行操作,属性数据表存储整个流程引擎级别的数据初始化表结构时,会默认插入四条记录,流程部署测试流程部署,先把上面的流程引擎配置的注解改为。如图资源流程定义数据表这里面存放的就是我们部署的资源元数据信息。 关于activiti是什么,我这里就不多说了,我们直接上路,O(∩_∩)O哈哈~ 引擎配置 配置方式有好几种:1): /** * 获取默认的流程引擎实例...
摘要:商品类型实体恒宇少年码云商品基本信息实体恒宇少年码云接下来我们继续创建相关的。注解是用于标注接口抽象类是被自动映射的标识,只有存在该注解才会将内部的接口方法自动实现。 MapStruct是一种类型安全的bean映射类生成java注释处理器。我们要做的就是定义一个映射器接口,声明任何必需的映射方法。在编译的过程中,MapStruct会生成此接口的实现。该实现使用纯java方法调用的源和目...
阅读 1442·2021-11-22 13:54
阅读 4322·2021-09-22 15:56
阅读 1814·2021-09-03 10:30
阅读 1317·2021-09-03 10:30
阅读 2085·2019-08-30 15:55
阅读 1850·2019-08-30 14:13
阅读 2058·2019-08-29 15:19
阅读 2340·2019-08-28 18:13