From da2cd4544e366c65c285a97b6112b89f0faed086 Mon Sep 17 00:00:00 2001 From: wangli <274027703@qq.com> Date: Tue, 24 Oct 2023 18:17:42 +0800 Subject: [PATCH] =?UTF-8?q?update=20-=20=E6=9B=B4=E6=96=B0=E5=B7=A5?= =?UTF-8?q?=E7=A8=8B=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 273 ++++++++++++++++++++++++++++++++++++++++++++++++--- imgs/img.png | Bin 0 -> 20142 bytes 2 files changed, 260 insertions(+), 13 deletions(-) create mode 100644 imgs/img.png diff --git a/README.md b/README.md index 7c372d567..1b559327f 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,26 @@ - flw_*:Flowable 新版本扩展功能相关的表,流程迁移的表。 ``` +### 2.1 本工程常用的库表 + +```text +// 运行时(仅保存运行中的流程相关数据) +act_ru_execution (执行中的实体) +act_ru_task (执行中的任务) +act_ru_variable (执行中的变量) + +// 元数据配置信息 +act_re_model (流程模型) +act_re_procdef (流程定义) +act_re_deployment (流程部署) + +// 历史数据 (运行时产生的数据也会及时写入历史) +act_hi_procinst (历史的流程实例) +act_hi_taskinst (历史的任务实例) +act_hi_varinst (历史的变量实例) + +act_ge_bytearray (很多元数据) +``` ## 3.国内工作流常用操作的名称解释 | 流程操作 | 描述 | @@ -45,7 +65,245 @@ | 交接 | 流程管理员权限,管理员将离职或换岗员工的待执行、待领取、代办他人、委托他人代办的任务转交给接管人,并删除与该员工相关的委托代理关系。交接员工所有直接参与的流 程实例中对应的参与者将自动由系统修改为接管人。(强制) | | 暂存 | 复杂表单,一次性填写不完,需要保存草稿功能,开始节点的暂存 | -已完成: +## 4. 如何配置一个流程? + +> 流程的运行依赖一种规则,得让引擎知道何时开始执行流程? 何时终止? 如何识别和控制每个步骤该怎么处理? 执行过程中如何走向不同的路径? +> 比如: 我们常见的需求, 某个节点需要会签/或签, 或者某个节点需要判断条件是否执行该步骤等等. +> +> 以上这些规则都需要再创建流程实例时, 提前就告诉引擎, 且运行过程和步骤中是不可变的. 引擎自己也未提供相应能力. +> +> 这种规则在 Flowable 的世界中,是以 BPMN2.0 协议规则进行编写和持久化. + +## 4.1 简单识别流程定义 + +![流程定义 png](imgs/img.png) + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +> 通过上面的 BPMN 协议的具体内容, 我们可以发现协议内容主要分为两大块, 一块是以``包裹, 它是主要的规则描述, +> 另一块是以``包裹, 它是规则可视化的元数据, 这部分一般我们无需关系, 但只要我们的业务涉及到流程配置的反显, +> 它又比不可少. + +### 4.2 Process 节点内容简易描述 + +> 通过上面的协议内容, 可以发现有以下节点, 我们对出现过的节点简单描述 + +- process 代表是一个可以被引擎处理的流程 +- startEvent 开始事件节点(**这里的需要注意命名, Event 代表是事件, 也就是说一个流程实例的开始, 是基于事件的,截止 23 年, 在 + Flowable 中开始事件分为 9 种, 我们只用了空启动事件**) + > - 空启动事件 + > - 定时器事件 + > - 信号启动事件 + > - 消息启动事件 + > - 异常启动事件 + > - 注册开始事件 + > - 开始变量监听事件 + > - 升级开始事件 + > - 条件启动事件 +- exclusiveGateway 排它网关(**控制流程如何流转, 是最常用的一个基础组件,只要涉及到条件流转,都需要用它,在 Flowable 中,网关也有 + 4 种, 我们只用了排它网关和并行网关**) + > - 排它网关 + > - 并行网关 + > - 事件网关 + > - 包容网关 +- userTask 用户任务(**需要人为介入的节点,一般情况下只要人类不对该节点做动作,那么流程就永远卡在这里,不再执行**) +- sequenceFlow 顺序流(**用于连接两个节点间的连线,很重要的组件,如果连线不正确,或者中断,会直接影响引擎的执行**) +- endEvent 结束事件节点 + +### 4.3 流程模型与流程定义 + +> 以上仅仅是冰山一角, 大致了解了最初级的协议内容后, 同时我们也称上面的协议内容为**流程定义**, 流程定义是一种较为底层的元数据. +> +> 因为我们一般针对某个流程规则, 会时不时的修改和调整, 修改后如何保证使用方无感知, 但对于工作流来说又是可追溯的, +> 所以需要再抽象一层来保存同一个流程的所有修改过程, 我们称这个抽象层为**流程模型**. +> 也就是说, 一个流程模型是一个流程, 这个流程的的多次修改(多版本)都在这个模型下. 而使用方也就只用关心流程模型的唯一标识即可. +> +> 我们编辑好流程模型与流程定义后, 还需要对流程模型(定义)发布, 才能真正的被使用. 发布后, 这一次的流程定义将定型,不再允许改变当前版本的内容. +> 如需修改规则, 则会产生新的版本. + +流程模型: 对应库表: act_re_model +流程定义: 对应库表: act_re_procdef +流程发布: 对应库表: act_re_deployment/act_ge_bytearray + +以上仅仅是冰山一角, 大致了解了最初级的协议内容后, 同时我们也称上面的协议内容为`流程定义`, 我们再看看如何运行它? + +## 5. 如何运行一个流程? + +> 在介绍上面的协议内容时, 说了我们只用了空启动事件, 那我们怎么启动呢? 虽然说它是事件, 但我们实际发起流程并不是直接操作的事件, +> 而是操作引擎为我们提供API. +> `org.flowable.engine.RuntimeService#ProcessInstanceBuilder#start()` + +### 5.1 简单了解 Flowable 引擎常用的 API (门面模式) + +> API 的相关作用,都在对应类有注解, 自行查阅 + +- org.flowable.engine.RuntimeService 运行时相关 +- org.flowable.engine.RepositoryService 元数据相关 +- org.flowable.engine.HistoryService 历史数据相关 +- org.flowable.engine.TaskService 审批任务相关 + +### 5.2 运行(前/中)的困惑? + +> 有没有发现一个问题? 我们从头到现在几乎没有说审批人相关问题或配置, 包括 BPMN 协议内容中也没有审批人的信息. 能发起成功吗? +> +> 首先回答: 能正常发起,并且流程也能处理运转. 只是像之前说的没有人为操作, 流程会永远的停止在第一个审批节点, +> 然后这前提是规则里面至少有一个 userTask. +> 如果 BPMN 规则里面,只有 startEvent 直接连接的 endEvent, 那么发起即结束. + +## 6. 流程引擎内部是如何运行的? + +> Flowable 分为多个引擎, 包括 BPMN 引擎/CMMN 引擎/DMN 引擎/Form 引擎/IDM 引擎, 而我们主要关注 BPMN 引擎即可. +> 所有的引擎的采用的设计模式是一致的, 统一的采用了命令模式. 引擎内部的每一步动作都是依靠一个Agenda(待办) +> 来临时存储后续动作的"命令". +> +> 所以我们研究引擎需要研究各种命令(XXXCmd). 这些命令是进入引擎内部的入口, 可以通过查看 +> org.flowable.common.engine.impl.interceptor.Command 的继承关系看到具体的命令. 当然引擎还有更多的逻辑, +> 真正处理各种类型节点的是对应的行为(XXXBehavior), 可以通过查看 `org.flowable.engine.impl.delegate.ActivityBehavior` +> 的继承关系看到具体的行为. +> +> 例如: 想看发起过程需要研究: StartProcessInstanceCmd#execute + +## 6. 如何与外部交互? + +> 工作流引擎作为服务端, 与外部系统交互分为两类, 一类是操作引擎, 一般提供 API 入口, 由外部推动. 另一类是引擎内部状态通知给使用方, +> 是由服务端广播出去. + +### 6.1 外部操作引擎 + +> 本工程提供两种使用接入方式, 一种是 jar 包集成, 可参考[这个说明](workflow-engine-core/src/main/resources/readme.md), +> 另一种是 Feign API 集成, 主要参考`workflow-engine-server`module +> 中包路径为 `src/main/java/cn/axzo/workflow/server/controller/web` 下的文件. + +### 6.2 引擎内部事件广播 + +> 引擎内部的事件类型非常多, org.flowable.common.engine.api.delegate.event.FlowableEngineEventType 通过这个类可以看到引擎会触发的事件类型. +> +> 本工程根据公司需要, 将引擎部分事件广播给使用方, 其他事件则不广播. 同时这些事件也被用于本工程的一些场景的消费. + +**广播的事件接口** + +- cn.axzo.workflow.core.listener.BpmActivityEventListener +- cn.axzo.workflow.core.listener.BpmTaskEventListener +- cn.axzo.workflow.core.listener.BpmProcessEventListener + +**以下总结下哪些接口触发哪些事件** + +- cn.axzo.workflow.client.feign.bpmn.ProcessInstanceApi.createProcessInstance + +> 发起流程 MQ 触发规则: +> 1. 当前流程实例会依次触发 process-instance-created 和 process-instance-started 事件 +> 2. 第一个审批任务会依次触发 process-task-assigned 和 process-task-created 事件 + +- cn.axzo.workflow.client.feign.bpmn.ProcessInstanceApi.cancelProcessInstance + +> 取消流程 MQ 触发规则: +> 1. 当前流程实例中现存的任务会依次触发 process-task-deleted 事件 +> 2. 当前流程实例会触发 process-instance-cancelled 事件 + +- cn.axzo.workflow.client.feign.bpmn.ProcessTaskApi.approveTask + +> 任务节点通过审批 MQ 触发规则: +> 1. 当前审批任务会依次触发 process-task-completed 和 process-task-deleted 事件(如果有下一级审批,则会触发第 2.1 点中的事件, + 如果当前审核任务最后一级审批,则会触发第 2.2 点中的事件) + > 2.1. 下一级审批任务会依次触发 process-task-assigned 和 process-task-created 事件 + > 2.2. 流程实例正常结束会触发 process-instance-completed 事件 + +- cn.axzo.workflow.client.feign.bpmn.ProcessTaskApi.rejectTask + +> MQ 触发规则: +> 1. 当前审批任务会触发 process-task-deleted 事件 +> 2. 当前流程实例会触发 process-instance-rejected 事件 + +## 99.建设状态(过时) + +### 99.1 已完成: 1. 全新搭建工作流微服务 2. 工作流模型中 JSON 格式的协议支持 @@ -55,7 +313,7 @@ 5. 重构工作流服务 Process 引擎相关接口,并按照框架简单接入和测试内置表单 6. 重构业务分类/流程状态/审批状态运行时以及历史数据的处理方式 -待办项: +### 99.2 待办项: 1. BPMN 协议兼容(XML/JSON已支持) 2. 工作流内部接入组织架构 @@ -63,14 +321,3 @@ 4. 审批模型审批人配置管理与持久化 5. 审批模型表单能力的接入和调整 5. Flowable Event MQ 事件生产与消费 - -## 一些暂无法归类的信息 - -1. BPMN 协议中,`UserTask`可以增加自定义的监听, 监听器类型分为两大类: 执行监听器(`ExecutionListener`) - 和任务监听器(`TaskListener`), 其中执行监听较为抽象, 在实例运行中,每个节点都有一个 ExecutionEntity, 目前这个 Execution - 概念还比较模糊. 而任务监听器是针对 UserTask 节点存在的. - -> `ExecutionListener` event 分为: start/end/take -> `TaskListener` event 分为: create/assignment/complete/delete - -2. diff --git a/imgs/img.png b/imgs/img.png new file mode 100644 index 0000000000000000000000000000000000000000..1de316847b6d9dbb29eaca55aa6a0967374e2199 GIT binary patch literal 20142 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 S2zakSAe4eSB3JIW=l>0iWdm0L literal 0 HcmV?d00001