共计 15613 个字符,预计需要花费 40 分钟才能阅读完成。
我的项目引入工作流引擎计划阐明
一、工作流引擎选型
1、flowable
flowable 基于 activiti6 衍生进去的版本,flowable 目前最新版本是 v6.6.0,开发团队是从 activiti 中决裂进去的,修复了一众 activiti6 的 bug,并在其根底上研发了 DMN 反对,BPEL 反对等等,绝对开源版,其商业版的性能会更弱小。以 flowable6.4.1 版本为分水岭,大力发展其商业版产品,* 开源版本保护不及时,局部性能曾经不再开源版公布,比方表单生成器(表单引擎)、历史数据同步至其余数据源、ES 等 *。Flowable 是一个应用 Java 编写的轻量级业务流程引擎,应用 Apache V2 license 协定开源。2016 年 10 月,Activiti 工作流引擎的次要开发者来到 Alfresco 公司并在 Activiti 分支根底上开启了 Flowable 开源我的项目。基于 Activiti v6 beta4 公布的第一个 Flowable release 版本为 6.0。Flowable 我的项目中包含 BPMN(Business Process Model and Notation)引擎、CMMN(Case Management Model and Notation)引擎、DMN(Decision Model and Notation)引擎、表单引擎(Form Engine)等模块。官方网站:https://flowable.com/open-sou…
2、Camunda
Camunda 基于 activiti5,所以其保留了 PVM,最新版本 Camunda7.15,放弃每年公布 2 个小版本的节奏,开发团队也是从 activiti 中决裂进去的,倒退轨迹与 flowable 类似,同时也提供了商业版,不过对于个别企业应用,开源版本也足够了,具体见:https://blog.csdn.net/wxz258/…。官方网站:https://docs.camunda.org/manu…。笔者强烈推荐 camunda 流程引擎,并在云程低代码平台中应用了 camunda, 性能和性能体现稳固。
抉择 camunda 的理由:
(1)通过压力测试验证 Camunda BPMN 引擎性能和稳定性更好。具体见:https://blog.csdn.net/wxz258/…
(2)性能比较完善,除了 BPMN,Camunda 还反对企业和社区版本中的 CMMN(案例治理)和 DMN(决策自动化)。Camunda 不仅带有引擎,还带有十分弱小的工具,用于建模,工作治理,操作监控和用户治理,所有这些都是开源的。具体见:https://blog.csdn.net/wxz258/…
3、Osworkflow
Osworkflow 是一个轻量化的流程引擎,基于状态机机制,数据库表很少,Osworkflow 提供的工作流形成元素有:步骤(step)、条件(conditions)、循环(loops)、分支(spilts)、合并(joins)等,* 但不反对会签、跳转、退回、加签等这些操作 *,须要本人扩大开发,有肯定难度,如果流程比较简单,osworkflow 是很号的抉择,笔者在 2008 年给某大型国企团体开发 OA 零碎,就是基于 Osworkflow,至今仍稳固运行,性能也很高。官方网站:http://www.opensymphony.com/o…
4、JBPM
JBPM 由 JBoss 公司开发,目前最高版本 JPBM7,不过从 JBPM5 开始曾经跟之前不是同一个产品了,JBPM5 的代码根底不是 JBPM4,而是从 Drools Flow 从新开始,* 基于 Drools Flow 技术在国内市场上用的很少,所有不倡议抉择 jBPM5 当前版本 *,jBPM4 诞生的比拟早,起初 JBPM4 创建者 Tom Baeyens 来到 JBoss 后,退出 Alfresco 后很快推出了新的基于 jBPM4 的开源工作流零碎 Activiti, * 另外 JBPM 以 hibernate 作为数据长久化 ORM 也已不是支流技术 *。笔者在 2012 年开发某团体 BPM 平台时,抉择的就是 JBPM4.4 版本,也是 4 系列的最初一个版本,进行了大量的扩大开发,才实现中国特色的流程需要。当初工夫节点抉择流程引擎,JBPM 不是最佳抉择。官方网站:https://www.jbpm.org/
5、Activiti
activiti 由 Alfresco 软件开发,目前最高版本 activiti 7。activiti 的版本比较复杂,有 activiti5、activiti6、activiti7 几个支流版本,选型时让人昏头昏脑,有必要先理解一下 activiti 这几个版本的倒退历史。activiti5 和 activiti6 的外围 leader 是 Tijs Rademakers,因为团队外部一致,在 2017 年时 Tijs Rademakers 来到团队,创立了起初的*flowable*, activiti6 以及 activiti5 代码曾经交接给了 Salaboy 团队, *activiti6 以及 activiti5 的代码官网曾经暂停保护了 *, *Salaboy 团队目前在开发 activiti7 框架,activiti7 内核应用的还是 activiti6,并没有为引擎注入更多的新个性,只是在 activiti 之外的下层封装了一些利用。论断是 activiti 审慎抉择 *。官方网站:https://www.activiti.org/
二、camunda 工作流平台搭建
Springboot2.5.4+jdk1.8+camunda7.16.0+mysql5.7
1、创立 springboot 我的项目,批改 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>loan-approval-spring-boot</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<camunda.spring-boot.version>7.16.0</camunda.spring-boot.version>
<spring-boot.version>2.5.4</spring-boot.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<mysql.version>8.0.19</mysql.version>
</properties>
<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>
<dependencies>
<!-- 流程引擎 -->
<!-- <dependency>-->
<!-- <groupId>org.camunda.bpm.springboot</groupId>-->
<!-- <artifactId>camunda-bpm-spring-boot-starter</artifactId>-->
<!-- <version>${camunda.spring-boot.version}</version>-->
<!-- </dependency>-->
<!-- Rest 服务接口,会主动加载 camunda-bpm-spring-boot-starter,所以下面的引入能够不要 -->
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-rest</artifactId>
<version>${camunda.spring-boot.version}</version>
</dependency>
<!-- web 界面模块,会主动加载 camunda-bpm-spring-boot-starter,所以下面的引入能够不要 -->
<!-- web 界面模块不必须,如果只是提供引擎服务能够不引入 -->
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId>
<version>${camunda.spring-boot.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.5</version>
</dependency>
<dependency>
<groupId>org.camunda.bpm</groupId>
<artifactId>camunda-engine-plugin-spin</artifactId>
<version>${camunda.spring-boot.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.camunda.spin/camunda-spin-dataformat-all -->
<dependency>
<groupId>org.camunda.spin</groupId>
<artifactId>camunda-spin-dataformat-all</artifactId>
<version>1.13.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
2、创立或批改 application.yaml 文件
server:
port: 8080
camunda:
bpm:
database:
type: mysql
admin-user:
id: admin
password: admin
first-name: zhou
last-name: lei
filter:
create: All tasks
#禁止主动部署 resources 上面的 bpmn 文件
auto-deployment-enabled: false
#禁止 index 跳转到 Camunda 自带的治理界面,默认 true
# webapp:
# ndex-redirect-enabled: false
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/camunda?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: root
3、创立我的项目启动类
package com.sxvbd;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author ZL
* @version 1.0
* @date 2022/1/18 16:59
*/
@SpringBootApplication
public class WebappExampleProcessApplication {public static void main(String... args) {SpringApplication.run(WebappExampleProcessApplication.class, args);
}
}
4、我的项目拜访门路
4.1 web 模块拜访门路
http://localhost:8080
4.2 RestAPI 申请门路
http://localhost:8080/engine-… 接口门路
例如:http://localhost:8080/engine-…
三、camunda modeler 搭建及应用
3.1、装置
关上下载地址 https://camunda.com/download/modeler/
下载对应零碎的版本,并解压到任意地位
执行 camunda-modeler.exe
(Windows), camunda-modeler.app
(Mac), or camunda-modeler.sh
(Linux),即可启动Camunda Modeler
3.2 汉化
1、首先 Camunda Modeler 的装置目录,camunda-modeler-4.12.0-win-x64\resources\plugins
2、git clone https://gitee.com/skay463/cam…
3、npm install
4、npm run build
5、在工具栏右侧抉择简体中文,重启 Camunda Modeler 失效
3.3 编辑流程
新建 BPMN 流程
点击 File > New File > BPMN Diagram,创立一个新的流程设计文件
编辑一个简略的流程
- 双击 开始 节点编辑标签,输出“付款申请”
标签能够换行,须要应用 Shift + 回车
- 点击右面显示的方框,增加一个新的流动
能够看到一个新的流动显示到画布上,双击将它命名为“刷卡付款”
- 点击 取信用卡 节点右面的扳手能够批改流动类型,这里咱们批改为Service Task(服务类型)
- 新增一个 完结 节点,并命名为“收到付款”
配置“刷卡付款”节点
服务类型有很多执行的办法,这次咱们应用“external(内部)”工作模式
- 点击“取信用卡”节点,在右侧的面板中批改 Implementation(实现)为
External
,批改 Topic 为charge-card
配置流程参数
- 点击画布的空白处,右侧的面板会显示以后流程自身的参数
这里咱们批改 id 为payment-retrieval,id 是辨别流程的标识
而后批改 Name 为“付款流程”
最初确保 Executable 是勾选的,只有 Executable 被勾选,流程能力执行
- 点击 File > Save File As.. 或者间接点击工具栏中的保留按钮,将流程保留到你喜爱的地位,命名为
payment.bpmn
到此第一局部完结,如果想间接获取到当初为止的进度,能够应用如下命令
git checkout -f Step-1
应用 Camunda Modeler 部署流程
点击工具栏中的部署按钮能够将以后流程部署到流程引擎,点击部署按钮,输出Deployment Name
为“Payment”,输出下方REST Endpoint
为http://localhost:8080/engine-rest
(camunda 平台门路),而后点击右下角 Deploy 部署
如果收到胜利提醒,示意部署胜利
四、流程部署及工作启动
步骤:
1、按第三章编辑流程步骤制作流程文件,将生成的 bpmn 文件到 camunda 平台
2、创立流程实例并启动流程
3、事务处理人解决以后节点工作。
4、流程进度查问
5、历史信息查问
五、camunda-rest API 阐明
Response Codes
Code | Media type | Description |
---|---|---|
200 | application/json | Request successful. |
400 | application/json | In case one of the bpmn resources cannot be parsed. See the Introduction for the error response format. |
5.1、部署
5.1.1 部署 bpmn 文件
办法:POST /deployment/create
参数:
参数 | 类型 | 阐明 |
---|---|---|
upload | File | bpmn 流程文件 |
响应后果:
Name | Type | Description |
---|---|---|
links | List | Link to the newly created deployment with method , href and rel . |
id | String | The id of the deployment. |
name | String | The name of the deployment. |
source | String | The source of the deployment. |
tenantId | String | The tenant id of the deployment. |
deploymentTime | String | The time when the deployment was created. |
deployedProcessDefinitions | Object | A JSON Object containing a property for each of the process definitions, which are successfully deployed with that deployment. The key is the process definition id, the value is a JSON Object corresponding to the process definition, which is defined in the Process Definition resource. |
deployedCaseDefinitions | Object | A JSON Object containing a property for each of the case definitions, which are successfully deployed with that deployment. The key is the case definition id, the value is a JSON Object corresponding to the case definition, which is defined in the Case Definition resource. |
deployedDecisionDefinitions | Object | A JSON Object containing a property for each of the decision definitions, which are successfully deployed with that deployment. The key is the decision definition id, the value is a JSON Object corresponding to the decision definition, which is defined in the Decision Definition resource. |
deployedDecisionRequirementsDefinitions | Object | A JSON Object containing a property for each of the decision requirements definitions, which are successfully deployed with that deployment. The key is the decision requirements definition id, the value is a JSON Object corresponding to the decision requirements definition, which is defined in the Decision Requirements Definition resource. |
测试用例:POST /deployment/create
胜利
{
"links": [
{
"method": "GET",
"href": "http://localhost:38080/rest-test/deployment/aDeploymentId",
"rel": "self"
}
],
"id": "aDeploymentId",
"name": "aName",
"source": "process application",
"deploymentTime": "2013-01-23T13:59:43.000+0200",
"tenantId": null,
"deployedProcessDefinitions": {
"aProcDefId": {
"id": "aProcDefId",
"key": "aKey",
"category": "aCategory",
"description": "aDescription",
"name": "aName",
"version": 42,
"resource": "aResourceName",
"deploymentId": "aDeploymentId",
"diagram": "aResourceName.png",
"suspended": true,
"tenantId": null,
"versionTag": null
}
},
"deployedCaseDefinitions": null,
"deployedDecisionDefinitions": null,
"deployedDecisionRequirementsDefinitions": null
}
失败
{
"type": "ParseException",
"message": "ENGINE-09005 Could not parse BPMN process. Errors: Exclusive Gateway'ExclusiveGateway_1'has outgoing sequence flow'SequenceFlow_0'without condition which is not the default flow.",
"details": {
"invoice.bpmn": {
"errors": [
{
"message": "Exclusive Gateway'ExclusiveGateway_1'has outgoing sequence flow'SequenceFlow_0'without condition which is not the default flow.",
"line": 77,
"column": 15,
"mainBpmnElementId": "ExclusiveGateway_1",
"bpmnElementIds": [
"ExclusiveGateway_1",
"SequenceFlow_0"
]
}
],
"warnings": [
{
"message": "It is not recommended to use a cancelling boundary timer event with a time cycle.",
"line": 87,
"column": 20,
"mainBpmnElementId": "BoundaryEvent_1",
"bpmnElementIds": ["BoundaryEvent_1"]
}
]
}
}
5.1.2 部署信息 List 查问
办法:GET /deployment
参数:
Name | Description |
---|---|
id | Filter by deployment id. |
name | Filter by the deployment name. Exact match. |
nameLike | Filter by the deployment name that the parameter is a substring of. The parameter can include the wildcard % to express like-strategy such as: starts with (% name), ends with (name% ) or contains (% name% ). |
source | Filter by the deployment source. |
withoutSource | Filter by the deployment source whereby source is equal to null . |
tenantIdIn | Filter by a comma-separated list of tenant ids. A deployment must have one of the given tenant ids. |
withoutTenantId | Only include deployments which belong to no tenant. Value may only be true , as false is the default behavior. |
includeDeploymentsWithoutTenantId | Include deployments which belong to no tenant. Can be used in combination with tenantIdIn . Value may only be true , as false is the default behavior. |
after | Restricts to all deployments after the given date. By default*, the date must have the format yyyy-MM-dd'T'HH:mm:ss.SSSZ , e.g., 2013-01-23T14:42:45.000+0200 . |
before | Restricts to all deployments before the given date. By default*, the date must have the format yyyy-MM-dd'T'HH:mm:ss.SSSZ , e.g., 2013-01-23T14:42:45.000+0200 . |
sortBy | Sort the results lexicographically by a given criterion. Valid values are id , name , deploymentTime and tenantId . Must be used in conjunction with the sortOrder parameter. |
sortOrder | Sort the results in a given order. Values may be asc for ascending order or desc for descending order. Must be used in conjunction with the sortBy parameter. |
firstResult | Pagination of results. Specifies the index of the first result to return. |
maxResults | Pagination of results. Specifies the maximum number of results to return. Will return less results if there are no more results left. |
响应后果:
ame | Type | Description |
---|---|---|
id | String | The id of the deployment. |
name | String | The name of the deployment. |
source | String | The source of the deployment. |
tenantId | String | The tenant id of the deployment. |
deploymentTime | Date | The date and time of the deployment. |
测试用例:GET /deployment?name=deploymentName
胜利
[
{
"id": "someId",
"name": "deploymentName",
"source": "process application",
"tenantId": null,
"deploymentTime": "2013-04-23T13:42:43.000+0200"
}
]
5.1.3 部署信息 List count 查问
办法:GET /deployment
参数:
Name | Description |
---|---|
id | Filter by deployment id. |
name | Filter by the deployment name. Exact match. |
nameLike | Filter by the deployment name that the parameter is a substring of. The parameter can include the wildcard % to express like-strategy such as: starts with (% name), ends with (name% ) or contains (% name% ). |
source | Filter by the deployment source. |
withoutSource | Filter by the deployment source whereby source is equal to null . |
tenantIdIn | Filter by a comma-separated list of tenant ids. A deployment must have one of the given tenant ids. |
withoutTenantId | Only include deployments which belong to no tenant. Value may only be true , as false is the default behavior. |
includeDeploymentsWithoutTenantId | Include deployments which belong to no tenant. Can be used in combination with tenantIdIn . Value may only be true , as false is the default behavior. |
after | Restricts to all deployments after the given date. By default*, the date must have the format yyyy-MM-dd'T'HH:mm:ss.SSSZ , e.g., 2013-01-23T14:42:45.000+0200 . |
before | Restricts to all deployments before the given date. By default*, the date must have the format yyyy-MM-dd'T'HH:mm:ss.SSSZ , e.g., 2013-01-23T14:42:45.000+0200 . |
sortBy | Sort the results lexicographically by a given criterion. Valid values are id , name , deploymentTime and tenantId . Must be used in conjunction with the sortOrder parameter. |
sortOrder | Sort the results in a given order. Values may be asc for ascending order or desc for descending order. Must be used in conjunction with the sortBy parameter. |
firstResult | Pagination of results. Specifies the index of the first result to return. |
maxResults | Pagination of results. Specifies the maximum number of results to return. Will return less results if there are no more results left. |
响应后果:
ame | Type | Description |
---|---|---|
id | String | The id of the deployment. |
name | String | The name of the deployment. |
source | String | The source of the deployment. |
tenantId | String | The tenant id of the deployment. |
deploymentTime | Date | The date and time of the deployment. |
测试用例:GET /deployment?name=deploymentName
胜利
[
{
"id": "someId",
"name": "deploymentName",
"source": "process application",
"tenantId": null,
"deploymentTime": "2013-04-23T13:42:43.000+0200"
}
]
5.1.4 删除部署信息
5.1.5 重新部署 redeploy
5.1.6 依据 ID 查问部署信息
5.1.7 部署信息资源列表查问
5.1.8 依据 ID 查问部署资源信息
5.1.9 依据 ID 查问部署资源信息(binary)
5.2、流程设计
const modeling = this.bpmnModeler.get('modeling');
const elementRegistry = this.bpmnModeler.get('elementRegistry');
let todo = data.filter(item => item.state == 1) // 已实现
let notStart = data.filter(item => item.state == 0) // 未开始
todo.forEach(item =>{let node = elementRegistry.filter(item_ => item_.id == item.key)
modeling.setColor(node[0], {stroke: 'green',});
})
notStart.forEach(item =>{let node = elementRegistry.filter(item_ => item_.id == item.key)
modeling.setColor(node[0], {stroke: 'red',});
})
5.3、工作发动解决
5.6、自定义事件
entryNode.appendChild(html)
// TODO JASON
if (entryNode.querySelector('input')) {if (entry.id === 'formKey') {entryNode.querySelector('input').addEventListener('click', function() {console.log(entry)
// 回显
let val={};val[entry.id] = "";
self.applyChanges(entry, val, entryNode)
// self._modeling.updateProperties(self._current.element,{'flowable:candidateUsers': 'userId_123'})
// console.log(entry)
v.$dialogFormKey.show(self)
})
}
// if (entry.id === 'candidateUsers' || entry.id === 'candidateStarterUsers') {if (entry.id === 'camunda:Approving' || entry.id === 'candidateStarterUsers') {entryNode.querySelector('input').addEventListener('click', function() {
// 回显
let val={};val[entry.id] = "";
self.applyChanges(entry, val, entryNode)
// self._modeling.updateProperties(self._current.element,{'flowable:candidateUsers': 'userId_123'})
// console.log(entry)
v.$dialog.show(self)
})
}
}
5.5、权限对接
5.6、历史流程跟踪
5.7、第三方对接