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