服务介绍
Flowable整体是通过ProcessEngine来操作的。即不管什么框架操作流程,都需要通过ProcessEngine这个类来处理。ProcessEngine是Flowable对外公开的门面。UML类图如下:

关系图如下:

- ProcessEngineConfiguration类通过读取xml文件配置流程引擎。
- 从ProcessEngine类中可以得到各种服务,值得注意的是,这些服务可以被认为是单例模式。
- RepositoryService是使用Flowable引擎时需要的第一个服务,它提供用于管理和操作部署和流程定义(BPMN )的操作,主要负责一些静态的配置。
- RuntimeService主要负责一些动态的任务,如启动一个新的实例,通常一个流程可以对应多个实例。它也用于检索和储存一些流程变量,比如在单向网关组件中需要传递之前的选择。
- TaskService主要和分配给人的任务相关,比如将任务分配给用户。
- IdentityService主要负责用户的管理。
- FormService是一个可选的服务,主要负责开始表单和结束表单。
- HistoryService保存历史信息。
- DynamicBpmnService可以动态的添加新的流程。
和actitivi的区别
flowable和actitivi的区别如下图,从actitivi6开始,团队内部发生分歧,Rademakers离队带走了部分人手开发了Flowable,剩下的Salaboy开发出Activiti7之后也离开了。目前Flowable有开源版本和商业版本两部分。

ProcessEngine

ProcessEngine是Flowable和Activiti中最最核心的类,相当于JDBC中的Connection一样,必须要优先配置,由于当前逆向学习的版本是基于Springboot的,所以创建过程和原生版本不同,首先,引入依赖:
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-process</artifactId>
</dependency>
flowable中的service
- ProcessEngineConfiguration类通过读取xml文件配置流程引擎。
- 从ProcessEngine类中可以得到各种服务,值得注意的是,这些服务可以被认为是单例模式。
- RepositoryService是使用Flowable引擎时需要的第一个服务,它提供用于管理和操作部署和流程定义(BPMN )的操作,主要负责一些静态的配置。
- RuntimeService主要负责一些动态的任务,如启动一个新的实例,通常一个流程可以对应多个实例。它也用于检索和储存一些流程变量,比如在单向网关组件中需要传递之前的选择。
- TaskService主要和分配给人的任务相关,比如将任务分配给用户。
- IdentityService主要负责用户的管理。
- FormService是一个可选的服务,主要负责开始表单和结束表单。
- HistoryService保存历史信息。
- DynamicBpmnService可以动态的添加新的流程。
表结构
1、Flowable的所有数据库表都以ACT_开头。第二部分是说明表用途的两字符标示符。服务API的命名也大略符合这个规则。
2、ACT_RE_: 'RE’代表repository。带有这个前缀的表包含“静态”信息,例如流程定义与流程资源(图片、规则等)。
3、ACT_RU_: 'RU’代表runtime。这些表存储运行时信息,例如流程实例(process instance)、用户任务(user task)、变量(variable)、作业(job)等。Flowable只在流程实例运行中保存运行时数据,并在流程实例结束时删除记录。这样保证运行时表小和快。
4、ACT_HI_: 'HI’代表history。这些表存储历史数据,例如已完成的流程实例、变量、任务等。
5、ACT_GE_: “GE”代表“General”(通用),通用数据。在多处使用。
说明:
ACT_RE_ : *’RE’表示repository(存储)。RepositoryService接口操作的表。带此前缀的表包含的是静态信息,如,流程定义,流程的资源(图片,规则等)。
ACT_RU_ : *’RU’表示runtime。这是运行时的表存储着流程变量,用户任务,变量,职责(job)等运行时的数据。flowable只存储实例执行期间的运行时数据,当流程实例结束时,将删除这些记录。这就保证了这些运行时的表小且快。
ACT_ID_ : *’ID’表示identity(组织机构)。这些表包含标识的信息,如用户,用户组,等等。
ACT_HI_ : *’HI’表示history。就是这些表包含着历史的相关数据,如结束的流程实例,变量,任务,等等。
ACT_GE_ : *普通数据,各种情况都使用的数据。
1)通用数据表
(2个)
- act_ge_bytearray:二进制数据表,如流程定义、流程模板、流程图的字节流文件;
- act_ge_property:属性数据表(不常用);
2)历史表
8个,HistoryService接口操作的表
- act_hi_actinst:历史节点表,存放流程实例运转的各个节点信息(包含开始、结束等非任务节点);
- act_hi_attachment:历史附件表,存放历史节点上传的附件信息(不常用);
- act_hi_comment:历史意见表;
- act_hi_detail:历史详情表,存储节点运转的一些信息(不常用);
- act_hi_identitylink:历史流程人员表,存储流程各节点候选、办理人员信息,常用于查询某人或部门的已办任务;
- act_hi_procinst:历史流程实例表,存储流程实例历史数据(包含正在运行的流程实例);
- act_hi_taskinst:历史流程任务表,存储历史任务节点;
- act_hi_varinst:流程历史变量表,存储流程历史节点的变量信息;
3)用户相关表
4个,IdentityService接口操作的表
- act_id_group:用户组信息表,对应节点选定候选组信息;
- act_id_info:用户扩展信息表,存储用户扩展信息;
- act_id_membership:用户与用户组关系表;
- act_id_user:用户信息表,对应节点选定办理人或候选人信息;
4)流程定义、流程模板相关表
3个,RepositoryService接口操作的表
- act_re_deployment:部署信息表,存储流程定义、模板部署信息;
- act_re_procdef:流程定义信息表,存储流程定义相关描述信息,流程定义的bpmn文件放在act_ge_bytearray表中,以字节形式存储;
- act_re_model:流程模板信息表,存储流程模板相关描述信息,流程定义的bpmn文件放在act_ge_bytearray表中,以字节形式存储;
5)流程运行时表
6个,RuntimeService,taskService接口操作的表
- act_ru_task:运行时流程任务节点表,存储运行中流程的任务节点信息,重要,常用于查询人员或部门的待办任务时使用;
- act_ru_event_subscr:监听信息表,不常用;
- act_ru_execution:运行时流程执行实例表,记录运行中流程运行的各个分支信息(当没有子流程时,其数据与act_ru_task表数据是一一对应的);
- act_ru_identitylink:运行时流程人员表,重要,常用于查询人员或部门的待办任务时使用;
- act_ru_job:运行时定时任务数据表,存储流程的定时任务信息;
- act_ru_variable:运行时流程变量数据表,存储运行中的流程各节点的变量信息;
表的设计
流程分类表
flowable本身并没有专门针对流程分类的表结构,所以产考ruoyi-flowable-plus 项目设计wf_category流程分类表。
CREATE TABLE `wf_category` (
`category_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '流程分类id',
`category_name` varchar(64) DEFAULT '' COMMENT '流程分类名称',
`code` varchar(64) DEFAULT '' COMMENT '分类编码',
`remark` varchar(500) DEFAULT '' COMMENT '备注',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
PRIMARY KEY (`category_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1619992711520260098 DEFAULT CHARSET=utf8 COMMENT='流程分类表';

表单配置表
为了在设计流程的时候以及在启动流程时候更加便捷,设计表单的配置

这个组件的名字叫做Form Generator,详情参见:
https://blog.csdn.net/weixin_42288182/article/details/108375824
CREATE TABLE `wf_form` (
`form_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '表单主键',
`form_name` varchar(64) DEFAULT '' COMMENT '表单名称',
`content` longtext COMMENT '表单内容',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
PRIMARY KEY (`form_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1624664649444954115 DEFAULT CHARSET=utf8 COMMENT='流程表单信息表';
配置好的表单点击保存按钮后,会存放到wf_form表中,以json的方式把表单数据放在content字段中
流程实例关联表单
模型管理是整个工作流中重要的一步,流程模型设计决定了工作流流转的各操作步骤。流程设计必须要有开始节点和结束节点。目前流程设计必须要有“申请人/发起人”节点。模型部署时会将使用到的表单另存一份存入wf_deploy_form表中,flowable的部署和activiti一样支持多版本,所以每个部署的版本都会存入一个表单。当流程部署时,WfModelServiceImpl.deployModel方法最后会把流程中包含的表单存放到wf_deploy_form中,注意是独立存放,也就是对应的content数据会完整的作为一个和流程版本一致保存到wf_deploy_form表中。
CREATE TABLE `wf_deploy_form` (
`deploy_id` varchar(64) NOT NULL COMMENT '流程实例主键',
`form_key` varchar(64) NOT NULL COMMENT '表单Key',
`node_key` varchar(64) NOT NULL COMMENT '节点Key',
`node_name` varchar(255) DEFAULT '' COMMENT '节点名称',
`content` longtext COMMENT '表单内容',
PRIMARY KEY (`deploy_id`,`form_key`,`node_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='流程实例关联表单';

流程操作
新增模型
对应代码:
@Override
public void insertModel(WfModelBo modelBo) {
//基于org.flowable.engine.repository的Model
Model model = repositoryService.newModel();
model.setName(modelBo.getModelName());
model.setKey(modelBo.getModelKey());
model.setCategory(modelBo.getCategory());
//通过buildMetaInfo方法给WfMetaInfoDto添加流程描述以及创建者的用户名
String metaInfo = buildMetaInfo(new WfMetaInfoDto(), modelBo.getDescription());
model.setMetaInfo(metaInfo);
// 保存流程模型
repositoryService.saveModel(model);
}
执行的sql:
insert into ACT_RE_MODEL(ID_, REV_, NAME_, KEY_, CATEGORY_, CREATE_TIME_, LAST_UPDATE_TIME_, VERSION_, META_INFO_, DEPLOYMENT_ID_, EDITOR_SOURCE_VALUE_ID_, EDITOR_SOURCE_EXTRA_VALUE_ID_, TENANT_ID_) values('b8cdb45f-8359-11ed-bdca-b40edef8b08d', 1, '测试请假流程v1.0', 'Process_1671865594483', 'test001', '2022-12-24T15:08:09.417+0800', '2022-12-24T15:08:09.417+0800', 1, '{"createUser":null,"description":null,"formType":null,"formId":null}', NULL, NULL, NULL, '')
根据3.4对表结构的介绍,流程定义相关的act_re为前缀的表是通过RepositoryService来进行操作的。
保存的数据将存入流程设计模型部署表( act_re_model )

流程设计
流程设计器使用的插件是bpmn-process-designer
详细了解可参见:
https://blog.csdn.net/weixin_43550766/article/details/121283568


通过开始配置的表单,动态的配置

指定用户,选择流程流转的审批人

- 多实例审批人设置“指定用户”有两人以上、角色或部门,则下方会显示多实例设置。
- 会签:所选的审批人全员通过后,才会进入到下一节点。
- 或签:所选的审批人有一名审批人同意后,即可进入到下一节点。
保存流程
@Override
@Transactional(rollbackFor = Exception.class)
public void saveModel(WfModelBo modelBo) {
log.info("保存流程设计");
// 查询模型信息
Model model = repositoryService.getModel(modelBo.getModelId());
if (ObjectUtil.isNull(model)) {
throw new RuntimeException("流程模型不存在!");
}
//把接收到的流程设计器转换的xml,通过flowable自带的bpmnXMLConverter.convertToBpmnModel转换成flowable中的BpmnModel
BpmnModel bpmnModel = ModelUtils.getBpmnModel(modelBo.getBpmnXml());
if (ObjectUtil.isEmpty(bpmnModel)) {
throw new RuntimeException("获取模型设计失败!");
}
String processName = bpmnModel.getMainProcess().getName();
// 获取开始节点
StartEvent startEvent = ModelUtils.getStartEvent(bpmnModel);
if (ObjectUtil.isNull(startEvent)) {
throw new RuntimeException("开始节点不存在,请检查流程设计是否有误!");
}
// 获取开始节点配置的表单Key
if (StrUtil.isBlank(startEvent.getFormKey())) {
throw new RuntimeException("请配置流程表单");
}
Model newModel;
//如果保存时选择了使用新版本保存,则版本号+1,数据库中新增加一条记录
if (Boolean.TRUE.equals(modelBo.getNewVersion())) {
newModel = repositoryService.newModel();
newModel.setName(processName);
newModel.setKey(model.getKey());
newModel.setCategory(model.getCategory());
newModel.setMetaInfo(model.getMetaInfo());
newModel.setVersion(model.getVersion() + 1);
} else {
newModel = model;d
// 设置流程名称
newModel.setName(processName);
}
// 保存流程模型
repositoryService.saveModel(newModel);
// 保存 BPMN XML
repositoryService.addModelEditorSource(newModel.getId(), ModelUtils.getBpmnXml(bpmnModel));
}
设计好流程图保存的时候会有提示,问是否以新版本保存,如果选是,则在act_re_model表中新增一条记录,Version+1,之前的一条记录作为历史记录存在。
设计好的流程通过下面语句保存在act_ge_bytearray表中,id作为act_re_model的外键EDITOR_SOURCE_VALUE_ID存放。
部署流程
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deployModel(String modelId) {
// 获取流程模型
Model model = repositoryService.getModel(modelId);
if (ObjectUtil.isNull(model)) {
throw new RuntimeException("流程模型不存在!");
}
// 获取流程图
String bpmnXml = queryBpmnXmlById(modelId);
BpmnModel bpmnModel = ModelUtils.getBpmnModel(bpmnXml);
String processName = model.getName() + ProcessConstants.SUFFIX;
// 部署流程,在act_re_deployment表新建一条记录
Deployment deployment = repositoryService.createDeployment()
.name(model.getName())
.key(model.getKey())
.addBpmnModel(processName, bpmnModel)
.category(model.getCategory())
.deploy();
// 保存部署表单
return deployFormService.saveInternalDeployForm(deployment.getId(), bpmnModel);
}
部署后执行的关键sql:
1.act_re_deployment表新建一条记录
insert into ACT_RE_DEPLOYMENT(ID_, NAME_, CATEGORY_, KEY_, TENANT_ID_, DEPLOY_TIME_, DERIVED_FROM_, DERIVED_FROM_ROOT_, PARENT_DEPLOYMENT_ID_, ENGINE_VERSION_) values('259114c2-835f-11ed-bdca-b40edef8b08d', '测试请假流程v1.0', 'test001', 'Process_1671865594483', '', '2022-12-24T15:46:59.374+0800', NULL, NULL, '259114c2-835f-11ed-bdca-b40edef8b08d', NULL)
ACT_RE_PROCDEF(流程定义表)表新建一条记录
insert into ACT_RE_PROCDEF(ID_, REV_, CATEGORY_, NAME_, KEY_, VERSION_, DEPLOYMENT_ID_, RESOURCE_NAME_, DGRM_RESOURCE_NAME_, DESCRIPTION_, HAS_START_FORM_KEY_, HAS_GRAPHICAL_NOTATION_ , SUSPENSION_STATE_, DERIVED_FROM_, DERIVED_FROM_ROOT_, DERIVED_VERSION_, TENANT_ID_, ENGINE_VERSION_) values ('Process_1673191270430:1:5c78488e-900b-11ed-b837-b40edef8b08d', 1, 'http://flowable.org/bpmn', '测试流程3.0', 'Process_1673191270430', 1, '5c67f4db-900b-11ed-b837-b40edef8b08d', '测试流程3.0.bpmn', '测试流程3.0.Process_1673191270430.png', NULL, true, true, 1, NULL, NULL, 0, '', NULL)
ACT_GE_BYTEARRAY,将该流程模型需要的.bpmn和png图片以二进制的形式,分为两行记录的方式分别存入该表中。通过DEPLOYMENT_ID_对应act_re_deployment的ID_。加上一开始流程模型创建时的source记录,一共三条记录。
INSERT INTO ACT_GE_BYTEARRAY(ID_, REV_, NAME_, BYTES_, DEPLOYMENT_ID_, GENERATED_) VALUES
('259114c3-835f-11ed-bdca-b40edef8b08d', 1, '测试请假流程v1.0.bpmn', 'java.io.ByteArrayInputStream@94beb82', '259114c2-835f-11ed-bdca-b40edef8b08d', false) ,
('25a2c804-835f-11ed-bdca-b40edef8b08d', 1, '测试请假流程v1.0.Process_1671865594483.png', 'java.io.ByteArrayInputStream@111647d5', '259114c2-835f-11ed-bdca-b40edef8b08d', true)
act_re_procdef新增一条流程定义记录,流程定义id=processKey:version:流程定义自己生成的uuid
流程定义被解析为一个内部可执行的对象模型,以便流程实例可以从中启动。
通过DEPLOYMENT_ID_对应act_re_deployment的ID_,和act_re_deployment是多对一的关系,因为一个部署的bar包里可能包含多个流程定义文件,每个流程定义文件都会有一条记录在 ACT_REPROCDEF 表内,每个流程定义的数据,都会对应ACT_GE_BYTEARRAY 表内的一个资源文件和 PNG 图片文件。和 ACT_GE_BYTEARRAY 的关联是通过程序用ACT_GE_BYTEARRAY.NAME 与 ACT_RE_PROCDEF.NAME 完成的,在数据库表结构中没有体现。
into ACT_RE_PROCDEF(ID_, REV_, CATEGORY_, NAME_, KEY_, VERSION_, DEPLOYMENT_ID_, RESOURCE_NAME_, DGRM_RESOURCE_NAME_, DESCRIPTION_, HAS_START_FORM_KEY_, HAS_GRAPHICAL_NOTATION_ , SUSPENSION_STATE_, DERIVED_FROM_, DERIVED_FROM_ROOT_, DERIVED_VERSION_, TENANT_ID_, ENGINE_VERSION_) values ('Process_1671865594483:1:25a31625-835f-11ed-bdca-b40edef8b08d', 1, 'http://flowable.org/bpmn', '测试请假流程v1.0', 'Process_1671865594483', 1, '259114c2-835f-11ed-bdca-b40edef8b08d', '测试请假流程v1.0.bpmn', '测试请假流程v1.0.Process_1671865594483.png', NULL, true, true, 1, NULL, NULL, 0, '', NULL)
wf_deploy_form
和流程捆绑的表单,也会在wf_deploy_form表中存入一份包含content的完整数据,因为后期的表单更新,并对流程重新部署后,原历史版本中的表单是不会变的。
创建新流程
SELECT RES.* from ACT_RE_PROCDEF RES WHERE RES.VERSION_ = (select max(VERSION_) from ACT_RE_PROCDEF where KEY_ = RES.KEY_ and ( (TENANT_ID_ IS NOT NULL and TENANT_ID_ = RES.TENANT_ID_) or (TENANT_ID_ IS NULL and RES.TENANT_ID_ IS NULL) ) ) and (RES.SUSPENSION_STATE_ = 1) order by RES.KEY_ asc LIMIT 10 OFFSET 0
SELECT RES.* from ACT_RE_DEPLOYMENT RES WHERE RES.ID_ = 'fb32e0be-7429-11ed-9514-b40edef8b08d' order by RES.ID_ asc
查询了两个表,因为设置了最新版本作为条件,所以先通过查询ACT_RE_PROCDEF的最大version获得最新版本的流程定义信息,在通过流程信息的delopymentId作为条件到ACT_RE_DEPLOYMENT中查询流程部署数据。
因此,在该方法中可以获得流程定义id,和部署id,分别存放在WfDefinitionVo的definitionId和deploymentId中。
public TableDataInfo<WfDefinitionVo> processList(PageQuery pageQuery) {
Page<WfDefinitionVo> page = new Page<>();
// 流程定义列表数据查询
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
.latestVersion()//最新版本
.active()//处于激活状态
.orderByProcessDefinitionKey()//通过流程定义key的升序排序
.asc();
long pageTotal = processDefinitionQuery.count();
if (pageTotal <= 0) {
return TableDataInfo.build();
}
int offset = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
List<ProcessDefinition> definitionList = processDefinitionQuery.listPage(offset, pageQuery.getPageSize());
List<WfDefinitionVo> definitionVoList = new ArrayList<>();
for (ProcessDefinition processDefinition : definitionList) {
String deploymentId = processDefinition.getDeploymentId();
Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
WfDefinitionVo vo = new WfDefinitionVo();
vo.setDefinitionId(processDefinition.getId());
vo.setProcessKey(processDefinition.getKey());
vo.setProcessName(processDefinition.getName());
vo.setVersion(processDefinition.getVersion());
vo.setDeploymentId(processDefinition.getDeploymentId());
vo.setSuspended(processDefinition.isSuspended());
// 流程定义时间
vo.setCategory(deployment.getCategory());
vo.setDeploymentTime(deployment.getDeploymentTime());
definitionVoList.add(vo);
}
page.setRecords(definitionVoList);
page.setTotal(pageTotal);
return TableDataInfo.build(page);
}
4254

被折叠的 条评论
为什么被折叠?



