摘要:本文介绍 VeapAI 如何把「一个流程驱动另一个流程」的联动逻辑从散落的埋点代码,收敛为可视化配置。核心是两张表:公共服务清单(wf_public_service)声明可复用的联动能力,驱动规则(wf_process_linkage_rule)显式回答「什么时候、触发什么、目标是谁、失败怎么办」。运行期由 WfLinkageEventListener 监听触发点、按契约执行并写入三级台账,失败必须抛出、不得吞错;同时强调 wf_* 只负责编排与追踪,业务事实以业务域为准。
> 关键词:多流程联动、流程驱动、服务编排 | 首发:CSDN
一个立项流程发起后要"禁用"旧项目的报销发起入口;合同审批完成后要"解锁"关联的付款流程。这类"一个流程驱动另一个流程"的联动,常见做法是埋代码:每个节点完成的地方加一段 if,判断要不要调用某某服务。时间一长,联动逻辑散得到处都是,没人说得清"到底谁锁了谁"。
VeapAI 把联动做成了可视化配置:公共服务清单 + 驱动规则。
## 公共服务清单:wf_public_service
表注释:WF-流程联动公共服务清单(可视化配置)。关键字段:
| 字段 | 说明 |
| --- | --- |
| `service_code` | 服务编码(租户内唯一,`uk_public_service_code`) |
| `service_title` | 服务标题(可视化显示) |
| `bean_name` | Spring BeanName(必须实现稳定接口;解析失败必须失败并抛出) |
| `service_kind` | `DISABLE_START` / `LOCK_TASKS` / `UNLOCK_TASKS` / `CLOSE_TASKS` / `CUSTOM` |
| `pre_service_code` | 依赖服务编码(顺序约束;禁止循环依赖) |
| `in_params_process` / `in_params_form` | 流程 / 表单依赖参数(JSON,稳定 schema 由实现定义) |
服务是清单化的:管理端先配置,运行期按 `service_code` 解析 bean、失败必须抛出的执行语义写进了契约。加一种联动能力是注册一个服务,不是改宿主代码。
## 驱动规则:wf_process_linkage_rule
表注释:WF-多流程驱动关系规则(主 / 子 / 独立流程联动)。关键字段:
| 字段 | 说明 |
| --- | --- |
| `src_proc_def_key` / `src_task_def_key` | 源流程 / 源节点(节点为空表示流程级触发) |
| `dest_proc_def_key` / `dest_task_def_key` | 目标流程 / 目标节点 |
| `trigger_point` | 触发点:`PROC_START_AFTER` / `TASK_CREATE_AFTER` / `TASK_COMPLETE_BEFORE` / `TASK_COMPLETE_AFTER` / `PROC_ENDED` 等 |
| `service_code` | 公共服务编码 |
| `required_rule_type/value` | 生效条件(`ALWAYS` / `STRUCTURED_V2` / `BY_BEAN`;结构化条件落 `wf_process_linkage_rule_cond_item` 分表) |
| `dest_select_kind` | 目标实例选择:`SAME_BUSINESS_KEY` / `PARENT_PROC_INST` / `VAR_PROC_INST_ID` |
| `dest_missing_strategy` | 目标缺失策略:`FAIL` / `START_NEW`(默认 FAIL) |
| `execute_phase` | 执行阶段:`IN_TX` / `AFTER_COMMIT` |
| `fail_strategy` | 失败策略:`BLOCK_SRC` / `NON_BLOCKING` |
| `dedupe_kind` / `dedupe_window_sec` | 幂等去重(`NONE` / `DEDUPE_WINDOW`) |
| `cycle_guard_kind` / `max_depth` | 防循环(`NONE` / `DEPTH_LIMIT`) |
可视化配置回答的是"什么时候、触发什么、目标是谁、失败怎么办"四个问题,全部显式声明,没有默认猜测。
![联动规则配置]
## 执行追踪:三级留痕
按契约,每次联动执行写 `wf_linkage_run_task / log / msg` 三级记录,成功 / 失败可追溯。三张台账表和前端运行日志页面(`veap-ui/src/views/workflow/wfLinkageRunLog/`)已经在代码里,失败不得吞错的规矩也在契约里。
契约还定了两个边界语义:bean 不存在、规则引用不合法、目标实例定位失败必须失败并抛出;而"没有命中任何联动规则"属于正常无动作,不等同于吞错继续。
![多流程联动执行链路]
## 当前实现状态,先说清楚
写这篇时,联动这条线的两端都已经接上:配置域(`wf_public_service`、`wf_process_linkage_rule` 加条件项分表、三级台账表)之外,运行期执行器也按契约补齐了。`WfLinkageEventListener` 监听四个 AFTER 触发点(`PROC_START_AFTER` / `TASK_CREATE_AFTER` / `TASK_COMPLETE_AFTER` / `PROC_ENDED`),发起与办理前的两个 BEFORE 触发点(`LINKAGE_REQUEST_BEFORE` / `TASK_COMPLETE_BEFORE`)由 `WfRuntimeFacade` 显式调用。条件评估、目标实例选择(含 `START_NEW` 启动新实例)、`IN_TX / AFTER_COMMIT` 执行阶段、`BLOCK_SRC / NON_BLOCKING` 失败策略、幂等窗口与深度防循环,在执行器里都有消费点。
一句话:规则配得出来,也真的会触发。执行 bean 要由业务侧按 `WfPublicServiceExecutor` 接口注册——bean 不存在或没实现接口,执行器会失败并写三级台账,不会假装成功。
## 一处较真的设计
业务事实不被 wf_* 替代。锁定状态、权限判断必须来自业务域的事实来源,wf_* 只负责编排与追踪,禁止在扩展层推断业务事实。
这条门禁是联动系统最容易翻车的地方:如果 wf_* 里存一份"锁定状态",业务域再存一份,两份迟早不一致。契约直接规定:wf_* 不存业务结论,只存"怎么调、调用结果如何"。谁锁的、锁没锁,以业务域为准。
## 复现
```bash
cd veap-cloud
mvn -B -pl veap-package/veap-monolith -am -DskipTests package
```
注册一个 `CUSTOM` 公共服务(bean 实现 `WfPublicServiceExecutor`),配一条 `TASK_COMPLETE_AFTER` 触发的联动规则,跑一遍流程:条件命中后服务被调用,`wf_linkage_run_task / log / msg` 三张台账表和 `wfLinkageRunLog` 页面能看到这次执行的成功 / 失败记录。
下一篇是本序列收尾:配置即模型。
联动契约原文在 `docs/AiWorkflow/多流程驱动-定义配置应用-全流程与数据流契约-详细方案.md`,运行日志页面在 `veap-ui/src/views/workflow/wfLinkageRunLog/`:https://gitee.com/mindock/veap