摘要:當一個根級的結束時,就會進行上述的緩存對象統一的持久化。解決的辦法也很簡單,改為監聽,判斷是否時需要修改的任務實體即可。這樣后面要進行駁回時,只要通過這樣關系表,馬上就可以定位到要駁回到的任務了。
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流程執行計劃
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/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方法調用的源和目...
閱讀 1443·2021-11-22 13:54
閱讀 4323·2021-09-22 15:56
閱讀 1815·2021-09-03 10:30
閱讀 1318·2021-09-03 10:30
閱讀 2086·2019-08-30 15:55
閱讀 1851·2019-08-30 14:13
閱讀 2059·2019-08-29 15:19
閱讀 2341·2019-08-28 18:13