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