摘要:當(dāng)一個根級的結(jié)束時,就會進行上述的緩存對象統(tǒng)一的持久化。解決的辦法也很簡單,改為監(jiān)聽,判斷是否時需要修改的任務(wù)實體即可。這樣后面要進行駁回時,只要通過這樣關(guān)系表,馬上就可以定位到要駁回到的任務(wù)了。
1.前言
本文內(nèi)容主要為以下兩點,因為內(nèi)容有交叉,所以會放在一起介紹。
1.以自由跳轉(zhuǎn)為基礎(chǔ)實現(xiàn)不改變原先任務(wù)id的駁回
關(guān)于Activiti6動態(tài)跳轉(zhuǎn)可以查看我的另一篇文章Activiti6實現(xiàn)自由跳轉(zhuǎn)
2.java類方式進行Activiti6配置、spring boot集成
因為有一些自定義的需求,如流程字體、自動部署、自定義監(jiān)聽器等,直接引入[activit-spring-boot]又沒有必要,所以參考activit6源碼中[activit-spring-boot]模塊的代碼完成。
關(guān)于自由跳轉(zhuǎn)的內(nèi)容我就不再多說,主要介紹如何修改Activiti生成的實體的id,以達到駁回時重新生成的任務(wù)id與原先的任務(wù)id一致。(某些業(yè)務(wù)場景下可能會用到,例如某流程中A環(huán)節(jié)提交的表單與task id綁定,當(dāng)環(huán)節(jié)提交又被駁回時,為保證表單內(nèi)容與任務(wù)關(guān)系不變,駁回后的任務(wù)id與原先任務(wù)id要一致)
2.1前提知識1.Activiti持久化實體的過程時先創(chuàng)建實體對象,記錄到緩存中,在完成執(zhí)行后統(tǒng)一進行緩存對象的持久化,并清空緩存。
2.Activiti采用命令模式執(zhí)行操作,所有操作都時一個CMD。執(zhí)行一個CMD的時候會創(chuàng)建一個上下文環(huán)境,包含待持久化的實體緩存等,如果在CMD中嵌套執(zhí)行CMD,新的CMD默認會使用上級上下文環(huán)境。當(dāng)一個根級的CMD結(jié)束時,Activiti就會進行上述的緩存對象統(tǒng)一的持久化。
3.Activiti有豐富的事件類型(具體可以查看事件枚舉類ActivitiEventType)供我們實現(xiàn)相應(yīng)監(jiān)聽器,進行特殊業(yè)務(wù)處理。例如ENTITY_CREATED——實體創(chuàng)建完成(task、activity、Execution等所有實體)、TASK_CREATED——任務(wù)創(chuàng)建完成(針對task)、TASK_COMPLETED——任務(wù)完成等等。
2.2關(guān)于修改任務(wù)id結(jié)合上述內(nèi)容我們就可以知道,只要在TASK_CREATED進行監(jiān)聽,直接在監(jiān)聽器中將id改為需要的值即可。理論上是這樣,但是需要注意,Activiti6中歷史任務(wù)實體創(chuàng)建是在TASK_CREATED之前的,如果你在TASK_CREATED中修改任務(wù)id,實際上歷史任務(wù)實體創(chuàng)建時是獲取不到的,這樣就會導(dǎo)致歷史任務(wù)的id與運行時任務(wù)id不一致。解決的辦法也很簡單,改為監(jiān)聽ENTITY_CREATED,判斷是否時需要修改id的任務(wù)實體即可。
實現(xiàn)代碼 properties配置文件# 是否更新數(shù)據(jù)庫表 spring.activiti.databaseSchemaUpdate=true # 是否激活異步執(zhí)行器 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 # 字體 下面內(nèi)容為轉(zhuǎn)成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 修改自動部署,當(dāng)前自動部署直接搬自[activit-spring-boot] //如果checkProcessDefinitions為true,則發(fā)布新版流程定義,后續(xù)可能根據(jù)流程定義文件MD5等判斷是否真正變化而進行發(fā)布 List實體創(chuàng)建完成監(jiān)聽器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中設(shè)置相應(yīng)流程變量即可。 String changeTaskId = (String)taskEntity.getVariable("changeTaskIdVarKey"); if(!StringUtils.isEmpty(changeTaskId)){ taskEntity.setId(changeTaskId); taskEntity.setVariable("changeTaskIdKey",""); } } } public boolean isFailOnException(){ return true; } }2.3關(guān)于如何獲取當(dāng)前任務(wù)的來源任務(wù),以進行駁回
我們知道Activiti中有TASK_CREATED和TASK_COMPLETED事件,在同一個流程實例中,一個任務(wù)A如果不是最后的結(jié)束任務(wù),那么在它完成后,必定會有一個新的任務(wù)B創(chuàng)建,而我們簡單理解為A為B的來源任務(wù)。(假設(shè)A是申請任務(wù),B就時審批任務(wù),B的處理人對當(dāng)前審批不同意要駁回時,流程就要回退到任務(wù)A。)
這樣一來,我們可以監(jiān)聽TASK_COMPLETED,在此時為流程設(shè)置一個變量fromTaskId,值為任務(wù)A的id,當(dāng)任務(wù)A的TASK_COMPLETED結(jié)束后,就來到的了任務(wù)B的TASK_CREATED中,我們此時從流程變量中獲取fromTaskId,并將次id作為任務(wù)B的來源id持久化到一張自己創(chuàng)建的任務(wù)關(guān)系表中。這樣后面要進行駁回時,只要通過這樣關(guān)系表,馬上就可以定位到要駁回到的任務(wù)id了。
// 關(guān)于監(jiān)聽器的注冊看上面配置類中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; } }任務(wù)創(chuàng)建完成監(jiān)聽器
@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); //此處進行任務(wù)關(guān)系持久化,自行實現(xiàn) xxxTaskInfoRepository.save(info); } public boolean isFailOnException(){ return true; } }3.最后
本來打算做一個Activiti小貼士列表,不過看篇幅已經(jīng)很長了,小貼士好像也湊不齊一篇文章,而且還沒人看:)
那就放到下次來說
todo
1.Activiti命令執(zhí)行模式
2.持久化過程與會話緩存(CRUD)
3.BPMN流程執(zhí)行計劃
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/68932.html
摘要:跳轉(zhuǎn)方法當(dāng)前任務(wù)獲取流程定義獲取目標節(jié)點定義刪除當(dāng)前運行任務(wù)流程執(zhí)行到來源節(jié)點刪除當(dāng)前運行時任務(wù)命令,并返回當(dāng)前任務(wù)的執(zhí)行對象這里繼承了,主要時很多跳轉(zhuǎn)業(yè)務(wù)場景下,要求不能時掛起任務(wù)。 前言 工作快2年的小白,如有錯誤,懇請大家批評指點,這也是開始寫博客的一個初衷,能夠在分享互動、知識梳理中進步。之前工作的項目使用activiti5進行企業(yè)流程系統(tǒng)開發(fā),現(xiàn)在這份工作也開始需要流程開發(fā),...
摘要:另外很容易構(gòu)建風(fēng)格的,簡單優(yōu)雅帥氣,正如它的名字。配置一些基本的信息。三寫生產(chǎn)文檔的注解通過注解表明該接口會生成文檔,包括接口名請求方法參數(shù)返回信息的等等。四參考資料中使用構(gòu)建強大的文檔 swagger,中文拽的意思。它是一個功能強大的api框架,它的集成非常簡單,不僅提供了在線文檔的查閱,而且還提供了在線文檔的測試。另外swagger很容易構(gòu)建restful風(fēng)格的api,簡單優(yōu)雅帥氣...
摘要:優(yōu)化當(dāng)我們在數(shù)據(jù)庫中增加字段時,需要在對應(yīng)的實體類中增加字段,中也需要去增加字段,去維護,會消耗大量的時間我們可以讓接口去繼承,刪除接口中的所有方法,因為中都已經(jīng)實現(xiàn)了。遇到這里問題不會報錯,只要注意打印出來的語句即可。 SpringBoot集成Mybatis 自動生成實體類和Mapper 1.使用IDEA創(chuàng)建一個空的SpringBoot項目 2.在pom.xml中引入以下配置 ...
摘要:如圖流程引擎創(chuàng)建完成后,只會對進行操作,屬性數(shù)據(jù)表存儲整個流程引擎級別的數(shù)據(jù)初始化表結(jié)構(gòu)時,會默認插入四條記錄,流程部署測試流程部署,先把上面的流程引擎配置的注解改為。如圖資源流程定義數(shù)據(jù)表這里面存放的就是我們部署的資源元數(shù)據(jù)信息。 關(guān)于activiti是什么,我這里就不多說了,我們直接上路,O(∩_∩)O哈哈~ 引擎配置 配置方式有好幾種:1): /** * 獲取默認的流程引擎實例...
摘要:商品類型實體恒宇少年碼云商品基本信息實體恒宇少年碼云接下來我們繼續(xù)創(chuàng)建相關(guān)的。注解是用于標注接口抽象類是被自動映射的標識,只有存在該注解才會將內(nèi)部的接口方法自動實現(xiàn)。 MapStruct是一種類型安全的bean映射類生成java注釋處理器。我們要做的就是定義一個映射器接口,聲明任何必需的映射方法。在編譯的過程中,MapStruct會生成此接口的實現(xiàn)。該實現(xiàn)使用純java方法調(diào)用的源和目...
閱讀 1531·2021-11-22 13:54
閱讀 4463·2021-09-22 15:56
閱讀 1887·2021-09-03 10:30
閱讀 1392·2021-09-03 10:30
閱讀 2135·2019-08-30 15:55
閱讀 1913·2019-08-30 14:13
閱讀 2133·2019-08-29 15:19
閱讀 2426·2019-08-28 18:13