/**
* 启动流程实例,动态设置assignee
*/
public class AssigneeUEL {
public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RuntimeService对象
RuntimeService runtimeService = processEngine.getRuntimeService();
//3.设置assignee的取值 用户可以在界面上设置流程的执行人
Map<String,Object> map = new HashMap<>();
map.put("assignee0","zhangsan");
map.put("assignee1","lishi");
map.put("assignee2","wangwu");
//4.启动流程实例,同时还要设置流程定义的assignee的值
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday2", map);
//5.输出
System.out.println(processEngine.getName());
}
}
我们也可以通过监听来分配assign:
开发中,一般通过上面的UEL的方式分配assign
Create:任务创建后触发
Assignment:任务分配后触发
Delete:任务完成后触发
All:所有事件发生都触发
public class MyTaskListener implements TaskListener{
@Override
public void notify(DelegateTask delegateTask) {
delegateTask.setAssignee("zhangsan");
}
}
例如,如果请假时间大于三天,需要总经理审核。小于等于三天的,部门经理审核即可。
注意:如果将 pojo 存储到流程变量中,必须实现序列化接口 serializable,为了防止由于新增字段无
法反序列化,需要生成 serialVersionUID。
流程变量的作用域默认是一个流程实例(processInstance),也可以是一个任务(task)或一个执行实例
(execution),这三个作用域流程实例的范围最大,可以 称为 global 变量,任务和执行实例仅仅是针对
一个任务和一个执行实例范围,范围没有流程实例大,为 称为 local 变量
一个变量的作用范围只在当前节点,就是local变量。
global变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前设置的变量值。
Local变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。
Local变量名也可以和 global变量名相同,没有影响。
第一步:设置流程变量
第二步:通过 UEL表达式使用流程变量
1> 可以在 assignee 处设置 UEL表达式,表达式的值为任务的负责人
比如:${assignee}
,assignee 就是一个流程变量名称
Activiti获取 UEL 表达式的值 ,即流程变量 assignee 的值 ,将 assignee 的值作为任务的负责人
进行任务分配
2> 可以在连线上设置 UEL表达式,决定流程走向
比如:${price>=10000}
和${price<10000}
: price 就是一个流程变量名称,uel 表达式结果类型为
布尔类型
如果 UEL表达式是 true,要决定 流程执行走向。
大于3天走总经理,小于等于三天部门经理审核即可。
public class Holiday implements Serializable {
private Integer id;
private String holidayName;//申请人的名字
private Date beginDate;//开始时间
private Date endDate;//结束日期
private Float num;//请假天数
private String reason;//事由
private String type;//请假类型
}
流程部署:
//新的请假流程定义的部署
public static void main1(String[] args) {
//1.得到ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RepositoryService对象
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.部署
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("diagram/holiday4.bpmn")
.addClasspathResource("diagram/holiday4.png")
.name("请假流程-流程变量")
.deploy();
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
//启动流程实例,同时还要设置流程变量的值
// act_ge_bytearray
// act_ru_variable
public static void main(String[] args) {
//1.得到ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RuntimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
//3.流程定义的key问题 myProcess_1
String key = "myProcess_1";
Map<String ,Object> map = new HashMap<>();
Holiday holiday = new Holiday();
holiday.setNum(5F);
map.put("holiday",holiday);
//4.启动流程实例,并且设置流程变量的值
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key, map);
//5.输出实例信息
System.out.println(processInstance.getName());
System.out.println(processInstance.getProcessDefinitionId());
}
执行任务:
//完成任务 zhangsan -----lishi----判断流程变量的请假天数,1天----分支:人事经理存档(zhaoliu)
public static void main3(String[] args) {
//1.得到ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到TaskService
TaskService taskService = processEngine.getTaskService();
//3.查询当前用户是否有任务
String key = "myProcess_1";
Task task = taskService.createTaskQuery().processDefinitionKey(key)
.taskAssignee("zhaoliu").singleResult();
//4.判断task!=null,说明当前用户有任务
if(task!=null){
taskService.complete(task.getId());
System.out.println("任务执行完毕");
}
}
注意,在启动流程实例的时候就别设置流程变量了哦。
//完成任务 zhangsan -----lishi----判断流程变量的请假天数,1天----分支:人事经理存档(zhaoliu)
public static void main(String[] args) {
//1.得到ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到TaskService
TaskService taskService = processEngine.getTaskService();
//3.查询当前用户是否有任务
String key = "myProcess_1";
Task task = taskService.createTaskQuery().processDefinitionKey(key)
.taskAssignee("zhangsan").singleResult();
//初始化一些参数
Map<String ,Object> map = new HashMap<>();
Holiday holiday = new Holiday();
holiday.setNum(5F);
map.put("holiday",holiday);
//4.判断task!=null,说明当前用户有任务
if(task!=null){
taskService.complete(task.getId(),map);//完成任务时,设置流程变量的值
System.out.println("任务执行完毕");
}
}
所以先需要启动流程实例,因为这样才有流程实例id
//新加入的:通过流程实例id,来测试流程变量
public static void main(String[] args) {
//1.得到ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RuntimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
//3.流程定义的key问题 myProcess_1
//Map map = new HashMap<>();
Holiday holiday = new Holiday();
holiday.setNum(5F);
// map.put("holiday",holiday);
//4.通过实例id,来设置流程变量
//第一个参数:流程实例的id
//第二个参数:流程变量名
//第三个变量:流程变量名,所对应的值
runtimeService.setVariable("2501","holiday",holiday);
}
设置流程变量会在当前执行流程变量表插入记录,同时也会在历史流程变量表也插入记录。
SELECT * FROM act_ru_variable #当前流程变量表
记录当前运行流程实例可使用的流程变量,包括 global和 local变量
Id_:主键
Type_:变量类型
Name_:变量名称
Execution_id_:所属流程实例执行 id,global和 local变量都存储
Proc_inst_id_:所属流程实例 id,global和 local变量都存储
Task_id_:所属任务 id,local变量存储
Bytearray_:serializable 类型变量存储对应act_ge_bytearray 表的 id
Double_:double 类型变量值
Long_:long类型变量值
Text_:text 类型变量值
SELECT * FROM act_hi_varinst #历史流程变量表
记录所有已创建的流程变量,包括 global和 local变量
字段意义参考当前流程变量表。
public void setLocalVariableByTaskId(){
//当前待办任务id
String taskId="1404";
TaskService taskService = processEngine.getTaskService();
Holiday holiday = new Holiday ();
holiday.setNum(3);
//通过任务设置流程变量
taskService.setVariableLocal(taskId, "holiday", holiday);
//一次设置多个值
//taskService.setVariablesLocal(taskId, variables)
}
第一步:查询组任务
指定候选人,查询该候选人当前的待办任务。
候选人不能办理任务。
第二步:拾取(claim)任务
该组任务的所有候选人都能拾取。
将候选人的组任务,变成个人任务。原来候选人就变成了该任务的负责人。
***如果拾取后不想办理该任务?
需要将已经拾取的个人任务归还到组里边,将个人任务变成了组任务。
第三步:查询个人任务
查询方式同个人任务部分,根据 assignee 查询用户负责的个人任务。
第四步:办理个人任务
/**
* 组任务的测试
*/
public class GroupTest {
//8.任务交接,前提要保证当前用户是这个任务的负责人,这时候他才可以有权限去将任务交接给其他候选人
/*public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到TaskService对象
TaskService taskService = processEngine.getTaskService();
//3.设置一些参数,流程定义的key,用户
String key = "myProcess_1";
String assignee="zhangsan";
//4.执行查询
Task task = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskAssignee(assignee) //设置任务的负责人
.singleResult();
//5.判断是否有这个任务
if(task!=null){
taskService.setAssignee(task.getId(),"lisi");//交接任务为lisi ,交接任务就是一个候选人拾取用户的过程
System.out.println("交接任务完成~!");
}
}*/
//7.当前用户完成自己的任务
public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到TaskService对象
TaskService taskService = processEngine.getTaskService();
//3.设置一些参数,流程定义的key,用户
String key = "myProcess_1";
String assignee="lisi";
//4.执行查询
Task task = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskAssignee(assignee) //设置任务的负责人
.singleResult();
//5.执行当前的任务
if(task!=null){
taskService.complete(task.getId());
System.out.println("任务执行完毕!");
}
}
//6.当前的用户查询自己的任务
/*public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到TaskService对象
TaskService taskService = processEngine.getTaskService();
//3.设置一些参数,流程定义的key,用户
String key = "myProcess_1";
String assignee="zhangsan";
//4.执行查询
List list = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskAssignee(assignee) //设置任务的负责人
.list();
//5.输出
for(Task task :list){
System.out.println(task.getProcessInstanceId());
System.out.println(task.getId());
System.out.println(task.getName());
System.out.println(task.getAssignee());//任务的执行人
}
}*/
//5.测试zhangsan用户,来拾取组任务
//抽取任务的过程就是将候选用户转化为真正任务的负责人(让任务的assignee有值)
/* public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到TaskService对象
TaskService taskService = processEngine.getTaskService();
//3.设置一些参数,流程定义的key,候选用户
String key = "myProcess_1";
String candidate_users="zhangsan";
//4.执行查询
Task task = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskCandidateUser(candidate_users)//设置候选用户
.singleResult();
if(task!=null){
taskService.claim(task.getId(),candidate_users);//第一个参数任务ID,第二个参数为具体的候选用户名
System.out.println("任务拾取完毕!");
}
}
*/
//4.查询候选用户的组任务
/*public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到TaskService对象
TaskService taskService = processEngine.getTaskService();
//3.设置一些参数,流程定义的key,候选用户
String key = "myProcess_1";
String candidate_users="zhangsan";
//4.执行查询
List list = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskCandidateUser(candidate_users)//设置候选用户
.list();
//5.输出
for(Task task :list){
System.out.println(task.getProcessInstanceId());
System.out.println(task.getId());
System.out.println(task.getName());
System.out.println(task.getAssignee());//为null,说明当前的zhangsan只是一个候选人,并不是任务的执行人
}
}*/
//3.填写请假单的任务要执行完成
/*public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到TaskService对象
TaskService taskService = processEngine.getTaskService();
//3.查询当前用户的任务
Task task = taskService.createTaskQuery()
.processDefinitionKey("myProcess_1")
.taskAssignee("xiaozhang")
.singleResult();
//4.处理任务,结合当前用户任务列表的查询操作的话,任务ID:task.getId()
if(task!=null){
taskService.complete(task.getId());
System.out.println("用户任务执行完毕...");
}
//5.输出任务的id
System.out.println(task.getId());
}*/
//2.启动流程实例
/* public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RunService对象
RuntimeService runtimeService = processEngine.getRuntimeService();
//3.创建流程实例 流程定义的key需要知道 holiday
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_1");
//4.输出实例的相关信息
System.out.println("流程定义ID"+processInstance.getProcessDefinitionId());//holiday:1:4
System.out.println("流程实例ID"+processInstance.getId());//2501
}*/
//1.部署流程定义
/*public static void main(String[] args) {
//1.创建ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RepositoryService实例
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.进行部署
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("diagram/holiday5.bpmn") //添加bpmn资源
// .addClasspathResource("diagram/holiday5.png")
.name("请假申请单流程")
.deploy();
//4.输出部署的一些信息
System.out.println(deployment.getName());
System.out.println(deployment.getId());
}*/
}
任务交接:部门经理有事,将这个交给别人来处理,而这个人就变成了候选人
数据库影响:
加了排他网关之后,默认走id值小的分支。
如果没有加排他网关,两个分支都会走。
设置分支条件时,如果所有分支条件都不是 true,报错:
org.activiti.engine.ActivitiException: No outgoing sequence flow of the exclusive gateway
‘exclusivegateway1’ could be selected for continuing the process
at
org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior.leave(ExclusiveGatewayActivit
yBehavior.java:85)
并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进
入和外出顺序流的:
数据库影响:
当执行到并行网关数据库跟踪如下:
当前任务表:SELECT * FROM act_ru_task #当前任务表
上图中:有两个(多个)任务当前执行。
通过流程实例执行表:SELECT * FROM act_ru_execution #流程实例的执行表
上图中,说明当前流程实例有多个分支(两个)在运行。
对并行任务的执行:
并行任务执行不分前后,由任务的负责人去执行即可。
当完成并任务中一个任务后:
已完成的任务在当前任务表 act_ru_task_已被删除。
在流程实例执行表:SELECT * FROM act_ru_execution 有中多个分支存在且有并行网关的汇聚结点。
有并行网关的汇聚结点:说明有一个分支已经到汇聚,等待其它的分支到达。
当所有分支任务都完成,都到达汇聚结点后:
流程实例执行表:SELECT * FROM act_ru_execution,执行流程实例不存在,说明流程执行结束。
总结:所有分支到达汇聚结点,并行网关执行完成。
/**
* 测试包含网关
* 特点:具有排他网关和并行网并的一些共同点
* 可以设置流程变量,当流程变量取值都成立时,此时若干个分支都可以执行
*
*/
public class InclusiveGateWayTest {
//3.任务执行完成
public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到TaskService对象
TaskService taskService = processEngine.getTaskService();
//3.查询当前用户的任务
Task task = taskService.createTaskQuery()
.processDefinitionKey("examine")
.taskAssignee("xiaowang")
.singleResult();
//4.处理任务,结合当前用户任务列表的查询操作的话,任务ID:task.getId()
if(task!=null){
taskService.complete(task.getId());
System.out.println("用户任务执行完毕...");
}
//5.输出任务的id
System.out.println(task.getId());
}
//2.启动流程实例
/* public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RunService对象
RuntimeService runtimeService = processEngine.getRuntimeService();
Integer userType = 2;//代表管理者
Map map = new HashMap<>();
map.put("userType",userType);//流程变量赋值
//3.创建流程实例 流程定义的key需要知道 holiday
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("examine",map);
//4.输出实例的相关信息
System.out.println("流程定义ID"+processInstance.getProcessDefinitionId());//holiday:1:4
System.out.println("流程实例ID"+processInstance.getId());//2501
}*/
//1.部署流程定义 带排他网关,同时还带并行网关
/* public static void main(String[] args) {
//1.创建ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RepositoryService实例
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.进行部署
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("diagram/day05/examine.bpmn") //添加bpmn资源
//.addClasspathResource("diagram/day05/examine.png")
.name("体检流程")
.deploy();
//4.输出部署的一些信息
System.out.println(deployment.getName());
System.out.println(deployment.getId());
}*/
}
如果包含网关设置的条件中,流程变量不存在,报错;
org.activiti.engine.ActivitiException: Unknown property used in expression: ${userType==‘1’ ||
userType==‘2’}
需要在流程启动时设置流程变量 userType
当执行到包含网关:
流程实例执行表:SELECT * FROM act_ru_execution
<properties>
<slf4j.version>1.6.6slf4j.version>
<log4j.version>1.2.12log4j.version>
properties>
<dependencies>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-engineartifactId>
<version>7.0.0.Beta1version>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-springartifactId>
<version>7.0.0.Beta1version>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-bpmn-modelartifactId>
<version>7.0.0.Beta1version>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-bpmn-converterartifactId>
<version>7.0.0.Beta1version>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-json-converterartifactId>
<version>7.0.0.Beta1version>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-bpmn-layoutartifactId>
<version>7.0.0.Beta1version>
dependency>
<dependency>
<groupId>org.activiti.cloudgroupId>
<artifactId>activiti-cloud-services-apiartifactId>
<version>7.0.0.Beta1version>
dependency>
<dependency>
<groupId>aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.5.4version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.40version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.0.7.RELEASEversion>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j.version}version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>${slf4j.version}version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>${slf4j.version}version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-nopartifactId>
<version>${slf4j.version}version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.5version>
dependency>
<dependency>
<groupId>commons-dbcpgroupId>
<artifactId>commons-dbcpartifactId>
<version>1.4version>
dependency>
dependencies>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/activiti"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="maxActive" value="3"/>
<property name="maxIdle" value="1"/>
bean>
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource"/>
<property name="transactionManager" ref="transactionManager"/>
<property name="databaseSchemaUpdate" value="drop-create"/>
bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration"/>
bean>
<bean id="repositoryService" factory-bean="processEngine"
factory-method="getRepositoryService"/>
<bean id="runtimeService" factory-bean="processEngine"
factory-method="getRuntimeService"/>
<bean id="taskService" factory-bean="processEngine"
factory-method="getTaskService"/>
<bean id="historyService" factory-bean="processEngine"
factory-method="getHistoryService"/>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
tx:attributes>
tx:advice>
<aop:config proxy-target-class="true">
<aop:advisor advice-ref="txAdvice"
pointcut="execution(* com.itheima.service.impl.*.*(..))"/>
aop:config>
beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:activiti-spring.xml")
public class ActivitiSpringIntegrationTest {
@Autowired
private RepositoryService repositoryService;
@Test
public void testDeploymentObj(){
System.out.println("部署对象:"+repositoryService);
}
}
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.0.RELEASEversion>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-spring-boot-starterartifactId>
<version>7.0.0.Beta2version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.5version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.27version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
如果没有history相关的表,参考:https://blog.csdn.net/m0_38052384/article/details/104612908
spring:
datasource:
url: jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT
username : root
password : root
driver-class-name: com.mysql.jdbc.Driver
/*
* Copyright 2018 Alfresco, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.itheima.activiti;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Configuration
@EnableWebSecurity
public class DemoApplicationConfiguration extends WebSecurityConfigurerAdapter {
private Logger logger = LoggerFactory.getLogger(DemoApplicationConfiguration.class);
@Override
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService());
}
@Bean
public UserDetailsService myUserDetailsService() {
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
String[][] usersGroupsAndRoles = {
{"salaboy", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"ryandawsonuk", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"erdemedeiros", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"},
{"admin", "password", "ROLE_ACTIVITI_ADMIN"},
};
for (String[] user : usersGroupsAndRoles) {
List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));
logger.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]");
inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]),
authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList())));
}
return inMemoryUserDetailsManager;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
package com.itheima.activiti;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import java.util.Collection;
@Component
public class SecurityUtil {
@Autowired
private UserDetailsService userDetailsService;
public void logInAs(String username) {
UserDetails user = userDetailsService.loadUserByUsername(username);
if (user == null) {
throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user");
}
SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
}
@Override
public Object getCredentials() {
return user.getPassword();
}
@Override
public Object getDetails() {
return user;
}
@Override
public Object getPrincipal() {
return user;
}
@Override
public boolean isAuthenticated() {
return true;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
@Override
public String getName() {
return user.getUsername();
}
}));
org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
}
}
在新特性中,流程部署会自动完成部署,只要bpmn文件放置在src\main\resources\processes下。
使用用户组,注意,这个用户组是springsecurity中定义过:GROUP_activitiTeam
要求以GROUP_开头,真正的组名是activitiTeam。
import org.activiti.api.process.model.ProcessDefinition;
import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.builders.TaskPayloadBuilder;
import org.activiti.api.task.runtime.TaskRuntime;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class Actviti7DemoApplicationTests {
@Autowired
private ProcessRuntime processRuntime;
@Autowired
private TaskRuntime taskRuntime;
@Autowired
private SecurityUtil securityUtil;
//
// /**
// * 查看流程定义
// */
@Test
public void contextLoads() {
securityUtil.logInAs("salaboy");
Page<ProcessDefinition> processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 10));
System.out.println("可用的流程定义数量:" + processDefinitionPage.getTotalItems());
for (ProcessDefinition pd : processDefinitionPage.getContent()) {
System.out.println("流程定义:" + pd);
}
}
//
// /**
// * 启动流程实例
// */
@Test
public void testStartProcess() {
securityUtil.logInAs("system");
ProcessInstance pi = processRuntime.start(ProcessPayloadBuilder.start().withProcessDefinitionKey("myProcess")
.build());
System.out.println("流程实例ID:" + pi.getId());
}
//
/**
* 查询任务,并完成自己的任务
*/
@Test
public void testTask() {
securityUtil.logInAs("ryandawsonuk");
Page<Task> taskPage=taskRuntime.tasks(Pageable.of(0,10));
if (taskPage.getTotalItems()>0){
for (Task task:taskPage.getContent()){
taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
System.out.println("任务:"+task);
taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
}
}
Page<Task> taskPage2=taskRuntime.tasks(Pageable.of(0,10));
if (taskPage2.getTotalItems()>0){
System.out.println("任务:"+taskPage2.getContent());
}
}
}
@RestController
public class TestController {
private Logger logger = LoggerFactory.getLogger(TestController.class);
@Autowired
private ProcessRuntime processRuntime;
@Autowired
private TaskRuntime taskRuntime;
@Autowired
private SecurityUtil securityUtil;
@RequestMapping(value = "/hello")
public void hello() {
// 首先,取出项目中的最多 10 个流程定义
Page<ProcessDefinition> processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 10));
if (processDefinitionPage.getTotalItems() > 0) {
// 然后,对取出的流程进行启动
for (ProcessDefinition definition : processDefinitionPage.getContent()) {
logger.info(" 流程定义信息:" + definition);
processRuntime.start(ProcessPayloadBuilder.start().withProcessDefinitionId(definition.getId()).build());
}
}
// 完成流程启动后,由于当前项目中只有 other.bpmn 一个流程,且该流程在设计时,已分配给activitiTeam 组
// 因此我们登录一个 activitiTeam 组成员 , 该账号信息会被设置到 security 上下文中, activiti 会对其信息进行读取
// 获取当前用户任务,最多 10 个
Page<Task> taskPage = taskRuntime.tasks(Pageable.of(0, 10));
// 由于目前只有一个流程,两个任务,我们尝试一下完成一个,看看会发生什么变化
if (taskPage.getTotalItems() > 0) {
for (Task task : taskPage.getContent()) {
logger.info(" 任务信息:" + task);
// 注意,完成任务前必须先声明
taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
// 完成任务
taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build()
);
}
}
// 上一轮任务完成,再看一下,现在流程是否走到了 second ?
Page<Task> taskPage2 = taskRuntime.tasks(Pageable.of(0, 10));
if (taskPage2.getTotalItems() > 0) {
logger.info(" 任务信息:" + taskPage2.getContent());
}
}
}
启动类:
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class Actviti7DemoApplication {
private Logger logger = LoggerFactory.getLogger(Actviti7DemoApplication.class);
public static void main(String[] args) {
SpringApplication.run(Actviti7DemoApplication.class, args);
}
@Bean
public Connector testConnector() {
return integrationContext -> {
logger.info("以前叫代理,现在叫连接器被调用啦~~");
return integrationContext;
};
}
}
activiti7新特性文档:https://github.com/Activiti/activiti-7-developers-guide/blob/51a1681c0e4bb5e2f96a6dea73516c9fd53d8521/getting-started/getting-started-activiti-core.md