flowable工作流

一、工作流选型

acitviti和flowable的比较?

Flowable是Activiti原班主创人员从activiti-6.0.0.Beta4 分支重新开发出来的一套工作流引擎,是一个业务流程管理(BPM)和工作流系统,适用于开发人员和系统管理员。其核心是超快速、稳定的BPMN2流程引擎,易于与 Spring集成使用。

目前Flowable已经修复了activiti6很多的bug,可以实现零成本从activiti迁移到flowable。

flowable目前已经支持加签、动态增加实例中的节点、支持cmmn、dmn规范。这些都是activiti6目前版本没有的。

1、flowable已经支持所有的历史数据使用mongdb存储,activiti没有。

2、flowable支持事务子流程,activiti没有。

3、flowable支持多实例加签、减签,activiti没有。

4、flowable支持httpTask等新的类型节点,activiti没有。

5、flowable支持在流程中动态添加任务节点,activiti没有。

6、flowable支持历史任务数据通过消息中间件发送,activiti没有。

7、flowable支持java11,activiti没有。

8、flowable支持动态脚本,,activiti没有。

9、flowable支持条件表达式中自定义juel函数,activiti没有。

10、flowable支持cmmn规范,activiti没有。

11、flowable修复了dmn规范设计器,activit用的dmn设计器还是旧的框架,bug太多。

12、flowable屏蔽了pvm,activiti6也屏蔽了pvm(因为6版本官方提供了加签功能,发现pvm设计的过于臃肿,索性直接移除,这样加签实现起来更简洁、事实确实如此,如果需要获取节点、连线等信息可以使用bpmnmodel替代)。

13、flowable与activiti提供了新的事务监听器。activiti5版本只有事件监听器、任务监听器、执行监听器。

14、flowable对activiti的代码大量的进行了重构。

15、activiti以及flowable支持的数据库有h2、hsql、mysql、oracle、postgres、mssql、db2。其他数据库不支持的。使用国产数据库的可能有点失望了,需要修改源码了。

16、flowable支持jms、rabbitmq、mongodb方式处理历史数据,activiti没有。

二、流程定义和流程实例

2.1 流程定义和流程实例的概念

比如我们要构建的流程是一个非常简单的请假流程,在Flowable术语中,我们将其称为一个流程定义(process definition)

一个流程定义可以启动多个流程实例(process instance)。在这个例子中,流程定义定义了请假的各个步骤,而一个流程实例对应某个雇员提出的一个请假申请。

2.2 BPMN 2.0

Flowable引擎需要流程定义为BPMN 2.0格式,这是一个业界广泛接受的XML标准。

BPMN 2.0存储为XML,并包含可视化的部分:使用标准方式定义了每个步骤类型(人工任务,自动服务调用,等等)如何呈现,以及如何互相连接。这样BPMN 2.0标准使技术人员与业务人员能用双方都能理解的方式交流业务流程。

1.png
  • 左侧的圆圈叫做启动事件(start event)。这是一个流程实例的起点。
  • 箭头:表示节点之间的流转指向。
  • 第一个矩形是一个用户任务(user task)。这是流程中人类用户操作的步骤。在这个例子中,经理需要批准或驳回申请。
  • 取决于经理的决定,排他网关(exclusive gateway) (带叉的菱形)会将流程实例路由至批准或驳回路径。
  • 如果批准,则需要进行下一轮HR的审批,审批通过之后整个请假流程就转至了流程结束节点。
  • 如果驳回,则进行自动化任务,自动化任务(左上角有齿轮形状的的圆角矩形)表示自动执行的节点。图中上面的表示请假被经理拒绝后自动发邮件通知给申请人。
  • 右侧的圆圈叫做结束事件(start event)。这是一个流程实例的终点。

一般来说,这样的流程定义使用可视化建模工具建立,如Flowable Designer(Eclipse)或Flowable Web Modeler(Web应用)。

3.png

2.3 流程定义XML讲解

根据可视化模型建立流程定义如下所示:LeaveApply.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:flowable="http://flowable.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.flowable.org/processdef">
      <process id="LeaveApply" name="LeaveApply" isExecutable="true">
        <documentation>请假申请</documentation>
        <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
        <userTask id="managerApproveTask" name="经理审批" flowable:assignee="manager-wanger" flowable:formFieldValidation="true">
          <extensionElements>
            <flowable:formProperty id="userId" name="用户ID" type="string"></flowable:formProperty>
            <flowable:formProperty id="startTime" name="请假起始时间" type="date" datePattern="MM-dd-yyyy"></flowable:formProperty>
            <flowable:formProperty id="endTime" name="请假结束日期" type="date" datePattern="MM-dd-yyyy"></flowable:formProperty>
            <modeler:activiti-idm-assignee xmlns:modeler="http://flowable.org/modeler"><![CDATA[true]]></modeler:activiti-idm-assignee>
            <modeler:assignee-info-email xmlns:modeler="http://flowable.org/modeler"><![CDATA[wanger@163.com]]></modeler:assignee-info-email>
            <modeler:assignee-info-firstname xmlns:modeler="http://flowable.org/modeler"><![CDATA[二]]></modeler:assignee-info-firstname>
            <modeler:assignee-info-lastname xmlns:modeler="http://flowable.org/modeler"><![CDATA[王]]></modeler:assignee-info-lastname>
            <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
          </extensionElements>
        </userTask>
        <sequenceFlow id="sid-6F07B954-19EA-411F-9BF4-B5657B1BCE9A" sourceRef="startEvent1" targetRef="managerApproveTask"></sequenceFlow>
        <exclusiveGateway id="managerApproval" name="同意还是拒绝">
          <documentation>经理审批</documentation>
        </exclusiveGateway>
        <exclusiveGateway id="hrApproval" name="hr审批"></exclusiveGateway>
        <endEvent id="sid-6BD1E626-5BB8-4669-8079-D3653D7BB8EA"></endEvent>
        <serviceTask id="rejectSms" name="发送拒绝信息" flowable:class="com.cyberwater.flowabletest.delegate.ManagerRejectDelegate"></serviceTask>
        <serviceTask id="hrRejectSms" name="发送拒绝信息" flowable:class="com.cyberwater.flowabletest.delegate.HrRejectDelegate"></serviceTask>
        <sequenceFlow id="sid-739755B6-4A7F-4820-98D7-78BD5F634C8D" sourceRef="managerApproveTask" targetRef="managerApproval"></sequenceFlow>
        <sequenceFlow id="sid-D31CD714-A5D3-419C-8E8B-952B25F5DF1C" sourceRef="hrApproveTask" targetRef="hrApproval"></sequenceFlow>
        <userTask id="hrApproveTask" name="HR审批" flowable:candidateGroups="hr" flowable:formFieldValidation="true">
          <extensionElements>
            <modeler:group-info-name-hr xmlns:modeler="http://flowable.org/modeler"><![CDATA[HR组]]></modeler:group-info-name-hr>
            <modeler:activiti-idm-candidate-group xmlns:modeler="http://flowable.org/modeler"><![CDATA[true]]></modeler:activiti-idm-candidate-group>
            <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
          </extensionElements>
        </userTask>
        <endEvent id="sid-202D8E03-F0EA-47D0-8631-F255BC57C763"></endEvent>
        <endEvent id="sid-02FF3C48-B36A-4858-A255-CBE281B1BD89"></endEvent>
        <sequenceFlow id="sid-AF25B78D-5DBC-4352-9923-D190A3FB94F5" sourceRef="hrRejectSms" targetRef="sid-02FF3C48-B36A-4858-A255-CBE281B1BD89"></sequenceFlow>
        <sequenceFlow id="sid-DA4E29A7-B5A8-4DE9-B17D-0D154D0E34DB" sourceRef="managerApproval" targetRef="rejectSms">
          <conditionExpression xsi:type="tFormalExpression"><![CDATA[${!approved}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="sid-815E7B01-B3F7-4E72-A41F-DCD981718F1D" sourceRef="managerApproval" targetRef="hrApproveTask">
          <conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="sid-E976984A-8D7F-4301-B96E-C4875978ACE0" sourceRef="hrApproval" targetRef="sid-6BD1E626-5BB8-4669-8079-D3653D7BB8EA">
          <conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="sid-DDF6A5B1-C131-4FB7-BC35-E3FF82FB259D" sourceRef="hrApproval" targetRef="hrRejectSms">
          <conditionExpression xsi:type="tFormalExpression"><![CDATA[${!approved}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="sid-ABA95260-D069-48D2-8672-49C6397BE154" sourceRef="rejectSms" targetRef="sid-202D8E03-F0EA-47D0-8631-F255BC57C763"></sequenceFlow>
      </process>
  • 每一个步骤(在BPMN 2.0术语中称作活动(activity))都有一个id属性,为其提供一个在XML文件中唯一的标识符。所有的活动都可以设置一个名字,以提高流程图的可读性。

  • 活动之间通过顺序流(sequence flow)连接,在流程图中是一个有向箭头。在执行流程实例时,执行(execution)会从启动事件沿着顺序流流向下一个活动。

  • 离开排他网关(带有X的菱形)的顺序流很特别:都以表达式(expression)的形式定义了条件(condition)。当流程实例的执行到达这个网关时,会计算条件,并使用第一个计算为true的顺序流。这就是排他的含义:只选择一个。当然如果需要不同的路由策略,可以使用其他类型的网关。

  • 这里用作条件的表达式为{approved},这是{approved == true}的简写。变量’approved’被称作流程变量(process variable)。流程变量是持久化的数据,与流程实例存储在一起,并可以在流程实例的生命周期中使用。在这个例子里,我们需要在特定的地方(当经理用户任务提交时,或者以Flowable的术语来说,完成(complete)时)设置这个流程变量,因为这不是流程实例启动时就能获取的数据。

2.4 任务办理方式

  • flowable的办理方式,分为两种:签收模式办理和直接办理。

  • 术语:

    Assignee: 任务的受理人,即执行人。它有两种情况(有值,NULL)

    Owner: 任务的委托人。

    CandidateGroup: 候选用户组

    CandidateUser: 候选人

    delegateTask: 委派任务/签收的任务

    resolveTask: 委派任务的代办,任务的拥有者把任务委派他人来办理,他人办完后,又重新回到任务拥有者,会产生流转记录。

    turnTask: 转办任务,只是改变当前任务的办理人而已,不会产生流转记录。

    CompleteTask: 完成任务,或叫办结提交下一步。

    claimTask:任务签收

一、签收后办理模式

任务创建后,流程进入一个等待状态,需要用户去签收任务,即接收任务的一个过程。原理就是它的执行实例表act_ru_execution创建了一条记录,但act_ru_task表中是没有创建这个记录,只有签收后act_ru_task才会生成一条任务记录。签收办理人,可以分为:候选人(一人或者多人,之间逗号分开)和候选用户组(一个或者多个组,之间逗号分开)。

这种模式就是任务的抢占模式,谁先签收,这个任务就归谁。真实的任务其实只有一条,只是还没有在act_ru_task生成。

二、直接办理模式

任务创建后,直接给了提交到下一个节点,给下一个节点创建任务时,就指定了这个任务的受理人(Assignee_)

(1)xml流程模型中直接设置固定死的用户id或者用户账号,例如:

```  <userTask id="usertask1" name="审批"  flowable:assignee="userId2"></userTask>```

  也可以配置成候选人或者候选组方式。

(2)任务节点上设置流程变量,这个流程变量的值由上一个节点来设置,例如:

 ``` <userTask id="usertask1" name="审批" flowable:assignee="#{userId}"></userTask> ```

(3)自定义一个监听类,实现TaskListener接口,细节查看如何定义监听器类及配置到流程模型xml中相关章节。

(4)候选人和候选组的配置:

    <userTask id="usertask2" name="部门经理审批" flowable:candidateGroups="group1,group2"></userTask>
    
    <userTask id="usertask2" name="部门经理审批" flowable:candidateUsers="user1,user2"></userTask>

2.4 与Spring的整合,并创建流程实例

  • 首先引入flowable相关jar
    <!--flowable工作流依赖-->
    <dependency>
        <groupId>org.flowable</groupId>
        <artifactId>flowable-spring-boot-starter</artifactId>
        <version>6.3.0</version>
    </dependency>
  • 在resources下创建processes文件夹,并将定义好的流程文件(**.bpmn20.xml)放到文件夹内部,这样在启动的时候spring会自动将文件载入,用到下面接口的时候直接注入即可.
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ProcessEngine processEngine;
    /**
     * 提交请假申请,开启流程
     */
    @RequestMapping(value = "leaveApply")
    @ResponseBody
    public String leaveApply(String userId, String startTime, String endTime) {
        //启动流程
        HashMap<String, Object> map = new HashMap<>();
        map.put("userId", userId);
        map.put("startTime", startTime);
        map.put("endTime", endTime);
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("LeaveApply", map);
        return "提交成功.流程Id为:" + processInstance.getId();
    }
  • LeaveApply是上述流程定义xml文件中流程ID,根据流程ID调用runtimeService.startProcessInstanceByKey即可开启流程实例.

2.5 数据库表名说明

Flowable的所有数据库表都以ACT_开头。第二部分是说明表用途的两字符标示符。服务API的命名也大略符合这个规则。

  • ACT_RE_*: 'RE’代表repository。带有这个前缀的表包含“静态”信息,例如流程定义与流程资源(图片、规则等)。

    • ACT_RE_DEPLOYMENT 流程部署表

    • ACT_RE_PROCDEF 流程定义信息表

    • ACT_RE_MODEL 模型信息表(此表已经舍弃,更换为ACT_DE_MODEL)

  • ACT_RU_*: 'RU’代表runtime。这些表存储运行时信息,例如流程实例(process instance)、用户任务(user task)、变量(variable)、作业(job)等。Flowable只在流程实例运行中保存运行时数据,并在流程实例结束时删除记录。这样保证运行时表小和快。

    • ACT_RU_EXECUTION 流程实例与分支执行表

    • ACT_RU_TASK 用户任务表

    • ACT_RU_VARIABLE 变量信息

    • ACT_RU_IDENTITYLINK 参与者相关信息表

    • ACT_RU_EVENT_SUBSCR 事件订阅表

    • ACT_RU_JOB 作业表

    • ACT_RU_TIMER_JOB 定时器表

    • ACT_RU_SUSPENDED_JOB 暂停作业表

    • ACT_RU_DEADLETTER_JOB 死信表

    • ACT_RU_HISTORY_JOB 历史作业表

  • ACT_HI_*: 'HI’代表history。这些表存储历史数据,例如已完成的流程实例、变量、任务等。

    • ACT_HI_PROCINST 历史流程实例表

    • ACT_HI_ACTINST 历史节点信息表

    • ACT_HI_TASKINST 历史任务表

    • ACT_HI_VARINST 历史变量

    • ACT_HI_IDENTITYLINK 历史参与者表

    • ACT_HI_DETAIL 历史的流程运行中的细节信息

    • ACT_HI_ATTACHMENT 附件表

    • ACT_HI_COMMENT 评论表

  • ACT_GE_*: 通用数据。在多处使用。

    • ACT_GE_PROPERTY 属性表(保存流程引擎的kv键值属性)

    • ACT_GE_BTYEARRAY 资源表(存储流程定义相关的资源)

详细表字段信息请参考:https://blog.csdn.net/zhongzk69/article/details/90944593

2.6 Flowable API

[图片上传失败...(image-273030-1587646419274)]

2.6.1 RuntimeService

  • RuntimeService 主要用于管理流程在运行时产生的数据(流程参数, 事件, 流程实例, 以及执行情况)以及对正在运行的流程进行操作的API.

  • 系统用户需要执行的任务是Flowable等BPM引擎的核心. 而任务周围的任务都被分组到TaskService中, 如:

  1. 查询分配给用户或组的任务.
  2. 创建新的独立任务. 这些是与流程实例无关的任务.
  3. 操作向哪个用户分配任务或者哪个用户以某种方式参与任务.
  4. 声称和完成一项任务. 声称意味着有人决参与任务, 这也意味着该用户将完成该任务. 完成意味着完成任务的工作.

2.6.2 IdentityService

  • IdentityService是非常简单的. 它支持组和用户的管理(创建, 更新,
    删除, 查询).

  • Flowable实际上不会在运行时对用户进行任何检查. 例如, 可以将任务分配给任何用户, 但是引擎不验证该用户是否被系统知晓. 这是意味着Flowable引擎也可以与LDAP, Active Directory等服务结合使用.

2.6.3 FormService

FormService 是一个可选的服务. 该服务引入了开始表和任务表的概念. 开始形式是流程实例启动前显示给用户的一种形式, 而任务形式是当一个用户要填写一份表的时候进行显示.

2.6.4 HistoryService

HistoryService 服务公开有关正在进行和历史进程实例的信息.

这与运行时信息不同, 因为此运行时信息仅在任何特定时刻包含实际运行时状态, 并且针对运行时进程执行性能进行了优化. 历史信息(谁做了哪些任务, 完成任务需要多长时间, 每个流程实例遵循哪条路径, 等等)经过优化, 易于查询, 并且永久保存.

2.6.5 ManagementService

ManagementService 为流程引擎上的管理和维护操作提供服务. 此外, 它还暴露了查询功能和作业管理操作. 作业在Flowable中用于各种事情, 如定时器, 异步延续, 延迟挂起/激活等等.

2.6.6 DynamicBpmnService

DynamicBpmnService 可以用来改变流程定义的一部分, 而无需重新部署. 例如, 可以在流程定义中更改用户任务的受理人或者更改服务任务的类名称.

2.7 自定义流程部署

  • 首先定义流程,最终会入库到ACT_DE_MODEL表中
    [图片上传失败...(image-f749c7-1587646419274)]

  • 调用自己写的流程部署接口进行流程部署,部署之后该流程就会出现在流程定义信息表中(ACT_RE_PROCDEF)

     /**
     * 流程部署
     */
    @PutMapping(value = "deploy")
    @ResponseBody
    public HttpEntity deployProcess(String modelId) {
        Model modelData =modelService.getModel(modelId);
        byte[] bytes = modelService.getBpmnXML(modelData);
        if(bytes==null){
            return new ResponseEntity<>("模型数据为空,请先设计流程并成功保存,再进行发布。", HttpStatus.BAD_REQUEST);
        }

        BpmnModel model = modelService.getBpmnModel(modelData);
        if(model.getProcesses().size()==0){
            return new ResponseEntity<>("数据模型不符要求,请至少设计一条主线流程。", HttpStatus.BAD_REQUEST);
        }
        byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
        String processName = modelData.getName()+".bpmn20.xml";
        repositoryService.createDeployment()
                .name(modelData.getName())
                .addBytes(processName,bpmnBytes)
                .deploy();

        return ResponseEntity.ok().build();
    }

三、设计器使用

  • 设计器主要有Flowable Designer(Eclipse)或Flowable Web Modeler(Web应用),以及actiBPM(idea插件)。

  • 三种我都尝试了一下其中actiBPM(idea插件)效果不好而且经常报错,其次Flowable Designer(Eclipse),在使用中只能生产.bpmn文件需要手动改后缀,个人用着效果同样不是很好,最终选择了基于web应用的Flowable Web Modeler.

3.1 Flowable Web部署

  • Flowable提供了几个web应用,用于演示及介绍Flowable项目提供的功能:

  • Flowable IDM: 身份管理应用。为所有Flowable UI应用提供单点登录认证功能,并且为拥有IDM管理员权限的用户提供了管理用户、组与权限的功能。

  • Flowable Modeler: 让具有建模权限的用户可以创建流程模型、表单、选择表与应用定义。

  • Flowable Task: 运行时任务应用。提供了启动流程实例、编辑任务表单、完成任务,以及查询流程实例与任务的功能。

  • Flowable Admin: 管理应用。让具有管理员权限的用户可以查询BPMN、DMN、Form及Content引擎,并提供了许多选项用于修改流程实例、任务、作业等。管理应用通过REST API连接至引擎,并与Flowable Task应用及Flowable REST应用一同部署。

  1. 下载地址:https://flowable.com/open-source/downloads/

  2. 将下面目录下的jar包copy到tomcat下,并启动
    [图片上传失败...(image-d12d9e-1587646419274)]

  3. 由于Flowable Web Modeler默认使用的是h2数据库,下次重启后相关数据都会消失,所以建议将其改成mysql数据库,改动配置如下,另外需要将mysql驱动包扔到lib目录下
    [图片上传失败...(image-e54fd3-1587646419274)]

  4. 重新启动tomcat,进入到如下地址:
    http://localhost:8080/flowable-idm/
    [图片上传失败...(image-7c5656-1587646419274)]

默认用户名:admin/test

3.2 Flowable Web model使用

  1. 点击创建流程
    [图片上传失败...(image-8382fa-1587646419274)]

  2. 填写模型名称以及定义模型Key
    [图片上传失败...(image-478a25-1587646419274)]

  3. 进行创建流程
    [图片上传失败...(image-83a3d1-1587646419274)]

3.2 BPMN 2.0结构

四、表单

4.1 表单定义

  • Flowable提供了一种简便灵活的方式,用来为业务流程中的人工步骤添加表单。有两种使用表单的方法:使用(由表单设计器创建的)表单定义的内置表单渲染,以及外部表单渲染。 使用外部表单渲染时,可以使用(自Explorer web应用V5版本支持的)表单参数;也可以使用表单key定义,引用外部的、使用自定义代码解析的表单。

  • 可以使用Flowable表单设计器创建表单定义。表单设计器是Flowable Modeler web应用的一部分。也可以直接使用JSON编辑器创建表单定义。 表单引擎用户手册中介绍了表单定义JSON的完整结构。表单支持下列表单字段类型:

    • Text: 渲染为文本框

    • Multiline text: 渲染为多行文本框

    • Number: 渲染为只允许数字值的文本框

    • CheckBox: 渲染为复选框

    • Date: 渲染为日期框

    • Dropdown: 渲染为下拉选择框,候选值由字段定义配置

    • Radio buttons: 渲染为单选按钮,候选值由字段定义配置

    • People: 渲染为选人框,可以选择用户身份表中的用户

    • Group of people: 渲染为选组框,可以选择组身份表中的组

    • Upload: 渲染为上传框

    • Expression: 渲染为一个标签,可以在标签文字中使用JUEL表达式,以使用变量及/或其他动态值

Flowable Task应用可以用表单定义JSON渲染出html表单。 也可以使用Flowable API,自行获取表单定义JSON。

4.2 表单数据库表结构

Flowable Form的数据库名称都以ACT_FO_开头。

  • ACT_FO_DATABASECHANGELOG:Liquibase使用此表来跟踪已运行的变更集。

  • ACT_FO_DATABASECHANGELOGLOCK:Liquibase使用此表来确保一次仅运行一个Liquibase实例。

  • ACT_FO_FORM_DEFINITION:该表包含已部署表单定义的定义信息。

  • ACT_FO_FORM_INSTANCE:此表包含具有用户填写的值的表单实例。

  • ACT_FO_FORM_DEPLOYMENT:该表包含部署元数据。

  • ACT_FO_FORM_RESOURCE:该表包含表单定义资源。

4.3 表单定义和部署

  • 普通的表单可定义在resources/forms文件下,项目在启动的时候会默认扫描这个文件夹下的表单,进行自动部署
    [图片上传失败...(image-1e08fc-1587646419274)]
    [图片上传失败...(image-9637a2-1587646419274)]
  • 流程中表单的引用
    [图片上传失败...(image-d3157-1587646419274)]

4.4 自定义表单部署,并嵌入流程中

  • 创建表单,表单入库(ACT_DE_MODEL)
    [图片上传失败...(image-bb7e2c-1587646419274)]
    [图片上传失败...(image-b96aac-1587646419274)]
    [图片上传失败...(image-1772d2-1587646419274)]
  • 部署表单,调用自定义的表单部署接口进行表单部署
    /**
     * 表单部署
     */
    @PutMapping(value = "formDeploy")
    @ResponseBody
    public HttpEntity formDeploy(String modelId) {
        Model modelData =modelService.getModel(modelId);
        if(modelData == null) {
            return new ResponseEntity<>("模型数据为空,请先设计表单并成功保存,再进行发布。", HttpStatus.BAD_REQUEST);
        }

        String formName = modelData.getName()+".form";
        formRepositoryService.createDeployment()
                .name(modelData.getName())
                .addString(formName, modelData.getModelEditorJson())
                .deploy();

        return ResponseEntity.ok().build();
    }
  • 流程中表单的引用
    [图片上传失败...(image-3d2363-1587646419274)]

[图片上传失败...(image-cc139b-1587646419274)]

4.5 基于非flowable的自定义表单与流程定义

  • 由于flowable中存储的表单模型过于简单,导致前端很难对其做到自定义渲染,因此需要引入第三方前端插件(表单设计器)来进行优化,这里用的是FormMaking.

原生flowable表单设计器存储的表单模型如下:

    {"name":"测试","key":"TestForm","version":0,"fields":[{"fieldType":"FormField","id":"label","name":"Label","type":"text","value":null,"required":false,"readOnly":false,"overrideId":false,"placeholder":null,"layout":null},{"fieldType":"FormField","id":"label","name":"Label","type":"decimal","value":null,"required":false,"readOnly":false,"overrideId":false,"placeholder":null,"layout":null}],"outcomes":[]}

FormMaking表单设计器存储的表单模型如下:

    {"list":[{"type":"input","icon":"icon-input","options":{"width":"100%","defaultValue":"","required":true,"dataType":"string","pattern":"","placeholder":"","disabled":false,"remoteFunc":"func_1583759794000_13255"},"name":"员工号","key":"1583759794000_13255","model":"userId","rules":[{"type":"string","message":"员工号格式不正确"},{"required":true,"message":"员工号必须填写"}]},{"type":"input","icon":"icon-input","options":{"width":"100%","defaultValue":"","required":true,"dataType":"string","pattern":"","placeholder":"","disabled":false,"remoteFunc":"func_1583759798000_407"},"name":"员工姓名","key":"1583759798000_407","model":"userName","rules":[{"type":"string","message":"员工姓名格式不正确"},{"required":true,"message":"员工姓名必须填写"}]},{"type":"date","icon":"icon-date","options":{"defaultValue":"","readonly":false,"disabled":false,"editable":true,"clearable":true,"placeholder":"","startPlaceholder":"","endPlaceholder":"","type":"datetime","format":"yyyy-MM-dd HH:mm:ss","timestamp":false,"required":true,"width":"100%","remoteFunc":"func_1583759800000_51050"},"name":"开始时间","key":"1583759800000_51050","model":"date_start","rules":[{"required":true,"message":"开始时间必须填写"}]},{"type":"date","icon":"icon-date","options":{"defaultValue":"","readonly":false,"disabled":false,"editable":true,"clearable":true,"placeholder":"","startPlaceholder":"","endPlaceholder":"","type":"datetime","format":"yyyy-MM-dd HH:mm:ss","timestamp":false,"required":true,"width":"100%","remoteFunc":"func_1583759803000_73145"},"name":"结束时间","key":"1583759803000_73145","model":"date_end","rules":[{"required":true,"message":"结束时间必须填写"}]}],"config":{"labelWidth":100,"labelPosition":"right","size":"small"}}
    
  • 在使用原生flowable表单设计器时,自定义表单与流程定义关联步骤关系如下:

    1. 表单模型设计
    2. 表单部署
    3. 定义流程并引入表单
    4. 流程定义部署
    5. 开启流程实例
  • 引入第三方表单设计器后,表单与流程定义关联步骤关系则改变为(因为存储JSON的格式发生了变化,原生flowable的ACT_DE_MODEL表中的表单模型不能进行部署,因此更换了新表,用于和ACT_DE_MODEL区别):

    1. 表单模型设计
    2. 定义流程(在表单标识处填入表单Key)
    3. 流程定义部署(将自定义表单以动态表单的形式嵌入流程定义中,此时表单参数是放在变量表当中)
    4. 开始流程实例

自定义新表用于存储表单数据:

    CREATE TABLE `T_FLOWABLE_FORM` (
      `ID` bigint(20) NOT NULL AUTO_INCREMENT,
      `FORM_KEY` varchar(100) NOT NULL COMMENT '表单key',
      `FORM_NAME` varchar(100) NOT NULL COMMENT '表单名称',
      `FORM_JSON` longtext COMMENT '表单内容json串',
      `CREATE_BY` varchar(32) DEFAULT NULL COMMENT '创建人',
      `CREATE_TIME` datetime DEFAULT NULL COMMENT '创建时间',
      `UPDATE_BY` varchar(32) DEFAULT NULL COMMENT '修改人',
      `UPDATE_TIME` datetime DEFAULT NULL COMMENT '修改时间',
      PRIMARY KEY (`ID`),
      KEY `FORM_KEY_INDEX` (`FORM_KEY`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='流程表单';

部署流程定义相关代码如下:

     @ApiOperation("流程部署")
    @PutMapping(value = "deploy/{modelId}")
    public HttpEntity deployProcess(@PathVariable String modelId) throws Exception {
        Model modelData = modelService.getModel(modelId);
        byte[] bytes = modelService.getBpmnXML(modelData);
        if (bytes == null) {
            return new ResponseEntity<>("模型数据为空,请先设计流程并成功保存,再进行发布。", HttpStatus.BAD_REQUEST);
        }

        BpmnModel model = modelService.getBpmnModel(modelData);
        if (model.getProcesses().size() == 0) {
            return new ResponseEntity<>("数据模型不符要求,请至少设计一条主线流程。", HttpStatus.BAD_REQUEST);
        }

        //开始流程定义部署
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        Process process = model.getMainProcess();
        Collection<FlowElement> flowElements = process.getFlowElements();

        //存放form元素
        Map<String, String> formKeyMap = new HashMap<>();

        for (FlowElement flowElement : flowElements) {
            String formKey = null;
            if (flowElement instanceof StartEvent) {
                //位于开始节点上表单元素
                StartEvent startEvent = (StartEvent) flowElement;
                if (startEvent.getFormKey() != null && startEvent.getFormKey().length() > 0) {
                    formKey = startEvent.getFormKey();
                }
            } else if (flowElement instanceof UserTask) {
                //位于任务节点上表单元素
                UserTask userTask = (UserTask) flowElement;
                if (userTask.getFormKey() != null && userTask.getFormKey().length() > 0) {
                    formKey = userTask.getFormKey();
                }
            }

            //将表单元素存入map中
            if (formKey != null && formKey.length() > 0) {
                if (!formKeyMap.containsKey(formKey)) {
                    String formKeyDefinition = formKey.replace(".form", "");
                    TFlowableForm form = tFlowableFormService.queryFlowableFormByKey(formKeyDefinition);
                    if (form != null && form.getFormJson() != null && form.getFormJson().length() > 0) {
                        byte[] formJson = form.getFormJson().getBytes(StandardCharsets.UTF_8);
                        ByteArrayInputStream bi = new ByteArrayInputStream(formJson);
                        deploymentBuilder.addInputStream(formKey, bi);
                        formKeyMap.put(formKey, formKey);
                    } else {
                        throw new FlowableObjectNotFoundException("Cannot find formJson with formKey " + formKeyDefinition);
                    }
                }
            }
        }

        byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
        String processName = modelData.getName() + ".bpmn20.xml";
        deploymentBuilder.name(modelData.getName())
                .addBytes(processName, bpmnBytes)
                .deploy();

        return ResponseEntity.ok().build();
    }

开启流程实例,查询流程实例图

[图片上传失败...(image-a5a896-1587646419274)]

五、权限管理

5.1 flowable权限说明

Flowable默认提供了一套自己的权限管理接口(IDM),但是从Flowable 6开始,IDM的组件被独立出来,分为几个不同的模块:flowable-idm-api, flowable-idm-engine, flowable-idm-spring 和 flowable-idm-engine-configurator。

使用Flowable提供的IdmEngine,也有三种方案:
方案一:Flowable默认提供的IdmEngine已经实现IdmIdentityService接口,包含对用户、组、权限等的操作;

方案二:集成LDAP来实现轻量级用户权限管理。LDAP内部已经实现IdmIdentityService接口,包含对用户、组、权限等的操作;

方案三:自定义实现IdmIdentityService接口,实现对用户、组、权限的操作;

5.2 flowable权限默认实现

Flowable默认提供了两种方式可以处理权限相关:

  • 1 通过IdentityService,这个服务主要用来管理用户和组,不能操作具体的权限。是简单版

       processEngine.getIdentityService();
    
  • 2 通过IdmIdentityService,可以用户和组,同时可以处理具体的权限(Privilege),在IdentityService之上做了增强,但两者是不同的接口。

     IdmEngineConfigurator idmEngineConfigurator = new IdmEngineConfigurator();
    
     cfg.setIdmEngineConfigurator(idmEngineConfigurator);
    
     // 会初始化processEngine,同时初始化配置在里面的Configurator,如IdmEngineConfigurator
     ProcessEngine processEngine = cfg.buildProcessEngine();
    
     IdmIdentityService idmIdentityService = idmEngineConfigurator.getIdmEngineConfiguration().getIdmIdentityService();
    

5.3 flowable权限相关表结构

  • ACT_ID_USER 存储的用户,调用saveUser接口会存储在里面
  • ACT_ID_INFO 存储用户的属性信息,setUserInfo接口的时候设置一key,value信息存储在其中
  • ACT_ID_GROUP 存储新创建的Flowable组信息,saveGroup会存储在其中
  • ACT_ID_MEMBERSHIP 用户和组的关系会存在里面,用户和组可以多对多
  • ACT_ID_PRIV 存储可以使用的权限,createPrivilege会新增一条记录
  • ACT_ID_PRIV_MAPPING 存储用户id或组id与权限的映射关系,addGroupPrivilegeMapping或者addUserPrivilegeMapping会新增一台记录
  • ACT_ID_TOKEN 用户相关Token,saveToken会新存储一条记录

5.4 权限定义

由于拥有其他的权限系统,因此对flowable中的权限模块需要重新定义.

目前的方案是在权限系统数据库连接上新增一个工作流的数据库实例,将工作流系统中需要用的相关表用视图进行替代,目前需要进行视图映射的表结构如下.

  • ACT_ID_USER 用户表 对应权限系统的用户表
  • ACT_ID_GROUP 候选组表 对应权限系统的角色表
  • ACT_ID_MEMBERSHIP 候选组人员关系表 对应角色用户表
  • ACT_ID_PRIV 权限表 对应权限系统的菜单(待定)

flowable文档地址: https://tkjohn.github.io/flowable-userguide/#_introduction

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

推荐阅读更多精彩内容