flowable

服务介绍

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

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

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

  • 多实例审批人设置“指定用户”有两人以上、角色或部门,则下方会显示多实例设置。
    1. 会签:所选的审批人全员通过后,才会进入到下一节点。
    2. 或签:所选的审批人有一名审批人同意后,即可进入到下一节点。

保存流程

@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);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

打工版程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值