# Workflow steps JSON SSOT 本文档是 Workflow `steps` JSON 的单一事实来源(SSOT),定义完整数据结构,适用于: - **查询场景**:理解 `+workflow-get` 返回的 `steps` 结构 - **创建/修改场景**:构造 `+workflow-create` / `+workflow-update` 的 `--json` body > 💡 **本文档是纯字段参考**。如需**创建/修改**工作流的完整示例,请阅读 [workflow-guide.md](lark-base-workflow-guide.md)。 --- ## 📖 快速导航 根据你的需求跳转到对应章节: | 需求 | 章节 | |------|------| | 了解 Step 基础结构 | [WorkflowStep 基础结构](#workflowstep-基础结构) | | 查询 Trigger 类型及 data 字段 | [Trigger data](#trigger-data-详细结构) | | 查询 Action 类型及 data 字段 | [Action data](#action-data-详细结构) | | 查询 Branch/Loop 结构 | [Branch data](#branch-data-详细结构) / [System data](#system-data-详细结构) | | 查询 ValueInfo/Condition 等公共类型 | [公共类型](#公共类型) | --- ## WorkflowStep 基础结构 每个步骤(Trigger / Action / Branch / System)共享以下字段: ```json { "id": "step_xxx", "type": "AddRecordTrigger", "title": "监控新订单", "next": "step_yyy", "data": {} } ``` | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `id` | string | 是 | 步骤唯一 ID(用户自定义,被 `next` 和 `children.links[].to` 引用) | | `type` | string | 是 | 步骤类型,见下方枚举 | | `title` | string | 否 | 步骤标题 | | `children` | StepChildren | 否 | 子关系边,承担所有分支/循环 | | `next` | string | null | 否 | 线性后继节点 ID;`null` 表示流程结束 | | `data` | object | 是 | 步骤详细配置,按 `type` 区分,见后续各节 | > **总原则**:连线写 `children`,扩展标识写 `meta`,输入参数写 `data`。 --- ## StepChildren 与 ChildLink ### StepChildren ```json { "links": [ /* ChildLink[] */ ] } ``` | 字段 | 类型 | 说明 | |------|------|------| | `links` | ChildLink[] | 子关系边列表;无子关系时为空数组 `[]` | ### ChildLink 每条关系边描述从当前节点到目标节点的有向连线: ```json { "kind": "if_true", "to": "step_4", "label": "branch_1", "desc": "金额大于1000" } ``` | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `kind` | string | 是 | 关系类型:`if_true` / `if_false` / `case` / `loop_start` / `slot` | | `to` | string | 是 | 目标节点 ID | | `label` | string | 否 | 可选标签(如 `branch_1`、`tool`、`llm`、`memory`) | | `desc` | string | 否 | 可选语义说明(如"销售部门"、"积极情绪") | `kind` 使用场景: | kind | 使用节点 | 说明 | |------|---------|------| | `if_true` | IfElseBranch | 条件为真时跳转 | | `if_false` | IfElseBranch | 条件为假时跳转 | | `case` | SwitchBranch / AIClassificationBranch | 多路分支,`label` 建议用 `branch_1` 等中性标签,`desc` 写语义 | | `loop_start` | Loop | 循环体入口 | | `slot` | AIAgentAction | 挂载 LLM / 工具 / 记忆子节点,`label` 为 `llm` / `tool` / `memory` | --- ## StepType 枚举 ### Trigger 类型 | type | 说明 | |------|------| | `AddRecordTrigger` | 新增记录时触发 | | `SetRecordTrigger` | 记录被修改时触发 | | `ChangeRecordTrigger` | 记录满足条件时触发 | | `TimerTrigger` | 定时触发 | | `ReminderTrigger` | 日期提醒触发 | | `ButtonTrigger` | 按钮点击触发 | | `LarkMessageTrigger` | 接收飞书消息触发 | > 所有 Trigger 节点**请勿设置** `children` ,通过 `next` 串联后继。 ### 触发器选型指南 | 需求描述 | 触发器 | |---------|--------| | 新增记录时 | `AddRecordTrigger` | | 字段变为特定值时(**仅修改**) | `SetRecordTrigger` | | **新增或修改**都触发 | `ChangeRecordTrigger` | | 拿不准用哪个 | `ChangeRecordTrigger` | > ⚠️ `SetRecordTrigger` 仅监听修改,`ChangeRecordTrigger` 同时监听新增 + 修改。 ### Action 类型 | type | 说明 | |------|------| | `AddRecordAction` | 新增记录 | | `SetRecordAction` | 更新记录 | | `FindRecordAction` | 查找记录 | | `HTTPClientAction` | HTTP 请求 | | `Delay` | 延迟 | | `LarkMessageAction` | 发送飞书消息 | | `GenerateAiTextAction` | AI 生成文本 | > 所有 Action 节点**请勿设置** `children` ,通过 `next` 串联后继。 ### Branch 类型 | type | 说明 | |------|------| | `IfElseBranch` | 条件分支,`children.links` 含 `if_true` 和 `if_false` | | `SwitchBranch` | 多路分支,`children.links` 含多个 `case` | ### System 类型 | type | 说明 | |------|------| | `Loop` | 循环,`children.links` 含 `loop_start` 指向循环体入口 | --- ## Trigger data 详细结构 ### AddRecordTrigger ```json { "table_name": "订单表", "watched_field_name": "状态", "trigger_control_list": ["pasteUpdate", "automationBatchUpdate"], "condition_list": [] /* AndCondition 数组 */ } ``` | 字段 | 必填 | 说明 | |------|------|------| | `table_name` | 是 | 监控的数据表名 | | `watched_field_name` | 是 | 监控的字段名 | | `trigger_control_list` | 否 | 触发控制,可选值:`pasteUpdate` / `automationBatchUpdate` / `syncUpdate` / `appendImport` / `openAPIBatchUpdate` | | `condition_list` | 否 | 过滤条件数组,数组中每个元素为 AndCondition 结构,多个 AndCondition 之间为 OR 关系 | ### ChangeRecordTrigger ```json { "table_name": "任务表", "trigger_control_list": [], "condition": null } ``` | 字段 | 必填 | 说明 | |------|------|------| | `table_name` | 是 | 监控的数据表名 | | `trigger_control_list` | 否 | 触发控制,可选值:`pasteUpdate` / `automationBatchUpdate` / `syncUpdate` / `appendImport` | | `condition_list` | 否 | 过滤条件数组,数组中每个元素为 AndCondition 结构,多个 AndCondition 之间为 OR 关系 | ### SetRecordTrigger ```json { "table_name": "订单表", "record_watch_conjunction": "and", "record_watch_info": [ /* FieldCondition[] */ ], "field_watch_info": [ { "field_name": "状态", "operator": "is", "value": [{ "value_type": "text", "value": "已发货" }] } ], "trigger_control_list": [], "condition_list": null } ``` | 字段 | 必填 | 说明 | |------|----|------| | `table_name` | 是 | 监控的数据表名 | | `record_watch_conjunction` | 否 | 记录筛选组合方式:`and` / `or`,默认 `and` | | `record_watch_info` | 否 | 记录级过滤条件(修改前值匹配),为空则监听全部 | | `field_watch_info` | 是 | 字段级监控条件列表,至少一个 | | `trigger_control_list` | 否 | 触发控制,可选值:`pasteUpdate` / `automationBatchUpdate` / `syncUpdate` / `appendImport` | | `condition_list` | 否 | 过滤条件数组,数组中每个元素为 AndCondition 结构,多个 AndCondition 之间为 OR 关系 | `FieldWatchItem`: | 字段 | 类型 | 说明 | |------|------|------| | `field_name` | string | 监听字段名称 | | `operator` | string | 操作符(仅明确要求字段满足条件时填) | | `value` | ValueInfo[] | 触发值 | ### TimerTrigger ```json { "rule": "WEEKLY", "start_time": "2025-01-01 09:00", "sub_unit": [1, 3, 5], "is_never_end": true } ``` | 字段 | 必填 | 说明 | |------|------|------| | `rule` | 是 | `NO_REPEAT` / `DAILY` / `WEEKLY` / `MONTHLY` / `YEARLY` / `WORKDAY` / `CUSTOM` | | `start_time` | 否 | 开始时间,格式 `yyyy-MM-dd HH:mm` | | `interval` | 否 | 自定义间隔 [1,30](仅 CUSTOM) | | `unit` | 否 | 自定义单位:`SECOND` / `MINUTE` / `HOUR` / `DAY` / `WEEK` / `MONTH` / `YEAR` | | `sub_unit` | 否 | 子单位(`WEEKLY` 时为星期几数组 0-6,`MONTHLY` 时为几号数组 1-31) | | `end_time` | 否 | 结束时间 | | `is_never_end` | 否 | 是否永不结束 | ### ReminderTrigger ```json { "table_name": "项目表", "field_name": "截止日期", "offset": 1, "unit": "DAY", "hour": 9, "minute": 0, "condition_list": null } ``` | 字段 | 必填 | 说明 | |------|------|------| | `table_name` | 是 | 数据表名 | | `field_name` | 是 | 日期字段名(必须为 `datetime` / `created_at` / `formula` / `lookup` 类型) | | `unit` | 是 | 偏移单位:`MINUTE` / `HOUR` / `DAY` / `WEEK` / `MONTH` | | `offset` | 是 | 提前/延后的偏移量(正数=提前,负数=延后;范围由 `unit` 决定):`MINUTE` ∈ {0, 5, 15, 30, -5, -15, -30};`HOUR` ∈ [-6, -1] ∪ [1, 6];`DAY` ∈ [-7, 7];`WEEK` ∈ [-7, -1] ∪ [1, 7];`MONTH` ∈ [-7, -1] ∪ [1, 7] | | `hour` | 是 | 触发小时 (0-23),默认 9 | | `minute` | 是 | 触发分钟 (0-59),默认 0 | | `condition_list` | 否 | 过滤条件数组,数组中每个元素为 AndCondition 结构,多个 AndCondition 之间为 OR 关系 | ### ButtonTrigger ```json { "button_type": "buttonField", "table_name": "审批表" } ``` | 字段 | 必填 | 说明 | |------|------|------| | `button_type` | 是 | 按钮类型:`buttonField`(表格里的按钮,可操作当前记录数据)/ `buttonElement`(仪表盘、应用页面上的按钮,可执行整体操作) | | `table_name` | 否 | 绑定的数据表名,仅 `button_type=buttonField` 时填写 | > `buttonField` 和 `buttonElement` 的输出能力不同,详见下方「ButtonTrigger(按钮触发器)」输出说明。 ### LarkMessageTrigger ```json { "receive_scene": "group", "receiver": [{ "value_type": "group", "value": {"id": "oc_xxxx", "name": "测试群"} }], "scope": "all", "filter": { "conjunction": "and", "content_contains": ["关键词"], "sender_contains": [{ "value_type": "user", "value": {"id": "ou_xxxx", "name": ""} }], "is_new_message": true, "is_message_contain_attachment": false } } ``` | 字段 | 必填 | 说明| |------|------|---| | `receive_scene` | 是 | 接收场景:`group`(群聊)/ `chat`(单聊)| | `receiver` | 是 | 触发来源,支持 `user` / `group` / `ref`。在单聊场景下,该字段指“可以和机器人单聊的用户”;在群聊场景下,该字段指“接收信息的群组”| | `scope` | 是 | 触发范围:`at`(@提及)/ `all`(所有消息)。该参数仅在群聊场景有效,单聊场景请勿指定该参数| | `filter` | 是 | MessageFilter 消息过滤条件| `MessageFilter`: | 字段 | 类型 | 说明 | |------|------|----| | `conjunction` | string | `and` 满足所有条件 / `or` 任一条件| | `content_contains` | string[] | 关键词列表| | `sender_contains` | ValueInfo[] | 筛选发送人(仅群聊+群组来源时生效,单聊场景请勿指定该参数)| | `is_new_message` | boolean | 仅新话题消息(仅群聊时有效,单聊场景请勿指定该参数)| | `is_message_contain_attachment` | boolean | 是否仅附件消息触发| ## Action data 详细结构 ### AddRecordAction ```json { "table_name": "订单表", "field_values": [ { "field_name": "客户名称", "value": [{ "value_type": "text", "value": "张三" }] }, { "field_name": "金额", "value": [{ "value_type": "number", "value": 100 }] }, { "field_name": "创建人", "value": [{ "value_type": "ref", "value": "$.trigger_1.fieldIdxxx" }] } ] } ``` | 字段 | 必填 | 说明 | |------|------|------| | `table_name` | 是 | 目标数据表名 | | `field_values` | 是 | RecordFieldValue[] | ### SetRecordAction ```json { "table_name": "订单表", "max_set_record_num": 10, "field_values": [ { "field_name": "状态", "value": [{ "value_type": "option", "value": { "id": "opt1", "name": "已完成" } }] } ], "filter_info": { /* RecordFilterInfo */ }, "ref_info": { "step_id": "step_trigger" } } ``` | 字段 | 必填 | 说明 | |------|------|------| | `table_name` | 是 | 目标数据表名 | | `max_set_record_num` | 否 | 最大更新记录数,默认 100,范围 1-15000 | | `field_values` | 是 | RecordFieldValue[] | | `filter_info` | 否* | RecordFilterInfo 过滤条件(与 `ref_info` 互斥) | | `ref_info` | 否* | RefInfo 引用前置步骤的记录(与 `filter_info` 互斥) | ### FindRecordAction ```json { "table_name": "客户表", "field_names": ["客户名称", "联系方式", "等级"], "should_proceed_when_no_results": true, "filter_info": { /* RecordFilterInfo */ } } ``` | 字段 | 必填 | 说明 | |------|------|------| | `table_name` | 是 | 目标数据表名 | | `field_names` | 是 | 要检索的字段名列表,至少一个 | | `should_proceed_when_no_results` | 否 | 无结果时是否继续后续步骤,默认 `true` | | `filter_info` | 否* | RecordFilterInfo(与 `ref_info` 互斥) | | `ref_info` | 否* | RefInfo(与 `filter_info` 互斥) | ### HTTPClientAction ```json { "method": "POST", "url": [{ "value_type": "text", "value": "https://api.example.com/webhook" }], "queries": [ { "key": "source", "value": [{ "value_type": "text", "value": "workflow" }] } ], "headers": [ { "key": "Content-Type", "value": [{ "value_type": "text", "value": "application/json" }] } ], "body_type": "raw", "raw_body": [ { "value_type": "text", "value": "{\"record_id\":\"" }, { "value_type": "ref", "value": "$.step_1.recordId" }, { "value_type": "text", "value": "\"}" } ], "response_type": "json", "response_value": "{\"success\":true,\"message\":\"data fetched successfully\"}" } ``` | 字段 | 必填 | 说明 | |------|-----|------| | `method` | 否 | 请求方法:`GET` / `POST` / `PUT` / `PATCH` / `DELETE`,默认 `POST` | | `url` | 是 | ValueInfo[],请求 URL,支持 `text` / `ref` 拼接 | | `queries` | 否 | KeyValue[],查询参数 | | `headers` | 否 | KeyValue[],请求头 | | `body_type` | 否 | 请求体类型:`none` / `raw` / `form-data` / `form-urlencoded`,默认 `raw` | | `raw_body` | 否 | ValueInfo[],原始请求体,仅 `body_type=raw` 时使用 | | `form_body` | 否 | KeyValue[],表单数据,仅 `body_type=form-data` 或 `body_type=form-urlencoded` 时使用 | | `response_type` | 否 | 响应类型:`none` / `text` / `json`,默认 `json` | | `response_value` | 否 | string,JSON 字符串形式的响应结果示例;仅当 `response_type=json` 时必填 | `KeyValue`: | 字段 | 类型 | 说明 | |------|------|------| | `key` | string | 参数名 / 请求头名 | | `value` | ValueInfo[] | 参数值 / 请求头值,支持 `text` / `ref` | ### Delay ```json { "duration": 30 } ``` | 字段 | 必填 | 说明 | |------|------|------| | `duration` | 是 | 延迟时长(分钟),范围 [1, 120] | ### LarkMessageAction ```json { "receiver": [{ "value_type": "user", "value": {"id": "ou_xxxx"} }], "send_to_everyone": false, "title": [{ "value_type": "text", "value": "新订单通知" }], "content": [ { "value_type": "text", "value": "客户 " }, { "value_type": "ref", "value": "$.trigger_1.fldCustomerName" }, { "value_type": "text", "value": " 创建了新订单" } ], "btn_list": [ { "text": "查看详情", "btn_action": "openLink", "link": [{ "value_type": "text", "value": "https://example.com" }] } ] } ``` | 字段 | 必填 | 说明 | |------|------|------| | `receiver` | 是 | ValueInfo[] | | `send_to_everyone` | 是 | 是否发送给所有人 | | `title` | 否 | TextRefItem[] 消息标题 | | `content` | 是 | TextRefItem[] 消息内容 | | `btn_list` | 是 | 按钮列表,不需要时为空数组 | `ButtonConfig`: | 字段 | 类型 | 说明 | |------|------|------| | `text` | string | 按钮文字 | | `btn_action` | string | `addRecord` / `setRecord` / `openLink` | | `link` | ValueInfo[] | 跳转链接(`openLink` 时使用) | | `table_name` | string | 操作表名(`addRecord` 时使用) | | `record_values` | RecordFieldValue[] | 记录赋值(`addRecord` / `setRecord` 时使用) | ### GenerateAiTextAction ```json { "prompt": [ { "value_type": "text", "value": "请总结以下内容:" }, { "value_type": "ref", "value": "$.step_1.fieldxxx" } ] } ``` | 字段 | 必填 | 说明 | |------|------|------| | `prompt` | 是 | TextRefItem[] 提示词,支持 `text` / `ref` | ## Branch data 详细结构 ### IfElseBranch `children.links` 包含 `if_true` 和 `if_false` 两条边,`next` 指向两个分支汇合后的后继节点。 **如果涉及到复杂的多分支场景(分支数目 >= 3时),你应该采用 SwitchBranch,而不是嵌套的 IfElseBranch** ```json { "condition": { "conjunction": "or", "conditions": [ { "conjunction": "and", "conditions": [ { "left_value": { "value_type": "ref", "value": "$.step_1.fieldxxx" }, "operator": "isGreater", "right_value": [{ "value_type": "number", "value": 1000 }] } ] } ] } } ``` | 字段 | 必填 | 说明 | |------|------|------| | `condition` | 是 | OrGroup 判断条件,结构为 `(A and B) or (C and D)` | ### SwitchBranch `children.links` 包含多个 `case` 边(`label` 建议用 `branch_1`、`branch_2`,语义写在 `desc`)。 ```json { "mode": "exclusive", "no_match_action": "classifyToOther", "child_branch_list": [ { "name": "高优先级", "condition": { "conjunction": "or", "conditions": [ { "conjunction": "and", "conditions": [ { "left_value": { "value_type": "ref", "value": "$.step_1.fieldxxx" }, "operator": "is", "right_value": [{ "value_type": "text", "value": "P0" }] } ] } ] } } ] } ``` | 字段 | 必填 | 说明 | |------|------|------| | `mode` | 否 | 分支模式。`exclusive`:排他模式,仅执行一个满足条件的子分支;`parallel`:并行模式,执行所有满足条件的子分支。默认 `exclusive` | | `no_match_action` | 否 | `mode=exclusive` 时使用,无匹配时的处理策略。`classifyToOther`:归类到其他分支;`fail`:报错终止。默认 `classifyToOther` | | `fail_mode` | 否 | `mode=parallel` 时使用,部分分支出错时策略。`partialSuccess`:部分成功即继续;`fail`:任一失败即终止。默认 `partialSuccess` | | `match_mode` | 否 | `mode=parallel` 时使用,所有分支不满足时策略。`noneMatchSkip`:跳过继续;`noneMatchFail`:报错终止。默认 `noneMatchSkip` | | `child_branch_list` | 是 | BranchItem[],1-10 个条件分支 | `BranchItem`: | 字段 | 类型 | 说明 | |------|------|------| | `name` | string | 分支名称 | | `condition` | OrGroup | 分支条件 | ## System data 详细结构 ### Loop `children.links` 包含 `loop_start` 边指向循环体入口,`next` 指向循环结束后的后继节点。 ```json { "loop_mode": "continue", "max_loop_times": 100, "data": [{ "value_type": "ref", "value": "$.find_record_stepIdxxx.fieldRecords" }] } ``` | 字段 | 必填 | 说明 | |------|------|------| | `data` | 是 | ValueInfo[](仅支持 `ref` 类型),循环数据源,只能填一个 | | `loop_mode` | 否 | 单次错误时是否继续:`end`(终止)/ `continue`(继续) | | `max_loop_times` | 否 | 最大循环次数 | --- ## 公共类型 ### ValueInfo 所有值的基础类型,通过 `value_type` 区分: | value_type | value 类型 | 说明 | 示例 | |------------|-----------|------|------| | `text` | string | 文本 | `"张三"` | | `number` | number | 数字 | `100` | | `boolean` | boolean | 布尔值 | `true` | | `date` | string | 日期,可以是具体时间字符串,或者相对时间值 | `"2025/01/01"`、`"2025/01/01 11:00"`、`"now"`、`"now 11:00"`、`"today"`、`"today 11:00"`、`"yesterday"`、`"yesterday 11:00"`、`"lastWeek"`、`"currentMonth"`、`"lastMonth"`、`"theLastWeek"`、`"theNextWeek"`、`"theLastMonth"`、`"theNextMonth"` | | `option` | `{ id, name }` | 选项 | `{ "id": "opt1", "name": "已完成" }` | | `link` | `{ text, link }` | 链接(含文字和 URL), 文字和 URL 的格式可以是 ValueInfo 中的 text/ref 类型 | `{ "text": [{ "value_type": "text", "value": "查看详情" }], "link": [{ "value_type": "text", "value": "https://example.com" }] }`、`{ "text": [{ "value_type": "text", "value": "查看详情" }], "link": [{ "value_type": "ref", "value": "$.step_1.fldXXX" }] }` | | `user` | `{ id, name }` | 用户 OpenID、名字 | `{ "id": "ou_xxxx", "name": "张三" }` | | `group` | `{ id, name }` | 群 Chat ID、名字 | `{ "id": "oc_xxx", "name": "测试群" }` | | `ref` | `string` | 引用前置节点输出的路径 | 参考 ref 引用变量详解 章节 | > ⚠️ **所有涉及用户的 value 中的 id 统一使用 OpenID(`ou_xxxx` 格式)**,由 CLI 层来完成转换 > ⚠️ **所有涉及群的 value 中的 id 统一使用 ChatID(`oc_xxxx` 格式)**,由 CLI 层来完成转换 ### ref 引用变量详解 `ref` 类型是工作流中节点间数据传递的核心机制。当 `value_type` 为 `ref` 时,`value` 指向前置节点的某个输出变量。本节详细描述每个节点可供引用的输出变量定义。 #### 引用路径格式 ``` $.{stepId} $.{stepId}.{pathId} $.{stepId}.{pathId}.{childPathId} $.{stepId}.{pathId}.{childPathId}.{grandChildPathId} ``` - `{stepId}`:前置节点的 `id`(即 WorkflowStep 中的 `id` 字段) - `{pathId}`:节点输出的路径标识符 - 支持多层下钻,如引用字段的属性:`$.step_1.fldXXX.name` --- #### 触发器节点输出 ##### 记录触发器(AddRecordTrigger / ChangeRecordTrigger / SetRecordTrigger / ReminderTrigger) 这 4 个触发器的输出结构完全一致: | pathId | 说明 | 引用示例 | |--------|------|----------| | `{fieldId}` | 字段id,从配置表的所有字段或者指定字段id生成,可下钻字段属性 | `$.{stepId}.{fieldId}` | | `{fieldId}.fieldId` | 字段id属性 | `$.{stepId}.{fieldId}.fieldId` | | `{fieldId}.fieldName` | 字段名属性 | `$.{stepId}.{fieldId}.fieldName` | | `startTime` | 触发时间戳 | `$.{stepId}.startTime` | | `recordId` | 记录 ID | `$.{stepId}.recordId` | | `recordLink` | 记录链接 | `$.{stepId}.recordLink` | | `recordCreatedUser` | 记录创建者 | `$.{stepId}.recordCreatedUser` | | `recordCreatedTime` | 记录创建时间 | `$.{stepId}.recordCreatedTime` | | `recordModifiedUser` | 最后修改者 | `$.{stepId}.recordModifiedUser` | | `recordModifiedTime` | 最后修改时间 | `$.{stepId}.recordModifiedTime` | **动态字段输出规则**: - 读取触发器所配置的数据表的所有字段 - 每个字段生成一条输出:`pathId` = fieldId - 若字段为关联字段,children 为关联表所有字段(单层下钻,不再递归) - 每个字段可下钻特定的字段属性(见「字段属性下钻」) **recordLink 的 children**:如果配置了数据表,则为该表所有视图的列表,每个视图 `{ pathId: viewId, pathName: viewName, pathType: 'string' }`。引用示例:`$.{stepId}.recordLink.{viewId}`。 ##### ButtonTrigger(按钮触发器) `ButtonTrigger` 的输出取决于 `button_type`: #### `button_type = buttonField` | pathId | 说明 | 引用示例 | |--------|------|----------| | `{fieldId}` | 字段id,从配置表的所有字段或者指定字段id生成,可下钻字段属性 | `$.{stepId}.{fieldId}` | | `{fieldId}.fieldId` | 字段id属性 | `$.{stepId}.{fieldId}.fieldId` | | `{fieldId}.fieldName` | 字段名属性 | `$.{stepId}.{fieldId}.fieldName` | | `recordId` | 记录 ID | `$.{stepId}.recordId` | | `recordLink` | 记录链接 | `$.{stepId}.recordLink` | | `recordCreatedUser` | 记录创建者 | `$.{stepId}.recordCreatedUser` | | `recordModifiedUser` | 最后修改者 | `$.{stepId}.recordModifiedUser` | | `recordModifiedTime` | 最后修改时间 | `$.{stepId}.recordModifiedTime` | | `time` | 触发时间 | `$.{stepId}.time` | | `user` | 触发人 | `$.{stepId}.user` | | `buttonName` | 触发的按钮名称 | `$.{stepId}.buttonName` | #### `button_type = buttonElement` | pathId | 说明 | 引用示例 | |--------|------|----------| | `time` | 触发时间 | `$.{stepId}.time` | | `user` | 触发人 | `$.{stepId}.user` | | `buttonName` | 触发的按钮名称 | `$.{stepId}.buttonName` | ##### TimerTrigger(定时触发器) | pathId | 说明 | 引用示例 | |--------|------|----------| | `scheduleTime` | 定时触发时间 | `$.{stepId}.scheduleTime` | ##### LarkMessageTrigger(飞书消息触发器) | pathId | 说明 | 引用示例 | |--------|------|----------| | `Sender` | 消息发送者 | `$.{stepId}.Sender` | | `AtUser` | 消息中被@的用户 | `$.{stepId}.AtUser` | | `SenderGroup` | 消息所在群(仅群聊场景) | `$.{stepId}.SenderGroup` | | `MessageSendTime` | 消息发送时间 | `$.{stepId}.MessageSendTime` | | `MessageContent` | 消息正文 | `$.{stepId}.MessageContent` | | `MessageType` | 消息类型标识 | `$.{stepId}.MessageType` | | `MessageID` | 消息唯一标识 | `$.{stepId}.MessageID` | | `MessageLink` | 消息链接(仅群聊场景) | `$.{stepId}.MessageLink` | | `ParentID` | 回复的消息 ID | `$.{stepId}.ParentID` | | `ThreadID` | 所在话题消息 ID | `$.{stepId}.ThreadID` | | `Attachments` | 消息中的附件 | `$.{stepId}.Attachments` | 条件限制: - 若场景为单聊(`receive_scene = "Chat"`),则 `SenderGroup` 和 `MessageLink` 不可用 --- #### 操作节点输出 ##### FindRecordAction(查找记录) | pathId | 说明 | 引用示例| |--------|------|-------| | `fieldRecords` | 所有找到的记录的引用(可用于 Loop 遍历) | `$.{stepId}.fieldRecords`| | `firstfieldsRecord` | 第一条匹配记录 | `$.{stepId}.firstfieldsRecord`| | `firstfieldsRecord.{fieldId}` | 首条记录的字段值,可下钻字段属性 | `$.{stepId}.firstfieldsRecord.{fieldId}`| | `firstfieldsRecord.recordId` | 记录 ID 数组 | `$.{stepId}.firstfieldsRecord.recordId`| | `fields` | 查找到的所有记录某列值 | 不支持引用| | `fields.{fieldId}` | 用户选择的字段 | `$.{stepId}.fields.{fieldId}`| | `fields.{fieldId}.fieldId` | 用户选择的字段id数组 | `$.{stepId}.fields.{fieldId}.fieldId`| | `fields.{fieldId}.fieldName` | 用户选择的字段名数组 | `$.{stepId}.fields.{fieldId}.fieldName`| | `fields.recordId` | 记录 ID 数组 | `$.{stepId}.fields.recordId`| | `recordNum` | 找到记录总数 | `$.{stepId}.recordNum`| ##### AddRecordAction(新增记录) | pathId | 说明 | 引用示例 | |--------|------|----------| | `{fieldId}` | 用户配置的字段值,可下钻字段属性 | `$.{stepId}.{fieldId}` | | `{fieldId}.fieldId` | 用户配置的字段id | `$.{stepId}.{fieldId}.fieldId` | | `{fieldId}.fieldName` | 用户配置的字段名 | `$.{stepId}.{fieldId}.fieldName` | | `recordId` | 新增的记录 ID | `$.{stepId}.recordId` | | `recordLink` | 新增的记录 URL | `$.{stepId}.recordLink` | ##### SetRecordAction(更新记录) | pathId | 说明 | 引用示例 | |--------|------|----------| | `{fieldId}` | 用户配置的字段值,可下钻字段属性 | `$.{stepId}.{fieldId}` | | `{fieldId}.fieldId` | 用户配置的字段id | `$.{stepId}.{fieldId}.fieldId` | | `{fieldId}.fieldName` | 用户配置的字段名 | `$.{stepId}.{fieldId}.fieldName` | | `recordId` | 记录 ID 数组(因可能更新多条记录) | `$.{stepId}.recordId` | ##### HTTPClientAction(HTTP 请求) HTTPClientAction 的输出取决于 `response_type`: | response_type | 是否可引用 | 输出说明 | 引用示例 | |--------------|-----------|----------|----------| | `none` | 否 | 无任何可引用输出 | 不支持引用 | | `text` | 是 | 整个响应文本作为节点整体输出 | `$.{stepId}` | | `json` | 是 | 响应体整体挂在 `body` 下,同时返回 `status_code`;仅可引用 `response_value` 中声明的字段 | `$.{stepId}.body`、`$.{stepId}.body.success`、`$.{stepId}.body.message`、`$.{stepId}.status_code` | **补充说明**: - 当 `response_type = none` 时,后续节点无法引用 HTTPClientAction 的任何输出 - 当 `response_type = text` 时,`$.{stepId}` 表示整个响应文本 - 当 `response_type = json` 时,`$.{stepId}.body` 表示整个 JSON body,`$.{stepId}.body.字段名` 表示 body 中某个字段 - 仅当 `response_type = json` 时,`$.{stepId}.status_code` 表示请求该 HTTP URL 后返回的 HTTP 状态码 - 仅当 `response_type = json` 时,`response_value` 必填 - 当 `response_type = json` 时,后续节点只能引用 `response_value` 中声明过的字段 **案例**: 假设某个 `HTTPClientAction` 的配置如下: ```json { "id": "step_http_1", "type": "HTTPClientAction", "data": { "response_type": "json", "response_value": "{\"success\":true,\"message\":\"ok\"}" } } ``` 则后续节点仅可以引用: - `$.step_http_1.body` - `$.step_http_1.body.success` - `$.step_http_1.body.message` - `$.step_http_1.status_code` 但**不能**引用未在 `response_value` 中声明的字段,例如: - `$.step_http_1.body.data` - `$.step_http_1.body.request_id` ##### GenerateAiTextAction(AI 生成文本) | pathId | 说明 | 引用示例 | |--------|------|----------| | (整体出参) | AI 生成的文本内容(不支持下钻,只能引用 `$.{stepId}`) | `$.{stepId}` | ##### 无输出的操作节点 以下节点不产生任何可引用的输出数据: - **Delay**(延时等待) - **LarkMessageAction**(发送飞书消息) --- #### 分支节点输出 以下分支节点均不产生任何可引用的输出数据: - **IfElseBranch**(条件分支) - **SwitchBranch**(多条件分支) --- #### 系统节点输出 ##### Loop(循环) | pathId | 说明 | 引用示例 | |--------|------|----------| | `item` | 当前循环元素 | `$.{stepId}.item` | | `index` | 从 0 开始的循环索引 | `$.{stepId}.index` | **`item` 的类型推断规则**(由循环数据源决定): **场景一:遍历组合记录** — 数据源为 `record` 类型时(如 FindRecordAction 的 `fieldRecords`),`item` 类型为 `record`,可向下选择具体字段: | 说明 | 引用示例 | |------|----------| | 当前遍历的记录(record) | `$.{loopStepId}.item` | | 记录的具体字段 | `$.{loopStepId}.item.{fieldId}` | | 从 0 开始的索引(number) | `$.{loopStepId}.index` | **场景二:遍历字段** — 数据源为某个多值类型字段时,比如附件字段、人员字段,`item` 继承该字段的类型并可继续下钻字段属性: | 说明 | 引用示例 | |------|----------| | 当前遍历的元素(类型继承数据源字段类型,例如人员字段) | `$.{loopStepId}.item` | | 用户姓名 | `$.{loopStepId}.item.name` | | 从 0 开始的索引(number) | `$.{loopStepId}.index` | --- #### 字段属性下钻 每个字段变量都可以进一步下钻选择字段的属性。所有字段至少支持 `fieldId` 和 `fieldName` 两个基础属性,部分字段还支持额外属性: | 字段类型 | 属性名称 | 属性 pathId | 属性 pathType | 说明 | |----------|---------|-------------|--------------|------| | **所有字段(基础)** | 字段 ID | `fieldId` | `string` | 字段的唯一标识 | | | 字段名称 | `fieldName` | `string` | 字段的显示名称 | | **人员字段**(`user` / `created_by` / `updated_by`) | 姓名 | `name` | `string` | 用户姓名 | | **日期字段**(`datetime` / `created_at` / `updated_at`) | 时间戳 | `timestamp` | `number` | 时间戳数值 | | **附件字段**(`attachment`) | 文件名 | `fileName` | `string` | 附件文件名 | | | 文件类型 | `fileType` | `string` | MIME 类型 | | | 文件大小 | `size` | `number` | 文件字节数 | | | 文件 Token | `fileToken` | `string` | 附件 token | | **超链接文本字段**(`text` 且 `style.type=url`) | 文本 | `text` | `string` | 链接文本部分 | | | 链接 | `link` | `string` | 链接 URL 部分 | | **自动编号字段**(`auto_number`) | 序号 | `sequence` | `number` | 编号的纯数字序号 | | **关联字段**(`link`) | 字段下钻 | `{fieldId}` | - | 可下钻到关联表的字段 | > 其他字段类型(如 `text`、`number`、`checkbox`、`select`、`location`、`formula`、`lookup` 等)仅支持 `fieldId` 和 `fieldName` 两个基础属性。 下钻引用示例: ``` $.{stepId}.{fieldId} → 字段值本身 $.{stepId}.{fieldId}.fieldId → 字段 ID(string) $.{stepId}.{fieldId}.fieldName → 字段名称(string) $.{stepId}.{fieldId}.name → 人员姓名列表(array,仅人员字段) $.{stepId}.{fieldId}.unionId → 人员 unionId 列表(array,仅人员字段) $.{stepId}.{fieldId}.timestamp → 时间戳(array,仅日期字段) $.{stepId}.{fieldId}.fileName → 文件名列表(array,仅附件字段) $.{stepId}.{fieldId}.fileToken → 文件 Token 列表(array,仅附件字段) ``` --- #### 节点输出能力总览 | 节点 | 类型 | 有输出 | 输出特性 | |------|------|--------|---------| | AddRecordTrigger | 触发器 | ✅ | 动态(表字段 + 记录属性) | | ChangeRecordTrigger | 触发器 | ✅ | 动态(表字段 + 记录属性) | | SetRecordTrigger | 触发器 | ✅ | 动态(表字段 + 记录属性) | | ReminderTrigger | 触发器 | ✅ | 动态(表字段 + 记录属性) | | ButtonTrigger | 触发器 | ✅ | 动态(表字段 + 记录属性;buttonElement 仅基础触发属性) | | TimerTrigger | 触发器 | ✅ | 静态(仅 scheduleTime) | | LarkMessageTrigger | 触发器 | ✅ | 静态(消息属性列表) | | FindRecordAction | 动作 | ✅ | 动态(用户选择的字段) | | AddRecordAction | 动作 | ✅ | 动态(用户配置的字段) | | SetRecordAction | 动作 | ✅ | 动态(用户配置的字段) | | HTTPClientAction | 动作 | ✅ | 动态(取决于用户配置的 HTTP 响应输出) | | GenerateAiTextAction | 动作 | ✅ | 静态(单 string) | | Delay | 动作 | ❌ | 无输出 | | LarkMessageAction | 动作 | ❌ | 无输出 | | IfElseBranch | 分支 | ❌ | 无输出 | | SwitchBranch | 分支 | ❌ | 无输出 | | Loop | 系统 | ✅ | 动态(取决于数据源) | --- ### TextRefItem 文本与引用混排,用于消息内容等动态拼接场景: ```json [ { "value_type": "text", "value": "客户 " }, { "value_type": "ref", "value": "$.step_1.fieldxxx" }, { "value_type": "text", "value": " 创建了新订单" } ] ``` ### RecordFieldValue ```json { "field_name": "客户名称", "value": [{ "value_type": "text", "value": "张三" }] } ``` ### AndCondition(Trigger 过滤条件) ```json { "conjunction": "and", "conditions": [ { "field_name": "状态", "operator": "is", "value": [{ "value_type": "text", "value": "进行中" }] } ] } ``` ### OrGroup(Branch 分支条件) ```json { "conjunction": "or", "conditions": [ { "conjunction": "and", "conditions": [ { "left_value": { "value_type": "ref", "value": "$.step_1.fieldxxx" }, "operator": "isGreater", "right_value": [{ "value_type": "number", "value": 1000 }] } ] } ] } ``` **operator 可选值:** `is` / `isNot` / `containsAny` / `doesNotContainAny` / /`containsAll`/ `isEmpty` / `isNotEmpty` / `isGreater` / `isGreaterEqual` / `isLess` / `isLessEqual` ### RecordFilterInfo ** 由于 conjunction 只支持 and,若需要实现 字段X 等于 A 或 B,你可以使用 containsAny ```json { "conjunction": "and", "conditions": [ { "field_name": "状态", "operator": "is", "value": [{ "value_type": "text", "value": "进行中" }] } ] } ``` ### `select` 字段多值匹配 | 操作 | operator | 正确写法 | |------|---------|---------| | 等于单个值 | `is` | `[{"value_type": "option", "value": {"name": "L2"}}]` | | 匹配多个值(L2 或 L3) | `containsAny` | `[{"value_type": "option", "value": {"name": "L2"}}, {"value_type": "option", "value": {"name": "L3"}}]` | > ⚠️ 不要用多个 `is` 条件(会被当作 OR,无法实现 AND)。推荐使用 `containsAny` 操作符匹配多个值。 > ⚠️ **Select 字段条件**:`value_type` 必须为 `option`,`value` 对象可只传 `name`(如 `{"name": "L2"}`),无需提供选项 ID。 ### RefInfo ```json { "step_id": "step_trigger" } ``` --- ## 完整示例:条件分支 + 发送消息 ```json { "title": "新订单自动通知", "steps": [ { "id": "step_1", "type": "AddRecordTrigger", "title": "当「订单表」新增记录时触发", "next": "step_2", "data": { "table_name": "订单表", "watched_field_name": "订单编号" } }, { "id": "step_2", "type": "IfElseBranch", "title": "判断订单金额是否大于 1000", "children": { "links": [ { "kind": "if_true", "to": "step_3" }, { "kind": "if_false", "to": "step_4" } ] }, "next": "step_5", "data": { "condition": { "conjunction": "or", "conditions": [{ "conjunction": "and", "conditions": [{ "left_value": { "value_type": "ref", "value": "$.step_1.fieldxxx" }, "operator": "isGreater", "right_value": [{ "value_type": "number", "value": 1000 }] }] }] } } }, { "id": "step_3", "type": "LarkMessageAction", "title": "通知主管审批大额订单", "next": null, "data": { "receiver": [{ "value_type": "ref", "value": "$.step_1.fieldxxx" }], "send_to_everyone": false, "title": [{ "value_type": "text", "value": "大额订单提醒" }], "content": [ { "value_type": "text", "value": "新订单金额为:" }, { "value_type": "ref", "value": "$.step_1.fieldxxx" }, { "value_type": "text", "value": "元,请及时审批。" } ], "btn_list": [] } }, { "id": "step_4", "type": "SetRecordAction", "title": "自动标记小额订单为已通过", "next": null, "data": { "table_name": "订单表", "ref_info": { "step_id": "step_1" }, "field_values": [ { "field_name": "审批状态", "value": [{ "value_type": "text", "value": "已通过" }] } ] } }, { "id": "step_5", "type": "GenerateAiTextAction", "title": "AI 生成订单处理日报", "next": null, "data": { "prompt": [ { "value_type": "text", "value": "请根据以下订单信息生成一份简要的处理日报:" }, { "value_type": "ref", "value": "$.step_1.fieldxxx" } ] } } ] } ``` --- ## 参考 - [lark-base-workflow-guide.md](lark-base-workflow-guide.md) — 完整示例和构造技巧 - 创建/更新时外层只承载 workflow 元信息,核心校验对象是 `steps`;列表只用于拿 workflow ID 和启停状态