4. 条件分支与路由:if-else逻辑在工作流中的应用、路由节点设计、动态路径选择

说实话,很多刚接触工作流设计的朋友,都会把流程想成一条直线——输入进来,处理一下,输出出去。但现实世界哪有这么简单?

你想想看,一个订单金额超过1000块要走审批,低于1000块直接发货。用户是VIP会员走专属通道,普通用户走标准流程。这些判断逻辑,就是工作流里的「岔路口」。

我个人习惯把条件分支比作铁路的「道岔」。没有道岔,火车只能沿着一条轨道跑。有了道岔,才能把不同的车厢送到不同的目的地。今天我们就来聊聊,怎么在工作流里搭好这些「道岔」。

4.1 if-else逻辑:最朴素的决策单元

if-else这东西,写代码的人再熟悉不过了。但在工作流里,它长什么样?

我见过不少团队,直接把代码里的if-else搬到了工作流里,结果画出来的流程图跟蜘蛛网似的。其实没必要那么复杂。

工作流里的if-else,核心就三个要素:

  • 条件表达式:判断的依据是什么?比如「订单金额 > 1000」
  • 真分支:条件成立时走哪条路
  • 假分支:条件不成立时走哪条路

举个例子,一个简单的客服工单分流:

{
  "nodeType": "condition",
  "name": "判断工单类型",
  "expression": "input.ticket_type == 'technical'",
  "trueBranch": "转技术部门",
  "falseBranch": "转客服部门"
}

嗯,这里要注意。条件表达式不要写得太复杂。我在项目中遇到过有人把五六个条件用and/or串在一起,结果调试的时候自己都看不懂。我的建议是:一个条件节点只做一个判断。复杂逻辑?拆成多个节点串联。

小技巧:条件表达式尽量用「正向判断」。比如「是VIP用户」比「不是普通用户」更容易理解。人脑处理正向逻辑比反向逻辑快得多。

4.2 路由节点设计:不止是二选一

if-else只能处理二选一。但实际业务中,经常遇到多选一的情况。比如:

  • 根据用户等级:普通、银卡、金卡、钻石,走四条不同的推荐策略
  • 根据错误类型:网络错误、参数错误、权限错误、未知错误,走四种不同的处理流程

这时候就需要「路由节点」了。说白了,路由节点就是一个多路开关。

我设计路由节点时,一般会包含这几个字段:

字段 说明 示例
路由字段 根据哪个字段做判断 user.level
路由表 字段值到分支的映射 { "gold": "分支A", "silver": "分支B" }
默认分支 没有匹配时的兜底 走标准流程

你看,这个结构其实很像switch-case。但比switch-case更灵活的是,路由表可以动态配置,甚至可以在运行时从数据库加载。

{
  "nodeType": "router",
  "name": "用户等级路由",
  "routeField": "user.level",
  "routeTable": {
    "diamond": "diamond_pipeline",
    "gold": "gold_pipeline",
    "silver": "silver_pipeline",
    "bronze": "bronze_pipeline"
  },
  "defaultBranch": "standard_pipeline"
}
注意:路由节点一定要有默认分支。我曾经见过一个系统,因为没有配置默认分支,某个新来的用户等级是「platinum」,结果路由找不到匹配,整个流程卡死了。嗯,血的教训。

4.3 动态路径选择:让流程自己「思考」

静态路由虽然好用,但不够聪明。什么叫动态路径选择?就是流程走到某个节点时,根据当前上下文、历史数据、甚至外部系统的反馈,实时决定下一步去哪。

举个例子。一个智能客服流程:

  1. 用户提问
  2. 调用NLP模型判断意图
  3. 根据意图路由到不同处理节点
  4. 处理完成后,根据用户满意度评分,决定是否转人工

你看,第三步和第四步的决策,都不是写死的。它们依赖于前一步的输出结果。这就是动态路径选择的核心——决策依赖运行时数据

我个人习惯把动态路径选择分为两种模式:

  • 数据驱动:根据输入数据的某个字段值做路由。比如根据用户所在地区路由到不同的数据中心。
  • 策略驱动:调用一个策略服务,由策略服务返回下一步该去哪。比如A/B测试场景,由策略服务决定用户走实验组还是对照组。

策略驱动的实现方式,我一般会设计一个「策略节点」:

{
  "nodeType": "policy",
  "name": "A/B测试分流",
  "policyService": "http://policy-center/api/decide",
  "params": {
    "user_id": "{{context.user_id}}",
    "experiment_id": "exp_2024_001"
  },
  "outputField": "next_node"
}

这个节点调用外部策略服务,拿到结果后,把结果写入上下文的 next_node 字段。然后下一个路由节点根据这个字段做跳转。

核心思路:动态路径选择的关键,是把「决策逻辑」从「流程结构」中解耦出来。流程只负责「怎么走」,不负责「往哪走」。往哪走,由数据和策略说了算。

4.4 实战中的坑与解法

讲了这么多理论,来点实际的。我在项目中踩过的几个坑,分享给你:

坑一:条件判断的「隐式转换」

有一次,我用 input.age > 18 做判断,结果 input.age 是个字符串 "18",而 18 是数字。在JavaScript里 "18" > 18 是 false。嗯,排查了半天。后来我强制要求所有条件表达式里,参与比较的字段必须先做类型转换。

坑二:路由表的「热更新」

路由表如果写死在配置文件里,每次修改都要重启服务。对于需要频繁调整路由的场景,我建议把路由表存在Redis或数据库里,流程引擎每次执行时实时拉取。这样改路由不用重启,秒级生效。

坑三:循环路由

动态路由最怕出现循环。A路由到B,B路由到C,C又路由回A。一旦出现这种循环,流程就会死循环,直到资源耗尽。我的解法是:每个节点设置一个「最大访问次数」,比如3次。超过次数就强制走默认分支,并触发告警。

避坑指南:我曾经在一个金融项目里,因为没做循环检测,导致某个审核流程在三个节点间来回跳了200多次,把数据库连接池打满了。后来我加了一个「路径追踪」功能,每次路由都记录走过的节点ID,如果发现重复节点,直接中断并报错。

4.5 小结

条件分支和路由,是工作流从「线性」走向「非线性」的关键。记住几个要点:

  • 简单二选一用if-else,多选一用路由节点
  • 路由表要支持动态配置,别写死
  • 动态路径选择的核心是「决策与流程分离」
  • 别忘了兜底策略和循环检测

下一章,我们会聊聊循环与迭代——当流程需要重复执行某个步骤时,该怎么设计。到时候见。