Bootstrap

flowable集成spring boot ---- 搭建服务

准备工作

引入依赖 flowable-spring-boot-starter
个人用的6.7.0版本的flowable

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

		<dependency>
			<groupId>org.flowable</groupId>
			<artifactId>flowable-spring-boot-starter</artifactId>
			<version>6.7.0</version>
		</dependency>

		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.9.2</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.9.2</version>
		</dependency>

application.yaml配置

flowable:
  #关闭定时任务JOB
  async-executor-activate: false
  #  将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。
  database-schema-update: true


# datasource configuration is required
spring.datasource:
  url: jdbc:mysql://localhost:3306/flowable?useUnicode=true&characterEncoding=utf8&nullCatalogMeansCurrent=true&useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC
  driver-class-name: com.mysql.cj.jdbc.Driver
  username: root
  password: root

绘制流程图

这个我没自己部署,找了别人在云服务器搭建好的画的,随便的,没啥逻辑,
关于流程图的绘画传参及表达式的使用,下篇文章说明。
在这里插入图片描述画好流程图后点击下载,可导出 bpmn20.xml结尾的文件
在这里插入图片描述

部署流程

 集成springboot的flowable可以自动部署,也可以选择手动部署
 项目启动时会自动部署resources下的bpmn文件

在这里插入图片描述手动部署
我写了一个上传文件部署的接口

package com.example.demo.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
@Api
@RestController
public class DeployProcess {
    @Autowired
    private RepositoryService repositoryService;
    @ApiOperation(value = "部署流程")
    @PostMapping("/deployProcess")
    public void deployProcess(MultipartFile file){
        String name =file.getOriginalFilename();
       try {
            InputStream inputStream = file.getInputStream();

            Deployment deployment = repositoryService.createDeployment()
                    //部署名
                    .name("ManualDeployment")
                    //resourceName,即文件名,一定要以BPMN或者BPMN20.xml结尾不然部署时不会插入act_re_procedf表,无法获取到流程
                    .addInputStream(name,inputStream)
                    .deploy();


        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

开启流程实例

runtimeService.startProcessInstanceByKey()

 @ApiOperation(value = "报销申请")
    @PostMapping("/start/{userId}/{amount}")
    public String startApply(@PathVariable String userId, @PathVariable String amount) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("userId", userId);
        map.put("amount", amount);
        ProcessInstance instance =
                runtimeService.startProcessInstanceByKey("reimbursement", map);
        if (instance == null) {
            return null;
        }else {
            return instance.getProcessInstanceId();
        }
    }

start的key为流程的Id,
在这里插入图片描述在这里插入图片描述

查询待办任务

taskService.createTaskQuery()

 @ApiOperation(value = "获取需要审核的项目申请列表")
    @GetMapping(value = "/project/approve/list")
    public @ResponseBody
    List<ReimbursementRecord> getAllParticipateRequest(String employee,String department, String auditLevel) {
        //get the taskList
        List<Task> tasks;

        if (StringUtils.isNotBlank(employee)) {
            //按候选人查询
            tasks = taskService.createTaskQuery().
                    taskName(auditLevel).taskAssignee(employee).
                   // taskCandidateOrAssigned(employee).
                    list();
        }else {
            //按候选组查询
            tasks = taskService.createTaskQuery().
                    taskName(auditLevel).
                    taskCandidateGroup(department).
                    list();
        }
        List<ReimbursementRecord> records = new ArrayList<ReimbursementRecord>(tasks.size());
        tasks.forEach( task -> {
            ReimbursementRecord record = new ReimbursementRecord();
            String taskId = task.getId();
            Map<String, Object> variables = taskService.getVariables(taskId);

            String userId =  (String)variables.get("userId") ;
            String amount = (String) variables.get("amount");
            record.setUserId(userId);
            record.setAmount(amount);
            record.setTaskId(taskId);

            records.add(record);
        });

        return records;
    }

完成任务

taskService.complete();

 @ApiOperation(value = "审核项目申请")
    @PostMapping(value = "/participateRequests/{taskId}")
    public boolean approveParticipateRequest(@PathVariable String taskId, boolean passed, String employee,String department) {
        Task task ;
        if("财务".equals(department)){
            task = taskService.createTaskQuery().taskAssignee(employee).taskId(taskId).singleResult();
        }else {
            task = taskService.createTaskQuery().taskCandidateGroup(department).taskId(taskId).singleResult();
        }

        if (task == null) {
            LOGGER.error("The task not found, task id is {}", taskId);
            return false;
        }else {
            //business logic here

            //Into next step
            LOGGER.info("The taskId is {}", taskId);
            Map<String, Object> variables = new HashMap<>();

            variables.put("approved", passed);
            taskService.complete(task.getId(), variables);
            return true;
        }
    }

查看运行中的流程图

这个我也是网上找来的,需要注意的是diagramGenerator.generateDiagram()方法在 从6.4版本开始增加了drawSequenceFlowNameWithNoLabelDI(是否显示连接线名称)这个参数,drawSequenceFlowNameWithNoLabelDI默认是false,如果你的连线名称不显示,记得设置这个属性为true就好了。6.4以前的版本有这个bug无法显示连接线名称。

 @RequestMapping(value = "processDiagram")
    public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) throws Exception {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();

        //流程走完的不显示图
        if (pi == null) {
            return;
        }
        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
        //使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
        String InstanceId = task.getProcessInstanceId();
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(InstanceId)
                .list();

        //得到正在执行的Activity的Id
        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,true);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;
        try {
            out = httpServletResponse.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }
;