springboot+flowable请假详细流程

1. flowable-ui

目前flowable-ui主要用于画流程图,流程图画完之后,再对XML做适当的修改

docker安装使用flowable

docker run -d -p 8080:8080 flowable/all-in-one

进入flowable-ui界面

http://127.0.0.1:8080/flowable-modeler

2. springboot 使用

2.1 准备流程文件

Holiday_Request.bpmn20.xml,将文件放在resource/processes目录下,启动是会自动找流程文件部署

<?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="holiday-request" name="Holiday Request" isExecutable="true">
    <startEvent id="startEvent" flowable:formFieldValidation="true"></startEvent>
    <sequenceFlow id="sequenceFlow-df79157f-2c4c-467c-8f27-50e3e1c65795" sourceRef="startEvent" targetRef="approveTask"></sequenceFlow>
    <userTask id="approveTask" name="Approve or reject request" flowable:candidateGroups="dept-managers" flowable:formFieldValidation="true"></userTask>
    <sequenceFlow id="sequenceFlow-e262d0a2-be78-462e-a382-58a507462e77" sourceRef="approveTask" targetRef="decision"></sequenceFlow>
    <exclusiveGateway id="decision"></exclusiveGateway>
    <sequenceFlow id="sequenceFlow-4395dd85-dfe3-41e1-9160-5f47ac0755a7" sourceRef="decision" targetRef="externalSystemCall">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sequenceFlow-17d5b350-554f-40ad-a475-b38196d562d7" sourceRef="decision" targetRef="sendRejectionMail">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${!approved}]]></conditionExpression>
    </sequenceFlow>
    <serviceTask id="externalSystemCall" name="Enter holidays in external system" flowable:class="com.hsm.flow.callback.HolidayCallback"></serviceTask>
    <sequenceFlow id="sequenceFlow-a0666bbf-eccf-4c2f-8567-6131445fa9b6" sourceRef="externalSystemCall" targetRef="holidayApprovedTask"></sequenceFlow>
    <userTask id="holidayApprovedTask" name="Holiday approved" flowable:assignee="managers" flowable:formFieldValidation="true">
      <extensionElements>
        <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
      </extensionElements>
    </userTask>
    <sequenceFlow id="sequenceFlow-e7697e72-a60f-4b97-9cde-7af165951805" sourceRef="holidayApprovedTask" targetRef="approveEnd"></sequenceFlow>
    <serviceTask id="sendRejectionMail" name="Send out rejection email" flowable:class="com.hsm.flow.callback.SendRejectionMailCallback"></serviceTask>
    <sequenceFlow id="sequenceFlow-fbd52a13-630e-452b-9136-5939252f8c9f" sourceRef="sendRejectionMail" targetRef="rejectEnd"></sequenceFlow>
    <endEvent id="approveEnd"></endEvent>
    <endEvent id="rejectEnd"></endEvent>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_holidayRequest">
    <bpmndi:BPMNPlane bpmnElement="holiday-request" id="BPMNPlane_holidayRequest">
      <bpmndi:BPMNShape bpmnElement="startEvent" id="BPMNShape_startEvent">
        <omgdc:Bounds height="30.0" width="30.0" x="240.0" y="255.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="approveTask" id="BPMNShape_approveTask">
        <omgdc:Bounds height="60.0" width="100.0" x="320.0" y="240.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="decision" id="BPMNShape_decision">
        <omgdc:Bounds height="40.0" width="40.0" x="470.0" y="250.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="externalSystemCall" id="BPMNShape_externalSystemCall">
        <omgdc:Bounds height="60.0" width="100.0" x="560.0" y="160.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="holidayApprovedTask" id="BPMNShape_holidayApprovedTask">
        <omgdc:Bounds height="60.0" width="100.0" x="710.0" y="160.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sendRejectionMail" id="BPMNShape_sendRejectionMail">
        <omgdc:Bounds height="60.0" width="100.0" x="560.0" y="320.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="approveEnd" id="BPMNShape_approveEnd">
        <omgdc:Bounds height="28.0" width="28.0" x="860.0" y="176.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="rejectEnd" id="BPMNShape_rejectEnd">
        <omgdc:Bounds height="28.0" width="28.0" x="745.0" y="335.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="sequenceFlow-e7697e72-a60f-4b97-9cde-7af165951805" id="BPMNEdge_sequenceFlow-e7697e72-a60f-4b97-9cde-7af165951805">
        <omgdi:waypoint x="809.9499999999999" y="190.0"></omgdi:waypoint>
        <omgdi:waypoint x="860.0" y="190.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sequenceFlow-fbd52a13-630e-452b-9136-5939252f8c9f" id="BPMNEdge_sequenceFlow-fbd52a13-630e-452b-9136-5939252f8c9f">
        <omgdi:waypoint x="659.949999999997" y="349.66442953020135"></omgdi:waypoint>
        <omgdi:waypoint x="745.0003059524752" y="349.09362216470777"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sequenceFlow-e262d0a2-be78-462e-a382-58a507462e77" id="BPMNEdge_sequenceFlow-e262d0a2-be78-462e-a382-58a507462e77">
        <omgdi:waypoint x="419.9499999999756" y="270.0"></omgdi:waypoint>
        <omgdi:waypoint x="470.0" y="270.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sequenceFlow-4395dd85-dfe3-41e1-9160-5f47ac0755a7" id="BPMNEdge_sequenceFlow-4395dd85-dfe3-41e1-9160-5f47ac0755a7">
        <omgdi:waypoint x="509.9189252336448" y="270.0"></omgdi:waypoint>
        <omgdi:waypoint x="522.0" y="270.0"></omgdi:waypoint>
        <omgdi:waypoint x="522.0" y="190.0"></omgdi:waypoint>
        <omgdi:waypoint x="560.0" y="190.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sequenceFlow-17d5b350-554f-40ad-a475-b38196d562d7" id="BPMNEdge_sequenceFlow-17d5b350-554f-40ad-a475-b38196d562d7">
        <omgdi:waypoint x="509.9189252336448" y="270.0"></omgdi:waypoint>
        <omgdi:waypoint x="522.0" y="270.0"></omgdi:waypoint>
        <omgdi:waypoint x="522.0" y="350.0"></omgdi:waypoint>
        <omgdi:waypoint x="559.9999999999769" y="350.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sequenceFlow-df79157f-2c4c-467c-8f27-50e3e1c65795" id="BPMNEdge_sequenceFlow-df79157f-2c4c-467c-8f27-50e3e1c65795">
        <omgdi:waypoint x="269.9499986183554" y="270.0"></omgdi:waypoint>
        <omgdi:waypoint x="319.9999999999394" y="270.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sequenceFlow-a0666bbf-eccf-4c2f-8567-6131445fa9b6" id="BPMNEdge_sequenceFlow-a0666bbf-eccf-4c2f-8567-6131445fa9b6">
        <omgdi:waypoint x="659.9499999999999" y="190.0"></omgdi:waypoint>
        <omgdi:waypoint x="710.0" y="190.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

2.2 准备pom文件

<dependencies>
    <!-- knife4j,swagger增强版 -->
    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-spring-boot-starter</artifactId>
        <version>2.0.7</version>
    </dependency>
    <!-- 工作流 -->
    <!-- Flowable spring-boot 版套餐 -->
    <dependency>
        <groupId>org.flowable</groupId>
        <artifactId>flowable-spring-boot-starter</artifactId>
        <version>6.6.0</version>
    </dependency>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.16</version>
        <scope>runtime</scope>
    </dependency>
 </dependencies>

2.3 配置数据源

server:
  port: 9099
spring:
  application:
    name: spring-flowable
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://ip:3306/flowable?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: 123456

2.4 配置流程引擎文件

ProcessEngineConfig.java

@Configuration
@Slf4j
public class ProcessEngineConfig {
    /**
     * 初始化流程引擎
     * @return
     */
    @Primary
    @Bean(name = "processEngine")
    public ProcessEngine initProcessEngine(DataSource dataSource) {
        log.info("=============================ProcessEngineBegin=============================");

        // 流程引擎配置
        ProcessEngineConfiguration cfg = null;

        try {
            cfg = new StandaloneProcessEngineConfiguration()
                    .setDataSource(dataSource)
                    // 初始化基础表,不需要的可以改为 DB_SCHEMA_UPDATE_FALSE
                    .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 初始化流程引擎对象
        ProcessEngine processEngine = cfg.buildProcessEngine();
        log.info("=============================ProcessEngineEnd=============================");
        return processEngine;
    }
}

2.5 开始写代码

实体类TaskResp.java

@Data
public class TaskResp {
    private String taskId;
    private String taskName;
}

控制器 HolidayController.java

@RestController
@Api(tags = "请求流程控制器", value = "请求流程控制器")
@RequestMapping("/holiday")
@Slf4j
public class HolidayController {
    @Autowired
    private IHolidayService holidayService;

    @PostMapping("create")
    @ApiOperation(value = "创建请假申请", notes = "创建请假申请")
    public boolean createRequest(@RequestParam("userName") String userName,
                                 @RequestParam("reason") String reason,
                                 @RequestParam("days") Integer days){
        return holidayService.createRequest(userName,reason,days);
    }

    @GetMapping("list/task")
    @ApiOperation(value = "任务列表查询", notes = "任务列表查询")
    public List<TaskResp> listTask(@RequestParam(value = "userGroup",required = false) String userGroup,
                                   @RequestParam(value = "username",required = false) String username,
                                   @RequestParam(value = "taskName",required = false) String taskName,
                                   @RequestParam(value = "businessKey",required = false) String businessKey){
        return holidayService.listTask(userGroup,username,taskName,businessKey);
    }

    @GetMapping("complete/task")
    @ApiOperation(value = "审核任务", notes = "审核任务")
    public boolean completeTask(@RequestParam(value = "taskId",required = true) String taskId,
                                   @RequestParam(value = "approved",required = true) boolean approved){
        return holidayService.completeTask(taskId,approved);
    }
}

实现类HolidayService.java

@Service
public class HolidayService implements IHolidayService {
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;


    private static final String processDefinitionId = "holiday-request";

    @Override
    public boolean createRequest(String userName, String reason, Integer days) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId)
                .singleResult();
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("userName", userName);
        variables.put("reason", reason);
        variables.put("days", days);
        ProcessInstance processInstance =
                runtimeService.startProcessInstanceByKey(processDefinitionId, variables);
        return true;
    }

    @Override
    public List<TaskResp> listTask(String userGroup, String username, String taskName, String businessKey) {
        List<TaskResp> dataList = new ArrayList<>();
        TaskQuery taskQuery = taskService.createTaskQuery();
        if(StringUtils.isNotEmpty(username)){
            taskQuery.taskAssignee(username);
        }
        if(StringUtils.isNotEmpty(userGroup)){
            taskQuery.taskCandidateGroup(userGroup);
        }
        List<Task> list = taskQuery.list();
        for (Task task : list) {
            TaskResp taskResp = new TaskResp();
            taskResp.setTaskId(task.getId());
            taskResp.setTaskName(task.getName());
            dataList.add(taskResp);
        }
        return dataList;
    }

    @Override
    public boolean completeTask(String taskId, boolean approved) {
        Map<String, Object> variables = new HashMap<>();
        variables.put("approved",approved);
        variables.put("var",123);
        taskService.complete(taskId,variables);
        return true;
    }
}

回调类

public class HolidayCallback  implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        System.out.println("审核通过了,保存假期数据入库");
    }
}
public class SendRejectionMailCallback implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        System.out.println("拒绝了申请,给申请人发送邮件");
    }
}

3. 流程说明

看图说话,这里需要仔细看xml文件和流程扭转了

github地址

https://github.com/Steven-hsm/java-interview

用户手册地址

https://tkjohn.github.io/flowable-userguide/#_getting_started

原文地址:https://www.cnblogs.com/steven158/p/15157527.html