PicoBot/skills/lark-base/references/lark-base-workflow-schema.md
ooodc a7883dbed9 refactor(todo): 重构待办事项管理逻辑及更新状态规则
- 移除 TodoItem 中的 priority、created_at 和 updated_at 字段
- 强制每个任务都必须有唯一 id,且由用户负责生成
- 修改合并模式逻辑,merge=true 下保留未提及的旧任务
- 支持已完成和已取消任务重新激活(状态改回 pending 或 in_progress)
- 禁止 in_progress 状态退回到 pending,必须标记为 completed 或 cancelled
- 优化状态转换校验,允许特定状态间合法切换
- 简化任务变更消息,移除详细的新增/更新/移除统计
- 更新文档和示例,明确 id 必须由用户生成和使用
- 修复和补充测试,增强状态转换和合并模式验证
- 调整任务时间戳生成逻辑,统一使用当前时间及索引
- 该变更提供更合理的任务状态机械及管理模式,提升稳定性和易用性
2026-06-13 09:22:33 +08:00

1072 lines
38 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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` | 否 | stringJSON 字符串形式的响应结果示例;仅当 `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` |
##### HTTPClientActionHTTP 请求)
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`
##### GenerateAiTextActionAI 生成文本)
| 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 → 字段 IDstring
$.{stepId}.{fieldId}.fieldName → 字段名称string
$.{stepId}.{fieldId}.name → 人员姓名列表array<string>,仅人员字段)
$.{stepId}.{fieldId}.unionId → 人员 unionId 列表array<string>,仅人员字段)
$.{stepId}.{fieldId}.timestamp → 时间戳array<number>,仅日期字段)
$.{stepId}.{fieldId}.fileName → 文件名列表array<string>,仅附件字段)
$.{stepId}.{fieldId}.fileToken → 文件 Token 列表array<string>,仅附件字段)
```
---
#### 节点输出能力总览
| 节点 | 类型 | 有输出 | 输出特性 |
|------|------|--------|---------|
| 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": "张三" }] }
```
### AndConditionTrigger 过滤条件)
```json
{
"conjunction": "and",
"conditions": [
{ "field_name": "状态", "operator": "is", "value": [{ "value_type": "text", "value": "进行中" }] }
]
}
```
### OrGroupBranch 分支条件)
```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 和启停状态