activiti 流程 java生成 activiti自定义流程_activiti 流程 java生成

Activiti 流程操作

1、流程定义

流程定义是线下按照bpmn2.0标准去描述业务流程,通常使用 idea 中的插件对业务流程进行建模。使用 idea下的designer设计器绘制流程,并会生成两个文件:.bpmn和.png

个人理解:流程定义就是一个大的层面,比如请假流程,是整个公司都通用的,它定义了整个公司的请假要走的流程,因此叫做流程定义。

如何生成 .png 图片文件?

  1. 先把 apply.bpmn 在 IDEA 重命名为:apply.xml
  2. 对着 apply.xml 鼠标右键,找到【Diagrams】->【Show BPMN 2.0 Designer】

activiti 流程 java生成 activiti自定义流程_Activiti_02

      3、找到顶部菜单的导出文件按钮,然后选择导出的目录,默认是 png 格式。导出即可。

activiti 流程 java生成 activiti自定义流程_启动流程_03

结果如图:

activiti 流程 java生成 activiti自定义流程_Activiti_04

 

生成 png 文件后,把 apply.xml 重命名回 apply.bpmn。然后把 apply.png 文件也放到 resources 目录下。

activiti 流程 java生成 activiti自定义流程_Test_05

 

2、流程定义的部署

将上面在设计器中定义的流程部署到activiti数据库中,就是流程定义部署。

通过调用 activiti 的api将流程定义的 bpmn和png两个文件一个一个添加部署到activiti中,也可以将两个文件打成zip包进行部署。

2-1、单个文件部署方式

/**
     * 部署流程定义
     */
    @Test
    public void testDeployment(){
        //创建 ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //得到 RepositoryService 实例
        RepositoryService service = processEngine.getRepositoryService();
        //使用 RepositoryService 实例进行部署
        Deployment deployment = service.createDeployment()
                .addClasspathResource("bpmn/apply.bpmn") //添加 bpmn 资源
                .addClasspathResource("bpmn/apply.png") //添加 png 资源
                .name("员工请假流程")
                .deploy();
        System.out.println("流程部署Id="+deployment.getId());
        System.out.println("流程部署name="+deployment.getName());
    }

点击运行,控制台输出:

流程部署Id=1
流程部署name=员工请假流程

 

2-2、压缩包部署方式

注意:如果使用了【单个文件的部署】方式,就不要使用压缩包的部署方式了,不然会导致重复的数据,后面的测试容易出错。如果数据出错,建议把数据库删掉,重新建表,弄测试数据来测。

需要把 apply.bpmn 和 apply.png 压缩成 zip 格式,压缩包名字叫做:apply.zip  并放到 resources/bpmn 目录下。

/**
     * 压缩包方式流程部署
     */
    @Test
    public void deployProcessByZip() {
        // 定义zip输入流
        InputStream inputStream = this
                .getClass()
                .getClassLoader()
                .getResourceAsStream("bpmn/apply.zip");
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);
        //创建 ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //得到 RepositoryService 实例
        RepositoryService service = processEngine.getRepositoryService();
        // 流程部署
        Deployment deployment = service.createDeployment()
                .addZipInputStream(zipInputStream)
                .name("员工请假流程-2")
                .deploy();
        System.out.println("流程部署id:" + deployment.getId());
        System.out.println("流程部署名称:" + deployment.getName());
    }

流程部署id:2501
流程部署名称:员工请假流程-2

 

2-3、流程定义部署后操作activiti的4张表如下:

act_re_deployment     流程定义部署表,每部署一次增加一条记录。

act_re_procdef            流程定义表,部署每个新的流程定义都会在这张表中增加一条记录

act_ge_bytearray        流程资源表 

act_ge_property        系统相关属性表

可以查看这几个表都有什么数据。

我们会发现每次部署流程,流程id 并不是递增1,而是递增了 2500。这是在系统属性表(act_ge_property)里控制的。

activiti 流程 java生成 activiti自定义流程_启动流程_06

这里说一下流程定义表:act_re_procdef,这个 KEY 是用来唯一识别不同流程的关键字。实际业务是不允许重复的,也就是部署流程的时候,相同的流程只需要部署一次。我们是测试演示的。

activiti 流程 java生成 activiti自定义流程_Activiti_07

 注意:

act_re_deployment和act_re_procdef一对多关系,一次部署在流程部署表生成一条记录,但一次部署可以部署多个流程定义,每个流程定义在流程定义表生成一条记录。每一个流程定义在act_ge_bytearray会存在两个资源记录,bpmn和png。

建议:一次部署一个流程,这样部署表和流程定义表是一对一有关系,方便读取流程部署及流程定义信息。

 

3、启动流程实例

流程定义部署在 activiti 后就可以通过工作流管理业务流程了,也就是说上边部署的请假申请流程可以使用了。

针对该流程,启动一个流程表示发起一个新的请假申请单,这就相当于java类与java对象的关系,类定义好后需要 new 创建一个对象使用,当然可以 new 多个对象。对于请假申请流程,张三发起一个请假申请单需要启动一个流程实例,请假申请单发起一个请假单也需要启动一个流程实例。

个人理解的流程实例:是具体到某一个流程定义的一个实际例子,叫流程实例。比如公司的请假流程叫做【流程定义】,它规定请假要经过多少人审批。而张三发起的请假申请,是流程实例,是具体的某一个案例,因此叫流程实例。

启动流程测试代码:

/**
     * 启动流程实例
     */
    @Test
    public void testStartProcess(){
        //1、创建 ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2、获取 RunTimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //3、根据流程定义ID启动流程
        ProcessInstance instance = runtimeService.startProcessInstanceByKey("myApply");
        System.out.println("流程定义id=" + instance.getProcessDefinitionId());
        System.out.println("流程实例id=" + instance.getId());
        System.out.println("当前活动Id=" + instance.getActivityId());

    }

运行结果:

流程定义id=myApply:3:5004
流程实例id=7501
当前活动Id=null

说明:因为我们部署了3次流程实例,因此下一次的实例id=7501

 

启动流程实例操作的数据表有:

act_hi_actinst     流程实例执行历史

act_hi_identitylink  流程的参与用户历史信息

act_hi_procinst      流程实例历史信息

act_hi_taskinst       流程任务历史信息

act_ru_execution   流程执行信息

act_ru_identitylink  流程的参与用户信息

act_ru_task              任务信息

 

4、任务查询

流程启动后,任务的负责人就可以查询自己当前需要处理的任务,查询出来的任务都是该用户的待办任务。

测试代码:

/**
     * 查询个人待执行的任务列表
     */
    @Test
    public void testFindPersonalTaskList(){
        //1、创建 ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //创建 TaskService 实例
        TaskService taskService = processEngine.getTaskService();
        //根据流程key 和任务负责人id 查询任务
        //注意 Task 引入的是:org.activiti.engine.task.Task
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey("myApply")
                .taskAssignee("zhangsan")
                .list();
        for(Task task : list){
            System.out.println("流程实例id=" + task.getProcessInstanceId());
            System.out.println("任务id=" + task.getId());
            System.out.println("任务负责人=" + task.getAssignee());
            System.out.println("任务名称=" + task.getName());
        }
    }

运行结果:

流程实例id=7501
任务id=7505
任务负责人=zhangsan
任务名称=创建请假申请

 

5、流程任务处理

任务负责人查询待办任务,选择任务进行处理,完成任务。

代码:

/**
     * 完成任务
     */
    @Test
    public void testCompleteTask(){
        //1、创建 ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2/创建 TaskService 实例
        TaskService taskService = processEngine.getTaskService();
        //根据流程key 和负责人id 查询任务
        Task task = taskService.createTaskQuery()
                .processDefinitionKey("myApply")//流程定义的key
                .taskAssignee("zhangsan")//要查询的负责人
                .singleResult();
        //完成任务,需要 taskId
        taskService.complete(task.getId());
    }

运行结果:

activiti 流程 java生成 activiti自定义流程_Test_08

 

6、流程定义信息查询

查询流程相关信息,包含流程定义,流程部署,流程定义版本

代码:

/**
     * 查询所有流程定义
     */
    @Test
    public void testQueryAllProcessDefinition(){
        //1、创建 ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //获取 RepositoryService 实例
        RepositoryService service = processEngine.getRepositoryService();
        //得到 ProcessDefinitionQuery 实例
        ProcessDefinitionQuery query = service.createProcessDefinitionQuery();
        //查询
        List<ProcessDefinition> list = query
                .processDefinitionKey("myApply")//增加流程定义的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());
        }
    }

输出:

流程定义 id=myApply:3:5004
流程定义 name=请假流程
流程定义 key=myApply
流程定义 Version=3
流程部署ID =5001

7、流程删除

代码:

/**
     * 流程删除
     */
    @Test
    public void deleteDeployment(){
        //1、创建 ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        // 通过流程引擎获取repositoryService
        RepositoryService service = processEngine.getRepositoryService();
        //删除流程定义,如果该流程定义已经有流程实例启动,则删除出错
        service.deleteDeployment("1",false);
        //设置true 级联删除流程定义,即使该流程有流程实例启动也可以删除,设置为false非级别删除方式
        // service.deleteDeployment("1", true);

    }

说明:

1、使用 repositoryService 删除流程定义,历史表信息不会被删除。

2、如果该流程定义下没有正在运行的流程,则可以用普通删除。

如果该流程定义下存在已经运行的流程,使用普通删除报错,可用级联删除方法将流程及相关记录全部删除:先删除没有完成流程节点,最后就可以完全删除流程定义信息。项目开发中级联删除操作一般只开放给超级管理员使用。

 

8、流程资源下载

现在我们的流程资源文件已经上传到数据库了,如果其他用户想要查看这些资源文件,可以从数据库中把资源文件下载到本地。

解决方案有:

1、jdbc对blob类型,clob类型数据读取出来,保存到文件目录

2、使用activiti的api来实现

使用commons-io.jar 解决IO的操作

引入commons-io依赖包

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

测试代码(需要在D盘创建 test 目录。或者指定别的目录):

/**
     * 下载 Bpmn 文件
     * @throws Exception
     */
    @Test
    public void downloadBpmnFile() throws Exception{
        //1、创建 ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        // 通过流程引擎获取repositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //3、得到查询器:ProcessDefinitionQuery,设置查询条件,得到想要的流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey("myApply")//根据流程定义的ke查询
                .latestVersion()//只获取最新的一条数据
                .singleResult();
        //4、通过流程定义信息,得到部署ID
        String deploymentId = processDefinition.getDeploymentId();
        //5、通过repositoryService的方法,实现读取图片信息和bpmn信息
        //png图片流,读到内存
        InputStream pngInputStream = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());
        //bpmn 文件流,读到内存
        InputStream bpmnInputStream = repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName());
        //6、构造OutputStream流
        File file_png = new File("D:/test/myApply.png");
        File file_bpmn = new File("D:/test/myApply.bpmn");
        FileOutputStream bpmnOut = new FileOutputStream(file_bpmn);
        FileOutputStream pngOut = new FileOutputStream(file_png);
        //7、输入流,输出流的转换
        IOUtils.copy(pngInputStream,pngOut);
        IOUtils.copy(bpmnInputStream,bpmnOut);
        //8、关闭流
        pngOut.close();
        bpmnOut.close();
        pngInputStream.close();
        bpmnInputStream.close();
    }

说明:

1)       deploymentId为流程部署ID

2)       resource_name为act_ge_bytearray表中NAME_列的值

3)       使用repositoryService的getDeploymentResourceNames方法可以获取指定部署下的所有文件的名称

4)       使用repositoryService的getResourceAsStream方法传入部署ID和资源图片名称可以获取部署下指定名称文件的输入流

最后的将输入流中的图片资源进行输出。

 

9、流程历史信息的查看

即使流程定义已经删除了,流程执行的历史信息通过前面的分析,依然保存在activiti的act_hi_*相关的表中。所以我们还是可以查询流程执行的历史信息,可以通过HistoryService来查看相关的历史记录。

代码:

/**
     * 查询历史信息
     */
    @Test
    public void findHistoryInfo(){
        //1、创建 ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //获取 HistoryService
        HistoryService historyService = processEngine.getHistoryService();
        //获取 actinst 表的查询对象
        HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
        //查询 actinst表,条件:根据 InstanceId 查询
        // instanceQuery.processInstanceId("2501");
        //查询 actinst表,条件:根据 DefinitionId 查询
        instanceQuery.processDefinitionId("myApply:3:5004");
        //增加排序操作,orderByHistoricActivityInstanceStartTime 根据开始时间排序 asc 升序
        instanceQuery.orderByHistoricActivityInstanceStartTime().asc();
        //查询所有内容
        List<HistoricActivityInstance> activityInstanceList = instanceQuery.list();
        //输出数据
        for (HistoricActivityInstance hi : activityInstanceList) {
            System.out.println(hi.getActivityId());
            System.out.println(hi.getActivityName());
            System.out.println(hi.getProcessDefinitionId());
            System.out.println(hi.getProcessInstanceId());
            System.out.println("--------------------------------------");
        }

    }

结果:

_2
请假申请流程
myApply:3:5004
7501
--------------------------------------
_3
创建请假申请
myApply:3:5004
7501
--------------------------------------
_4
主管审批
myApply:3:5004
7501
--------------------------------------

注意,这个 processDefinitionId 就是如图所示的:

act_hi_actinst 表的 PROC_DEF_ID_ 字段。

activiti 流程 java生成 activiti自定义流程_Activiti_09