文章目录
版本依赖
- 开发工具 IDEA
- SpringBoot 2.4.5(这里我试过SpringBoot 3.1.1版本,Activiti没有启动,应该是依赖冲突了,后改成了2.4.5版本)
- Activiti 7.1.0.M6
父项目pom.xml
<dependencyManagement><dependencies><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.5</version><type>pom</type><scope>import</scope></dependency><!-- https://mvnrepository.com/artifact/org.activiti.dependencies/activiti-dependencies --><dependency><groupId>org.activiti.dependencies</groupId><artifactId>activiti-dependencies</artifactId><version>7.1.0.M6</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>
子项目pom.xml
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency></dependencies>
配置文件
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
username: root
password:111111url: jdbc:mysql://127.0.0.1:3306/activiti?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&useInformationSchema=truehikari:#连接池做大连接数maximum-pool-size:30#连接池空闲连接最小数量#minimum-idle: 10#允许连接在连接池中闲置最长时间#idle-timeout: 30000#池中连接最长生命周期max-lifetime:120000#等待来自池的连接的最大毫秒数connection-timeout:30000activiti:#自动更新数据库结构#1.flase:默认值。activiti在启动时,对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常#2.true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建#3.create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)#4.drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)database-schema-update:true#activiti7默认不生成历史信息表,开启历史表db-history-used:true#记录历史等级 可配置的历史级别有none, activity, audit, full#none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。#activity:级别高于none,保存流程实例与流程行为,其他数据不保存。#audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性。audit为history的默认值。#full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等。history-level: full
#自动检查、部署流程定义文件check-process-definitions:false# asyncExecutorActivate是指activiti在流程引擎启动就激活AsyncExecutor,异步:true-开启(默认)、false-关闭async-executor-activate:true
配置文件这里注意要设置
database-sechema-update
的属性为
true
,才会自动创建表。
需要注意的问题
在初次启动时可能会报错,报错如下:
Caused by:org.springframework.beans.factory.NoSuchBeanDefinitionException:No qualifying bean of type 'org.springframework.security.core.userdetails.UserDetailsService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:{}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1790)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1346)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)...142 more
这里是需要一个
UserDetailsService
的Bean,需要创建一个类去实现
UserDetailsService
接口
UserDetailsServiceImpl.class
packageorg.example.config;importorg.example.service.UserService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.core.userdetails.UsernameNotFoundException;importorg.springframework.stereotype.Component;@ComponentpublicclassUserDetailsServiceImplimplementsUserDetailsService{@AutowiredUserService userService;@OverridepublicUserDetailsloadUserByUsername(String username)throwsUsernameNotFoundException{return userService.findOneUserByName(username);}}
UserService.class
packageorg.example.service;importorg.springframework.security.core.GrantedAuthority;importorg.springframework.security.core.authority.AuthorityUtils;importorg.springframework.security.core.userdetails.User;importorg.springframework.stereotype.Service;importjava.util.List;@ServicepublicclassUserService{publicUserfindOneUserByName(String username){List<GrantedAuthority> authorities =AuthorityUtils.commaSeparatedStringToAuthorityList("admin");returnnewUser(username,"",authorities);}}
启动成功后,数据库会生成25张表
画流程图
在使用IDEA时,需要下载一个插件
当这个插件安装好以后,新建文件会多一个选项
新建的xml文件可以通过view BPMN Diagram,转化为图表操作
如何通过图表操作画流程这里就不说了,可以自己摸索看看。
activiti服务类进行编写
packageorg.example.service;importorg.activiti.engine.*;importorg.activiti.engine.history.HistoricProcessInstance;importorg.activiti.engine.repository.Deployment;importorg.activiti.engine.repository.DeploymentQuery;importorg.activiti.engine.repository.ProcessDefinition;importorg.activiti.engine.repository.ProcessDefinitionQuery;importorg.activiti.engine.runtime.ProcessInstance;importorg.activiti.engine.task.Task;importorg.apache.commons.io.IOUtils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importjava.io.IOException;importjava.io.InputStream;importjava.nio.charset.StandardCharsets;importjava.util.List;importjava.util.Map;@ServicepublicclassActivitiService{@AutowiredRepositoryService repositoryService;@AutowiredRuntimeService runtimeService;@AutowiredTaskService taskService;@AutowiredHistoryService historyService;/**
* 部署流程服务
*/publicvoiddeployProcess(){String file ="bpmn/test.bpmn20.xml";Deployment deployment = repositoryService.createDeployment().addClasspathResource(file).name("流程部署测试").deploy();System.out.println("流程部署名称:"+deployment.getName());}/**
* 查询所有部署的流程定义
* @return
*/publicList<ProcessDefinition>getAllProcessDefinition(){ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();return query.orderByProcessDefinitionVersion().desc().list();}/**
* 查询所有的部署
* @return
*/publicList<Deployment>getAllDeployment(){DeploymentQuery query = repositoryService.createDeploymentQuery();return query.list();}/**
* 查询所有流程实例
* @return
*/publicList<ProcessInstance>getAllProcessInstance(){List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().list();return list;}publicList<Task>getAllProcessTask(){List<Task> list = taskService.createTaskQuery().list();return list;}/**
* 查询用户待完成和待认领的任务
* @param username
* @return
*/publicList<Task>getAllProcessTaskByCandidateOrAssigned(String username){List<Task> list = taskService.createTaskQuery().taskCandidateOrAssigned(username).list();return list;}publicList<Task>getAllProcessTaskByCandidate(String username){List<Task> list = taskService.createTaskQuery().taskCandidateUser(username).list();return list;}/**
* 查询用户的待完成任务
* @param username
* @return
*/publicList<Task>getAllProcessTaskByAssigned(String username){List<Task> list = taskService.createTaskQuery().taskAssignee(username).list();return list;}publicvoidcomplateTask(Task task,Map<String,Object> map){
taskService.complete(task.getId(),map);}publicvoidcomplateTask(Task task){
taskService.complete(task.getId());}publicvoidclaimTask(Task task,String username){
taskService.claim(task.getId(),username);}publicProcessInstancestartProcess(String processDefinitionKey,String businessKey,Map<String,Object> map){return runtimeService.startProcessInstanceById(processDefinitionKey,businessKey,map);}publicStringgetProcessDefinitionXml(String processDefinitionId){ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);String resourceName = processDefinition.getResourceName();InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);try{returnIOUtils.toString(resourceAsStream,StandardCharsets.UTF_8);}catch(IOException e){// handle exception}returnnull;}publicList<HistoricProcessInstance>getHistoryByBusinessKey(String businessKey){List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).list();return list;}}
上图是,如果需要使用activiti中提供的服务类,直接注入即可。即springboot整合activiti启动过程中,已经在容器中自动装配了对应的服务类,需要的时候,仅仅需要取来用即可。如果启动以后报错,容器没有自动装配对应的服务类,那很有可能是依赖冲突,activiti没有正常启动。
流程部署
流程部署有多种方式,这是通过资源路径部署的,也可以通过压缩包的形式部署流程。那就需要用到
DeploymentBuilder
的
addZipInputStream
方法,这里也是用到了设计模式中的建造者模式。
流程定义
这是一个查询所有部署的流程定义的方法
当一个流程部署以后,就会生成一个流程定义,流程图的xml信息也会存入数据库。每次部署流程图都会生成一个新的流程定义。
启动流程
启动流程可以通过流程定义的id或者key,如果想通过流程定义的id启动一个流程可以使用
startProcessInstanceById
方法,如果想通过流程定义的key启动一个流程可以使用
startProcessInstanceByKey
方法。
这里要明白流程定义id和key的区别,流程定义的key是在画流程的时候可以设置的,通过设置流程的id,这个就是流程的key。
那流程的id呢,这是部署以后在数据库表的id。可以根据需要自行选择启动流程的方法。
流程实例
查询所有启动的流程实例
每次开始一个流程以后,就会生成一个流程实例。通过这个方法可以查看所有的流程实例。
测试流程
packageorg.example;importorg.activiti.engine.history.HistoricProcessInstance;importorg.activiti.engine.repository.Deployment;importorg.activiti.engine.repository.ProcessDefinition;importorg.activiti.engine.runtime.ProcessInstance;importorg.activiti.engine.task.Task;importorg.assertj.core.util.DateUtil;importorg.example.common.PrintUtil;importorg.example.entity.User;importorg.example.service.ActivitiService;importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importjavax.annotation.Resource;importjavax.sql.DataSource;importjava.sql.SQLException;importjava.util.*;@SpringBootTestpublicclassSpringBootApplicationTest{@ResourceDataSource dataSource;@AutowiredActivitiService activitiService;@Testvoidcontext()throwsSQLException{System.out.println(dataSource.getConnection());}@TestpublicvoiddeployProcess(){
activitiService.deployProcess();}@TestpublicvoidTestStartProcess(){ProcessDefinition processDefinition = activitiService.getAllProcessDefinition().get(0);String bussinessKey =this.getClass().getSimpleName();Map<String,Object> map =newHashMap<String,Object>();
map.put("creator","Echo");
activitiService.startProcess(processDefinition.getId(),bussinessKey,map);}@TestpublicvoidfindAllDeployment(){List<Deployment> list = activitiService.getAllDeployment();PrintUtil.printTable(list);}@TestpublicvoidfindAllProcessDefinition(){List<ProcessDefinition> list = activitiService.getAllProcessDefinition();PrintUtil.printTable(list);}@TestpublicvoidfindAllProcessInstance(){List<ProcessInstance> list = activitiService.getAllProcessInstance();PrintUtil.printTable(list);}@TestpublicvoidfindAllProcessTask(){List<Task> list = activitiService.getAllProcessTask();PrintUtil.printTable(list);}@TestpublicvoidfindTaskByUserId(){String userId ="keaizp";List<Task> list = activitiService.getAllProcessTaskByCandidateOrAssigned(userId);PrintUtil.printTable(list);}@TestpublicvoidclaimTask(){String userId="keaizp";List<Task> list = activitiService.getAllProcessTaskByCandidate(userId);PrintUtil.printTable(list);
activitiService.claimTask(list.get(0),userId);
list = activitiService.getAllProcessTaskByCandidate("zengpei");PrintUtil.printTable(list);}@TestpublicvoidcomplateUserTask(){String userId ="Echo";List<Task> list = activitiService.getAllProcessTaskByAssigned(userId);List<String> candidateUsers =newArrayList<String>();
candidateUsers.add("keaizp");
candidateUsers.add("zengpei");Map<String,Object> map =newHashMap<String,Object>();
map.put("approver",candidateUsers);
activitiService.complateTask(list.get(0),map);}@TestpublicvoidprocessDefinitionXml(){List<ProcessDefinition> list = activitiService.getAllProcessDefinition();String xmlStr = activitiService.getProcessDefinitionXml(list.get(0).getId());System.out.println(xmlStr);}@TestpublicvoidfindHistoryByBusinessKey(){List<HistoricProcessInstance> list = activitiService.getHistoryByBusinessKey("2023-07-02T22:45:45");PrintUtil.printTable(list);}@TestpublicvoidprintTest(){List<User> users =newArrayList<>();
users.add(newUser("keaizpeeeeeeeee",24,"male"));
users.add(newUser("Echo",24,"male"));
users.add(newUser("nick",24,"male"));
users.add(newUser("amy",24,"male"));PrintUtil.printTableFixed(users,16);}}
启动流程
这里我用的是一个格式化的日期做bussinessKey,实际业务中最好使用 流程作业类型 + 一个业务台账id,比如:请假申请要走审批流程, 离职申请也要走审批流程,如果只用id作为bussinessKey的话,bussinessKey就无法满足唯一性。所以最好 前面加上作业类型。这里的map传入的是流程图里面的参数。
传入受理人,这里就会产生一个Task,就是用户的代办任务。
完成任务
当用户登录系统的时候,应该给用户提示,用户有未完成的代办事项,然后给出用户代办事项列表,做完一切以后,就是完成用户任务,这个时候,可以传入下一流程节点参与任务的人,当然你也可以传入几个候选人,
candidate Users
就是 候选人。可以传入一个用户id的列表,然后这几个人就会收到可以受理的任务,可以选择是否接受该任务。只要接受了任务,其他候选人就无法再接受此任务,同时这个任务的
Assignee
就会设置成受理人的id
受理任务
因为用户
“keaizp”
成为了候选者,他就可以看到自己可以受理的这个任务,用
claim
方法就可以受理该任务,当受理任务完成以后,再去看看另外一名候选者的受理任务,会发现已经没有待受理的任务了。
版权归原作者 啊几 所有, 如有侵权,请联系我们删除。