20000 words, the handsome guy made it clear that SpringBoot integrates Activiti workflow engine

Posted by toro04 on Mon, 13 Dec 2021 01:29:53 +0100

Lalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalalala!! Welcome to collect and share

The first step is to import the jar package

		  <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter-basic</artifactId>
            <version>5.22.0</version>
        </dependency>

This is the core package of activiti. We also add other jar packages

		  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.30</version>
        </dependency>

The second step is to write a configuration file

server:
  port: 8089
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/activiti
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: 123456

The third step is to create the activiti library

Step 4: run the startup class


OK, there is a problem at this time. This is the rich students stepping on the pit for everyone. Remember to avoid it
Startup error: Could not find class [org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration]
The problem is that activiti itself depends on Security, so we exclude it and add it to the startup class

exclude = SecurityAutoConfiguration.class


OK, let's start. At this time, we encounter another problem
Caused by: java.io.FileNotFoundException: class path resource [processes/] cannot be resolved to URL because it does not exist
This is because activiti will look for bpm files when starting, and will look for them in the default processes folder,
At this time, we add in the configuration class

  activiti:
    check-process-definitions: false


It will start successfully at this time!

Step 5 Check our database after successful startup


You will find that 25 tables have been automatically generated!
If you haven't read the first article of rich and noble students, please move to this address to check it SpringBoot integrated Activiti (I)
The first step is to cancel the configuration of yml, because we need to create a process

leave.bpmn file thank you( https://gitee.com/shenzhanwang/Spring-activiti )Process documents provided

<?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="leave" name="My process" isExecutable="true">
    <userTask id="deptleaderaudit" name="Approval of department leaders" activiti:assignee="${deptleader}" activiti:candidateGroups="division manager"></userTask>
    <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>
    <userTask id="hraudit" name="Personnel approval" activiti:assignee="${hr}" activiti:candidateGroups="personnel matters"></userTask>
    <sequenceFlow id="flow3" name="agree" sourceRef="exclusivegateway1" targetRef="hraudit">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptleaderapprove=='true'}]]></conditionExpression>
    </sequenceFlow>
    <userTask id="modifyapply" name="Adjustment application" activiti:assignee="${applyuserid}"></userTask>
    <sequenceFlow id="flow4" name="refuse" sourceRef="exclusivegateway1" targetRef="modifyapply">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptleaderapprove=='false'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow6" sourceRef="deptleaderaudit" targetRef="exclusivegateway1"></sequenceFlow>
    <exclusiveGateway id="exclusivegateway2" name="Exclusive Gateway"></exclusiveGateway>
    <sequenceFlow id="flow7" sourceRef="modifyapply" targetRef="exclusivegateway2"></sequenceFlow>
    <sequenceFlow id="flow8" name="Reapply" sourceRef="exclusivegateway2" targetRef="deptleaderaudit">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${reapply=='true'}]]></conditionExpression>
    </sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow9" name="End process" sourceRef="exclusivegateway2" targetRef="endevent1">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${reapply=='false'}]]></conditionExpression>
    </sequenceFlow>
    <exclusiveGateway id="exclusivegateway3" name="Exclusive Gateway"></exclusiveGateway>
    <sequenceFlow id="flow10" sourceRef="hraudit" targetRef="exclusivegateway3"></sequenceFlow>
    <sequenceFlow id="flow11" name="refuse" sourceRef="exclusivegateway3" targetRef="modifyapply">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${hrapprove=='false'}]]></conditionExpression>
    </sequenceFlow>
    <userTask id="reportback" name="Leave cancellation" activiti:assignee="${applyuserid}"></userTask>
    <sequenceFlow id="flow12" name="agree" sourceRef="exclusivegateway3" targetRef="reportback">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${hrapprove=='true'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow13" sourceRef="reportback" targetRef="endevent1"></sequenceFlow>
    <startEvent id="startevent1" name="Start" activiti:initiator="${applyuserid}"></startEvent>
    <sequenceFlow id="flow14" sourceRef="startevent1" targetRef="deptleaderaudit"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_leave">
    <bpmndi:BPMNPlane bpmnElement="leave" id="BPMNPlane_leave">
      <bpmndi:BPMNShape bpmnElement="deptleaderaudit" id="BPMNShape_deptleaderaudit">
        <omgdc:Bounds height="55.0" width="105.0" x="250.0" y="220.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1">
        <omgdc:Bounds height="40.0" width="40.0" x="535.0" y="227.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="hraudit" id="BPMNShape_hraudit">
        <omgdc:Bounds height="55.0" width="105.0" x="620.0" y="220.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="modifyapply" id="BPMNShape_modifyapply">
        <omgdc:Bounds height="55.0" width="105.0" x="503.0" y="310.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway2" id="BPMNShape_exclusivegateway2">
        <omgdc:Bounds height="40.0" width="40.0" x="535.0" y="410.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="890.0" y="413.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway3" id="BPMNShape_exclusivegateway3">
        <omgdc:Bounds height="40.0" width="40.0" x="770.0" y="228.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="reportback" id="BPMNShape_reportback">
        <omgdc:Bounds height="55.0" width="105.0" x="855.0" y="221.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="140.0" y="230.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="575.0" y="247.0"></omgdi:waypoint>
        <omgdi:waypoint x="620.0" y="247.0"></omgdi:waypoint>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="48.0" width="24.0" x="575.0" y="247.0"></omgdc:Bounds>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
        <omgdi:waypoint x="555.0" y="267.0"></omgdi:waypoint>
        <omgdi:waypoint x="555.0" y="310.0"></omgdi:waypoint>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="48.0" width="24.0" x="555.0" y="267.0"></omgdc:Bounds>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
        <omgdi:waypoint x="355.0" y="247.0"></omgdi:waypoint>
        <omgdi:waypoint x="535.0" y="247.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7">
        <omgdi:waypoint x="555.0" y="365.0"></omgdi:waypoint>
        <omgdi:waypoint x="555.0" y="410.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8">
        <omgdi:waypoint x="535.0" y="430.0"></omgdi:waypoint>
        <omgdi:waypoint x="302.0" y="429.0"></omgdi:waypoint>
        <omgdi:waypoint x="302.0" y="275.0"></omgdi:waypoint>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="48.0" width="48.0" x="361.0" y="438.0"></omgdc:Bounds>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9">
        <omgdi:waypoint x="575.0" y="430.0"></omgdi:waypoint>
        <omgdi:waypoint x="890.0" y="430.0"></omgdi:waypoint>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="48.0" width="48.0" x="659.0" y="437.0"></omgdc:Bounds>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow10" id="BPMNEdge_flow10">
        <omgdi:waypoint x="725.0" y="247.0"></omgdi:waypoint>
        <omgdi:waypoint x="770.0" y="248.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow11" id="BPMNEdge_flow11">
        <omgdi:waypoint x="790.0" y="268.0"></omgdi:waypoint>
        <omgdi:waypoint x="789.0" y="337.0"></omgdi:waypoint>
        <omgdi:waypoint x="608.0" y="337.0"></omgdi:waypoint>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="48.0" width="24.0" x="672.0" y="319.0"></omgdc:Bounds>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow12" id="BPMNEdge_flow12">
        <omgdi:waypoint x="810.0" y="248.0"></omgdi:waypoint>
        <omgdi:waypoint x="855.0" y="248.0"></omgdi:waypoint>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="48.0" width="24.0" x="810.0" y="248.0"></omgdc:Bounds>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow13" id="BPMNEdge_flow13">
        <omgdi:waypoint x="907.0" y="276.0"></omgdi:waypoint>
        <omgdi:waypoint x="907.0" y="413.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow14" id="BPMNEdge_flow14">
        <omgdi:waypoint x="175.0" y="247.0"></omgdi:waypoint>
        <omgdi:waypoint x="250.0" y="247.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

This is a simple leave application process

Step 6, after putting it into the specified folder, we restart the class

Step 7, check our data sheet

ACT_ RE_ Our process can be viewed in procdef

After that, our process has been created. Now let's start our api operation

Step 8: because we want to connect with our business, we create a leave form

CREATE TABLE `leave` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `process_instance_id` varchar(45) DEFAULT NULL COMMENT 'Flow chart id',
  `user_id` varchar(20) DEFAULT NULL COMMENT 'user id',
  `start_time` varchar(45) DEFAULT NULL COMMENT 'start time',
  `end_time` varchar(45) DEFAULT NULL COMMENT 'End time',
  `leave_type` varchar(45) DEFAULT NULL COMMENT 'Leave type',
  `reason` varchar(400) DEFAULT NULL COMMENT 'Reason for leave',
  `apply_time` datetime DEFAULT NULL COMMENT 'Passing time',
  `reality_start_time` varchar(45) DEFAULT NULL COMMENT 'Leave start time',
  `reality_end_time` varchar(45) DEFAULT NULL COMMENT 'Leave end time',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='Leave form';

Step 9: integrate the underlying framework

By the way, we don't have a database framework. We use it here mybatisPlusPro As the underlying framework

Step 10: create classes such as controller

import com.wangfugui.activiti.dao.LeaveDO;
import com.wangfugui.activiti.service.LeaveService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author MaSiyi
 * @version 1.0.0 2021/12/2
 * @since JDK 1.8.0
 */
@RestController
@RequestMapping("/leave")
public class LeaveController extends BaseController<LeaveService, LeaveDO>{


}
import com.baomidou.mybatisplus.extension.service.IService;
import com.wangfugui.activiti.dao.LeaveDO;

/**
 * @author MaSiyi
 * @version 1.0.0 2021/12/2
 * @since JDK 1.8.0
 */
public interface LeaveService extends IService<LeaveDO> {
}
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wangfugui.activiti.dao.LeaveDO;
import com.wangfugui.activiti.dao.mapper.LeaveMapper;
import com.wangfugui.activiti.service.LeaveService;
import org.springframework.stereotype.Service;

/**
 * @author MaSiyi
 * @version 1.0.0 2021/12/2
 * @since JDK 1.8.0
 */
@Service
public class LeaveServiceImpl extends ServiceImpl<LeaveMapper, LeaveDO> implements LeaveService {

}
```java
hellow Ah, pretty boy, have you seen the previous content?
Please move to the previous content to see the content of (I) (II)
Then we began to integrate with our business.

Let's first review the content of the previous issue. There is a problem left over from the previous issue, that is, the mabatis jar package will conflict, so we

		  <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter-basic</artifactId>
            <version>5.22.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis</artifactId>
                </exclusion>
            </exclusions>

        </dependency>

Step 11: OK. In order to facilitate the interface test, we use swagger to quickly build the interface

 <!--swagger-->
        <dependency><!--add to Swagger rely on -->
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency><!--add to Swagger-UI rely on -->
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
        </dependency>
        <!--swagger-->

Step 12: write the controller class

import com.wangfugui.activiti.dao.LeaveVo;
import com.wangfugui.activiti.dao.Leaves;
import com.wangfugui.activiti.dao.dto.HandleDto;
import com.wangfugui.activiti.dao.dto.UpcomingDto;
import com.wangfugui.activiti.service.LeaveService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author MaSiyi
 * @version 1.0.0 2021/12/2
 * @since JDK 1.8.0
 */
@RestController
@RequestMapping("/leave")
@Api(tags = "leave")
public class LeaveController {

    @Autowired
    protected LeaveService leaveService;

    @PostMapping("/startLeave")
    @ApiOperation("Initiate a leave process")
    public String startLeave(@RequestBody Leaves leave) {
        return leaveService.startLeave(leave);
    }

    @PostMapping("/upcoming")
    @ApiOperation("Get to-do list")
    public List<LeaveVo> listTask(@RequestBody UpcomingDto upcomingDto) {
        return leaveService.upcoming(upcomingDto);
    }

    @PostMapping("/handle")
    @ApiOperation("Processing flow")
    public String handle(@RequestBody HandleDto handleDto) {
        return leaveService.handle(handleDto);
    }



}

This time, let's start with a simple creation process to obtain the to-do process and approval process
So we see these three interfaces

Step 13, we go to the implementation class to see

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wangfugui.activiti.dao.LeaveVo;
import com.wangfugui.activiti.dao.Leaves;
import com.wangfugui.activiti.dao.dto.HandleDto;
import com.wangfugui.activiti.dao.dto.UpcomingDto;
import com.wangfugui.activiti.dao.mapper.LeaveMapper;
import com.wangfugui.activiti.service.LeaveService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * @author MaSiyi
 * @version 1.0.0 2021/12/2
 * @since JDK 1.8.0
 */
@Service
public class LeaveServiceImpl extends ServiceImpl<LeaveMapper, Leaves> implements LeaveService {

    @Autowired
    private RuntimeService runtimeservice;
    @Autowired
    private IdentityService identityservice;
    @Autowired
    private TaskService taskservice;


    /**
     * Start a process
     *
     * @param leave
     * @Param: [leave]
     * @return: org.activiti.engine.runtime.ProcessInstance
     * @Author: MaSiyi
     * @Date: 2021/12/3
     */
    @Override
    public String startLeave(Leaves leave) {
        leave.setId(UUID.randomUUID().toString().substring(0, 8));
        //Set user
        identityservice.setAuthenticatedUserId(leave.getUserId());
        //Use the primary key of the leave apply table as the business key to connect business data and process data
        String businesskey = String.valueOf(leave.getId());
        //Initiate leave process
        Map<String, Object> map = new HashMap<>();
        //Next step handler
        map.put("deptleader", "Tom");
        ProcessInstance processInstance = runtimeservice.startProcessInstanceByKey("leave", businesskey, map);
        String instanceid = processInstance.getId();
        //Associated process table
        leave.setProcessInstanceId(instanceid);
        this.save(leave);
        return "Successful application";
    }

    /**
     * Get to-do list
     *
     * @param upcomingDto
     * @Param: [upcomingDto]
     * @return: java.util.List<com.wangfugui.activiti.dao.Leaves>
     * @Author: MaSiyi
     * @Date: 2021/12/3
     */
    @Override
    public List<LeaveVo> upcoming(UpcomingDto upcomingDto) {
        List<LeaveVo> leaves = new ArrayList<>();
        // Query the to-do list using task candidates
        List<Task> tasks = taskservice.createTaskQuery().taskAssignee(upcomingDto.getUsername()).taskName("Approval of department leaders")
                .listPage(upcomingDto.getPage(), upcomingDto.getSize());
        for (Task task : tasks) {
            String instanceId = task.getProcessInstanceId();
            //Query running task s
            ProcessInstance processInstance = runtimeservice.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
            String businessKey = processInstance.getBusinessKey();
            Leaves leave = this.getById(businessKey);
            LeaveVo leaveVo = new LeaveVo();
            BeanUtils.copyProperties(leave,leaveVo);
            leaveVo.setTaskId(task.getId());
            leaves.add(leaveVo);
        }
        return leaves;
    }

    /**
     * Approval task

     * @Param: [taskid]
     * @return: java.lang.String
     * @Author: MaSiyi
     * @Date: 2021/12/3
     */
    @Override
    public String handle(HandleDto handleDto) {
        //Complete the specified task
        taskservice.claim(handleDto.getTaskId(), handleDto.getUserName());
        //Proceed to the next task
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("deptleaderapprove", handleDto.getApprove());
        variables.put("hr", handleDto.getNextUserNam());
        taskservice.complete(handleDto.getTaskId(), variables);
        return "Processing succeeded";
    }
}

These three explanations have been written in the code. You can look at the comments

Step 14, let's take a look at our test

Initiate a process


Check our to-do list


Handle our to-do list


Well, that's all for today's simple basic method
The next article introduces the integration of viewing historical processes, etc
Today, let's talk about the historical process

Step 15 first of all, we need to know that the historical Service in activiti is HistoryService

So the first step is to inject the HistoryService

Step 16: write the method to obtain the history process

   @GetMapping("/myleave")
    @ApiOperation("Leave process initiated by me")
    public List<LeaveVo> myleave(@RequestParam String userId) {
        return leaveService.myleave(userId);
    }

This interface is used to obtain the initiated leave process according to userId

    @GetMapping("/getHistory")
    @ApiOperation("Get historical process information")
    public HistoricProcessInstance getHistory(@RequestParam String procInstId) {
        return leaveService.getHiProcByProcInstId(procInstId);
    }

What is this interface? What is procInstId?


Third interface

    @GetMapping("/getHiProcByProcKeyAndBusinessID")
    @ApiOperation("Get historical tasks")
    public HistoricProcessInstance getHiProcByProcKeyAndBusinessID(@RequestParam String procKey, @RequestParam String businessID) {
        return leaveService.getHiProcByProcKeyAndBusinessID(procKey, businessID);
    }

What are procKey and businessID?

businessID is our business primary key

Association in this table


Well, it's not easy for rich students to knock by hand. It's 20000 characters. I hope it can help you. Full code, please move to SpringBoot+Activiti see
I hope you will give me some praise and attention. Thank you

Topics: Java Spring Boot Activiti