Spring Boot 集成 Activiti 工作流引擎 极简教程

Activiti 简介

Java 通用型工作流引擎:Activiti。

源代码:https://github.com/Activiti/Activiti

Activiti is the leading lightweight, java-centric open-source BPMN engine supporting real-world process automation needs.

Activiti 项目是一项新的基于Apache许可的开源BPM平台,从基础开始构建,旨在提供支持新的BPMN 2.0标准。

Activiti 是一种轻量级,可嵌入的BPM引擎,而且还设计适用于可扩展的云架构。 Activiti将提供宽松的 Apache 许可2.0,同时促进Activiti BPM引擎和BPMN 2.0的匹配。

Activiti 的核心服务组件

1.RepositoryService:提供一系列管理流程部署和流程定义的API。
2.RuntimeService:在流程运行时对流程实例进行管理与控制。
3.TaskService:对流程任务进行管理,例如任务提醒、任务完成和创建任务等。
4.IdentityService:提供对流程角色数据进行管理的API,这些角色数据包括用户组、用户及它们之间的关系。
5.ManagementService:提供对流程引擎进行管理和维护的服务。
6.HistoryService:对流程的历史数据进行操作,包括查询、删除这些历史数据。
7.FormService:表单服务。

Activiti应用场景:

多人协作的(或者需要动态变动)的业务流程场景。

业务流程模型 BPMN xml 配置文件

一个xml文件,activiti去解析这个文件,了解我们到底想干什么事。

test01.bpmn20.xml 实例:

<?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/processdef">

    <process id="test01" name="test01" isExecutable="true">
        <startEvent id="sid-d0c54d06-2073-4018-8381-e5e43f1b5578" name="Start"/>

        <serviceTask id="sid-0396a986-7e6b-4634-883b-1ee7db9aeb45"
                     activiti:exclusive="true"
                     name="Step1"
                     activiti:class="com.example.actividemo.ServiceTask1">
            <extensionElements>
                <activiti:field name="text1">
                    <activiti:string><![CDATA[test1]]></activiti:string>
                </activiti:field>
            </extensionElements>
        </serviceTask>

        <sequenceFlow id="sid-05a29cc5-7a04-4282-be89-4d83f943774b" sourceRef="sid-d0c54d06-2073-4018-8381-e5e43f1b5578"
                      targetRef="sid-0396a986-7e6b-4634-883b-1ee7db9aeb45"/>

        <serviceTask id="sid-c43c4370-4407-4746-893f-dcee3e4e9936"
                     activiti:exclusive="true"
                     name="Step2"
                     activiti:class="com.example.actividemo.ServiceTask2">
            <extensionElements>
                <activiti:field name="text2">
                    <activiti:string><![CDATA[test2]]></activiti:string>
                </activiti:field>
            </extensionElements>
        </serviceTask>

        <endEvent id="sid-665b6b28-9b81-4ab7-ba77-6eb50da3c810" name="End"/>
        <sequenceFlow id="sid-26493bea-97f7-44d4-b7ec-255266dcb8ab" sourceRef="sid-0396a986-7e6b-4634-883b-1ee7db9aeb45"
                      targetRef="sid-c43c4370-4407-4746-893f-dcee3e4e9936"/>
        <sequenceFlow id="sid-9f0f635b-4e97-4e95-b1fd-c9996bf659af" sourceRef="sid-c43c4370-4407-4746-893f-dcee3e4e9936"
                      targetRef="sid-665b6b28-9b81-4ab7-ba77-6eb50da3c810"/>
    </process>
</definitions>

其中,activiti:class="com.example.actividemo.ServiceTask1"activiti:class="com.example.actividemo.ServiceTask2"

对应到代码里:

package com.example.actividemo;


import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.delegate.JavaDelegate;

public class ServiceTask1 implements JavaDelegate {
    //流程变量
    private Expression text1;

    //重写委托的提交方法
    @Override
    public void execute(DelegateExecution execution) {
        System.out.println("serviceTask1 已经执行已经执行!");
        String value1 = (String) text1.getValue(execution);
        System.out.println(value1);
        execution.setVariable("var1", new StringBuffer(value1).reverse().toString());
    }
}

package com.example.actividemo;


import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.delegate.JavaDelegate;

public class ServiceTask2 implements JavaDelegate {
    //流程变量
    private Expression text2;

    //重写委托的提交方法
    @Override
    public void execute(DelegateExecution execution) {
        System.out.println("serviceTask2 已经执行已经执行!");
        String value1 = (String) text2.getValue(execution);
        System.out.println(value1);
        execution.setVariable("var1", new StringBuffer(value1).reverse().toString());
    }
}

Spring Boot 集成 Activiti 项目实战

创建 Spring Boot 工程,目录结构如下:

.
├── HELP.md
├── activi-demo.iml
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── actividemo
    │   │               ├── ActivitiController.java
    │   │               ├── ActivityConsumerService.java
    │   │               ├── ActivityConsumerServiceImpl.java
    │   │               ├── Application.java
    │   │               ├── ServiceTask1.java
    │   │               └── ServiceTask2.java
    │   └── resources
    │       ├── application.properties
    │       ├── static
    │       ├── templates
    │       └── test01.bpmn20.xml
    └── test
        └── java
            └── com
                └── example
                    └── actividemo
                        └── ApplicationTests.java

14 directories, 14 files

源代码如下:

ActivitiController

package com.example.actividemo;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/activiti")
public class ActivitiController {

    @Autowired
    private ActivityConsumerService activityConsumerService;

    /**
     * 注册流程
     *
     * @return
     */
    @RequestMapping("/createDeployment")
    public Boolean createDeployment() {
        return activityConsumerService.createDeployment();
    }

    /**
     * 启动流程
     *
     * @return
     */
    @RequestMapping("/startActivityDemo")
    public Boolean startActivityDemo() {
        return activityConsumerService.startActivityDemo("test01");
    }

    /**
     * 获取待办
     *
     * @return
     */
    @RequestMapping("/getTaskList")
    public List getTaskList() {
        return activityConsumerService.getTaskList();
    }

    /**
     * 提交
     *
     * @param taskId
     * @return
     */
    @RequestMapping("/complete")
    public boolean complete(String taskId) {
        return activityConsumerService.complete(taskId);
    }

    /**
     * 根据流程id直接结束流程
     *
     * @return
     * @paramru
     */
    @RequestMapping("/deleteProcessInstance")
    public boolean deleteProcessInstance(String runId) {
        return activityConsumerService.deleteProcessInstance(runId);
    }


}

ActivityConsumerService

package com.example.actividemo;


import java.util.List;

public interface ActivityConsumerService {
    boolean createDeployment();

    boolean startActivityDemo(String key);

    List getTaskList();

    boolean complete(String taskId);

    boolean deleteProcessInstance(String runId);
}

ActivityConsumerServiceImpl

package com.example.actividemo;


import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service("activityService")
public class ActivityConsumerServiceImpl implements ActivityConsumerService {

    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private RepositoryService repositoryService;

    /**
     * 注册一个流程
     *
     * @return
     */
    @Override
    public boolean createDeployment() {
        DeploymentBuilder builder = repositoryService.createDeployment();
        builder.addClasspathResource("test01.bpmn20.xml");
        builder.deploy();
        return true;
    }

    /**
     * 查询待办
     *
     * @return
     */
    public List getTaskList() {
        return historyService.createHistoricActivityInstanceQuery().list();
    }


    /**
     * 根据流程key开启一个流程
     *
     * @param key
     * @return
     */
    @Override
    public boolean startActivityDemo(String key) {
        ProcessInstance test01 = runtimeService.startProcessInstanceByKey(key);
        String id = test01.getId();
        System.out.println("流程id=" + id);
        return true;
    }

    /**
     * 根据任务id提交任务
     *
     * @param taskId
     * @return
     */
    @Override
    public boolean complete(String taskId) {
        taskService.complete(taskId);
        return true;
    }

    /**
     * 根据流程id直接结束流程
     *
     * @param runId
     * @return
     */
    @Override
    public boolean deleteProcessInstance(String runId) {
        runtimeService.deleteProcessInstance(runId, "结束");
        return true;
    }


}

Application

package com.example.actividemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication(exclude = {
        org.activiti.spring.boot.SecurityAutoConfiguration.class
})
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

ServiceTask1 、ServiceTask2

package com.example.actividemo;


import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.delegate.JavaDelegate;

public class ServiceTask1 implements JavaDelegate {
    //流程变量
    private Expression text1;

    //重写委托的提交方法
    @Override
    public void execute(DelegateExecution execution) {
        System.out.println("serviceTask1 已经执行已经执行!");
        String value1 = (String) text1.getValue(execution);
        System.out.println(value1);
        execution.setVariable("var1", new StringBuffer(value1).reverse().toString());
    }
}

package com.example.actividemo;


import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.delegate.JavaDelegate;

public class ServiceTask2 implements JavaDelegate {
    //流程变量
    private Expression text2;

    //重写委托的提交方法
    @Override
    public void execute(DelegateExecution execution) {
        System.out.println("serviceTask2 已经执行已经执行!");
        String value1 = (String) text2.getValue(execution);
        System.out.println(value1);
        execution.setVariable("var1", new StringBuffer(value1).reverse().toString());
    }
}

项目配置文件 application.properties

server.port=8080
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/activi_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=88888888
spring.activiti.check-process-definitions=false
spring.activiti.process-definition-location-prefix=classpath:*
spring.activiti.process-definition-location-suffixes=.bpmn20.xml

Maven 项目配置文件 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>activi-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>activi-demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <activiti.version>6.0.0</activiti.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-engine</artifactId>
            <version>${activiti.version}</version>
        </dependency>

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring</artifactId>
            <version>${activiti.version}</version>
        </dependency>

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter-basic</artifactId>
            <version>${activiti.version}</version>
        </dependency>

        <!-- Activiti 流程图 -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-common-rest</artifactId>
            <version>${activiti.version}</version>
        </dependency>
        <!-- Activiti 在线设计 -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-modeler</artifactId>
            <version>5.22.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>spring-beans</artifactId>
                    <groupId>org.springframework</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>spring-context</artifactId>
                    <groupId>org.springframework</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>spring-core</artifactId>
                    <groupId>org.springframework</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>spring-tx</artifactId>
                    <groupId>org.springframework</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>spring-web</artifactId>
                    <groupId>org.springframework</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>spring-security-config</artifactId>
                    <groupId>org.springframework.security</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>spring-security-core</artifactId>
                    <groupId>org.springframework.security</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>spring-security-crypto</artifactId>
                    <groupId>org.springframework.security</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>spring-security-web</artifactId>
                    <groupId>org.springframework.security</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>spring-webmvc</artifactId>
                    <groupId>org.springframework</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>activation</artifactId>
                    <groupId>javax.activation</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>commons-io</artifactId>
                    <groupId>commons-io</groupId>
                </exclusion>
            </exclusions>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</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-web</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-actuator</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

运行测试

先部署任务:http://127.0.0.1:8080/activiti/createDeployment

然后启动工作流实例:http://127.0.0.1:8080/activiti/startActivityDemo

运行输出:

serviceTask1 已经执行已经执行!
test1
serviceTask2 已经执行已经执行!
test2
流程id=7501

查看历史流程:http://127.0.0.1:8080/activiti/getTaskList

[
  {
    "id": "10003",
    "processInstanceId": "10001",
    "processDefinitionId": "test01:3:5044",
    "startTime": "2021-06-13T07:20:19.009+00:00",
    "endTime": "2021-06-13T07:20:19.010+00:00",
    "durationInMillis": 1,
    "deleteReason": null,
    "activityId": "sid-d0c54d06-2073-4018-8381-e5e43f1b5578",
    "activityName": "Start",
    "activityType": "startEvent",
    "executionId": "10002",
    "assignee": null,
    "taskId": null,
    "calledProcessInstanceId": null,
    "tenantId": "",
    "persistentState": {
      "executionId": "10002",
      "durationInMillis": 1,
      "endTime": "2021-06-13T07:20:19.010+00:00",
      "assignee": null,
      "deleteReason": null
    },
    "time": "2021-06-13T07:20:19.009+00:00",
    "inserted": false,
    "deleted": false,
    "updated": false
  },
  ...
,
  {
    "id": "7514",
    "processInstanceId": "7508",
    "processDefinitionId": "test01:3:5044",
    "startTime": "2021-06-13T06:47:16.733+00:00",
    "endTime": "2021-06-13T06:47:16.733+00:00",
    "durationInMillis": 0,
    "deleteReason": null,
    "activityId": "sid-665b6b28-9b81-4ab7-ba77-6eb50da3c810",
    "activityName": "End",
    "activityType": "endEvent",
    "executionId": "7509",
    "assignee": null,
    "taskId": null,
    "calledProcessInstanceId": null,
    "tenantId": "",
    "persistentState": {
      "executionId": "7509",
      "durationInMillis": 0,
      "endTime": "2021-06-13T06:47:16.733+00:00",
      "assignee": null,
      "deleteReason": null
    },
    "time": "2021-06-13T06:47:16.733+00:00",
    "inserted": false,
    "deleted": false,
    "updated": false
  }
]

工作流的数据库表数据:

数据库共28张表。一些比较重要的表有:

  1. act_ru_execution 执行对象表
  2. act_ru_task 用户任务表
  3. act_hi_actinst 活动节点历史表
  4. act_hi_procinst 流程实例历史表
  5. act_hi_taskinst 历史任务表

参考资料

https://www.cnblogs.com/qq376324789/p/12023530.html
https://www.jianshu.com/p/972613558ba1

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容