I. Deployment of Activiti process

Once the process is defined, the first step is to deploy the process, mainly by reading BPMN resource files.

Upload process definition:

To view the flowchart details:

@ Override public void uploadStreamAndDeployment (MultipartFile file) throws IOException {/ / access to upload the file name String fileName = file.getOriginalFilename(); FileInputStream = file.getInputStream(); file.getInputStream(); / / to get extension String extension = FilenameUtils. GetExtension (fileName); If (extension. Equals ("zip")) {ZipInputStream = new ZipInputStream(fileInputStream); repositoryService.createDeployment() .addZipInputStream(zipInputStream) .deploy(); }else{ repositoryService.createDeployment() .addInputStream(fileName, fileInputStream) .deploy(); }}

Process deployment involves major tables:

  • ACT_GE_BYTEARRAY binary data table
  • ACT_RE_DEPLOYMENT deployment information table
  • The act_re_procdef process defines the data table

Act_ge_bytearray table:

The field DEPLOYMENT_ID_ relates to the ID_ field of the ACT_RE_Deployment table.

Act_re_deployment table:

Act_re_procdef table:

Two, start the process instance

1. Start the instance

New leave:

Public int insertWorkflowLeave(workflowLeave) public int insertWorkflowLeave(workflowLeave workflowLeave) { String id = UUID.randomUUID().toString(); workflowLeave.setId(id); workflowLeave.setCreateTime(DateUtils.getNowDate()); String join = StringUtils.join(sysUserService.selectUserNameByPostCodeAndDeptId("se", SecurityUtils.getLoginUser().getUser().getDeptId()), ","); ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder .start() .withProcessDefinitionKey("leave")  .withName(workflowLeave.getTitle()) .withBusinessKey(id) .withVariable("deptLeader",join) .build()); workflowLeave.setInstanceId(processInstance.getId()); workflowLeave.setState("0"); workflowLeave.setCreateName(SecurityUtils.getNickName()); workflowLeave.setCreateBy(SecurityUtils.getUsername()); workflowLeave.setCreateTime(DateUtils.getNowDate()); return workflowLeaveMapper.insertWorkflowLeave(workflowLeave); }

workflow_leaveTable of business:

Startup instance ID:

InstanceId = 1dfab580-4218-11eb-ba85-005056c00001

Table involved:

  • act_ru_execution: Represents the executing process instance table. If the current executing process instance ends, the row in this table will be dropped, so this table is also a temporary table
  • act_ru_task: Represents the current task table. This table is a temporary table. If the current task is completed, the task will be deleted from this table
  • act_hi_actinst: Each element that appears on the flowchart is called an activity, and elements that are executing on the flowchart or that have completed execution are called activity instances
  • act_hi_procinst: Process history table
  • ACT_HI_TASKINST history task table

Act_ru_execution table:

Execute to the first node:

The first record binds the specific business BUSINESS_KEY_ for the instance, and the specific business name. The second record is an instance bound to the [Department Head Approval] node, and bound to the node ACT_ID_= DeptLeaderver


Act_ru_task table:

Act_hi_actinst table:


*** act_hi_actinst * 1, Description * act:activiti * hi:history * actinst:activity instance * Each element that appears on the flowchart is called an activity * The elements on the flowchart that are executing or that have completed executing are called activity instance * 2, field * proc_def_id:pdid * proc_inst_id: process instance ID * execution_id_: execution ID * act_id_:activity * act_name * act_type**

Act_hi_procinst table:


*** * End_time_ = NULL ** End_time_ = NULL ** End_time_ = NULL ** End_time_ = NULL ** End_time_ = NULL ** End_time_ = NULL ** End_time_ = NULL ** End_time_ = NULL ** End_time_ = NULL Indicates that the process instance has ended **

Act_ru_identitylink table:

Act_workflow_formdata table:

Flow chart file:

<?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: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" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.kafeitu.me/activiti/leave">
  <process id="leave" name="请假流程-普通表单" isExecutable="true">
    <startEvent id="startevent1" name="Start" />
    <userTask id="deptLeaderVerify" name="部门领导审批" activiti:formKey="deptLeaderVerify" activiti:candidateUsers="${deptLeader}">
        <activiti:formProperty id="FormProperty_3qipis2--__!!radio--__!!审批意见--__!!i--__!!同意--__--不同意" type="string" />
        <activiti:formProperty id="FormProperty_0lffpcm--__!!textarea--__!!批注--__!!f__!!null" type="string" />
    <exclusiveGateway id="exclusivegateway5">
    <userTask id="hrVerify" name="人事审批" activiti:formKey="hrVerify" activiti:candidateGroups="hr">
        <activiti:formProperty id="FormProperty_23u95jb--__!!radio--__!!审批意见--__!!i--__!!同意--__--不同意" type="string" />
        <activiti:formProperty id="FormProperty_3t7tfkv--__!!textarea--__!!批注--__!!f--__!!null" type="string" />
    <exclusiveGateway id="exclusivegateway6">
    <endEvent id="endevent1" name="End">
    <sequenceFlow id="flow2" sourceRef="startevent1" targetRef="deptLeaderVerify" />
    <sequenceFlow id="flow3" sourceRef="deptLeaderVerify" targetRef="exclusivegateway5" />
    <sequenceFlow id="flow5" name="同意" sourceRef="exclusivegateway5" targetRef="hrVerify">
      <conditionExpression xsi:type="tFormalExpression">${FormProperty_3qipis2==0}</conditionExpression>
    <sequenceFlow id="flow6" sourceRef="hrVerify" targetRef="exclusivegateway6" />
    <sequenceFlow id="Flow_0p85954" sourceRef="exclusivegateway6" targetRef="endevent1">
        <activiti:executionListener class="com.ruoyi.leave.instener.LeaveEndStateListener" event="take">
          <activiti:field name="state">
      <conditionExpression xsi:type="tFormalExpression">${FormProperty_23u95jb==0}</conditionExpression>
    <sequenceFlow id="Flow_0ji7qcv" sourceRef="exclusivegateway6" targetRef="endevent1">
        <activiti:executionListener class="com.ruoyi.leave.instener.LeaveEndStateListener" event="take">
          <activiti:field name="state">
    <sequenceFlow id="Flow_0q3bbjl" sourceRef="exclusivegateway5" targetRef="endevent1">
        <activiti:executionListener class="com.ruoyi.leave.instener.LeaveEndStateListener" event="take">
          <activiti:field name="state">
  <bpmndi:BPMNDiagram id="BPMNDiagram_leave">
    <bpmndi:BPMNPlane id="BPMNPlane_leave" bpmnElement="leave">
      <bpmndi:BPMNEdge id="Flow_0q3bbjl_di" bpmnElement="Flow_0q3bbjl">
        <omgdi:waypoint x="260" y="83" />
        <omgdi:waypoint x="260" y="140" />
        <omgdi:waypoint x="582" y="140" />
      <bpmndi:BPMNEdge id="Flow_0ji7qcv_di" bpmnElement="Flow_0ji7qcv">
        <omgdi:waypoint x="505" y="83" />
        <omgdi:waypoint x="505" y="140" />
        <omgdi:waypoint x="582" y="140" />
      <bpmndi:BPMNEdge id="Flow_0p85954_di" bpmnElement="Flow_0p85954">
        <omgdi:waypoint x="525" y="63" />
        <omgdi:waypoint x="600" y="63" />
        <omgdi:waypoint x="600" y="122" />
      <bpmndi:BPMNEdge id="BPMNEdge_flow6" bpmnElement="flow6">
        <omgdi:waypoint x="453" y="63" />
        <omgdi:waypoint x="485" y="63" />
      <bpmndi:BPMNEdge id="BPMNEdge_flow5" bpmnElement="flow5">
        <omgdi:waypoint x="280" y="63" />
        <omgdi:waypoint x="348" y="63" />
          <omgdc:Bounds x="300" y="46" width="22" height="11" />
      <bpmndi:BPMNEdge id="BPMNEdge_flow3" bpmnElement="flow3">
        <omgdi:waypoint x="185" y="63" />
        <omgdi:waypoint x="240" y="63" />
      <bpmndi:BPMNEdge id="BPMNEdge_flow2" bpmnElement="flow2">
        <omgdi:waypoint x="35" y="63" />
        <omgdi:waypoint x="80" y="63" />
      <bpmndi:BPMNShape id="BPMNShape_startevent1" bpmnElement="startevent1">
        <omgdc:Bounds x="0" y="46" width="35" height="35" />
          <omgdc:Bounds x="5" y="81" width="25" height="14" />
      <bpmndi:BPMNShape id="BPMNShape_deptLeaderVerify" bpmnElement="deptLeaderVerify">
        <omgdc:Bounds x="80" y="36" width="105" height="55" />
      <bpmndi:BPMNShape id="BPMNShape_exclusivegateway5" bpmnElement="exclusivegateway5" isMarkerVisible="true">
        <omgdc:Bounds x="240" y="43" width="40" height="40" />
      <bpmndi:BPMNShape id="BPMNShape_hrVerify" bpmnElement="hrVerify">
        <omgdc:Bounds x="348" y="36" width="105" height="55" />
      <bpmndi:BPMNShape id="BPMNShape_exclusivegateway6" bpmnElement="exclusivegateway6" isMarkerVisible="true">
        <omgdc:Bounds x="485" y="43" width="40" height="40" />
      <bpmndi:BPMNShape id="BPMNShape_endevent1" bpmnElement="endevent1">
        <omgdc:Bounds x="582" y="122" width="35" height="35" />
          <omgdc:Bounds x="590" y="157" width="20" height="14" />

It is very important to analyze the Task in detail.

The concept of a task: a task that needs someone to approve or apply for it

Type of situation for the person performing the task:

Case 1: Before entering the node, we can determine the instance of the executor of the task: Such as “leave application” process, the start is to submit “leave application”, you will need to know who submit “leave”, obviously, in a system, who log in to the system, who have submitted “off duty” to submit, then executor can determine is the login. Case two: It is possible that the executor of a task node is fixed.

Example: For example, in the process of “corporate financial reimbursement”, the last person to approve must be the biggest BOSS of the financial department. Therefore, the executive at the last node of the process has been determined to be the “biggest BOSS of the financial department”.

Situation 3: A node task has no executor (unknown) before, and the executor can only be determined when the person with the identity logs in the system and enters the system.

Instance: for example, if the current process instance is performing “approval” letter of recommendation, this time, no task executor of examination and approval letter of recommendation, because the approver can be many, who are not sure, only when the consultant after login system can give the task assignment to execution, which exist as long as it is consultant landing, you can see all of the “cover letter”.

Case 4: A task node has n people who can perform the task, but as long as one person completes the task, the task is completed: group task

Example: For example, the process of “access to the subway station”. In general, there are N entrances for security inspection in the subway, and there are many individuals conducting inspection. If we want to pass the inspection, then any inspector just needs to pass.

Detailed analysis: Situation 1: Steps:

(1) Build the flowchart first :(Note the difference between the first drawing above)

(2) will BPMN content, generate a PNG picture (this operation method, above have been very detailed, not much to say)

(3) Code implementation steps:


/** * @test public void startDeployTest(){processEngine processEngine = processEngine ProcessEngines.getDefaultProcessEngine(); ProcessEngine. GetRepositoryService (.) createDeployment (). The name (" leave process: A "). AddClasspathResource (" com/hnu/SCW/task/shenqing BPMN "). The deploy (); }

Database information:


@test public void testStartPi (){/** * Give <userTask id=" Request for leave "name=" Request for leave" name=" Request for leave "name=" Request for leave" name=" Request for leave" Activiti :assignee="#{student}"></ UserTask > * student assignment */ Map<String, Object> variables = new HashMap<String, Object> variables = new HashMap<String, Object>(); Variables. Put ("student", "Xiao Ming "); ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRuntimeService() .startProcessInstanceById("shenqing1:1:1304",variables); }

Database information:

Analysis: If we install the following code execution, then the following error appears

@test public void testStartPi (){processEngine processEngine = processEngine processEngine = processEngine processEngine = processEngine ProcessEngines.getDefaultProcessEngine(); processEngine.getRuntimeService() .startProcessInstanceById("shenqing1:1:1304"); }

When we process the node, we need to assign an executor. If we do not assign an executor, the above error will occur. If we do not assign an executor, the following error will occur. And then go back and think, is that our first case? Because, at the beginning of the implementation of the process of leave, in fact, the applicant is already can be determined, is the user of the login.

3: The following code is as follows:

/** * When completing the leave application, */ @test public void testFinishTask_Teacher(){Map<String, Object> variables = new HashMap<String, Object> variables = new HashMap<String, Object> variables = new HashMap<String, Object>(); Variables. Put ("teacher", "I'm Xiao Ming's head teacher"); ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getTaskService() .complete("1405", variables); // Set the process variable while completing the task} /** * In the case of completing the head teacher's approval, */ @test public void testFinishTask_Manager(){Map<String, Object> variables = new HashMap<String, Object>(); Variables. Put ("manager", "I am Xiao Ming's Dean of Academic Affairs "); ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getTaskService() .complete("1603", variables); @test public void testFinishTask(){processEngine processEngine = processEngine processEngine = processEngine processEngine = processEngine ProcessEngines.getDefaultProcessEngine(); processEngine.getTaskService() .complete("1703"); }

Summary: For case one, we must assign an executor before entering the node for execution.

Case 2: This case is not introduced here, because in the previous knowledge points, the executor was already assigned at the time of drawing the flow chart. You can go back and see.

Case 3:

Steps :(1) draw a flowchart, here is not much introduction, just say the need to modify the place.

(2) written TaskListener listening class

package com.hnu.scw.tasklistener; import org.activiti.engine.delegate.DelegateTask; import org.activiti.engine.delegate.TaskListener; /** * @Author Administrator * @Create 2018-01-16 11:10 * @Desc Tack Task Listening, Mainly to dynamically allocated executor * * / public class MyTaskListener implements TaskListener {@ Override public void notify (DelegateTask }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} (must be in the web environment) WebApplicationContext ac = WebApplicationContextUtils * .getWebApplicationContext(ServletActionContext.getServletContext()); xxxxService xxxxService = (xxxxService) ac.getBean("xxxxService"); Method 2: */ / Dynamic allocation (this is taken from the Map of Tack variables from the previous node, only the process is not finished, /*String value = (String) DelegateTask.getVariable ("aaa"); delegateTask.setAssignee(value); */ // Static DelegateTask. setAssignee(" I am the class teacher "); }}

In this way, when a “leave application” is submitted, the node of the “head teacher” will automatically assign the executor.

Situation 4:

The flow chart is as follows:

The specific test code :(Note the content of the comment written, you can understand the corresponding database table)

package com.hnu.scw.test; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.task.IdentityLink; import org.activiti.engine.task.Task; import org.junit.Test; import java.util.List; /** * @author SCW * @create 2018-01-23 15:45 * @desc public class groupTaskTest {/** * public class groupTaskTest { There are N people, but all it takes is one of them to pass * for the task to pass, so for such a business requirement, Will have the following content * / @ Test public void deployTashTest () {ProcessEngine ProcessEngine. = ProcessEngines getDefaultProcessEngine (); processEngine.getRepositoryService() .createDeployment() .addClasspathResource("com/hnu/scw/test/task3.bpmn") AddClasspathResource (" com/hnu/SCW/test/task3. PNG "). The name (" the test set of tasks "). The deploy (); } /** * After starting the process instance, enter the "Computer Repair" node, which is a group task * at this time, The group task candidates are inserted into both tables. * act_ru_identityLink contains the candidates for the currently executing group task */ @test public void processTaskStartTest(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRuntimeService() .startProcessInstanceByKey("task3"); } /** * For the ACT_HI_IDENTITYLINK table, by task ID, Namely query candidate * / @ TASK_ID field Test public void testQueryCandidateByTaskId () {ProcessEngine ProcessEngine = ProcessEngines.getDefaultProcessEngine(); List<IdentityLink> identityLinks = processEngine.getTaskService() .getIdentityLinksForTask("2104"); for (IdentityLink identityLink : identityLinks) { System.out.println(identityLink.getUserId()); }} /** * For the act_hi_identitylink table, / @test public void testQueryTaskByCandidate(){processEngine processEngine = processEngine ProcessEngines.getDefaultProcessEngine(); List < Task > tasks = processEngine. GetTaskService () createTaskQuery () taskCandidateUser (1 "engineer"). The List (); for (Task task : tasks) { System.out.println(task.getName()); }} /** * one of the candidates claim the task */ @Test public void TestClaimTask (){processEngine processEngine = processEngine ProcessEngines.getDefaultProcessEngine(); ProcessEngine. GetTaskService () / * * * the first parameter for taskId * the second parameter for claim * /. The claim (" 2104 ", "engineer 2"); }}

Four, additional knowledge points

1. Activiti affiliate business

Question: There are a lot of data tables in Activiti itself, and there are association relationships in it. So how to associate its own tables with tables in our actual business?

In fact, Activiti has already thought of this problem, is through the ACT_RU_EXECTUTION table business_key field to associate.

Example analysis: for example, for the above leave process, then, we must be in their own business, we need a leave of information table, for example, which contains, leave the reason, leave the person, leave the time and so on the basic leave information. Then, our other business, can also according to the contents of this form, to continuously extend, for example, on each leave also need to record information, each node of examination and approval for a complete description of each person in the information, so that there is a detailed table “leave approval”, obviously, the two tables is through the “ask for leave in the table primary key ID” to associated, So as a foreign key in the “Leave Details Form”… So, in the same way, we associate our business primary key with the business_key field of the act_ru_exectution table. So that connects our own business to Activiti.

2. The meaning of the built-in data table of Activiti workflow

ACT_RE_DEPLOYMENT (1) ACT_RE_DEPLOYMENT (2) ACT_RE_MODEL (3) ACT_RE_PROCDEF (2) Table 1) ACT_RU_IDENTITYLINK Runtime Process Personnel Table 2) ACT_RU_IDENTITYLINK Runtime Process Personnel Table 3) ACT_RU_TASK run-time task node table 4) ACT_RU_VARIABLE run-time process variable data table (3) : History database table 1) ACT_HI_ACTINST history node table 2) ACT_HI_ATTACHMENT history attachment table 3) ACT_HI_COMMENT history comment table 4) ACT_HI_IDENTITYLINK history process staff table 5) ACT_HI_DETAIL history table, which provides a query for historical variables 6) ACT_HI_PROCHINST history process instance 7) ACT_HI_TASKINST history task instance 8) ACT_HI_VARINST history variable table (4) : 2) ACT_ID_INFO User Extension Information Table 3) ACT_ID_MEMBERSHIP User and User Group Information Table 4) ACT_ID_USER User Information Table 4) ACT_ID_USER User Information Table 4) ACT_ID_USER User Information Table As for user authentication, it is suggested to develop a set by ourselves. The built-in functions of the component are too simple, and there are many requirements that are difficult to meet in use (5) : The ACT_GE_PROPERTY property data table stores data at the entire process engine level. When the table structure is initialized, three records are inserted by default.

Related article: Hand in Hand How to Play Activiti Workflow