am928 发表于 5 天前

安全管理平台工单申请流程催生工作流引擎开发及经验分享

最近开发的安全管理平台增添了诸多工单申请流程需求,像加白申请、开通申请等。最初的两个需求,为图便利,没作过多考虑,便径直开发了相应的业务代码。

但同类需求持续增多,感觉照这样写会累死人,于是踏上工作流引擎开发之路。查找一些资料后,开发出现阶段的工作流引擎,文章后面会予以介绍

现在基本能满足日常需求。但感觉不够智能。还有很多优化空间。所以借此机会。详细了解了一些完善的工作流引擎框架。以及架构设计上需注意的点。形成了这篇文章。分享给大家。

什么是工作流

先看一下维基百科对于工作流的定义:

工作流,是对工作流程以及各操作步骤间业务规则的抽象概括性描述。工作流建模,就是把工作流程里工作前后组织在一起的逻辑与规则,在计算机里用恰当模型表达出来并实施计算。

工作流要解决的主要问题是:为了达成某个业务目标。利用计算机来自动传递文档。利用计算机来自动传递信息。利用计算机来自动传递任务。是在多个参与者之间按照某种预定规则进行传递。

简而言之工作流是对业务的流程化抽象WFMC即工作流程管理联盟给出了如下工作流参考模型

举个例子 公司办公用的OA系统 存在大量申请审批流程 处理这些流程时 若每个流程都对应一套代码 显然不现实 会造成很大程度的代码冗余 开发工作量也会骤增

这时需要一个与业务无关的引擎。该引擎高度抽象且封装。它能统一处理。借助这个引擎。可灵活配置工作流程。还能依据配置自动进行状态变更与流程流转。这便是工作流引擎。

简单的工作流

那么,一个工作流引擎需要支持哪些功能呢?

这个问题不存在标准答案。需依据实际业务场景与需求展开分析。在此处,我借助工单流程的演进。该演进是从简单到复杂的。我会循序渐进地介绍都包含哪些基础功能。

最简单流程

有一个流程工单很简单。申请人发起这个流程。每个节点都有审批人。审批人会逐个进行审批。最终这个流程结束。

会签

在这个过程中,节点分成了两大类:简单节点和复杂节点。

简单节点处理逻辑维持不变。处理完后会自动前往下一个节点。复杂节点例如会签节点则不一样。需其下所有子节点都处理完毕。才能够到达下一个节点。

并行

同样属于复杂节点。其任何一个子节点处理完毕后。便都能够进入到下一个节点。

条件判断

需要根据不同的表单内容进入不同的分支流程。

举个例子。比如进行休假申请时。请假一天。需直属领导审批。若请假天数大于三天。则需部门领导审批。

动态审批人

审批节点的审批人需要动态获取,并且可配置。

审批人的获取方式可以分以下几种:

固定审批人从申请表单中获取信息。该信息依据组织架构。动态获取是从配置的角色组或者权限组中进行。还能获取撤销和驳回操作。

节点状态变更存在多种情况。其一,申请人可撤回。其二,审批人能同意。其三,审批人可驳回。当驳回时,存在两种情况。一种是直接驳回到开始节点,此时流程结束。另一种是到上一个节点。更复杂的是,甚至能到前面流程的任意一个节点

自动化节点

有一些节点无需人工参与。比如联动其他系统自动处理。或者审批节点有时间限制。超时便自动失效。

个性化通知

节点审批之后,可以配置不同的通知方式来通知相关人。

以上是我所列举的一些较为常见的需求点。还有加签、代理、脚本执行等功能。若这些功能都实现,将会是一项庞大的工作量。当然,要是目标是打造一个商业化产品,那么功能需更加丰富。

但要是把这些常见的需求点都实现。那么应该基本能够满足大部分需求。至少就我们系统的工单流程而言。目前是能够满足的。

工作流引擎对比

这是常见需求。那需我们自己开发吗?市面上有可使用的开源项目吗?

答案是肯定的。当前,市场上较知名的开源流程引擎有多个。其中,Jbpm等四个框架同宗同源。它们的祖先是Jbpm4。开发者只要用过其中一个框架。基本上就会用其他三个。

它是轻量化流程引擎。基于状态机机制。数据库表数量少。其提供的工作流构成元素包含步骤、条件、循环、分支、合并等。不过它不支持会签、跳转、退回、加签等操作。若有这些需求需自行扩展开发。存在一定难度。

如果流程比较简单, 是一个很不错的选择。

JBPM

JBPM 是由 JBoss 公司研发的。其当前的最高版本为 JPBM7。然而,自 JBPM5 起,它就与之前不同了。JBPM5 的代码基础并非 JBPM4,而是重新基于 Flow 构建。Flow 技术在国内市场应用较少。所以,不建议选用 JBPM5 之后的版本。

JBPM4出现得较早。后来JBPM4的创建者Tom离开JBoss。他加入后很快推出了新的基于JBPM4的开源工作流系统。另外,JBPM将其作为数据持久化的ORM也已不是主流技术。

软件开发的目前最高版本,其版本较为复杂,存在几个主流版本,选型时令人晕头转向,有必要先了解一下这几个版本的发展历史。

和的核心人物是Tijs。因团队内部分歧,2017年Tijs离开团队,创建了后来的。以及代码已交接给团队。并且的代码官方已暂停维护。

团队当下正在开展框架的开发工作。内核运用的仍是原来的。没有给引擎增添更多新的特性。仅仅是在其之外的上层部分进行了一些应用的封装。

它是用Java编写的轻量级业务流程引擎,以V2协议开源。2016年10月,工作流引擎的主要开发者离开公司,在分支基础上开启了开源项目。基于v6 beta4发布的首个版本是6.0

项目里包含BPMN(Model and)引擎。还包含CMMN(Case Model and)引擎。也包含DMN(Model and)引擎。以及表单引擎(Form)等模块。

其商业版功能比开源版更强大。以.4.1版本为界限,大力发展商业版产品。开源版本维护不及时。部分功能不再在开源版发布。例如表单生成器(表单引擎)。历史数据同步至其他数据源。还有ES等。

基于某种原因,所以它保留了PVM,其最新版本是.15,保持每年发布两个小版本的节奏。它的开发团队是从某个地方分裂出来的,发展轨迹与另一个事物相似。同时它也提供了商业版,不过对于一般企业应用而言,开源版本就足够了。

以上便是每个项目的大致介绍。接下来着重对比Jbpm等。仅看文字或许对它们间的关系尚不清晰。故而我绘制了一张图。其能更明晰地展现每个项目的发展轨迹。

那么若要挑选其中一个项目来使用该如何选择呢?我列出了几项自己较为关注的要点。还制作了一张对比表格。如下:

7

6

JBPM 7

流程协议

BPMN2.0、XPDL、PDL

BPMN2.0、XPDL、XPDL

BPMN2.0、XPDL、XPDL

BPMN2.0

开源情况

开源

商业和开源版

商业和开源版

开源

开发基础

JBPM4

5 & 6

5

版本 5 之后Flow

数据库

、SQL 、MySQL

、SQL 、MySQL、

、SQL 、MySQL、

MySQL,

架构

boot 2

boot 1.5

boot 2

Kie

运行模式

独立运行和内嵌

独立运行和内嵌

独立运行和内嵌

流程设计器

bpmn.js

活跃度

活跃

相对活跃

相对活跃

表数量

引入 25 张表

引入 47 张表

引入 19 张表

jar 包数量

引入 10 个 jar

引入 37 个 jar

引入 15 个 jar

应用举例

要是选择借助开源项目去开发自身的引擎,或者嵌入到已有的项目里,那该怎么使用呢?在此通过来举例阐释

使用存在两种方式。一种是内嵌方式。另一种是独立部署方式。现在对这两种方式分别进行说明。

内嵌模式创建 maven 工程

先创建一个普通的maven工程。添加引擎的依赖。添加h2内嵌数据库的依赖。也能够使用MySQL数据库来进行持久化。

<p><pre class="syl-page-code">    <code>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-engine</artifactId>
<version>6.7.2</version>
</dependency>
<dependency>
https://img0.baidu.com/it/u=1554643986,2476707765&fm=253&fmt=JPEG&app=138&f=JPEG?w=500&h=643

<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.192</version>
</dependency>
复制代码</code></pre></p>
创建流程引擎实例

<p><pre class="syl-page-code">    <code>导入 org.flowable.engine.ProcessEngine
导入 org.flowable.engine.ProcessEngineConfiguration
导入 org.flowable.engine.impl.cfg 包下的 StandaloneProcessEngineConfiguration 类
public class HolidayRequest {
公共静态无效主函数(字符串数组参数) {
它使用独立流程引擎配置类进行实例化
设置JDBC连接URL为“jdbc:h2:mem:flowable;DB_CLOSE_DELAY=-1”
      .setJdbcUsername("sa")
      .setJdbcPassword("")
设置JDBC驱动为“org.h2.Driver”
设置数据库模式更新为(流程引擎配置中的)数据库模式更新为真
创建过程由cfg.buildProcessEngine()完成
}
}
复制代码</code></pre></p>
接下来 我们能够往这个引擎实例部署一个流程 xml 比如说 我们要建立一个员工请假流程

<p><pre class="syl-page-code">    <code><?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:activiti="http://activiti.org/bpmn"
             typeLanguage="http://www.w3.org/2001/XMLSchema"
             expressionLanguage="http://www.w3.org/1999/XPath"
             targetNamespace="http://www.flowable.org/processdef">
    <process id="holidayRequest" name="Holiday Request" isExecutable="true">
      <startEvent id="startEvent"/>
      <sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>
      <userTask id="approveTask" name="Approve or reject request" activiti:candidateGroups="managers"/>
      <sequenceFlow sourceRef="approveTask" targetRef="decision"/>
      <exclusiveGateway id="decision"/>
      <sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[
          ${approved}
      ]]>
            </conditionExpression>
      </sequenceFlow>
      <sequenceFlow sourceRef="decision" targetRef="sendRejectionMail">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[
          ${!approved}
      ]]>
            </conditionExpression>
      </sequenceFlow>
      <serviceTask id="externalSystemCall" name="Enter holidays in external system"
                     activiti:class="org.example.CallExternalSystemDelegate"/>
      <sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>
      <userTask id="holidayApprovedTask" name="Holiday approved" activiti:assignee="${employee}"/>
      <sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>
      <serviceTask id="sendRejectionMail" name="Send out rejection email"
                     activiti:class="org.flowable.SendRejectionMail"/>
      <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>
      <endEvent id="approveEnd"/>
      <endEvent id="rejectEnd"/>
    </process>
</definitions>
复制代码</code></pre></p>
此xml是一种标准格式。它符合bpmn2.0规范。其对应的流程图如下:

接下来 我们把这个文件传给流程引擎 让流程引擎依据该文件 创建一个工作流

<p><pre class="syl-page-code">    <code>获取流程引擎的仓库服务。将其赋值给仓库服务变量。
通过repositoryService调用createDeployment方法来实现
添加类路径资源(“holiday-request.bpmn20.xml”)
.deploy();
复制代码</code></pre></p>
创建之后 实际上就写入内存数据库h2了 我们还能够将其查出来

<p><pre class="syl-page-code">    <code>ProcessDefinition processDefinition 等于 repositoryService 创建流程定义查询
部署 ID(通过部署获取其 ID)
.singleResult();
System.out.println("找到流程定义 : " + processDefinition.getName()); 可改写为 System.out.println("已发现流程定义 : " + processDefinition.getName()); 或 System.out.println("寻得流程定义 : " + processDefinition.getName());或 System.out.println("察觉到流程定义 : " + processDefinition.getName());或 System.out.println("发觉流程定义 : " + processDefinition.getName());或 System.out.println("知晓流程定义 : " + processDefinition.getName());或 System.out.println("获悉流程定义 : " + processDefinition.getName());或 System.out.println("得知流程定义 : " + processDefinition.getName());或 System.out.println("了解到流程定义 : " + processDefinition.getName());或 System.out.println("知道有流程定义 : " + processDefinition.getName());或 System.out.println("发现存在流程定义 : " + processDefinition.getName());或 System.out.println("找到有流程定义 : " + processDefinition.getName());或 System.out.println("寻得有流程定义 : " + processDefinition.getName());或 System.out.println("察觉到有流程定义 : " + processDefinition.getName());或 System.out.println("发觉有流程定义 : " + processDefinition.getName());或 System.out.println("知晓有流程定义 : " + processDefinition.getName());或 System.out.println("获悉有流程定义 : " + processDefinition.getName());或 System.out.println("得知有流程定义 : " + processDefinition.getName());或 System.out.println("了解到有流程定义 : " + processDefinition.getName());或 System.out.println("知道存在流程定义 : " + processDefinition.getName());或 System.out.println("发现流程定义如下 : " + processDefinition.getName());或 System.out.println("找到流程定义如下 : " + processDefinition.getName());或 System.out.println("寻得流程定义如下 : " + processDefinition.getName());或 System.out.println("察觉到流程定义如下 : " + processDefinition.getName());或 System.out.println("发觉流程定义如下 : " + processDefinition.getName());或 System.out.println("知晓流程定义如下 : " + processDefinition.getName());或 System.out.println("获悉流程定义如下 : " + processDefinition.getName());或 System.out.println("得知流程定义如下 : " + processDefinition.getName());或 System.out.println("了解到流程定义如下 : " + processDefinition.getName());或 System.out.println("知道有如下流程定义 : " + processDefinition.getName());或 System.out.println("发现存在如下流程定义 : " + processDefinition.getName());或 System.out.println("找到有如下流程定义 : " + processDefinition.getName());或 System.out.println("寻得有如下流程定义 : " + processDefinition.getName());或 System.out.println("察觉到有如下流程定义 : " + processDefinition.getName());或 System.out.println("发觉有如下流程定义 : " + processDefinition.getName());或 System.out.println("知晓有如下流程定义 : " + processDefinition.getName());或 System.out.println("获悉有如下流程定义 : " + processDefinition.getName());或 System.out.println("得知有如下流程定义 : " + processDefinition.getName());或 System.out.println("了解到有如下流程定义 : " + processDefinition.getName());或 System.out.println("知道存在如下流程定义 : " + processDefinition.getName());或 System.out.println("打印流程定义名称 : " + processDefinition.getName());或 System.out.println("输出流程定义名称 : " + processDefinition.getName());或 System.out.println("显示流程定义名称 : " + processDefinition.getName());或 System.out.println("呈现流程定义名称 : " + processDefinition.getName());或 System.out.println("展示流程定义名称 : " + processDefinition.getName());或 System.out.println("给出流程定义名称 : " + processDefinition.getName());或 System.out.println("提供流程定义名称 : " + processDefinition.getName());或 System.out.println("告知流程定义名称 : " + processDefinition.getName());或 System.out.println("说明流程定义名称 : " + processDefinition.getName());或 System.out.println("阐述流程定义名称 : " + processDefinition.getName());或 System.out.println("讲述流程定义名称 : " + processDefinition.getName());或 System.out.println("表明流程定义名称 : " + processDefinition.getName());或 System.out.println("指明流程定义名称 : " + processDefinition.getName());或 System.out.println("标示流程定义名称 : " + processDefinition.getName());或 System.out.println("标记流程定义名称 : " + processDefinition.getName());
复制代码</code></pre></p>
创建工作流实例

创建工作流实例时需要提供一些输入参数比如创建员工请假流程参数要有员工姓名要有请假天数还要有事由等

<p><pre class="syl-page-code">    <code>创建一个Scanner对象。该对象用于从系统输入中读取数据。此对象名为scanner 。它是通过new Scanner(System.in)来实例化的
系统输出打印“你是谁?”
读取一行输入内容并赋值给名为employee的字符串变量
System.out.println("你想要申请多少天假期?");
将从扫描器读取的下一行内容转换为整型数值并赋值给nrOfHolidays
系统输出打印“你为什么需要它们?”
String description获取扫描器输入的下一行内容
运行时服务对象runtimeService通过流程引擎获取得到。运行时服务对象runtimeService由流程引擎的getRuntimeService方法获取。
Map<String, Object> variables = new HashMap<String, Object>();
存入的值为employee
放入的值为nrOfHolidays
变量.put("description", 描述);(注:“变量”和“描述”需根据具体语境替换为准确表述,因不清楚具体指代内容,这里保留英文形式以示意改写方式)
复制代码</code></pre></p>
参数准备好后,就可以传给工作流了:

<p><pre class="syl-page-code">    <code>流程实例 流程实例等于
运行时服务通过流程定义键“holidayRequest”启动流程实例并传入变量
复制代码</code></pre></p>
此时,就会根据流程定义里的:

<p><pre class="syl-page-code">    <code><userTask id="approveTask" name="Approve or reject request" activiti:candidateGroups="managers"/>
复制代码</code></pre></p>
https://img1.baidu.com/it/u=1885487755,2988513891&fm=253&fmt=JPEG&app=138&f=JPEG?w=800&h=1131

创建一个任务。任务有个标签。这里的标签能让人猜得出。是给建了个审批任务。

查询并审批任务

基于查询任务:

<p><pre class="syl-page-code">    <code>获取流程引擎的任务服务。将其赋值给TaskService类型的taskService。
List<Task>任务 = 任务服务创建任务查询。任务候选组为“经理们”。列出结果。
System.out.println("个任务:");
for (int i=0; i<tasks.size(); i++) {
System.out.println((i+1) + ") " + tasks.get(i).getName());
}
复制代码</code></pre></p>
审批任务:

<p><pre class="syl-page-code">    <code>若等于则将布尔变量approved赋值为true
variables = new HashMap<String, Object>();
变量存入“approved”,其值为已批准的状态
任务服务完成任务。任务通过其标识获取。同时传入变量。
复制代码</code></pre></p>
这里将全局变量设为了true 。之后提交给引擎 。引擎会依据这里变量是true还是false 。选择走不同分支 。如下:

<p><pre class="syl-page-code">    <code><sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
    <conditionExpression xsi:type="tFormalExpression">
      <![CDATA[
${approved}
]]>
    </conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="decision" targetRef="sendRejectionMail">
    <conditionExpression xsi:type="tFormalExpression">
      <![CDATA[
${!approved}
]]>
    </conditionExpression>
</sequenceFlow>
复制代码</code></pre></p>
回调用户代码

审批后,就会进入下一个节点:

<p><pre class="syl-page-code">    <code><serviceTask id="externalSystemCall" name="Enter holidays in external system"
             activiti:class="org.example.CallExternalSystemDelegate"/>
复制代码</code></pre></p>
这里有个 class,就是需要我们自己实现的:

最后,流程就走完结束了。

REST API 模式

它以一个jar的形式存在。被内嵌到我们的程序之中。创建引擎实例后。由我们的业务程序驱动引擎运行。引擎与业务代码处于同一个进程里。

第二种方式。它能作为独立服务运行。还能提供REST API接口。如此一来。非Java语言开发的系统也可使用该引擎。

这个只需我们下载官方的zip包。包里有个rest的war包。该war包可直接放到里运行。

部署工作流

在这种方式中 若要达成上面所举例子的员工请假流程 能够借助调接口来达成

启动工作流:

其他接口就不一一展示了,可以参考官方文档。

通过页面进行流程建模

截至目前 创建工作流程靠建立xml来实现 这极为不便 所以 系统提供通过页面可视化方式创建流程 用鼠标拖拽相应组件就能完成

但是体验下来颇为辛苦。功能众多。名词更是繁多。有好多都不知啥意思。只能不断尝试去理解。

开源 VS 自研

已有成熟开源产品,还需自研吗?这是个老生常谈的问题。那该如何选择?其实不难,归根结底要符合自身业务特点,还要符合实际需求。

开源优势:

入门门槛不高 存在诸多可复用成果 一般来讲 功能较为丰富 周边生态相对完善 投入产出比例较高 简言之 投入较少 收效较快

开源劣势:

内核难以把控。其门槛不低。一般来说开源功能与实际业务无法完全契合。诸多开源产品在开箱即用方面表现欠佳。需要进行大量优化。简言之。入门轻松但掌控困难。

自研优势:

产品核心技术掌控程度高。能更好地贴合业务需求。能定制得更优。基于这两点。通常更易实现良好的性能表现。简而言之。就是量身定制。

自研劣势:

投入产出比稍微低一些。对团队成员的能力曲线有较高要求。另外封闭的生态会致使周边支持不足。当有新需求时常常都得进行定制开发。总之什么事都得依靠自己

基于上述分析。结合我们自身业务情况。我总结出以下几点内容可供参考:

开源项目全是Java技术栈。我们较多使用Go。技术栈与开源项目不匹配。开源项目功能丰富。我们业务相对简单。用起来负担重。开源项目不是拿来就能用。要结合业务特点做定制开发。学习成本高。维护成本高

综上所述,我觉得自研更适合我们现阶段的产品特点。

工作流引擎架构设计

要是选择自己研发,那架构该怎么设计呢?有哪些比较关键的模块?又有哪些需要留意的点呢?下面详细讲讲。

BPMN

它指的是业务流程模型和符号

可理解为一种规范。在此规范中。哪些地方用空心圆有明确定义。哪些地方用矩形有明确定义。哪些地方用菱形有明确定义。

这意味着只要是依据此规范开发的系统。那么该系统所创建的流程全部都能够通用。

实际上要是仅开发一个内部系统不遵守此规范并无问题但要是制作一个产品为使通用性更强最好还是遵守此规范

流程设计器

对于工作流引擎而言 流程设计器的选型十分关键 它具备可视化的流程编排能力 其选型决定了用户体验的优劣

目前主流的流程设计器有多个,比如bpmn-js等。下面来做一个简单介绍。

-

开源版本带有Web版流程设计器。在项目中存在。其优点是集成简便,开发工作量少。缺点是界面不美观,用户体验不佳。

是一个强大的流程图前端库。它能快速创建交互式图表。还能创建图表应用程序。国内外著名的和draw.io都是借助该库创建的强大在线流程图绘制网站。

它是一个开放的js绘图开发框架。我们能够开发出很炫的样式。也能够完全依照项目需求定制。

官方网站:..io/

bpmn-js

bpmn-js是BPMN2.0渲染工具包及Web模型。bpmn-js正致力于成为BPM的一部分。bpmn-js借助Web建模工具能便捷构建BPMN图表。它可将BPMN图表嵌入项目,且易于扩展

bpmn-js 基于原生 js 打造。它能够被集成到 vue 等开源框架里。它也能够被集成到 react 等开源框架里。

官方网站:bpmn.io/

以上介绍的皆是功能强大且完备的框架。此外,存在其他基于Vue或React开发的可视化编辑工具。大家能够依据自身实际需求加以选择。

流程引擎

最后讲讲流程引擎 它是整个系统的核心 引擎设计得好不好 决定了整个系统的稳定性 可用性 扩展性等方面

整体架构如图所示,主要包括一下几个部分:

流程设计器主要借助一系列工具来创建工作流程描述。该描述要能被计算机处理。流程建模一般由众多离散的节点步骤构成。它需涵盖流程的所有必要信息。这些信息有流程的起始条件与结束条件。还包括节点之间的流转情况。以及要承担的用户任务。另外还有被调用的应用程序等。

流程引擎的职责众多。它主要负责流程实例化。还要进行流程控制。也负责节点实例化。以及节点调度等工作。在执行进程里。工作流引擎会提供流程的相关信息。对流程的运行加以管理。监控流程的运行状态。并且记录流程运行的历史数据。

工作流系统通常需要支持各种常见的数据库存储

四、组织模型不在工作流系统建设范围内。流程设计器建模时会引用组织模型,比如定义任务节点参与者。流程流转时也需引用组织模型,像任务指派时要从组织模型确定任务执行者。

工作流引擎内部能够采用平台自身的统一用户组织架构。工作流引擎内部也能够适配第三方所提供的用户组织架构。

工作流引擎作为基础支撑服务。它被提供给各业务系统使用。它还对第三方系统开放标准服务。

后记

下面讲讲我目前开发的系统的支持程度,以及未来可能的发展方向。毕竟它不是专门的工单系统,工单申请只是其中一个模块,所以整体功能与完整的工作流引擎相比差距很大。

第一版

第一版没有流程引擎。开发方式既简单又粗暴。每增加一个流程。就得重新开发相应的表。还要重新开发业务代码。

这样做的缺点是非常明显的:

每个流程都要单独去开发。工作量很大。开发效率比较低。流程功能相类似。代码重复量多。存在冗余情况。不利于进行维护。属于定制化开发。缺少扩展性

第二版,也就是目前的版本。

工单流程渐渐增多。工作量不断增大。于是着手优化流程。开发出了现阶段的工作流引擎。

新增一个工单流程时 要先做工作流配置 配置其基础信息 自定义字段 状态 流转这些信息 还支持配置自动化节点 能根据条件 由程序自动完成相关操作并审批

配置完成后 后端不用开发 由统一引擎代码处理 涵盖节点审批流转 状态变更等 只需开发前端的创建页面与查询页面 相比第一版 开发效率有了大幅提升

目前版本需要优化的点:

缺少可视化流程设计器。无法做到拖拽式设计。流程节点之间状态流转不够灵活。缺少分布式事物支持。缺少异常处理机制。下一个版本

针对以上不足,下一个版本准备主要优化三点,如下:

要支持可视化流程设计器。这能让流程设计更简便。能依据流程配置灵活自动生成前端页面。做到新增一种工单类型时。不需要开发就能增加节点自动化能力。具备异常处理机制。从而提高系统稳定性
页: [1]
查看完整版本: 安全管理平台工单申请流程催生工作流引擎开发及经验分享