SpringBoot2.x简单集成Flowable

环境和版本

window10

java1.8

mysql8

flowable6

springboot 2.7.6

配置

使用IDEA创建一个SpringBoot项目

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>flowable-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>flowable-demo</name>
    <description>flowable-demo</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.7.6</spring-boot.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter</artifactId>
            <version>6.4.1</version>
            <exclusions>
            <!--        这里要排除mybatis,否则会覆盖mybatis-plus引入的mybatis版本        -->
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!--    mybatis-plus    -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!--    mybatis-plus generator    -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.3</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.example.flowable.demo.FlowableDemoApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

application.yml中配置

yaml 复制代码
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/flowable_demo?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&allowMultiQueries=true&nullCatalogMeansCurrent=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: qk123
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      minimum-idle: 5
      maximum-pool-size: 15
      auto-commit: true
      idle-timeout: 30000
      pool-name: DatebookHikariCP
      max-lifetime: 1800000
      connection-timeout: 30000
      connection-test-query: SELECT 1

  # jackson 配置
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    locale: zh
    time-zone: GMT+8

#server.servlet.context-path=/
# swagger2使用,不配置这个项目报错 Failed to start bean 'documentationPluginsBootstrapper'
flowable:
  # 第一次改为true,创建完数据库表结构后,改为false
  database-schema-update: true
  async-executor-activate: false
server:
  port: 11000
# 设置flowable日志级别
logging:
  level:
    org.flowable: debug
#spring.mvc.pathmatch.matching-strategy=ant-path-matcher

再创建一个数据库

然后运行

数据库中会自动生成表结构

表结构位置

常用的类

flowable的autoconfig包已经自动配置好了需要的类

直接@Resource就可以使用

使用

部署一个简单流程

java 复制代码
import com.example.flowable.demo.controller.vo.DefinitionRequest;
import com.example.flowable.demo.vo.R;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.impl.bpmn.deployer.ResourceNameUtil;
import org.flowable.engine.repository.DeploymentBuilder;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ModelQuery;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.List;


@Slf4j
@RestController
@RequestMapping("/modeler")
public class ModelerController {
    @Resource
    private RepositoryService repositoryService;

    /**
     * 部署流程
     * @param definitionRequest
     * @return
     */
    @PutMapping("/deploy")
    public R deployModeler(@RequestBody DefinitionRequest definitionRequest) {
        String xmlDefinition = definitionRequest.getXmlDefinition();
        DeploymentBuilder deployment = repositoryService.createDeployment();
        byte[] bytes = xmlDefinition.getBytes(StandardCharsets.UTF_8);
        int i = RandomUtils.nextInt();
        String key = "demo_flow_" + i;
        String name = "示例流程";
        String category = "hello_" + i;
        // 流程定义的名称,必须是特定的结尾,否则不会解析
        String resourceName = "demo_flow_name_" + i + "." + ResourceNameUtil.BPMN_RESOURCE_SUFFIXES[0];
        String id = deployment.addBytes(resourceName, bytes)
                .key(key)
                .category(category)
                .name(name)
                .deploy()
                .getId();
        log.info("部署后id为:{}", id);
        Model model = repositoryService.newModel();
        model.setDeploymentId(id);
        model.setCategory("model_" + category);
        model.setKey("model_key_" + i);
        model.setName("model_name_" + i);
        model.setVersion(1);
        repositoryService.saveModel(model);
        log.info("模型保存后id:{}", model.getId());
        // 设置模型可编辑资源
        repositoryService.addModelEditorSource(model.getId(), bytes);
        return R.success(id);
    }

    /**
     * 查看流程模型列表
     * @return
     */
    @GetMapping("/list")
    public R list() {
        ModelQuery modelQuery = repositoryService.createModelQuery();
        List<Model> list = modelQuery.list();
        return R.success();
    }

}

用postman发送一个请求

json 复制代码
{
  "xmlDefinition": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<definitions xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\"\n             xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n             xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n             xmlns:bpmndi=\"http://www.omg.org/spec/BPMN/20100524/DI\"\n             xmlns:omgdc=\"http://www.omg.org/spec/DD/20100524/DC\"\n             xmlns:omgdi=\"http://www.omg.org/spec/DD/20100524/DI\"\n             xmlns:flowable=\"http://flowable.org/bpmn\"\n             typeLanguage=\"http://www.w3.org/2001/XMLSchema\"\n             expressionLanguage=\"http://www.w3.org/1999/XPath\"\n             targetNamespace=\"http://www.flowable.org/processdef\">\n\n    <process id=\"holidayRequest\" name=\"Holiday Request\" isExecutable=\"true\">\n\n        <startEvent id=\"startEvent\"/>\n        <sequenceFlow sourceRef=\"startEvent\" targetRef=\"approveTask\"/>\n\n        <userTask id=\"approveTask\" name=\"Approve or reject request\"/>\n        <sequenceFlow sourceRef=\"approveTask\" targetRef=\"decision\"/>\n\n        <exclusiveGateway id=\"decision\"/>\n        <sequenceFlow sourceRef=\"decision\" targetRef=\"externalSystemCall\">\n            <conditionExpression xsi:type=\"tFormalExpression\">\n                <![CDATA[\n    ${approved}    ]]>\n            </conditionExpression>\n        </sequenceFlow>\n        <sequenceFlow  sourceRef=\"decision\" targetRef=\"sendRejectionMail\">\n            <conditionExpression xsi:type=\"tFormalExpression\">\n                <![CDATA[\n    ${approved}    ]]>\n            </conditionExpression>\n        </sequenceFlow>\n\n        <serviceTask id=\"externalSystemCall\" name=\"Enter holidays in external system\"\n                     flowable:class=\"org.flowable.CallExternalSystemDelegate\"/>\n        <sequenceFlow sourceRef=\"externalSystemCall\" targetRef=\"holidayApprovedTask\"/>\n\n        <userTask id=\"holidayApprovedTask\" name=\"Holiday approved\"/>\n        <sequenceFlow sourceRef=\"holidayApprovedTask\" targetRef=\"approveEnd\"/>\n\n        <serviceTask id=\"sendRejectionMail\" name=\"Send out rejection email\"\n                     flowable:class=\"org.flowable.SendRejectionMail\"/>\n        <sequenceFlow sourceRef=\"sendRejectionMail\" targetRef=\"rejectEnd\"/>\n\n        <endEvent id=\"approveEnd\"/>\n\n        <endEvent id=\"rejectEnd\"/>\n    </process>\n\n</definitions>"
}

在数据库中会出现对应的数据

接着发起流程

java 复制代码
import com.example.flowable.demo.controller.vo.DefinitionRequest;
import com.example.flowable.demo.controller.vo.ProcessDefinitionResp;
import com.example.flowable.demo.vo.R;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/process")
public class ProcessController {
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private RepositoryService repositoryService;

    /**
     * 查看流程定义列表
     * @return
     */
    @GetMapping("/list")
    public R list() {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        List<ProcessDefinition> list = processDefinitionQuery.list();
        log.info("得到流程定义数量:{}", list.size());
        // ProcessDefinition无法序列化,需要自己转
        List<ProcessDefinitionResp> respList = new ArrayList<>();
        for (ProcessDefinition processDefinition : list) {
            respList.add(ProcessDefinitionResp.copy(processDefinition));
        }
        return R.success(respList);
    }

    /**
     * 启动流程
     * @param definitionRequest
     * @return
     */
    @PutMapping("/create")
    public R create(@RequestBody DefinitionRequest definitionRequest) {
        String deploymentId = definitionRequest.getDeploymentId();
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        ProcessDefinition processDefinition = processDefinitionQuery.deploymentId(deploymentId).singleResult();
        Map<String, Object> variables = new HashMap<>();
        variables.put("employee","张三") ;// 谁申请请假
        variables.put("nrOfHolidays",3); // 请几天假
        variables.put("description","工作累了,想出去玩玩"); // 请假的原因
        ProcessInstance holidayRequest = runtimeService.startProcessInstanceByKey("holidayRequest", variables);
        String id = holidayRequest.getId();
        log.info("启动的流程实例id:{}, 流程定义id:{}", id, processDefinition.getId());
        return R.success(id);
    }
}

查看任务

java 复制代码
import com.example.flowable.demo.controller.vo.TaskResp;
import com.example.flowable.demo.vo.R;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.TaskService;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@RestController
@RequestMapping("/task")
public class TaskController {
    @Resource
    private TaskService taskService;

    /**
     * 查看所有待办任务
     * @return
     */
    @GetMapping("/list")
    public R list() {
        TaskQuery taskQuery = taskService.createTaskQuery();
        List<Task> list = taskQuery.list();
        List<TaskResp> list1 = new ArrayList<>();
        for (Task task : list) {
            list1.add(TaskResp.copy(task));
        }
        return R.success(list1);
    }
}

其他类

java 复制代码
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

import java.io.Serializable;


@Getter
@Setter
@Accessors(chain = true)
public class R implements Serializable {
    private int code;
    private String message;
    private Object data;

    public static R gen(int code, String message, Object data) {
        return new R().setCode(code).setMessage(message).setData(data);
    }
    public static R success() {
        return R.success(null);
    }
    public static R success(Object data) {
        return R.success("请求成功", data);
    }
    public static R success(String message, Object data) {
        return R.gen(0, message, data);
    }
    public static R fail() {
        return R.fail(null);
    }
    public static R fail(Object data) {
        return R.fail("请求失败", data);
    }
    public static R fail(String message, Object data) {
        return R.gen(-1, message, data);
    }
}
java 复制代码
import lombok.Getter;
import lombok.Setter;

/**
 * 流程定义请求
 */
@Getter
@Setter
public class DefinitionRequest {
    // 流程xml定义,部署流程用
    private String xmlDefinition;
    // 部署id,创建流程用
    private String deploymentId;
}
java 复制代码
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.flowable.engine.repository.ProcessDefinition;

import java.io.Serializable;

/**
 * 流程定义
 */
@Getter
@Setter
@Accessors(chain = true)
public class ProcessDefinitionResp implements Serializable {
    String id;
    String category;
    String name;
    String key;
    String description;
    int version;
    String resourceName;
    String deploymentId;
    String diagramResourceName;
    boolean hasStartFormKey;
    boolean hasGraphicalNotation;
    boolean suspended;
    String tenantId;
    String derivedFrom;
    String derivedFromRoot;
    int derivedVersion;
    String engineVersion;
    public static ProcessDefinitionResp copy(ProcessDefinition processDefinition) {
        ProcessDefinitionResp definitionResp = new ProcessDefinitionResp();
        definitionResp.setId(processDefinition.getId());
        definitionResp.setCategory(processDefinition.getCategory());
        definitionResp.setName(processDefinition.getName());
        definitionResp.setKey(processDefinition.getKey());
        definitionResp.setDescription(processDefinition.getDescription());
        definitionResp.setVersion(processDefinition.getVersion());
        definitionResp.setResourceName(processDefinition.getResourceName());
        definitionResp.setDeploymentId(processDefinition.getDeploymentId());
        definitionResp.setDiagramResourceName(processDefinition.getDiagramResourceName());
        definitionResp.setHasStartFormKey(processDefinition.hasStartFormKey());
        definitionResp.setHasGraphicalNotation(processDefinition.hasGraphicalNotation());
        definitionResp.setSuspended(processDefinition.isSuspended());
        definitionResp.setTenantId(processDefinition.getTenantId());
        definitionResp.setDerivedFrom(processDefinition.getDerivedFrom());
        definitionResp.setDerivedFromRoot(processDefinition.getDerivedFromRoot());
        definitionResp.setDerivedVersion(processDefinition.getDerivedVersion());
        definitionResp.setEngineVersion(processDefinition.getEngineVersion());
        return definitionResp;
    }
}
java 复制代码
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.flowable.task.api.Task;

import java.io.Serializable;
import java.util.*;

/**
 * 任务
 */
@Getter
@Setter
@Accessors(chain = true)
public class TaskResp implements Serializable {

    String id;
    String name;
    String description;
    int priority;
    String owner;
    String assignee;
    String processInstanceId;
    String executionId;
    String taskDefinitionId;
    String processDefinitionId;
    String scopeId;
    String subScopeId;
    String scopeType;
    String scopeDefinitionId;
    Date createTime;
    String taskDefinitionKey;
    Date dueDate;
    String category;
    String parentTaskId;
    String tenantId;
    String formKey;
    Map<String, Object> taskLocalVariables;
    Map<String, Object> processVariables;
    List<IdentityLinkInfoResp> identityLinks;
    Date claimTime;

    public static TaskResp copy(Task task) {
        TaskResp taskResp = new TaskResp();
        taskResp.setId(task.getId());
        taskResp.setName(task.getName());
        taskResp.setDescription(task.getDescription());
        taskResp.setPriority(task.getPriority());
        taskResp.setOwner(task.getOwner());
        taskResp.setAssignee(task.getAssignee());
        taskResp.setProcessInstanceId(task.getProcessInstanceId());
        taskResp.setExecutionId(task.getExecutionId());
        taskResp.setTaskDefinitionId(task.getTaskDefinitionId());
        taskResp.setProcessDefinitionId(task.getProcessDefinitionId());
        taskResp.setScopeId(task.getScopeId());
        taskResp.setSubScopeId(task.getSubScopeId());
        taskResp.setScopeType(task.getScopeType());
        taskResp.setScopeDefinitionId(task.getScopeDefinitionId());
        taskResp.setCreateTime(task.getCreateTime());
        taskResp.setTaskDefinitionKey(task.getTaskDefinitionKey());
        taskResp.setDueDate(task.getDueDate());
        taskResp.setCategory(task.getCategory());
        taskResp.setParentTaskId(task.getParentTaskId());
        taskResp.setTenantId(task.getTenantId());
        taskResp.setFormKey(task.getFormKey());
        taskResp.setTaskLocalVariables(task.getTaskLocalVariables());
        taskResp.setProcessVariables(task.getProcessVariables());
//        List<? extends IdentityLinkInfo> identityLinks1 = task.getIdentityLinks();
        taskResp.setIdentityLinks(new ArrayList<>());
//        if (Objects.nonNull(identityLinks1) && !identityLinks1.isEmpty()) {
//            for (IdentityLinkInfo identityLinkInfo : identityLinks1) {
//                taskResp.getIdentityLinks().add(IdentityLinkInfoResp.copy(identityLinkInfo));
//            }
//        }
        taskResp.setClaimTime(task.getClaimTime());
        return taskResp;
    }
}

Flowable基本的模块

Modeler 模型

Process 流程

Task 任务

模型部署后,就是流程定义。

从流程定义创建流程实例。

流程实例中有多个任务,任务有很多种类。

相关推荐
ZGi.ai8 小时前
知识库权限变了,怎样避免答错?
agent·权限管理·知识库·工作流·zgi·客服运营
ZGi.ai14 小时前
工作流失败后,怎样快速定位?
工作流·故障排查·知识管理·模型管理·zgi·agentruntime
凤山老林1 天前
Spring Boot 整合 Flowable 的企业级落地指南
数据库·spring boot·后端·flowable·工作流
ShallWeL4 天前
【Agent工程】(15)—— 评测集与回归门禁
人工智能·agent·工作流
AI工具人PM产品经理5 天前
Claude Code TDD 实战:84 个测试护体的 spec→plan 开发流
jest·tdd·工作流·ai 编程·claude code
ShallWeL7 天前
RAG Embedding 模型替换与索引回归
人工智能·embedding·知识库·工作流·rag
RuoyiOffice8 天前
SpringBoot3+Vue3 最推荐的开源 OA 系统 2026:流程驱动、资源闭环、多端互通
spring boot·vue3·flowable·oa·协同办公·spring boot 3·开源oa
2601_962097369 天前
Python工作流实战:SpiffWorkflow深度应用与BPMN自动化指南
python·自动化·工作流·bpmn·spiffworkflow
ShallWeL9 天前
RAG 向量索引重建与回归
人工智能·agent·知识库·工作流·rag
阿图灵13 天前
LangGraph 实战 03:Workflows 与 Agents——六种工作流模式与智能体实战(附 6 个可运行示例)
java·前端·javascript·工作流·ai agent·智能体·langgraph