我的项目引入工作流引擎计划阐明
一、工作流引擎选型
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 ,创立一个新的流程设计文件
编辑一个简略的流程
- 双击 开始 节点编辑标签,输出“付款申请”
标签能够换行,须要应用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) }) } }