首页 文章 精选 留言 我的

精选列表

搜索[流计算],共10000篇文章
优秀的个人博客,低调大师

工作流自动节点的同步与异步:两条实现路径的取舍

> 自动步骤要不要占一个事务、要不要落作业队列,是流程引擎设计里最实际的分歧之一。本文从建模单元、触发时机、失败恢复与可观测性四个角度做对照,并给出实施侧的取舍建议。 ## 一、过程节点是什么:从一个枚举说起 `NodeExt.ts` 里 `WhoExeIt` 是节点属性面板上的一个系统枚举下拉: ``` @0=操作员执行 @1=机器执行 @2=混合执行 @3=流水线执行 @4=过程执行 ``` 它回答的问题是「这个节点由谁做完」。其中 `4` 就是过程执行(过程节点): - **由系统执行**,不需要人点发送; - **在同一次发送请求内同步连发**,不是等定时任务扫描; - **仍然是一个节点**:有接收人、有方向条件、有发送前事件、有轨迹。 对照该平台自己的另外几种执行方式,就能看清过程节点的定位: |WhoExeIt|名称|触发时机|是否进普通待办| | --- | --- | --- | --- | |0|操作员执行|人点发送|进| |1|机器执行|后台定时扫描|不进(查询排除)| |2|混合执行|打开表单时先试发|失败才进| |3|流水线执行|工位扫码发送|进独立工位通道| |4|过程执行|当次发送请求内连发|不进| 关键在最后一行:**过程节点和机器节点都不是「人」,但它们的触发路径不同**。机器节点把继续流转交给定时任务(`AutoRun_WhoExeIt.cs` 里那条 `WHERE WhoExeIt IN (1,2)` 的 SQL,明确把 3、4 排除掉);过程节点则在发送线程里当场串起来跑完。 --- ## 二、该引擎过程节点怎么跑起来 ### 2.1 属性落在节点,运行时抄到待办 发送生成待办时,引擎把节点上的值抄到待办行(`WorkNode.cs`): ``` wl.WhoExeIt = town.HisNode.WhoExeIt; // 设置谁执行它 ``` 批量的插入语句同样带上这一列(`WorkNodePlus.cs`): ``` INSERT INTO WF_GenerWorkerList (WorkID,FK_Emp,FK_Node,Idx,AtPara,IsEnable,WhoExeIt) VALUES(...) ``` 所以 `WhoExeIt` 不是设计期的装饰字段,而是**运行时调度依据**:待办查询、定时任务、消息推送、流水线列表、过程页标签读的都是它。 ### 2.2 发送循环内的同步连发 过程节点不进定时任务,由发送时同步连发。逻辑可以概括为: ``` 发送完成后 while 下一节点的 WhoExeIt == 4 且流程未结束 且存在待办: 切换到该待办处理人(Port_Login) 以系统身份走完整发送(方向条件、接收人、发送前事件仍生效) 把本次过程返回叠加进总消息 切回原操作员 ``` 「同步」两个字的含义是:这一次 HTTP 请求不返回,直到这一串过程节点跑完、或者某一步抛错停下。这跟机器节点「先落一条待办、后台慢慢发」是两条完全不同的路径。 ### 2.3 明确不进定时任务 `AutoRun_WhoExeIt.cs` 里的注释写得很直白: ``` // 只捞机器执行(1)、混合执行(2)。流水线(3)、过程执行(4)不能进定时任务:4 由发送时同步连发。 string sql = "SELECT WorkID,FID,FK_Emp,FK_Node,FK_Flow,AtPara FROM WF_GenerWorkerList WHERE WhoExeIt IN (1,2) AND IsPass=0 AND IsEnable=1 ORDER BY FK_Emp"; ``` 这条边界非常重要:**配置人员如果把一串连续系统节点误设成机器执行(1),它们就会变成一条条等待扫描的待办,时延、事务边界、失败位置都和过程执行(4)不一样。** 这是实施时最容易踩的坑。 ### 2.4 过程可观测:时间轴与流程图 过程节点若仍弹一句「发送成功」,集成场景基本没法用——外部系统可能跑几十秒,人不知道卡在哪一步、返回了什么。该引擎用节点属性 **发送后转向(6/7)** 接上过程执行页(`MyProNodes.vue`): 1. 工具栏先打开大窗(默认时间轴或流程图),状态置为「执行中」,再并发调用发送; 1. 过程执行页轮询轨迹,后端额外下发全流程节点(含 `WhoExeIt`),前端据此打标签: ``` // 前端模块源码目录 const exeLabel = (whoExeIt?: number) => (Number(whoExeIt) === 4 ? '[过程执行]' : '[人工执行]'); ``` 1. 时间轴展示节点名、并行分支、处理人、耗时、下达时间、**过程返回**; 1. 发送返回里的过程明细(JSON、许可 / Kafka / FTP 报文)优先贴到「过程执行」的完成项上,并过滤引擎套话; 1. 超时约一分钟加强「外部系统处理中」提示。 后端在取待办数据时专门补了一张表,把节点的 `WhoExeIt` 带给前端: ``` // ProNodes:全流程节点含 WhoExeIt,用于时间轴 [过程执行]/[人工执行] 标签 Nodes flowNodes = new Nodes(this.FlowNo); ds.Tables.Add(flowNodes.ToDataTableField("WF_FlowNodes")); ``` 人看到的不再是黑盒自动任务,而是**人机混合轨迹**:人工节点显示处理人头像,过程节点显示系统返回与等待时长。 ### 2.5 过程节点不是「跳过引擎」的脚本钩子 这是设计上必须强调的一点:连发过程仍走**完整发送**——`ExecEvent.DoNode(EventListNode.SendWhen, ...)` 照常触发,方向条件、接收人规则、阻塞模式全部生效(见 `WorkNode.cs` `NodeSend`)。身份切到待办处理人再切回,审计主体仍然是人,而不是一个匿名的「引擎账号」。 外部 API 失败时,抛出带「系统执行发送」前缀的异常,流程停在**具体那个过程节点**,前端过程页展示失败节点与返回原文。对国内审批留痕的习惯来说,这比「引擎账号执行、活动上无办理人」更贴近预期。 --- ## 三、Flowable / Camunda 怎么表达「系统执行」 ### 3.1 换图元,而不是改属性 BPMN 2.0 的答案不是「节点的执行方式」,而是**换一种活动类型**: |BPMN 图元|职责|与该引擎大致对等| | --- | --- | --- | |User Task|人工等待,TaskService 查询/签收/完成|操作员执行(0)| |Service Task + 类/表达式|引擎内同步调用 Java 逻辑|过程执行(同步部分)| |Service Task + asyncBefore|落作业,Job Executor 续跑|机器执行(1)| |External Task(C7/Flowable)/ Job Worker(C8)|外部进程按类型拉取、加锁、完成/失败|无直接一等对等| |Timer Event|到点继续|延期发送 / 机器执行| |Script / Business Rule Task|脚本或决策表|无独立图元| 同一张流程图上,人做的、系统做的、外部系统做的是**不同的图形元素**。图上信息量大、语义精确,代价是实施人员要熟悉规范。 ### 3.2 Service Task 的两种形态 **同步委托**(`camunda:class` / `flowable:class`):引擎事务里直接调用 Java 代码。这跟该引擎过程执行的「同一次请求内连发」最接近——**都在调用方线程/事务里把逻辑跑完**。 **异步延续**(`asyncBefore="true"` / `flowable:async="true"`):到达活动时先提交当前事务、落一条作业,返回调用方,由 Job Executor 用**独立事务、独立线程**续跑。官方文档对它的定位是「引入事务边界 / 保存点」,而不只是「让节点自动跑」: > By setting the `asyncBefore='true'` attribute we introduce an additional save point at which the process state will be persisted and committed to the database. A separate job executor thread will continue the process asynchronously by using a separate database transaction. ### 3.3 Job Executor 在解决什么 Flowable 的作业在 API 调用期间就写入 `ACT_RU_JOB` 表,成功的提交会触发 Async Executor 执行;执行器有可配置线程池,并有「acquire」线程去抢占未被加锁的作业。Camunda 7 的 JobExecutor 默认使用 **exclusive job**:**同一流程实例的排他作业同一时刻只执行一个**,避免并行合流处产生乐观锁冲突。 失败处理是这套模型的核心价值: - 作业失败会**自动重试**(Camunda 默认重试 2 次,可用 `camunda:failedJobRetryTimeCycle` 配成 `R5/PT5M` 这类策略); - 抛普通异常 → 走重试;抛 `BpmnError` → 跳过重试,交给边界错误事件; - **重试耗尽 → 生成 Incident(事故)**,人在 Cockpit / Operate 里处理。 对照该引擎:机器执行用「扫待办、发一次、失败记日志」近似「有活就发」,**没有同等的作业锁协议、重试退避和事故工作台**。社区/集群部署下要靠部署约定而不是引擎级 job lock。这一点对比必须写明:在高并发自动节点与失败恢复上,BPMN 作业模型更完整。 ### 3.4 External Task 与 Job Worker:把「自动步骤」也变成待办 External Task 的类比是:**服务任务也是一种待办,只是领取者不是人而是程序**。`fetchAndLock` → 做事 → `complete` / `failure`,工人可多语言、可水平扩展,引擎不持有长调用。 Camunda 8(Zeebe)把这条路走得更彻底:Service Task 到达即生成一条持久化 job,worker 用 `ActivateJobs`(带 type、worker、timeout、maxJobs)拉取,再回 `CompleteJob` / `FailJob`(递减重试、可设 backoff)/ `ThrowError`。超时会重新分配作业,因此**投递语义是 at-least-once,处理逻辑必须幂等**。文档明确指出:worker 与引擎之间**不存在横跨两边的技术事务**,要靠业务事务来兜一致性。 这条路的代价是运维更重:要跑一套消费者、要处理超时与重试、要保证幂等。换来的是跨语言、长耗时、可独立扩缩的集成能力。 --- ## 四、三类实现的核心区别 ### 4.1 一张总表 |维度|该引擎(WhoExeIt=4)|Flowable / Camunda 7|Camunda 8(Zeebe)| | --- | --- | --- | --- | |建模方式|同一节点改属性|换 BPMN 图元|换 BPMN 图元| |过程执行的默认形态|发送请求内同步连发|Service Task 同步委托|默认走 Job Worker| |异步执行载体|待办行(WhoExeIt 区分)|ACT_RU_JOB|持久化 job 队列| |调度者|发送线程 / 定时任务|Job Executor|Worker 轮询 / 推送| |作业锁|无引擎级锁|exclusive job|job 独占 + 超时重分配| |重试|需再次发送或人工处理|引擎自动重试|worker 上报递减重试 + backoff| |失败落点|具体过程节点,返回带异常原文|作业/事故(Incident)|job 失败/事故,Operate 可见| |人怎么看|业务侧过程执行页(时间轴/流程图)|运维侧 Cockpit / 历史活动|运维侧 Operate| |事务边界|一次请求内可能很长|asyncBefore 切分保存点|每步独立提交、at-least-once| |典型优势|同一套节点生命周期、配置面直接、过程可视化给业务看|规范、作业可靠、生态大|分布式、多语言工人、水平扩展| ### 4.2 差异一:自动步骤「还算不算原来那个节点」 该引擎的过程节点仍是**胖节点**:接收人、方向条件、退回、撤销、轨迹、表单权限都挂在它身上。把过程节点从「操作员」改成「过程执行」,这些能力**一个都不用重配**。 BPMN 里 Service Task 是**瘦活动**:它天然没有办理人、没有中国式审批的退回/加签/抄送。要在自动步骤上做这类事,得靠监听器、执行表达式或扩展属性自己补。 这不是谁优谁劣,而是**复杂性放在了哪里**: - 该引擎把复杂度放在「同一套节点生命周期的正确性」上——**共享生命周期,就要自己处理待办过滤和消息短路**。机器节点仍然生成待办行(只是查询排除),若还发「请审批」到达消息,产品就是错的。所以 `WorkNode.cs` 里对机器节点显式短路: ``` if (this.town != null && this.town.HisNode.WhoExeIt == 1) return; // 机器执行不推到达消息 ``` - BPMN 把复杂度放在「规范与作业协议」上——Service Task 往往**根本不生成 User Task**,自然不会进 Tasklist,也就没有这个正确性义务。 ### 4.3 差异二:触发时机的哲学 **该引擎过程执行**优化的是「人点一次发送、盯着集成跑完」。它把一串系统节点串在同一次请求里,业务上等价于「这一批动作是一件事」。 代价同样清楚: - HTTP 超时、中途宕机、逐步重试**不是这条路径的强项**; - 请求内事务可能拉得很长; - 长耗时、需重试的外部调用,更适合机器执行(定时再发)或自建工人,而不是无限拉长当次请求。 **Camunda 7 的官方建议恰恰相反**:即便一串自动活动可以在同一事务里跑完,也建议用 `asyncBefore` / `asyncAfter` 切开,避免长事务与锁竞争。**Flowable / Camunda 用作业把外部 IO 移出引擎事务,这是多年踩坑后的默认结论。** |环节|该引擎的过程执行|Flowable / Camunda 的异步作业| | --- | --- | --- | |起点|人工点发送|人工完成任务并提交| |第 1 步|过程节点 A,同一请求内连发|提交事务,落作业 1| |第 2 步|过程节点 B,同一请求内连发|独立事务执行活动 A,落作业 2| |第 3 步|过程节点 C,同一请求内连发|独立事务执行活动 B,落作业 3| |收尾|汇总结果返回调用方|独立事务执行活动 C| |事务边界|整条链路一个事务|每个活动一个保存点| |失败后|停在具体过程节点,返回异常原文|作业重试,耗尽后生成事故| 左边一条链在同一事务里走完,快、直观、可视化好;右边每一步都有保存点,崩了能从作业恢复,但多了调度与运维成本。 左边一条链在同一事务里走完,快、直观、可视化好;右边每一步都有保存点,崩了能从作业恢复,但多了调度与运维成本。 ### 4.4 差异三:失败之后谁看见、谁重试 ||该引擎过程执行|Camunda 同步委托|异步作业 / Worker| | --- | --- | --- | --- | |失败位置|当前过程节点,返回带「系统执行发送」前缀|当前活动(事务回滚范围取决于实现)|作业/工人失败,实例停在该活动| |重试|需再次发送或人工处理|同请求内难自动重试|引擎/worker 重试 + 事故| |人怎么看|业务侧过程执行页失败态 + 返回原文|异常栈 / 业务自绘|Operate / Cockpit(运维视角)| 过程执行页把集成报文展示给**业务人员**;Cockpit / Operate 面向**运维人员**。两者不互相替代。 --- ## 五、场景怎么选 |场景|更贴该引擎的用法|更贴 Flowable / Camunda 的用法| | --- | --- | --- | |人点发送后连续调内部 API,要盯进度|过程执行(4)+ 发送后转向 6/7|一串 Service Task + 自建进度页;要可靠则每步 async| |夜间过账、不占待办|机器执行(1)|asyncBefore Service Task| |打开即过、过不了再填|混合执行(2)|网关拆分,或监听器 Complete| |工位扫码过站|流水线执行(3)|User Task + 自研工位端| |跨语言、长耗时、需重试扩缩|不宜单靠过程同步连发|External Task / Job Worker| |到点再发已填好的单|延期发送(改执行方式)|Timer 事件(图上可见)| 混合使用也常见:核心审批用该引擎,超长集成用独立工人回写后「发送」推进。那是架构组合,不是某引擎的失败。 --- ## 六、给实施与二次开发的要点 1. **连续系统步骤请设过程执行(4)**,不要设成机器执行(1)——后者进定时扫描,前者在当次发送内连发,时延与事务边界不同。 1. **过程节点仍要配接收人**:连发会按待办人切换身份,便于权限与审计。 1. **外部调用放在节点发送事件 / 过程里**,返回信息会出现在过程页「结果」区。 1. **需要人盯进度时**,上一人工节点的「发送后转向」选 6 或 7。 1. **长耗时、要重试的外部集成**,不要无限拉长过程执行的同步请求——改用机器执行或独立工人。 1. **机器/流水线节点默认不进普通待办**:若业务必须通知人,用独立消息策略,不要依赖默认到达推送。 --- ## 七、收束 三家都在解决同一件事:**人工等待与系统继续如何在一条流程里共存**。分歧不在「有没有自动节点」,而在**建模单元**和**调度哲学**: - Flowable / Camunda 问的是:活动是什么类型,令牌和作业怎么往前走; - 该引擎问的是:这个节点由谁、按什么节奏执行,同一节点进哪条通道。 实现上可以记成:**同一节点、五种执行方式、同一套发送**。机器靠待办扫描,过程靠当次连发,混合靠打开试发,流水线走工位接口;`WhoExeIt` 同时管待办过滤、消息短路和轨迹标签。 过程节点(`WhoExeIt=4`)是这套设计里最典型的一个切面:它把「一串系统动作」仍然当成节点来管——有接收人、有事件、失败停在具体节点、业务人员能在过程页看见人机混合轨迹。而 Flowable / Camunda 用作业模型换来了**可恢复、可重试、可多语言扩展**的自动步骤。复杂性放的位置不同,验收标准就不该混用。 --- ## 附:本文涉及的代码与文件索引 |文件|作用| | --- | --- | |前端模块源码目录|WhoExeIt 前端属性定义(@4=过程执行)| |前端模块源码目录|设计器枚举与写回节点| |引擎源码模块|后端节点实体枚举定义| |引擎源码模块|WhoExeIt 字段常量| |引擎源码模块|Node.WhoExeIt 取属性值| |引擎源码模块|待办抄写 WhoExeIt、发送前事件、机器节点消息短路| |引擎源码模块|待办批量插入带 WhoExeIt| |引擎源码模块|机器/混合节点定时执行(排除 3、4)| |引擎源码模块|下发全流程节点 WhoExeIt 给过程页| |前端模块源码目录|过程执行页时间轴 / 流程图 / 标签| |引擎源码模块|流水线(WhoExeIt=3)待办接口| |引擎源码模块卫星设计院任务流程控制系统/...xml|实际流程中过程节点(WhoExeIt=4)的配置样例|

优秀的个人博客,低调大师

工作流引擎二开能力评估:三层挂接模型的对照视角

> 一份面向研发与架构的评估框架:把二开能力抽象为交互层、服务层与配置编排层,并用同一把尺子对照 Java 与 .NET 两侧常见开源产品的实现方式与能力边界。 ## 1. 先把「二开」说清楚 ### 1.1 什么叫流程引擎的二开 > **流程引擎二开** = 流程(模板)设计完成之后,在**不修改引擎内核发送 / 流转代码**的前提下,用脚本、类、配置或 Worker,与引擎在固定生命周期点交互,从而完成业务校验、集成、通知、台账等逻辑的过程。 |对比项|改引擎源码|流程二开(本文范围)| | --- | --- | --- | |改动位置|发送 / 退回 / 调度内核|外挂、事件、自定义 Activity、配置执行体| |升级成本|高,难合并|相对低,核心可独立升级| |职责边界|引擎与业务缠在一起|引擎管流转,二开管业务| |可交付性|难复用、难交接|可按流程模板 / 模块绑定交付| 典型动作:发送前校验、发送后同步第三方、退回拦截、流程结束写台账、自定义活动节点、自定义办理页按钮等。 ### 1.2 为什么产品名词各异,却是同一件事 各产品对「挂业务」的叫法并不统一:Listener、Delegate、Action、StepBody、Custom Activity、外挂、事件配置、ServiceTask…… 评估时不必纠结名词,应看三件事是否成立: 1. **有没有稳定的生命周期时钟**(发送前 / 发送后 / 完成 / 退回 / 结束……) 1. **业务代码是否落在内核之外**(可独立升级、可按模板绑定) 1. **前端、后端、配置是否都能接到同一套语义**(或至少有清晰分层) 这三件事,正是下文「三层挂接模型」要回答的。 --- ## 2. 三层挂接模型:L1 / L2 / L3 把二开能力按「谁写、写在哪、管什么边界」拆成三层: |层级|名称|一句话|谁写|典型载体|擅长|不适合单独承担| | --- | --- | --- | --- | --- | --- | --- | |L1|交互层|前端 / 页面侧写脚本交互|前端 / 全栈|办理页钩子、表单脚本、设计器 UI 插件|发送前校验、按钮定制、提示、字段联动|强事务写库、跨系统强一致| |L2|服务层|后端 / 进程内写代码|后端|JavaDelegate、Listener、FlowEvent、StepBody、自定义 Activity|强事务、改接收人 / 跳转、同步 ERP、审计|纯 UI 即时反馈| |L3|配置 / 编排层|设计器或模型配置|实施 / 低代码|SQL / HTTP / 脚本 / 表达式 / CodeAction|少写代码也能挂业务|复杂分支、深度引擎变量重构| 运行时可以理解为同一条业务动作的分层叠加(示意): ``` 用户点击「发送」 ├─ L1 交互层:页面校验 / 拦截 / 按钮与提示 └─ 请求进入引擎 ├─ L2 服务层:进程内代码(可改路由、写库、调系统) └─ L3 配置层:SQL / HTTP / 脚本 / 表达式(实施可配) ``` > **关键认识**:三层不是互斥三选一,而是**允许叠加**。常见顺序是 L1 先拦人机边界 → L2 再守系统真相 → L3 承接标准化连接。 ### 2.1 这一能力的作用 1. **把「流转」和「业务」拆开**:引擎回答怎么走,二开回答走到某点时业务做什么。 1. **把交付从「改核」变成「挂点」**:项目差异落在外挂与配置,而不是 fork 一份引擎。 1. **让不同角色都能参与**:前端、后端、实施不必挤在同一条技术路径上。 1. **让升级成为可能**:内核独立演进,业务扩展可保留、可迁移、可按模板复用。 ### 2.2 为什么它是重要评估指标 选型时如果只看「有没有设计器 / 是否 BPMN」,很容易漏掉真正决定项目成败的问题: |若二开能力弱|项目侧常见后果| | --- | --- | |只能改源码挂业务|升级噩梦、合并冲突、人员不敢动引擎| |只有后端代码扩展|前端交互与实施配置缺位,交付慢、协作成本高| |只有配置没有代码出口|复杂规则被迫塞进脚本地狱,难测难重构| |事件时钟不产品化|团队靠猜「什么时候会触发」,集成不可控| 因此:**二开不是售后补丁,而是引擎产品能力的一等公民。** 尤其在政企审批、ERP 集成、多系统待办同步等场景,L1/L2/L3 是否齐全,往往比「画布好不好看」更能决定能不能按期上线、能不能长期维护。 --- ## 3. 三层分别解决什么问题 ### 3.1 L1 交互层:人还在页面上时 **问题**:发送前要不要先提示?字段要不要联动?按钮要不要定制?成功后要不要局部刷新? **价值**:反馈即时,减少无效请求;把「人机交互边界」拦在浏览器侧。 **边界**:页面可拦截,但**业务真相以服务端为准**——金额、库存、权限最终仍应在 L2/L3 再校验一遍。 常见形态: - 办理页 / 表单脚本(发送前、打开后、字段变更) - 前端外挂类 / Override 钩子 - 设计器 UI 扩展(工具栏、表单控件、画布元素) ### 3.2 L2 服务层:进入引擎事务边界之后 **问题**:要不要改下一节点接收人?要不要同步 ERP?要不要写审计台账?要不要在同一事务里失败回滚? **价值**:强类型、可调试、可测试;能访问运行时上下文(当前节点、实例 ID、变量、发送结果等)。 **边界**:适合系统集成与规则编排;不适合用它替代页面即时交互。 常见形态: - Java:`JavaDelegate`、`ExecutionListener`、`TaskListener` - .NET:`StepBody`、Custom Activity、Action Provider、流程事件基类 - 全局拦截 / 中间件(平台级策略)+ 流程级绑定(模板级策略) ### 3.3 L3 配置 / 编排层:少写代码也能挂 **问题**:没有专职开发时,实施能否在设计器里把「一条 SQL / 一个 HTTP / 一段表达式」挂上? **价值**:把简单集成从程序员日程里解放出来;缩短交付周期。 **边界**:擅长标准化连接;复杂分支、深度重构仍应回到 L2。 常见形态: - BPMN 扩展:Listener / ServiceTask 上挂 class / expression / script - 设计器事件:SQL、WebApi、存储过程、业务单元 - 方案内 CodeAction、表达式语言(JUEL / FEEL / 自有 DSL) --- ## 4. 流行引擎怎么实现:Java 阵营 > 资料依据为各产品公开文档与社区常见做法。评级是「机制完整度」,不是业务场景总分。 **强** = 产品级、文档/样例完整;**中** = 能做但需自建较多;**弱** = 基本不覆盖或需从零实现。 ### 4.1 总对照(Java) |产品|定位倾向|L1 交互层|L2 服务层|L3 配置编排|二开主叙事| | --- | --- | --- | --- | --- | --- | |Camunda(社区版 / 平台)|BPMN 标准向编排 + 运维|中(表单/办理页多自建或另接;Cockpit/Tasklist 可扩展但非审批门户一站式)|强(JavaDelegate、Execution/Task Listener、外部任务)|强(脚本、表达式、Listener 配置、Connector 生态)|Listener + Delegate + External Task| |Flowable|BPMN / CMMN 引擎族|中(UI 多自建;企业版能力更完整)|强(Delegate、Listener、解析期注入 Listener)|强(expression / script / class 挂接)|与 Camunda 同源思想,Delegate Code 体系| |Activiti|经典 BPMN 引擎|中偏弱(产品 UI 依赖版本与生态)|强(JavaDelegate、Listener)|强偏中(脚本/表达式可用,实施配置体验因发行版而异)|经典委托代码模型| |该引擎|流程 + 表单一体化 BPM(Java)|强(办理页前端外挂协议产品化)|强(流程事件基类 / 后端外挂)|强(模板事件配置:SQL/HTTP/脚本等)|前端外挂 + 后端外挂 + 事件配置| #### Camunda / Flowable / Activiti:同一套「BPMN 扩展点」思维 三者同属 Activiti 谱系或其近亲,二开骨架高度相似: |层级|典型落点| | --- | --- | |L2|JavaDelegate(Service Task)、ExecutionListener(执行开始/结束)、TaskListener(人工任务 create/assignment/complete…)| |L3|BPMN extensionElements 中配置 class / delegateExpression / expression / script;Script Task;条件表达式| |L1|引擎本身通常不强制提供与审批办理页对齐的「前端外挂协议」;表单与工作台多由业务系统自建,或依赖 Tasklist / 商业套件 / 自研前端| **特点**: - **L2/L3 极强**:开发者友好,生态成熟,适合微服务与标准 BPMN。 - **L1 往往外置**:若你的项目强依赖「办理页发送前校验、按钮定制、字段联动」的产品级协议,需要额外评估表单/门户方案。 - **External Task(Camunda 等)** 把重活外移到 Worker,适合解耦与多语言工人进程——这是 L2 的「进程外变体」,仍属服务侧挂接。 #### 该引擎:把三层做成并列入口 该引擎与下文 .NET 侧的该引擎 **同根同源、语言不同**:事件名、分层思想、调度顺序一致,差异主要在 Java 包机制与 .NET 程序集机制。 产品概念上常称为:**前端外挂(L1)、后端外挂(L2)、模板事件配置(L3)**。详见第 6 节。 ### 4.2 Java 阵营怎么读表 |你更关心…|相对更贴的路径| | --- | --- | |标准 BPMN + 强代码扩展 + 云原生 Worker|Camunda / Flowable| |轻量嵌入、经典委托模型|Activiti 谱系引擎| |审批办理页也能产品级挂脚本,且配置/代码并列|该引擎这类一体化 BPM| --- ## 5. 流行引擎怎么实现:.NET 阵营 ### 5.1 总对照(.NET) |产品|定位倾向|L1 交互层|L2 服务层|L3 配置编排|二开主叙事| | --- | --- | --- | --- | --- | --- | |Elsa Workflows|通用长流程编排 + Studio|中偏强(Studio 元数据/UIHint;业务办理页多自建)|强(Custom Activity、Middleware、Module/Feature)|中(活动组合与表达式强;SQL/实施配置弱于 BPM)|自定义 Activity 生态| |Workflow Core|轻量嵌入式流程库|弱(几乎无产品级办理 UI)|强(StepBody + 中间件)|中(JSON/YAML 引用 Step 类型)|步骤即扩展单元| |WorkflowEngine.NET|可嵌入引擎(生产多需商业许可)|强(设计器模板 / 表单可定制)|强(Action Provider、Plugin、Custom Activity)|强(方案内 CodeActions 等)|Action + Plugin| |Slickflow|BPMN 风格 .NET 引擎|中(设计师可嵌;业务页多自建)|强(ExternalService、引擎 API)|强(本地类 / WebApi / SQL / 过程)|节点 Action 多执行体| |该引擎|流程 + 表单 + 组织一体化 BPM|强(Vue 前端外挂)|强(FlowEventBase 后端外挂)|强(设计器事件 + SQL/WebApi/过程等)|前端外挂 + 后端外挂 + 事件配置| #### Elsa / Workflow Core:开发者编排优先 - **Elsa**:二开主路径是「自定义积木」(Activity)+ 中间件 + 可打包扩展;对人机审批语义(会签、组织待办)通常要自建。 - **Workflow Core**:`StepBody` 即业务单元,嵌入成本低;**几乎没有**产品级设计器/表单/待办门户——二开 ≈ 写代码 + 自建 UI。 #### WorkflowEngine.NET / Slickflow:设计器与节点执行体 - **WorkflowEngine.NET**:Action/Condition、CodeActions、Plugin、Custom Activity 文档完整;许可需单独评估。 - **Slickflow**:节点上挂本地服务 / WebApi / SQL / 过程,BPMN 语义清晰;前端「办理页外挂协议」完整度因产品线而异。 #### 该引擎:与该引擎同一套三层协议 见下一节——用同源实现对 L1/L2/L3 做「可核对」说明,而不是把某一品牌写成唯一正确答案。 ### 5.2 .NET 阵营怎么读表 |你更关心…|相对更贴的路径| | --- | --- | |自定义流程积木、云原生编排|Elsa、Workflow Core| |设计器内 Action / 商业嵌入|WorkflowEngine.NET| |BPMN 节点挂服务 / SQL / WebApi|Slickflow| |审批生命周期 + 办理页外挂 + 配置事件|该引擎 / 同类一体化 BPM| --- ## 6. 同源实证:该引擎(Java)与该引擎(.NET)如何落在三层上 > 二者事件模型同源:同一套事件语义(如发送前 / 发送成功 / 流程结束后),三种写法并列。 本节只做**机制对照与源码索引**,便于读者用公开实现核对「三层模型」是否可落地;不作为唯一选型结论。 ### 6.1 概念映射 |三层模型|产品概念(该引擎)|含义| | --- | --- | --- | |L1 交互层|前端外挂|浏览器侧挂流程脚本:校验、按钮、提示、联动| |L2 服务层|后端外挂|服务端强类型事件类:事务、改人、集成、审计| |L3 配置层|模板事件配置|设计器挂 SQL / HTTP / 脚本 / 业务单元等| 服务端调度思想(示意):全局拦截 → 流程级后端外挂 → 配置事件 → 消息推送(与业务事件共用时钟、职责分离)。 ``` 用户点击发送 ├─ ① L1:前端外挂(可拦截) └─ ② HTTP → 引擎发送编排 └─ 统一事件调度 ├─ 全局后端拦截 ├─ L2:流程事件基类(后端外挂) ├─ L3:事件配置 / 数据源执行体(模板事件配置) └─ 消息推送(同事件标记,独立配置) └─ ③ L1:发送成功后的前端副作用 ``` ### 6.2 L1:前端外挂(以该引擎 Vue3 为例) 约定:外挂类名以 `FlowExt_` 开头,并绑定流程号;与后端认同一套事件名(如 `SendWhen` / `SendSuccess`)。 ``` protected constructor(classID: string, flowNo: string) { if (classID.includes('FlowExt_') == false) { message.warning('外挂类名[' + classID + ']不符合规范,必须是以 FlowExt_ 开头.'); return; } super(classID); this.FlowNo = flowNo; } ``` |适合|说明| | --- | --- | |发送前弹窗校验、字段联动、按钮定制|反馈在页面完成| |发送成功后的提示 / 局部刷新|不替代服务端写库| ### 6.3 L2:后端外挂(.NET 与 Java 对照) 基类约定要点(两端一致): 1. 子类重写事件方法与引擎交互 1. **一个子类与一个(组)流程模板绑定**(`FlowMark` / `getFlowMark`) 1. 基类暴露运行时变量,降低重复查询 1. 类进入约定程序集 / 包后由工厂发现 **.NET Demo(该引擎)**: ``` public class 后端外挂示例 : FlowEventBase { public override string FlowMark { get { return ",065,"; } } public override string SendWhen { if (1 == 3) return "err@不符合流程发起条件,阻止流程发送。"; if (1 == 1) return "后端外挂 /App/Demo/后端外挂示例 SendWhen 已经执行成功,节点ID:" + this.HisNode.NodeID + ",WorkID:" + this.WorkID; ``` **Java Demo(该引擎)**:`bp.App.Demo.WaiGua.WaiGuaFlow` 继承 `bp.wf.FlowEventBase`,通过 `getFlowMark` 绑定流程,重写 `SendWhen` 等——与 .NET 侧同一设计。 |适合|说明| | --- | --- | |同步 ERP、改接收人、写台账|强一致、可调试| |全公司统一审计|走全局拦截,而不是每个流程复制粘贴| ### 6.4 L3:模板事件配置 在设计器为节点 / 流程 / 表单挂执行体,写入事件配置(如 `Sys_事件配置`),执行体可为 SQL、WebApi、存储过程、事件类、业务单元等。 |挂接点示例|配置做法(L3)|代码做法(L2)| | --- | --- | --- | |发送前|SQL 校验金额是否超限|后端外挂复杂规则 + 改接收人| |发送成功|WebApi 同步外部系统|后端外挂写第三方待办| |流程结束|过程归档|后端关外部待办 + 前端提示| ### 6.5 同源实现的启发(公平表述) 该引擎证明了一件事: > **三层挂接不必只存在于论文里**——可以把 L1/L2/L3 做成同一事件时钟上的并列入口,让前端、后端、实施各走各的路,又在同一生命周期点汇合。 同时应看到边界:这类产品的扩展叙事偏「审批事件挂业务」,与 Elsa 那种「自定义画布 Activity 生态」不是同一赛道;选型时应按自己的主场景对齐,而不是按品牌热度对齐。 --- ## 7. 用三层模型做选型:一张检查清单 评估任意引擎时,可用下面清单做「可核对」提问(建议对方给文档或样例,而不是口头承诺): ### L1 交互层 - 办理页是否有稳定的发送前 / 发送后钩子? - 能否定制按钮、提示、字段联动,且**不改引擎前端内核**? - 前端事件名是否与后端生命周期对齐(或有明确映射表)? ### L2 服务层 - 能否在发送前拦截并回滚? - 能否读取并改写路由 / 接收人 / 流程变量? - 扩展是否按流程模板绑定,并可独立部署(程序集 / 包 / NuGet / jar)? - 是否区分「全局策略」与「单流程策略」? ### L3 配置编排层 - 设计器能否挂 SQL / HTTP / 脚本 / 表达式,而无需每次发版? - 配置执行体失败时,错误是否可观测、可阻断? - 简单集成走配置、复杂逻辑走代码,路径是否清晰? ### 跨层原则 - 三层是否允许叠加,顺序是否文档化? - 消息推送与业务脚本是否解耦(同事件点、分职责)? - 升级引擎时,业务扩展目录是否默认不被覆盖? --- ## 8. 场景 × 层级:怎么选,而不是怎么站队 |场景|更优先的层|原因| | --- | --- | --- | |发送前弹窗、禁用按钮、字段联动|L1|反馈即时| |同步 ERP、改接收人、写业务台账|L2|强一致、可调试| |一条 SQL / 一个 HTTP 就能完成|L3|实施可配,最快| |全公司统一审计 / 组织策略|L2 全局|一次拦截,全流程生效| |前端团队强、后端紧|L1 + L3|交互与简单集成分流| |后端团队强、要长期演进|L2 为主|可测试、可重构、可版本管理| |标准 BPMN 微服务编排|L2 Activity/Delegate + 可选 External Task|积木与 Worker 更贴| |政企审批 + 表单一体化交付|L1+L2+L3 产品化是否齐全|三层缺一都会转嫁成本| --- ## 9. 结语 流程引擎的竞争力,不只在「把图画出来」,更在: > **业务能不能稳稳挂上去——挂在交互层、服务层、还是配置层——并且始终不污染内核。** 三层挂接模型给出的是一把**跨产品、跨语言**的尺子: - **L1** 守人机边界 - **L2** 守系统真相 - **L3** 释放实施生产力 Java 阵营里,Camunda / Flowable / Activiti 把 L2/L3 做到了行业标杆,L1 多依赖外围表单与自建工作台;该引擎则把三层做成办理页协议与设计器配置的并列能力。 .NET 阵营里,Elsa / Workflow Core 偏开发者编排,WorkflowEngine.NET / Slickflow 偏设计器与节点执行体,该引擎与该引擎同源,用前端外挂 / 后端外挂 / 事件配置覆盖三层。 **没有绝对的第一名,只有与场景对齐的挂接方式。** 选型时,把对方的名词翻译回 L1/L2/L3,再用第 7 节清单逐项核对——比比较口号更接近工程真相。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册