跳转至

流程路由条件与子流程

本章解决「流程走到岔路口时往哪走」一类问题:路径分支条件、经过路径时副作用、子流程选择与参数、自动节点动作,以及在脚本里判断当前是回退还是提交。配套的审批人计算见 流程审批人动态计算,流程历史与人工干预见 流程查询、审批历史与人工干预

场景清单

场景 落点 / Label 关键契约
路径进入条件分支 WORKFLOW:RELATION_CONDITION(属性 condition Boolean,true 走该路径
路径经过时更新字段 WORKFLOW:RELATION_PASS_ACTION(属性 action 无返回值,副作用
选择 / 启动哪个子流程 WORKFLOW:SUBFLOW_SELECT(属性 subflowScript String,子流程 id
子流程实例数(按用户数等) WORKFLOW:SUBFLOW_INSTANCE(实例脚本) Number
子流程参数传递 WORKFLOW:SUBFLOW_PARAM(属性 paramPassingScript 与目标字段类型相符的值
自动节点动作 WORKFLOW:AUTONODE_ACTION 无强制,副作用
判断当前操作类型(回退/保存/提交) 操作前置 / 值脚本 getParameter("_flowType")

路径送出校验 WORKFLOW:RELATION_PASS_VALIDATE 属于「校验」语义(失败返回提示 String、成功返回 ""),见 校验


路径进入条件分支

业务目标:流程提交时根据当前表单字段值(金额、部门、类型等)动态决定走哪条路径(关联线),而不仅靠设计器里写死的字符串规则。

写在哪儿:流程域 → 流程设计器 → 双击关联线(路径)→ 路径进入条件脚本 → 值脚本。对应 .flow 属性 condition,Label WORKFLOW:RELATION_CONDITION。挂载点说明见 where-to-use/flow/flow-path.md

触发时机与上下文:提交流程、计算路径分支时触发。可用环境变量 WebUserCurrentDocument(即 getCurrentDocument() 拿到的当前文档)。

返回值契约Booleantrue 表示走该路径;false 表示不走。与隐藏脚本语义方向一致(true 生效)。

示例代码

// 按表单"部门"字段判断是否进入"总部审批"分支
(function () {
  var doc = getCurrentDocument();
  var deptName = doc.getItemValueAsString("部门");
  return deptName === "总部";
})()

代码说明

变体与扩展

// 金额 > 10000 走"高管复审"分支
(function () {
  var amount = getItemValueAsString("金额");
  var n = Number(amount);
  return !isNaN(n) && n > 10000;
})()
// 当前用户为"风控"角色时强制走风控分支
(function () {
  var user = getWebUser();
  var roles = user.getRoleList(); // Collection<UserRole>
  if (roles != null) {
    for (var iter = roles.iterator(); iter.hasNext();) {
      var role = iter.next();
      if (role.getName() === "风控") {
        return true;
      }
    }
  }
  return false;
})()

常见坑

  • 返回的是 Boolean,不是字符串 "true";直接 return true,不要写 return "true"
  • 多条关联线的进入条件若同时为 true,平台会按设计器内部顺序选其一;若需"互斥分支",请确保条件之间覆盖完整(如 金额>10000金额<=10000)。
  • 字段值比较务必用 ===(详见 GraalVM 差异);旧示例里的 == 在与数字混用时易出隐式转换。
  • 路径分支脚本与"路径送出校验"是两件事——前者定方向,后者定能否送出。

路径经过时更新字段

业务目标:流程经过某条关联线(路径)时,自动给表单写一个字段值(如把"审批状态"置为"已审批"),无需等到下一节点打开表单。

写在哪儿:流程域 → 流程设计器 → 双击关联线(路径)→ 路径执行脚本 → 值脚本。对应 .flow 属性 action,Label WORKFLOW:RELATION_PASS_ACTION。挂载点说明见 where-to-use/flow/flow-path.md

触发时机与上下文:流程提交经过该路径时触发(即在路径条件成立之后、目标节点收到之前)。可用 WebUserCurrentDocument

返回值契约:无强制(纯副作用脚本)。建议用 IIFE 包装但不返回。

示例代码

// 路径经过时把"审批状态"字段置为"已审批"
(function () {
  var process = getDocumentProcess();
  var doc = getCurrentDocument();
  doc.findItem("审批状态").setValue("已审批");
  process.doUpdate(doc); // 必须落库,否则只改内存
})()

代码说明

变体与扩展

// 经过路径时记录最近处理人和时间
(function () {
  var doc = getCurrentDocument();
  var user = getWebUser();
  doc.findItem("最近处理人").setValue(user.getName());
  doc.findItem("最近处理时间").setValue(format(new Date(), "yyyy-MM-dd HH:mm:ss"));
  getDocumentProcess().doUpdate(doc);
})()

常见坑

  • 只调 setValue、忘记 doUpdate,是最常见错误——内存里改了但库里没变。
  • 这是在路径经过时副作用,**不要**用它做"是否允许送出"的判断;后者用 WORKFLOW:RELATION_PASS_VALIDATE(见 表单与导入校验)。
  • 若字段较多或要做复杂联动,建议改在目标节点的"操作后置"或"审批人脚本"里写,更可控。

选择 / 启动哪个子流程

业务目标:主流程到达"子流程节点"时,按当前表单数据动态决定要启动哪个子流程模板(如技术部走"技术子流程"、其它部门走"普通子流程")。

写在哪儿:流程域 → 流程设计器 → 子流程节点 → 启动子流程脚本。对应 .flow 属性 subflowScript,Label WORKFLOW:SUBFLOW_SELECT。挂载点说明见 where-to-use/flow/flow-node.md

触发时机与上下文:主流程到达子流程节点、即将启动子流程时触发。可用 WebUserCurrentDocument

返回值契约String,子流程模板 id(即设计器里的"流程 id")。返回空或 null 会按节点默认配置走。

示例代码

// 根据部门名走不同子流程
(function () {
  var deptId = getItemValueAsString("部门");
  var process = getDepartmentProcess();
  var dept = process.doView(deptId);
  var techSubFlowId = "技术子流程实例id";     // 设计器中复制
  var normalSubFlowId = "普通子流程实例id";
  if (dept != null && dept.getName() === "技术部") {
    return techSubFlowId;
  }
  return normalSubFlowId;
})()

代码说明

变体与扩展

// 按表单"金额"是否超过阈值选择子流程
(function () {
  var amount = Number(getItemValueAsString("金额") || "0");
  return amount > 50000 ? "大额子流程id" : "常规子流程id";
})()

常见坑

  • 返回值是**子流程模板 id**(设计器里看到的字符串),不是流程实例 id,也不是表单名。
  • 比较部门对象时不要直接 dept == "技术部"(旧素材这样写不严谨,比较的是对象);应取 dept.getName() 后用 === 比较。
  • 子流程模板 id 在不同环境(开发/生产)可能不同,建议放配置项或常量,避免硬编码到生产环境失配。

子流程实例数(按用户数等动态)

业务目标:主流程到子流程节点时,按表单里"用户"字段的人数(或其它计数)启动多个并行的子流程实例——每个用户一个独立实例。

写在哪儿:流程域 → 流程设计器 → 子流程节点 → 启动 → 实例脚本(属性同 subflowScript 节点的实例数次级脚本)。挂载点说明见 where-to-use/flow/flow-node.md

触发时机与上下文:启动子流程时触发。可用 WebUserCurrentDocument

返回值契约Number,要启动的子流程实例数(必须 ≥ 1)。

示例代码

// 表单"用户"字段以";"分隔,按人数启动 N 个子流程实例
(function () {
  var userIds = getItemValueAsString("用户");
  var arr = userIds != null && userIds !== "" ? userIds.split(";") : [];
  var num = arr.length > 0 ? arr.length : 1;
  return num;
})()

代码说明

变体与扩展

// 按"金额"梯度决定实例数(这里仅示意,业务上罕用)
(function () {
  var amount = Number(getItemValueAsString("金额") || "0");
  if (amount > 100000) return 3;
  if (amount > 10000)  return 2;
  return 1;
})()

常见坑

  • 返回 0 或负数会让流程引擎异常;用 arr.length > 0 ? arr.length : 1 兜底。
  • 表单字段值为空时,"".split(";") 在 JS 里会得到 [""](长度为 1),需先判空再 split,否则会启动 1 个无意义实例。 -「子流程实例数」与「子流程实例序号」是两个概念——前者在主流程启动时定个数,后者是子流程自身查询自己是第几个(见 common-script-collection/master-sub-flow-instance.md,通过 doc.getParent() + SQL order by created 遍历匹配当前文档 id)。

子流程参数传递

业务目标:主流程启动子流程时,给子流程某字段填一个初始值(如把主流程的"申请人"传给子流程的"申请人"字段,或扩展字段值)。

写在哪儿:流程域 → 流程设计器 → 子流程节点 → 参数传递脚本。对应 .flow 属性 paramPassingScript,Label WORKFLOW:SUBFLOW_PARAM。挂载点说明见 where-to-use/flow/flow-node.md

触发时机与上下文:启动子流程时触发。可用 WebUserCurrentDocument

返回值契约:与目标字段类型相符的值(Object)。返回值会作为子流程对应字段的初始值。

示例代码

// 把主流程"用户"字段对应的拓展字段值传给子流程
(function () {
  var doc = getCurrentDocument();
  var userId = doc.getItemValueAsString("用户");
  var user = getUserById(userId);
  if (user != null) {
    return user.getField4(); // 用户拓展字段4的值
  }
  return userId;
})()

代码说明

变体与扩展

// 把主流程的流水号 + 当前时间一起以 JSON 字符串传给子流程的"备注"字段
(function () {
  var doc = getCurrentDocument();
  var sn = doc.getItemValueAsString("流水号");
  return "SN=" + sn + ";T=" + format(new Date(), "yyyy-MM-dd HH:mm:ss");
})()

常见坑

  • 返回值的类型必须能塞进目标字段(字符串字段返回字符串、日期字段返回日期);类型不符会落库为 null 或抛错。
  • 不要在此脚本里 doUpdate 当前文档——这是参数脚本,不是副作用脚本。

自动节点动作

业务目标:流程走到"自动节点"(无人工审批、自动执行后流向下一节点)时,执行一段自定义动作(如打日志、调外部接口、做计算后写字段)。

写在哪儿:流程域 → 流程设计器 → 自动节点 → 脚本。对应 Label WORKFLOW:AUTONODE_ACTION

触发时机与上下文:流程流转到自动节点时触发。可用 WebUserCurrentDocument

返回值契约:无强制(副作用脚本)。

示例代码

(function () {
  var doc = getCurrentDocument();
  var user = getWebUser();
  // 写日志(println 输出到平台日志)
  println("auto node hit: doc=" + doc.getId() + " by " + user.getName());
})()

代码说明

  • println(...) 输出到平台日志,调试常用。
  • 自动节点的"送出时机"另由 auditDateTimeScript 等节点时间脚本控制(见 iscript-usage/flow.md 中"自动节点送出时机"示例)。

变体与扩展

// 自动节点:根据金额字段写"复审标记"
(function () {
  var doc = getCurrentDocument();
  var amount = Number(doc.getItemValueAsString("金额") || "0");
  if (amount > 50000) {
    doc.findItem("复审标记").setValue("需复审");
    getDocumentProcess().doUpdate(doc);
  }
})()

常见坑

  • 自动节点**没有人工干预**——脚本里的异常不会有人看到,建议用 try/catch 包裹关键逻辑并把错误 println 到日志。
  • auditDateTimeScript 用于控制自动节点的提交时间,常用 parseDate 解析表单日期字段。注意源素材里写法 v.trim().length(属性)是 GraalVM 合规的(详见 GraalVM 差异),不要照搬某些旧示例的 .length()

判断当前操作类型(回退 / 保存 / 提交)

业务目标:在保存前、提交前、回退前等脚本里区分"当前用户在做什么操作",从而走不同分支(如回退时清空"审批状态"、提交时记录提交时间)。

写在哪儿:表单域 → 表单 → 控件或表单的"保存前/提交前/回退前脚本"(操作前后置),也可用于任意值脚本。无固定 Label(取决于挂在哪),核心是用 getParameter 取平台注入参数。

触发时机与上下文:保存/提交/暂存/回退前触发(具体看挂在哪个动作上)。可用 WebUserCurrentDocumentgetParameter("_flowType")getParameter("_attitude")getParameter("_operation")

返回值契约:依挂载点而定(值脚本/操作前置的契约见 前言)。这里只讨论"如何取参数判断操作类型"。

示例代码

// 区分回退 vs 保存/提交/暂存;并在回退时把"备注"写到"回退原因"字段
(function () {
  var flowType = getParameter("_flowType");
  var doc = getCurrentDocument();

  if (flowType === "81") {
    // 回退
    var remark = getParameter("_attitude"); // 操作备注/意见
    doc.findItem("回退原因").setValue(remark);
    return "已记录回退原因";
  }

  if (flowType === "80") {
    // 暂存 / 保存 / 提交(三者都会返回 80)
    doc.findItem("最后操作时间").setValue(format(new Date(), "yyyy-MM-dd HH:mm:ss"));
    return "";
  }

  return "其它操作类型:" + flowType;
})()

代码说明

  • getParameter(name) 取平台注入参数 → iscript-guid/functions/system-functions.md
  • getParameter("_flowType")"81" = 回退;"80" = 暂存/保存/提交(三种操作**都会**返回 80,无法用它进一步细分,需结合 _operation 等参数或挂载到更精确的位置)
  • getParameter("_attitude"):用户填写的审批意见/备注(可能为空,使用前需判空)
  • format(date, pattern) 格式化日期 → iscript-guid/functions/date-functions.md

变体与扩展

// 仅在回退时执行副作用(写日志),其它操作直接放行
(function () {
  if (getParameter("_flowType") === "81") {
    var doc = getCurrentDocument();
    println("rollback: " + doc.getId() + " reason=" + getParameter("_attitude"));
  }
  return "";
})()
// 取并清洗 _attitude(参考 common-script-collection/get-form-current-operation)
(function () {
  var remark = getParameter("_attitude");
  if (remark != null && remark.trim().length > 0) { // length 是属性
    return remark.trim();
  }
  return "(无备注)";
})()

常见坑

  • _flowType 不能区分"保存"和"提交"——两者都返回 "80"。需要细分时挂到不同的"保存前/提交前"位置,或结合 _operation 等参数;详见 common-script-collection/get-form-current-operation.md 的说明。
  • _attitude 在非审批场景可能为 null,使用前必须判空;remark.trim().length(属性)不要写成 .length() 方法,详见 GraalVM 差异
  • 比较参数值用 === "81"(字符串字面量比较),不要用 .equals(...)

相关场景