一、Flowable开发—简介安装

1. Flowable简介


  • Flowable是一个使用Java编写的轻量级业务流程引擎。Flowable流程引擎可用于部署BPMN 2.0流程定义(用于定义流程的行业XML标准), 创建这些流程定义的流程实例,进行查询,访问运行中或历史的流程实例与相关数据,等等。
  • Flowable可以十分灵活地加入你的应用/服务/构架。可以将JAR形式发布的Flowable库加入应用或服务,来嵌入引擎。 以JAR形式发布使Flowable可以轻易加入任何Java环境:Java SE;Tomcat、Jetty或Spring之类的servlet容器;JBoss或WebSphere之类的Java EE服务器,等等。 另外,也可以使用Flowable REST API进行HTTP调用。也有许多Flowable应用(Flowable Modeler, Flowable Admin, Flowable IDM 与 Flowable Task),提供了直接可用的UI示例,可以使用流程与任务。

  • 可以在官网下载对应的jar包在本地部署运行,官方提供了下面的五个应用程序:

    • Flowable Modeler:流程定义管理
    • Flowable Task:用户任务管理
    • Flowable IDM:用户组权限管理
    • Flowable REST API:流程引擎对外提供的API接口
    • Flowable Admin:后台管理

      2. 流程引擎使用架构

  • Flowable引擎在使用前需要先通过配置来初始化ProcessEngine。

  • 初始化ProcessEngineConfiguration一般有两种方式:
    • 通过Spinrg配置文件进行依赖注入,通过flowable.cfg.xml文件来初始化ProcessEngineConfiguration(这里的文件名必须为flowable.cfg.xml,否则Flowable识别不到)
    • 通过编写程序的方式来构造ProcessEngineConfiguration对象

流程引擎API架构图:Flowable - 图1

流程引擎API架构图

模型图:Flowable - 图2

模型图

ProcessEngineConfiguration在初始化过程中会同时初始化数据库,如果数据库已经存在,则不会做创建更新操作,如果数据库不存在,则会默认执行数据库创建脚本。

3. 流程引擎初体验

  • 简单了解Bpmn
    • Task任务:
      用户任务(userTask)
      系统任务(serviceTask )
    • Event事件:
      定时器事件(timerEventDefinition)
    • Gateway网关:
      排他网关(exclusive gateway)
  • 目标:实现以下简化版的请假流程

Flowable - 图3

请假流程图(简单版)
步骤1:定义相应的BPMN文件

步骤2:配置flowable.cfg.xml

步骤3:将流程定义添加到Repository仓储中

步骤4:Runtime开始一个流程实例

4. 下载安装

4.1. 下载

Flowable - 图4

4.2. 解压

Flowable - 图5

  1. **目标结构**

Flowable - 图6

4.3. 部署war包

4.3.1. 部署

  • 拷贝war包到Tomcat的wapapps目录:
    Flowable - 图7
  • 部署

4.3.2. 启动Tomcat

Flowable - 图8

应用权限

4.3.3.数据库分成两套

  • 项目数据库:项目所需表和flowable通过jar包生成的表(34张)
  • Flowable数据库:用于流程图创建保存,测试,监控(74张)
  • 项目部署只需项目数据库

5. Flowable的用户权限体系

  1. 在接入Flowable的用户权限体系的时候,有四种方式:
  • 使用Flowable提供的默认IdmEngine进行用户体系管理,该引擎包含了用户、组的概念。
  • 集成LDAP,实现轻量级用户权限管理。通过IdentityService进行认证,用于由IdentityService处理所有认证业务的场景。
  • 实现IdmIdentityService接口,自定义实现用户、组的查询
  • 接入自定义的权限体系
    用户id => 获取到租户id、角色id集、部门id集
    • 单用户(assignee=”用户id”)、多用户(candidateUsers=”用户id1,用户id2”)
    • 单角色、多角色(candidateGroups=”:角色id1,:角色id2”)
    • 单部门、多部门(candidateGroups=”部门id1:,部门id2:”)
    • 角色或部门(candidateGroups=”角色id1:, :部门id1”)
    • 角色且部门

6. 数据表结构

  1. ACT_RE_ *:RE代表repository。具有此前缀的表包含静态信息,例如流程定义和流程资源(图像,规则等)。
  2. ACT_RU_ *:RU代表runtime。这些是包含运行时的流程实例,用户任务,变量,作业等的运行时数据的运行时表。
  3. Flowable仅在流程实例执行期间存储运行时数据,并在流程实例结束时删除记录。这使运行时表保持小而快。
  4. ACT_HI_ *:HI代表history。这些是包含历史数据的表,例如过去的流程实例,变量,任务等。
  5. ACT_GE_ *:general数据,用于各种用例。
  6. ACT_ID_*:Idm的用户、组

数据表: ACT_HI_ACTINST 流程实例的历史运行节点表 ACT_HI_TASKINST 流程实例的历史任务表 ACT_HI_VARINST 流程实例的历史运行节点的变量表 ACT_HI_PROCINST 流程历史部署记录 ACT_HI_IDENTITYLINK 对应ACT_RU_IDENTITYLINK的历史记录表 ACT_RE_DEPLOYMENT 流程部署 ACT_RE_PROCDEF 流程定义表 ACT_RU_EXECUTION 流程实例执行过程的所有节点记录 ACT_RU_IDENTITYLINK 流程实例运行过程中,各节点对应的用户 ACT_RU_TASK 流程实例运行时的任务表 ACT_RU_VARIABLE 流程实例运行时节点的变量表 ACT_GE_BYTEARRAY 资源文件表

7. Docker环境运行

7.1. 前置条件

Docker 环境

7.2. 运行 Flowable6.4.2

Docker Hub上提供了所有工作流的UI应用程序。
要启动 Flowable REST 应用需要H2内存数据库:

  1. [root@localhost ~]# docker run -p8080:8080 flowable/flowable-rest

这个API文档的访问地址为:http://localhost:8080/flowable-rest/docs/
用户名:rest-admin 密码:test
要运行完整的Flowable 工作流,可以运行run the ‘All-in-One’ Docker 镜像,这个镜像包含Flowable IDM、Modeler、Task 、Admin UI 应用,运行容器为Tomcat,数据为内存数据库H2。

  1. [root@localhost ~]# docker run -p8080:8080 flowable/all-in-one

Flowable Modeler; http://localhost:8080/flowable-modeler
Flowable Task; http://localhost:8080/flowable-task
Flowable Admin; http://localhost:8080/flowable-admin
Flowable IDM; http://localhost:8080/flowable-idm

7.3. 运行测试

1) 用户组权限管理

(用户名/密码: admin/test)
http://192.168.247.130:8080/flowable-idm/#/login

Flowable - 图9

用户管理

2) 流程定义管理

http://192.168.247.130:8080/flowable-modeler

Flowable - 图10

流程图

Flowable - 图11

流程图

3) 用户任务管理

http://192.168.247.130:8080/flowable-task/#/

Flowable - 图12

任务管理

4) 后台管理

http://192.168.247.130:8080/flowable-admin

Flowable - 图13

二、Flowable开发—主要引擎

1. Flowable五大引擎

  • flowable包含五个引擎,分别是:
    1、内容引擎 ContentEngine
    2、身份识别引擎 IdmEngine
    3、表单引擎 FormEngine
    4、决策引擎 DmnEngine
    5、流程核心引擎 ProcessEngine

Flowable - 图14

  • Flowable有五大引擎,每个之间都是相互独立互不影响。
  • ProcessEngine是里面最核心也是最重要的一个引擎,如果失去它那Flowable也就意义了。

2. 引擎包含的服务

每个引擎由相对应的 EngineConfiguration进行创建,在创建过程中对每个引擎使用的服务进行初始化。

2.1. 内容引擎 ContentEngine

内容引擎包含的服务有:

1) ContentManagementService

ContentManagementService提供对数据库表的管理操作,包括:

  1. Map<String, Long> getTableCount() 获取每个表的记录数量;
  2. String getTableName(Class<?> flowableEntityClass); 根据实体类获得对应的数据库表名;
  3. TableMetaData getTableMetaData(String tableName); 根据数据库表名获得表的列名和列类型;
  4. TablePageQuery createTablePageQuery(); 创建一个可以进行排序、根据条件分页的查询类。

2) ContentService

实现对内容的创建、删除、保存和获取的基本操作。

  1. ContentItem newContentItem();
  2. void saveContentItem(ContentItem contentItem);
  3. void saveContentItem(ContentItem contentItem, InputStream inputStream);
  4. InputStream getContentItemData(String contentItemId);
  5. void deleteContentItem(String contentItemId);
  6. void deleteContentItemsByProcessInstanceId(String processInstanceId);
  7. void deleteContentItemsByTaskId(String taskId);
  8. ContentItemQuery createContentItemQuery();

3) ContentEngineConfiguration

ContentEngineConfiguration最主要的作用是提供Mybatis的封装,实现数据库相关配置的获取。
同时,内容引擎配置还提供了操作系统级的文件操作的路径设置、文件读取、文件保存的功能。

2.2. 身份识别引擎 IdmEngine

身份识别引擎包含的服务有:

1) IdmIdentityService

提供用户的创建、修改、删除、密码修改、登录、用户头像设置等;
提供组Group的创建、删除、用户与组关系的关联、删除关联;
提供权限的创建、删除、关联等。

2) IdmManagementService

对身份识别相关的数据库表进行统计、获取表的列信息。

3) IdmEngineConfiguration

提供数据库配置信息。

2.3. 表单引擎 FormEngine

表单引擎包含的服务有:

  1. FormManagementService
  2. FormRepositoryService
  3. FormService
  4. FormEngineConfiguration

2.4. 决策引擎 DmnEngine

决策引擎包含的服务有:

  1. DmnManagementService
  2. DmnRepositoryService
  3. DmnRuleService
  4. DmnHistoryService
  5. DmnEngineConfiguration

2.5. 流程引擎 ProcessEngine

流程引擎包含的服务有:

  1. RepositoryService
  2. RuntimeService
  3. HistoryService
  4. IdentityService
  5. TaskService
  6. FormService
  7. ManagementService
  8. DynamicBpmnService

三、Flowable开发—SpringBoot集成

1. Maven依赖

  1. <!--web依赖-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>
  6. <!--数据库操作依赖-->
  7. <dependency>
  8. <groupId>org.springframework.boot</groupId>
  9. <artifactId>spring-boot-starter-data-jdbc</artifactId>
  10. </dependency>
  11. <dependency>
  12. <groupId>mysql</groupId>
  13. <artifactId>mysql-connector-java</artifactId>
  14. <scope>runtime</scope>
  15. <version>5.1.6</version>
  16. </dependency>
  17. <!--mybatis-plus 插件-->
  18. <dependency>
  19. <groupId>com.baomidou</groupId>
  20. <artifactId>mybatis-plus-boot-starter</artifactId>
  21. <version>3.1.2</version>
  22. </dependency>
  23. <!--druid-->
  24. <dependency>
  25. <groupId>com.alibaba</groupId>
  26. <artifactId>druid-spring-boot-starter</artifactId>
  27. <version>1.1.18</version>
  28. </dependency>
  29. <!--工作流-->
  30. <dependency>
  31. <groupId>org.flowable</groupId>
  32. <artifactId>flowable-spring-boot-starter</artifactId>
  33. <version>6.4.2</version>
  34. </dependency>
  35. <dependency>
  36. <groupId>org.projectlombok</groupId>
  37. <artifactId>lombok</artifactId>
  38. <optional>true</optional>
  39. </dependency>
  40. <dependency>
  41. <groupId>org.springframework.boot</groupId>
  42. <artifactId>spring-boot-starter-test</artifactId>
  43. <scope>test</scope>
  44. <exclusions>
  45. <exclusion>
  46. <groupId>org.junit.vintage</groupId>
  47. <artifactId>junit-vintage-engine</artifactId>
  48. </exclusion>
  49. </exclusions>
  50. </dependency>

2. application.properties配置

  1. server.port=7001
  2. # 数据库连接
  3. spring.datasource.url=jdbc:mysql://localhost:3306/workflow_flowable?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2b8
  4. spring.datasource.username=root
  5. spring.datasource.password=1234
  6. # 数据库连接池
  7. spring.datasource.druid.filters=stat
  8. #spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
  9. spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
  10. # 配置初始化大小/最小/最大
  11. spring.datasource.druid.initial-size=2
  12. spring.datasource.druid.min-idle=2
  13. spring.datasource.druid.max-active=30
  14. # 获取连接等待超时时间
  15. spring.datasource.druid.max-wait=60000
  16. # 间隔多久进行一次检测,检测需要关闭的空闲连接
  17. spring.datasource.druid.time-between-eviction-runs-millis=60000
  18. # 一个连接在池中最小生存的时间
  19. spring.datasource.druid.min-evictable-idle-time-millis=300000
  20. spring.datasource.druid.validation-query=SELECT 'x'
  21. spring.datasource.druid.test-while-idle=true
  22. spring.datasource.druid.test-on-borrow=false
  23. spring.datasource.druid.test-on-return=false
  24. # 打开PSCache,并指定每个连接上PSCache的大小。
  25. # oracle设为true,mysql设为false。分库分表较多推荐设置为false
  26. spring.datasource.druid.pool-prepared-statements=false
  27. spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20
  28. ## mybatis-plus配置
  29. mybatis-plus.mapper-locations=classpath*:/mappers/**/*.xml
  30. # 实体扫描,多个package用逗号或者分号分隔
  31. mybatis-plus.type-aliases-package=com.xtsz.workflow.entity
  32. # 配置banner
  33. mybatis-plus.global-config.banner=false
  34. # #主键类型 AUTO:"数据库ID自增", INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
  35. mybatis-plus.global-config.db-config.id-type=id_worker
  36. # 字段策略 IGNORED:"忽略判断",NOT_NULL:"非 NULL 判断"),NOT_EMPTY:"非空判断"
  37. #mybatis-plus.global-config.db-config.field-strategy=not_empty
  38. # 逻辑删除全局值(1表示已删除,这也是Mybatis Plus的默认配置)
  39. mybatis-plus.global-config.db-config.logic-delete-value=1
  40. #逻辑未删除全局值(0表示未删除,这也是Mybatis Plus的默认配置)
  41. mybatis-plus.global-config.db-config.logic-not-delete-value=0
  42. # 配置返回数据库(column下划线命名&&返回java实体是驼峰命名),
  43. # 自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId)
  44. mybatis-plus.configuration.map-underscore-to-camel-case=true
  45. mybatis-plus.configuration.cache-enabled=false
  46. # 设置全局属性用于控制数据库的类型
  47. mybatis-plus.configuration-properties.dbType=mysql
  48. #在格式:logging.level.Mapper类的包=debug 会在控制台打印出sql语句
  49. #logging.level.com.xtsz.admin.modules.system.mapper=debug
  50. # 上传附件的目录设置以及系统一块模板word、excel的存放路径
  51. # 这样做的原因是由于spring boot发布时打包成了jar,所以没有办法往jar中写文件
  52. filepath.uploadpath=D:\\uploadfile\\
  53. filepath.templatepath=D:\\templatefile\\
  54. # flowable spring boot时自动部署resource/processes中的流程文件
  55. flowable.check-process-definitions=true
  56. # db-identity-used: true
  57. # 自动生成flowable相关表 第一次生成后建议关闭提高运行速度
  58. # 将databaseSchemaUpdate设置为true。
  59. # 当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。
  60. flowable.database-schema-update=true
  61. # 保存历史数据级别设置为full最高级别,便于历史数据的追溯
  62. flowable.history-level=full
  63. # 关闭定时任务JOB
  64. flowable.async-executor-activate=false

测试方便flowable配置为默认的即可。为了测试时方便看日志信息,我这里将flowable的定时job功能暂时关闭。
初次运行时flowable会将自动执行flowable中的初始化脚本完成工作流所需要的数据表的建立,如果指定的数据库中还未创建过flowable的相关数据表的话。

3. 定义流程文件

flowable建议采用业界标准BPMN2.0的XML来描述需要定义的工作流。

  1. 创建目录processes 在项目的resource目录下,创建目录processes。
    Flowable - 图15
    创建目录processes
  2. 添加流程文件 ExpenseProcess.bpmn20.xml
    流程定义文件:文件的命名必须是XXXX.bpmn20.xml,注意命名规范,扩展名必须是bpmn20.xml。
    流程定义图片:用BPMN2.0规范定义的各种图形描绘(BMPN2.0的符号及画布上的坐标信息),一般是PNG格式。
    表单文件:把表单内容保存在一个文件中,扩展名为drl。
    规则文件:扩展名为drl。
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
  4. xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
  5. typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
  6. targetNamespace="http://www.flowable.org/processdef">
  7. <process id="Expense" name="ExpenseProcess" isExecutable="true">
  8. <documentation>报销流程</documentation>
  9. <startEvent id="start" name="开始"></startEvent>
  10. <userTask id="fillTask" name="出差报销" flowable:assignee="${taskUser}">
  11. <extensionElements>
  12. <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler">
  13. <![CDATA[false]]></modeler:initiator-can-complete>
  14. </extensionElements>
  15. </userTask>
  16. <exclusiveGateway id="judgeTask"></exclusiveGateway>
  17. <userTask id="directorTak" name="经理审批">
  18. <extensionElements>
  19. <flowable:taskListener event="create"
  20. class="com.xtsz.workflow.handler.ManagerTaskHandler"></flowable:taskListener>
  21. </extensionElements>
  22. </userTask>
  23. <userTask id="bossTask" name="老板审批">
  24. <extensionElements>
  25. <flowable:taskListener event="create"
  26. class="com.xtsz.workflow.handler.BossTaskHandler"></flowable:taskListener>
  27. </extensionElements>
  28. </userTask>
  29. <endEvent id="end" name="结束"></endEvent>
  30. <sequenceFlow id="directorNotPassFlow" name="驳回" sourceRef="directorTak" targetRef="fillTask">
  31. <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='驳回'}]]></conditionExpression>
  32. </sequenceFlow>
  33. <sequenceFlow id="bossNotPassFlow" name="驳回" sourceRef="bossTask" targetRef="fillTask">
  34. <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='驳回'}]]></conditionExpression>
  35. </sequenceFlow>
  36. <sequenceFlow id="flow1" sourceRef="start" targetRef="fillTask"></sequenceFlow>
  37. <sequenceFlow id="flow2" sourceRef="fillTask" targetRef="judgeTask"></sequenceFlow>
  38. <sequenceFlow id="judgeMore" name="大于500元" sourceRef="judgeTask" targetRef="bossTask">
  39. <conditionExpression xsi:type="tFormalExpression"><![CDATA[${money > 500}]]></conditionExpression>
  40. </sequenceFlow>
  41. <sequenceFlow id="bossPassFlow" name="通过" sourceRef="bossTask" targetRef="end">
  42. <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='通过'}]]></conditionExpression>
  43. </sequenceFlow>
  44. <sequenceFlow id="directorPassFlow" name="通过" sourceRef="directorTak" targetRef="end">
  45. <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='通过'}]]></conditionExpression>
  46. </sequenceFlow>
  47. <sequenceFlow id="judgeLess" name="小于500元" sourceRef="judgeTask" targetRef="directorTak">
  48. <conditionExpression xsi:type="tFormalExpression"><![CDATA[${money <= 500}]]></conditionExpression>
  49. </sequenceFlow>
  50. </process>
  51. <bpmndi:BPMNDiagram id="BPMNDiagram_Expense">
  52. <bpmndi:BPMNPlane bpmnElement="Expense" id="BPMNPlane_Expense">
  53. <bpmndi:BPMNShape bpmnElement="start" id="BPMNShape_start">
  54. <omgdc:Bounds height="30.0" width="30.0" x="285.0" y="135.0"></omgdc:Bounds>
  55. </bpmndi:BPMNShape>
  56. <bpmndi:BPMNShape bpmnElement="fillTask" id="BPMNShape_fillTask">
  57. <omgdc:Bounds height="80.0" width="100.0" x="405.0" y="110.0"></omgdc:Bounds>
  58. </bpmndi:BPMNShape>
  59. <bpmndi:BPMNShape bpmnElement="judgeTask" id="BPMNShape_judgeTask">
  60. <omgdc:Bounds height="40.0" width="40.0" x="585.0" y="130.0"></omgdc:Bounds>
  61. </bpmndi:BPMNShape>
  62. <bpmndi:BPMNShape bpmnElement="directorTak" id="BPMNShape_directorTak">
  63. <omgdc:Bounds height="80.0" width="100.0" x="735.0" y="110.0"></omgdc:Bounds>
  64. </bpmndi:BPMNShape>
  65. <bpmndi:BPMNShape bpmnElement="bossTask" id="BPMNShape_bossTask">
  66. <omgdc:Bounds height="80.0" width="100.0" x="555.0" y="255.0"></omgdc:Bounds>
  67. </bpmndi:BPMNShape>
  68. <bpmndi:BPMNShape bpmnElement="end" id="BPMNShape_end">
  69. <omgdc:Bounds height="28.0" width="28.0" x="771.0" y="281.0"></omgdc:Bounds>
  70. </bpmndi:BPMNShape>
  71. <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
  72. <omgdi:waypoint x="315.0" y="150.0"></omgdi:waypoint>
  73. <omgdi:waypoint x="405.0" y="150.0"></omgdi:waypoint>
  74. </bpmndi:BPMNEdge>
  75. <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
  76. <omgdi:waypoint x="505.0" y="150.16611295681062"></omgdi:waypoint>
  77. <omgdi:waypoint x="585.4333333333333" y="150.43333333333334"></omgdi:waypoint>
  78. </bpmndi:BPMNEdge>
  79. <bpmndi:BPMNEdge bpmnElement="judgeLess" id="BPMNEdge_judgeLess">
  80. <omgdi:waypoint x="624.5530726256983" y="150.44692737430168"></omgdi:waypoint>
  81. <omgdi:waypoint x="735.0" y="150.1392757660167"></omgdi:waypoint>
  82. </bpmndi:BPMNEdge>
  83. <bpmndi:BPMNEdge bpmnElement="directorNotPassFlow" id="BPMNEdge_directorNotPassFlow">
  84. <omgdi:waypoint x="785.0" y="110.0"></omgdi:waypoint>
  85. <omgdi:waypoint x="785.0" y="37.0"></omgdi:waypoint>
  86. <omgdi:waypoint x="455.0" y="37.0"></omgdi:waypoint>
  87. <omgdi:waypoint x="455.0" y="110.0"></omgdi:waypoint>
  88. </bpmndi:BPMNEdge>
  89. <bpmndi:BPMNEdge bpmnElement="bossPassFlow" id="BPMNEdge_bossPassFlow">
  90. <omgdi:waypoint x="655.0" y="295.0"></omgdi:waypoint>
  91. <omgdi:waypoint x="771.0" y="295.0"></omgdi:waypoint>
  92. </bpmndi:BPMNEdge>
  93. <bpmndi:BPMNEdge bpmnElement="judgeMore" id="BPMNEdge_judgeMore">
  94. <omgdi:waypoint x="605.4340277777778" y="169.56597222222223"></omgdi:waypoint>
  95. <omgdi:waypoint x="605.1384083044983" y="255.0"></omgdi:waypoint>
  96. </bpmndi:BPMNEdge>
  97. <bpmndi:BPMNEdge bpmnElement="directorPassFlow" id="BPMNEdge_directorPassFlow">
  98. <omgdi:waypoint x="785.0" y="190.0"></omgdi:waypoint>
  99. <omgdi:waypoint x="785.0" y="281.0"></omgdi:waypoint>
  100. </bpmndi:BPMNEdge>
  101. <bpmndi:BPMNEdge bpmnElement="bossNotPassFlow" id="BPMNEdge_bossNotPassFlow">
  102. <omgdi:waypoint x="555.0" y="295.0"></omgdi:waypoint>
  103. <omgdi:waypoint x="455.0" y="295.0"></omgdi:waypoint>
  104. <omgdi:waypoint x="455.0" y="190.0"></omgdi:waypoint>
  105. </bpmndi:BPMNEdge>
  106. </bpmndi:BPMNPlane>
  107. </bpmndi:BPMNDiagram>
  108. </definitions>

这样当flowable框架启动的时候它会默认加载resource目录下的processes时就可以将此流程配置加载到数据库进行持久化了。
flowable:assignee=”${taskUser}” 与代码Map中的taskUser键对应。
flowable:taskListener 中的class指定处理器。

4. 代码编写

  1. 定义配置类
    FlowableConfig.java
  1. /**
  2. * 解决flowable图片中的中文乱码
  3. */
  4. @Configuration
  5. public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
  6. @Override
  7. public void configure(SpringProcessEngineConfiguration engineConfiguration) {
  8. engineConfiguration.setActivityFontName("宋体");
  9. engineConfiguration.setLabelFontName("宋体");
  10. engineConfiguration.setAnnotationFontName("宋体");
  11. }
  12. }
  1. 处理器类
  1. public class BossTaskHandler implements TaskListener {
  2. @Override
  3. public void notify(DelegateTask delegateTask) {
  4. delegateTask.setAssignee("老板");
  5. }
  6. }
  7. public class ManagerTaskHandler implements TaskListener {
  8. @Override
  9. public void notify(DelegateTask delegateTask) {
  10. delegateTask.setAssignee("经理");
  11. }
  12. }
  1. 控制器类
  1. @RestController
  2. @RequestMapping(value = "expense")
  3. public class ExpenseController {
  4. @Autowired
  5. private RuntimeService runtimeService;
  6. @Autowired
  7. private TaskService taskService;
  8. @Autowired
  9. private RepositoryService repositoryService;
  10. @Resource
  11. private ProcessEngine processEngine;
  12. /**
  13. * 1. 添加报销
  14. * 接收用户的一个请求传入用户的ID和金额以及描述信息来
  15. * 开启一个报销流程,并返回给用户这个流程的Id
  16. * @param userId 用户Id
  17. * @param money 报销金额
  18. * @param descption 描述
  19. */
  20. @RequestMapping(value = "add")
  21. public String addExpense(String userId, Integer money, String descption) {
  22. // 启动流程
  23. HashMap<String, Object> map = new HashMap<>();
  24. map.put("taskUser", userId);
  25. map.put("money", money);
  26. ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("Expense", map);
  27. return "提交成功.流程Id为:" + processInstance.getId();
  28. }
  29. /**
  30. * 获取审批管理列表
  31. * 获取出此用户需要处理的流程
  32. */
  33. @RequestMapping(value = "/list")
  34. public Object list(String userId) {
  35. List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).orderByTaskCreateTime().desc().list();
  36. for (Task task : tasks) {
  37. System.out.println(task.toString());
  38. }
  39. return tasks.toArray().toString();
  40. }
  41. /**
  42. * 批准
  43. * 通过前端传入的任务ID来对此流程进行同意处理
  44. * @param taskId 任务ID
  45. */
  46. @RequestMapping(value = "apply")
  47. public String apply(String taskId) {
  48. Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
  49. if (task == null) {
  50. throw new RuntimeException("流程不存在");
  51. }
  52. //通过审核
  53. HashMap<String, Object> map = new HashMap<>();
  54. map.put("outcome", "通过");
  55. taskService.complete(taskId, map);
  56. return "processed ok!";
  57. }
  58. /**
  59. * 拒绝
  60. */
  61. @RequestMapping(value = "reject")
  62. public String reject(String taskId) {
  63. HashMap<String, Object> map = new HashMap<>();
  64. map.put("outcome", "驳回");
  65. taskService.complete(taskId, map);
  66. return "reject";
  67. }
  68. /**
  69. * 生成流程图
  70. *
  71. * @param processId 任务ID
  72. */
  73. @RequestMapping(value = "processDiagram")
  74. public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) throws Exception {
  75. ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
  76. //流程走完的不显示图
  77. if (pi == null) {
  78. return;
  79. }
  80. Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
  81. //使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
  82. String InstanceId = task.getProcessInstanceId();
  83. List<Execution> executions = runtimeService
  84. .createExecutionQuery()
  85. .processInstanceId(InstanceId)
  86. .list();
  87. //得到正在执行的Activity的Id
  88. List<String> activityIds = new ArrayList<>();
  89. List<String> flows = new ArrayList<>();
  90. for (Execution exe : executions) {
  91. List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
  92. activityIds.addAll(ids);
  93. }
  94. //获取流程图
  95. BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
  96. ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
  97. ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
  98. InputStream in = diagramGenerator.generateDiagram(bpmnModel,"png",activityIds,flows,engconf.getActivityFontName(), engconf.getLabelFontName(),engconf.getAnnotationFontName(), engconf.getClassLoader(),1.0,true);
  99. OutputStream out = null;
  100. byte[] buf = new byte[1024];
  101. int legth = 0;
  102. try {
  103. out = httpServletResponse.getOutputStream();
  104. while ((legth = in.read(buf)) != -1) {
  105. out.write(buf, 0, legth);
  106. }
  107. } finally {
  108. if (in != null) {
  109. in.close();
  110. }
  111. if (out != null) {
  112. out.close();
  113. }
  114. }
  115. }
  116. }

5. 运行测试

  1. 创建一个流程
    访问:http://localhost:7001/expense/add?userId=123&money=123321
    Flowable - 图16
    创建一个流程
    返回:提交成功.流程Id为:1294e612-f289-11e9-8bad-005056c00008
  2. 查询待办列表
    访问:http://localhost:7001/expense/list?userId=123
    Flowable - 图17
    查询待办列表
    输出:Task[id=129979fb-f289-11e9-8bad-005056c00008, name=出差报销]
    Flowable - 图18
    查询待办列表
  3. 同意
    访问:http://localhost:7001/expense/apply?taskId=129979fb-f289-11e9-8bad-005056c00008
    Flowable - 图19
    同意
    返回:processed ok!
  4. 生成流程图
    访问:http://localhost:7001/expense/processDiagram?processId=1294e612-f289-11e9-8bad-005056c00008
    Flowable - 图20
    生成流程图

6. 常见问题

  1. 数据库版本问题
    请使用:5.x.x 版本,不要太高。
  2. 自动布署问题
    配置文件:
  1. # flowable spring boot时自动部署resource/processes中的流程文件
  2. flowable.check-process-definitions=true

四、Flowable开发—流程部署

1. 部署相关的表

  1. act_re_deployment:流程模型部署对象表
    每部署一次生成一条记录,首先生成这条数据,它的id主键将会被act_re_procdef和act_ge_bytearray作为外键。
  2. actre_procdef:流程定义表
    一次部署可能采用zip/bar进行部署,里面是有多份流程定义文件xml的,这时候act_re_deployment只有一条部署信息,但act_re_procdef有多个记录(一个流程定义对应一条),这个表有DEPLOYMENT_ID
    外键字段,用它关联act_re_deployment。
  3. act_ge_bytearray:资源文件表
    流程模型资源文件的真正存放地方,它每部署一次就会产生2条记录,一条是关于bpmn规范的文件内容存放在BYTES字段中,另一条是图片信息,采用二进制格式存储。
    提示:可以部署后解析bpmn文件的内容自动生成流程图,实现流程图的跟踪线路。
  4. act_re_model:这张表,在xml进行部署时,它没有内容(flowable放弃了此表改用act_de_model保存流程模型信息)

2. Maven依赖

针对Model部署需增加:

  1. <dependency>
  2. <groupId>org.flowable</groupId>
  3. <artifactId>flowable-json-converter</artifactId>
  4. <version>6.4.2</version>
  5. </dependency>
  6. <!--流程设计器-->
  7. <dependency>
  8. <groupId>org.flowable</groupId>
  9. <artifactId>flowable-ui-modeler-rest</artifactId>
  10. <version>6.4.2</version>
  11. <exclusions>
  12. <exclusion>
  13. <groupId>org.apache.logging.log4j</groupId>
  14. <artifactId>log4j-slf4j-impl</artifactId>
  15. </exclusion>
  16. </exclusions>
  17. </dependency>

3. 流程图部署

  1. 流程资源xml部署
  1. @SpringBootTest
  2. @Slf4j
  3. class WorkflowFlowableApplicationTests {
  4. @Autowired
  5. private RepositoryService repositoryService;
  6. @Resource
  7. private ProcessEngine processEngine;
  8. /**
  9. * 流程资源xml部署
  10. */
  11. @Test
  12. void deployFlow() {
  13. String filePath = "processes/OrderApproval.bpmn20.xml";
  14. DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
  15. .addClasspathResource(filePath);
  16. Deployment deployment = deploymentBuilder.deploy();
  17. log.info("成功:部署工作流成:" + filePath);
  18. log.info("部署ID:"+deployment.getId());
  19. log.info("部署时间:"+deployment.getDeploymentTime());
  20. }
  21. }
  1. zip/bar打包,多个流程资源文件部署
  1. /**
  2. * zip/bar打包,多个流程资源文件部署
  3. */
  4. @Test
  5. void deployFlowZip() {
  6. /**
  7. * 需要将所有流程图进行打包
  8. */
  9. String file = "diagrams/approve.zip";
  10. InputStream in = this.getClass().getClassLoader().getResourceAsStream(file);
  11. ZipInputStream zipInputStream = new ZipInputStream(in);
  12. // 获取流程定义和部署对象相关的Service
  13. Deployment deployment = processEngine.getRepositoryService()
  14. // 创建部署对象
  15. .createDeployment()
  16. // 使用zip方式部署,将approve.bpmn和approve.png压缩成zip格式的文件
  17. .addZipInputStream(zipInputStream)
  18. .deploy(); // 完成部署
  19. log.info("部署ID:" + deployment.getId());
  20. log.info("部署时间:" + deployment.getDeploymentTime());
  21. }
  1. Model部署
  1. /**
  2. * Model部署
  3. */
  4. void deployFlowModel() {
  5. String modelId = "modelId";
  6. // 通过act_de_model中存放的Modeler内容来部署
  7. Model modelData = repositoryService.getModel(modelId);
  8. ObjectNode modelNode = null;
  9. try {
  10. // 获取模型
  11. byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
  12. if (null == bytes) {
  13. log.error("模型数据为空,请先设计流程并成功保存,再进行发布。");
  14. }
  15. modelNode = (ObjectNode) new ObjectMapper().readTree(bytes);
  16. BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
  17. if (model.getProcesses().size() == 0) {
  18. log.error("数据模型不符要求,请至少设计一条主线流程。");
  19. }
  20. byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
  21. // 发布流程
  22. String processName = modelData.getName() + ".bpmn20.xml";
  23. repositoryService.createDeployment().name(modelData.getName()).addString(processName, new String(bpmnBytes, "UTF-8")).deploy();
  24. } catch (IOException e) {
  25. e.printStackTrace();
  26. }
  27. }

4. 流程部署示例

  1. 创建流程图

Flowable - 图21

流程图

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <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">
  3. <process id="test_bpmn" name="测试BPMN模型" isExecutable="true">
  4. <documentation>测试BPMN模型</documentation>
  5. <startEvent id="start" name="开始"></startEvent>
  6. <endEvent id="end" name="结束"></endEvent>
  7. <userTask id="testUser" name="用户任务测试"></userTask>
  8. <sequenceFlow id="sid-8D834F3C-45A8-4C88-9AD1-1AC426CC9002" sourceRef="start" targetRef="testUser"></sequenceFlow>
  9. <sequenceFlow id="sid-AB59612A-1B33-4FB8-8758-5D773EDF9C44" sourceRef="testUser" targetRef="end"></sequenceFlow>
  10. </process>
  11. <bpmndi:BPMNDiagram id="BPMNDiagram_test_bpmn">
  12. <bpmndi:BPMNPlane bpmnElement="test_bpmn" id="BPMNPlane_test_bpmn">
  13. <bpmndi:BPMNShape bpmnElement="start" id="BPMNShape_start">
  14. <omgdc:Bounds height="30.0" width="30.0" x="210.0" y="60.0"></omgdc:Bounds>
  15. </bpmndi:BPMNShape>
  16. <bpmndi:BPMNShape bpmnElement="end" id="BPMNShape_end">
  17. <omgdc:Bounds height="28.0" width="28.0" x="525.0" y="61.0"></omgdc:Bounds>
  18. </bpmndi:BPMNShape>
  19. <bpmndi:BPMNShape bpmnElement="testUser" id="BPMNShape_testUser">
  20. <omgdc:Bounds height="80.0" width="100.0" x="315.0" y="35.0"></omgdc:Bounds>
  21. </bpmndi:BPMNShape>
  22. <bpmndi:BPMNEdge bpmnElement="sid-8D834F3C-45A8-4C88-9AD1-1AC426CC9002" id="BPMNEdge_sid-8D834F3C-45A8-4C88-9AD1-1AC426CC9002">
  23. <omgdi:waypoint x="239.94999779398907" y="75.0"></omgdi:waypoint>
  24. <omgdi:waypoint x="315.0" y="75.0"></omgdi:waypoint>
  25. </bpmndi:BPMNEdge>
  26. <bpmndi:BPMNEdge bpmnElement="sid-AB59612A-1B33-4FB8-8758-5D773EDF9C44" id="BPMNEdge_sid-AB59612A-1B33-4FB8-8758-5D773EDF9C44">
  27. <omgdi:waypoint x="414.9499999999903" y="75.0"></omgdi:waypoint>
  28. <omgdi:waypoint x="525.0" y="75.0"></omgdi:waypoint>
  29. </bpmndi:BPMNEdge>
  30. </bpmndi:BPMNPlane>
  31. </bpmndi:BPMNDiagram>
  32. </definitions>
  1. 创建业务方法
  1. public interface IFlowService {
  2. /**
  3. * 部署工作流
  4. */
  5. Deployment createFlow(String filePath);
  6. }
  7. @Primary
  8. @Service
  9. @Slf4j
  10. public class FlowServiceImpl implements IFlowService {
  11. /**
  12. * Flowable运行时服务
  13. */
  14. @Autowired
  15. private RepositoryService repositoryService;
  16. @Override
  17. public Deployment createFlow(String filePath) {
  18. //解析BPMN模型看是否成功
  19. XMLStreamReader reader = null;
  20. InputStream inputStream = null;
  21. try {
  22. BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
  23. XMLInputFactory factory = XMLInputFactory.newInstance();
  24. inputStream=new FileInputStream(new File(filePath));
  25. reader = factory.createXMLStreamReader(inputStream);
  26. BpmnModel model = bpmnXMLConverter.convertToBpmnModel(reader);
  27. List<Process> processes = model.getProcesses();
  28. Process curProcess = null;
  29. if (CollectionUtils.isEmpty(processes)) {
  30. log.error("BPMN模型没有配置流程");
  31. return null;
  32. }
  33. curProcess = processes.get(0);
  34. inputStream=new FileInputStream(new File(filePath));
  35. DeploymentBuilder deploymentBuilder = repositoryService.createDeployment().name("TEST_FLOW")
  36. .addInputStream(curProcess.getName(),inputStream);
  37. Deployment deployment= deploymentBuilder.deploy();
  38. log.warn("部署流程 name:"+curProcess.getName() + " "+deployment);
  39. return deployment;
  40. }
  41. catch (Exception e){
  42. log.error("BPMN模型创建流程异常",e);
  43. return null;
  44. }
  45. finally {
  46. try {
  47. reader.close();
  48. } catch (XMLStreamException e) {
  49. log.error("关闭异常",e);
  50. }
  51. }
  52. }
  53. }
  1. 创建控制器
  1. @RestController
  2. @RequestMapping(value = "flow")
  3. @Slf4j
  4. public class FlowController {
  5. @Autowired
  6. private IFlowService flowService;
  7. @RequestMapping("/create")
  8. public Map<String, String> createFlow() throws FileNotFoundException {
  9. Map<String, String> res = new HashMap<>();
  10. String flowPath = "processes/test.bpmn20.xml";
  11. File file = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX + flowPath);
  12. if (null == flowService.createFlow(file.getAbsolutePath())) {
  13. res.put("msg", "创建流程失败");
  14. res.put("res", "0");
  15. return res;
  16. }
  17. res.put("msg", "创建流程成功");
  18. res.put("res", "1");
  19. return res;
  20. }
  21. }
  1. 测试
    http://localhost:7001/flow/create
    Flowable - 图22
    测试

部署ID表:act_re_deployment

Flowable - 图23

部署记录

布署内容表:act_ge_bytearray

Flowable - 图24

五、Flowable开发—流程审批

1. 流程审批后涉及到的表

表名 描述
act_fo_form_instance 存储用户填充后表单实例信息,FORMDEFINITION_ID字段 2bb4ecac-cfb8-11e9-9f13-1a1dea14efe7
act_fo_form_resource NAME_字段 form-2bb4ecac-cfb8-11e9-9f13-1a1dea14efe7
act_hi_actinst 历史的流程实例 插入多条数据
act_hi_identitylink 历史的流程运行过程中用户关系 插入多条数据
act_hi_procinst 历史的流程实例 REV_ 1未完成 2已完成
act_hi_taskinst 历史的任务实例 REV_ 1待认领 2等审批 3已审批
act_hi_varinst 历史的流程运行中的变量信息
act_ru_actinst 存储运行时节点信息 与act_hi_actinst同时存储
act_ru_execution 执行实例表和actrun_task表,一起控制了用户任务的产生与完成等,当在并行网关和会签多实例时,它是会产生多个执行实例,IS_ACTIVE这个字段的值都是为1,即激活状态,当每完成一个执行实例时,它会把IS_ACTIVE设为0,非激活状态,当所有执行实例完成后,它才会转移到历史,把这个多实例自动删除
act_ru_identitylink 运行时用户关系
act_ru_task 运行时任务表
act_ru_variable 运行的流程中的变量信息

当流程全部走完后,actru表的数据清空了,全部移到了acthi

2. 流程示例

  1. 创建流程图
    创建OrderApproval.bpmn20.xml文件
    Flowable - 图25
    创建文件
    Flowable - 图26
    创建文件
    Flowable - 图27
    改更扩展名
    Flowable - 图28
    流程图设计
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <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">
  3. <!--订单审批,必须定义ID-->
  4. <process id="OrderApproval" name="订单审批" isExecutable="true">
  5. <!--开始事件-->
  6. <startEvent id="startEvent" name="采购订单"></startEvent>
  7. <!--流程-->
  8. <sequenceFlow id="sequenceFlow-3" sourceRef="startEvent" targetRef="approveTask"></sequenceFlow>
  9. <!--定义任务 flowable:assignee 指定用户ID-->
  10. <userTask id="approveTask" name="订单审批" flowable:assignee="${userId}">
  11. <extensionElements>
  12. <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
  13. </extensionElements>
  14. </userTask>
  15. <!--定义网关-->
  16. <exclusiveGateway id="decision"></exclusiveGateway>
  17. <!--定义流程条件-->
  18. <sequenceFlow id="sequenceFlow-9" sourceRef="decision" targetRef="fail">
  19. <conditionExpression xsi:type="tFormalExpression"><![CDATA[${!approved}]]></conditionExpression>
  20. </sequenceFlow>
  21. <!--定义任务->同意-->
  22. <serviceTask id="success" name="通过" flowable:class="com.xtsz.workflow.delegate.ReviewApprove"></serviceTask>
  23. <!--定义结束事件-->
  24. <endEvent id="approveEnd"></endEvent>
  25. <endEvent id="rejectEnd"></endEvent>
  26. <sequenceFlow id="sequenceFlow-e" sourceRef="success" targetRef="approveEnd"></sequenceFlow>
  27. <sequenceFlow id="sequenceFlow-1" sourceRef="fail" targetRef="rejectEnd"></sequenceFlow>
  28. <!--定义任务->拒绝-->
  29. <serviceTask id="fail" name="拒绝" flowable:class="com.xtsz.workflow.delegate.ReviewNoApprove"></serviceTask>
  30. <sequenceFlow id="sequenceFlow-5" sourceRef="approveTask" targetRef="decision"></sequenceFlow>
  31. <sequenceFlow id="sequenceFlow-c" sourceRef="decision" targetRef="success">
  32. <conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved}]]></conditionExpression>
  33. </sequenceFlow>
  34. </process>
  35. <bpmndi:BPMNDiagram id="BPMNDiagram_OrderApproval">
  36. <bpmndi:BPMNPlane bpmnElement="OrderApproval" id="BPMNPlane_OrderApproval">
  37. <bpmndi:BPMNShape bpmnElement="startEvent" id="BPMNShape_startEvent">
  38. <omgdc:Bounds height="30.0" width="30.0" x="0.0" y="95.0"></omgdc:Bounds>
  39. </bpmndi:BPMNShape>
  40. <bpmndi:BPMNShape bpmnElement="approveTask" id="BPMNShape_approveTask">
  41. <omgdc:Bounds height="60.0" width="100.0" x="75.0" y="75.0"></omgdc:Bounds>
  42. </bpmndi:BPMNShape>
  43. <bpmndi:BPMNShape bpmnElement="decision" id="BPMNShape_decision">
  44. <omgdc:Bounds height="40.0" width="40.0" x="230.0" y="90.0"></omgdc:Bounds>
  45. </bpmndi:BPMNShape>
  46. <bpmndi:BPMNShape bpmnElement="success" id="BPMNShape_success">
  47. <omgdc:Bounds height="60.0" width="100.0" x="320.0" y="0.0"></omgdc:Bounds>
  48. </bpmndi:BPMNShape>
  49. <bpmndi:BPMNShape bpmnElement="approveEnd" id="BPMNShape_approveEnd">
  50. <omgdc:Bounds height="28.0" width="28.0" x="620.0" y="16.0"></omgdc:Bounds>
  51. </bpmndi:BPMNShape>
  52. <bpmndi:BPMNShape bpmnElement="rejectEnd" id="BPMNShape_rejectEnd">
  53. <omgdc:Bounds height="28.0" width="28.0" x="570.0" y="175.0"></omgdc:Bounds>
  54. </bpmndi:BPMNShape>
  55. <bpmndi:BPMNShape bpmnElement="fail" id="BPMNShape_false">
  56. <omgdc:Bounds height="60.0" width="100.0" x="315.0" y="150.0"></omgdc:Bounds>
  57. </bpmndi:BPMNShape>
  58. <bpmndi:BPMNEdge bpmnElement="sequenceFlow-5" id="BPMNEdge_sequenceFlow-5">
  59. <omgdi:waypoint x="174.95" y="105.0"></omgdi:waypoint>
  60. <omgdi:waypoint x="202.5" y="105.0"></omgdi:waypoint>
  61. <omgdi:waypoint x="202.5" y="110.0"></omgdi:waypoint>
  62. <omgdi:waypoint x="230.0" y="110.0"></omgdi:waypoint>
  63. </bpmndi:BPMNEdge>
  64. <bpmndi:BPMNEdge bpmnElement="sequenceFlow-c" id="BPMNEdge_sequenceFlow-c">
  65. <omgdi:waypoint x="269.9189252336448" y="110.0"></omgdi:waypoint>
  66. <omgdi:waypoint x="282.0" y="110.0"></omgdi:waypoint>
  67. <omgdi:waypoint x="282.0" y="30.000000000000004"></omgdi:waypoint>
  68. <omgdi:waypoint x="319.999999999994" y="30.0"></omgdi:waypoint>
  69. </bpmndi:BPMNEdge>
  70. <bpmndi:BPMNEdge bpmnElement="sequenceFlow-1" id="BPMNEdge_sequenceFlow-1">
  71. <omgdi:waypoint x="414.95000000000005" y="180.0"></omgdi:waypoint>
  72. <omgdi:waypoint x="460.0" y="180.0"></omgdi:waypoint>
  73. <omgdi:waypoint x="460.0" y="189.0"></omgdi:waypoint>
  74. <omgdi:waypoint x="570.0" y="189.0"></omgdi:waypoint>
  75. </bpmndi:BPMNEdge>
  76. <bpmndi:BPMNEdge bpmnElement="sequenceFlow-e" id="BPMNEdge_sequenceFlow-e">
  77. <omgdi:waypoint x="419.94999999998697" y="30.0"></omgdi:waypoint>
  78. <omgdi:waypoint x="620.0" y="30.0"></omgdi:waypoint>
  79. </bpmndi:BPMNEdge>
  80. <bpmndi:BPMNEdge bpmnElement="sequenceFlow-3" id="BPMNEdge_sequenceFlow-3">
  81. <omgdi:waypoint x="29.949987029268733" y="110.0"></omgdi:waypoint>
  82. <omgdi:waypoint x="52.5" y="110.0"></omgdi:waypoint>
  83. <omgdi:waypoint x="52.5" y="105.0"></omgdi:waypoint>
  84. <omgdi:waypoint x="74.99999999999241" y="105.0"></omgdi:waypoint>
  85. </bpmndi:BPMNEdge>
  86. <bpmndi:BPMNEdge bpmnElement="sequenceFlow-9" id="BPMNEdge_sequenceFlow-9">
  87. <omgdi:waypoint x="269.9189252336448" y="110.0"></omgdi:waypoint>
  88. <omgdi:waypoint x="282.0" y="110.0"></omgdi:waypoint>
  89. <omgdi:waypoint x="282.0" y="180.0"></omgdi:waypoint>
  90. <omgdi:waypoint x="314.9999999999916" y="180.0"></omgdi:waypoint>
  91. </bpmndi:BPMNEdge>
  92. </bpmndi:BPMNPlane>
  93. </bpmndi:BPMNDiagram>
  94. </definitions>

Flowable - 图29

流程图

  1. 创建委托
  1. import lombok.extern.slf4j.Slf4j;
  2. import org.flowable.engine.delegate.DelegateExecution;
  3. import org.flowable.engine.delegate.JavaDelegate;
  4. /**
  5. * 批准委托类
  6. */
  7. @Slf4j
  8. public class ReviewApprove implements JavaDelegate {
  9. @Override
  10. public void execute(DelegateExecution delegateExecution) {
  11. //可以发送消息给某人
  12. log.info("通过,userId是:{}",delegateExecution.getVariable("userId"));
  13. }
  14. }
  15. import lombok.extern.slf4j.Slf4j;
  16. import org.flowable.engine.delegate.DelegateExecution;
  17. import org.flowable.engine.delegate.JavaDelegate;
  18. /**
  19. * 驳回委托类
  20. */
  21. @Slf4j
  22. public class ReviewNoApprove implements JavaDelegate {
  23. @Override
  24. public void execute(DelegateExecution delegateExecution) {
  25. //可以发送消息给某人
  26. log.info("拒绝,userId是:{}",delegateExecution.getVariable("userId"));
  27. }
  28. }
  1. 创建控制器
  1. @RestController
  2. @RequestMapping("/orderFlow")
  3. @Slf4j
  4. public class OrderFlowController {
  5. @Autowired
  6. private RepositoryService repositoryService;
  7. @Autowired
  8. private RuntimeService runtimeService;
  9. @Autowired
  10. private TaskService taskService;
  11. @Autowired
  12. private HistoryService historyService;
  13. @Resource
  14. private ProcessEngine processEngine;
  15. /**
  16. * 1.提交采购订单的审批请求
  17. *
  18. * @param userId 用户id
  19. */
  20. @PostMapping("/start/{userId}/{purchaseOrderId}")
  21. public Result startFlow(@PathVariable String userId, @PathVariable String purchaseOrderId) {
  22. HashMap<String, Object> map = new HashMap<>();
  23. map.put("userId", userId);
  24. map.put("purchaseOrderId", purchaseOrderId);
  25. // 流程ID->OrderApproval
  26. ProcessInstance processInstance =
  27. runtimeService.startProcessInstanceByKey("OrderApproval", map);
  28. String processId = processInstance.getId();
  29. // 名称由布署时指定
  30. String name = processInstance.getName();
  31. log.info(processId + ":" + name);
  32. return Result.ok(processId + ":" + name);
  33. }
  34. /**
  35. * 2.获取用户的任务
  36. *
  37. * @param userId 用户id
  38. */
  39. @GetMapping("/getTasks/{userId}")
  40. public Result getTasks(@PathVariable String userId) {
  41. List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).orderByTaskCreateTime().desc().list();
  42. return Result.ok(tasks.toString());
  43. }
  44. /**
  45. * 3.审批通过
  46. */
  47. @PostMapping("/success/{taskId}")
  48. public Result success(@PathVariable String taskId) {
  49. Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
  50. if (task == null) {
  51. return Result.error("流程不存在");
  52. }
  53. //通过审核
  54. HashMap<String, Object> map = new HashMap<>();
  55. // 在流程图中获取进行处理
  56. map.put("approved", true);
  57. taskService.complete(taskId, map);
  58. return Result.ok("流程审核通过!");
  59. }
  60. /**
  61. * 4.审批不通过
  62. */
  63. @PostMapping("/fail/{taskId}")
  64. public Result fail(@PathVariable String taskId) {
  65. Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
  66. if (task == null) {
  67. return Result.error("流程不存在");
  68. }
  69. //通过审核
  70. HashMap<String, Object> map = new HashMap<>();
  71. // 在流程图中获取进行处理
  72. map.put("approved", false);
  73. taskService.complete(taskId, map);
  74. return Result.ok();
  75. }
  76. /**
  77. * 5. 生成流程图
  78. *
  79. * @param httpServletResponse
  80. * @param processId
  81. * @throws Exception
  82. */
  83. @PostMapping(value = "processDiagram")
  84. public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) {
  85. /**
  86. * 获得当前活动的节点
  87. */
  88. String processDefinitionId = "";
  89. boolean isFinish = historyService.createHistoricProcessInstanceQuery().finished()
  90. .processInstanceId(processId).count() > 0;
  91. if (isFinish) {// 如果流程已经结束,则得到结束节点
  92. HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery()
  93. .processInstanceId(processId).singleResult();
  94. processDefinitionId = pi.getProcessDefinitionId();
  95. } else {// 如果流程没有结束,则取当前活动节点
  96. // 根据流程实例ID获得当前处于活动状态的ActivityId合集
  97. ProcessInstance pi = runtimeService.createProcessInstanceQuery()
  98. .processInstanceId(processId).singleResult();
  99. processDefinitionId = pi.getProcessDefinitionId();
  100. }
  101. List<String> highLightedActivitis = new ArrayList<String>();
  102. /**
  103. * 获得活动的节点
  104. */
  105. List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list();
  106. for (HistoricActivityInstance tempActivity : highLightedActivitList) {
  107. String activityId = tempActivity.getActivityId();
  108. highLightedActivitis.add(activityId);
  109. }
  110. List<String> flows = new ArrayList<>();
  111. //获取流程图
  112. BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
  113. ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
  114. ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
  115. // 注意:如果是PNG格式那么输出的是背景色是黑色,如果连接线上有字不容易看清楚。可以使用bmp
  116. InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitis, flows, engconf.getActivityFontName(),
  117. engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, true);
  118. OutputStream out = null;
  119. byte[] buf = new byte[1024];
  120. int legth = 0;
  121. try {
  122. out = httpServletResponse.getOutputStream();
  123. while ((legth = in.read(buf)) != -1) {
  124. out.write(buf, 0, legth);
  125. }
  126. } catch (IOException e) {
  127. log.error("操作异常", e);
  128. } finally {
  129. IOUtils.closeQuietly(out);
  130. IOUtils.closeQuietly(in);
  131. }
  132. }
  133. }

3. 运行测试

  1. 提交采购订单的审批请求
    http://localhost:7001/orderFlow/start/1/1
    Flowable - 图30
    提交采购订单的审批请求
    返回:
  1. {
  2. "msg": "3c6dc4b2-f30a-11e9-bc68-005056c00008:null",
  3. "code": 0
  4. }
  1. 获取用户的任务
    http://localhost:7001/orderFlow/getTasks/1
    Flowable - 图31
    获取用户的任务
    返回:
  1. {
  2. "msg": "[Task[id=3c6dc4b2-f30a-11e9-bc68-005056c00008, name=订单审批]]",
  3. "code": 0
  4. }
  1. 审批通过
    http://localhost:7001/orderFlow/success/3c6dc4b2-f30a-11e9-bc68-005056c00008
    Flowable - 图32
    审批通过
    返回:
  1. {
  2. "msg": "流程审核通过!",
  3. "code": 0
  4. }
  1. 审批驳回
    http://localhost:7001/orderFlow/fail/fac2256e-f30e-11e9-a987-005056c00008
    任务ID: fac2256e-f30e-11e9-a987-005056c00008
    Flowable - 图33
    审批驳回
    返回:
  1. {
  2. "msg": "success",
  3. "code": 0
  4. }
  1. 生成流程图
    http://localhost:7001/orderFlow/processDiagram?processId=4fdcbade-f311-11e9-acac-005056c00008
    processId: 为流程实例ID
    Flowable - 图34
    生成流程图

4. 事件监听器实现

事件监听器的唯一要求是实现org.flowable.engine.delegate.event.FlowableEventListener。
一个事件侦听器基类,可用于侦听特定类型的实体或所有实体的实体相关事件。它隐藏掉类型检查,并提供4种方法应覆盖:onCreate(..),onUpdate(..)并onDelete(..)创建实体时,更新或删除。

5. 常见问题

  1. “code”:401,”error”:”Unauthorized.”
    排除配置
  1. @SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
  1. 流程文档部署时没生成流程图片
    如果流程文档部署时没生成流程图片,且流程定义中包含必要的“图形交换(diagram interchange)”信息,Flowable引擎会生成流程图。
    如果由于某种原因,不需要或不希望在部署时生成流程图,可以在流程引擎配置中设置isCreateDiagramOnDeploy参数:
  1. <property name="createDiagramOnDeploy" value="false" />
  1. Waiting for changelog lock….
    数据库中执行:
  1. SELECT `LOCKED` FROM workflow_flowable.ACT_DMN_DATABASECHANGELOGLOCK WHERE ID=1
  2. UPDATE workflow_flowable.ACT_DMN_DATABASECHANGELOGLOCK
  3. SET locked=0 WHERE ID=1
  1. 没有生成流程图
    bpmn 任务中没有加入:
  1. <extensionElements>
  2. <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
  3. </extensionElements>

六、Flowable开发—Modeler集成

1. Flowable-Modeler功能

https://gitee.com/lwj/flow-modeler-sduty/tree/master/src/main

提供可视化编辑器,编辑BPMN流程,编辑CASE模型,编辑Form表单,编辑App应用,编辑决策表
提供可视化参数配置:每个流程可以配置详细的参数设置,按照流程对应的规范来设计。
提供导入导出功能:方便将流程结果导入到其他应用程序
在我们实际项目中,我们的流程配置和表单都是在一个系统中操作的,不可能在flowable的war包上做流程配置。
所以集成modeler是flowable使用的开端。

2. 源码下载与编译

  1. 源码下载
    下载地址:
    https://github.com/flowable/flowable-engine
    Flowable - 图35
    源码
  2. 导入idea编译
    Flowable - 图36
    导入idea
    Flowable - 图37
    编译

编译

编译结果:

  1. [INFO] ------------------------------------------------------------------------
  2. [INFO] Reactor Summary for Flowable 6.5.0-SNAPSHOT:
  3. [INFO]
  4. [INFO] Flowable ........................................... SUCCESS [ 0.326 s]
  5. [INFO] Flowable - BPMN Model .............................. SUCCESS [ 0.767 s]
  6. [INFO] Flowable - Process Validation ...................... SUCCESS [ 0.070 s]
  7. [INFO] Flowable - BPMN Layout ............................. SUCCESS [ 0.106 s]
  8. [INFO] Flowable - Image Generator ......................... SUCCESS [ 0.102 s]
  9. [INFO] Flowable - Engine Common API ....................... SUCCESS [ 0.059 s]
  10. [INFO] Flowable - Variable Service API .................... SUCCESS [ 0.036 s]
  11. [INFO] Flowable - Engine Common ........................... SUCCESS [ 0.455 s]
  12. [INFO] Flowable - BPMN Converter .......................... SUCCESS [ 0.143 s]
  13. [INFO] Flowable - Entity Link Service API ................. SUCCESS [ 0.021 s]
  14. [INFO] Flowable - Entity Link Service ..................... SUCCESS [ 0.163 s]
  15. [INFO] Flowable - Variable Service ........................ SUCCESS [ 0.198 s]
  16. [INFO] Flowable - Identity Link Service API ............... SUCCESS [ 0.028 s]
  17. [INFO] Flowable - Identity Link Service ................... SUCCESS [ 0.118 s]
  18. [INFO] Flowable - Event Subscription Service API .......... SUCCESS [ 0.028 s]
  19. [INFO] Flowable - Event Subscription Service .............. SUCCESS [ 0.127 s]
  20. [INFO] Flowable - Task Service API ........................ SUCCESS [ 0.030 s]
  21. [INFO] Flowable - IDM API ................................. SUCCESS [ 0.023 s]
  22. [INFO] Flowable - Task Service ............................ SUCCESS [ 0.169 s]
  23. [INFO] Flowable - Job Service API ......................... SUCCESS [ 0.023 s]
  24. [INFO] Flowable - Job Service ............................. SUCCESS [ 0.179 s]
  25. [INFO] Flowable Job Spring Service ........................ SUCCESS [ 0.141 s]
  26. [INFO] Flowable - Batch Service API ....................... SUCCESS [ 0.017 s]
  27. [INFO] Flowable - Batch Service ........................... SUCCESS [ 0.124 s]
  28. [INFO] Flowable - IDM Engine .............................. SUCCESS [ 0.229 s]
  29. [INFO] flowable-idm-engine-configurator ................... SUCCESS [ 0.108 s]
  30. [INFO] Flowable - Form API ................................ SUCCESS [ 0.023 s]
  31. [INFO] Flowable - Form Model .............................. SUCCESS [ 0.024 s]
  32. [INFO] flowable-form-json-converter ....................... SUCCESS [ 0.037 s]
  33. [INFO] Flowable - Form Engine ............................. SUCCESS [ 0.211 s]
  34. [INFO] Flowable - CMMN Model .............................. SUCCESS [ 0.062 s]
  35. [INFO] Flowable - DMN Model ............................... SUCCESS [ 0.024 s]
  36. [INFO] Flowable - DMN API ................................. SUCCESS [ 0.058 s]
  37. [INFO] Flowable - CMMN API ................................ SUCCESS [ 0.118 s]
  38. [INFO] Flowable - Content API ............................. SUCCESS [ 0.041 s]
  39. [INFO] Flowable - Engine .................................. SUCCESS [ 1.188 s]
  40. [INFO] Flowable - Form Engine Configurator ................ SUCCESS [ 0.384 s]
  41. [INFO] Flowable - CMMN Converter .......................... SUCCESS [ 0.060 s]
  42. [INFO] Flowable - CMMN Image Generator .................... SUCCESS [ 0.041 s]
  43. [INFO] Flowable - CMMN Engine ............................. SUCCESS [ 0.415 s]
  44. [INFO] Flowable - CMMN Engine Configurator ................ SUCCESS [ 0.477 s]
  45. [INFO] Flowable - App Engine API .......................... SUCCESS [ 0.020 s]
  46. [INFO] Flowable - App Engine .............................. SUCCESS [ 0.168 s]
  47. [INFO] flowable-spring-security ........................... SUCCESS [ 0.045 s]
  48. [INFO] ------------------------------------------------------------------------
  49. [INFO] BUILD SUCCESS
  50. [INFO] ------------------------------------------------------------------------
  51. [INFO] Total time: 9.645 s
  52. [INFO] Finished at: 2019-10-20T23:21:14+08:00
  53. [INFO] ------------------------------------------------------------------------

3. Modeler集成

3.1. 拷贝全都静态资源到项目中resources

Flowable - 图38

源码文件

3.2. Maven依赖

由于flowable-modeler的流程设计器页面很多操作会访问后台接口,所以在这里导入依赖文件。

  1. <!--web依赖-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. <exclusions>
  6. <exclusion>
  7. <groupId>org.hibernate.validator</groupId>
  8. <artifactId>hibernate-validator</artifactId>
  9. </exclusion>
  10. </exclusions>
  11. </dependency>
  12. <!--Springboot-->
  13. <dependency>
  14. <groupId>org.springframework.boot</groupId>
  15. <artifactId>spring-boot-starter</artifactId>
  16. <exclusions>
  17. <exclusion>
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot-starter-logging</artifactId>
  20. </exclusion>
  21. </exclusions>
  22. </dependency>
  23. <!--log4j-->
  24. <dependency>
  25. <groupId>org.springframework.boot</groupId>
  26. <artifactId>spring-boot-starter-log4j2</artifactId>
  27. </dependency>
  28. <!--健康检查-->
  29. <dependency>
  30. <groupId>org.springframework.boot</groupId>
  31. <artifactId>spring-boot-starter-actuator</artifactId>
  32. </dependency>
  33. <!--数据库操作依赖-->
  34. <dependency>
  35. <groupId>org.springframework.boot</groupId>
  36. <artifactId>spring-boot-starter-data-jdbc</artifactId>
  37. </dependency>
  38. <dependency>
  39. <groupId>com.h2database</groupId>
  40. <artifactId>h2</artifactId>
  41. </dependency>
  42. <!--mybatis-plus 插件-->
  43. <dependency>
  44. <groupId>com.baomidou</groupId>
  45. <artifactId>mybatis-plus-boot-starter</artifactId>
  46. <version>3.1.2</version>
  47. </dependency>
  48. <!--工作流-->
  49. <dependency>
  50. <groupId>org.flowable</groupId>
  51. <artifactId>flowable-ui-common</artifactId>
  52. <version>6.4.2</version>
  53. </dependency>
  54. <dependency>
  55. <groupId>org.flowable</groupId>
  56. <artifactId>flowable-ui-modeler-conf</artifactId>
  57. <version>6.4.2</version>
  58. </dependency>
  59. <dependency>
  60. <groupId>org.flowable</groupId>
  61. <artifactId>flowable-ui-modeler-rest</artifactId>
  62. <version>6.4.2</version>
  63. </dependency>
  64. <dependency>
  65. <groupId>org.flowable</groupId>
  66. <artifactId>flowable-ui-modeler-logic</artifactId>
  67. <version>6.4.2</version>
  68. </dependency>
  69. <!--单元测试-->
  70. <dependency>
  71. <groupId>org.springframework.boot</groupId>
  72. <artifactId>spring-boot-starter-test</artifactId>
  73. <scope>test</scope>
  74. <exclusions>
  75. <exclusion>
  76. <groupId>org.junit.vintage</groupId>
  77. <artifactId>junit-vintage-engine</artifactId>
  78. </exclusion>
  79. </exclusions>
  80. </dependency>

3.3. 配置文件

配置文件:application.properties

  1. server.port=8002
  2. server.servlet.context-path=/flowable-modeler
  3. management.endpoints.jmx.unique-names=true
  4. # 这是强制使用JDK代理而不是使用CGLIB所必需的。
  5. spring.aop.proxy-target-class=false
  6. spring.aop.auto=false
  7. spring.application.name=flowable-ui-modeler
  8. # 安全配置
  9. spring.security.filter.dispatcher-types=REQUEST,FORWARD,ASYNC
  10. spring.liquibase.enabled=false
  11. # 必须指定用于生成对象名的默认域。否则,当多个spring引导应用程序在同一个servlet容器中启动时
  12. # 所有这些都将使用相同的名称创建(例如com.zaxxer.hikari:name=datasource,type=hikaridatasource)
  13. spring.jmx.default-domain=${spring.application.name}
  14. # 健康检查
  15. # 将所有执行器端点暴露在Web上它们是公开的,但只有经过身份验证的用户才能看到/info和/health
  16. # abd具有access admin的用户才能看到其他用户
  17. management.endpoints.web.exposure.include=*
  18. # 只有在授权用户时才应显示完整的运行状况详细信息
  19. management.endpoint.health.show-details=when_authorized
  20. # 只有具有角色access admin的用户才能访问完整的运行状况详细信息
  21. management.endpoint.health.roles=access-admin
  22. # 数据库 默认H2数据库
  23. spring.datasource.username=flowable
  24. spring.datasource.password=flowable
  25. # 数据库连接池
  26. spring.datasource.hikari.maxLifetime=600000
  27. # 5 minutes
  28. spring.datasource.hikari.idleTimeout=300000
  29. spring.datasource.hikari.minimumIdle=5
  30. spring.datasource.hikari.maximumPoolSize=50
  31. # 大文件上传限制。设置为-1可设置为“无限制”。以字节表示
  32. spring.servlet.multipart.max-file-size=10MB

配置文件:flowable-default.properties

  1. # spring在角色前面加上role。然而,flowable还没有这个概念,所以我们需要用空字符串覆盖它。
  2. flowable.common.app.role-prefix=
  3. flowable.common.app.idm-url=http://localhost:8002/flowable-idm
  4. flowable.common.app.idm-admin.user=admin
  5. flowable.common.app.idm-admin.password=test
  6. flowable.modeler.app.deployment-api-url=http://localhost:8002/flowable-task/app-api
  7. # Rest API
  8. flowable.modeler.app.rest-enabled=true
  9. flowable.rest.app.authentication-mode=verify-privilege

配置文件:version.properties

  1. type=modeler
  2. version.major=6
  3. version.minor=4
  4. version.revision=2
  5. version.edition=Flowable

3.4. 创建流程模型包

创建包:org.flowable.ui
在java中创建包名:org.flowable.ui

Flowable - 图39

创建包

3.5. 去除认证

1)创建org.flowable.ui.common.rest.idm.remote包,添加类:

  1. import org.flowable.ui.common.model.UserRepresentation;
  2. import org.flowable.ui.common.security.DefaultPrivileges;
  3. import org.flowable.ui.common.service.exception.NotFoundException;
  4. import org.flowable.ui.common.service.idm.RemoteIdmService;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RequestMethod;
  8. import org.springframework.web.bind.annotation.RestController;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11. @RestController
  12. @RequestMapping("/app")
  13. public class RemoteAccountResource {
  14. @Autowired
  15. private RemoteIdmService remoteIdmService;
  16. /**
  17. * GET /rest/account -> get the current user.
  18. */
  19. @RequestMapping(value = "/rest/account", method = RequestMethod.GET, produces = "application/json")
  20. public UserRepresentation getAccount() {
  21. // UserRepresentation userRepresentation = null;
  22. // String currentUserId = SecurityUtils.getCurrentUserId();
  23. // if (currentUserId != null) {
  24. // RemoteUser remoteUser = remoteIdmService.getUser(currentUserId);
  25. // if (remoteUser != null) {
  26. // userRepresentation = new UserRepresentation(remoteUser);
  27. //
  28. // if (remoteUser.getGroups() != null && remoteUser.getGroups().size() > 0) {
  29. // List<GroupRepresentation> groups = new ArrayList<>();
  30. // for (RemoteGroup remoteGroup : remoteUser.getGroups()) {
  31. // groups.add(new GroupRepresentation(remoteGroup));
  32. // }
  33. // userRepresentation.setGroups(groups);
  34. // }
  35. //
  36. // if (remoteUser.getPrivileges() != null && remoteUser.getPrivileges().size() > 0) {
  37. // userRepresentation.setPrivileges(remoteUser.getPrivileges());
  38. // }
  39. //
  40. // }
  41. // }
  42. UserRepresentation userRepresentation = new UserRepresentation();
  43. userRepresentation.setFirstName("admin");
  44. userRepresentation.setLastName("admin");
  45. userRepresentation.setFullName("admin");
  46. userRepresentation.setId("admin");
  47. List<String> pris = new ArrayList<>();
  48. pris.add(DefaultPrivileges.ACCESS_MODELER);
  49. pris.add(DefaultPrivileges.ACCESS_IDM);
  50. pris.add(DefaultPrivileges.ACCESS_ADMIN);
  51. pris.add(DefaultPrivileges.ACCESS_TASK);
  52. pris.add(DefaultPrivileges.ACCESS_REST_API);
  53. userRepresentation.setPrivileges(pris);
  54. if (userRepresentation != null) {
  55. return userRepresentation;
  56. } else {
  57. throw new NotFoundException();
  58. }
  59. }
  60. }

2)创建包:org.flowable.ui.common.security 包添加以下类:

  1. import org.flowable.idm.api.User;
  2. import org.flowable.ui.common.model.RemoteUser;
  3. import org.springframework.security.core.GrantedAuthority;
  4. import org.springframework.security.core.context.SecurityContext;
  5. import org.springframework.security.core.context.SecurityContextHolder;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. public class SecurityUtils {
  9. private static User assumeUser;
  10. private SecurityUtils() {
  11. }
  12. /**
  13. * Get the login of the current user.
  14. */
  15. public static String getCurrentUserId() {
  16. User user = getCurrentUserObject();
  17. if (user != null) {
  18. return user.getId();
  19. }
  20. return null;
  21. }
  22. /**
  23. * @return the {@link User} object associated with the current logged in user.
  24. */
  25. public static User getCurrentUserObject() {
  26. if (assumeUser != null) {
  27. return assumeUser;
  28. }
  29. // User user = null;
  30. // FlowableAppUser appUser = getCurrentFlowableAppUser();
  31. // if (appUser != null) {
  32. // user = appUser.getUserObject();
  33. // }
  34. RemoteUser user = new RemoteUser();
  35. // FlowableAppUser appUser = getCurrentFlowableAppUser();
  36. // if (appUser != null) {
  37. // user = appUser.getUserObject();
  38. // }
  39. user.setId("admin");
  40. user.setDisplayName("admin");
  41. user.setFirstName("admin");
  42. user.setLastName("admin");
  43. user.setEmail("admin@admin.com");
  44. user.setPassword("test");
  45. List<String> pris = new ArrayList<>();
  46. pris.add(DefaultPrivileges.ACCESS_MODELER);
  47. pris.add(DefaultPrivileges.ACCESS_IDM);
  48. pris.add(DefaultPrivileges.ACCESS_ADMIN);
  49. pris.add(DefaultPrivileges.ACCESS_TASK);
  50. pris.add(DefaultPrivileges.ACCESS_REST_API);
  51. user.setPrivileges(pris);
  52. return user;
  53. }
  54. public static FlowableAppUser getCurrentFlowableAppUser() {
  55. FlowableAppUser user = null;
  56. SecurityContext securityContext = SecurityContextHolder.getContext();
  57. if (securityContext != null && securityContext.getAuthentication() != null) {
  58. Object principal = securityContext.getAuthentication().getPrincipal();
  59. if (principal instanceof FlowableAppUser) {
  60. user = (FlowableAppUser) principal;
  61. }
  62. }
  63. return user;
  64. }
  65. public static boolean currentUserHasCapability(String capability) {
  66. FlowableAppUser user = getCurrentFlowableAppUser();
  67. for (GrantedAuthority grantedAuthority : user.getAuthorities()) {
  68. if (capability.equals(grantedAuthority.getAuthority())) {
  69. return true;
  70. }
  71. }
  72. return false;
  73. }
  74. public static void assumeUser(User user) {
  75. assumeUser = user;
  76. }
  77. public static void clearAssumeUser() {
  78. assumeUser = null;
  79. }
  80. }

3)创建包:org.flowable.ui.modeler.conf 添加安全配置类:

  1. import org.flowable.ui.common.properties.FlowableRestAppProperties;
  2. import org.flowable.ui.common.security.ActuatorRequestMatcher;
  3. import org.flowable.ui.common.security.ClearFlowableCookieLogoutHandler;
  4. import org.flowable.ui.common.security.DefaultPrivileges;
  5. import org.flowable.ui.modeler.properties.FlowableModelerAppProperties;
  6. import org.flowable.ui.modeler.security.AjaxLogoutSuccessHandler;
  7. import org.flowable.ui.modeler.security.RemoteIdmAuthenticationProvider;
  8. import org.slf4j.Logger;
  9. import org.slf4j.LoggerFactory;
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
  12. import org.springframework.boot.actuate.health.HealthEndpoint;
  13. import org.springframework.boot.actuate.info.InfoEndpoint;
  14. import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
  15. import org.springframework.context.annotation.Configuration;
  16. import org.springframework.core.annotation.Order;
  17. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  18. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  19. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  20. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  21. import org.springframework.security.config.http.SessionCreationPolicy;
  22. import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;
  23. /**
  24. */
  25. @Configuration
  26. @EnableWebSecurity
  27. public class SecurityConfiguration {
  28. private static final Logger LOGGER = LoggerFactory.getLogger(org.flowable.ui.modeler.conf.SecurityConfiguration.class);
  29. public static final String REST_ENDPOINTS_PREFIX = "/app/rest";
  30. @Autowired
  31. protected RemoteIdmAuthenticationProvider authenticationProvider;
  32. // @Bean
  33. // public FlowableCookieFilterRegistrationBean flowableCookieFilterRegistrationBean(RemoteIdmService remoteIdmService, FlowableCommonAppProperties properties) {
  34. // FlowableCookieFilterRegistrationBean filter = new FlowableCookieFilterRegistrationBean(remoteIdmService, properties);
  35. // filter.addUrlPatterns("/app/*");
  36. // filter.setRequiredPrivileges(Collections.singletonList(DefaultPrivileges.ACCESS_MODELER));
  37. // return filter;
  38. // }
  39. @Autowired
  40. public void configureGlobal(AuthenticationManagerBuilder auth) {
  41. // Default auth (database backed)
  42. try {
  43. auth.authenticationProvider(authenticationProvider);
  44. } catch (Exception e) {
  45. LOGGER.error("Could not configure authentication mechanism:", e);
  46. }
  47. }
  48. @Configuration
  49. @Order(10)
  50. public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
  51. // @Autowired
  52. // protected FlowableCookieFilterRegistrationBean flowableCookieFilterRegistrationBean;
  53. @Autowired
  54. protected AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler;
  55. @Override
  56. protected void configure(HttpSecurity http) throws Exception {
  57. http
  58. .sessionManagement()
  59. .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  60. .and()
  61. // .addFilterBefore(flowableCookieFilterRegistrationBean.getFilter(), UsernamePasswordAuthenticationFilter.class)
  62. .logout()
  63. .logoutUrl("/app/logout")
  64. .logoutSuccessHandler(ajaxLogoutSuccessHandler)
  65. .addLogoutHandler(new ClearFlowableCookieLogoutHandler())
  66. .and()
  67. .csrf()
  68. .disable() // Disabled, cause enabling it will cause sessions
  69. .headers()
  70. .frameOptions()
  71. .sameOrigin()
  72. .addHeaderWriter(new XXssProtectionHeaderWriter())
  73. .and()
  74. .authorizeRequests()
  75. // .antMatchers(REST_ENDPOINTS_PREFIX + "/**").hasAuthority(DefaultPrivileges.ACCESS_MODELER);
  76. .antMatchers(REST_ENDPOINTS_PREFIX + "/**").permitAll();
  77. }
  78. }
  79. //
  80. // BASIC AUTH
  81. //
  82. @Configuration
  83. @Order(1)
  84. public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
  85. protected final FlowableRestAppProperties restAppProperties;
  86. protected final FlowableModelerAppProperties modelerAppProperties;
  87. public ApiWebSecurityConfigurationAdapter(FlowableRestAppProperties restAppProperties,
  88. FlowableModelerAppProperties modelerAppProperties) {
  89. this.restAppProperties = restAppProperties;
  90. this.modelerAppProperties = modelerAppProperties;
  91. }
  92. protected void configure(HttpSecurity http) throws Exception {
  93. http
  94. .sessionManagement()
  95. .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  96. .and()
  97. .csrf()
  98. .disable();
  99. http.antMatcher("/api/**").authorizeRequests().antMatchers("/api/**").permitAll();
  100. // if (modelerAppProperties.isRestEnabled()) {
  101. //
  102. // if (restAppProperties.isVerifyRestApiPrivilege()) {
  103. // http.antMatcher("/api/**").authorizeRequests().antMatchers("/api/**").hasAuthority(DefaultPrivileges.ACCESS_REST_API).and().httpBasic();
  104. // } else {
  105. // http.antMatcher("/api/**").authorizeRequests().antMatchers("/api/**").authenticated().and().httpBasic();
  106. //
  107. // }
  108. //
  109. // } else {
  110. // http.antMatcher("/api/**").authorizeRequests().antMatchers("/api/**").denyAll();
  111. //
  112. // }
  113. }
  114. }
  115. //
  116. // Actuator
  117. //
  118. @ConditionalOnClass(EndpointRequest.class)
  119. @Configuration
  120. @Order(5) // Actuator configuration should kick in before the Form Login there should always be http basic for the endpoints
  121. public static class ActuatorWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
  122. protected void configure(HttpSecurity http) throws Exception {
  123. http
  124. .sessionManagement()
  125. .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  126. .and()
  127. .csrf()
  128. .disable();
  129. http
  130. .requestMatcher(new ActuatorRequestMatcher())
  131. .authorizeRequests()
  132. .requestMatchers(EndpointRequest.to(InfoEndpoint.class, HealthEndpoint.class)).authenticated()
  133. .requestMatchers(EndpointRequest.toAnyEndpoint()).hasAnyAuthority(DefaultPrivileges.ACCESS_ADMIN)
  134. .and().httpBasic();
  135. }
  136. }
  137. }

3.6. 启动类配置

修改原有启动类:包名com.xtsz.modeler

  1. package com.xtsz.modeler;
  2. import org.flowable.ui.modeler.conf.ApplicationConfiguration;
  3. import org.flowable.ui.modeler.servlet.AppDispatcherServletConfiguration;
  4. import org.springframework.boot.SpringApplication;
  5. import org.springframework.boot.autoconfigure.SpringBootApplication;
  6. import org.springframework.context.annotation.Import;
  7. /**
  8. * 导入配置
  9. */
  10. @Import({
  11. ApplicationConfiguration.class,
  12. AppDispatcherServletConfiguration.class
  13. })
  14. @SpringBootApplication
  15. public class ModelerApplication {
  16. public static void main(String[] args) {
  17. SpringApplication.run(ModelerApplication.class, args);
  18. }
  19. }

3.7. 流程模型汉化

拷贝文件到resources目录:

Flowable - 图40

源汉化文件

Flowable - 图41

目标汉化文件

4. 启动测试

  1. 请求地址:http://localhost:8002/flowable-modeler/
    Flowable - 图42
    模型流程
  2. 创建流程
    Flowable - 图43
    创建流程
    Flowable - 图44
    创建流程
    Flowable - 图45
    设计流程

5. 常见问题

  1. Cannot convert value ‘2019-10-22 08:09:24.000000’ from column 6 to TIMESTAMP
    原因:MySql数据库必须使用8.0.0+。
    依赖:
  1. <dependency>
  2. <groupId>mysql</groupId>
  3. <artifactId>mysql-connector-java</artifactId>
  4. <scope>runtime</scope>
  5. </dependency>

数据库配置:

  1. # 数据库
  2. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  3. spring.datasource.url=jdbc:mysql://localhost:3306/workflow_flowable?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2b8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
  4. spring.datasource.username=root
  5. spring.datasource.password=1234

七、Flowable开发—核心数据库表

1. 数据模型设计

  1. 清单 | 数据表分类 | 描述 | | :—- | :—- | | ACTGE | 通用数据表 | | ACTRE | 流程定义存储表 | | ACTID | 身份信息表 | | ACTRU | 运行时数据库表 | | ACTHI* | 历史数据库表 |
  1. 通用数据库 | 数据表 | 描述 | | :—- | :—- | | ACT_GE_PROPERTY | 属性表(保存流程引擎的kv键值属性)—PropertyEntityImpl | | ACT_GE_BTYEARRAY | 资源表(存储流程定义相关的资源)—ByteArrayEntityImpl |
  1. 流程定义存储表 | 数据表 | 描述 | | :—- | :—- | | ACT_RE_DEPLOYMENT | 流程部署表—DeploymentEntityImpl | | ACT_RE_PROCDEF | 流程定义信息表—ProcessDefinitionEntityImpl | | ACT_RE_MODEL | 模型信息表(用于Web设计器)—ModelEntityImpl | | ACT_PROCDEF_INFO | 流程定义动态改变信息表—ProcessDefinitionInfoEntityImpl |
  1. 身份数据表 | 数据表 | 描述 | | :—- | :—- | | ACT_ID_USER | 用户基本信息表—UserEntityImpl | | ACT_ID_INFO | 用户扩展表—IdentityInfoEntityImpl | | ACT_ID_GROUP | 群组表(用于Web设计器)—GroupEntityImpl | | ACT_ID_MEMBERSHIP | 户与群主关系表—MemberShipEntityImpl | | ACT_ID_BYTEARRAY | 二进制数据表(flowable)— | | ACT_ID_PRIV | 权限表(flowable)— | | ACT_ID_PRIV_MAPPING | 用户或组权限关系表(flowable)— | | ACT_ID_PROPERTY | 属性表(flowable)— | | ACT_ID_TOKEN | 系统登录日志表(flowable)— |
  1. 运行时流程数据表 | 数据表 | 描述 | | :—- | :—- | | ACT_RU_EXECUTION | 流程实例与分支执行表—ExecutionEntityImpl | | ACT_RU_TASK | 用户任务表—TaskEntityImpl | | ACT_RU_VARIABLE | 变量信息—VariableInstanceEntityImpl | | ACT_RU_IDENTITYLINK | 参与者相关信息表—IdentityLinkEntityImpl | | ACT_RU_EVENT_SUBSCR | 事件订阅表—EventSubscriptionEntityImpl | | ACT_RU_JOB | 作业表—JobEntityImpl | | ACT_RU_TIMER_JOB | 定时器表—TimerJobEntityImpl | | ACT_RU_SUSPENDED_JOB | 暂停作业表—SuspendedJobEntityImpl | | ACT_RU_DEADLETTER_JOB | 死信表—DeadLetterJobEntityImpl | | ACT_RU_HISTORY_JOB | 历史作业表(flowable)— |
  1. 历史流程数据表 | 数据表 | 描述 | | :—- | :—- | | ACT_HI_PROCINST | 历史流程实例表—HistoricProcessInstanceEntityImpl | | ACT_HI_ACTINST | 历史节点信息表—HistoricActivityInstanceEntityImpl | | ACT_HI_TASKINST | 历史任务表—HistoricTaskInstanceEntityImpl | | ACT_HI_VARINST | 历史变量—HistoricVariableInstanceEntityImpl | | ACT_HI_IDENTITYLINK | 历史参与者表—HistoricIdentityLinkEntityImpl | | ACT_HI_DETAIL | 历史的流程运行中的细节信息—HistoricDetailEntityImpl | | ACT_HI_ATTACHMENT | 附件表—AttachmentEntityImpl | | ACT_HI_COMMENT | 评论表—CommentEntityImpl | | ACT_EVT_LOG | 事件日志表—EventLogEntryEntityImpl |

2. 表结构

  1. 通用类表

act_ge_property(全局配置文件)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
NAME_ 名称 NO varchar 64 PRI schema.version schema.history next.dbid
VALUE_ NULL YES varchar 300 5. create(5.)
REV_ 版本号 NULL YES int NULL version
  1. 注:
  2. 1.全局参数, 默认三个参数next.dbid IdGenerator区间, schema.history 自动执行sql历史, schema.version
  3. sql版本。
  4. 2.属性数据表。存储整个流程引擎级别的数据。

act_ge_bytearray(二进制文件)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NO varchar 64 PRI
REV_ 版本号 NULL YES int NULL version
NAME_ 名称 NULL YES varchar 255 部署的文件名称,如:mail.bpmn、mail.png 、mail.bpmn20.xml
DEPLOYMENTID 部署ID NULL YES varchar 64 ACT_RE_DEPLOYMENT
BYTES_ 字节(二进制数据) NULL YES longblob 4294967295
GENERATED_ 是否系统生成 NULL YES tinyint NULL 0为用户上传,1为系统自动生成, 比如系统会自动根据xml生成png
  1. 注:
  2. 1.用来保存部署文件的大文本数据
  3. 2.所有二进制内容都会保存在这个表里, 比如部署的process.bpmn20.xml, process.png, user.form, 附件, bean序列
  4. 化为二进制的流程变量。
  5. act_ge_property属性数据表存储整个流程引擎级别的数据,初始化表结构时,会默认插入三条记录。
  1. 历史类表

act_hi_actinst(历史节点表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NULL NO varchar 64 PRI
PROCDEF_ID 流程定义ID NULL NO varchar 64
PROCINST_ID 流程实例ID NULL NO varchar 64 MUL
ACTID 节点ID NULL NO varchar 255
TASKID 任务ID NULL YES varchar 64 任务实例ID 其他节点类型实例ID在这里为空
CALLPROC_INST_ID 调用外部的流程实例ID NULL YES varchar 64
ACTNAME 节点名称 NULL YES varchar 255
ACTTYPE 节点类型 NULL NO varchar 255 如startEvent、userTask
ASSIGNEE_ 签收人 NULL YES varchar 255 经办人
STARTTIME 开始时间 NULL NO datetime NULL MUL
ENDTIME 结束时间 NULL YES datetime NULL MUL
DURATION_ 耗时 NULL YES bigint NULL 毫秒值
TENANTID 多租户 YES varchar 255
  1. 注:
  2. 1. 历史活动信息。这里记录流程流转过的所有节点,与HI_TASKINST不同的是,taskinst只记录usertask内容。
  3. 2. TENANT_ID 是后续才加入的多租户

act_hi_attachment(历史附件表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键ID NULL NO varchar 64 PRI
REV_ 版本号 NULL YES int NULL version
USERID 用户ID NULL YES varchar 255
NAME_ 名称 NULL YES varchar 255
DESCRIPTION_ 描述 NULL YES varchar 4000
TYPE_ 类型 NULL YES varchar 255
TASKID 任务ID NULL YES varchar 64
PROCINST_ID 流程实例ID NULL YES varchar 64
URL_ 附件地址 NULL YES varchar 4000 附件的URL地址
CONTENTID 字节表ID NULL YES varchar 64 ACT_GE_BYTEARRAY的ID
TIME_ 时间 NULL YES datetime NULL

注:
1.存放历史流程相关的附件。
2.时间是后续版本加入

act_hi_comment(历史审批意见表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NULL NO varchar 64 PRI
TYPE_ 类型 NULL YES varchar 255 类型:event(事件) comment(意见)
TIME_ 时间 NULL NO datetime NULL
USERID 用户ID NULL YES varchar 255
TASKID 任务ID NULL YES varchar 64
PROCINST_ID 流程实例ID NULL YES varchar 64
ACTION_ 行为类型 NULL YES varchar 255
MESSAGE_ 基本内容 NULL YES varchar 4000 用于存放流程产生的信息,比如审批意见
FULLMSG 全部内容 NULL YES longblob 4294967295 附件

注:

  1. 存放历史流程的审批意见。
  2. 行为类型。值为下列内容中的一种:AddUserLink、DeleteUserLink、AddGroupLink、DeleteGroupLink、AddComment、AddAttachment、DeleteAttachment

act_hi_detail(历史详情信息表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NULL NO varchar 64 PRI
TYPE_ 类型 NULL NO varchar 255 类型: FormProperty, //表单 VariableUpdate //参数
PROCINST_ID 流程实例ID NULL YES varchar 64 MUL
EXECUTIONID 执行实例 NULL YES varchar 64
TASKID 任务ID NULL YES varchar 64 MUL
ACTINST_ID 节点实例ID NULL YES varchar 64 ACT_HI_ACTINST
NAME_ 名称 NULL NO varchar 255 MUL
VARTYPE 参数类型 NULL YES varchar 255
REV_ 版本号 NULL YES int NULL version
TIME_ 时间戳 NULL NO datetime NULL MUL 创建时间
BYTEARRAYID 字节表ID NULL YES varchar 64 ACT_GE_BYTEARRAY
DOUBLE_ 浮点值 NULL YES double NULL 存储变量类型为Double
LONG_ 长整型 NULL YES bigint NULL 存储变量类型为long
TEXT_ 文本值 NULL YES varchar 4000 存储变量值类型为String
TEXT2_ 字符串 NULL YES varchar 4000 此处存储的是JPA持久化对象时,才会有值。此值为对象ID,jpa变量text存className,text2存id

注:
1.历史详情表:流程中产生的变量详细,包括控制流程流转的变量,业务表单中填写的流程需要用到的变量等。
2.参数类型: jpa-entity、boolean、bytes、serializable(可序列化)、自定义type(根据你自身配置)、CustomVariableType、date、double、integer、long、null、short、string

act_hi_identitylink(历史流程人员表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
NO varchar 64 PRI
GROUPID 用户组ID NULL YES varchar 255
TYPE_ 类型 NULL YES varchar 255 类型,主要分为以下几种:assignee、candidate、owner、starter 、participant
USERID 用户ID NULL YES varchar 255 MUL
TASKID 任务ID NULL YES varchar 64 MUL
PROCINST_ID 流程实例ID NULL YES varchar 64 MUL

注:

  1. 任务参与者数据表。主要存储当前节点参与者的信息。

act_hi_procinst(流程实例历史*核心表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NULL NO varchar 64 PRI
PROCINST_ID 流程实例ID NULL NO varchar 64 UNI
BUSINESSKEY 业务标识 NULL YES varchar 255 MUL 业务主键,业务表单的ID
PROCDEF_ID 流程实例ID NULL NO varchar 64
STARTTIME 开始时间 NULL NO datetime NULL
ENDTIME 结束时间 NULL YES datetime NULL MUL
DURATION_ 耗时 NULL YES bigint NULL
STARTUSER_ID 流程发起人ID NULL YES varchar 255
STARTACT_ID 开始节点ID NULL YES varchar 255
ENDACT_ID 结束节点ID NULL YES varchar 255
SUPERPROCESS_INSTANCE_ID 父流程实例ID NULL YES varchar 64
DELETEREASON 删除原因 NULL YES varchar 4000
TENANTID 租户ID YES varchar 255
NAME_ 名称 NULL YES varchar 255

注:
1.核心表之一。
2.存放历史的流程实例。
3.设计历史流程实例表的初衷之一就是为了使得运行时库数据量尽可能小,效率最优。

act_hi_taskinst(历史任务流程实例信息*核心表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NULL NO varchar 64 PRI
PROCDEF_ID 流程实例ID NULL YES varchar 64
TASKDEF_KEY 任务节点定义ID NULL YES varchar 255 任务定义标识(环节ID)
PROCINST_ID 流程实例ID NULL YES varchar 64 MUL
EXECUTIONID 执行实例ID NULL YES varchar 64
NAME_ 任务名称 NULL YES varchar 255
PARENTTASK_ID 父任务节点ID NULL YES varchar 64
DESCRIPTION_ 描述 NULL YES varchar 4000
OWNER_ 被代理人 NULL YES varchar 255 委托人(默认为空,只有在委托时才有值)
ASSIGNEE_ 经办人 NULL YES varchar 255
STARTTIME 开始时间 NULL NO datetime NULL
CLAIMTIME 签收时间 NULL YES datetime NULL
ENDTIME 结束时间 NULL YES datetime NULL
DURATION_ 耗时 NULL YES bigint NULL
DELETEREASON 删除原因 NULL YES varchar 4000 删除原因(completed,deleted)
PRIORITY_ 优先级 NULL YES int NULL
DUEDATE 截止时间 NULL YES datetime NULL 过期时间,表明任务应在多长时间内完成
FORMKEY FORM表单的KEY NULL YES varchar 255 desinger节点定义的 form_key属性
CATEGORY_ 分类 NULL YES varchar 255
TENANTID 租户ID YES varchar 255

注:

  1. 1. 历史任务实例表。
  2. 2. 存放已经办理的任务。
  3. 3. CATEGORYTNANT_ID是后续版本才加进来的。

act_hi_varinst(历史变量表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NULL NO varchar 64 PRI
PROCINST_ID 流程实例ID NULL YES varchar 64 MUL
EXECUTIONID 执行实例ID NULL YES varchar 64
TASKID 任务ID NULL YES varchar 64 MUL
NAME_ 名称 NULL NO varchar 255 MUL
VARTYPE 变量类型 NULL YES varchar 100
REV_ 版本号 NULL YES int NULL version
BYTEARRAYID 字节流ID NULL YES varchar 64 ACT_GE_BYTEARRAY
DOUBLE_ 浮点值 NULL YES double NULL 存储DoubleType类型的数据
LONG_ 长整型 NULL YES bigint NULL 存储LongType类型的数据
TEXT_ 文本值 NULL YES varchar 4000 存储变量值类型为String,如此处存储持久化对象时,值jpa对象的class
TEXT2_ 文本值 NULL YES varchar 4000
CREATETIME 创建时间 NULL YES datetime NULL
LASTUPDATED_TIME 最后更新时间 NULL YES datetime NULL

注:

  1. 主要存放历史变量数据。

act_evt_log(事件日志)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
LOGNR 主键 NULL NO bigint NULL PRI
TYPE_ 类型 NULL YES varchar 64
PROCDEF_ID 流程定义ID NULL YES varchar 64
PROCINST_ID 流程实例ID NULL YES varchar 64
EXECUTIONID 执行ID NULL YES varchar 64
TASKID 任务ID NULL YES varchar 64
TIMESTAMP 时间 CURRENT_TIMESTAMP(3) NO timestamp NULL
USERID 用户ID NULL YES varchar 255
DATA_ 数据 NULL YES longblob 4294967295
LOCKOWNER 锁定节点 NULL YES varchar 255
LOCKTIME 锁定时间 NULL YES timestamp NULL
ISPROCESSED 是否正在执行 0 YES tinyint NULL

注:
1.事件日志表
2.事件日志, 默认不开启。
3.从Activiti 5.16开始,引入了(试验性)的事件记录机制。记录机制基于Activiti引擎的事件机制的一般用途,并默认禁用。其思想是,来源于引擎的事件会被捕获,并创建一个包含了所有事件数据(甚至更多)的映射,提供给
org.activiti.engine.impl.event.logger.EventFlusher,由它将这些数据刷入其他地方。默认情况下,使用简单的基于数据库的事件处理/刷入,会使用Jackson将上述映射序列化为JSON,并将其作为EventLogEntryEntity接口存入数据库。如果不使用事件记录,可以删除这个表。
4.配置启用事件日志:
processEngineConfiguration.setEnableDatabaseEventLogging(true);
5.运行时启用事件日志:
databaseEventLogger = new EventLogger(processEngineConfiguration.getClock());
runtimeService.addEventListener(databaseEventLogger);
6.可以扩展EventLogger类。如果默认的数据库记录不符合要求,需要覆盖createEventFlusher()方法返回一个org.activiti.engine.impl.event.logger.EventFlusher接口的实例。可以通过Activiti的
managementService.getEventLogEntries(startLogNr, size)?获取EventLogEntryEntity实例。
容易看出这个表中的数据可以通过JSON放入大数据NoSQL存储,例如MongoDB,Elastic Search,等等。
也容易看出这里使用的类
(org.activiti.engine.impl.event.logger.EventLogger/EventFlusher与许多其他 EventHandler类)是可插入的,可以按你的使用场景调整(例如不将JSON存入数据库,而是将其直接发送给一个队列或大数据存储)。
请注意这个事件记录机制是额外于Activiti的“传统”历史管理器的。尽管所有数据都在数据库表中,但并未对查询或快速恢复做优化。实际使用场景是末端审计并将其存入大数据存储。

用户身份类
act_id_group(用户组)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NO varchar 64 PRI
REV_ 版本号 NULL YES int NULL version
NAME_ 名称 NULL YES varchar 255
TYPE_ 类型 NULL YES varchar 255

注:
1.Activiti自带的用户组表,用于组任务。

act_id_info(用户扩展信息表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NO varchar 64 PRI
REV_ 版本号 NULL YES int NULL version
USERID 用户ID NULL YES varchar 64
TYPE_ 类型 NULL YES varchar 64
KEY_ 属性名 NULL YES varchar 255
VALUE_ 属性值 NULL YES varchar 255
PASSWORD_ 密码 NULL YES longblob 4294967295
PARENTID 父级ID NULL YES varchar 255

注:

act_id_membership( 用户与分组对应信息表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
USERID 用户ID NO varchar 64 PRI(ACT_ID_USER)
GROUPID 用户组ID NO varchar 64 PRI(ACT_ID_GROUP)

注:
1.用来保存用户的分组信息。

act_id_user(用户信息表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NO varchar 64 PRI
REV_ 版本号 NULL YES int NULL version
FIRST_ NULL YES varchar 255 FIRST_NAME
LAST_ NULL YES varchar 255 LAST_NAME
EMAIL_ 邮箱 NULL YES varchar 255
PWD_ 密码 NULL YES varchar 255
PICTUREID 头像ID NULL YES varchar 64 ACT_GE_BYTEARRAY

注:
1.Activiti用户信息表。

流程定义存储表

act_procdef_info(流程定义更新信息)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NULL NO varchar 64 PRI
PROCDEF_ID 流程定义ID NULL NO varchar 64 UNI(ACT_RE_PROCDEF)
REV_ 版本号 NULL YES int NULL version
INFOJSON_ID 内容 NULL YES varchar 64 MUL(ACT_GE_BYTEARRAY)

注:
1.流程版本升级的数据。

act_re_deployment( 部署信息表*核心表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NO varchar 64 PRI
NAME_ 名称 NULL YES varchar 255
CATEGORY_ 分类 NULL YES varchar 255
TENANTID 租户ID YES varchar 255
DEPLOYTIME 部署时间 NULL YES timestamp NULL

注:
1. 部署流程定义时需要被持久化保存下来的信息。

act_re_model( 流程设计模型部署表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NULL NO varchar 64 PRI
REV_ 版本号 NULL YES int NULL version
NAME_ 名称 NULL YES varchar 255
KEY_ 标识 NULL YES varchar 255
CATEGORY_ 分类 NULL YES varchar 255
CREATETIME 创建时间 NULL YES imestamp NULL
LASTUPDATE_TIME 最后更新时间 NULL YES timestamp NULL
VERSION_ 版本 NULL YES int NULL
METAINFO 元数据 NULL YES varchar 4000 以json格式保存流程定义的信息
DEPLOYMENTID 部署ID NULL YES varchar 64 MUL(ACT_RE_DEPLOYMENT)
EDITORSOURCE_VALUE_ID 二进制文件ID NULL YES varchar 64 MUL(ACT_GE_BYTEARRAY) 设计器原始信息
EDITORSOURCE_EXTRA_VALUE_ID 二进制文件ID NULL YES varchar 64 MUL(ACT_GE_BYTEARRAY) 设计器扩展信息
TENANTID 租户ID YES varchar 255

注:
1.该表是流程设计器设计流程模型保存的数据。

act_re_procdef(流程定义数据表*核心表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NULL NO varchar 64 PRI
REV_ 版本号 NULL YES int NULL version
CATEGORY_ 分类 NULL YES varchar 255 流程定义的Namespace就是类别
NAME_ 名称 NULL YES varchar 255
KEY_ 标识 NULL NO varchar 255 MUL
VERSION_ 版本 NULL NO int NULL
DEPLOYMENTID 部署ID NULL YES varchar 64
RESOURCENAME 资源名称 NULL YES varchar 4000 流程bpmn文件名称
DGRMRESOURCE_NAME 图片资源名称 NULL YES varchar 4000
DESCRIPTION_ 描述 NULL YES varchar 4000
HASSTART_FORM_KEY 拥有开始表单标识 NULL YES tinyint NULL start节点是否存在formKey 0否 1是
HASGRAPHICAL_NOTATION 拥有图形信息 NULL YES tinyint NULL
SUSPENSIONSTATE 挂起状态 NULL YES int NULL 暂停状态 1激活 2暂停
TENANTID 租户ID YES varchar 255

注:
1. 业务流程定义数据表。此表和ACTRE_DEPLOYMENT是多对一的关系,即,一个部署的bar包里可能包含多个流程定义文件,每个流程定义文件都会有一条记录在ACT_REPROCDEF表内,每个流程定义的数据,都会对于ACT_GE_BYTEARRAY表内的一个资源文件和PNG图片文件。和ACT_GE_BYTEARRAY的关联是通过程序用ACT_GE_BYTEARRAY.NAME与ACT_RE_PROCDEF.NAME完成的,在数据库表结构中没有体现。

运行时流程数据表
act_ru_event_subscr(事件订阅)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NULL NO varchar 64 PRI
REV_ 版本号 NULL YES int NULL varsion
EVENTTYPE 事件类型 NULL NO varchar 255
EVENTNAME 事件名称 NULL YES varchar 255
EXECUTIONID 执行实例ID NULL YES varchar 64 MUL(ACT_RU_EXECUTION)
PROCINST_ID 流程实例ID NULL YES varchar 64
ACTIVITYID 节点ID NULL YES varchar 64
CONFIGURATION_ 配置 NULL YES varchar 255 MUL
CREATED_ 创建时间 CURRENT_TIMESTAMP(3) NO timestamp NULL
PROCDEF_ID 流程定义ID NULL YES varchar 64
TENANTID 租户ID YES varchar 255

注:
1.该表是后续版本加进来的。

act_ru_execution(运行时流程执行实例表*核心表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NO varchar 64 PRI
REV_ 版本号 NULL YES int NULL
PROCINST_ID 流程实例ID NULL YES varchar 64 MUL(ACT_RU_EXECUTION)
BUSINESSKEY 业务标识 NULL YES varchar 255 MUL
PARENTID 父级ID NULL YES varchar 64 MUL(ACT_RU_EXECUTION)
PROCDEF_ID 流程定义ID NULL YES varchar 64 MUL(ACT_RE_PROCDEF)
SUPEREXEC 父流程实例中对应的执行 NULL YES varchar 64 MUL(ACT_RU_EXECUTION)
ACTID 节点ID NULL YES varchar 255
ISACTIVE 是否激活 NULL YES tinyint NULL
ISCONCURRENT 是否分支(并行) NULL YES tinyint NULL 是否为并行(true/false)
ISSCOPE 是否处于多实例或环 节嵌套状态 NULL YES tinyint NULL
ISEVENT_SCOPE 是否激活状态 NULL YES tinyint NULL
SUSPENSIONSTATE 挂起状态 NULL YES int NULL 暂停状态 1激活 2暂停
CACHEDENT_STATE 缓存状态 NULL YES int NULL 缓存的状态, 1 事件 监听 2 人工任务 3 异步作业
TENANTID 租户ID YES varchar 255
NAME_ 名称 NULL YES varchar 255
LOCKTIME 锁定时间 NULL YES timestamp NULL

注:
1.TENANT_ID、NAME、LOCK_TIME是后续版本加入的。

act_ru_identitylink( 运行时流程人员表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NO varchar 64 PRI
REV_ 版本号 NULL YES int NULL version
GROUPID 用户组ID NULL YES varchar 255 MUL
TYPE_ 类型 NULL YES varchar 255
USERID 用户ID NULL YES varchar 255 MUL
TASKID 任务ID NULL YES varchar 64 MUL(ACT_RU_TASK)
PROCINST_ID 流程实例ID NULL YES varchar 64 MUL(ACT_RU_EXECUTION)
PROCDEF_ID 流程定义ID NULL YES varchar 64 MUL(ACT_RE_PROCDEF)

注:
1.任务参与者数据表。主要存储当前节点参与者的信息。

act_ru_job(运行时定时任务数据表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NULL NO varchar 64 PRI
REV_ 版本号 NULL YES int NULL
TYPE_ 类型 NULL NO varchar 255
LOCKEXP_TIME 锁定过期时间 NULL YES timestamp NULL
LOCKOWNER 挂起者 NULL YES varchar 255
EXCLUSIVE_ 是否唯一 NULL YES tinyint NULL
EXECUTIONID 执行实例ID NULL YES varchar 64
PROCESSINSTANCE_ID 流程实例ID NULL YES varchar 64
PROCDEF_ID 流程定义ID NULL YES varchar 64
RETRIES_ 重试次数 NULL YES int NULL
EXCEPTIONSTACK_ID 异常堆栈 NULL YES varchar 64 MUL(ACT_GE_BYTEARRAY)
EXCEPTIONMSG 异常信息 NULL YES varchar 4000
DUEDATE_ 截止时间 NULL YES timestamp NULL
REPEAT_ 重复 NULL YES varchar 255
HANDLERTYPE 处理器类型 NULL YES varchar 255
HANDLERCFG 处理器配置 NULL YES varchar 4000
TENANTID 租户ID YES varchar 255

注:
1.作业执行器数据。
2.需要启用JOB组件:JobExecutor 是管理一组线程的组件,这些线程用于触发定时器(包括后续的异步消息)。在单元测试场景下,使用多线程会很笨重。
因此API提供 ManagementService.createJobQuery 用于查询,以及 ManagementService.executeJob 用于执行作业。这样作业的执
行就可以在单元测试内部控制。为了避免作业执行器的干扰,可以将它关闭。
默认情况下, JobExecutor 在流程引擎启动时激活。当你不希望 JobExecutor 随流程引擎启动时,设置:

3.11. 启用异步执行器 Async executor activation
AsyncExecutor 是管理线程池的组件,这个线程池用于触发定时器与异步任务。
默认情况下,由于历史原因,当使用 JobExecutor 时, AsyncExecutor 不生效。然而我们建议使用新的 AsyncExecutor 代替
JobExecutor ,通过定义两个参数实现

asyncExecutorEnabled参数用于启用异步执行器,代替老的作业执行器。 第二个参数asyncExecutorActivate命令Activiti引擎在启动时
启动异步执行器线程池。

act_ru_task( 运行时任务节点表*核心表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NO varchar 64 PRI
REV_ 版本号 NULL YES int NULL version
EXECUTIONID 执行实例ID NULL YES varchar 64 MUL(ACT_RU_EXECUTION)
PROCINST_ID 流程实例ID NULL YES varchar 64 MUL(ACT_RU_EXECUTION)
PROCDEF_ID 流程定义ID NULL YES varchar 64 MUL(ACT_RE_PROCDEF)
NAME_ 名称 NULL YES varchar 255
PARENTTASK_ID 父任务ID NULL YES varchar 64
DESCRIPTION_ 描述 NULL YES varchar 4000
TASKDEF_KEY 人物定义标识 NULL YES varchar 255
OWNER_ 被代理人 NULL YES varchar 255 (一般情况下为空,只有在委托时才有值)
ASSIGNEE_ 经办人 NULL YES varchar 255 签收人或者委托人
DELEGATION_ 委托状态 NULL YES varchar 64 委托状态 PENDING委托中,RESOLVED已处理
PRIORITY_ 优先级 NULL YES int NULL
CREATETIME 创建时间 NULL YES timestamp NULL MUL
DUEDATE 截止时间 NULL YES datetime NULL
CATEGORY_ 分类 NULL YES varchar 255
SUSPENSIONSTATE 挂起状态 NULL YES int NULL 暂停状态 1激活 2暂停
TENANTID 租户ID YES varchar 255
FORMKEY 表单标识 NULL YES varchar 255

注:
1.运行时任务数据表

act_ru_variable( 运行时流程变量数据表*核心表)

字段 字段名称 字段默认值 是否允许为空 数据类型 字段长度 备注
ID_ 主键 NULL NO varchar 64 PRI
REV_ 版本号 NULL YES int NULL version
TYPE_ 类型 NULL NO varchar 255 见备注
NAME_ 名称 NULL NO varchar 255
EXECUTIONID 执行实例ID NULL YES varchar 64 MUL(ACT_RU_EXECUTION)
PROCINST_ID 流程实例ID NULL YES varchar 64 MUL(ACT_RU_EXECUTION)
TASKID 任务ID NULL YES varchar 64 MUL(ACT_RU_TASK)
BYTEARRAYID 资源ID NULL YES varchar 64 MUL(ACT_GE_BYTEARRAY)
DOUBLE_ 浮点值 NULL YES double NULL 存储变量类型为Double
LONG_ 长整型 NULL YES bigint NULL 存储变量类型为long
TEXT_ 文本值 NULL YES varchar 4000 存储变量值类型为String 如此处存储持久化对象时,值jpa对象的class
TEXT2_ 文本值 NULL YES varchar 4000 此处存储的是JPA持久化对象时,才会有值。此值为对象ID

注:
1.运行时流程变量数据表。
2.类型:jpa-entity、boolean、bytes、serializable(可序列化)、自定义type(根据你自身配置)、
CustomVariableType、date、double、integer、long、null、short、string