我的项目引入工作流引擎计划阐明

一、工作流引擎选型

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: 8080camunda:  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: falsespring:  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 */@SpringBootApplicationpublic 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 ,创立一个新的流程设计文件

编辑一个简略的流程

  1. 双击 开始 节点编辑标签,输出“付款申请”
标签能够换行,须要应用Shift +回车
  1. 点击右面显示的方框,增加一个新的流动

能够看到一个新的流动显示到画布上,双击将它命名为“刷卡付款”

  1. 点击取信用卡节点右面的扳手能够批改流动类型,这里咱们批改为Service Task(服务类型)

  1. 新增一个完结节点,并命名为“收到付款”

配置“刷卡付款”节点

服务类型有很多执行的办法,这次咱们应用“external(内部)”工作模式

  1. 点击“取信用卡”节点,在右侧的面板中批改Implementation(实现)为 External ,批改Topic为 charge-card

配置流程参数

  1. 点击画布的空白处,右侧的面板会显示以后流程自身的参数

这里咱们批改id为payment-retrieval,id是辨别流程的标识

而后批改Name 为“付款流程”

最初确保 Executable 是勾选的,只有Executable被勾选,流程能力执行

  1. 点击 File > Save File As.. 或者间接点击工具栏中的保留按钮,将流程保留到你喜爱的地位,命名为 payment.bpmn
到此第一局部完结,如果想间接获取到当初为止的进度,能够应用如下命令
git checkout -f Step-1

应用 Camunda Modeler 部署流程

点击工具栏中的部署按钮能够将以后流程部署到流程引擎,点击部署按钮,输出Deployment Name 为 “Payment” ,输出下方REST Endpointhttp://localhost:8080/engine-rest (camunda平台门路),而后点击右下角Deploy部署

如果收到胜利提醒,示意部署胜利

四、流程部署及工作启动

步骤:

1、按第三章编辑流程步骤制作流程文件,将生成的bpmn文件到camunda平台

2、创立流程实例并启动流程

3、事务处理人解决以后节点工作。

4、流程进度查问

5、历史信息查问

五、camunda-rest API阐明

Response Codes
CodeMedia typeDescription
200application/jsonRequest successful.
400application/jsonIn 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

参数:

参数类型阐明
uploadFilebpmn流程文件

响应后果:

NameTypeDescription
linksListLink to the newly created deployment with method, href and rel.
idStringThe id of the deployment.
nameStringThe name of the deployment.
sourceStringThe source of the deployment.
tenantIdStringThe tenant id of the deployment.
deploymentTimeStringThe time when the deployment was created.
deployedProcessDefinitionsObjectA 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.
deployedCaseDefinitionsObjectA 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.
deployedDecisionDefinitionsObjectA 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.
deployedDecisionRequirementsDefinitionsObjectA 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

参数:

NameDescription
idFilter by deployment id.
nameFilter by the deployment name. Exact match.
nameLikeFilter 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%).
sourceFilter by the deployment source.
withoutSourceFilter by the deployment source whereby source is equal to null.
tenantIdInFilter by a comma-separated list of tenant ids. A deployment must have one of the given tenant ids.
withoutTenantIdOnly include deployments which belong to no tenant. Value may only be true, as false is the default behavior.
includeDeploymentsWithoutTenantIdInclude deployments which belong to no tenant. Can be used in combination with tenantIdIn. Value may only be true, as false is the default behavior.
afterRestricts 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.
beforeRestricts 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.
sortBySort the results lexicographically by a given criterion. Valid values are id, name, deploymentTime and tenantId. Must be used in conjunction with the sortOrderparameter.
sortOrderSort 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.
firstResultPagination of results. Specifies the index of the first result to return.
maxResultsPagination of results. Specifies the maximum number of results to return. Will return less results if there are no more results left.

响应后果:

ameTypeDescription
idStringThe id of the deployment.
nameStringThe name of the deployment.
sourceStringThe source of the deployment.
tenantIdStringThe tenant id of the deployment.
deploymentTimeDateThe 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

参数:

NameDescription
idFilter by deployment id.
nameFilter by the deployment name. Exact match.
nameLikeFilter 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%).
sourceFilter by the deployment source.
withoutSourceFilter by the deployment source whereby source is equal to null.
tenantIdInFilter by a comma-separated list of tenant ids. A deployment must have one of the given tenant ids.
withoutTenantIdOnly include deployments which belong to no tenant. Value may only be true, as false is the default behavior.
includeDeploymentsWithoutTenantIdInclude deployments which belong to no tenant. Can be used in combination with tenantIdIn. Value may only be true, as false is the default behavior.
afterRestricts 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.
beforeRestricts 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.
sortBySort the results lexicographically by a given criterion. Valid values are id, name, deploymentTime and tenantId. Must be used in conjunction with the sortOrderparameter.
sortOrderSort 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.
firstResultPagination of results. Specifies the index of the first result to return.
maxResultsPagination of results. Specifies the maximum number of results to return. Will return less results if there are no more results left.

响应后果:

ameTypeDescription
idStringThe id of the deployment.
nameStringThe name of the deployment.
sourceStringThe source of the deployment.
tenantIdStringThe tenant id of the deployment.
deploymentTimeDateThe 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、第三方对接