flowable工作流

2020-04-23  本文已影响0人  迷糊小生

一、工作流选型

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

一般来说,这样的流程定义使用可视化建模工具建立,如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>

2.4 任务办理方式

一、签收后办理模式

任务创建后,流程进入一个等待状态,需要用户去签收任务,即接收任务的一个过程。原理就是它的执行实例表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工作流依赖-->
    <dependency>
        <groupId>org.flowable</groupId>
        <artifactId>flowable-spring-boot-starter</artifactId>
        <version>6.3.0</version>
    </dependency>
    @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();
    }

2.5 数据库表名说明

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

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

2.6 Flowable API

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

2.6.1 RuntimeService

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

2.6.2 IdentityService

2.6.3 FormService

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

2.6.4 HistoryService

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

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

2.6.5 ManagementService

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

2.6.6 DynamicBpmnService

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

2.7 自定义流程部署

     /**
     * 流程部署
     */
    @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();
    }

三、设计器使用

3.1 Flowable Web部署

  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 Task应用可以用表单定义JSON渲染出html表单。 也可以使用Flowable API,自行获取表单定义JSON。

4.2 表单数据库表结构

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

4.3 表单定义和部署

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

    /**
     * 表单部署
     */
    @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-cc139b-1587646419274)]

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

原生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"}}
    

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

    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默认提供了两种方式可以处理权限相关:

5.3 flowable权限相关表结构

5.4 权限定义

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

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

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

上一篇 下一篇

猜你喜欢

热点阅读