其他分享
首页 > 其他分享> > 工作流学习日志 -(Activiti7进阶)

工作流学习日志 -(Activiti7进阶)

作者:互联网

工作流学习日志 -(Activiti7进阶)

文章目录


前言

开发OA人力资源管理系统时在人员调动时、请假审批等需要使用到工作流,以此作为学习契机。
工作流学习日志(基础-概念、基本api使用)


一、流程实例

流程实例(ProcessInstance)通过流程定义(ProcessDefinition)创建,代表了流程定义的实例。

1. 添加业务标识(BusinessKey)

BusinessKey:业务标识,通常是业务表的主见id,业务标识和流程实例一一对应。存储业务标识就是根据业务标识来关联查询业务系统的数据。其中的业务和流程是分开的,activiti主要是控制流程,而不是控制所有的的业务,所以需要使用有一张其他的表表示业务,最后可以进行关联。

例如:出差流程是一个流程实例,可以将出差单(业务)id作为BusinessKey存储到activiti中。将来查询activiti流程实例的信息就可以获取到对应的出差单(业务)id从而关联到业务系统数据库中的出差单信息。

添加businessKey并启动流程实例代码:

//1.获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

//2.获取RuntimeService
RuntimeService runtimeService = processEngine.getRuntimeService();

//3.添加businessKey(可以指某用户Id,开启一个新的工作流程 例如为1001)
ProcessInstance processInstance = runtimeService
        .startProcessInstanceByKey("myBusinessTrip", "1001");

System.out.println("流程业务名称="+processInstance.getBusinessKey());

添加成功后可以在Activiti数据库act_ru_execution中查看到存储的businessKey。

查询关联的businessKey:

// 获取processEngine 
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); 
// 获取TaskService 
TaskService taskService = processEngine.getTaskService(); 
// 获取RuntimeService 
RuntimeService runtimeService = processEngine.getRuntimeService(); 
// 查询流程定义的对象 
Task task = taskService.createTaskQuery() 
	.processDefinitionKey("myEvection1") 
	.taskAssignee("张三") .singleResult(); 
// 使用task对象获取实例id 
String processInstanceId = task.getProcessInstanceId(); 
// 使用实例id,获取流程实例对象 
ProcessInstance processInstance = runtimeService
	.createProcessInstanceQuery() 
	.processInstanceId(processInstanceId) 
	.singleResult(); 
// 使用processInstance,得到 businessKey 
String businessKey = processInstance.getBusinessKey(); 
System.out.println("businessKey=="+businessKey); }

2. 查询流程实例

流程在运行的过程中可以查询流程实例的状态,当前运行节点等信息

// 流程定义key 
String processDefinitionKey = "evection"; ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); 
// 获取RunTimeService 
RuntimeService runtimeService = processEngine.getRuntimeService(); 
List<ProcessInstance> list = runtimeService .createProcessInstanceQuery()
 .processDefinitionKey(processDefinitionKey)// .list();
 
for (ProcessInstance processInstance : list) { 
	System.out.println("----------------------------"); 
	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()); 
}

3. 挂起、激活流程实例

某些情况下流程变更需要当前运行的流程暂停而不是直接删除,那么可以将流程进行挂起,流程挂起后将不能继续执行流程,需要激活后才能继续执行。

例如:当审批员有变更的时候,那么审批的流程需要进行暂停,待审批员变更完成后才能继续进行执行;或者在非工作日时不能进行流程执行操作。

①. 全部流程实例挂起、激活

代码如下:

//1.获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.获取到流程定义的查询对象
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
        .processDefinitionKey("myBusinessTrip")
        .singleResult();
//4.查询processDefinition流程定义的状态
boolean suspended = processDefinition.isSuspended();
//5.如果挂起,那么激活流程定义
String definitionId = processDefinition.getId();
if (suspended) {
    //参数1:流程定义id,参数2:流程是否激活,参数3:流程激活时间
    repositoryService.activateProcessDefinitionById(definitionId,true,null);
    System.out.println("流程定义id为:"+definitionId+"激活成功");
} else{
    //6.如果正在活动,那么将流程定义挂起
    //参数1:流程定义id,参数2:流程是否挂起,参数3:流程挂起时间
    repositoryService.suspendProcessDefinitionById(definitionId,true,null);
    System.out.println("流程定义id为:"+definitionId+"挂起成功");
}

②. 单个流程实例挂起、激活

操作流程实例的对象,针对单个流程执行挂起操作,某个流程实例挂起则此流程不再继续进行执行,完成该流程实例的当前任务将报异常。

代码如下:

//1.获取processEngine 引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取runtimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
//3.根据processInstanceId获取指定的流程实例查询对象
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
        .processInstanceId("2501")
        .singleResult();
//4.根据processInstance流程实例获取其状态
boolean suspended = processInstance.isSuspended();
//5.如果为挂起状态,那么激活
String instanceId = processInstance.getId();
if(suspended){
    runtimeService.activateProcessInstanceById(instanceId);
    System.out.println("流程实例对象Id="+instanceId+",已经激活");
}else {
    //6.如果流程实例的状态为激活状态,那么将其挂起
    runtimeService.suspendProcessInstanceById(instanceId);
    System.out.println("流程实例对象Id="+instanceId+",已经挂起");
}
//1.获取流程引擎processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取对应的taskService
TaskService taskService = processEngine.getTaskService();
//3.根据taskService获取对应的流程任务查询
Task task = taskService.createTaskQuery()
        .processInstanceId("2501")//根据流程实例id进行查询
        .taskAssignee("lisi")
        .singleResult();

System.out.println("流程实例id="+task.getProcessInstanceId());
System.out.println("任务Id="+task.getId());
System.out.println("任务负责人="+task.getAssignee());
System.out.println("任务名称="+task.getName());

//4.根据taskId完成对应的任务
taskService.complete(task.getId());
}

二、个人任务

1. 分配任务负责人

①. 固定分配方式

直接在BPMN editor中将property为Assignee的值value设置为固定值,例:张三
在这里插入图片描述

②. 表达式分配

表达式分配可以解决固定分配情况下的assignee无法动态获取值的问题。

UEL表达式

Activiti使用UEL表达式,UEL是java EE6规范的一部分,UEL(Unified Expression Language)即统一表达式语言,activiti支持两个UEL表达式:UEL-value和UEL-method。

1)UEL-value:
可以直接设置一个变量assignee0,该变量为activiti的一个流程变量。
在这里插入图片描述
也可以通过调用某个类中的属性获取值,例如user是activiti中一个流程变量,user.assignee表示通过调用user的getter方法获取值。
在这里插入图片描述
2)UEL-method方式
UserBean是spring容器中的一个bean,表示调用该bean的getUserId()方法。
在这里插入图片描述
3)UEL-method与UEL-value结合

再比如: ${ldapService.findManagerForEmployee(emp)} ldapService 是 spring 容器的一个 bean, findManagerForEmployee 是该 bean 的一个方法,emp 是 activiti 流程变量, emp 作为参数传到
ldapService.findManagerForEmployee 方法中。

4)其他

表达式支持解析基础类型、 bean、 list、 array 和 map,也可作为条件判断。 如下: ${order.price > 100 && order.price < 250}

在启动流程实例时设置流程变量,如下:

//1.获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取runtimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
//3.启动定义的流程,startProcessInstanceByKey两参数,一:流程实例的key,二:流程中定义的负责人名称使用map进行封装
//使用map封装负责人的值,可以在act_ru_variable数据表中查看设置的map值
Map map = new HashMap();
map.put("assignee0","张三");
map.put("assignee1","李四");
map.put("assignee2","王五");
map.put("assignee3","赵六");
runtimeService.startProcessInstanceByKey("myProcess_1",map);
//4.输出
System.out.println("流程启动成功="+processEngine.getName());

执行成功后可以在数据库act_ru_variable表中看到刚才map中的数据

:由于使用了表达式分配,必须保证在任务执行过程表达式执行成功,比如: 某个任务使用了表达式${order.price > 100 && order.price < 250},当执行该任务时必须保证 order 在 流程变量中存在,否则 activiti 异常。

③. 监听器分配

Activiti中也有内置的监听器可以用来完成流程中的业务。可以使用任务监听器(Task Listener)指定负责人,那么在流程设计时可以不需要指定assignee。任务监听器是发生对应的任务相关事件时执行自定义java逻辑或表达式。

在这里插入图片描述
Event事件中包含了4个触发事件:

  1. create:任务创建后触发
  2. Assignment:任务分配后触发
  3. Delete:任务完成后触发
  4. ALL:所有事件发生都触发

代码如下:(定义任务监听类必须实现org.activiti.engine.delegate.TaskListener接口)

public class TestListener implements TaskListener {

    @Override
    public void notify(DelegateTask delegateTask) {
        //判断
        if("创建申请".equals(delegateTask.getName()) && "create".equals(delegateTask.getEventName())){
            delegateTask.setAssignee("张三");
        } 
    }
    
}

:使用监听器分配的方式,按照监听事件去执行监听类的notify方法,如果不能正常执行也会影响任务的执行。

2. 查询任务

查询任务负责人的待办任务,代码如下:

//选择负责人
String assignee = "zhangsan";

//获取到processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

//获取到taskService
TaskService taskService = processEngine.getTaskService();

//使用TaskService根据流程的key和任务负责人查询任务
List<Task> list = taskService.createTaskQuery()
        .processDefinitionKey("myBusinessTrip")//流程的key
        .taskAssignee(assignee)//查询该负责人的任务
        .list();

//遍历获取任务内容
for (Task task : list) {
    System.out.println("流程实例:"+task.getProcessInstanceId());
    System.out.println("任务id:"+task.getId());
    System.out.println("任务负责人:"+task.getAssignee());
    System.out.println("任务名称:"+task.getName());
}

代码如下(示例):

data = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())

3. 办理任务

:实际应用中,完成任务前需要检验任务的负责人是否具有该任务的办理权限

String assignee = "zhangsan";

//1.创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

//2.获取到TaskService
TaskService taskService = processEngine.getTaskService();

//完成任务前,需要检验该负责人是否可以完成当前任务
//检验方法:根据任务的id和任务负责人查询当前任务,如果查到该用户有权限,那么就完成。

//3.使用TaskService中的方法(使用任务id)完成任务
//taskService.complete("2505");

//4.根据流程的key以及负责人名称获取对应的任务id,动态进行完成任务
Task task = taskService.createTaskQuery()
        .processDefinitionKey("myBusinessTrip")//流程的key
        .taskAssignee(assignee)//任务负责人名称
        .singleResult();//获取单个task任务

System.out.println("任务的id="+task.getProcessDefinitionId());
System.out.println("任务id="+task.getId());
System.out.println("任务的名称="+task.getName());
System.out.println("任务的负责人="+task.getAssignee());

//5.动态获取任务id完成任务
taskService.complete(task.getId());

三. 流程变量

1. 概念

流程变量的activiti中是一个非常重要的角色,流程运转有时需要靠流程变量,业务系统和activiti结合时少不了流程变量,流程变量就是activiti在管理工作流时根据管理需要而设置的变量。比如:在出差申请流程流转时如果出差天数大于3天那么由总经理审核,否则由部门经理进行审核,在这里的出差天数就可以设置为流程变量,在流程运转时使用。

:流程变量中可以存储业务数据,可以通过activiti的api查询流程变量从而实现查询业务流程,单一一般不这样做,因为业务数据查询由业务系统负责,activiti设置流程变量是为了流程执行需要而创建。

2. 流程变量类型

:如果需要将pojo存储在流程变量中,那么必须实现序列化接口serializable。为了防止由于新增字段无法反序列化,需要生成serialVersionUID。

类型名称:

3. 流程变量作用域

流程变量作用域可以是一个流程实例(processInstance)、任务(task)、执行实例(execution)

①. global变量

流程变量的默认作用域是流程实例。当一个流程变量的作用域为流程实例时,可以称为 global 变量

如: Global变量:userId(变量名)、zhangsan(变量值)
global 变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前设置的变量值。

②. local变量

任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大, 称为 local 变量。

Local 变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。Local 变量名也可以和 global 变量名相同,没有影响。

4. 流程变量的使用方法

①. 在属性上使用UEL表达式

在assignee设置UEL表达式设置为任务的负责人

②. 在连线上使用UEL表达式

在连线上设置UEL表达式,可以决定流程的走向,UEL表达式结果为布尔类型。如图:
在这里插入图片描述

③. 设置global变量

通过设置global变量控制流程走向

在部门经理审核前设置流程变量,变量值为出差单信息(包括出差天数),部门经理审核后可以根据流程变量的值决定流程走向。

在设置流程变量时,可以在启动流程时设置,也可以在任务办理时设置

创建POJO对象

/**
 * 流程封装的业务类
 * 出差申请pojo
 */
public class BusinessTrip implements Serializable {
    /**
     * 主键id
     */
    private Long id;
    /**
     * 出差申请单名称
     */
    private String businessName;
    /**
     * 出差天数
     */
    private Double num;
    /**
     * 预计开始时间
     */
    private Date beginDate;
    /**
     * 预计结束时间
     */
    private Date endDate;
    /**
     * 目的地
     */
    private String destination;
    /**
     * 出差事由
     */
    private String reason;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getBusinessName() {
        return businessName;
    }

    public void setBusinessName(String businessName) {
        this.businessName = businessName;
    }

    public Double getNum() {
        return num;
    }

    public void setNum(Double num) {
        this.num = num;
    }

    public Date getBeginDate() {
        return beginDate;
    }

    public void setBeginDate(Date beginDate) {
        this.beginDate = beginDate;
    }

    public Date getEndDate() {
        return endDate;
    }

    public void setEndDate(Date endDate) {
        this.endDate = endDate;
    }

    public String getDestination() {
        return destination;
    }

    public void setDestination(String destination) {
        this.destination = destination;
    }

    public String getReason() {
        return reason;
    }

    public void setReason(String reason) {
        this.reason = reason;
    }
}

启动流程时设置变量
在启动流程时设置流程变量,变量的作用域是整个流程实例。通过Map设置流程变量,map中可以设置多个变量,key就是流程变量的名字

流程变量作用域是一个流程实例,流程变量使用Map存储,同一个流程实例设置变量map中key相同,后者覆盖前者。

        //1.创建流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        //2.获取runtimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();

        //获取到流程的key
        String processInstanceKey = "buiness-global1";
        //设置任务负责人参数
        BusinessTrip businessTrip = new BusinessTrip();
        businessTrip.setNum(3d);
        Map variable = new HashMap();
        variable.put("businessTrip",businessTrip);
        variable.put("assignee0","张三员工1");
        variable.put("assignee1","李四部门经理1");
        variable.put("assignee2","王五总经理1");
        variable.put("assignee3","赵六财务1");

        //3.根据processInstance的id以及传入的参数进行开启流程
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceByKey(processInstanceKey, variable);

        //4.输出
        System.out.println("流程实例名称="+processInstance.getName());
        System.out.println("流程定义id=="+processInstance.getProcessDefinitionId());

任务办理时设置变量

在完成任务时设置流程变量,该流程变量只有在该任务完成后其他结点才可以使用该变量,它的作用域是整个流程实例,如果设置的流程变量的key在流程实例中已存在相同的名字,则后设置的变量替换前边设置的变量。

//1.获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

//2.获取到taskService
TaskService taskService = processEngine.getTaskService();

/*//3.获取需要进行查询的参数67501
String processInstanceId = "55001";
String assignee = "赵六财务";*/

String processInstanceId = "85001";
String assignee = "赵六财务1";

BusinessTrip businessTrip = new BusinessTrip();
businessTrip.setNum(2d);
Map<String, Object> map = new HashMap<>();
map.put("businessTrip",businessTrip);

//4.创建任务查询
Task task = taskService.createTaskQuery()
        .processInstanceId(processInstanceId)
        .taskAssignee(assignee)
        .singleResult();

if (task != null){
    //5.如果task不为空,那么完成任务。
    taskService.complete(task.getId(),map);
    System.out.println("任务执行完成");
}

通过当前流程实例设置

通过流程实例id设置全局变量,该流程实例必须未执行完成

// 当前流程实例执行 id,通常设置为当前执行的流程实例 
String executionId="2601"; 
// 获取processEngine 
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); 
// 获取RuntimeService 
RuntimeService runtimeService = processEngine.getRuntimeService(); 
// 创建出差pojo对象 
Evection evection = new Evection(); 
// 设置天数 
evection.setNum(3d); 
// 通过流程实例 id设置流程变量
runtimeService.setVariable(executionId, "evection", evection); 
// 一次设置多个值 
// runtimeService.setVariables(executionId, variables)

四. 组任务

1. 需求

在流程定义中在任务结点的 assignee 固定设置任务负责人,在流程定义时将参与者固定设置在.bpmn 文件中,如果临时任务负责人变更则需要修改流程定义,系统可扩展性差。

针对这种情况可以给任务设置多个候选人,可以从候选人中选择参与者来完成任务。

2. 设置任务候选人

在流程图中任务节点的配置中设置candidate-users(候选人),多个候选人之间用逗号分开。
在这里插入图片描述

3. 组任务

组任务办理流程

①. 查询组任务

指定候选人,查询该候选人当前的待办任务

//设置需要获取到的任务查询的条件
String processDefinitionKey = "testcandidate";
String candidateUser = "王副部门经理";
//1.获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取taskService
TaskService taskService = processEngine.getTaskService();
//3.使用service获取到任务查询list
List<Task> list = taskService.createTaskQuery()
        .processDefinitionKey(processDefinitionKey)
        .taskCandidateUser(candidateUser)
        .list();
//4.输出信息
for (Task task : list) {
    System.out.println("----------------------------");
    System.out.println("流程实例id:" + task.getProcessInstanceId());
    System.out.println("任务id:" + task.getId());
    System.out.println("任务负责人:" + task.getAssignee());
    System.out.println("任务名称:" + task.getName());
}

②. 拾取(claim)任务

该任务的所有候选人都能拾取。将候选人的组任务变成个人任务,原来候选人就变成该任务的负责人。

//1.设置需要获取的任务查询条件
String taskId = "105002";
String candidateUser = "王副部门经理";
//2.获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//3.获取taskService
TaskService taskService = processEngine.getTaskService();
//4.创建任务查询
Task task = taskService.createTaskQuery()
        .taskId(taskId)
        .taskCandidateUser(candidateUser)
        .singleResult();
//5.如果该任务查询出来存在
if(task != null){
    //根据任务的id以及拾取候选人为负责人
    taskService.claim(task.getId(),candidateUser);
    System.out.println("任务id--"+taskId+"--的候选人--"+candidateUser+"拾取任务完成");
}

如果拾取后不想办理该任务,需要将已经拾取的个人任务归还到组里,将个人任务变成了组任务。

//1.设置需要获取的任务查询条件
String taskId = "105002";
String assignee = "王副部门经理";
//2.获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//3.获取taskService
TaskService taskService = processEngine.getTaskService();
//4.创建任务查询
Task task = taskService.createTaskQuery()
        .taskId(taskId)
        .taskAssignee(assignee)
        .singleResult();
//5.如果该任务查询出来存在
if(task != null){
    //根据任务的id设置负责人为null即为归还负责人
    taskService.setAssignee(task.getId(),null);
    System.out.println("任务id--"+taskId+"--的负责人--"+assignee+"归还任务完成");
}

任务的交接,实质上就是讲assignee进行修改

String taskId = "105002";
String assignee = "王副部门经理";
String candidateUser = "李部门经理";
//1.获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取taskService
TaskService taskService = processEngine.getTaskService();
//3.根据taskService获取到任务查询
Task task = taskService.createTaskQuery()
        .taskId(taskId)
        .taskAssignee(assignee)
        .singleResult();
//4.如果能够查询出来任务,那么就将任务的负责人进行交接
if(task != null){
    taskService.setAssignee(task.getId(),candidateUser);
    System.out.println("任务id--"+taskId+"--的负责人--"+assignee+"交接任务给"+candidateUser+"完成");
}

③. 查询个人任务

查询方法同个人人物部分,根据assignee查询用户负责的个人任务。

④. 办理个人任务

同以前的完成任务

五. 网关

网管主要是用来控制流程的流向。我们在前面学习了使用UEL表达式的方式直接在连线上来控制流程,但该方法并有时候会造成创建两次流程实例的情况,导致不能按照我们设定的要求进行走向;如果条件都不满足,流程就结束了(异常结束)。所以一般还需要网关进行控制流程走向。
在这里插入图片描述

1. 排他网关ExclusiveGateway

①. 概念

排他网关,用在流程中实现决策,当流程执行了该网关,所有的分支会判断条件是否为true。如果为true则执行该分支。

②. 用法

用法和以前一样,部署、开启流程实例、完成执行实例。

图片如下:
在这里插入图片描述

2. 并行网关ParallelGateway

并行网关允许多个分支并行执行,也可以将多条分支汇聚到一起,并行网关的功能基于进入和外出顺序流的:

:如果同一个并行网关有多个进入和多个外出顺序流,他就同时具有了分支和汇聚的功能,这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。与其他网关主要区别是,并行网关不会解析条件,即使顺序流定义了条件,也会忽略执行。
在这里插入图片描述

3. 包含网关InclusiveGetaway

包含网关可以看做排他网关和并行网关的结合体。

可以和排他网关一样,在顺序流上定义条件,包含网关会解析他们,但是主要区别是包含网关可以选择多与一条顺序流,这和并行网关一样。

如图所示:无论什么条件都会进入到人是审批中。接着从技术经理或项目经理满足条件的一个进行汇聚。
在这里插入图片描述

总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

标签:task,进阶,实例,流程,任务,Activiti7,日志,processEngine,id
来源: https://blog.csdn.net/weixin_46621774/article/details/115612158