1. Import dependencies
flowable depends on:
<dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring-boot-starter</artifactId> <version>6.7.2</version> </dependency>
pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>flowable_study</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.6</version> </parent> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.29</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.8</version> </dependency> <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring-boot-starter</artifactId> <version>6.7.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.5</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> </dependency> </dependencies> </project>
Note: The above is the pom.xml file of the entire project, which can be modified according to your actual situation. If you only want to add Flowable to the existing environment, you can directly import the top Flowable dependency
2. Database configuration
Flowable needs to connect to the database and will create a new table when the project starts. So we configure in application.yml:
spring: datasource: driverClassName: com.mysql.cj.jdbc.Driver username: root password: 199692 url: jdbc:mysql://127.0.0.1:3306/flowable?serverTimezone=Asia/Shanghai &useSSL=false
After the configuration is successful, when we start the project, it will automatically create a table for us, as shown in the figure:
3. Download the plug-in Flowable BPMN visualizer
If the IDE chooses IDEA, you can download the plug-in Flowable BPMN visualizer.
Shortcut key Ctrl + Alt + S Open the settings, find Plugins, select Marketplace, enter Flowable BPMN visualizer, and finally Click Install.
Click Apply when the installation is complete, and finally click OK to close the window.
4. Drawing
We create a new file in the resources directory of the project, and we can see that there is an additional New Flowable BPMN 2.0 file option. We select it and create a new file for the leave process. The file name is: ask_for_leave, which can be based on Name your own business.
After the creation, we right-click the file and select View BPMN (Flowable) Diagram :
Open the visual interface and start drawing the flow chart.
Right-click the blank space to create various events and processes.
The figure shows a simple schematic diagram of the leave process:
When drawing a picture, we need to change some of its properties below, such as: ID, Name, etc., so that we can see the name and meaning of the code later.
After drawing, we can see that the content of the file will be automatically generated for us:
<?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:flowable="http://flowable.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.flowable.org/processdef"> <process id="ask_for_leave" name="ask_for_leave" isExecutable="true"> <startEvent id="start_leave" name="start leave"/> <userTask id="employee" name="employee" flowable:assignee="#{employee}"/> <sequenceFlow id="flow_start" sourceRef="start_leave" targetRef="employee"/> <userTask id="leader" name="Leader" flowable:assignee="#{leader}"/> <sequenceFlow id="leave" sourceRef="employee" targetRef="leader" name="leave"/> <exclusiveGateway id="leader_judge"/> <sequenceFlow id="leader_audit" sourceRef="leader" targetRef="leader_judge" name="leader audit"/> <serviceTask id="send_message" flowable:exclusive="true" name="Send Leave Failed Message" flowable:class="src.com.dxc.service.AskForLeaveFail"/> <sequenceFlow id="leader_reject" sourceRef="leader_judge" targetRef="send_message" name="reject"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${checkResult=='rejected'}]]></conditionExpression> </sequenceFlow> <userTask id="manager" name="General Manager" flowable:assignee="#{manager}"/> <sequenceFlow id="leader_agree" sourceRef="leader_judge" targetRef="manager" name="agree"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${checkResult=='agree'}]]></conditionExpression> </sequenceFlow> <endEvent id="leave fail" name="leave_fail"/> <sequenceFlow id="flow_fail_end" sourceRef="send_message" targetRef="Leave Failed"/> <exclusiveGateway id="manager_judge"/> <sequenceFlow id="manager_audit" sourceRef="manager" targetRef="manager_judge" name="manager_judge"/> <sequenceFlow id="manager_rejet" sourceRef="manager_judge" targetRef="send_message" name="reject"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${checkResult=='rejected'}]]></conditionExpression> </sequenceFlow> <endEvent id="leave success" name="leave_success"/> <sequenceFlow id="manager_agree" sourceRef="manager_judge" targetRef="leave success" name="agree"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${checkResult=='agree'}]]></conditionExpression> </sequenceFlow> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_ask_for_leave"> <bpmndi:BPMNPlane bpmnElement="ask_for_leave" id="BPMNPlane_ask_for_leave"> <bpmndi:BPMNShape id="shape-ad5ec363-de3b-4ede-883e-629cbe497515" bpmnElement="start_leave"> <omgdc:Bounds x="-555.0" y="-580.0" width="30.0" height="30.0"/> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="shape-0d5a8676-83b3-46ea-a710-b73fb38a6ded" bpmnElement="employee"> <omgdc:Bounds x="-452.44943" y="-605.0" width="100.0" height="80.0"/> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="edge-bf2b83dc-ad4f-4ffa-ba9d-82d8128c2dde" bpmnElement="flow_start"> <omgdi:waypoint x="-525.0" y="-565.0"/> <omgdi:waypoint x="-485.0497" y="-565.0"/> <omgdi:waypoint x="-452.44943" y="-565.0"/> </bpmndi:BPMNEdge> <bpmndi:BPMNShape id="shape-d00296f1-1a06-4052-bd3c-d373c1f9ad74" bpmnElement="leader"> <omgdc:Bounds x="-260.0" y="-605.00006" width="100.0" height="80.0"/> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="edge-6a821f76-9583-438b-810e-5a8b48466ebb" bpmnElement="leave"> <omgdi:waypoint x="-352.44943" y="-565.0"/> <omgdi:waypoint x="-260.0" y="-565.00006"/> </bpmndi:BPMNEdge> <bpmndi:BPMNShape id="shape-57050807-ffe2-4a4d-a60b-eb224575ef4b" bpmnElement="leader_judge"> <omgdc:Bounds x="-75.0" y="-585.0001" width="40.0" height="40.0"/> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="edge-795a0427-2f83-4607-a40d-4b359790275e" bpmnElement="leader_audit"> <omgdi:waypoint x="-160.0" y="-565.00006"/> <omgdi:waypoint x="-75.0" y="-565.0001"/> </bpmndi:BPMNEdge> <bpmndi:BPMNShape id="shape-35bcb335-0516-4c89-9148-ead6c956472d" bpmnElement="send_message"> <omgdc:Bounds x="-105.0" y="-465.0" width="100.0" height="80.0"/> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="edge-c6b5f535-d7f2-4816-8d3a-e8499ad0c923" bpmnElement="leader_reject"> <omgdi:waypoint x="-55.0" y="-545.0001"/> <omgdi:waypoint x="-55.0" y="-465.0"/> </bpmndi:BPMNEdge> <bpmndi:BPMNShape id="shape-79a775c0-5c7a-4adf-b544-9954eedb08f6" bpmnElement="manager"> <omgdc:Bounds x="50.0" y="-605.0001" width="100.0" height="80.0"/> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="edge-57cd436f-f129-4044-b65c-637cf63465a6" bpmnElement="leader_agree"> <omgdi:waypoint x="-35.0" y="-565.0001"/> <omgdi:waypoint x="50.0" y="-565.0001"/> </bpmndi:BPMNEdge> <bpmndi:BPMNShape id="shape-37759eb0-7cf1-4da0-8aba-39c8443675ac" bpmnElement="Leave Failed"> <omgdc:Bounds x="-240.0" y="-440.0" width="30.0" height="30.0"/> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="edge-3acbccbb-9a56-4ff0-86b1-f2f6641f125c" bpmnElement="flow_fail_end"> <omgdi:waypoint x="-105.0" y="-425.0"/> <omgdi:waypoint x="-210.0" y="-425.0"/> </bpmndi:BPMNEdge> <bpmndi:BPMNShape id="shape-b3da428e-37fc-410e-a90f-40eb0230e892" bpmnElement="manager_judge"> <omgdc:Bounds x="250.0" y="-585.0001" width="40.0" height="40.0"/> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="edge-e56b296d-0690-44e3-bdaf-6e3864a826ca" bpmnElement="manager_audit"> <omgdi:waypoint x="150.0" y="-565.0001"/> <omgdi:waypoint x="250.0" y="-565.0001"/> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="edge-a806347f-3f50-49da-ab26-d571a6104c0c" bpmnElement="manager_rejet"> <omgdi:waypoint x="270.0" y="-545.0001"/> <omgdi:waypoint x="269.99997" y="-424.99997"/> <omgdi:waypoint x="-5.0" y="-425.0"/> </bpmndi:BPMNEdge> <bpmndi:BPMNShape id="shape-416e3b9e-2e1a-489a-808e-fa5fd3f8c691" bpmnElement="leave successfully"> <omgdc:Bounds x="395.0" y="-580.00006" width="30.0" height="30.0"/> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="edge-33f48358-b60a-4ea8-bd80-9729cfdf6a11" bpmnElement="manager_agree"> <omgdi:waypoint x="290.0" y="-565.0001"/> <omgdi:waypoint x="395.0" y="-565.00006"/> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>
Friends who don’t want to draw can directly copy my content.
In this file, we only need to pay attention to the content wrapped by
In fact, the content of XML is very easy to understand. According to the label and the flow chart, you can know the specific meaning:
: Indicates a complete workflow. : The starting position in the workflow, which is the green button in the figure. : The end position in the workflow, which is the red button in the figure. : Represents a task review node (team leader, manager, etc.), There is a flowable:assignee attribute on this node, which indicates who should handle this node , when calling in the Java code in the future, we need to specify the ID of the corresponding handler or other unique tags. : This is a service task. In a specific implementation, this task can do anything. Here we use the flowable:class attribute to configure a class, which means that if you go When reaching this node, the method in this class will be executed automatically. : Logical judgment node, which is equivalent to the diamond box in the flowchart. : A line linking each node, the sourceRef attribute indicates the starting node of the line, and the targetRef attribute indicates the node the line points to , the lines in our figure belong to this.
According to the above explanation of each tag, the generated XML file can be easily understood.
5. Code testing
Gitee code address: https://gitee.com/du_xin_cheng/flowable_study
Startup class:
package src.com.dxc; import org.flowable.engine.RepositoryService; import org.flowable.engine.repository.DeploymentBuilder; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import javax.annotation.Resource; import java.io.InputStream; /** * Startup class * * @Author xincheng.du * @Date 2023/5/22 16:50 */ @SpringBootApplication public class Application implements CommandLineRunner {<!-- --> public static void main(String[] args) {<!-- --> SpringApplication.run(Application.class, args); } @Resource private RepositoryService repositoryService; /** * Project startup loading process file * * @param args args */ @Override public void run(String... args) {<!-- --> InputStream inputStream = this.getClass().getResourceAsStream("/ask_for_leave.bpmn20.xml"); DeploymentBuilder deploymentBuilder = repositoryService. createDeployment(); deploymentBuilder.addInputStream("ask_for_leave.bpmn20.xml", inputStream); deploymentBuilder.deploy(); } }
Initiate leave process parameter class AskForLeaveStartDTO:
package src.com.dxc.model; import lombok. AllArgsConstructor; import lombok.Data; import lombok. NoArgsConstructor; /** * Initiate leave process parameters * * @Author xincheng.du * @Date 2023/5/23 15:00 */ @Data @NoArgsConstructor @AllArgsConstructor public class AskForLeaveStartDTO {<!-- --> /** * employee id */ private String employeeId; /** * employee's name */ private String name; /** * Reason for leave */ private String reason; /** * Number of days off */ private Integer days; }
Audit parameter class AuditDTO:
package src.com.dxc.model; import lombok. AllArgsConstructor; import lombok.Data; import lombok. NoArgsConstructor; /** * Review parameters * * @Author xincheng.du * @Date 2023/5/23 15:12 */ @Data @NoArgsConstructor @AllArgsConstructor public class AuditDTO {<!-- --> /** * Subordinate id {employee id || leader id} */ private String lowerId; /** * subordinate name {employee || leader} * The name here corresponds to the flowable:assignee attribute under the <userTask> tag in the ask_for_leave.bpmn20.xml file */ private String lowerName; /** * superior id {team leader id || manager id} */ private String superiorId; /** * Superior name {leader || manager} * The name here corresponds to the flowable:assignee attribute under the <userTask> tag in the ask_for_leave.bpmn20.xml file */ private String superiorName; /** * Review Result {Agree || Reject} */ private String checkResult; /** * Whether it is the highest level * true->leaderId is the highest level, managerId is empty * false->leaderId is not the highest level, managerId is the superior of leaderId */ private Boolean isTop; }
Leave process interface AskForLeaveService:
package src.com.dxc.service.impl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.stereotype.Service; import src.com.dxc.model.AskForLeaveStartDTO; import src.com.dxc.model.AuditDTO; import src.com.dxc.service.AskForLeaveService; import java.util.*; /** * Leave process * * @Author xincheng.du * @Date 2023/5/23 14:51 */ @Slf4j @Service @RequiredArgsConstructor public class AskForLeaveServiceImpl implements AskForLeaveService {<!-- --> private final RuntimeService runtimeService; private final TaskService taskService; public static final String EMPLOYEE = "employee"; public static final String FLOW_KEY = "ask_for_leave"; public static final String NAME = "name"; public static final String REASON = "reason"; public static final String DAYS = "days"; public static final String CHECK_RESULT = "checkResult"; @Override public void askForLeave(AskForLeaveStartDTO dto) {<!-- --> HashMap<String, Object> map = new HashMap<>(); map.put(EMPLOYEE, dto.getEmployeeId()); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(FLOW_KEY, map); runtimeService.setVariable(processInstance.getId(), NAME, dto.getName()); runtimeService.setVariable(processInstance.getId(), REASON, dto.getReason()); runtimeService.setVariable(processInstance.getId(), DAYS, dto.getDays()); log.info("Create leave process processId: {}", processInstance.getId()); } @Override public void executeProcess(AuditDTO dto) {<!-- --> // Find the task of the subordinate, and then submit it to the superior for approval List<Task> list = taskService.createTaskQuery().taskAssignee(dto.getLowerId()).orderByTaskId().desc().list(); for (Task task : list) {<!-- --> log.info("Task ID: {}; Task handler: {}; Whether the task is suspended: {}", task.getId(), task.getAssignee(), task.isSuspended()); Map<String, Object> map = new HashMap<>(); // If the current handler is not the top handler, then the superior id of the current handler needs to be specified Optional.of(dto.getIsTop()).ifPresent(isTop -> {<!-- --> if (Objects. equals(Boolean. FALSE, isTop)) {<!-- --> map.put(dto.getSuperiorName(), dto.getSuperiorId()); } }); Optional.ofNullable(dto.getCheckResult()).ifPresent(result -> map.put(CHECK_RESULT, result)); taskService.complete(task.getId(), map); } } }
Leave process implementation class AskForLeaveServiceImpl:
package src.com.dxc.service.impl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.stereotype.Service; import src.com.dxc.model.AskForLeaveStartDTO; import src.com.dxc.model.AuditDTO; import src.com.dxc.service.AskForLeaveService; import java.util.*; /** * Leave process * * @Author xincheng.du * @Date 2023/5/23 14:51 */ @Slf4j @Service @RequiredArgsConstructor public class AskForLeaveServiceImpl implements AskForLeaveService {<!-- --> private final RuntimeService runtimeService; private final TaskService taskService; public static final String EMPLOYEE = "employee"; public static final String FLOW_KEY = "ask_for_leave"; public static final String NAME = "name"; public static final String REASON = "reason"; public static final String DAYS = "days"; public static final String CHECK_RESULT = "checkResult"; @Override public void askForLeave(AskForLeaveStartDTO dto) {<!-- --> HashMap<String, Object> map = new HashMap<>(); map.put(EMPLOYEE, dto.getEmployeeId()); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(FLOW_KEY, map); runtimeService.setVariable(processInstance.getId(), NAME, dto.getName()); runtimeService.setVariable(processInstance.getId(), REASON, dto.getReason()); runtimeService.setVariable(processInstance.getId(), DAYS, dto.getDays()); log.info("Create leave process processId: {}", processInstance.getId()); } @Override public void executeProcess(AuditDTO dto) {<!-- --> // Find the task of the subordinate, and then submit it to the superior for approval List<Task> list = taskService.createTaskQuery().taskAssignee(dto.getLowerId()).orderByTaskId().desc().list(); for (Task task : list) {<!-- --> log.info("Task ID: {}; Task handler: {}; Whether the task is suspended: {}", task.getId(), task.getAssignee(), task.isSuspended()); Map<String, Object> map = new HashMap<>(); // If the current handler is not the top handler, then the superior id of the current handler needs to be specified Optional.of(dto.getIsTop()).ifPresent(isTop -> {<!-- --> if (Objects. equals(Boolean. FALSE, isTop)) {<!-- --> map.put(dto.getSuperiorName(), dto.getSuperiorId()); } }); Optional.ofNullable(dto.getCheckResult()).ifPresent(result -> map.put(CHECK_RESULT, result)); taskService.complete(task.getId(), map); } } }
Execution class AskForLeaveFail for leave failure:
package src.com.dxc.service; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.delegate.JavaDelegate; @Slf4j public class AskForLeaveFail implements JavaDelegate {<!-- --> @Override public void execute(DelegateExecution execution) {<!-- --> log.info("Leave request failed..."); } }
To view the process progress Controller, view it by starting the project call interface:
package src.com.dxc.controller; import lombok.RequiredArgsConstructor; import org.flowable.bpmn.model.BpmnModel; import org.flowable.engine.ProcessEngine; import org.flowable.engine.ProcessEngineConfiguration; import org.flowable.engine.RepositoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.runtime.Execution; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.image.ProcessDiagramGenerator; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; /** * View process progress * * @Author xincheng.du * @Date 2023/5/23 15:25 */ @RestController @RequiredArgsConstructor public class ShowController {<!-- --> private final RuntimeService runtimeService; private final RepositoryService repositoryService; private final ProcessEngine processEngine; @GetMapping("/showFlowPic") public void showPic(HttpServletResponse resp, String processId) throws Exception {<!-- --> ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult(); if (pi == null) {<!-- --> return; } List<Execution> executions = runtimeService .createExecutionQuery() .processInstanceId(processId) .list(); List<String> activityIds = new ArrayList<>(); List<String> flows = new ArrayList<>(); for (Execution exe : executions) {<!-- --> List<String> ids = runtimeService.getActiveActivityIds(exe.getId()); activityIds. addAll(ids); } BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId()); ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration(); ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator(); InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, false); OutputStream out = null; byte[] buf = new byte[1024]; int length = 0; try {<!-- --> out = resp. getOutputStream(); while ((legth = in.read(buf)) != -1) {<!-- --> out.write(buf, 0, legth); } } finally {<!-- --> if (in != null) {<!-- --> in. close(); } if (out != null) {<!-- --> out. close(); } } } }
Process test class AskForLeaveServiceImplTest :
package src.com.dxc.service.impl; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import src.com.dxc.Application; import src.com.dxc.model.AskForLeaveStartDTO; import src.com.dxc.model.AuditDTO; import src.com.dxc.service.AskForLeaveService; @Slf4j @SpringBootTest(classes = Application. class) class AskForLeaveServiceImplTest {<!-- --> @Autowired public AskForLeaveService askForLeaveService; /** * Initiate leave process */ @Test void askForLeave() {<!-- --> AskForLeaveStartDTO dto = new AskForLeaveStartDTO(); dto.setEmployeeId("1"); dto.setName("xiaoMing"); dto.setReason("Travel"); dto.setDays(10); askForLeaveService. askForLeave(dto); } /** * Employees submit the process to the team leader */ @Test void submit() {<!-- --> AuditDTO dto = new AuditDTO(); dto.setLowerId("1"); dto.setLowerName("employee"); dto.setSuperiorId("2"); dto.setSuperiorName("leader"); dto.setIsTop(false); askForLeaveService. executeProcess(dto); } /** * Team Leader Approval - Agree */ @Test void leaderAgree() {<!-- --> AuditDTO dto = new AuditDTO(); dto.setLowerId("2"); dto.setLowerName("leader"); dto.setSuperiorId("3"); dto.setSuperiorName("manager"); dto.setCheckResult("agree"); dto.setIsTop(false); askForLeaveService. executeProcess(dto); } /** * Team Leader Approval - Rejected */ @Test void leaderReject() {<!-- --> AuditDTO dto = new AuditDTO(); dto.setLowerId("2"); dto.setLowerName("leader"); dto.setSuperiorId("3"); dto.setSuperiorName("manager"); dto.setCheckResult("rejected"); dto.setIsTop(false); askForLeaveService. executeProcess(dto); } /** * Manager Approval - Agree */ @Test void managerAgree() {<!-- --> AuditDTO dto = new AuditDTO(); dto.setLowerId("3"); dto.setLowerName("manager"); dto.setCheckResult("agree"); dto.setIsTop(true); askForLeaveService. executeProcess(dto); } /** * Manager Approval - Reject */ @Test void managerReject() {<!-- --> AuditDTO dto = new AuditDTO(); dto.setLowerId("3"); dto.setLowerName("manager"); dto.setCheckResult("rejected"); dto.setIsTop(true); askForLeaveService. executeProcess(dto); } }
You can check whether the current progress is correct by accessing the interface /showFlowPic at each step, and the processId required in the interface is printed by the askForLeave() method processId from the output.