本文內容主要爲如下兩點,由於內容有交叉,因此會放在一塊兒介紹。java
關於自由跳轉的內容我就再也不多說,主要介紹如何修改Activiti生成的實體的id,以達到駁回時從新生成的任務id與原先的任務id一致。(某些業務場景下可能會用到,例如某流程中A環節提交的表單與task id綁定,當環節提交又被駁回時,爲保證表單內容與任務關係不變,駁回後的任務id與原先任務id要一致)spring
結合上述內容咱們就能夠知道,只要在TASK_CREATED進行監聽,直接在監聽器中將id改成須要的值便可。理論上是這樣,可是須要注意,Activiti6中歷史任務實體建立是在TASK_CREATED以前的,若是你在TASK_CREATED中修改任務id,實際上歷史任務實體建立時是獲取不到的,這樣就會致使歷史任務的id與運行時任務id不一致。解決的辦法也很簡單,改成監聽ENTITY_CREATED,判斷是否時須要修改id的任務實體便可。數據庫
# 是否更新數據庫表 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=\u5b8b\u4f53 spring.activiti.labelFontName=\u5b8b\u4f53
@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 List<String> processDefinitionLocationSuffixes = Arrays.asList("**.bpmn20.xml", "**.bpmn"); private String restApiMapping = "/api/*"; private String restApiServletName = "activitiRestApi"; private boolean jpaEnabled = true; // true by default private List<String> customMybatisMappers; private List<String> 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<Resource> procDefResources = discoverProcessDefinitionResources(activitiProperties.getProcessDefinitionLocationPrefix(), activitiProperties.getProcessDefinitionLocationSuffixes(),this.activitiProperties.isCheckProcessDefinitions()); configuration.setDeploymentResources(procDefResources.toArray(new Resource[procDefResources.size()])); Map<String, List<ActivitiEventListener>> 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<Resource> discoverProcessDefinitionResources(String prefix, List<String> suffixes, boolean checkPDs) throws IOException { if (checkPDs) { List<Resource> 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; } }
咱們知道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了。segmentfault
// 關於監聽器的註冊看上面配置類中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; } }
原本打算作一個Activiti小貼士列表,不過看篇幅已經很長了,小貼士好像也湊不齊一篇文章,並且還沒人看:)
那就放到下次來講
todo
1.Activiti命令執行模式
2.持久化過程與會話緩存(CRUD)
3.BPMN流程執行計劃api