The senior architecture of Dachang will take you from activiti entry to soil, API combined with examples to explain

I was thinking about having nothing to do. I just used the workflow Activiti framework in the project to write a blog. However, things are always mixed and delayed until now. This section originally wanted to write about Activiti. APIHowever, too much thinking about this blog, and it seems too stiff, difficult to understand, so that APIin the actual demo to explain.

1. Establish a flowchart

Before starting the workflow, we should first reflect the specific business in the deployment flowchart of the workflow, and pass the test, which is equivalent to half of the success, and the subsequent development of the specific business is relatively easy.

First, let's take a look at what controls are in the idea, and the commonly used controls are marked.

Let's talk about the specific process of establishing a flowchart.

First, we need to pull into a starting node to the bpmnfile, which is a graphical interface, you can just pull.

Then, we pulled into a control from UserTaskthe user node to the task bpmnfile.

In this way, there are two approval nodes. If some other business requirements are needed, we can also add some gateways , which will not be added here.

Finally, we need only one end node EndEventto complete the deployment diagram drawing workflow.

Let's finally look at the complete example.

It seems that the drawing of the entire flow chart has been completed, but the catch is that we currently have not set up who will approve the tutor approval and the tutor approval, so we still need to take a look at how to set up the approvers .

First, we need to select an approval node , for example, select the supervisor approval node.

Secondly, we can see an obvious idea, called the left side of the editor of the BPMN editorproperties box, which includes an all attributes of the user task node can be set .

Note: Candidate users, candidate groups, task listeners, these three attributes are not mentioned here for the time being, and will be added later.

Since this step we need to set the approver, so we need to Assigneeset our approver this property.

As shown in the above figure, here is the approver for the tutor approving this node sihai. In addition to setting the approver directly, there are two ways to set it, which will be added later.

Another approval node can also be set in this way to complete the setting of the approver.

Very good, this basically completes the creation of a flowchart. Next, we will explain the Activiti API in detail through examples .

2. Examples to explain API

In the creation of the above flowchart, we have not generated a png image, so if you don't know how to generate it, you can refer to this previous article: Activiti workflow from entry to soil: integrating spring .

Since I'm explaining the API, let's take a look at the main APIs first, so that we can have an overall grasp.

How to use these APIs in detail, let's come one by one.

2.1 Process definition

Since it is a process definition, it must be how to deploy the process definition.

2.1.1 Deployment process definition method 1

 

 @Autowired
    private ProcessEngine processEngine;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private HistoryService historyService;

    /**
     * 部署流程定义(从classpath)
     */
    @Test
    public void deploymentProcessDefinition_classpath(){
        Deployment deployment = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service
                .createDeployment()//创建一个部署对象
                .name("流程定义")//添加部署的名称
                .addClasspathResource("bpmn/hello.bpmn")//从classpath的资源中加载,一次只能加载一个文件
                .addClasspathResource("bpmn/hello.png")//从classpath的资源中加载,一次只能加载一个文件
                .deploy();//完成部署
        System.out.println("部署ID:"+deployment.getId());
        System.out.println("部署名称:"+deployment.getName());
    }

Note: The junit test environment after integrating spring is used here. For how to integrate spring, please see this article: Activiti workflow from entry to soil: integrating spring .

Output result:

In this way, we deployed this process. So how does it work? Let's take a look at the whole process.

  • Get process engine object: This is integrated with spring.

  • Obtained a RepositoryService object (warehouse object) through the process engine

  • A deployment object configuration object is generated by the service object of the warehouse to encapsulate the relevant configuration of the deployment operation.

  • This is a chain programming. Set the display name in the deployment configuration object and upload the process definition rule file

  • Store the rule information of the process definition in the database table.

In fact, in this step, three tables in the Activiti database are used, which are: act_re_deployment (deployment object table), act_re_procdef (process definition table), act_ge_bytearray (resource file table).

We look at the changes in these three tables:
1) act_re_deployment

 

As you can see, the deployment ID and deployment name are stored in this table.

2)act_re_procdef

In this table, information such as the id of the deployed Deployment_ID deployment process, the name of the bpmn resource file, and the name of the png image are stored.

3)act_ge_bytearray

Storage process definition related deployment information. That is, the storage location of the process definition document. Two records will be added for each deployment, one for the bpmn rule file and one for the picture (if only one file is specified for bpmn during deployment, activiti will parse the content of the bpmn file during deployment and automatically generate a flowchart). The two files are not very large and are stored in the database in binary form.

2.1.2 Deployment process definition method 2

 

 /**
     * 部署流程定义(从zip)
     */
    @Test
    public void deploymentProcessDefinition_zip(){
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("bpmn/hello.zip");
        ZipInputStream zipInputStream = new ZipInputStream(in);
        Deployment deployment = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service
                .createDeployment()//创建一个部署对象
                .name("流程定义")//添加部署的名称
                .addZipInputStream(zipInputStream)//指定zip格式的文件完成部署
                .deploy();//完成部署
        System.out.println("部署ID:"+deployment.getId());//
        System.out.println("部署名称:"+deployment.getName());//
    }

The project structure is as follows:

 

Output result:

 

In this way, there is no problem. The only difference is that it is compressed into a zip file. The input stream of zip is used as the deployment process definition, and other uses are no different.

After deploying the process definition, we should want to check some information about the process definition.

2.1.3 View process definition

 

/**
     * 查询流程定义
     */
    @Test
    public void findProcessDefinition(){
        List<ProcessDefinition> list = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service
                .createProcessDefinitionQuery()//创建一个流程定义的查询
                /**指定查询条件,where条件*/
//                      .deploymentId(deploymentId)//使用部署对象ID查询
//                      .processDefinitionId(processDefinitionId)//使用流程定义ID查询
//                      .processDefinitionKey(processDefinitionKey)//使用流程定义的key查询
//                      .processDefinitionNameLike(processDefinitionNameLike)//使用流程定义的名称模糊查询

                /**排序*/
                .orderByProcessDefinitionVersion().asc()//按照版本的升序排列
//                      .orderByProcessDefinitionName().desc()//按照流程定义的名称降序排列

                /**返回的结果集*/
                .list();//返回一个集合列表,封装流程定义
//                      .singleResult();//返回惟一结果集
//                      .count();//返回结果集数量
//                      .listPage(firstResult, maxResults);//分页查询
        if(list!=null && list.size()>0){
            for(ProcessDefinition pd:list){
                System.out.println("流程定义ID:"+pd.getId());//流程定义的key+版本+随机生成数
                System.out.println("流程定义的名称:"+pd.getName());//对应hello.bpmn文件中的name属性值
                System.out.println("流程定义的key:"+pd.getKey());//对应hello.bpmn文件中的id属性值
                System.out.println("流程定义的版本:"+pd.getVersion());//当流程定义的key值相同的相同下,版本升级,默认1
                System.out.println("资源名称bpmn文件:"+pd.getResourceName());
                System.out.println("资源名称png文件:"+pd.getDiagramResourceName());
                System.out.println("部署对象ID:"+pd.getDeploymentId());
                System.out.println("*********************************************");
            }
        }
    }

Output result:

 

Summary of query process definition:

  • The process definition and the service related to the deployment object are both RepositoryService, and you will find both about the process definition later RepositoryService.

  • With this createProcessDefinitionQuery()setting method a number of query parameters, conditions such as by descending ascending like.

2.1.4 Delete process definition

By deleting the information with the deployment ID 2501.

 

/**
     * 删除流程定义
     */
    @Test
    public void deleteProcessDefinition(){
        //使用部署ID,完成删除,指定部署对象id为2501删除
        String deploymentId = "2501";
        /**
         * 不带级联的删除
         *    只能删除没有启动的流程,如果流程启动,就会抛出异常
         */
//      processEngine.getRepositoryService()//
//                      .deleteDeployment(deploymentId);

        /**
         * 级联删除
         *    不管流程是否启动,都能可以删除
         */
        processEngine.getRepositoryService()//
                .deleteDeployment(deploymentId, true);
        System.out.println("删除成功!");
    }

Output result:

 

To view the database and found that act_re_deploymentdata does not exist anymore.

  • Here or through getRepositoryService()acquisition method to deploy custom object, and then delete the specified ID information.

2.1.5 Get the resources of the process definition document

The function here is mainly to query pictures, which can be used for process display later. Let's see how to check it out.

 

/**
     * 查看流程图
     *
     * @throws IOException
     */
    @Test
    public void viewPic() throws IOException {
        /**将生成图片放到文件夹下*/
        String deploymentId = "5001";
        //获取图片资源名称
        List<String> list = processEngine.getRepositoryService()//
                .getDeploymentResourceNames(deploymentId);
        //定义图片资源的名称
        String resourceName = "";
        if (list != null && list.size() > 0) {
            for (String name : list) {
                if (name.indexOf(".png") >= 0) {
                    resourceName = name;
                }
            }
        }

        //获取图片的输入流
        InputStream in = processEngine.getRepositoryService()//
                .getResourceAsStream(deploymentId, resourceName);

        //将图片生成到F盘的目录下
        File file = new File("F:/" + resourceName);

        //将输入流的图片写到磁盘
        FileUtils.copyInputStreamToFile(in, file);
    }

You can find pictures under the F drive.

 

2.1.6 Query the latest version of the process definition

 

 /**
     * 查询最新版本的流程定义
     */
    @Test
    public void findLastVersionProcessDefinition() {
        List<ProcessDefinition> list = processEngine.getRepositoryService()//
                .createProcessDefinitionQuery()//
                .orderByProcessDefinitionVersion().asc()//使用流程定义的版本升序排列
                .list();
        /**
         map集合的特点:当map集合key值相同的情况下,后一次的值将替换前一次的值
         */
        Map<String, ProcessDefinition> map = new LinkedHashMap<String, ProcessDefinition>();
        if (list != null && list.size() > 0) {
            for (ProcessDefinition pd : list) {
                map.put(pd.getKey(), pd);
            }
        }
        List<ProcessDefinition> pdList = new ArrayList<ProcessDefinition>(map.values());
        if (pdList != null && pdList.size() > 0) {
            for (ProcessDefinition pd : pdList) {
                System.out.println("流程定义ID:" + pd.getId());//流程定义的key+版本+随机生成数
                System.out.println("流程定义的名称:" + pd.getName());//对应hello.bpmn文件中的name属性值
                System.out.println("流程定义的key:" + pd.getKey());//对应hello.bpmn文件中的id属性值
                System.out.println("流程定义的版本:" + pd.getVersion());//当流程定义的key值相同的相同下,版本升级,默认1
                System.out.println("资源名称bpmn文件:" + pd.getResourceName());
                System.out.println("资源名称png文件:" + pd.getDiagramResourceName());
                System.out.println("部署对象ID:" + pd.getDeploymentId());
                System.out.println("*********************************************************************************");
            }
        }
    }

Output result:

 

2.1.7 Summary of process definition

1. The deployment process definition uses the following tables of Activiti.

  • act_re_deployment: deployment object table
  • act_re_procdef: Process definition table
  • act_ge_bytearray: resource file table
  • act_ge_property: Primary key generation strategy table

2, we find operational deployment process definitions are in RepositoryServiceoperation under this class, we only need to pass getRepositoryService()all operations to get the object, you can define the deployment process by the chain rule.

2.2 Use of complete workflow examples

In this section, we use a complete example to summarize some of the basic knowledge mentioned earlier, so that we can better learn the previous and subsequent knowledge points, which is also a transitional chapter.

Going back to the establishment flow chart of the first section , we have already established the basic bpmn diagram, but we need to make a complete example, we still need to add some content, so that we can complete such an example. Let's take the bpmn picture from the first section first.

First of all, we need to be clear: this diagram so far, we have only simply drawn the process, for example, when we need to review, it needs to be reviewed by a specific person, so we need Set up specific personnel for review at each node.

Note: The reviewer who sets the node will discuss it in detail later. This is just a simple example, so you only need to understand it here and do it well.

Set up reviewer steps

First, we need to select a node, for example, the "mentor approval" node in the figure below.

 

Next, in the toolbar on the left, we will see a lot of options, one is Assignee, we need to set the approver that our node needs to set in this option.

Assignee setting format: You can use English or Chinese directly, for example sihai,, more complicated settings will be discussed later.

The node settings below are exactly the same as above.

The examiner approved by the counselor is Ouyang Sihai.

 

Perfect, so that the task of the flowchart is completed, and then we can proceed to the test phase of this example.

1) Deployment process definition The
deployment process definition, as mentioned in the previous chapter, has two ways to process, one is to load the bpmn file and the png file, and the other is to compress these two files into a zip format. File, then load. Here we use the first method for processing.

 

/**
     * 部署流程定义(从classpath)
     */
    @Test
    public void deploymentProcessDefinition_classpath() {
        Deployment deployment = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service
                .createDeployment()//创建一个部署对象
                .name("hello")//添加部署的名称
                .addClasspathResource("bpmn/hello.bpmn")//从classpath的资源中加载,一次只能加载一个文件
                .addClasspathResource("bpmn/hello.png")//从classpath的资源中加载,一次只能加载一个文件
                .deploy();//完成部署
        log.info("部署ID:" + deployment.getId());
        log.info("部署名称:" + deployment.getName());
    }

Now that the process definition is available, we need to start this process instance.

Regarding what has been done in this step, you can view it in the previous chapter.

2) Start the process instance

 

 /**
     * 启动流程实例
     */
    @Test
    public void startProcessInstance(){
        //1、流程定义的key,通过这个key来启动流程实例
        String processDefinitionKey = "hello";
        //2、与正在执行的流程实例和执行对象相关的Service
        // startProcessInstanceByKey方法还可以设置其他的参数,比如流程变量。
        ProcessInstance pi = processEngine.getRuntimeService()
                .startProcessInstanceByKey(processDefinitionKey);//使用流程定义的key启动流程实例,key对应helloworld.bpmn文件中id的属性值,使用key值启动,默认是按照最新版本的流程定义启动
        log.info("流程实例ID:"+pi.getId());//流程实例ID
        log.info("流程定义ID:"+pi.getProcessDefinitionId());//流程定义ID
    }

 


Note: processDefinitionKey is the name of the bpmn file.

 

Step
1 Obtain the runtimeService instance.
2 Start the process instance through the name of the bpmn file, which is processDefinitionKey.
3 After starting the process, the task of the process goes to the supervisor approval node.

The following is the query of personal tasks, we can query the tasks of the supervisor approval node.

3) Query personal tasks

 

/**
     * 查询当前人的个人任务
     */
    @Test
    public void findPersonalTask(){
        String assignee = "sihai";
        List<Task> list = processEngine.getTaskService()//与正在执行的任务管理相关的Service
                .createTaskQuery()//创建任务查询对象
                /**查询条件(where部分)*/
                .taskAssignee(assignee)//指定个人任务查询,指定办理人
//                      .taskCandidateUser(candidateUser)//组任务的办理人查询
//                      .processDefinitionId(processDefinitionId)//使用流程定义ID查询
//                      .processInstanceId(processInstanceId)//使用流程实例ID查询
//                      .executionId(executionId)//使用执行对象ID查询
                /**排序*/
                .orderByTaskCreateTime().asc()//使用创建时间的升序排列
                /**返回结果集*/
//                      .singleResult()//返回惟一结果集
//                      .count()//返回结果集的数量
//                      .listPage(firstResult, maxResults);//分页查询
                .list();//返回列表
        if(list!=null && list.size()>0){
            for(Task task:list){
                log.info("任务ID:"+task.getId());
                log.info("任务名称:"+task.getName());
                log.info("任务的创建时间:"+task.getCreateTime());
                log.info("任务的办理人:"+task.getAssignee());
                log.info("流程实例ID:"+task.getProcessInstanceId());
                log.info("执行对象ID:"+task.getExecutionId());
                log.info("流程定义ID:"+task.getProcessDefinitionId());
                log.info("********************************************");
            }
        }
    }

By sihaithis reviewer, the query to the following information.

 

Analysis step
1 First, obtain the TaskService object through the getTaskService method.
2 Create a query object through the createTaskQuery method.
3 Set the reviewer through the taskAssignee method.
4 For the return of the result, we can set other information such as sorting through orderByTaskCreateTime().asc().

One thing to note here is that an important piece of information queried is: task id (taskId). Next, we need to use this task id to complete the task.

4) Handle personal tasks

 

/**
     * 完成我的任务
     */
    @Test
    public void completePersonalTask() {
        //任务ID,上一步查询得到的。
        String taskId = "7504";
        processEngine.getTaskService()//与正在执行的任务管理相关的Service
                .complete(taskId);
        log.info("完成任务:任务ID:" + taskId);
    }

Complete the task with the task id of the previous step: 7504.

Step
1 First, get the TaskService object through the getTaskService method.
2 Call the complete method and complete the task given a specific task id.

5) Query process status (judge which node
the process goes to ) this interface is still very needed. When we are in a specific business, we need to determine what the state of our process is, or which one our process has reached When it comes to nodes, this interface allows us to save a lot of things in our business.

 

/**
     * 查询流程状态(判断流程走到哪一个节点)
     */
    @Test
    public void isProcessActive() {
        String processInstanceId = "7501";
        ProcessInstance pi = processEngine.getRuntimeService()//表示正在执行的流程实例和执行对象
                .createProcessInstanceQuery()//创建流程实例查询
                .processInstanceId(processInstanceId)//使用流程实例ID查询
                .singleResult();
        if (pi == null) {
            log.info("流程已经结束");
        } else {
            log.info("流程没有结束");
            //获取任务状态
            log.info("节点id:" + pi.getActivityId());
        }
    }

Steps:
1 Get the ProcessInstance object of the process instance.
2 Obtain the instance Id (node ​​id) through the getActivityId method.

So 节点 Idwhat's the effect of getting it?
In fact, after having this Id, we can judge where the process is. For example, the node id of the output above is _4, and this _4 is the corresponding 辅导员审批节点的 id, so we can judge that the process has actually reached this node, and it will play a role when the process status is displayed on the page later.

6) Query the historical information of the process execution
By checking the official API interface of activiti 5, it is found that the following query interface is available for viewing historical information.

Below we use the above examples to test the following methods one by one.

Historical activity instance query interface

 

/**
     * 历史活动查询接口
     */
    @Test
    public void findHistoryActivity() {
        String processInstanceId = "7501";
        List<HistoricActivityInstance> hais = processEngine.getHistoryService()//
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .list();
        for (HistoricActivityInstance hai : hais) {
            log.info("活动id:" + hai.getActivityId()
                    + "   审批人:" + hai.getAssignee()
                    + "   任务id:" + hai.getTaskId());
            log.info("************************************");
        }
    }

Through this interface, not only this information can be found, but there are other methods to obtain more other information about historical activities .

Historical process instance query interface

 

/**
     * 查询历史流程实例
     */
    @Test
    public void findHistoryProcessInstance() {
        String processInstanceId = "7501";
        HistoricProcessInstance hpi = processEngine.getHistoryService()// 与历史数据(历史表)相关的Service
                .createHistoricProcessInstanceQuery()// 创建历史流程实例查询
                .processInstanceId(processInstanceId)// 使用流程实例ID查询
                .orderByProcessInstanceStartTime().asc().singleResult();
        log.info(hpi.getId() + "    " + hpi.getProcessDefinitionId() + "    " + hpi.getStartTime() + "    "
                + hpi.getEndTime() + "     " + hpi.getDurationInMillis());
    }

This interface can query all information about historical process instances .

Historical task instance query interface

 

 /**
     * 查询历史任务
     */
    @Test
    public void findHistoryTask() {
        String processInstanceId = "7501";
        List<HistoricTaskInstance> list = processEngine.getHistoryService()// 与历史数据(历史表)相关的Service
                .createHistoricTaskInstanceQuery()// 创建历史任务实例查询
                .processInstanceId(processInstanceId)//
                .orderByHistoricTaskInstanceStartTime().asc().list();
        if (list != null && list.size() > 0) {
            for (HistoricTaskInstance hti : list) {
                log.info("\n 任务Id:" + hti.getId() + "    任务名称:" + hti.getName() + "    流程实例Id:" + hti.getProcessInstanceId() + "\n 开始时间:"
                        + hti.getStartTime() + "   结束时间:" + hti.getEndTime() + "   持续时间:" + hti.getDurationInMillis());
            }
        }
    }

This query interface can query historical task information .

Historical process variable query interface

 

/**
     * 查询历史流程变量
     */
    @Test
    public void findHistoryProcessVariables() {
        String processInstanceId = "7501";
        List<HistoricVariableInstance> list = processEngine.getHistoryService()//
                .createHistoricVariableInstanceQuery()// 创建一个历史的流程变量查询对象
                .processInstanceId(processInstanceId)//
                .list();
        if (list != null && list.size() > 0) {
            for (HistoricVariableInstance hvi : list) {
                log.info("\n" + hvi.getId() + "   " + hvi.getProcessInstanceId() + "\n" + hvi.getVariableName()
                        + "   " + hvi.getVariableTypeName() + "    " + hvi.getValue());
            }
        }
    }

In this example, no process variables are set, so no historical information can be queried here.

This interface is mainly about some information about the setting of historical process variables .

Historical local interface query interface

 

/**
     * 通过执行sql来查询历史数据,由于activiti底层就是数据库表。
     */
    @Test
    public void findHistoryByNative() {
        HistoricProcessInstance hpi = processEngine.getHistoryService()
                .createNativeHistoricProcessInstanceQuery()
                .sql("查询底层数据库表的sql语句")
                .singleResult();
        log.info("\n" + hpi.getId() + "    " + hpi.getProcessDefinitionId() + "    " + hpi.getStartTime()
                + "\n" + hpi.getEndTime() + "     " + hpi.getDurationInMillis());
    }

This interface is provided through a direct sql 语句query of historical information, we only need to sql()write the sql statement native method can perform data queries.

At this point, I think we should introduce the API of Activiti workflow through such a complete example, and we will say goodbye to this section. Let's look back at the API interface at the beginning of the article. This is also a summary of this section.

 

 

At last:

Through the above study, I found that there are quite a lot of interviewees this year. For many people who are interviewing recently, I have also compiled quite a lot of interview special materials (spring, mybatis, jvm, Zookeeper, distributed, etc.). You can click below Link) and the real questions of the 2020 latest Ali interview.

The answers to the above interview questions are organized into document notes. I also sorted out some interview materials & the latest interview questions collected by some big companies in 2020 (all organized into documents, a small part of the screenshots), if necessary, you can  click to enter to view the materials .

I hope it will be helpful to everyone. If it is useful, please give me support!

Guess you like

Origin blog.csdn.net/SQY0809/article/details/108827620