activiti学习笔记二

 

 上一篇文章大概讲了下什么是流程引擎,为什么我们要用流程引擎,他的基本原理是啥,以及怎么进行基本的使用,这篇文章我们再讲下其他的一些使用。

 

删除流程部署
package activiti02;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;

/**
 *  删除流程部署
 *      影响了三张表:
 *          act_ge_bytearray
 *          act_re_deployment
 *          act_re_procdef
 *          历史表信息会被保留,如果级联删除,会把全部记录删除
 * */
public class ActivitiDeleteProcessDefinition {

    public static void main(String[] args) {
        // 1、创建ProcessEngine流程引擎对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到Repositoryervice实例
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 3、查询流程部署
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("holiday")
                .singleResult();

        // 4.1、删除流程定义
        // repositoryService.deleteDeployment(processDefinition.getDeploymentId());

        // 4.2、如果还有未结束的流程节点,可以使用级联删除(true)
        // deleteDeployment(String deploymentId, boolean cascade)
        repositoryService.deleteDeployment(processDefinition.getDeploymentId(),true);
    }
}

 

删除流程实例
package activiti02;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;

import java.util.List;

/**
 *  删除流程实例
 *  历史表信息会被保留
 * */
public class ActivitiDeleteProcessInstance {

    public static void main(String[] args) {
        // 1、创建ProcessEngine流程引擎对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        RuntimeService runtimeService = processEngine.getRuntimeService();
        TaskService taskService = processEngine.getTaskService();

        Task task = taskService.createTaskQuery().taskAssignee("zhangsan").singleResult();

        List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery()
                .processInstanceId(task.getProcessInstanceId()).list();

        for (ProcessInstance processInstance : processInstanceList) {
            runtimeService.deleteProcessInstance(processInstance.getId(),"删除流程实例");
        }

        System.err.println("ok");
    }
}

 

如果流程实例删除了,那这次的流程就不用再继续执行了。act_ru_* 表的相关记录都删除

 

挂起/激活流程定义:

package activiti03;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;

/**
 *  挂起全部流程实例,使其无法启动新的流程实例,未执行完的流程实例也不允许继续执行
 *
 * */
public class ActivitiSuspendAllProcessInstance {
    public static void main(String[] args) {
        // 1、得到processEngine对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到repositoryService对象
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 3、查询流程定义的对象
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("holiday")
                .singleResult();

        // 4、得到当前流程定义的实例是否都为暂停状态
        boolean suspended = processDefinition.isSuspended();

        // 5、判断
        if (suspended) {
            // 5.1、如果是暂停,那就全部激活
            repositoryService.activateProcessDefinitionById(processDefinition.getId(),true,null);
            System.err.println("流程定义:" + processDefinition.getId() + "激活");
        }else {
            // 5.2、如果是活动状态,那就全部挂起
            repositoryService.suspendProcessDefinitionById(processDefinition.getId(),true,null);
            System.err.println("流程定义:" + processDefinition.getId() + "挂起");
        }
    }
}

 

挂起流程定义后,就不能再创建流程实例了,同时未完成的流程实例、流程任务也无法继续完成。

 

挂起/激活单个流程实例:

package activiti03;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.runtime.ProcessInstance;

/**
 *  挂起指定一个流程实例,使其无法续执行。
 *  给流程定义依旧可以发起新的流程实例,其他流程实例不受影响
 *
 *  和挂起全部区别:
 *      挂起全部是针对流程定义来的,当流程定义被挂起后,该流程定义下的所有流程实例都自动被挂起
 *      挂起一个是针对具体的流程实例来的,只挂起一个流程实例,并不会影响整个流程定义,也不会影响其他流程实例
 * */
public class ActivitiSuspendSingleProcessInstance {
    public static void main(String[] args) {
        // 1、得到processEngine对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到runtimeService对象
        RuntimeService runtimeService = processEngine.getRuntimeService();

        // 3、查询流程实例的对象
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey("holiday")
                .singleResult();

        // 4、得到当前流程实例的实例是否都为暂停状态
        boolean suspended = processInstance.isSuspended();

        // 5、判断
        if (suspended) {
            // 5.1、如果是暂停,那就全部激活
            runtimeService.activateProcessInstanceById(processInstance.getId());
            System.err.println("流程实例定义id:" + processInstance.getId() + "激活");
        }else {
            // 5.2、runtimeService,那就全部挂起
            runtimeService.suspendProcessInstanceById(processInstance.getId());
            System.err.println("流程实例定义id:" + processInstance.getId() + "挂起");
        }
    }
}

 

 

activiti流程绑定业务记录(BusinessKey):

package activiti03;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.runtime.ProcessInstance;

/**
 *  activiti与自定义业务结合。
 *  原理就是在启动activiti流程实例的时候,传一个业务流程数据的唯一id进来。
 *  也就是说,activiti只是负责管理流程的推进,业务数据则是由我们自己来进行处理。
 *  两者通过act_ru_execution表的business_key来进行关联
 * */
public class ActivitiBusinessKey {
    public static void main(String[] args) {
        // 1、得到processEngine对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到RuntimeService对象
        RuntimeService runtimeService = processEngine.getRuntimeService();

        // 3、得到流程实例,需要直到流程定义的key,也就是流程process文件的Id,可以在bpmn里面查看,也可以在数据库act_re_procdef找到该流程的key
        // startProcessInstanceByKey(String processDefinitionKey, String businessKey)
        // 假设我们的业务请假id为1001
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday","1001");

        // 4、输出相关信息
        System.out.println("流程部署id  ===>  "+processInstance.getDeploymentId());
        System.out.println("流程实例id  ===>  "+processInstance.getProcessInstanceId());
        System.out.println("活动id  ===>  "+processInstance.getActivityId());
        System.out.println("business_key  ===>  "+processInstance.getBusinessKey());
    }
}

 

其实也就是在启动流程实例的时候,就绑定一个我们自己的业务key,这样以后我们就可以根据这个businessKey来我们自己的表查询相关业务

 

查询历史节点记录:

package activiti02;

import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;

import java.util.List;

/**
 *  历史数据查询
 * */
public class ActivitiHistoryQuery {

    public static void main(String[] args) {
        // 1、创建ProcessEngine流程引擎对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到HistoryService实例
        HistoryService historyService = processEngine.getHistoryService();

        // 3、获得HistoricActivityInstanceQuery对象,一个查询器
        HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery();

        // 4、设置条件,并查询
        HistoricActivityInstanceQuery historicActivity = historicActivityInstanceQuery.processInstanceId("12501");
        List<HistoricActivityInstance> historicActivityInstanceList = historicActivity.orderByHistoricActivityInstanceStartTime()
                .asc().list();

        // 5、输出流程定义
        for (HistoricActivityInstance processDefinition : historicActivityInstanceList) {
            System.out.println("节点id==》"+processDefinition.getActivityId());
            System.out.println("节点名==》"+processDefinition.getActivityName());
            System.out.println("流程定义id==》"+processDefinition.getProcessDefinitionId());
            System.out.println("流程实例id==》"+processDefinition.getProcessInstanceId());
            System.out.println();
        }
    }
}

 

 

查询流程定义:

package activiti02;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;

import java.util.List;

public class ActivitiQueryProcessDefinition {

    public static void main(String[] args) {
        // 1、创建ProcessEngine流程引擎对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到Repositoryervice实例
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 3、获得ProcessDefinitionQuery对象,一个查询器
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();

        // 4、设置条件,并查询出当前的所有流程定义
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.processDefinitionKey("holiday")
                .orderByProcessDefinitionVersion()      // 设置排序
                .desc().list();

        // 5、输出流程定义
        for (ProcessDefinition processDefinition : processDefinitionList) {
            System.out.println("流程定义id==》"+processDefinition.getId());
            System.out.println("流程定义key==》"+processDefinition.getKey());
            System.out.println("流程定义名==》"+processDefinition.getName());
            System.out.println("流程定义version==》"+processDefinition.getVersion());
            System.out.println("流程部署id==》"+processDefinition.getDeploymentId());
        }
    }
}

 

 

导出流程文件bpmn和图片:

package activiti02;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.apache.commons.io.IOUtils;

import java.io.*;

/**
 *  需求:
 *      1、从activiti的act_ge_bytearray读取两个资源文件
 *      2、将这两个资源文件放到指定目录
 *
 *  实现方案:
 *      1、使用activiti自带的api,调用IO流转换,输出到磁盘
 *      2、使用jdbc的对blob类型、clob类型数据的读取,并 调用IO流转换,输出到磁盘
 *
 * */
public class DownloadBPMNFile {
    public static void main(String[] args) throws IOException {
        // 1、创建ProcessEngine流程引擎对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到Repositoryervice实例
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 3、得到查询器ProcessDefinitionQuery对象
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();

        // 4、设置查询条件
        processDefinitionQuery.processDefinitionKey("holiday");

        // 5、执行查询操作,得到想要的流程定义
        ProcessDefinition processDefinition = processDefinitionQuery.singleResult();

        // 6、通过流程定义信息,得到部署id
        String deploymentId = processDefinition.getDeploymentId();

        // 7、通过 RepositoryService 的方法,实现读取图片信息以及bpmn文件信息(输入流)
        // getResourceAsStream(String deploymentId, String resourceName)
        // processDefinition.getDiagramResourceName():获取流程图片名字
        InputStream pngStream = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());
        // processDefinition.getResourceName():获取流程bpmn文件名字
        InputStream bpmnStream = repositoryService.getResourceAsStream(deploymentId,processDefinition.getResourceName());

        // 8、构建输出流
        OutputStream pngOutputStream = new FileOutputStream("C:\\Users\\HongCheng\\Desktop\\holiday\\" + processDefinition.getDiagramResourceName());
        OutputStream bpmnOutputStream = new FileOutputStream("C:\\Users\\HongCheng\\Desktop\\holiday\\" + processDefinition.getResourceName());

        // 9、转换输入流并保存文件
        IOUtils.copy(pngStream,pngOutputStream);
        IOUtils.copy(bpmnStream,bpmnOutputStream);

        // 10、关闭流
        pngOutputStream.close();
        bpmnOutputStream.close();
        pngStream.close();
        pngStream.close();
    }
}

 

 

流程变量

package activiti04;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.runtime.ProcessInstance;

import java.io.Serializable;
import java.util.HashMap;

/**
 *  启动流程实例,流程节点参与人使用UEL表达式 ${变量名}  ${变量名.属性名}   ${变量名.方法名()}
 * */
public class ActivitiStartInstanceUEL {
    public static void main(String[] args) {
        // 1、得到processEngine对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到RuntimeService对象
        RuntimeService runtimeService = processEngine.getRuntimeService();

        // 3、设置assignee的值,   流程节点的参与人可以动态选择
        HashMap<String, Object> map = new HashMap<>();
        map.put("assignee","zhaoliu");
        map.put("user",new User("苏七","666","suqi"));

        // 4、启动流程实例,同时传入流程定义的参数值
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday3", map);

        // 5、输出相关信息
        System.out.println(processInstance.getProcessDefinitionName());
    }
}

@Getter
@Setter
@AllArgsConstructor
class User implements Serializable {
    private String name;
    private String id;
    private String assignee;

    public String getUserAssignee(){
        return id + name + assignee;
    }
}

 

流程变量是指在整个流程实例中使用的变量,activiti支持UEL表达式作为流程变量。

UEL表达式有三种写法: ${变量名}       ${变量名.属性名}        ${变量名.方法名()}

另外,流程变量分成全局流程变量和局部流程变量。

流程变量分为:

  • 全局global变量,所有节点都可以用。
  • 任务节点local变量,仅当前节点可用

全局的流程变量可以通过流程实例来添加,也可以通过任务节点来添加
  ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday4", map);
  runtimeService.setVariable(String processInstanceId, String variableName, Object value);
  runtimeService.setVariables( String processInstanceId, Map<String, ? extends Object> variables);

  taskService.complete(String taskId, Map<String, Object> variables)
  taskService.setVariables(String taskId, Map<String, ? extends Object> variables)
  taskService.setVariable(String taskId, String variableName, Object value)

 

局部流程变量:
  只能在当前任务节点使用,一旦当前任务节点结束,这个流程变量就会被删除。
  但是历史表里面依旧还是会保存我们的局部流程变量,依旧还是可以获取到。只是当前正在执行的任务不能直接获取局部流程变量了而已,曲线获取还是可以的。

局部流程变量的设置:
  runtimeService.setVariableLocal(String processInstanceId, String variableName, Object value);
  runtimeService.setVariablesLocal( String processInstanceId, Map<String, ? extends Object> variables);

  taskService.setVariablesLocal(String taskId, Map<String, ? extends Object> variables)
  taskService.setVariableLocal(String taskId, String variableName, Object value)

注意:如果局部变量是加在流程实例上,那依旧还是把所有任务节点共享

 

下面给一个使用流程变量来确定动态指定任务责任人的例子:

 

 

 

 

 

 

 

 

 

 

 

 流程变量的使用大致就是这样子。

 我们可以在数据库act_ru_variable、act_hi_varinst、act_ge_bytearray找到相关信息

 

 

 

 

 

 

任务监听器:

 

 

 

 

 

 

 

 

 

任务监听器是在发生对应的任务相关事件时执行的自定义java逻辑或表达式。

有四种类型的任务监听器,好像只是用在task 节点上才有用:

  • create:任务创建后触发
  • assignment:任务分配后触发
  • complete:任务完成后触发
  • all:所有任务事件都会触发


监听器的执行者有四种:

  • java类:必须实现org.activiti.engine.delegate.TaskListener这个接口
  • Expression 表达式:
  • Delegate Expression 表达式:
  • script脚本

 

 

 我们可以在监听器里面改变任务处理人,也可以进行其他操作

 

 

动态选择任务节点的处理人:

参考前面流程变量时最后举得那个例子

 

简单的任务分支:

 

 

 

 

 

 

需要用到流程变量,流程变量可以是UEL表达式,但条件必须是boolean类型。


注意:

  1. 如果UEL表达式中流程变量名不存在会报错
  2. 如果UEL表达式中流程变量为空null,流程不按UEL表达式执行,直接流程结束
  3. 如果UEL表达式都不符合条件,流程结束
  4. 如果任务连线不设置条件,会走flow序号最小的那天=条路径
  5. 这种简单的分支下,如果多条分支的条件都成立时,会多条分支一起走,会出现问题。所以分支一般用网关

 

这种分支方式很简单,但也有一些限制,如果不是很复杂的流程,可以这样使用,但还是建议使用网关。

 

 

 flow序号其实就是线的id

 

 

任务候选人/任务候选组:

 

我们之前指定任务都是使用assignee来指定具体哪一个人来执行任务,但是如果这个任务可以有多个执行人,也就是说张三、李四、王五、赵六四个人都可以执行同一个任务,那么就不能通过assignee来指定了。

此时就可以用candidate user(候选人)来指定一堆执行人,将来这一堆执行人都可以看到这个任务,但是只能有一个人将这个任务领取并执行。多个候选人之间通过逗号隔开

另外也有一个candidate groups(候选组)来指定一个人员组,在这个组里的人都是候选人,就不用一个个人来指定了。候选组其实也就是一个角色


组任务的办理流程:

  1、指定候选人,或指定候选人组部署流程

  2、创建流程实例
  3、候选人查询当前待办任务
  4、候选人领取任务,将组任务变成个人任务
    ***如果该候选人领取任务后又不想处理了,可以将任务归还,个人任务变成组任务
  5、根据assignee来查询自己的任务
  6、处理任务

taskService.claim(task.getId(),任务负责人);领取任务
taskService.setAssignee(task.getId(),null);放弃任务
taskService.setAssignee(task.getId(),任务负责人);委派任务


在我们正常的业务中,

assignee应该是我们的系统用户标识,可以是用户id,也可以是用户名,反正能唯一区分就行。其意思就是这个任务指定某个人完成,一般都是使用流程变量,动态决定谁可以做。例如我们在用钉钉发起审批时,有时就可以指定给哪个领导审。

candidate user(候选人)应该是多个系统用户标识,其意思就是这个任务可以是这堆人中的某一个来完成。

candidate groups(候选组)应该是用户的角色,在正常的系统中,使用角色将用户进行分组。而我们的实际生活中也是这样,有领导角色身份的人,才能进行最后的审批决定。

package activiti04.group;

import activiti01.ActivitiDeployment;
import org.activiti.engine.*;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.junit.Test;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.zip.ZipInputStream;

/**
 *  组任务
 * */
public class ActivitiTaskGroup {
    public static void main(String[] args) {
        ActivitiTaskGroup activitiTaskGroup = new ActivitiTaskGroup();
        activitiTaskGroup.giveUpTask();
    }


    /**
     * 放弃任务。
     * 原理:
     *      直接将task的assignee设置成null,让候选人重新抢任务。
     *      也可以将任务指定给某个具体的用户,相当于委派给别人执行
     *  注意:
     *      如果这个任务不是指定候选组的,那么当这个人放弃后,将没有人可以再领取这个任务
     */
    @Test
    public void giveUpTask() {
        // 1、得到processEngine对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到TaskService对象
        TaskService taskService = processEngine.getTaskService();

        // 3、查询用户的组任务
        String userId = "hongcheng";
        List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup")
                .taskAssignee(userId).list();

        // 4、放弃任务
        for (Task task : taskList) {
            System.err.println("流程实例id ==> " + task.getProcessInstanceId());
            System.err.println("任务定义key ==> " + task.getTaskDefinitionKey());
            System.err.println("任务id ==> " + task.getId());
            System.err.println("任务处理人 ==> " + task.getAssignee());
            System.err.println("任务名 ==> " + task.getName());
            if (userId.equals(task.getAssignee())) {
                // 放弃任务
                taskService.setAssignee(task.getId(), null);
                // 任务委派
//                taskService.setAssignee(task.getId(),"hongcheng");
            }
            System.err.println("任务放弃成功 ==> " + userId);
        }
    }



    /**
     *  认领组任务,claim也可以说是对外宣称任务
     * */
    @Test
    public void claimGroupTaskCandidateUserGroup() {
        // 1、得到processEngine对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到TaskService对象
        TaskService taskService = processEngine.getTaskService();

        // 3、查询用户的组任务
        String userId = "hongcheng";
        List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup")
                .taskCandidateGroup("leader").list();

        // 4、认领任务
        for (Task task : taskList) {
            System.err.println("流程实例id ==> "+task.getProcessInstanceId());
            System.err.println("任务定义key ==> "+task.getTaskDefinitionKey());
            System.err.println("任务id ==> "+task.getId());
            System.err.println("任务处理人 ==> "+task.getAssignee());
            System.err.println("任务名 ==> "+task.getName());
            // 认领任务
            taskService.claim(task.getId(),userId);
            System.err.println("任务认领成功 ==> "+userId);

        }
    }


    /**
     *  查询组任务,也就是候选人有自己的任务,此时该组任务并没有真正确定谁是任务的最终负责人assignee
     *  按候选人组进行查询
     * */
    @Test
    public void queryGroupTaskCandidateUserGroup() {
        // 1、得到processEngine对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到TaskService对象
        TaskService taskService = processEngine.getTaskService();

        // 3、查询用户的组任务
        List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup")
                .taskCandidateGroup("leader").list();

        // 5、输出
        for (Task task : taskList) {
            System.err.println("流程实例id ==> "+task.getProcessInstanceId());
            System.err.println("任务定义key ==> "+task.getTaskDefinitionKey());
            System.err.println("任务id ==> "+task.getId());
            System.err.println("任务处理人 ==> "+task.getAssignee());
            System.err.println("任务名 ==> "+task.getName());
        }
    }


    /**
     *  认领组任务,claim也可以说是对外宣称任务
     * */
    @Test
    public void claimGroupTaskCandidateUser() {
        // 1、得到processEngine对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到TaskService对象
        TaskService taskService = processEngine.getTaskService();

        // 3、查询用户的组任务
        String userId = "wangwu";
        List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup")
                .taskCandidateUser(userId).list();

        // 4、认领任务
        for (Task task : taskList) {
            System.err.println("流程实例id ==> "+task.getProcessInstanceId());
            System.err.println("任务定义key ==> "+task.getTaskDefinitionKey());
            System.err.println("任务id ==> "+task.getId());
            System.err.println("任务处理人 ==> "+task.getAssignee());
            System.err.println("任务名 ==> "+task.getName());
            // 认领任务
            taskService.claim(task.getId(),userId);
            System.err.println("任务认领成功 ==> "+userId);

        }
    }





    /**
     *  查询组任务,也就是候选人有自己的任务,此时该组任务并没有真正确定谁是任务的最终负责人assignee
     *  按候选人列表进行查询
     * */
    @Test
    public void queryGroupTaskCandidateUser() {
        // 1、得到processEngine对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到TaskService对象
        TaskService taskService = processEngine.getTaskService();

        // 3、查询用户的组任务
        List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup")
                .taskCandidateUser("lisi").list();

        // 5、输出
        for (Task task : taskList) {
            System.err.println("流程实例id ==> "+task.getProcessInstanceId());
            System.err.println("任务定义key ==> "+task.getTaskDefinitionKey());
            System.err.println("任务id ==> "+task.getId());
            System.err.println("任务处理人 ==> "+task.getAssignee());
            System.err.println("任务名 ==> "+task.getName());
        }
    }



    /**
     *  完成任务
     * */
    @Test
    public void complete() {
        // 1、得到processEngine对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        // 2、得到TaskService对象
        TaskService taskService = processEngine.getTaskService();
        // 3、结合任务查询,将查询到的任务进行处理
        List<Task> taskList = taskService.createTaskQuery()
                .processDefinitionKey("holidayGroup")
                .taskAssignee("zhangsan")
                .list();
        // 4、完成任务
        for (Task task : taskList) {
            taskService.complete(task.getId());
            System.err.println(task.getName());
        }
    }

    /**
     *  查询自己的个人任务
     * */
    @Test
    public void taskQuery() {
        // 1、得到processEngine对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        // 2、得到TaskService对象
        TaskService taskService = processEngine.getTaskService();
        // 3、用流程定义的key和负责人assignee来实现当前用户的任务列表查询
        List<Task> taskList = taskService.createTaskQuery()
                .processDefinitionKey("holidayGroup").taskAssignee("zhangsan")
                .list();
        // 4、任务列表查询
        for (Task task : taskList) {
            System.err.println("流程实例id ==> "+task.getProcessInstanceId());
            System.err.println("任务定义key ==> "+task.getTaskDefinitionKey());
            System.err.println("任务id ==> "+task.getId());
            System.err.println("任务处理人 ==> "+task.getAssignee());
            System.err.println("任务名 ==> "+task.getName());
        }
    }

    /**
     *  启动流程
     * */
    @Test
    public void startInstance() {
        // 1、得到processEngine对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到RuntimeService对象
        RuntimeService runtimeService = processEngine.getRuntimeService();

        // 3、设置assignee的值,   流程节点的参与人可以动态选择
        HashMap<String, Object> map = new HashMap<>();

        // 4、启动流程实例,同时传入流程定义的参数值
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holidayGroup", map);

        // 5、输出相关信息
        System.out.println(processInstance.getProcessDefinitionName());
        System.out.println(processInstance.getId());
    }


    /**
     *  部署流程
     * */
    @Test
    public void deployment() {
        // 1、创建ProcessEngine流程引擎对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到Repositoryervice实例
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 3、获取压缩文件
        InputStream inputStream = ActivitiDeployment.class.getClassLoader().getResourceAsStream("bpmn/group/holidayGroup.zip");

        // 4、创建一个ZipInputStream流
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);

        // 3、进行部署,bpmn文件是一定要的,图片文件可以没有,流程key相同的话,会使用最新部署的流程定义
        Deployment deployment = repositoryService.createDeployment()
                .addZipInputStream(zipInputStream)
                .name("请假申请流程候选人")
                .deploy();

        // 4、输出部署的信息
        System.out.println(deployment.getName());
        System.out.println(deployment.getId());
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd
="http://www.w3.org/2001/XMLSchema"
xmlns:activiti
="http://activiti.org/bpmn"
xmlns:bpmndi
="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc
="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi
="http://www.omg.org/spec/DD/20100524/DI"
typeLanguage
="http://www.w3.org/2001/XMLSchema"
expressionLanguage
="http://www.w3.org/1999/XPath"
targetNamespace
="http://www.activiti.org/test"> <process id="holidayGroup" name="候选人流程" isExecutable="true"> <startEvent id="startevent1" name="Start"></startEvent> <userTask id="usertask1" name="填写请假申请" activiti:assignee="zhangsan"></userTask> <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow> <userTask id="usertask2" name="部门经理审核" activiti:candidateUsers="lisi,wangwu"></userTask> <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow> <userTask id="usertask3" name="总经理审核" activiti:candidateGroups="leader"></userTask> <sequenceFlow id="flow3" sourceRef="usertask2" targetRef="usertask3"></sequenceFlow> <endEvent id="endevent1" name="End"></endEvent> <sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_holidayGroup"> <bpmndi:BPMNPlane bpmnElement="holidayGroup" id="BPMNPlane_holidayGroup"> <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"> <omgdc:Bounds height="35.0" width="35.0" x="110.0" y="230.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1"> <omgdc:Bounds height="55.0" width="105.0" x="190.0" y="220.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2"> <omgdc:Bounds height="55.0" width="105.0" x="340.0" y="220.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3"> <omgdc:Bounds height="55.0" width="105.0" x="490.0" y="220.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"> <omgdc:Bounds height="35.0" width="35.0" x="640.0" y="230.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1"> <omgdi:waypoint x="145.0" y="247.0"></omgdi:waypoint> <omgdi:waypoint x="190.0" y="247.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2"> <omgdi:waypoint x="295.0" y="247.0"></omgdi:waypoint> <omgdi:waypoint x="340.0" y="247.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3"> <omgdi:waypoint x="445.0" y="247.0"></omgdi:waypoint> <omgdi:waypoint x="490.0" y="247.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4"> <omgdi:waypoint x="595.0" y="247.0"></omgdi:waypoint> <omgdi:waypoint x="640.0" y="247.0"></omgdi:waypoint> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>

 

 

网关:

网关其实就是控制流程进行分支、汇聚的一个节点

activiti里面网关有4种:

  • 排他网关
  • 并行网关
  • 包含网关
  • 事件网关

和简单任务分支相比就是多个了网关节点,其他东西不变。

 

排他网关:

  • 只会执行一条路径,如果有多条路径可以走,那么默认会走下一个节点的id比较小的那条路径。可以防止多条分支同时成立,同时走了多条分支。
  • 如果没有一个分支可以走,那就会报错。
  • 必须保证有一条路径可以走

并行网关:

  • 可以作为多条分支汇聚(只有所有汇聚分支都完成,才会进去下一步)。
  • 也可以作为多条分支分散,分散分支时没有条件的,即便你设置了条件,也会被忽略掉,他肯定是全部分支都要执行。

 

 

 

包含网关:

  • 同时拥有排他网关和并行网关的特性。也就是说根据条件分散执行条件成立的一个或多个分支,也可以等待一个或多个分支汇聚。

针对上图,当userType==1时,会同时走常规项目和抽血化验这两条路径,如果userType==2,会走三条路径

 

任务定时提醒:

我们实际的流程中,经常会有人一直不审批,所以就需要有一个提醒机制

这个时候我们就需要用到边界事件。有3篇博客介绍的挺不错的,有兴趣的自己去看:

https://blog.csdn.net/qq_33333654/article/details/101374202

https://blog.csdn.net/qq_33333654/article/details/101373157

https://www.cnblogs.com/dengjiahai/p/6942310.html

iso 8601格式解析

R2/2015-06-04T19:25:16.828696-07:00/P1DT10S

上面的字符串通过"/"分为了三部分即:

重复次数/开始时间/运行间隔

重复次数

  • R - 将永远重复
  • R1 - 将重复一次
  • R231 - 将重复231次。

开始时间

任务第一次运行的时间。如果开始日期时间已经过去,将返回一个错误。

其中"T"用来分割日期和时间,时间后面跟着的"-07:00"表示西七区,注意"-"是连字符,不是减号。

时区默认是0时区,可以用"Z"表示,也可以不写。

对于我国,要使用"+08:00",表示东八区。
上面的字符串表示 2015年6月4日,19点25分16秒828696纳秒,西七区。

运行间隔

运行间隔以"P"开始,和上面一样也是用"T"分割日期和时间,如P1Y2M10DT2H30M15S

  • P 开始标记
  • 1Y - 一年
  • 2M - 两个月
  • 10D - 十天
  • T - 时间和日期分的割标记
  • 2H - 两个小时
  • 30M - 三十分钟
  • 15S 十五秒钟

例子,注意如果没有年月日,"T"也不能省略

  • P1DT1M - 一天一分钟执行一次
  • P1W - 一周执行一次
  • PT1H - 一小时执行一次
  • PT10S - 十秒执行一次

Time duration:延迟时间,使用“运行间隔”的格式,例如:PT1M30S,一分30秒后执行。

Time date:可以是java.util.Date的变量,也可以是符合ISO 8601 的一个时间点字符串

Time cycle:格式:“循环次数/执行间隔”,例如:R3/P1DT3H,循环执行3次,每次间隔1天3小时。这里也可以使用cron表达式。

cancel activiti:是否取消当前节点任务,true表示只要超时了,就取消这个当前节点任务。

 

原理:activiti会在act_ru_timer_job表增加一条记录,到指定的时间时,会读取这条记录,同时判断这个任务完成没有,如果没有完成,就会执行TimerBoundaryEvent连着的下一个任务。如果制定了要取消当前任务,就会把当前任务取消。

 

 注意:想要启动定时器,加上这段配置
 

  注意:想要启动定时器,加上这段配置

 

 

Service Task服务任务:

之前我们都是使用user task,那这两者有啥区别呢?

user task是我们的用户自定义任务,需要我们自己来手动完成

但是service task是服务任务,只要进入了这个任务,系统就会自动执行,不需要我们手动执行。

我们上个例子就用了service task来做循环提醒

 

 

 

 需要注意的是,你得实现

org.activiti.engine.delegate.JavaDelegate这个接口

 

流程驳回:

我们的实际中的流程经常会被打回,这种情况下我个建议使用排他网关进行分支。当然,网上也有其他的方式进行撤回,但我还是感觉这样子更简单点

 

posted @ 2020-06-26 16:49  _ME  阅读(1148)  评论(2编辑  收藏  举报