工作流概念
工作流是指业务过程的部分或整体在计算机应用环境下的自动化。是对工作流程及其各操作步骤之间业务规则的抽象、概括描述。
activiti介绍
activiti是一个工作流引擎,可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN进行定义,业务流程按照预先定义的流程进行执行。实现了系统的流程由activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作流量,从而提高系统的健壮性,同时也减少了系统开发维护成本。
BPM(Business Process Management)
业务流程管理
BPM软件
通过BPM软件可以实现对业务流程的整个生命周期进行建模、自动化、管理监控和优化。
BPMN(Business Process MOdel And Notation 业务流程模型和符号)
业务流程模型和符号,是由BPMI开发的一套标准的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。
ProcessDefinition:流程定义
ProcessInstance:流程实例
部署activiti
activiti是一个工作流程,业务系统访问activiti的接口,就可以方便操作流程间的相关数据,这样就可以把工作流环境和业务系统的环境集成在一起。
流程定义
使用activiti建模工具定义业务流程,生成.bpmn文件(通过xml定义业务流程)。
流程定义部署
使用activiti提供的api把流程定义的内容存储起来,再Activiti执行过中可以查询的内容。activiti执行把流程定内容存储在数据库中。
启动一个流程实例
流程实例也叫ProcessInstance
启动一个流程实例表示开始一次业务流程的执行
比如同一个流程,张三可以启动,李四也可以启动,但是两次流程实例是互不影响的。
用户查询待办任务
由于集成了activiti,所有的任务都可以直接通过activiti提供的api进行查询,不需要我们自己查库。
用户办理任务
流程办理等操作,也是可以直接使用activiti提供的api即可。
流程结束
当没有下一个需要办理的节点,说明任务就结束了。
idea软件安装actiBPM插件,若idea找不到插件,可去官网下载,导入即可,可参考
https://blog.csdn.net/weixin_40496191/article/details/125097860
pom配置
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-spring-boot-starterartifactId>
<version>7.1.0.M1version>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-json-converterartifactId>
<version>7.1.0.M1version>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-image-generatorartifactId>
<version>7.1.0.M1version>
dependency>
<dependency>
<groupId>org.apache.xmlgraphicsgroupId>
<artifactId>batik-allartifactId>
<version>1.10version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.2version>
dependency>
配置文件配置activiti相关参数和数据库信息
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: tiger
url: jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf-8 &allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8&nullCatalogMeansCurrent=true
type: com.alibaba.druid.pool.DruidDataSource
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
activiti:
# false:默认,数据库表不变,但是如果版本不对或者缺失表会抛出异常(生产使用)
# true:表不存在,自动创建(开发使用)
# create_drop: 启动时创建,关闭时删除表(测试使用)
# drop_create: 启动时删除表,在创建表 (不需要手动关闭引擎)
database-schema-update: true
#监测历史表是否存在,activities7默认不开启历史表
db-history-used: true
#none:不保存任何历史数据,流程中这是最高效的
#activity:只保存流程实例和流程行为
#audit:除了activity,还保存全部的流程任务以及其属性,audit为history默认值
#full:除了audit、还保存其他全部流程相关的细节数据,包括一些流程参数
history-level: full
#校验流程文件,默认校验resources下的process 文件夹的流程文件
check-process-definitions: true
启动项目,即可生成相关表

相关表关系
re表:流程定义和流程相关属性
ru表:运行时产生的数据
hi表:历史信息
ge表:通用信息

画图
创建demo.bpmn文件,简单实现流程图,id为“myLeave“,流程名称为“员工请假审批流程”,责任人从上往下依次为worker、manager、financer。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-84gAnIlx-1659512850380)(C:\Users\86188\AppData\Roaming\Typora\typora-user-images\image-20220718234740001.png)]](https://1000bd.com/contentImg/2022/08/09/082100250.png)
画完图后,可以将流程图导出来
1)复制一份新的文件,以xml结尾
2)右键显示图标

3)导出png图片

/**
* 部署流程
*/
@RequestMapping("createProcesses")
@ResponseBody
public void createProcesses() {
//使用获取RepositoryService进行部署
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("static/bpmn/demo.bpmn")//添加bpmn资源
.addClasspathResource("static/bpmn/demo.png")//添加png资源
.name("员工请假审批流程")
.deploy();//部署流程
//输出流程部署的信息
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
//结果:
//流程部署id:1fb480d0-0773-11ed-8dc3-005056c00001
//流程部署名称:员工请假审批流程
执行后可以去数据库查看相关的表数据
act_re_deployment:查看相关的流程的创建
act_re_procdef:查看流程的定义
act_ge_bytearray:流程文件存储
/**
* 查询流程的定义
*/
@RequestMapping("searchProcess")
@ResponseBody
public void searchProcess() {
String key= "myLeave";
//获取ProcessDefinitionQuery对象,用来查询操作
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
List<ProcessDefinition> list = processDefinitionQuery.processDefinitionKey(key)
.orderByProcessDefinitionVersion()//安装版本信息
.desc()//倒序
.list();
//输出流程定义的信息
for (ProcessDefinition processDefinition : list) {
System.out.println("流程定义的id:" + processDefinition.getId());
System.out.println("流程定义的name:" + processDefinition.getName());
System.out.println("流程定义的key:" + processDefinition.getKey());
System.out.println("流程定义的version:" + processDefinition.getVersion());
System.out.println("流程部署的id:" + processDefinition.getDeploymentId());
System.out.println("--------------------------------------------------");
}
}
//结果
//相同key,则自动提升版本
//流程定义的id:myLeave:2:f6844e42-0774-11ed-96b3-005056c00001
//流程定义的name:员工请假审批流程
//流程定义的key:myLeave
//流程定义的version:2
//流程部署的id:f66888df-0774-11ed-96b3-005056c00001
//--------------------------------------------------
//流程定义的id:myLeave:1:fc307169-0773-11ed-9b87-005056c00001
//流程定义的name:员工请假审批流程
//流程定义的key:myLeave
//流程定义的version:1
//流程部署的id:fc1a2a46-0773-11ed-9b87-005056c00001
//--------------------------------------------------
/**
* 删除流程
*/
@RequestMapping("deleteProcess")
@ResponseBody
public void deleteProcess() {
String id = "f66888df-0774-11ed-96b3-005056c00001";
//设置true,择优级联删除的效果。false如果已有实例,则会删除错误
repositoryService.deleteDeployment(id, true);
}
/**
* 读取数据库中的资源文件
*/
@RequestMapping("searchProcesslFile")
@ResponseBody
public void searchProcesslFile() throws IOException {
String id = "myLeave";
//查询器
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey(id)
.singleResult();
//获取流程部署id
String deploymentId = processDefinition.getDeploymentId();
//通过repositoryService对象的相关方法来获取图片信息和bpmn信息
//png图片
InputStream pngInput = repositoryService
.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());
//bpmn 文件的流
InputStream bpmnInput = repositoryService
.getResourceAsStream(deploymentId, processDefinition.getResourceName());
//文件的保存
File filePng = new File("E:/学习/activiti/evection.png");
File fileBpmn = new File("E:/学习/activiti/evection.bpmn");
OutputStream pngOut = new FileOutputStream(filePng);
OutputStream bpmnOut = new FileOutputStream(fileBpmn);
//输入流和输出流的转化
IOUtils.copy(pngInput, pngOut);
IOUtils.copy(bpmnInput, bpmnOut);
pngInput.close();
pngOut.close();
bpmnInput.close();
bpmnOut.close();
}
/**
* 创建流程实例
*/
@RequestMapping("addApplication")
@ResponseBody
public void addApplication() {
String businessKey = "myLeave";
//启动一个流程实例
//ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(businessKey);
//启动一个流程实例,设置业务id,长度最大255,通过processInstance.getBusinessKey()获取
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(businessKey,"businessKey");
System.out.println("流程定义id:" + processInstance.getProcessDefinitionId());
System.out.println("流程实例id:" + processInstance.getId());
System.out.println("当前活动id:" + processInstance.getActivityId());
}
//结果:
//流程定义id:myLeave:2:a07c05cb-076f-11ed-afa5-005056c00001
//流程实例id:f6f5bf5a-0772-11ed-8dc3-005056c00001
//当前活动id:null
/**
* 查询流程下存在哪些实例
* 可通过.processInstanceId等相关属性过滤
*/
@RequestMapping("searchProcessRunInstance")
@ResponseBody
public void searchProcessRunInstance() {
String key = "myLeave";
List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().processDefinitionKey(key).list();
//输出流程定义的信息
for (ProcessInstance processInstance : list) {
System.out.println("流程实例id:" + processInstance.getProcessInstanceId());
System.out.println("所属流程定义id:" + processInstance.getProcessDefinitionId());
System.out.println("是否完成:" + processInstance.isEnded());
System.out.println("是否暂停:" + processInstance.isSuspended());
System.out.println("当前活动标识:" + processInstance.getActivityId());
System.out.println("业务关键字:" + processInstance.getBusinessKey());
System.out.println("------------" );
}
}
//结果:
//流程实例id:906f1e33-0776-11ed-96b3-005056c00001
//所属流程定义id:myLeave:2:f6844e42-0774-11ed-96b3-005056c00001
//是否完成:false
//是否暂停:false
//当前活动标识:null
//业务关键字:null
//------------
//流程实例id:fece74da-0773-11ed-9b87-005056c00001
//所属流程定义id:myLeave:1:fc307169-0773-11ed-9b87-005056c00001
//是否完成:false
//是否暂停:false
//当前活动标识:null
//业务关键字:null
//------------
/**
* 查询任务查询
* 也可以不加过滤条件,查询出全部
*/
@RequestMapping("searchTask")
@ResponseBody
public void searchTask() {
String assignee = "worker";
String key = "myLeave";
//根据流程的key和任务负责人 查询任务
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskAssignee(assignee)
.list();
//输出当前用户具有的任务
for (Task task : list) {
System.out.println("流程实例id:" + task.getProcessDefinitionId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
}
//结果:
//流程实例id:myLeave:2:a07c05cb-076f-11ed-afa5-005056c00001
//任务id:2da138fa-0772-11ed-a901-005056c00001
//任务负责人:worker
//任务名称:创建请假流程
/**
* 根据流程的key和任务负责人 处理任务
*/
@RequestMapping("solveTask")
@ResponseBody
public void solveTask() {
String assignee = "worker";
String key = "myLeave";
//根据流程的key和任务负责人 查询任务
Task task = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskAssignee(assignee)
.singleResult();
//完成任务,跳到下一个流程
taskService.complete(task.getId());
}
act_ru_execution:任务操作
act_ru_task:目前需要处理的任务表
/**
* 流程历史信息查看
* 即时删除了流程,但是流程的历史信息还是会被保存下来,存在hi**表中
* 也可以不加过滤条件,查询出全部
*/
@RequestMapping("searchHistoricActivityInstance")
@ResponseBody
public void searchHistoricActivityInstance() {
String id = "myLeave:1:fc307169-0773-11ed-9b87-005056c00001";
//获取actinst表的查询对象
HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery();
//查询actinst表,根据流程id查询
HistoricActivityInstanceQuery historicActivityInstanceQuery1 = historicActivityInstanceQuery.processDefinitionId(id);
//查询actinst表,根据流程实例id查询
//historicActivityInstanceQuery.processInstanceId(流程实例id);
//排序
historicActivityInstanceQuery1.orderByHistoricActivityInstanceStartTime().desc();
//获取查询结果
List<HistoricActivityInstance> list = historicActivityInstanceQuery1.list();
//输出查询结果
for (HistoricActivityInstance hi : list) {
System.out.println(hi.getActivityId());
System.out.println(hi.getActivityName());
System.out.println(hi.getActivityType());
System.out.println(hi.getAssignee());
System.out.println(hi.getProcessDefinitionId());
System.out.println(hi.getProcessInstanceId());
}
}
//结果:
//_4
//部门经理审批
//userTask
//manager
//myLeave:1:fc307169-0773-11ed-9b87-005056c00001
//fece74da-0773-11ed-9b87-005056c00001
//_3
//创建请假流程
//userTask
//worker
//myLeave:1:fc307169-0773-11ed-9b87-005056c00001
//fece74da-0773-11ed-9b87-005056c00001
//_2
//StartEvent
//startEvent
//null
//myLeave:1:fc307169-0773-11ed-9b87-005056c00001
//fece74da-0773-11ed-9b87-005056c00001
/**
* 全部流程实例挂起和激活
*/
@RequestMapping("allLockOrOpenPeocess")
@ResponseBody
public void allLockOrOpenPeocess() {
String id = "myLeave";
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey(id)
.singleResult();
//获取流程的状态
boolean suspended = processDefinition.isSuspended();
String pdId = processDefinition.getId();
//如果激活就挂起,挂起就激活
if (suspended) {
//表示当前是挂起的,需要激活。
//参数:流程定义id、是否激活、激活时间
repositoryService.activateProcessDefinitionById(pdId, true, null);
System.out.println("流程定义:" + pdId + ",已激活");
} else {
//激活状态,则需要挂起
//参数:流程定义id、是否挂起、挂起时间
repositoryService.suspendProcessDefinitionById(pdId, true, null);
System.out.println("流程定义:" + pdId + ",已挂起");
}
}
/**
* 单个流程实例挂起和激活
*/
@RequestMapping("singleLockOrOpenPeocess")
@ResponseBody
public void singleLockOrOpenPeocess() {
//task表的proc_inst_id
String id = "dc609b81-082e-11ed-a65d-005056c00001";
//获取实例对象
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(id)
.singleResult();
//获取状态
boolean suspended = processInstance.isSuspended();
String pdId = processInstance.getId();
if (suspended) {
//表示当前是挂起的,需要激活
runtimeService.activateProcessInstanceById(pdId);
System.out.println("流程定义:" + pdId + ",已激活");
} else {
//激活状态,则需要挂起
runtimeService.suspendProcessInstanceById(pdId);
System.out.println("流程定义:" + pdId + ",已挂起");
}
}
流程变量
Global变量:这个是流程变量的默认作用域,表示是一个完整的流程实例。Global变量名不能重复,如果重复则会被后面的覆盖。
Local变量:Local变量只针对一个任务或者一个执行实例,变量名可相同,互不影响。
ps1:如果设置了流程变量,就必须复制,否则将会报错导致流程结束。
ps2:如果连线分支不设置条件,默认走sequenceFlow id(可在xml查看)小的那条线。
画分支图,并且设置流程审批人变量,依次设置为
a
s
s
i
g
n
e
e
0
、
{assignee0}、
assignee0、{assignee1}、
a
s
s
i
g
n
e
e
2
、
{assignee2}、
assignee2、{assignee3}

启动流程,并且创建实例(带变量)
/**
* 启动流程(创建新的申请流程实例列表)
*/
@RequestMapping("addApplication")
@ResponseBody
public void addApplication() {
Map<String,Object> variables=new HashMap<>();
Map<String ,Object> evention=new HashMap<>();
evention.put("num",2);
variables.put("evention",evention);
variables.put("assignee0","申请人");
variables.put("assignee1","部门经理");
variables.put("assignee2","总经理");
variables.put("assignee3","财务人事");
String key = "myLeave1";
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key,variables);
}
依次调用solveTask方法,可以发现最终走的分支是num小于3的分支,即不经过总经理审批。
也可以在每次执行时,重新设置本地变量
ps:这里需要留意,7.1.0.M1版本的activiti无法直接在complete时覆盖全局和本地变量,只能通过设置本地变量覆盖。
/**
* 启动流程(创建新的申请流程实例列表)
*/
@RequestMapping("addApplication")
@ResponseBody
public void addApplication() {
Map<String,Object> variables=new HashMap<>();
Map<String ,Object> evention=new HashMap<>();
evention.put("num",2);
variables.put("evention",evention);
variables.put("assignee0","申请人");
variables.put("assignee1","部门经理");
variables.put("assignee2","总经理");
variables.put("assignee3","财务人事");
String key = "myLeave1";
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key,variables);
}
/**
* 根据流程的key和任务负责人 处理任务
*/
@RequestMapping("solveTask")
@ResponseBody
public void solveTask() {
String assignee = "申请人";
String id = "myLeave1";
//根据流程的key和任务负责人 查询任务
Task task = taskService.createTaskQuery()
.processDefinitionKey(id)
.taskAssignee(assignee)
.singleResult();
//完成任务,跳到下一个流程
Map<String,Object> variables=new HashMap<>();
Map<String ,Object> evention=new HashMap<>();
evention.put("num",2);
variables.put("evention",evention);
variables.put("assignee0","申请人1");
variables.put("assignee1","部门经理1");
variables.put("assignee2","总经理1");
variables.put("assignee3","财务人事1");
taskService.setVariablesLocal(task.getId(),variables);
taskService.complete(task.getId());
}
/**
* 根据流程的key和任务负责人 处理任务
*/
@RequestMapping("solveTaskForVariables")
@ResponseBody
public void solveTaskForVariables() {
String assignee = "部门经理1";
String id = "myLeave1";
//根据流程的key和任务负责人 查询任务
Task task = taskService.createTaskQuery()
.processDefinitionKey(id)
.taskAssignee(assignee)
.singleResult();
//完成任务,跳到下一个流程
Map<String,Object> variables=new HashMap<>();
Map<String ,Object> evention=new HashMap<>();
evention.put("num",2);
variables.put("evention",evention);
variables.put("assignee0","申请人2");
variables.put("assignee1","部门经理2");
variables.put("assignee2","总经理2");
variables.put("assignee3","财务人事2");
taskService.setVariablesLocal(task.getId(),variables);
taskService.complete(task.getId());
}
然后查询当前任务
/**
* 根据流程的key查询存在哪些任务
*/
@RequestMapping("searchTaskByKey")
@ResponseBody
public void searchTaskByKey() {
String key = "myLeave1";
//根据流程的key和任务负责人 查询任务
List<Task> tasks = taskService.createTaskQuery()
.processDefinitionKey(key)
.list();
for (Task task : tasks) {
System.out.println(task.getId());
System.out.println(task.getName());
System.out.println(task.getAssignee());
System.out.println("--------------");
}
}
//结果
//3c18d52e-0841-11ed-b1d5-005056c00001
//财务部审批
//财务人事2
//--------------
概念:用来在流程中实现决策。 当流程执行到这个网关,所有分支都会判断是否为true,如果为ture则执行。如果多个为true,则执行id值小的。如果没 有位true的,则抛出异常
流程图

概念:并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起。并行网关的线上面,写上条件也没有用,在并行网关的后面分支都要执行完成之后,才会走下一个任务,不然,只要有一个分支没有完成,后面的任务就不会走
流程图

概念:包含网关可以看做是排他网关和并行网关的结合体。和排他网关一样,你可以在外出顺序流上定义条件,包含网关会解析它们,不定义则默认放行。 但是主要的区别是包含网关可以选择多于一条顺序流,这和并行网关一样。这也是用的最多的网关。
流程图 
概念
即任务的下一个处理人不再是一个具体的人员,而是一组候选人。候选人可以主动拾取任务,然后办理。
流程图,即再流程的第二步,经理或总经理需要拾取任务才能进行处理

代码实现
由于activiti需要整合Security框架,所以需要做好权限配置,这里是简单做了个lisi用户的配置
package activiti.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
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 {
private Logger logger = LoggerFactory.getLogger(SecurityUtil.class);
@Autowired
@Qualifier("myUserDetailsService")
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");
}
logger.info("> Logged in as: " + username);
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);
}
}
package activiti.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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
public class SpringSecurityConfiguration {
private Logger logger = LoggerFactory.getLogger(SpringSecurityConfiguration.class);
@Bean
public UserDetailsService myUserDetailsService() {
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
//这里添加用户,后面处理流程时用到的任务负责人,需要添加在这里
String[][] usersGroupsAndRoles = {
{"lisi", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"system", "password", "ROLE_ACTIVITI_USER"},
{"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;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
查找并且拾取任务
/**
* 根据流程的key和候选人拾取任务
* 由于是测试,所以返回单个任务。正常应该是列表展示所有候选任务,然后选择拾取。
*/
@RequestMapping("claimTask")
@ResponseBody
public void claimTask() {
String taskCandidateUser = "lisi";
String id = "myLeave5";
//根据流程的key和任务负责人 查询任务
Task task = taskService.createTaskQuery()
.processDefinitionKey(id)
.taskCandidateUser(taskCandidateUser)
.singleResult();
if(task!=null){
taskService.claim(task.getId(),taskCandidateUser);
}
}
lisi处理任务
/**
* 根据流程的key和任务负责人 处理任务
*/
@RequestMapping("solveTask")
@ResponseBody
public void solveTask() {
String assignee = "lisi";
String id = "myLeave5";
//根据流程的key和任务负责人 查询任务
Task task = taskService.createTaskQuery()
.processDefinitionKey(id)
.taskAssignee(assignee)
.singleResult();
//完成任务,跳到下一个流程
taskService.complete(task.getId());
}