auto backup 2026-05-15 10:57:05

This commit is contained in:
xiaobian-bot 2026-05-15 10:57:05 +08:00
parent 69eaef2474
commit a281f1357b
2989 changed files with 609056 additions and 1348 deletions

View File

@ -0,0 +1,52 @@
---
name: lark-approval
version: 1.0.0
description: "飞书审批 API审批实例、审批任务管理。"
metadata:
requires:
bins: ["lark-cli"]
cliHelp: "lark-cli approval --help"
---
# approval (v4)
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md),其中包含认证、权限处理**
## API Resources
```bash
lark-cli schema approval.<resource>.<method> # 调用 API 前必须先查看参数结构
lark-cli approval <resource> <method> [flags] # 调用 API
```
> **重要**:使用原生 API 时,必须先运行 `schema` 查看 `--data` / `--params` 参数结构,不要猜测字段格式。
### instances
- `get` — 获取单个审批实例详情
- `cancel` — 撤回审批实例
- `cc` — 抄送审批实例
- `initiated` — 查询用户的已发起列表
### tasks
- `remind` — 催办审批人
- `approve` — 同意审批任务
- `reject` — 拒绝审批任务
- `transfer` — 转交审批任务
- `query` — 查询用户的任务列表
## 权限表
| 方法 | 所需 scope |
|------|-----------|
| `instances.get` | `approval:instance:read` |
| `instances.cancel` | `approval:instance:write` |
| `instances.cc` | `approval:instance:write` |
| `instances.initiated` | `approval:instance:read` |
| `tasks.remind` | `approval:instance:write` |
| `tasks.approve` | `approval:task:write` |
| `tasks.reject` | `approval:task:write` |
| `tasks.transfer` | `approval:task:write` |
| `tasks.query` | `approval:task:read` |

View File

@ -0,0 +1,57 @@
---
name: lark-attendance
version: 1.0.0
description: "飞书考勤打卡:查询自己的考勤打卡记录"
metadata:
requires:
bins: ["lark-cli"]
cliHelp: "lark-cli attendance --help"
---
# attendance (v1)
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md),其中包含认证、权限处理**
## 默认参数自动填充规则
调用任何 API 时,以下参数 **必须自动填充,禁止向用户询问**
| 参数 | 固定值 | 说明 |
|------|--------|------------------------------------|
| `employee_type` | `"employee_no"` | `employee_type`始终等于`"employee_no"` |
| `user_ids` | `[]`(空数组) | `user_ids`始终等于`[]` |
### 填充示例
当构建 `--params` 参数时,自动注入上述字段:
- `employee_type` 保持 `"employee_no"` 不变
当构建 `--data` 参数时,自动注入上述字段:
```json
{
"user_ids": [],
...用户提供的参数
}
```
> **注意**`user_ids` 数组保持为空[]`employee_type` 保持 `"employee_no"` 不变。
## API Resources
```bash
lark-cli schema attendance.<resource>.<method> # 调用 API 前必须先查看参数结构
lark-cli attendance <resource> <method> [flags] # 调用 API
```
> **重要**:使用原生 API 时,必须先运行 `schema` 查看 `--data` / `--params` 参数结构,不要猜测字段格式。
### user_tasks
- `query` — 查询用户考勤打卡记录
## 权限表
| 方法 | 所需 scope |
|------|-----------|
| `user_tasks.query` | `attendance:task:readonly` |

View File

@ -0,0 +1,336 @@
---
name: lark-base
version: 1.2.0
description: "当需要用 lark-cli 操作飞书多维表格Base时调用搜索 Base、建表、字段管理、记录读写、记录分享链接、视图配置、历史查询以及角色/表单/仪表盘管理/工作流;也适用于把旧的 +table / +field / +record 写法改成当前命令写法。涉及字段设计、公式字段、查找引用、跨表计算、行级派生指标、数据分析需求时也必须使用本 skill。"
metadata:
requires:
bins: ["lark-cli"]
cliHelp: "lark-cli base --help"
---
# base
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md)。
> **执行前必做:** 执行任何 `base` 命令前,必须先阅读对应命令的 reference 文档,再调用命令。
> **命名约定:** Base 业务命令仅使用 `lark-cli base +...` 形式;如需先解析 Wiki 链接,可先调用 `lark-cli wiki ...`
> **分流规则:** 如果用户要“把本地文件导入成 Base / 多维表格 / bitable”第一步不是 `base`,而是 `lark-cli drive +import --type bitable`;导入完成后再回到 `lark-cli base +...` 做表内操作。
## 1. 何时使用本 Skill
### 1.1 触发条件
以下场景应使用本 skill
- 用户明确要操作飞书多维表格 / Base。
- 用户要建表、改表、查表、删表,或管理字段、记录、视图。
- 用户要做公式字段、lookup 字段、派生指标、跨表计算。
- 用户要做临时统计、聚合分析、比较排序、求最值。
- 用户要管理 workflow、dashboard、表单、角色权限。
- 用户给出 `/base/{token}` 链接。
- 用户给出 `/wiki/{token}` 链接,且最终解析为 `bitable`
- 用户要把旧的 Base 聚合式写法改成当前原子命令写法,例如把旧 `+table / +field / +record / +view / +history / +workspace` 改写成当前命令。
以下场景不应使用本 skill
- 用户只是做认证、初始化配置、切换 `--as user/bot`、处理 scope。此时先读 `../lark-shared/SKILL.md`
- 用户只是泛化地讨论“数据分析 / 字段设计”,但并不在 Base 场景中。不要因为提到“统计 / 公式 / lookup”就误触发。
### 1.2 前置约束
1. 先阅读 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md)。
2. Base 业务命令仅使用 `lark-cli base +...` 形式的 shortcut 命令;如果输入是 Wiki 链接,可先调用 `lark-cli wiki spaces get_node` 解析真实 token。
3. 定位到命令后,先读该命令对应的 reference再执行命令。
4. 如果用户要把本地 Excel / CSV / `.base` 快照导入成 Base / 多维表格 / bitable第一步不是 `base`,而是 `lark-cli drive +import --type bitable`;导入完成后再回到 `lark-cli base +...` 做表内操作。
5. 不要在 Base 场景改走 `lark-cli api /open-apis/bitable/v1/...`
6. 如果用户只给 Base 名称、关键词,或说“帮我找一个多维表格”,先通过 `lark-cli docs +search --query <keyword> --filter '{"doc_types":["BITABLE"]}'` 搜索 `BITABLE` 资源;拿到 Base URL 后再使用本 skill 的 `base +...` 命令。复杂搜索再读 [`../lark-doc/references/lark-doc-search.md`](../lark-doc/references/lark-doc-search.md):标题精确匹配、限定创建者/群/文件夹/时间范围、只搜标题/评论、分页/全量搜索。
## 2. 模块与命令导航
本章按“先选模块,再选命令”的方式组织。先判断用户目标属于哪个大模块,再进入对应子模块,按要求阅读 reference 后执行命令。
### 2.1 模块地图
| 大模块 | 处理什么问题 | 包含的小模块 / 能力 |
|------|-------------|-------------------|
| Base 模块 | 管理 Base 本体,或从链接进入 Base 场景 | `base-create / base-get / base-copy`Base / Wiki 链接解析 |
| 表与数据模块 | 管理 Base 内部结构与日常数据操作 | `table / field / record / view` |
| 公式 / Lookup 模块 | 处理派生字段、条件判断、跨表计算、固定查找引用 | `formula / lookup` 字段创建与更新 |
| 数据分析模块 | 做一次性筛选、分组、聚合分析 | `data-query` |
| Workflow 模块 | 管理自动化流程 | `workflow-list / get / create / update / enable / disable` |
| Dashboard 模块 | 管理仪表盘和图表组件 | `dashboard-* / dashboard-block-*` |
| 表单模块 | 管理表单和表单题目 | `form-* / form-questions-*` |
| 权限与角色模块 | 管理高级权限和自定义角色 | `advperm-* / role-*` |
### 2.2 Base 模块
用于管理 Base 本体,或从用户给出的链接进入后续 Base 操作。
模块索引:[`references/lark-base-workspace.md`](references/lark-base-workspace.md)
| 命令 | 用途 / 何时使用 | 必读 reference | 路由提醒 |
|------|------------------|----------------|----------|
| `lark-cli docs +search --query <keyword> --filter '{"doc_types":["BITABLE"]}'` | 按名称、关键词查找 Base / 多维表格 / bitable | 复杂搜索再读 [`../lark-doc/references/lark-doc-search.md`](../lark-doc/references/lark-doc-search.md) | 先定位资源,再回到 `base +...` 操作表内数据 |
| `+base-create` | 创建新的 Base | [`lark-base-base-create.md`](references/lark-base-base-create.md)、[`lark-base-workspace.md`](references/lark-base-workspace.md) | 写入操作;执行前先读 reference`--folder-token`、`--time-zone` 都是可选项 |
| `+base-get` | 获取 Base 信息 | [`lark-base-base-get.md`](references/lark-base-base-get.md)、[`lark-base-workspace.md`](references/lark-base-workspace.md) | 适合确认 Base 本体信息,不替代表/字段结构读取 |
| `+base-copy` | 复制已有 Base | [`lark-base-base-copy.md`](references/lark-base-base-copy.md)、[`lark-base-workspace.md`](references/lark-base-workspace.md) | 写入操作;执行前先读 reference复制成功后应主动返回新 Base 标识信息 |
### 2.3 表与数据模块
这是最常用的大模块,包含 `table / field / record / view` 四类子模块。
补充示例:[`references/examples.md`](references/examples.md),适合需要串联 table / record / view 完整操作链路时再读。
#### 2.3.1 Table 子模块
子模块索引:[`references/lark-base-table.md`](references/lark-base-table.md)
| 命令 | 用途 / 何时使用 | 必读 reference | 路由提醒 |
|------|------------------|----------------|----------|
| `+table-list / +table-get` | 列出数据表,或获取单个表详情 | [`lark-base-table-list.md`](references/lark-base-table-list.md)、[`lark-base-table-get.md`](references/lark-base-table-get.md) | `+table-list` 只能串行执行;`+table-get` 适合删除/修改前确认目标 |
| `+table-create / +table-update / +table-delete` | 创建、更新或删除数据表 | [`lark-base-table-create.md`](references/lark-base-table-create.md)、[`lark-base-table-update.md`](references/lark-base-table-update.md)、[`lark-base-table-delete.md`](references/lark-base-table-delete.md) | 创建适合一次性建表;更新前先确认目标表;删除时用户已明确目标可直接执行并带 `--yes` |
#### 2.3.2 Field 子模块
普通字段管理走这里;如果字段类型是 `formula``lookup`,转到下方“公式 / Lookup 模块”。
子模块索引:[`references/lark-base-field.md`](references/lark-base-field.md)
| 命令 | 用途 / 何时使用 | 必读 reference | 路由提醒 |
|------|------------------|----------------|----------|
| `+field-list / +field-get` | 列出字段结构,或获取单个字段详情 | [`lark-base-field-list.md`](references/lark-base-field-list.md)、[`lark-base-field-get.md`](references/lark-base-field-get.md) | 写记录、写字段、做分析前常先读 `+field-list``+field-list` 只能串行执行;`+field-get` 适合删除/更新前确认目标 |
| `+field-create / +field-update / +field-delete` | 创建、更新或删除普通字段 | [`lark-base-field-create.md`](references/lark-base-field-create.md)、[`lark-base-field-update.md`](references/lark-base-field-update.md)、[`lark-base-field-delete.md`](references/lark-base-field-delete.md)、[`lark-base-shortcut-field-properties.md`](references/lark-base-shortcut-field-properties.md) | 写字段前先看字段属性规范;如果涉及类型转换,直接按 `+field-update` 中的字段类型变更规则执行,只在安全白名单内考虑原地转换;如果类型是 `formula / lookup`,先转去读对应 guide删除时用户已明确目标可直接执行并带 `--yes` |
| `+field-search-options` | 查询字段可选项 | [`lark-base-field-search-options.md`](references/lark-base-field-search-options.md) | 适合单选/多选等选项型字段 |
#### 2.3.3 Record 子模块
子模块索引:[`references/lark-base-record.md`](references/lark-base-record.md)、[`references/lark-base-history.md`](references/lark-base-history.md)
| 命令 | 用途 / 何时使用 | 必读 reference | 路由提醒 |
|------|------------------|----------------|----------|
| `+record-search / +record-list / +record-get` | 按关键词检索记录、读取记录明细 / 分页导出,或按 ID 获取一条或多条记录 | [`lark-base-record-read-sop.md`](references/lark-base-record-read-sop.md) | 记录读取统一先读 read SOP guide已知 `record_id``+record-get`;明确关键词用 `+record-search`;普通明细用 `+record-list`;明确筛选 / 排序 / Top N 用临时视图投影后 `+record-list --view-id`;统计聚合才分流到 `+data-query``+record-get` 支持重复 `--record-id``--json` 读取多条记录 |
| `+record-upsert / +record-batch-create / +record-batch-update` | 创建、更新或批量写入记录 | [`lark-base-record-upsert.md`](references/lark-base-record-upsert.md)、[`lark-base-record-batch-create.md`](references/lark-base-record-batch-create.md)、[`lark-base-record-batch-update.md`](references/lark-base-record-batch-update.md)、[`lark-base-cell-value.md`](references/lark-base-cell-value.md) | 写前先 `+field-list`;只写存储字段;`+record-batch-update` 为同值更新(同一 patch 应用到多条记录);批量单次不超过 `200` 条;附件不要走这里 |
| `+record-upload-attachment` | 给已有记录上传附件 | [`lark-base-record-upload-attachment.md`](references/lark-base-record-upload-attachment.md) | 附件上传专用链路,不要用 `+record-upsert` / `+record-batch-*` 伪造附件值 |
| `lark-cli docs +media-download` | 下载 Base 附件文件到本地 | [`../lark-doc/references/lark-doc-media-download.md`](../lark-doc/references/lark-doc-media-download.md) | Base 附件的 `file_token``+record-get` 返回的附件字段数组里取;**不要用 `lark-cli drive +download`**(对 Base 附件返回 403 |
| `+record-delete` | 删除一条或多条记录 | [`lark-base-record-delete.md`](references/lark-base-record-delete.md) | 删除多条时重复传 `--record-id` 指定多个记录;用户已明确目标可直接执行并带 `--yes` |
| `+record-history-list` | 查询指定记录的变更历史 | [`lark-base-record-history-list.md`](references/lark-base-record-history-list.md) | 按 `table-id + record-id` 查询,不支持整表扫描;`+record-history-list` 只能串行执行 |
| `+record-share-link-create` | 为一条或多条记录生成分享链接 | [`lark-base-record-share-link-create.md`](references/lark-base-record-share-link-create.md) | 单次最多 100 条;重复 record_id 会自动去重;适合分享单条记录或批量分享场景 |
#### 2.3.4 View 子模块
子模块索引:[`references/lark-base-view.md`](references/lark-base-view.md)
| 命令 | 用途 / 何时使用 | 必读 reference | 路由提醒 |
|------|------------------|----------------|----------|
| `+view-list / +view-get` | 列出视图,或获取视图详情 | [`lark-base-view-list.md`](references/lark-base-view-list.md)、[`lark-base-view-get.md`](references/lark-base-view-get.md) | `+view-list` 只能串行执行;`+view-get` 适合查看已有视图配置 |
| `+view-create / +view-delete / +view-rename` | 创建、删除或重命名视图 | [`lark-base-view-create.md`](references/lark-base-view-create.md)、[`lark-base-view-delete.md`](references/lark-base-view-delete.md)、[`lark-base-view-rename.md`](references/lark-base-view-rename.md) | 创建前先确认表和视图类型;删除前先确认目标;用户已明确新名字时可直接重命名 |
| `+view-get-filter / +view-set-filter` | 读取或配置筛选条件 | [`lark-base-view-get-filter.md`](references/lark-base-view-get-filter.md)、[`lark-base-view-set-filter.md`](references/lark-base-view-set-filter.md)、[`lark-base-record-read-sop.md`](references/lark-base-record-read-sop.md) | 常与 `+record-list` 组合,用于按视图筛选读取 |
| `+view-get-sort / +view-set-sort` | 读取或配置排序 | [`lark-base-view-get-sort.md`](references/lark-base-view-get-sort.md)、[`lark-base-view-set-sort.md`](references/lark-base-view-set-sort.md) | 字段名必须来自真实结构 |
| `+view-get-group / +view-set-group` | 读取或配置分组 | [`lark-base-view-get-group.md`](references/lark-base-view-get-group.md)、[`lark-base-view-set-group.md`](references/lark-base-view-set-group.md) | 字段名必须来自真实结构 |
| `+view-get-visible-fields / +view-set-visible-fields` | 读取或配置视图可见字段 | [`lark-base-view-get-visible-fields.md`](references/lark-base-view-get-visible-fields.md)、[`lark-base-view-set-visible-fields.md`](references/lark-base-view-set-visible-fields.md) | 用于控制视图中的字段顺序与可见性;字段名必须来自真实结构 |
| `+view-get-card / +view-set-card` | 读取或配置卡片视图 | [`lark-base-view-get-card.md`](references/lark-base-view-get-card.md)、[`lark-base-view-set-card.md`](references/lark-base-view-set-card.md) | 适合卡片展示场景 |
| `+view-get-timebar / +view-set-timebar` | 读取或配置时间轴视图 | [`lark-base-view-get-timebar.md`](references/lark-base-view-get-timebar.md)、[`lark-base-view-set-timebar.md`](references/lark-base-view-set-timebar.md) | 适合时间线展示场景 |
### 2.4 公式 / Lookup 模块
只要用户诉求涉及派生指标、条件判断、文本处理、日期差、跨表计算、跨表筛选后取值,都要先判断是否进入本模块。
默认优先考虑 `formula`:适合常规计算、条件判断、文本处理、日期差、跨表聚合,以及需要长期显示在表里的派生结果。
只有当用户明确要求 `lookup`,或场景天然符合 `from / select / where / aggregate` 这种固定查找建模时,再使用 `lookup`
| 命令 | 用途 / 何时使用 | 必读 reference | 路由提醒 |
|------|------------------|----------------|----------|
| `+field-create``type=formula` | 创建公式字段 | [`formula-field-guide.md`](references/formula-field-guide.md)、[`lark-base-field-create.md`](references/lark-base-field-create.md)、[`lark-base-shortcut-field-properties.md`](references/lark-base-shortcut-field-properties.md) | 没读 guide 前不要直接创建 |
| `+field-update``type=formula` | 更新公式字段 | [`formula-field-guide.md`](references/formula-field-guide.md)、[`lark-base-field-update.md`](references/lark-base-field-update.md)、[`lark-base-shortcut-field-properties.md`](references/lark-base-shortcut-field-properties.md) | 先拿当前表结构 |
| `+field-create``type=lookup` | 创建 lookup 字段 | [`lookup-field-guide.md`](references/lookup-field-guide.md)、[`lark-base-field-create.md`](references/lark-base-field-create.md)、[`lark-base-shortcut-field-properties.md`](references/lark-base-shortcut-field-properties.md) | 没读 guide 前不要直接创建 |
| `+field-update``type=lookup` | 更新 lookup 字段 | [`lookup-field-guide.md`](references/lookup-field-guide.md)、[`lark-base-field-update.md`](references/lark-base-field-update.md)、[`lark-base-shortcut-field-properties.md`](references/lark-base-shortcut-field-properties.md) | 跨表时还要拿目标表结构 |
### 2.5 数据分析模块
用于一次性分析和临时聚合查询。用户要的是“这次算出来的结果”,而不是把结果沉淀成字段时,优先进入本模块。
进入本模块前先确认几件事:
- `+data-query` 只做聚合查询(分组、过滤、排序、聚合计算),不用于列出原始记录或逐条明细。
- 调用者必须是目标多维表格的管理员,拥有目标多维表格的 FAFull Access / 完全访问权限),否则会返回权限错误。
- `+data-query` 只支持白名单字段类型;`formula`、`lookup`、附件、系统字段、关联等字段不能用于 `dimensions / measures / filters / sort`
| 命令 | 用途 / 何时使用 | 必读 reference | 路由提醒 |
|------|------------------|----------------|----------|
| `+data-query` | 做分组统计、SUM / AVG / COUNT / MAX / MIN、条件筛选后的聚合分析 | [`lark-base-data-query.md`](references/lark-base-data-query.md) | 字段名必须精确匹配真实字段名;不要用 `+record-list` / `+record-search` 拉全量再手算;`+data-query` 不返回原始记录;使用前先确认权限和字段类型是否受支持 |
### 2.6 Workflow 模块
这是高约束模块。执行任何 workflow 命令前,都必须先读对应命令文档和 schema。
模块索引:[`references/lark-base-workflow.md`](references/lark-base-workflow.md)
| 命令 | 用途 / 何时使用 | 必读 reference | 路由提醒 |
|------|------------------|----------------|----------|
| `+workflow-list / +workflow-get` | 列出 workflow或获取完整 workflow 结构 | [`lark-base-workflow-list.md`](references/lark-base-workflow-list.md)、[`lark-base-workflow-get.md`](references/lark-base-workflow-get.md)、[`lark-base-workflow-schema.md`](references/lark-base-workflow-schema.md) | `+workflow-list` 只返回摘要且只能串行执行;需要完整结构时用 `+workflow-get` |
| `+workflow-create / +workflow-update` | 创建或更新 workflow | [`lark-base-workflow-create.md`](references/lark-base-workflow-create.md)、[`lark-base-workflow-update.md`](references/lark-base-workflow-update.md)、[`lark-base-workflow-schema.md`](references/lark-base-workflow-schema.md) | 先读 schema禁止凭自然语言猜 `type`;先确认真实表名和字段名 |
| `+workflow-enable / +workflow-disable` | 启用或停用 workflow | [`lark-base-workflow-enable.md`](references/lark-base-workflow-enable.md)、[`lark-base-workflow-disable.md`](references/lark-base-workflow-disable.md)、[`lark-base-workflow-schema.md`](references/lark-base-workflow-schema.md) | 启用或停用前先确认目标 workflow`workflow_id` 与 `table_id` 需按前缀区分 |
### 2.7 Dashboard 模块
当用户提到“仪表盘、dashboard、数据看板、图表、可视化、block、组件、添加组件、创建图表”等关键词时进入本模块并先阅读 [`lark-base-dashboard.md`](references/lark-base-dashboard.md)。
| 命令 | 用途 / 何时使用 | 必读 reference | 路由提醒 |
|------|------------------|----------------|----------|
| `+dashboard-list / +dashboard-get` | 列出仪表盘,或获取仪表盘详情 | [`lark-base-dashboard-list.md`](references/lark-base-dashboard-list.md)、[`lark-base-dashboard-get.md`](references/lark-base-dashboard-get.md)、[`lark-base-dashboard.md`](references/lark-base-dashboard.md) | 进入仪表盘语义后先读 guide`+dashboard-list` 只能串行执行 |
| `+dashboard-create / +dashboard-update / +dashboard-delete` | 创建、更新或删除仪表盘 | [`lark-base-dashboard-create.md`](references/lark-base-dashboard-create.md)、[`lark-base-dashboard-update.md`](references/lark-base-dashboard-update.md)、[`lark-base-dashboard-delete.md`](references/lark-base-dashboard-delete.md)、[`lark-base-dashboard.md`](references/lark-base-dashboard.md) | 创建前先明确看板目标和展示场景;更新前先读取当前配置;删除前先确认目标 |
| `+dashboard-block-list / +dashboard-block-get` | 列出图表组件,或获取单个 block 详情 | [`lark-base-dashboard-block-list.md`](references/lark-base-dashboard-block-list.md)、[`lark-base-dashboard-block-get.md`](references/lark-base-dashboard-block-get.md)、[`lark-base-dashboard.md`](references/lark-base-dashboard.md)、[`dashboard-block-data-config.md`](references/dashboard-block-data-config.md) | `+dashboard-block-list` 只能串行执行;查看配置细节时读 block config 文档 |
| `+dashboard-block-create / +dashboard-block-update / +dashboard-block-delete` | 创建、更新或删除图表组件 | [`lark-base-dashboard-block-create.md`](references/lark-base-dashboard-block-create.md)、[`lark-base-dashboard-block-update.md`](references/lark-base-dashboard-block-update.md)、[`lark-base-dashboard-block-delete.md`](references/lark-base-dashboard-block-delete.md)、[`lark-base-dashboard.md`](references/lark-base-dashboard.md)、[`dashboard-block-data-config.md`](references/dashboard-block-data-config.md) | 涉及 `data_config`、图表类型、filter 时要读 block config 文档;删除前先确认目标 |
### 2.8 表单模块
用于管理表单本体和表单题目。
模块索引:[`references/lark-base-form.md`](references/lark-base-form.md)、[`references/lark-base-form-questions.md`](references/lark-base-form-questions.md)
表单问题相关操作依赖 `form-id`;具体获取方式见 `form-list``form-create` 的 reference。
| 命令 | 用途 / 何时使用 | 必读 reference | 路由提醒 |
|------|------------------|----------------|----------|
| `+form-list / +form-get` | 列出表单,或获取单个表单 | [`lark-base-form-list.md`](references/lark-base-form-list.md)、[`lark-base-form-get.md`](references/lark-base-form-get.md) | `+form-list` 可用来获取 `form-id``+form-get` 适合查看已有表单配置 |
| `+form-create / +form-update / +form-delete` | 创建、更新或删除表单 | [`lark-base-form-create.md`](references/lark-base-form-create.md)、[`lark-base-form-update.md`](references/lark-base-form-update.md)、[`lark-base-form-delete.md`](references/lark-base-form-delete.md) | 创建后可继续进入表单问题相关操作;更新或删除前先确认目标表单 |
| `+form-questions-list` | 列出表单题目 | [`lark-base-form-questions-list.md`](references/lark-base-form-questions-list.md) | 适合查看已有题目结构 |
| `+form-questions-create / +form-questions-update / +form-questions-delete` | 创建、更新或删除题目 | [`lark-base-form-questions-create.md`](references/lark-base-form-questions-create.md)、[`lark-base-form-questions-update.md`](references/lark-base-form-questions-update.md)、[`lark-base-form-questions-delete.md`](references/lark-base-form-questions-delete.md) | 先确认 `form-id`;更新或删除前先确认题目目标 |
### 2.9 权限与角色模块
用于启用高级权限,以及管理 Base 自定义角色。
涉及 `+advperm-enable / +advperm-disable / +role-*` 时,操作用户必须为 Base 管理员,否则会返回权限错误。
| 命令 | 用途 / 何时使用 | 必读 reference | 路由提醒 |
|------|------------------|----------------|----------|
| `+advperm-enable / +advperm-disable` | 启用或停用高级权限 | [`lark-base-advperm-enable.md`](references/lark-base-advperm-enable.md)、[`lark-base-advperm-disable.md`](references/lark-base-advperm-disable.md) | 管理角色前必须先启用;停用是高风险操作,会使已有自定义角色失效 |
| `+role-list / +role-get` | 列出角色,或获取角色详情 | [`lark-base-role-list.md`](references/lark-base-role-list.md)、[`lark-base-role-get.md`](references/lark-base-role-get.md)、[`role-config.md`](references/role-config.md) | `+role-list` 只能串行执行;`+role-get` 适合查看完整权限配置 |
| `+role-create / +role-update / +role-delete` | 创建、更新或删除角色 | [`lark-base-role-create.md`](references/lark-base-role-create.md)、[`lark-base-role-update.md`](references/lark-base-role-update.md)、[`lark-base-role-delete.md`](references/lark-base-role-delete.md)、[`role-config.md`](references/role-config.md) | `+role-create` 仅支持 `custom_role``+role-update` 采用 Delta Merge`role_name` 和 `role_type` 即使不改也必须传当前值;`+role-delete` 不可逆 |
## 3. 多维表格通用知识
飞书多维表格英文名是 `Base`,曾用名 `Bitable`;因此旧文档、返回字段、参数名或错误信息里出现 `bitable` 多属历史兼容,不代表应改用另一套命令体系。
### 3.1 字段分类与可写性
| 字段类型 | 含义 | 能否直接作为 `+record-upsert / +record-batch-create / +record-batch-update` 写入目标 | 说明 |
|----------|------|-----------------------------------------------------------|------|
| 存储字段 | 真实存用户输入的数据 | 可以 | 常见如文本、数字、日期、单选、多选、人员、关联 |
| 附件字段 | 存储文件附件 | 不应直接按普通字段写 | 上传附件走 `+record-upload-attachment`;下载附件走 `lark-cli docs +media-download` |
| 系统字段 | 平台自动维护 | 不可以 | 常见如创建时间、更新时间、创建人、修改人、自动编号 |
| `formula` 字段 | 通过表达式计算 | 不可以 | 只读字段 |
| `lookup` 字段 | 通过跨表规则查找引用 | 不可以 | 只读字段 |
### 3.2 任务选路心智模型
| 用户诉求 | 优先方案 | 不要误走 |
|---------|----------|----------|
| 一次性分析 / 临时统计 | `+data-query` | 不要用 `+record-list` / `+record-search` 拉全量后手算 |
| 要把结果长期显示在表里 | `formula` 字段 | 不要只给一次性手工分析结果 |
| 用户明确要求 lookup或天然是固定查找配置 | `lookup` 字段 | 不要默认先上 lookup先判断 formula 是否更合适 |
| 读取原始记录明细 / 关键词检索 / 导出 | `+record-search / +record-list / +record-get` | 不要拿 `+data-query` 当取数命令 |
| 上传附件到记录 | `+record-upload-attachment` | 不要用 `+record-upsert` / `+record-batch-*` 伪造附件值 |
| 下载记录里的附件文件 | `lark-cli docs +media-download --token <file_token> --output <path>` | `file_token``+record-get` 返回的附件字段里取;用法见 [`../lark-doc/references/lark-doc-media-download.md`](../lark-doc/references/lark-doc-media-download.md) |
| 基于视图做筛选读取 | `+view-set-filter` + `+record-list` | 不要跳过视图筛选直接猜条件 |
| 本地 Excel / CSV / `.base` 导入为 Base | `lark-cli drive +import --type bitable` | 不要误走 `+base-create`、`+table-create` 或 `+record-upsert` |
### 3.3 表名、字段名与表达式引用
1. 表名、字段名必须精确匹配真实返回,来源应是 `+table-list / +table-get / +field-list`
2. 不要凭自然语言猜名称,不要自行改写用户口述中的表名、字段名。
3. `formula / lookup / data-query / workflow` 中出现的名称同样必须精确匹配表达式引用、where 条件、DSL 字段名、workflow 配置都遵守同一规则。
4. 跨表场景必须额外读取目标表结构,不能只看当前表。
### 3.4 Token 与链接
这是高优先级章节。只要用户输入里出现链接、token或报错涉及 `baseToken` / `wiki_token` / `obj_token`,都应优先回到这里检查。
| 输入类型 | 正确处理方式 | 说明 |
|---------|--------------|------|
| 直接 Base 链接 `/base/{token}` | 直接提取 token 作为 `--base-token` | 不要把完整 URL 直接作为 `--base-token` |
| Wiki 链接 `/wiki/{token}` | 先 `wiki.spaces.get_node`,再取 `node.obj_token` | 不要把 `wiki_token` 直接当 `--base-token` |
| URL 中的 `?table={id}` | 先按前缀判断对象类型 | `tbl` 开头表示数据表 `table-id`,可作为 `--table-id``blk` 开头表示仪表盘 `dashboard-ID``wkf` 开头表示 `workflow-ID``ldx` 开头表示内嵌文档,不要一律当成 `--table-id` |
| URL 中的 `?view={id}` | 提取为 `--view-id` | 适合直接定位视图 |
| `lark-cli wiki spaces get_node` 返回的 `obj_type` | 后续路线 | 说明 |
|-----------------------------------------------|----------|------|
| `bitable` | 优先走 `lark-cli base +...` | 如果 shortcut 不覆盖,再用 `lark-cli base <resource> <method>`;不要改走 `lark-cli api /open-apis/bitable/v1/...` |
| `docx` | 转到文档 / Drive 相关 skill | 不继续使用本 skill 的 Base 命令 |
| `sheet` | 转到 Sheets 相关 skill | 不继续使用本 skill 的 Base 命令 |
| `slides` | 转到 Drive 相关 skill | 不继续使用本 skill 的 Base 命令 |
| `mindnote` | 转到 Drive 相关 skill | 不继续使用本 skill 的 Base 命令 |
### 3.5 身份选择与权限降级策略
多维表格通常属于用户的个人或团队资源。**默认应优先使用 `--as user`(用户身份)执行所有 Base 操作**,始终显式指定身份。
- **`--as user`(推荐)**:以当前登录用户身份操作其有权访问的 Base。执行前先完成用户授权
```bash
lark-cli auth login --domain base
```
- **`--as bot`(降级)**:仅当 user 身份权限不足、且 bot 身份确实拥有目标 Base 的访问权限时才降级使用。bot 看不到用户私有资源,行为以应用身份执行。
**执行规则**
1. 所有操作默认先用 `--as user`
2. 若 user 身份返回权限错误,先判断是否为**不可重试错误码**(如 `91403`)。若是,**立即停止**,不做任何重试或降级,直接按 `lark-shared` 权限不足处理流程引导用户解决。
3. 非不可重试错误码时,检查错误响应中是否包含 `permission_violations` / `hint` 等提权引导信息:
- **有提权引导**:按 `lark-shared` 权限不足处理流程,先引导用户完成 user 身份提权(`auth login --scope`);确认提权成功后,以 `--as user` 重试。
- **无提权引导**(如资源级无访问权限、非 scope 不足):切换到 `--as bot` 重试**一次**。
4. 若 bot 身份仍然返回权限错误,**立即停止重试**,根据错误响应按 `lark-shared` 流程引导用户解决(引导去开发者后台开通 scope 或确认资源访问权限)。
5. 只有在用户明确要求"用应用身份 / bot 身份操作",才跳过 user 直接使用 `--as bot`
## 4. 执行规则
### 4.1 标准执行顺序
1. 先判断任务属于哪个模块,选对命令族。
2. 如果用户给了链接,先解析 token不要把 wiki token、完整 URL 或其他对象 ID 误当成 `base_token`
3. 先拿结构,再写命令,避免猜表名、字段名、表达式引用。
4. 定位到命令后,先读对应 reference再执行命令。
5. 执行命令,并按返回结果判断下一步。
6. 回复时返回关键结果和后续可继续操作的信息,方便 agent 链式执行下一步。
### 4.2 不可违反规则
1. 先拿结构,再写命令;至少先拿当前表结构,跨表时还要拿目标表结构。
2. 不要猜表名、字段名、表达式引用,一律以真实返回为准。
3. 只使用原子命令;不要回退到旧的聚合式 `+table / +field / +record / +view / +history / +workspace`
4. 写记录前先读字段结构;先 `+field-list`,再按 [`lark-base-cell-value.md`](references/lark-base-cell-value.md) 构造 CellValue。
5. 写字段前先看字段属性规范;先读 `lark-base-shortcut-field-properties.md`,再构造 `+field-create / +field-update` 的 JSON。
6. 只写可写字段;系统字段、附件字段、`formula`、`lookup` 默认不作为普通记录写入目标。
7. 聚合分析与取数分流;统计走 `+data-query`,关键词检索走 `+record-search`,明细走 `+record-list / +record-get`
8. 筛选查询按视图能力执行;先用 `+view-set-filter` 配置筛选,再结合 `+record-list` 读取。
9. Base 场景不要改走裸 API不要切去 `lark-cli api /open-apis/bitable/v1/...`
10. 统一使用 `--base-token`
11. workflow 场景先读 schema不要凭自然语言猜 `type`
12. dashboard 场景先读 guide提到图表、看板、block 就先进入 dashboard 模块。
13. formula / lookup 场景先读 guide没读 guide 前不要直接创建或更新。
### 4.3 并发、分页与批量限制
- `+table-list / +field-list / +record-list / +view-list / +record-history-list / +role-list / +dashboard-list / +dashboard-block-list / +workflow-list` 禁止并发调用,只能串行执行。
- `+record-list` 分页时,`--limit` 最大 `200`;先拉首批并检查 `has_more`,只有用户明确需要更多数据时再继续翻页。
- 批量写入时,单批不超过 `200` 条。
- 连续写入同一表时,必须串行写入,批次间延迟 `0.51` 秒。
### 4.4 确认与回复规则
- 视图重命名时,用户已明确“把哪个视图改成什么名字”时,`+view-rename` 直接执行即可。
- 删除记录 / 字段 / 表时,如果用户已经明确说要删除,且目标明确,`+record-delete / +field-delete / +table-delete` 可直接执行,并带 `--yes`
- 删除目标仍有歧义时,先用 `+record-get / +field-get / +table-get` 或相应 list 命令确认。
- `+base-create / +base-copy` 成功后,回复中必须主动返回新 Base 的标识信息;若结果带可访问链接,也应一并返回。
- 若 Base 由 bot 身份创建或复制shortcut 会自动尝试为当前 CLI 用户补授 `full_access`,并在输出中返回 `permission_grant`agent 不需要再手动编排单独授权。owner 转移必须单独确认,禁止擅自执行。
## 5. 常见错误与恢复
| 错误 / 现象 | 含义 | 恢复动作 |
|-------------|------|----------|
| `1254064` | 日期格式错误 | 传 `YYYY-MM-DD HH:mm:ss` 字符串,不要写相对时间 |
| `1254068` | 超链接格式错误 | `"https://example.com"``"[文本](https://example.com)"` |
| `1254066` | 人员字段错误 | `[{ "id": "ou_xxx" }]` |
| `1254045` | 字段名不存在 | 检查字段名(含空格、大小写) |
| `1254015` | 字段值类型不匹配 | 先 `+field-list`,再按类型构造 |
| `param baseToken is invalid` / `base_token invalid` | 把 wiki token、workspace token 或其他 token 当成了 `base_token` | 如果输入来自 `/wiki/...`,先用 `lark-cli wiki spaces get_node` 取真实 `obj_token`;当 `obj_type=bitable` 时,用 `node.obj_token` 作为 `--base-token` 重试,不要改走 `bitable/v1` |
| `not found` 且用户给的是 wiki 链接 | 常见于把 wiki token 当成 base token | 优先回退检查 wiki 解析,而不是改走 `bitable/v1` |
| formula / lookup 创建失败 | 指南未读或结构不合法 | 先读 `formula-field-guide.md` / `lookup-field-guide.md`,再按 guide 重建请求 |
| `ignored_fields` / `READONLY` | 只读字段被当成可写字段常见于系统字段、formula、lookup | 移除只读字段,只写存储字段;计算结果交给 formula / lookup / 系统字段自动产出 |
| `1254104` | 批量超 200 条 | 分批调用 |
| `1254291` | 并发写冲突 | 串行写入 + 批次间延迟 |
| `91403` | 无权限访问该 Base | **不要重试**。按 `lark-shared` 权限不足处理流程引导用户解决权限问题 |

View File

@ -0,0 +1,350 @@
# dashboard block data_config 参考
Block 的 `data_config` 字段因 `type` 不同而变化。本文档描述所有共享结构。
## 支持的组件类型(`type` 枚举)
| type 值 | 说明 |
|---------|------|
| `column` | 柱状图 |
| `bar` | 条形图 |
| `line` | 折线图 |
| `pie` | 饼图 |
| `ring` | 环形图 |
| `area` | 面积图 |
| `combo` | 组合图 |
| `scatter` | 散点图 |
| `funnel` | 漏斗图 |
| `wordCloud` | 词云 |
| `radar` | 雷达图 |
| `statistics` | 指标卡 |
| `text` | 文本(支持 Markdown |
## 字段类型与操作符速查AI 决策用)
> 先用 `+field-list` / `+field-get` 确认字段 `type`;本节使用当前字段接口里的 canonical 类型名:`number`、`text`、`select`、`datetime`、`checkbox`、`user`。
```
text: is, isNot, contains, doesNotContain, isEmpty, isNotEmpty
number: is, isNot, isGreater, isGreaterEqual, isLess, isLessEqual, isEmpty, isNotEmpty
selectmultiple=false: is, isNot, isEmpty, isNotEmpty
selectmultiple=true: is, isNot, contains, doesNotContain, isEmpty, isNotEmpty
datetime: is, isGreater, isGreaterEqual, isLess, isLessEqual, isEmpty, isNotEmpty
checkbox: is (value: true/false)
user / created_by / updated_by: is, isNot, isEmpty, isNotEmpty
```
## data_config 通用结构
| 字段 | 类型 | 说明 |
|------|------|------|
| `table_name` | string | 关联数据表名称 |
| `series` | `[{ "field_name": "xxx", "rollup": "SUM" }]` | 指标/Y 轴(与 `count_all` 二选一。rollup 支持 `SUM` / `MAX` / `MIN` / `AVERAGE` |
| `count_all` | boolean | COUNTA 聚合,统计所有记录数(与 `series` 二选一) |
| `group_by` | `[{ "field_name": "xxx", "mode": "integrated", "sort": {...} }]` | X 轴分组维度。`mode` 必填,`sort` 可选,见下方说明 |
| `filter` | object | 筛选条件 |
| `filter.conjunction` | `"and"` / `"or"` | 筛选逻辑 |
| `filter.conditions` | `[{ "field_name", "operator", "value" }]` | 筛选条件数组value 类型因字段类型而异(见下方 filter 格式规则) |
### text 类型特殊结构
`text` 类型组件用于展示富文本内容,**不需要数据源配置**(无 `table_name`、`series`、`group_by`、`filter`)。
| 字段 | 类型 | 说明 |
|------|------|------|
| `text` | string | **必填**。支持 Markdown 语法,详见下方说明 |
**支持的 Markdown 语法:**
| 语法 | 示例 | 效果 |
|------|------|------|
| 一级标题 | `# 标题` | 大标题 |
| 二级标题 | `## 标题` | 中标题 |
| 三级标题 | `### 标题` | 小标题 |
| 加粗 | `**文字**` | **文字** |
| 斜体 | `*文字*` | *文字* |
| 删除线 | `~~文字~~` | ~~文字~~ |
| 有序列表 | `1. 项目` | 1. 项目 |
| 无序列表 | `- 项目` | - 项目 |
> **注意**:以上未提及的 Markdown 语法(如链接、图片、代码块、表格等)均不支持。
## group_by 详细说明
### mode 枚举
| mode | 含义 | 适用场景 |
|------|------|----------|
| `integrated` | 聚合分组(默认) | 绝大部分场景,按字段值分组统计 |
| `enumerated` | 多值拆分统计 | 多选、人员等多值字段,将每个选项/人员拆开独立统计 |
> 多选、人员等多值字段默认用 `enumerated`;其他字段默认用 `integrated`
### sort 排序
| sort.type | 含义 | 典型场景 |
|-----------|------|----------|
| `group` | 按横轴值排序 | 按月份升序、按品类名字母序 |
| `value` | 按纵轴值排序 | 按销售额从大到小 |
| `view` | 按数据源记录顺序 | 保持原表行序(不常用) |
`sort.order``asc`(升序)/ `desc`(降序)
示例 — 柱状图按销售额降序:
```json
{
"table_name": "订单表",
"series": [{ "field_name": "金额", "rollup": "SUM" }],
"group_by": [{ "field_name": "类别", "mode": "integrated", "sort": {"type": "value", "order": "desc"} }]
}
```
## filter 格式规则
**基本结构:**
```json
{
"filter": {
"conjunction": "and",
"conditions": [
{ "field_name": "字段名", "operator": "操作符", "value": "值" }
]
}
}
```
**多条件示例and/or**
```json
{
"filter": {
"conjunction": "and",
"conditions": [
{ "field_name": "状态", "operator": "is", "value": "已完成" },
{ "field_name": "金额", "operator": "isGreater", "value": 1000 }
]
}
}
```
**操作符:**
| 操作符 | 含义 | 是否需要 value |
|--------|------|---------------|
| `is` | 等于 | 是 |
| `isNot` | 不等于 | 是 |
| `contains` | 包含 | 是 |
| `doesNotContain` | 不包含 | 是 |
| `isEmpty` | 为空 | 否 |
| `isNotEmpty` | 不为空 | 否 |
| `isGreater` | 大于 | 是 |
| `isGreaterEqual` | 大于等于 | 是 |
| `isLess` | 小于 | 是 |
| `isLessEqual` | 小于等于 | 是 |
**各字段类型的 value 格式:**
| 字段类型 | value 类型 | 适用操作符 | 示例 |
|----------|-----------|-----------|------|
| `text` | string | is, isNot, contains, doesNotContain, isEmpty, isNotEmpty | `{"field_name":"姓名","operator":"contains","value":"张"}` |
| `number` | number | is, isNot, isGreater, isGreaterEqual, isLess, isLessEqual, isEmpty, isNotEmpty | `{"field_name":"金额","operator":"isGreater","value":0}` |
| `select` (`multiple=false`) | string选项名 | is, isNot, isEmpty, isNotEmpty | `{"field_name":"状态","operator":"is","value":"已完成"}` |
| `select` (`multiple=true`) | string[](选多个)/ string选单个 | is, isNot, contains, doesNotContain, isEmpty, isNotEmpty | 多选传数组如 `["标签1","标签2"]`;单选传单个字符串 |
| `datetime` / `created_at` / `updated_at` | numberUnix 毫秒时间戳13位 | is, isGreater, isGreaterEqual, isLess, isLessEqual, isEmpty, isNotEmpty | `{"field_name":"创建日期","operator":"isGreater","value":1704038400000}` |
| `checkbox` | boolean | is | `{"field_name":"已审核","operator":"is","value":true}` |
| `user` / `created_by` / `updated_by` | string 或 string[](用户 ID格式 `ou_xxx`)。不知道 `open_id` 时先用 `lark-cli contact +search-user --query "<姓名/邮箱/手机号>" --as user` 查 id。 | is, isNot, isEmpty, isNotEmpty | `{"field_name":"负责人","operator":"is","value":"ou_xxxxxxxxxxxxxxxx"}` |
| 所有类型(为空/不为空) | 不需要 value | isEmpty, isNotEmpty | `{"field_name":"备注","operator":"isEmpty"}` |
> `value` 类型为 `string | number | boolean | string[]`,需根据字段类型匹配正确格式
## 约束与本地校验
- 必填与互斥
- 图表类型必填:`table_name`
- text 类型必填:`text`
- 互斥:`series` 与 `count_all` 二选一,且至少提供其一(仅图表类型)
- text 类型**不支持**`series`、`count_all`、`group_by`、`filter`
- 长度/结构
- `group_by` 最多 2 个;每项 `field_name` 必填
- `group_by[].sort.type` 取值 `group|value|view``order` 取值 `asc|desc`
- 规范化CLI 自动处理)
- `series[].rollup` 自动转成大写(如 `sum``SUM`
- `group_by[].sort.type/order` 自动转成小写
- 本地校验(可通过 `--no-validate` 跳过)
- `+dashboard-block-create` 默认对 `data_config` 做轻量校验;失败会聚合错误并给出修复建议
- `+dashboard-block-update` 不做强类型校验,由后端验证具体字段
- 仅需传入合法 JSONCLI 不会擅自改写你的业务含义
## 可复制模板
**按意图选择模板:**
- 比较不同类别数值 → 柱状图 / 条形图
- 看趋势变化 → 折线图 / 面积图
- 看占比分布 → 饼图 / 环形图 / 词云
- 多指标对比 → 组合图
- 看两变量关系 → 散点图
- 看流程转化 → 漏斗图
- 看多维度评分 → 雷达图
- 显示单个指标 → 指标卡(统计数字或记录数)
最小柱状图:
```json
{
"table_name": "表名",
"series": [{ "field_name": "数值字段", "rollup": "SUM" }],
"group_by": [{ "field_name": "分组字段", "mode": "integrated" }]
}
```
最小饼图/环形图(按分类字段统计行数占比):
```json
{
"table_name": "表名",
"count_all": true,
"group_by": [{ "field_name": "分类字段", "mode": "integrated" }]
}
```
折线图(按月趋势):
```json
{
"table_name": "表名",
"series": [{ "field_name": "金额", "rollup": "SUM" }],
"group_by": [{ "field_name": "月份", "mode": "integrated", "sort": {"type":"group","order":"asc"} }]
}
```
条形图(横向柱状图):
```json
{
"table_name": "表名",
"series": [{ "field_name": "数值字段", "rollup": "SUM" }],
"group_by": [{ "field_name": "分组字段", "mode": "integrated" }]
}
```
面积图(趋势填充):
```json
{
"table_name": "表名",
"series": [{ "field_name": "数值字段", "rollup": "SUM" }],
"group_by": [{ "field_name": "时间字段", "mode": "integrated", "sort": {"type":"group","order":"asc"} }]
}
```
组合图(柱+线等多指标对比):
```json
{
"table_name": "表名",
"series": [
{ "field_name": "指标1", "rollup": "SUM" },
{ "field_name": "指标2", "rollup": "SUM" }
],
"group_by": [{ "field_name": "分类字段", "mode": "integrated" }]
}
```
散点图(两变量相关性):
```json
{
"table_name": "表名",
"series": [{ "field_name": "Y轴字段数值/指标)", "rollup": "SUM" }],
"group_by": [{ "field_name": "X轴字段分类/维度)", "mode": "integrated" }]
}
```
漏斗图(流程转化):
```json
{
"table_name": "表名",
"series": [{ "field_name": "数值字段", "rollup": "SUM" }],
"group_by": [{ "field_name": "状态字段", "mode": "integrated" }]
}
```
词云(文本频率):
```json
{
"table_name": "表名",
"count_all": true,
"group_by": [{ "field_name": "文本字段", "mode": "integrated" }]
}
```
雷达图(多维度评分):
```json
{
"table_name": "表名",
"series": [
{ "field_name": "维度1", "rollup": "SUM" },
{ "field_name": "维度2", "rollup": "SUM" },
{ "field_name": "维度3", "rollup": "SUM" }
],
"group_by": [{ "field_name": "分类字段", "mode": "integrated" }]
}
```
指标卡(统计数字):
```json
{
"table_name": "数据表",
"series": [{ "field_name": "数字", "rollup": "SUM" }]
}
```
指标卡(统计记录数):
```json
{
"table_name": "数据表",
"count_all": true
}
```
文本组件Markdown 富文本):
```json
{
"text": "# 🚀 一级标题\n这是一个 **加粗** *斜体* ~~删除线~~ 的示例。\n\n## 📌 二级标题\n1. 有序列表项 1\n2. 有序列表项 2\n\n### 📌 三级标题\n- 无序列表项 1\n- 无序列表项 2"
}
```
> **注意**text 类型组件不需要 `table_name`、`series`、`group_by`、`filter` 等数据源相关字段。
## 常见错误与修复
- 同时存在 `series``count_all`
- 现象:后端/本地校验报互斥错误
- 修复:见「关键约束」章节的二选一规则
- 缺少 `table_name`
- 现象:本地校验缺少必填字段
- 修复:指定数据源表名(使用表名,非表 ID
- `series[].rollup` 大小写/取值不合法
- 现象:本地校验提示枚举不支持
- 修复:改为 `SUM|MAX|MIN|AVERAGE` 中之一不区分大小写CLI 会统一为大写;计数请使用 `count_all:true`
- `group_by` 超出 2 个或字段名为空
- 修复:保留前 2 个,或补齐 `field_name`
- 排序枚举不合法
- 修复:`group_by.sort.type` 仅能为 `group|value|view``order` 为 `asc|desc`
- filter 写法不规范
- 修复:`conjunction` 取 `and|or``conditions[].operator` 必须在本页表格列举的范围内;除 `isEmpty/isNotEmpty` 外需提供 `value`
## 坑点
- **`count_all``series` 二选一** — 两者不能同时使用
- **filter `value` 类型因字段而异** — 文本/单选为 string数字为 number日期为毫秒时间戳多选/人员可为 string[],复选框为 boolean`isEmpty`/`isNotEmpty` 不需要 value
- **`data_config` 结构随 `type` 变化** — 不同组件类型的字段不同,创建前务必确认类型对应的字段
- **表名用 name不是 ID**`table_name` 对应的是表名称(如「订单表」),不是 `table_id`

View File

@ -0,0 +1,140 @@
# 飞书多维表格使用场景完整示例base
本文档提供基于 `lark-cli base +...` shortcut 的完整示例。
> **返回**: [SKILL.md](../SKILL.md) | **参考**: [shortcut 字段 JSON 规范](lark-base-shortcut-field-properties.md) · [CellValue 规范](lark-base-cell-value.md)
---
## 场景 1用 unified Shortcut 快速建表
适合已经明确字段结构、希望一次性完成建表的场景。
```bash
lark-cli base +table-create \
--base-token bascnXXXXXXXX \
--name "客户管理表" \
--fields '[
{"name":"客户名称","type":"text","description":"主标题字段"},
{"name":"负责人","type":"user","multiple":false,"description":"用于标记客户跟进的直接负责人"},
{"name":"签约日期","type":"datetime"},
{"name":"状态","type":"select","multiple":false,"options":[{"name":"进行中"},{"name":"已完成"}]}
]'
```
---
## 场景 2创建数据表并查看字段
适合需要先建表、再确认字段结构的场景。
### 步骤 1在已有 Base 中创建数据表
```bash
lark-cli base +table-create \
--base-token bascnXXXXXXXX \
--name "客户管理表"
```
### 步骤 2列出字段
```bash
lark-cli base +field-list \
--base-token bascnXXXXXXXX \
--table-id tblXXXXXXXX \
--limit 100
```
> 提示Base token 统一通过 `--base-token` 传入;表 ID 统一通过 `--table-id` 传入。
---
## 场景 3创建、读取、更新单条记录
### 新增记录
```bash
lark-cli base +record-upsert \
--base-token bascnXXXXXXXX \
--table-id tblXXXXXXXX \
--json '{
"客户名称":"字节跳动",
"负责人":[{"id":"ou_xxx"}],
"状态":"进行中"
}'
```
### 列出记录
```bash
lark-cli base +record-list \
--base-token bascnXXXXXXXX \
--table-id tblXXXXXXXX \
--limit 100
```
### 更新记录
```bash
lark-cli base +record-upsert \
--base-token bascnXXXXXXXX \
--table-id tblXXXXXXXX \
--record-id recXXXXXXXX \
--json '{
"状态":"已完成"
}'
```
### 删除记录
```bash
lark-cli base +record-delete \
--base-token bascnXXXXXXXX \
--table-id tblXXXXXXXX \
--record-id recXXXXXXXX \
--yes
```
---
## 场景 4配置视图筛选后按视图读取记录
需要筛选查询时,推荐先写视图筛选,再通过 `view_id` 读取记录。
### 更新视图筛选条件
```bash
lark-cli base +view-set-filter \
--base-token bascnXXXXXXXX \
--table-id tblXXXXXXXX \
--view-id vewXXXXXXXX \
--json '{
"logic":"and",
"conditions":[
{
"field_name":"状态",
"operator":"is",
"value":["进行中"]
}
]
}'
```
### 按视图读取记录
```bash
lark-cli base +record-list \
--base-token bascnXXXXXXXX \
--table-id tblXXXXXXXX \
--view-id vewXXXXXXXX \
--limit 100
```
---
## 场景 5什么时候优先用 Shortcut
- 需要一次性建表并附带字段、视图时,优先 `lark-cli base +table-create`
- 需要按业务字段名做 upsert 时,优先 `lark-cli base +record-upsert`
- 需要配置筛选视图时,优先 `lark-cli base +view-set-filter`
- 需要记录历史时,优先 `lark-cli base +record-history-list`

View File

@ -0,0 +1,735 @@
# Base Formula Writing Guide
## Mandatory Read Acknowledgement
When creating or updating a formula field with `lark-cli base +field-create/+field-update --json ...` and `type` is `formula`, you should read this guide first and only then add `--i-have-read-guide` to the command.
Do **not** proactively add `--i-have-read-guide` before reading this guide. Without it, the CLI will fail fast and direct you back to this guide.
## Default strategy
**All cross-table references, aggregations, and computed fields should use Formula fields by default.** Do NOT use Lookup fields unless the user explicitly requests it. Formula is a strict superset of Lookup — anything Lookup can do, Formula can do with a single expression.
## Usage
When creating a formula field, the Agent should:
1. Get all table names: `lark-cli base +table-list --base-token <base>` — returns `items[].table_name`
2. Get table structure: `lark-cli base +table-get --base-token <base> --table-id <table>` — returns `fields[]`
3. If the formula references other tables, also get those tables' structures
4. Write the formula expression following this guide
5. Construct the Formula field JSON and submit it to create or update the field
**Key constraints**:
- The JSON must include `"type": "formula"` — this field is required
- Table names and field names in the formula must **exactly match** those returned by `+table-list` / `+table-get`
- The `expression` value is a string containing the formula expression; double quotes inside the expression must be properly escaped in JSON (e.g. `\"text\"`)
---
## Section 1: Core Concepts — Scalar vs List
This is the foundation of formula logic. You must determine this before writing any formula.
| Syntax | Meaning | Return type | Example |
| --------------------- | -------------------------------------------- | ---------------------- | -------------------------------------------- |
| `[Field]` | Value of this field in the current row | Scalar (single value) | `[Name]``"Alice"` |
| `[TableName].[Field]` | All values of this field in the target table | List (multiple values) | `[Employees].[Name]``["Alice","Bob",...]` |
| `[TableName]` | The target table (entire table) | Table reference | Used as data range for FILTER/COUNTIF etc. |
**Rules**:
- Scalars can be used directly in operations: `[Price] * [Quantity]`
- Lists cannot be used as scalars — they must be processed first: use `SUM()` for sum, `ARRAYJOIN(",")` for joining, `FIRST()`/`LAST()`/`NTH()` for single value extraction
- Link field access `[LinkField].[TargetField]` returns a list (values of the target field for all linked records)
- **LISTCOMBINE flattening rule**: When a FILTER's result column is itself a multi-value field (`select` with `multiple=true`, `link`, etc.), it produces a 2D array and **must** be flattened with `.LISTCOMBINE()`; for single-value fields (`number`, `text`, etc.) it can be omitted, but adding it is never wrong:
```
[Table].FILTER(CurrentValue.[Field] = [Value]).[Tags].LISTCOMBINE() ← required for multi-value columns
[Table].FILTER(CurrentValue.[Field] = [Value]).[NumberCol].LISTCOMBINE() ← optional for single-value columns
```
---
## Section 2: Data Types and Type Conversion
### Field storage types
| Type | Description | Supported operations |
|------|-------------|----------------------|
| `number` | Stored as numeric value | Math operations, comparisons, auto-converts to string for concatenation |
| `text` | Stored as string | String operations; can participate in math if content is numeric, otherwise errors |
| `datetime` | Date object | Date functions, add/subtract with numbers; auto-converts to default format string when using `&` — use TEXT to format first for controlled output |
| `select` (`multiple=true`) | Data list | List functions, CONTAIN checks |
| `link` | Links to other table records | Chained access `[LinkField].[Field]`, result is a list |
| `checkbox` | TRUE/FALSE | Logical operations; auto-converts to number when compared with numbers |
### Implicit type conversion
| Scenario | Conversion rule |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------- |
| Number + Float | → Float |
| Date + Number | → Date (adds/subtracts days). Use `+`/`-` for whole days, use `DURATION()` for hour/minute/second precision |
| Date - Date | → Duration |
| Boolean compared with Number | Boolean auto-converts to number (TRUE=1, FALSE=0) |
| `&` concatenation | Both sides auto-convert to string |
### Type consistency in comparisons
When using comparison operators (`>`, `>=`, `<`, `<=`, `=`, `!=`), **both sides should be the same type** to avoid semantic errors or unexpected results.
**Principle**: When types differ, explicitly convert one side rather than relying on implicit conversion:
- `number` vs `text` → use `VALUE()` to convert text to number
- `datetime` vs `text` → use `TEXT()` to convert date to text
- `datetime` vs `datetime` equality → dates include time components, so direct `=` comparison may fail due to different hours/minutes/seconds. For day-level equality, convert to text first: `TEXT([DateA], "YYYY/MM/DD") = TEXT([DateB], "YYYY/MM/DD")`
- `select` and `user` fields can be compared with both same-type values and text
- `text` fields in numeric aggregation (SUM/AVERAGE/MIN/MAX etc.) → convert to number with `VALUE()` first. For FILTER results, use `.MAP(VALUE(CurrentValue)).SUM()`
---
## Section 3: CurrentValue
**CurrentValue is the iteration variable in FILTER/MAP/COUNTIF/SUMIF functions, representing the "current item" being processed in the data range.**
### CurrentValue meaning in different contexts
| Data range type | CurrentValue represents | Access pattern | Example |
| ---------------------------- | ----------------------- | --------------------------- | --------------------------------------------------------- |
| Entire table `[TableName]` | A row in the table | `CurrentValue.[FieldName]` | `[Orders].FILTER(CurrentValue.[Amount] > 100).[Customer]` |
| Column `[TableName].[Field]` | A single field value | Use `CurrentValue` directly | `[Orders].[Amount].FILTER(CurrentValue > 100)` |
| `select` (`multiple=true`) field `[Tags]` | One option | Use `CurrentValue` directly | `[Tags].FILTER(CurrentValue = "Important")` |
| LIST-generated list | One element | Use `CurrentValue` directly | `LIST(1,2,3).MAP(CurrentValue * 2)` |
### Key rules
1. **When data range is a table**, use `CurrentValue.[FieldName]` to access row fields
2. **When data range is a column/list**, use `CurrentValue` directly for the element value — **cannot** use `CurrentValue.[FieldName]`
3. CurrentValue can **only** appear inside the condition/mapping parameters of FILTER/MAP/COUNTIF/SUMIF functions
4. To reference the current table's field value in a condition, write `[FieldName]` directly — it refers to the formula row's value, not a property of CurrentValue
### Anti-patterns
| Wrong | Reason | Correct |
| ---------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------ |
| `[Table].[Col].FILTER(CurrentValue.[Col] > 0)` | Data range is a column; CurrentValue is a scalar, cannot use `.` to access fields | `[Table].[Col].FILTER(CurrentValue > 0)` |
| `[Table].FILTER(CurrentValue > 100)` | Data range is a table; CurrentValue is a row, cannot compare directly | `[Table].FILTER(CurrentValue.[Amount] > 100).[Amount]` |
| `CurrentValue + 1` (at top level) | CurrentValue can only be used inside iteration functions | Use inside MAP/FILTER etc. |
---
## Section 4: Operators
Base formulas **only allow** the following operators. `like`, `in`, `<>`, `**`, `^` etc. are prohibited.
| Category | Operators | Description |
| ------------- | -------------------------- | -------------------------------------------------------------------------- |
| Arithmetic | `+` `-` `*` `/` `%` | Add, subtract, multiply, divide, modulo (`%` is equivalent to `MOD()`) |
| Comparison | `>` `>=` `<` `<=` `=` `!=` | Greater than, greater or equal, less than, less or equal, equal, not equal |
| Logical | `&&` `\|\|` | AND, OR |
| Concatenation | `&` | Text concatenation; non-text values auto-convert to string |
**Important**:
- Equality uses `=` (single equals), not `==`
- Not-equal uses `!=`, not `<>`
- String concatenation uses `&`, not `+`
- Both `&&`/`||` and AND()/OR() functions are supported
---
## Section 5: Link Fields and Cross-Table References
### Link field description
When a field type is described as `FieldName: Link [target table: X, foreign key: Y]`, it links to target table X using field Y as the join key.
### Chained cross-table access
```
[LinkField].[TargetField]
```
Retrieves the target field values for all linked records as a list. Supports continued chaining: `[LinkA].[LinkB].[Field]`.
### Equivalent expanded form
- Multi-value link: `[TargetTableX].FILTER([LinkField].CONTAIN(CurrentValue.[Y])).[TargetField].LISTCOMBINE()`
- Single-value link: `[TargetTableX].FILTER(CurrentValue.[Y] = [LinkField]).[TargetField].LISTCOMBINE()`
(`.LISTCOMBINE()` is required when `[TargetField]` is a multi-value field; optional for single-value fields)
### Notes
- Link fields typically return **lists** (possibly empty)
- To output a single value, use aggregation (SUM/MAX), joining (ARRAYJOIN), or extraction (FIRST/LAST/NTH)
- Do not nest FILTER inside FILTER for cross-table queries — prefer link field chained access
---
## Section 6: Function Call Conventions
### Two calling styles
| Style | Format | Description |
| ---------- | ------------------ | ----------------------------------- |
| Functional | `FUNC(arg1, arg2)` | Works for all functions |
| Chained | `arg1.FUNC(arg2)` | Moves the first argument before `.` |
**Rules**:
- Zero-argument functions cannot be chained: `NOW()`, `TODAY()`, `PI()`, `TRUE()`, `FALSE()`
- SORTBY can **only** be chained: `[Table].SORTBY([Table].[SortCol]).[OutputCol]`. The sort column always uses the original table's column name (`[TableName].[Field]` format); the engine aligns rows internally, even when the data range is a FILTER result
- FILTER is recommended to be chained: `[Table].FILTER(condition).[OutputCol]`
### FILTER / SORTBY result column rules
- **When data range is a table** `[TableName]`, FILTER / SORTBY returns a table reference. The chain **must** end with `.[Field]` to specify the result column, otherwise the formula fails:
```
Correct: [Sales].FILTER(CurrentValue.[Amount] > 100).[Customer]
Correct: [Sales].FILTER(condition).SORTBY([Sales].[SortCol]).[Customer] ← result column at end of chain
Wrong: [Sales].FILTER(CurrentValue.[Amount] > 100) ← missing result column
```
- **When data range is a column** `[TableName].[Field]` or a list, FILTER returns the filtered list directly — **no** result column needed:
```
Correct: [Sales].[Amount].FILTER(CurrentValue > 100)
```
After the result column, it's recommended to flatten with `.LISTCOMBINE()` first (especially when the result column is a multi-value field), then chain aggregation functions:
```
[Sales].FILTER(CurrentValue.[Amount] > 100).[Amount].LISTCOMBINE().SUM()
```
---
## Section 7: Hard Constraints
1. **Nesting prohibition**: FILTER / SUMIF / COUNTIF / MAP **must not be nested** inside each other's condition/mapping expressions. None of these functions can appear inside the condition or mapping parameter of another.
- Prohibited: `[Table1].FILTER(CurrentValue.[Col] = [Table2].FILTER(...).[Col])` ← FILTER inside FILTER condition
- Prohibited: `[Table].MAP([Table2].MAP(...))` ← MAP inside MAP mapping
- **Allowed**: `[Table].FILTER(cond1).[Col].FILTER(cond2)` ← chained call; the first FILTER's output is the second's data range, not nesting
2. **Function whitelist**: Only use functions listed in Section 8. No unlisted functions.
3. **Exact name matching**: Table names and field names in formulas must **exactly match** those returned by `+table-get` — no renaming or adding spaces.
4. **Operator whitelist**: Only use operators listed in Section 4.
5. **Strings use double quotes**: Strings must be wrapped in double quotes `"`, single quotes are not supported.
6. **Do not use LOOKUP**: FILTER is a superset of LOOKUP. All LOOKUP formulas can be rewritten with FILTER. Use FILTER exclusively to reduce complexity.
---
## Section 8: Complete Function Reference
### 8.1 Logic functions
| Function | Signature | Return type | Description |
| ------------- | ------------------------------------------------------------------ | -------------------- | -------------------------------------------------------------------------------------------- |
| IF | `IF(condition, true_val, [false_val])` | Matches branch type | Returns true_val when TRUE, false_val otherwise; omitting false_val returns false (not null) |
| IFS | `IFS(cond1, val1, cond2, val2, ...)` | Matches branch type | Multi-condition branching; returns value for the first TRUE condition |
| SWITCH | `SWITCH(expr, match1, result1, [match2, result2, ...], [default])` | Matches branch type | Matches expression value and returns corresponding result |
| IFERROR | `IFERROR(expr, fallback)` | Matches branch type | Returns fallback when expression errors |
| IFBLANK | `IFBLANK(expr, fallback)` | Matches branch type | Returns fallback when expression is blank (blank = NULL/empty string/empty list) |
| AND | `AND(cond1, cond2, ...)` | Boolean | TRUE when all conditions are TRUE |
| OR | `OR(cond1, cond2, ...)` | Boolean | TRUE when any condition is TRUE |
| NOT | `NOT(condition)` | Boolean | Logical negation |
| ISBLANK | `ISBLANK(value)` | Boolean | Tests if blank (NULL/empty string/empty list are blank; 0 and FALSE are not) |
| ISNULL | `ISNULL(value)` | Boolean | Tests if NULL (only NULL is true; empty string is not) |
| ISERROR | `ISERROR(expr)` | Boolean | Tests if expression errors |
| ISNUMBER | `ISNUMBER(value)` | Boolean | Tests if value is a number |
| CONTAIN | `CONTAIN(search_range, value, ...)` | Boolean | Tests if a list or `select` (`multiple=true`) contains the value; **does NOT do text substring matching** |
| CONTAINSALL | `CONTAINSALL(search_range, value, ...)` | Boolean | Tests if a list or `select` (`multiple=true`) contains all specified values |
| CONTAINSONLY | `CONTAINSONLY(search_range, value, ...)` | Boolean | Tests if a list or `select` (`multiple=true`) contains only the specified values |
| TRUE | `TRUE()` | Boolean | Returns TRUE |
| FALSE | `FALSE()` | Boolean | Returns FALSE |
| RECORD_ID | `RECORD_ID()` | Text | Returns the current row's record ID |
| RANDOMBETWEEN | `RANDOMBETWEEN(min_int, max_int, [keep_updating])` | Number | Random integer in the specified range |
| RANDOMITEM | `RANDOMITEM(list, [keep_updating])` | Matches element type | Randomly picks one element from a list |
### 8.2 Numeric functions
| Function | Signature | Return type | Description |
| --- | --- | --- | --- |
| SUM | `SUM(val1, val2, ...)` | Number | Sum; accepts multiple values or a list |
| AVERAGE | `AVERAGE(val1, val2, ...)` | Number | Average |
| MAX | `MAX(val1, val2, ...)` | Number | Maximum |
| MIN | `MIN(val1, val2, ...)` | Number | Minimum |
| MEDIAN | `MEDIAN(val1, val2, ...)` | Number | Median |
| COUNTA | `COUNTA(val1, val2, ...)` | Number | Count of non-blank values |
| COUNTIF | `COUNTIF(data_range, condition)` | Number | Count matching items. Data range can be a **table** (CurrentValue is a row, use `CurrentValue.[Field]`) or a **column** (CurrentValue is a scalar value) |
| SUMIF | `SUMIF(data_range, condition)` | Number | Sum matching values. Data range **must be a numeric column** (e.g. `[Table].[NumField]`); CurrentValue is each value in that column (scalar), cannot use `CurrentValue.[Field]` to access other fields. For cross-field conditions, use FILTER+SUM instead |
| ROUND | `ROUND(number, digits)` | Number | Round. digits: 1=one decimal, 0=integer, -1=tens place |
| ROUNDUP | `ROUNDUP(number, digits)` | Number | Round away from zero. Same digits semantics as ROUND |
| ROUNDDOWN | `ROUNDDOWN(number, digits)` | Number | Round toward zero. Same digits semantics as ROUND |
| FLOOR | `FLOOR(number, [base])` | Number | Round down to nearest multiple of base (default 1) |
| CEILING | `CEILING(number, [base])` | Number | Round up to nearest multiple of base (default 1) |
| ABS | `ABS(number)` | Number | Absolute value |
| INT | `INT(number)` | Integer | Truncate to integer |
| MOD | `MOD(dividend, divisor)` | Number | Modulo |
| POWER | `POWER(base, exponent)` | Number | Exponentiation |
| QUOTIENT | `QUOTIENT(dividend, divisor)` | Number | Integer division |
| VALUE | `VALUE(text)` | Number | Convert text to number |
| ISODD | `ISODD(number)` | Boolean | Tests if number is odd |
| RANK | `RANK(value, search_range, [ascending])` | Number | Rank of value in range; default descending |
| SEQUENCE | `SEQUENCE(start, end, [step])` | List | Generate number sequence |
| PI | `PI()` | Number | Pi constant |
| SIN/COS/TAN/ASIN/ACOS/ATAN/ATAN2/SINH/COSH/TANH/ASINH/ACOSH/ATANH | `func(radians_or_value)` | Number | Trigonometric and hyperbolic functions; arguments in radians |
### 8.3 Text functions
| Function | Signature | Return type | Description |
| --------------- | ---------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------- |
| CONCATENATE | `CONCATENATE(text1, text2, ...)` | Text | Concatenate multiple texts; supports lists as input |
| LEN | `LEN(text)` | Number | Character count |
| LEFT | `LEFT(text, [count])` | Text | Extract from left; default 1 |
| RIGHT | `RIGHT(text, [count])` | Text | Extract from right; default 1 |
| MID | `MID(text, start, count)` | Text | Extract from middle |
| FIND | `FIND(search_val, search_range, [start])` | Number | Find substring position (case-sensitive); returns -1 if not found |
| REPLACE | `REPLACE(text, start, count, new_text)` | Text | Replace by position |
| SUBSTITUTE | `SUBSTITUTE(text, old_text, new_text, [occurrence])` | Text | Replace by content; can specify which occurrence |
| UPPER | `UPPER(text)` | Text | Convert to uppercase |
| LOWER | `LOWER(text)` | Text | Convert to lowercase |
| TRIM | `TRIM(text)` | Text | Remove leading/trailing spaces |
| TEXT | `TEXT(value, format)` | Text | Format output. Date formats: `"YYYY-MM-DD"`, `"YYYY/MM/DD hh:mm:ss"`; number formats: `"00"`, `"000.00"` |
| CONTAINTEXT | `CONTAINTEXT(text, search_text)` | Boolean | Tests if text contains substring (text substring matching) |
| SPLIT | `SPLIT(text, delimiter)` | List | Split text by delimiter |
| TODATE | `TODATE(value)` | Date | Convert date string to date type |
| CHAR | `CHAR(number)` | Text | ASCII code to character |
| FORMAT | `FORMAT(template, [val1, val2, ...])` | Text | Template string formatting; use `{1}`, `{2}` as placeholders |
| HYPERLINK | `HYPERLINK(url, [display_text])` | Hyperlink | Create a hyperlink |
| ENCODEURL | `ENCODEURL(text)` | Text | URL encode |
| REGEXMATCH | `REGEXMATCH(text, regex)` | Boolean | Regex match test |
| REGEXEXTRACT | `REGEXEXTRACT(text, regex)` | List | Extract first match's capture groups |
| REGEXEXTRACTALL | `REGEXEXTRACTALL(text, regex)` | 2D List | Extract all matches |
| REGEXREPLACE | `REGEXREPLACE(text, regex, replacement)` | Text | Regex replace |
### 8.4 Date functions
| Function | Signature | Return type | Description |
| ----------- | ----------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------- |
| NOW | `NOW()` | Date | Current date and time |
| TODAY | `TODAY()` | Date | Current date (midnight) |
| DATE | `DATE(year, month, day)` | Date | Construct a date |
| YEAR | `YEAR(date)` | Number | Extract year |
| MONTH | `MONTH(date)` | Number | Extract month |
| DAY | `DAY(date)` | Number | Extract day |
| HOUR | `HOUR(date)` | Number | Extract hour |
| MINUTE | `MINUTE(date)` | Number | Extract minute |
| SECOND | `SECOND(date)` | Number | Extract second |
| WEEKDAY | `WEEKDAY(date, [type])` | Number | Day of week |
| WEEKNUM | `WEEKNUM(date, [type])` | Number | Week number |
| DAYS | `DAYS(end_date, start_date)` | Number | Days between two dates (end - start), includes decimals. **Note parameter order: end date comes first** |
| DATEDIF | `DATEDIF(start_date, end_date, [unit])` | Number | Whole days/months/years between dates. Unit: `"D"`(default)/`"M"`/`"Y"`. **Start must be before end** |
| DURATION | `DURATION(days, [hours], [minutes], [seconds])` | Duration | Create a duration for date arithmetic |
| EDATE | `EDATE(date, months)` | Date | Date N months later |
| EOMONTH | `EOMONTH(date, [months])` | Date | End of month N months later; months default 0 |
| WORKDAY | `WORKDAY(start_date, days, [holidays])` | Date | Date N workdays later (skips weekends and holidays) |
| NETWORKDAYS | `NETWORKDAYS(start_date, end_date, [holidays])` | Number | Workdays between dates (inclusive) |
### 8.5 List functions
| Function | Signature | Return type | Description |
| --- | --- | --- | --- |
| LIST | `LIST(val1, val2, ...)` | List | Create a list |
| FIRST | `FIRST(list)` | Scalar | First element |
| LAST | `LAST(list)` | Scalar | Last element |
| NTH | `NTH(list, index)` | Scalar | Nth element (1-based) |
| FILTER | `[Table].FILTER(condition).[ResultCol]` or `[Table].[Col].FILTER(condition)` | List | Filter by condition. When data range is a table, result column is **required**; when it's a column/list, it's not needed. Use CurrentValue in conditions. Add `.LISTCOMBINE()` when result column is multi-value |
| MAP | `data_range.MAP(mapping_expr)` | List | Apply mapping to each element. Use CurrentValue in mapping |
| SORT | `SORT(list, [ascending])` | List | Sort; default ascending (TRUE) |
| SORTBY | `[Table].SORTBY([Table].[SortCol], [ascending]).[OutputCol]` | List | Sort by column then extract output column. **Chain-only, must include output column** |
| UNIQUE | `UNIQUE(list)` | List | Deduplicate |
| ARRAYJOIN | `ARRAYJOIN(list, [delimiter])` | Text | Join list elements as text; default comma-separated |
| LISTCOMBINE | `LISTCOMBINE(val1, [val2, ...])` or `list.LISTCOMBINE()` | List | Two uses: (1) merge values/lists into one list; (2) chained call to flatten 2D array (commonly used when FILTER result column is a multi-value field) |
| DISTANCE | `DISTANCE(location1, location2)` | Number | Distance between two geographic locations (km) |
---
## Section 9: Commonly Confused Functions
### CONTAIN vs CONTAINTEXT
| | CONTAIN | CONTAINTEXT |
| ----------- | -------------------------------------------------------------- | ---------------------------------------------------------- |
| Purpose | Tests if a **list / `select` (`multiple=true`)** contains a value | Tests if **text** contains a substring |
| Example | `[Tags].CONTAIN("Urgent")` | `[Notes].CONTAINTEXT("completed")` |
| Wrong usage | `CONTAIN([Notes], "completed")` — cannot do substring matching | `CONTAINTEXT([Tags], "Urgent")` — Tags is a list, not text |
### ISBLANK vs ISNULL
| | ISBLANK | ISNULL |
| ----------------- | ------- | ------ |
| NULL | TRUE | TRUE |
| `""` empty string | TRUE | FALSE |
| Empty list `[]` | TRUE | FALSE |
| `0` | FALSE | FALSE |
| `FALSE` | FALSE | FALSE |
### DAYS vs DATEDIF
| | DAYS | DATEDIF |
| --------------- | ------------------------------------------------------------ | ----------------------------------------- |
| Parameter order | `DAYS(end, start)` — end first | `DATEDIF(start, end, unit)` — start first |
| Precision | Includes decimals (hours/minutes/seconds as fractional days) | Integer only (whole days/months/years) |
| Negative values | Returns negative when start is after end | **Errors** when start is after end |
### SUM vs SUMIF
| | SUM | SUMIF |
| --------- | ---------------------------------------------- | -------------------------------------------------------------- |
| Purpose | Sum all values | Sum values **matching a condition** |
| Arguments | `SUM(val1, val2, ...)` or `SUM([Table].[Col])` | `SUMIF(data_range, condition)` with CurrentValue in condition |
| Example | `SUM([Orders].[Amount])` — sum all | `SUMIF([Orders].[Amount], CurrentValue > 100)` — sum only >100 |
### FILTER+aggregation vs COUNTIF/SUMIF
| | FILTER+aggregation | COUNTIF/SUMIF |
| ----------- | ----------------------------------------------------- | ------------------------------------------------------------------------------ |
| Nature | Filter then aggregate (two steps) | One-step (syntactic sugar) |
| Equivalence | `[Table].FILTER(cond).[Col].LISTCOMBINE().SUM()` | `SUMIF([Table].[Col], cond)` (only when condition involves only column values) |
| When to use | Conditions span multiple fields, or multi-step needed | Conditions only involve column values (e.g. `CurrentValue > 100`) |
---
## Section 10: Decision Trees
### Cross-table queries: which approach?
```
Need data from another table?
├─ Current table has a link field to the target table?
│ ├─ Yes → Use chained access: [LinkField].[TargetField]
│ │ Need aggregation? → .SUM() / .ARRAYJOIN(",") / .FIRST()
│ └─ No → Need to match by field value?
│ ├─ Field matching or complex filtering → [TargetTable].FILTER(CurrentValue.[MatchField] = [Value]).[OutputCol]
│ └─ Only counting or summing → COUNTIF([TargetTable], condition) / FILTER+SUM
```
### Conditional logic: IF vs IFS vs SWITCH?
```
Need conditional logic?
├─ Single condition → IF(condition, true_val, false_val)
├─ Multiple mutually exclusive conditions (if-elseif-else) → IFS(cond1, val1, cond2, val2, ...)
├─ Matching a value against fixed options → SWITCH(expr, option1, result1, option2, result2, ..., default)
└─ Need error handling?
├─ Catch errors → IFERROR(expr, fallback)
└─ Catch blanks → IFBLANK(expr, fallback)
```
### Aggregation: which function?
```
Need to aggregate data?
├─ Sum/average/max/min for entire column → SUM/AVERAGE/MAX/MIN([Table].[Col])
├─ Count non-blank → COUNTA([Table].[Col])
├─ Conditional count → COUNTIF([Table], CurrentValue.[Field] = [Value])
├─ Conditional sum (column-only condition) → SUMIF([Table].[Col], CurrentValue > threshold)
├─ Conditional sum (cross-field condition) → [Table].FILTER(CurrentValue.[Field]=value).[NumCol].LISTCOMBINE().SUM()
├─ Count unique → [Table].[Col].UNIQUE().COUNTA()
└─ Ranking → RANK([Value], [Table].[Col])
```
---
## Section 11: Common Formula Patterns
### Pattern 1: Cross-table conditional count
Count rows in target table matching a condition:
```
[TargetTable].COUNTIF(CurrentValue.[MatchField] = [CurrentTableField])
```
### Pattern 2: Cross-table conditional sum
Filter target table by current row's value, then sum:
```
[TargetTable].FILTER(CurrentValue.[MatchField] = [CurrentTableField]).[NumCol].LISTCOMBINE().SUM()
```
SUMIF works when data range is a column and conditions only involve column values:
```
SUMIF([TargetTable].[NumCol], CurrentValue > 100)
```
Note: COUNTIF can use a table as data range (only counting, no specific column needed), but SUMIF's data range **must be a numeric column** (needs values to sum), so `CurrentValue` is each value in that column (scalar) — cannot use `CurrentValue.[OtherField]` to access other fields. For cross-field conditions, use FILTER with a table as data range.
### Pattern 3: Cross-table lookup
```
[TargetTable].FILTER(CurrentValue.[MatchCol] = [CurrentTableField]).[ReturnCol]
```
### Pattern 4: Link field values + aggregation
```
SUM([LinkField].[NumField])
[LinkField].[TextField].UNIQUE().ARRAYJOIN(",")
```
### Pattern 5: Conditional text concatenation
```
IF([Condition], "prefix" & [Field] & "suffix", "default text")
```
### Pattern 6: Date difference
```
DATEDIF([StartDate], [EndDate], "D") & " days"
DAYS([EndDate], [StartDate])
```
### Pattern 7: List element mapping
```
[SelectField(which multiple=true)].MAP(CurrentValue & " tag")
SPLIT([TextField], ",").MAP(TRIM(CurrentValue))
```
### Pattern 8: Cross-table with sorting
```
[TargetTable].SORTBY([TargetTable].[SortCol], FALSE).[OutputCol]
[TargetTable].FILTER(CurrentValue.[Field] = [Value]).SORTBY([TargetTable].[SortCol]).[OutputCol]
```
---
## Section 12: Anti-Pattern Collection
### Mistake 1: Extra argument in MAP
```
Wrong: [Table].[Col].MAP([Table2].[Col], CurrentValue + 1)
Correct: [Table].[Col].MAP(CurrentValue + 1)
```
Reason: MAP takes only two arguments (data range + mapping expression), no "lookup range".
### Mistake 2: Inverted FILTER syntax
```
Wrong: condition.[Table].FILTER()
Correct: [Table].FILTER(condition).[ResultCol] (result column required when data range is a table)
```
Reason: FILTER's data range comes first, condition is passed as the argument.
### Mistake 3: Using CurrentValue.[Field] on a column range
```
Wrong: SUMIF([Sales].[Revenue], CurrentValue.[Salesperson] = [Name])
Correct: [Sales].FILTER(CurrentValue.[Salesperson] = [Name]).[Revenue].LISTCOMBINE().SUM()
```
Reason: `SUMIF([Sales].[Revenue], ...)` uses "Revenue" column as data range. CurrentValue is each revenue value (scalar), not a row — cannot use `.` to access other fields. Use FILTER with the table as data range for cross-field conditions.
### Mistake 4: Missing result column after FILTER
```
Wrong: [Sales].FILTER(CurrentValue.[Amount] > 100)
Correct: [Sales].FILTER(CurrentValue.[Amount] > 100).[Customer]
```
Reason: FILTER on a table returns a table reference; must specify result column with `.[Field]` at the end.
### Mistake 5: Nested FILTER
```
Wrong: [Table1].FILTER(CurrentValue.[ID] = [Table2].FILTER(CurrentValue.[Status]="Done").[ID])
Correct: [Table1].FILTER(CurrentValue.[ID] = [CurrentRowField]).[OutputCol]
```
Reason: FILTER/MAP/SUMIF/COUNTIF cannot be nested inside each other's conditions. Split into multiple steps or use link fields.
### Mistake 6: SORTBY without output column
```
Wrong: [Table].SORTBY([Table].[Col])
Correct: [Table].SORTBY([Table].[Col]).[OutputCol]
```
Reason: SORTBY must have an output column at the end; otherwise the result cannot be represented as an array.
### Mistake 7: SORTBY sort column without table name
```
Wrong: [Table].SORTBY([Col]).[OutputCol]
Correct: [Table].SORTBY([Table].[Col]).[OutputCol]
```
Reason: SORTBY's sort column must use `[TableName].[FieldName]` format.
### Mistake 8: Using CONTAIN for text substring matching
```
Wrong: CONTAIN([Notes], "urgent")
Correct: CONTAINTEXT([Notes], "urgent")
```
Reason: CONTAIN checks if a list or `select` (`multiple=true`) contains a whole value, not substring matching. Use CONTAINTEXT for text substrings.
### Mistake 9: Date concatenation without formatting
```
Not recommended: "Deadline: " & [DateField] ← output format is uncontrolled
Recommended: "Deadline: " & TEXT([DateField], "YYYY-MM-DD")
```
Reason: Concatenating a date with `&` won't error, but uses the default format. Use TEXT to specify the format explicitly.
### Mistake 10: Reversed DAYS parameter order
```
Wrong: DAYS([StartDate], [EndDate]) → returns negative
Correct: DAYS([EndDate], [StartDate]) → returns positive
```
Reason: DAYS parameter order is end date first, start date second.
### Mistake 11: Chaining zero-argument functions
```
Wrong: TODAY.DAYS([Date])
Correct: TODAY().DAYS([Date])
```
Reason: NOW, TODAY, PI and other zero-argument functions must include parentheses.
---
## Section 13: Complete Examples
### Example 1: Employee sales summary
**Table structure** (from `+table-get`):
- Employees: EmployeeID (Text), Name (Text), Department (Text)
- Sales: ContractID (Number), SalespersonID (Text), Quantity (Number), Total (Number)
**Current table**: Employees
**Requirement**: For each employee, output "Sold XX orders" if they have sales records, otherwise "No sales records".
**Formula**:
```
IF(
[Sales].COUNTIF(CurrentValue.[SalespersonID] = [EmployeeID]) >= 1,
"Sold " & [Sales].COUNTIF(CurrentValue.[SalespersonID] = [EmployeeID]) & " orders",
"No sales records"
)
```
**Field JSON**:
```json
{
"type": "formula",
"name": "Sales Summary",
"expression": "IF([Sales].COUNTIF(CurrentValue.[SalespersonID] = [EmployeeID]) >= 1, \"Sold \" & [Sales].COUNTIF(CurrentValue.[SalespersonID] = [EmployeeID]) & \" orders\", \"No sales records\")"
}
```
**Explanation**: `[Sales].COUNTIF(...)` uses the entire Sales table as data range. CurrentValue represents each row in Sales, accessing `CurrentValue.[SalespersonID]` for that row's salesperson. `[EmployeeID]` refers to the current row in the Employees table (where the formula lives).
### Example 2: Chained cross-table access via link fields
**Table structure**:
- Orders: ID (`auto_number`), OrderItems (`link` [target: OrderItems, foreign key: ID])
- OrderItems: ID (`auto_number`), Product (`link` [target: Products, foreign key: ID])
- Products: ID (`auto_number`), ProductName (`text`)
**Current table**: Orders
**Requirement**: Deduplicate and comma-join all product names from linked order items.
**Formula**:
```
[OrderItems].[Product].[ProductName].UNIQUE().ARRAYJOIN(",")
```
**Field JSON**:
```json
{
"type": "formula",
"name": "Product List",
"expression": "[OrderItems].[Product].[ProductName].UNIQUE().ARRAYJOIN(\",\")"
}
```
**Explanation**: `[OrderItems]` gets linked order item records, `.[Product]` expands to each item's linked product, `.[ProductName]` gets all product names, `.UNIQUE()` deduplicates, `.ARRAYJOIN(",")` joins with commas.
### Example 3: Cross-table filter + sort
**Table structure**:
- Projects: ProjectName (Text), Status (Text), Owner (Text)
- Tasks: TaskName (Text), Project (Text), Priority (Number), DueDate (Date)
**Current table**: Projects
**Requirement**: Find the highest-priority (lowest number) task name for the current project.
**Formula**:
```
FIRST(
[Tasks].FILTER(CurrentValue.[Project] = [ProjectName]).SORTBY([Tasks].[Priority], TRUE).[TaskName]
)
```
**Field JSON**:
```json
{
"type": "formula",
"name": "Top Priority Task",
"expression": "FIRST([Tasks].FILTER(CurrentValue.[Project] = [ProjectName]).SORTBY([Tasks].[Priority], TRUE).[TaskName])"
}
```
**Explanation**: `[Tasks].FILTER(CurrentValue.[Project] = [ProjectName])` filters tasks belonging to the current project. `.SORTBY([Tasks].[Priority], TRUE)` sorts by priority ascending. `.[TaskName]` extracts task names. `FIRST(...)` gets the first one (highest priority).
---
## Section 14: Translating User Requirements to Formulas
When the user describes their formula need in natural language, follow these rules to convert it into a precise expression:
1. **Numbers must use precise values**: "less than 80%" → field value less than `0.8`. "above 1000" → `>= 1000`.
2. **Interval boundaries**: "above/below/within" = closed (inclusive); "less than/more than/outside" = open (exclusive).
3. **Branching logic** must be organized as an ordered list with a fallback branch. Each branch has a condition and output.
- Example: "return risk level for 1-3" → `IFS([Value] = 1, "low", [Value] = 2, "medium", [Value] = 3, "high")` with an `IFERROR` or trailing empty-string fallback.
4. **Multi-level branches must be flattened** to a single level. Nested if-else chains → flat IFS.
5. **Branch conditions must be mutually exclusive**. If the user's conditions overlap, rewrite to eliminate ambiguity.
6. **Reorder branches by logical priority** if the user's order is illogical (e.g., check specific conditions before catch-all).
---
## Section 15: Constraint Summary
- Request body must include `"type": "formula"` — this field is required
- Only use functions and operators listed in this document
- FILTER/SUMIF/COUNTIF/MAP must not be nested inside each other's conditions (chained calls are not nesting)
- Do not use LOOKUP — use FILTER exclusively
- Table and field names must exactly match `+table-get` output
- Strings must use double quotes `"`
- Format dates with TEXT before concatenating, to control output format
- SORTBY can only be chained and must include an output column
- Link fields return lists — aggregate or extract single values before output

View File

@ -0,0 +1,83 @@
# base +advperm-disable
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
停用指定 Base 的高级权限。停用后自定义角色等高级权限功能将不可用。
## 推荐命令
```bash
# 停用高级权限
lark-cli base +advperm-disable \
--base-token VwGhbYCXQaYGMzsWlEZcBbfMnod
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token27 位字母数字字符串 |
## API 入参详情
**HTTP 方法和路径:**
```
PUT /open-apis/base/v3/bases/:base_token/advperm/enable?enable=false
```
**Path 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `base_token` | 是 | Base 的唯一标识27 位字母数字字符串 |
**Query 参数:**
| 参数 | 必填 | 类型 | 说明 |
|------|------|------|------|
| `enable` | 是 | bool | 固定为 `false`,表示停用高级权限 |
## API 出参详情
**Response**
| 字段 | 类型 | 说明 |
|------|------|------|
| `code` | int32 | 错误码0 表示成功 |
| `message` | string | 错误信息 |
| `data` | string | 成功时为空 |
## 返回值
命令成功后输出 JSON
```json
{
"ok": true,
"data": {
"success": true
}
}
```
## 工作流
> [!CAUTION]
> 这是**高风险写入操作** — 停用高级权限会影响所有已配置的自定义角色,执行前必须向用户确认。
1. 向用户确认 `--base-token`,并提醒停用会影响已有角色配置
2. 执行命令
3. 确认返回 `code: 0` 表示停用成功
## 坑点
- ⚠️ **操作用户必须为 Base 管理员**:非管理员调用会返回权限错误
- ⚠️ **停用影响已有角色**:停用高级权限后,已创建的自定义角色将失效
- ⚠️ **API 路径版本**:本接口使用 `base/v3`,路径必须从原始文档提取,不要用 WebSearch 补全
- ⚠️ **data 字段是 JSON 字符串**:响应中 `data` 是 string 类型(非 object需要双重解析
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,80 @@
# base +advperm-enable
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
启用指定 Base 的高级权限。启用后可使用自定义角色等高级权限功能。
## 推荐命令
```bash
# 启用高级权限
lark-cli base +advperm-enable \
--base-token VwGh**************Mnod
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token27 位字母数字字符串 |
## API 入参详情
**HTTP 方法和路径:**
```
PUT /open-apis/base/v3/bases/:base_token/advperm/enable?enable=true
```
**Path 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `base_token` | 是 | Base 的唯一标识27 位字母数字字符串 |
**Query 参数:**
| 参数 | 必填 | 类型 | 说明 |
|------|------|------|------|
| `enable` | 是 | bool | 固定为 `true`,表示启用高级权限 |
## API 出参详情
**Response**
| 字段 | 类型 | 说明 |
|------|------|------|
| `code` | int32 | 错误码0 表示成功 |
| `message` | string | 错误信息 |
| `data` | string | 成功时为空 |
## 返回值
命令成功后输出 JSON
```json
{
"ok": true,
"data": {
"success": true
}
}
```
## 工作流
1. 向用户确认 `--base-token`
2. 执行命令
3. 确认返回 `code: 0` 表示启用成功
## 坑点
- ⚠️ **操作用户必须为 Base 管理员**:非管理员调用会返回权限错误
- ⚠️ **API 路径版本**:本接口使用 `base/v3`,路径必须从原始文档提取,不要用 WebSearch 补全
- ⚠️ **data 字段是 JSON 字符串**:响应中 `data` 是 string 类型(非 object需要双重解析
- ⚠️ **启用后才能管理角色**`+role-create / +role-update / +role-delete` 等角色操作需要先启用高级权限
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,74 @@
# base +base-copy
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
复制一个已有 Base可选只复制结构不复制内容。
## 推荐命令
```bash
lark-cli base +base-copy \
--base-token app_xxx \
--name "Copied Base"
lark-cli base +base-copy \
--base-token app_xxx \
--name "Copied Base" \
--folder-token fld_xxx \
--time-zone Asia/Shanghai \
--without-content
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | 源 Base Token |
| `--name <name>` | 否 | 新 Base 名称 |
| `--folder-token <token>` | 否 | 目标文件夹 token |
| `--time-zone <tz>` | 否 | 时区,如 `Asia/Shanghai` |
| `--without-content` | 否 | 只复制结构,不复制内容 |
## API 入参详情
**HTTP 方法和路径:**
```
POST /open-apis/base/v3/bases/:base_token/copy
```
## 返回重点
- 返回 `base`
- CLI 会额外标记 `copied: true`
- 回复结果时,必须主动返回新 Base 的可访问链接:
- 优先使用返回结果中的 `base.url`
- 同时返回新 Base 的 token
- 如果本次返回没有 `url`,至少返回新 Base 的名称和 token
> [!IMPORTANT]
> 如果 Base 是**以应用身份bot复制**出来的shortcut 会在复制成功后自动尝试为当前 CLI 用户添加该 Base 的 `full_access`(管理员)权限,并在输出中附带 `permission_grant` 字段。
>
> `permission_grant.status` 语义如下:
> - `granted`:当前 CLI 用户已获得该 Base 的管理员权限
> - `skipped`Base 已复制成功,但没有可授权的当前 CLI 用户,或复制结果缺少可授权 token
> - `failed`Base 已复制成功但自动授权失败结果中会包含失败原因用户可稍后重试授权或继续使用应用身份bot处理该 Base
>
> 回复复制结果时,除 `base token` 和可访问链接外,还必须明确告知用户 `permission_grant` 的结果。
>
> **仍然不要擅自执行 owner 转移。** 如果用户需要把 owner 转给自己,必须单独确认。
## 工作流
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。
1. 先确认源 Base Token。
2. `--name`、`--folder-token`、`--time-zone` 都是可选项;用户没要求时不要为这些可选参数额外追问。
3. 只要结构时,显式传 `--without-content`
4. 复制成功后,整理并返回:新 Base 名称、token以及响应中已有的可访问链接。
## 参考
- [lark-base-workspace.md](lark-base-workspace.md) — base / workspace 索引页
- [lark-base-base-create.md](lark-base-base-create.md) — 创建全新 Base

View File

@ -0,0 +1,68 @@
# base +base-create
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
创建一个新的 Base可选指定父文件夹和时区。
## 推荐命令
```bash
lark-cli base +base-create \
--name "New Base"
lark-cli base +base-create \
--name "项目管理" \
--folder-token fld_xxx \
--time-zone Asia/Shanghai
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--name <name>` | 是 | 新 Base 名称 |
| `--folder-token <token>` | 否 | 目标文件夹 token |
| `--time-zone <tz>` | 否 | 时区,如 `Asia/Shanghai` |
## API 入参详情
**HTTP 方法和路径:**
```
POST /open-apis/base/v3/bases
```
## 返回重点
- 返回 `base`
- CLI 会额外标记 `created: true`
- 回复结果时,必须主动返回新 Base 的可访问链接:
- 优先使用返回结果中的 `base.url`
- 同时返回新 Base 的 token
- 如果本次返回没有 `url`,至少返回新 Base 的名称和 token
> [!IMPORTANT]
> 如果 Base 是**以应用身份bot创建**的shortcut 会在创建成功后自动尝试为当前 CLI 用户添加该 Base 的 `full_access`(管理员)权限,并在输出中附带 `permission_grant` 字段。
>
> `permission_grant.status` 语义如下:
> - `granted`:当前 CLI 用户已获得该 Base 的管理员权限
> - `skipped`Base 已创建成功,但没有可授权的当前 CLI 用户,或创建结果缺少可授权 token
> - `failed`Base 已创建成功但自动授权失败结果中会包含失败原因用户可稍后重试授权或继续使用应用身份bot处理该 Base
>
> 回复创建结果时,除 `base token` 和可访问链接外,还必须明确告知用户 `permission_grant` 的结果。
>
> **仍然不要擅自执行 owner 转移。** 如果用户需要把 owner 转给自己,必须单独确认。
## 工作流
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。
1. 先确认 Base 名称。
2. `--folder-token`、`--time-zone` 都是可选项;用户没要求时不要为此额外追问。
3. 创建成功后整理并返回Base 名称、token以及响应中已有的可访问链接。
## 参考
- [lark-base-workspace.md](lark-base-workspace.md) — base / workspace 索引页
- [lark-base-base-copy.md](lark-base-base-copy.md) — 复制 Base

View File

@ -0,0 +1,39 @@
# base +base-get
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
读取一个 Base 的详情。
## 推荐命令
```bash
lark-cli base +base-get \
--base-token app_xxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token
```
## 返回重点
- 返回 `base`,通常包含 `base_token / name / url` 等信息。
## 坑点
- ⚠️ 先确认传入的是 `base_token`,不是 `workspace_token`
- ⚠️ 如果最初输入来自 `/wiki/...`,不要直接把 `wiki_token``--base-token`;若报 `param baseToken is invalid` / `base_token invalid`,先用 `lark-cli wiki spaces get_node``node.obj_token`,再重试 `+base-get`
## 参考
- [lark-base-workspace.md](lark-base-workspace.md) — base 索引页

View File

@ -0,0 +1,151 @@
# base CellValue 规范lark-base-cell-value
> 适用命令:`lark-cli base +record-upsert`、`lark-cli base +record-batch-create`、`lark-cli base +record-batch-update`
本文件定义 **shortcut 写记录**`CellValue` 的推荐格式,目标是让 AI 一次写对。不同命令的外层 JSON 形状不同,但每个 cell 都以本文为 source of truth。
## 1. 顶层规则(必须遵守)
- `--json` 必须是 JSON 对象。
- `+record-upsert`:顶层直接传字段映射:`{"字段名或字段ID": CellValue}`。
- `+record-batch-create``rows` 是 `CellValue[][]`,列顺序由 `fields` 决定。
- `+record-batch-update``patch` 是 `Map<FieldNameOrID, CellValue>`,同一份 `patch` 会应用到所有 `record_id_list`
- 一次 payload 里同一字段只用一种 key字段名或字段 ID不要重复。
- 写入前先 `+field-list` 获取字段 `type/style/multiple`,再构造值。
- 需要清空字段时优先传 `null`(字段允许清空时)。
## 2. 各类型 CellValue
### 2.1 text / phone / url
用字符串。URL 字段也传 URL 字符串;普通文本里可以保留 Markdown 风格链接文本,平台会按字段类型处理。
```json
{
"标题": "Hello",
"联系电话": "1380000000000",
"官网": "https://example.com"
}
```
### 2.2 number
用 JSON number不要用带单位或千分位的字符串。货币、百分比、进度、评分等数字类字段也按数字写入展示格式由字段配置决定。
```json
{
"工时": 12.5,
"预算": 3000,
"完成度": 0.65,
"评分": 4
}
```
### 2.3 select单选/多选)
单选用选项名字符串;多选用选项名数组。选项名建议与字段配置一致;写入未知选项时平台可能自动新增选项,因此不要把自然语言近义词当成已有选项传入。
```json
{
"单选": "Todo",
"多选": ["后端", "高优"]
}
```
### 2.4 datetime
优先用 `YYYY-MM-DD HH:mm:ss` 字符串,这是最稳妥的写法,也和常见 API 输出更容易对齐。不要写相对时间(如“明天上午”)。
```json
{
"截止时间": "2026-03-24 10:00:00"
}
```
### 2.5 checkbox
用 JSON boolean`true` 或 `false`,不要用 `"true"`、`"是"`、`1`。
```json
{
"已完成": true
}
```
### 2.6 user / group_chat
用对象数组,元素至少包含 `id`。人员字段传用户 ID`ou_xxx`),群字段传群 ID`oc_xxx`);单值/多值都统一使用数组。
> **人员字段:不要猜 ID。** 不知道 `open_id` 时,先用 `lark-contact` 查 id`lark-cli contact +search-user --query "<姓名/邮箱/手机号>" --as user`。
> **群组字段:不要猜 ID。** 不知道 `chat_id` 时,先用 `lark-im` 搜群:`lark-cli im +chat-search --query "<群名关键词>" --as user`;取结果里的 `oc_xxx`
```json
{
"负责人": [
{ "id": "ou_xxx" },
{ "id": "ou_xxx2" }
],
"协作群": [
{ "id": "oc_xxx" }
]
}
```
### 2.7 link
用对象数组,元素包含 `id`,值为目标记录的 `record_id`。不要传记录标题;先用 `+record-list` / `+record-search` 找到目标记录 ID。
```json
{
"关联任务": [
{ "id": "<record_id>" }
]
}
```
### 2.8 location
用对象 `{lng, lat}`,两者都是数字;`lng` 是经度,`lat` 是纬度。
```json
{
"坐标": {
"lng": 116.397428,
"lat": 39.90923
}
}
```
### 2.9 attachment不作为普通 CellValue 写入)
用户要把本地文件加到记录里时,必须使用 `lark-cli base +record-upload-attachment --file <path>` 上传到已有记录。不能用普通记录操作接口来上传附件。
`+record-get` 返回的附件字段单元格包含 `file_token` 和文件名,可以把 `file_token` 交给 `lark-cli docs +media-download` 进行附件下载。
## 3. 只读字段(不要写)
以下字段在写记录时应视为只读:
- `auto_number`
- `lookup`
- `formula`
- `created_at` / `updated_at`
- `created_by` / `updated_by`
写入只读字段通常不会更新数据;返回里可能出现 `ignored_fields`reason 会说明 `READONLY`。看到这种返回时,不要重试同一 payload应移除只读字段只写存储字段。
## 4. 完整示例
```json
{
"标题": "Created from shortcut",
"状态": "Todo",
"标签": ["高优", "外部依赖"],
"工时": 8,
"截止时间": "2026-03-24 10:00:00",
"已完成": false,
"负责人": [{ "id": "ou_123" }],
"关联任务": [{ "id": "rec_456" }],
"坐标": { "lng": 116.397428, "lat": 39.90923 }
}
```

View File

@ -0,0 +1,83 @@
# base +dashboard-arrange
> **前置条件:** 先阅读 [lark-base-dashboard.md](lark-base-dashboard.md) 了解整体工作流
自动重新排列仪表盘组件布局。服务端根据组件数量和类型进行智能布局优化。
## 使用场景
| 场景 | 说明 |
|------|------|
| **从 0 到 1 搭建后** | 使用 `+dashboard-create``+dashboard-block-create` 创建仪表盘后,默认布局可能不够工整美观,推荐使用本命令做一次整体重排 |
| **用户明确要求** | 用户主动要求对已有仪表盘进行布局重排或美化时 |
> [!CAUTION]
> - **不建议**在已有仪表盘上自动调用此命令,除非用户明确要求
> - 排列结果是**服务端智能推荐**,不一定完全符合用户预期
> - 无法指定具体位置(如"第一排放 A第二排放 B"),排列逻辑是**自适应**的
## 推荐命令
```bash
# 基础用法
lark-cli base +dashboard-arrange \
--base-token xxx \
--dashboard-id blk_xxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--dashboard-id <id>` | 是 | 仪表盘 ID |
| `--user-id-type <type>` | 否 | 用户 ID 类型open_id / union_id / user_id |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 返回示例
```json
{
"dashboard_id": "blk_xxx",
"name": "数据分析",
"blocks": [
{
"block_id": "chtbxxxx",
"block_name": "总销售额",
"block_type": "statistics",
"layout": {
"x": 0,
"y": 0,
"w": 6,
"h": 6
}
},
{
"block_id": "chtbcrxxxx",
"block_name": "月度趋势",
"block_type": "column",
"layout": {
"x": 6,
"y": 0,
"w": 6,
"h": 6
}
}
],
"arranged": true
}
```
## 返回重点
| 字段 | 说明 |
|------|------|
| `blocks[].layout` | 重排后的布局信息,包含 x/y/w/h |
| `arranged` | 是否重排成功 |
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。
## 参考
- [lark-base-dashboard.md](lark-base-dashboard.md) — dashboard 模块指引

View File

@ -0,0 +1,108 @@
# base +dashboard-block-create
> **前置条件:** 先阅读 [lark-base-dashboard.md](lark-base-dashboard.md) 了解整体工作流
> **关键:** 创建前必须阅读 [dashboard-block-data-config.md](dashboard-block-data-config.md) 了解组件类型和 data_config 结构
在仪表盘中创建一个组件Block
## 关键约束
- **`type` 创建后不可修改**,创建时务必选对
- **`data_config` 结构随 `type` 变化**,不同组件类型字段不同,**⚠️ 必须阅读 [dashboard-block-data-config.md](dashboard-block-data-config.md) 了解如何构造**
- **组件创建必须串行执行**,不能并发
## 推荐命令
```bash
# 简单示例:创建一个指标卡(统计记录数)
lark-cli base +dashboard-block-create \
--base-token xxx \
--dashboard-id blk_xxx \
--name "总记录数" \
--type statistics \
--data-config '{"table_name":"订单表","count_all":true}'
# 文本组件示例Markdown 富文本)
lark-cli base +dashboard-block-create \
--base-token xxx \
--dashboard-id blk_xxx \
--name "说明文字" \
--type text \
--data-config '{"text":"# 标题\n## 副标题\n**加粗** *斜体* ~~删除~~\n1. 列表1\n2. 列表2"}'
# 复杂配置用文件传入
lark-cli base +dashboard-block-create \
--base-token xxx \
--dashboard-id blk_xxx \
--name "销售额趋势" \
--type line \
--data-config @config.json
```
完整流程参考 [lark-base-dashboard.md](lark-base-dashboard.md) 的「场景 1从 0 到 1 创建仪表盘」
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--dashboard-id <id>` | 是 | 仪表盘 ID`+dashboard-list/get` 获取) |
| `--name <name>` | **是** | 组件名称(允许重名) |
| `--type <type>` | **是** | 组件类型,见下方枚举值。**不同 type 对应不同的 data_config 结构**,常用:`column`(柱状图)、`line`(折线图)、`pie`(饼图)、`statistics`(指标卡)、`text`(文本) |
| `--data-config <json>` | 否 | 数据配置 JSON**结构随 type 变化**。**⚠️ 必须阅读 [dashboard-block-data-config.md](dashboard-block-data-config.md) 了解如何构造**。创建时会做本地校验,更新时由后端校验 |
| `--user-id-type <type>` | 否 | 用户 ID 类型filter 涉及人员字段时使用 |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
### type 枚举值
| 值 | 说明 |
|----|------|
| `column` | 柱状图 |
| `bar` | 条形图 |
| `line` | 折线图 |
| `pie` | 饼图 |
| `ring` | 环形图 |
| `area` | 面积图 |
| `combo` | 组合图 |
| `scatter` | 散点图 |
| `funnel` | 漏斗图 |
| `wordCloud` | 词云 |
| `radar` | 雷达图 |
| `statistics` | 指标卡 |
| `text` | 文本(支持 Markdown |
## 返回示例
```json
{
"block": {
"block_id": "chtxxxxxxxx",
"name": "总记录数",
"type": "statistics",
"data_config": {
"table_name": "电商交易明细",
"count_all": true
}
},
"created": true
}
```
## 返回重点
| 字段 | 说明 |
|------|------|
| `block.block_id` | 组件 ID后续编辑/删除需要用到,务必记录 |
| `block.name` | 组件名称 |
| `block.type` | 组件类型 |
| `block.data_config` | 实际创建的数据配置(可能包含后端自动添加的默认值)|
| `created` | 是否创建成功 |
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。
## 参考
- [lark-base-dashboard.md](lark-base-dashboard.md) — dashboard 模块指引
- [dashboard-block-data-config.md](dashboard-block-data-config.md) — data_config 结构、图表类型、filter 规则

View File

@ -0,0 +1,46 @@
# base +dashboard-block-delete
> **前置条件:** 先阅读 [lark-base-dashboard.md](lark-base-dashboard.md) 了解整体工作流。
删除仪表盘中的一个组件Block不可恢复。
## 推荐命令
```bash
lark-cli base +dashboard-block-delete \
--base-token bascn***************CtadY \
--dashboard-id blkxxx \
--block-id chtxxxxxxxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--dashboard-id <id>` | 是 | 仪表盘 ID |
| `--block-id <id>` | 是 | Block ID |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 返回示例
```json
{
"block_id": "chtxxxxxxxx",
"deleted": true
}
```
## 返回重点
| 字段 | 说明 |
|------|------|
| `block_id` | 被删除的组件 ID |
| `deleted` | 是否删除成功 |
> [!CAUTION]
> 这是**写入操作**且**不可逆** — 执行前必须向用户确认。
## 参考
- [lark-base-dashboard.md](lark-base-dashboard.md) — dashboard 模块指引

View File

@ -0,0 +1,57 @@
# base +dashboard-block-get
> **前置条件:** 先阅读 [lark-base-dashboard.md](lark-base-dashboard.md) 了解整体工作流。
获取仪表盘中单个组件的详情(包含 data_config 完整配置。常用于1) 查看组件的完整配置2) 编辑组件前了解当前配置。
## 推荐命令
```bash
lark-cli base +dashboard-block-get \
--base-token bascn***************CtadY \
--dashboard-id blkxxx \
--block-id chtxxxxxxxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--dashboard-id <id>` | 是 | 仪表盘 ID |
| `--block-id <id>` | 是 | Block ID |
| `--user-id-type <type>` | 否 | 用户 ID 类型open_id / union_id / user_id |
| `--format <fmt>` | 否 | 输出格式 |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 返回示例
```json
{
"block": {
"block_id": "chtxxxxxxxx",
"name": "柱状图",
"type": "column",
"data_config": {
"table_name": "电商交易明细",
"series": [{"field_name": "营销费用", "rollup": "SUM"}],
"group_by": [{"field_name": "品类", "mode": "integrated"}]
},
"layout": {"x": 0, "y": 0, "w": 6, "h": 4}
}
}
```
## 返回重点
| 字段 | 说明 |
|------|-------------------------------|
| `block.block_id` | 组件 ID |
| `block.name` | 组件名称 |
| `block.type` | 组件类型(如 `column`/`line`/`pie` |
| `block.data_config` | 数据配置(新建/编辑组件时可基于此字段修改) |
| `block.layout` | 布局信息只读x/y/w/h 坐标和尺寸) |
## 参考
- [lark-base-dashboard.md](lark-base-dashboard.md) — dashboard 模块指引

View File

@ -0,0 +1,53 @@
# base +dashboard-block-list
> **前置条件:** 先阅读 [lark-base-dashboard.md](lark-base-dashboard.md) 了解整体工作流。
分页列出仪表盘中的所有组件Block。常用于1) 查看仪表盘有哪些组件2) 获取组件 ID 和类型用于后续编辑/删除。
## 推荐命令
```bash
lark-cli base +dashboard-block-list \
--base-token bascn***************CtadY \
--dashboard-id blkxxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--dashboard-id <id>` | 是 | 仪表盘 ID |
| `--page-size <n>` | 否 | 每页数量,默认 20最大 100 |
| `--page-token <token>` | 否 | 分页标记 |
| `--format <fmt>` | 否 | 输出格式json / pretty / table / csv / ndjson |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 返回示例
```json
{
"items": [
{"block_id": "chtxxxxxxxx", "name": "图表", "type": "column"},
{"block_id": "chtxxxxxxxx", "name": "总利润", "type": "statistics"}
],
"total": 4,
"has_more": false
}
```
## 返回重点
| 字段 | 说明 |
|------|------|
| `items` | 组件列表,每项包含 `block_id`ID、`name`(名称)、`type`(类型)|
| `total` | 组件总数 |
| `has_more` | 是否有更多组件(为 `true` 时需用 `page_token` 继续获取)|
## 坑点
- `+dashboard-block-list` 禁止并发调用;批量执行时只能串行。
## 参考
- [lark-base-dashboard.md](lark-base-dashboard.md) — dashboard 模块指引

View File

@ -0,0 +1,84 @@
# base +dashboard-block-update
> **前置条件:** 先阅读 [lark-base-dashboard.md](lark-base-dashboard.md) 了解整体工作流
> **关键:** 更新前必须阅读 [dashboard-block-data-config.md](dashboard-block-data-config.md) 了解 data_config 结构和更新规则
更新仪表盘中组件的名称或数据配置。
## 关键约束
- **不可修改 `type` 和 `layout`** — 只能更新 `name``data_config`
- **`data_config` 顶层按 key merge** — 只需传入要修改的顶层字段,未传的字段保留原值;但每个字段内部是全量替换(如传新 `filter` 会完整覆盖旧 `filter`)。
- **`series``count_all` 二选一** — 且至少提供其一。
- **表名用 name不是 ID**`table_name` 对应的是表名称(如「订单表」),不是 `table_id`
- **`user_id_type`** 仅在 filter 涉及人员字段时有意义。
> [!TIP]
> CLI 默认会对 `data_config` 做轻量校验与规范化;如需兼容特殊场景,可加 `--no-validate` 跳过。
## 推荐命令
```bash
# 示例 1更新组件名称
lark-cli base +dashboard-block-update \
--base-token xxx \
--dashboard-id blk_xxx \
--block-id chtxxxxxxxx \
--name "新名称"
# 示例 2更新数据配置只传要改的字段未传字段保留原值
lark-cli base +dashboard-block-update \
--base-token xxx \
--dashboard-id blk_xxx \
--block-id chtxxxxxxxx \
--data-config '{"filter":{"conjunction":"and","conditions":[{"field_name":"状态","operator":"is","value":"已完成"}]}}'
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--dashboard-id <id>` | 是 | 仪表盘 ID |
| `--block-id <id>` | 是 | Block ID |
| `--name <name>` | 否 | 新名称 |
| `--data-config <json>` | 否 | 数据配置 JSON。**结构随 block 的 `type` 变化**。**⚠️ 必须阅读 [dashboard-block-data-config.md](dashboard-block-data-config.md) 了解如何构造** |
| `--user-id-type <type>` | 否 | 用户 ID 类型filter 涉及人员字段时使用 |
| `--no-validate` | 否 | 跳过 data_config 本地校验(用于兼容特殊场景) |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 返回示例
```json
{
"block": {
"block_id": "chtxxxxxxxx",
"name": "新名称",
"type": "column",
"data_config": {
"table_name": "订单表",
"series": [{"field_name": "金额", "rollup": "SUM"}],
"group_by": [{"field_name": "类别", "mode": "integrated"}]
}
},
"updated": true
}
```
## 返回重点
| 字段 | 说明 |
|------|------|
| `block.block_id` | 组件 ID |
| `block.name` | 更新后的名称 |
| `block.type` | 组件类型(不可修改)|
| `block.data_config` | 更新后的数据配置 |
| `updated` | 是否更新成功 |
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。
## 参考
- [lark-base-dashboard.md](lark-base-dashboard.md) — dashboard 模块指引
- [dashboard-block-data-config.md](dashboard-block-data-config.md) — data_config 结构、图表类型、filter 规则

View File

@ -0,0 +1,73 @@
# base +dashboard-create
> **前置条件:** 先阅读 [lark-base-dashboard.md](lark-base-dashboard.md) 了解整体工作流。
创建空白仪表盘。创建成功后务必记录返回的 `dashboard_id`,后续添加组件和管理仪表盘都需要用到。
## 关键约束
- **dashboard_id** 在 create 返回中取得,后续 get/update/delete 使用。
## 推荐命令
```bash
# 创建仪表盘
lark-cli base +dashboard-create \
--base-token VwGhb**************fMnod \
--name "销售报表"
# 创建仪表盘(指定主题)
lark-cli base +dashboard-create \
--base-token VwGhb**************fMnod \
--name "销售报表" \
--theme-style default
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--name <name>` | 是 | 仪表盘名称 |
| `--theme-style <style>` | 否 | 主题风格(见下方枚举) |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
### theme-style 枚举
| 值 | 说明 |
|------|------|
| `default` | 默认主题 |
| `SimpleBlue` | 简约蓝 |
| `DarkGreen` | 深绿 |
| `summerBreeze` | 夏日微风 |
| `simplistic` | 简洁 |
| `energetic` | 活力 |
| `deepDark` | 深色 |
| `futuristic` | 未来感 |
## 返回示例
```json
{
"dashboard_id": "blkxxxxxxxxxxxx",
"name": "数据分析仪表盘",
"theme": {
"theme_style": "default"
}
}
```
## 返回重点
| 字段 | 说明 |
|------|------|
| `dashboard_id` | 仪表盘 ID`blkxxxxxxxxxxxx`),后续操作都需要用到,务必记录 |
| `name` | 仪表盘名称 |
| `theme.theme_style` | 主题风格 |
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。
## 参考
- [lark-base-dashboard.md](lark-base-dashboard.md) — dashboard 模块指引

View File

@ -0,0 +1,44 @@
# base +dashboard-delete
> **前置条件:** 先阅读 [lark-base-dashboard.md](lark-base-dashboard.md) 了解整体工作流。
删除仪表盘(会同时删除其下所有组件,不可恢复)。
## 推荐命令
```bash
lark-cli base +dashboard-delete \
--base-token VwGhb**************fMnod \
--dashboard-id blkxxxxxxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--dashboard-id <id>` | 是 | 仪表盘 ID |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 返回示例
```json
{
"dashboard_id": "blkxxxxxxxxxxxx",
"deleted": true
}
```
## 返回重点
| 字段 | 说明 |
|------|------|
| `dashboard_id` | 被删除的仪表盘 ID |
| `deleted` | 是否删除成功 |
> [!CAUTION]
> 这是**写入操作**且**不可逆** — 执行前必须向用户确认。删除仪表盘会同时删除其下所有组件,不可恢复。
## 参考
- [lark-base-dashboard.md](lark-base-dashboard.md) — dashboard 模块指引

View File

@ -0,0 +1,59 @@
# base +dashboard-get
> **前置条件:** 先阅读 [lark-base-dashboard.md](lark-base-dashboard.md) 了解整体工作流。
获取仪表盘详情名称、主题配置、包含的所有组件列表。常用于1) 查看仪表盘有哪些组件2) 获取组件 ID 用于后续编辑/删除。
## 推荐命令
```bash
lark-cli base +dashboard-get \
--base-token VwGhb**************fMnod \
--dashboard-id blkxxxxxxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--dashboard-id <id>` | 是 | 仪表盘 ID |
| `--format <fmt>` | 否 | 输出格式 |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 返回示例
```json
{
"dashboard_id": "blkxxxxxxxxxxxx",
"name": "数据分析仪表盘",
"theme": {
"theme_style": "default"
},
"blocks": [
{
"block_id": "chtxxxxxxxx",
"block_name": "总净利润",
"block_type": "statistics"
},
{
"block_id": "chtxxxxxxxx",
"block_name": "品类占比",
"block_type": "pie"
}
]
}
```
## 返回重点
| 字段 | 类型 | 说明 |
|------|------|------|
| `dashboard_id` | string | 仪表盘 ID`blkxxxxxxxxxxxx`|
| `name` | string | 仪表盘名称 |
| `theme.theme_style` | string | 主题风格:`default` / `SimpleBlue` / `DarkGreen` / `summerBreeze` / `simplistic` / `energetic` / `deepDark` / `futuristic` |
| `blocks` | []object | 组件列表,每项包含 `block_id`组件ID、`block_name`(名称)、`block_type`(类型,如 `column`/`line`/`pie`|
## 参考
- [lark-base-dashboard.md](lark-base-dashboard.md) — dashboard 模块指引

View File

@ -0,0 +1,52 @@
# base +dashboard-list
> **前置条件:** 先阅读 [lark-base-dashboard.md](lark-base-dashboard.md) 了解整体工作流。
分页列出一个 Base 下的所有仪表盘。常用于1) 查看当前有哪些仪表盘2) 获取 dashboard_id 用于后续操作(如添加组件、查看详情)。
## 关键约束
- `+dashboard-list` 禁止并发调用;批量列多个 Base 时必须串行。
## 推荐命令
```bash
lark-cli base +dashboard-list \
--base-token VwGhb**************fMnod
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--page-size <n>` | 否 | 每页数量 |
| `--page-token <token>` | 否 | 分页标记 |
| `--format <fmt>` | 否 | 输出格式json / pretty / table / csv / ndjson |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 返回示例
```json
{
"items": [
{"dashboard_id": "blkxxxxxxxxxxxx", "name": "商品总览仪表盘"},
{"dashboard_id": "blkxxxxxxxxxxxx", "name": "订单总览仪表盘"},
{"dashboard_id": "blkxxxxxxxxxxxx", "name": "销售数据分析仪表盘"}
],
"total": 3,
"has_more": false
}
```
## 返回重点
| 字段 | 说明 |
|------|------|
| `items` | 仪表盘列表,每项包含 `dashboard_id`ID`name`(名称)|
| `total` | 总数 |
| `has_more` | 是否有下一页(为 `true` 时需用 `page_token` 继续获取)|
## 参考
- [lark-base-dashboard.md](lark-base-dashboard.md) — dashboard 模块指引

View File

@ -0,0 +1,69 @@
# base +dashboard-update
> **前置条件:** 先阅读 [lark-base-dashboard.md](lark-base-dashboard.md) 了解整体工作流。
更新仪表盘名称或主题。
## 推荐命令
```bash
lark-cli base +dashboard-update \
--base-token VwGhb**************fMnod \
--dashboard-id blkxxxxxxx \
--name "新名称" \
--theme-style default
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--dashboard-id <id>` | 是 | 仪表盘 ID |
| `--name <name>` | 否 | 新名称 |
| `--theme-style <style>` | 否 | 主题风格(见下方枚举) |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
### theme-style 枚举
| 值 | 说明 |
|------|------|
| `default` | 默认主题 |
| `SimpleBlue` | 简约蓝 |
| `DarkGreen` | 深绿 |
| `summerBreeze` | 夏日微风 |
| `simplistic` | 简洁 |
| `energetic` | 活力 |
| `deepDark` | 深色 |
| `futuristic` | 未来感 |
## 返回示例
```json
{
"dashboard": {
"dashboard_id": "blkxxxxxxxxxxxx",
"name": "新名称",
"theme": {
"theme_style": "default"
}
},
"updated": true
}
```
## 返回重点
| 字段 | 说明 |
|------|------|
| `dashboard` | 更新后的仪表盘对象 |
| `dashboard.name` | 新名称(如果更新了)|
| `dashboard.theme.theme_style` | 新主题(如果更新了)|
| `updated` | 是否更新成功 |
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。
## 参考
- [lark-base-dashboard.md](lark-base-dashboard.md) — dashboard 模块指引

View File

@ -0,0 +1,240 @@
# Dashboard仪表盘/数据看板)模块指引
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
Dashboard 是 Base 中的数据可视化看板,可以把表格数据变成**组件**(图表、指标卡等)进行展示。
## 核心概念
- **Dashboard仪表盘**:容器,包含多个组件
- **Block组件**:仪表盘中的单个可视化元素(柱状图、折线图、饼图、指标卡等)
- **data_config**:组件的数据源配置(表名、字段、分组等)
## 能力速览
| 你想做什么 | 用这些命令 | 关键文档 |
|------|-----------|---------|
| 创建/删除/改名称 | `+dashboard-create/delete/update` | 本页下方「仪表盘管理」 |
| 在仪表盘里添加组件 | `+dashboard-block-create` | 先读 [lark-base-dashboard-block-create.md](lark-base-dashboard-block-create.md),再读 [dashboard-block-data-config.md](dashboard-block-data-config.md) |
| 修改组件 | `+dashboard-block-update` | 先读 [lark-base-dashboard-block-update.md](lark-base-dashboard-block-update.md),再读 [dashboard-block-data-config.md](dashboard-block-data-config.md) |
| 查看仪表盘有哪些组件 | `+dashboard-get``+dashboard-block-list` | 本页下方「查看仪表盘」 |
| 智能重排组件布局 | `+dashboard-arrange` | [lark-base-dashboard-arrange.md](lark-base-dashboard-arrange.md) |
## 典型场景工作流
### 场景 1从 0 到 1 创建仪表盘
示例:搭建一个销售数据分析仪表盘
```bash
# 第 1 步:创建空白仪表盘
lark-cli base +dashboard-create --base-token xxx --name "销售数据分析"
# 记录返回的 dashboard_id
# 第 2 步:获取数据源信息
lark-cli base +table-list --base-token xxx
lark-cli base +field-list --base-token xxx --table-id tbl_xxx
# 第 3 步:规划应该创建哪些组件(根据用户需求确定组件类型和数量)
# 例如:总销售额(指标卡)、月度趋势(折线图)、品类占比(饼图)
# 第 4 步:顺序创建每个组件(必须串行执行,不能并发)
# 重要:创建组件前,先阅读 [lark-base-dashboard-block-create.md](lark-base-dashboard-block-create.md) 了解命令参数
# 再阅读 [dashboard-block-data-config.md](dashboard-block-data-config.md) 了解 data_config 结构、组件类型和 filter 规则
# 第 1 个组件
lark-cli base +dashboard-block-create \
--base-token xxx \
--dashboard-id blk_xxx \
--name "总销售额" \
--type statistics \
--data-config '{"table_name":"订单表","series":[{"field_name":"金额","rollup":"SUM"}]}'
# 第 2 个组件(等上一个完成后再执行)
lark-cli base +dashboard-block-create \
--base-token xxx \
--dashboard-id blk_xxx \
--name "月度趋势" \
--type line \
--data-config '{"table_name":"订单表","series":[{"field_name":"金额","rollup":"SUM"}],"group_by":[{"field_name":"月份","mode":"integrated"}]}'
# 继续创建其他组件...
# 第 5 步:组件创建完成后,使用 arrange 命令智能重排布局(可选但推荐)
# 默认布局可能不够美观arrange 会根据组件数量和类型自动优化布局
lark-cli base +dashboard-arrange \
--base-token xxx \
--dashboard-id blk_xxx
```
### 场景 2在已有仪表盘上添加新组件
```bash
# 第 1 步:列出仪表盘,定位到当前仪表盘
lark-cli base +dashboard-list --base-token xxx
# 获取目标 dashboard_id
# 第 2 步:根据用户诉求规划组件类型和数据源
# 建议先查看当前仪表盘已有组件,避免重复创建,或作为参考
lark-cli base +dashboard-get --base-token xxx --dashboard-id blk_xxx
# 第 3 步:获取数据源信息
lark-cli base +table-list --base-token xxx
lark-cli base +field-list --base-token xxx --table-id tbl_xxx
# 第 4 步:顺序创建每个新组件(必须串行执行,不能并发)
# 重要:先阅读 [lark-base-dashboard-block-create.md](lark-base-dashboard-block-create.md) 了解命令参数
# 再阅读 [dashboard-block-data-config.md](dashboard-block-data-config.md) 了解 data_config 结构
lark-cli base +dashboard-block-create \
--base-token xxx \
--dashboard-id blk_xxx \
--name "新组件名" \
--type column \
--data-config '{...}'
```
### 场景 3编辑已有组件
> [!IMPORTANT]
> `+dashboard-block-update` **不能修改组件的 `type`**(图表类型),只能更新 `name``data_config`
> 如需更换组件类型,必须先删除再重新创建。
```bash
# 第 1 步:列出仪表盘,定位到当前仪表盘
lark-cli base +dashboard-list --base-token xxx
# 第 2 步:列出组件,获取到目标组件
lark-cli base +dashboard-block-list --base-token xxx --dashboard-id blk_xxx
# 获取目标 block_id
# 提示:查看已有组件可作为参考,或检查是否重复创建相似组件
# 第 3 步:获取组件当前详情
lark-cli base +dashboard-block-get --base-token xxx --dashboard-id blk_xxx --block-id chtxxxxxxxx
# 第 4 步:根据用户编辑诉求准备更新
# 如果编辑诉求涉及数据源变更,需要先获取数据源信息
lark-cli base +table-list --base-token xxx
lark-cli base +field-list --base-token xxx --table-id tbl_xxx
# 第 5 步:执行更新
# 重要:先阅读 [lark-base-dashboard-block-update.md](lark-base-dashboard-block-update.md) 了解命令参数
# 再阅读 [dashboard-block-data-config.md](dashboard-block-data-config.md) 了解 data_config 更新规则
lark-cli base +dashboard-block-update \
--base-token xxx \
--dashboard-id blk_xxx \
--block-id chtxxxxxxxx \
--data-config '{...}'
```
### 场景 4重排仪表盘布局
当用户明确要求对已有仪表盘进行布局重排或美化时使用。
> [!CAUTION]
> - 排列结果是**服务端智能推荐**,不一定完全符合用户预期
> - 无法指定具体位置(如"第一排放 A第二排放 B"),排列逻辑是**自适应**的
> - **不建议**在已有仪表盘上自动调用,除非用户明确要求
```bash
# 第 1 步:列出仪表盘,定位到目标仪表盘
lark-cli base +dashboard-list --base-token xxx
# 第 2 步:执行智能重排
lark-cli base +dashboard-arrange \
--base-token xxx \
--dashboard-id blk_xxx
```
### 场景 5读取仪表盘或组件现状
**选择查询方式:**
- 想看仪表盘整体结构(含主题、所有组件名称和类型)→ 用 **方式 A**
- 只想快速查看有哪些组件 → 用 **方式 B**
- 想看某个组件的详细 data_config 配置 → 用 **方式 C**
```bash
# 第 1 步:列出仪表盘,定位到当前仪表盘
lark-cli base +dashboard-list --base-token xxx
# 第 2 步:根据用户诉求查看详情
# 方式 A查看仪表盘整体情况包含所有组件列表
lark-cli base +dashboard-get --base-token xxx --dashboard-id blk_xxx
# 方式 B列出所有组件
lark-cli base +dashboard-block-list --base-token xxx --dashboard-id blk_xxx
# 方式 C查看某个组件的详细配置
lark-cli base +dashboard-block-get --base-token xxx --dashboard-id blk_xxx --block-id chtxxxxxxxx
# 最后:把获取到的现状信息整理好告诉用户
```
## 组件类型选择
组件 `type` 决定展示形式:
| 用户想看什么 | 选什么 type | 说明 |
|-------------|------------|------|
| 数据趋势(时间变化) | line | 折线图组件 |
| 类别比较(谁高谁低) | column | 柱状图组件 |
| 占比分布(各部分比例) | pie | 饼图组件 |
| 单个关键指标 | statistics | 指标卡组件 |
| 富文本说明/标题/注释 | text | 文本组件(支持 Markdown |
详细组件类型和 data_config 完整规则:[dashboard-block-data-config.md](dashboard-block-data-config.md)
## 常见问题
**Q: 创建组件的命令和 data_config 怎么写?**
A:
1. 先读 [lark-base-dashboard-block-create.md](lark-base-dashboard-block-create.md) 了解 `--name`、`--type`、`--data-config` 等参数
2. 再读 [dashboard-block-data-config.md](dashboard-block-data-config.md) 了解:
- 全部组件类型的可复制模板
- filter 筛选条件格式
- 字段类型与操作符对应表
**Q: 为什么组件创建失败了?**
A: 常见原因:
- `table_name` 用了 table_id 而不是表名(必须用表名称,如「订单表」)
- `series``count_all` 同时存在(必须二选一,互斥)
- 字段名拼写错误(必须用 `+field-list` 获取的真实字段名,禁止猜测)
- 组件创建并发执行(必须串行,等上一个完成再执行下一个)
**Q: 可以一次创建多个组件吗?**
A: 不可以,必须串行执行。等上一个 `+dashboard-block-create` 完成后再执行下一个。
**Q: 组件的 `type` 创建后能改吗?**
A: 不能。`+dashboard-block-update` 只能修改 `name``data_config`,不能修改 `type`
**Q: 更新组件的命令和 data_config 怎么写?**
A:
1. 先读 [lark-base-dashboard-block-update.md](lark-base-dashboard-block-update.md) 了解更新参数
2. 再读 [dashboard-block-data-config.md](dashboard-block-data-config.md) 了解 data_config 结构
**data_config 更新策略(顶层 key merge**
- 只传入需要修改的顶层字段(如 `series`、`filter`
- 未传的顶层字段(如 `group_by`)自动保留原值
- 但每个传入的字段内部是**全量替换**(如传新 `filter` 会完整覆盖旧 `filter`
**Q: 查看已有组件有什么用?**
A: 在「添加新组件」或「编辑组件」前查看已有组件可以:
- 了解当前仪表盘已有哪些可视化
- 避免重复创建相似的组件
- 参考已有组件的 data_config 结构作为模板
## 命令详细文档
| CLI 命令 | 说明 | 详细文档 |
|----------|------|----------|
| `+dashboard-list` | 列出所有仪表盘 | [lark-base-dashboard-list.md](lark-base-dashboard-list.md) |
| `+dashboard-get` | 获取仪表盘详情(含所有组件)| [lark-base-dashboard-get.md](lark-base-dashboard-get.md) |
| `+dashboard-create` | 创建仪表盘 | [lark-base-dashboard-create.md](lark-base-dashboard-create.md) |
| `+dashboard-update` | 修改仪表盘 | [lark-base-dashboard-update.md](lark-base-dashboard-update.md) |
| `+dashboard-delete` | 删除仪表盘 | [lark-base-dashboard-delete.md](lark-base-dashboard-delete.md) |
| `+dashboard-arrange` | 智能重排布局 | [lark-base-dashboard-arrange.md](lark-base-dashboard-arrange.md) |
| `+dashboard-block-list` | 列出组件 | [lark-base-dashboard-block-list.md](lark-base-dashboard-block-list.md) |
| `+dashboard-block-get` | 获取单个组件详情 | [lark-base-dashboard-block-get.md](lark-base-dashboard-block-get.md) |
| `+dashboard-block-create` | 创建组件 | [lark-base-dashboard-block-create.md](lark-base-dashboard-block-create.md) |
| `+dashboard-block-update` | 更新组件 | [lark-base-dashboard-block-update.md](lark-base-dashboard-block-update.md) |
| `+dashboard-block-delete` | 删除组件 | [lark-base-dashboard-block-delete.md](lark-base-dashboard-block-delete.md) |

View File

@ -0,0 +1,417 @@
# base +data-query
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
对多维表格数据进行聚合查询(分组、过滤、排序、聚合计算),基于以下语法的 JSON DSL
## 限制
- **权限要求**(按文档类型分流):
- **普通多维表格**:调用者拥有文档的**阅读权限**即可
- **高级权限多维表格**:调用者必须是文档管理员,拥有 **FAFull Access / 完全访问权限)**
权限不足时返回权限错误。
## 推荐命令
```bash
# 按字段分组计数
lark-cli base +data-query \
--base-token MAGObxxxxx \
--dsl '{
"datasource": {"type": "table", "table": {"tableId": "tblxxxxxxxx"}},
"dimensions": [{"field_name": "城市", "alias": "dim_city"}],
"measures": [{"field_name": "城市", "aggregation": "count", "alias": "count"}],
"shaper": {"format": "flat"}
}'
# 带过滤条件 + 排序 + 限制条数
lark-cli base +data-query \
--base-token MAGObxxxxx \
--dsl '{
"datasource": {"type": "table", "table": {"tableId": "tblxxxxxxxx"}},
"dimensions": [{"field_name": "城市", "alias": "dim_city"}],
"measures": [{"field_name": "金额", "aggregation": "sum", "alias": "total_amount"}],
"filters": {
"type": 1,
"conjunction": "and",
"conditions": [{"field_name": "城市", "operator": "isNot", "value": [""]}]
},
"sort": [{"field_name": "total_amount", "order": "desc"}],
"pagination": {"limit": 100},
"shaper": {"format": "flat"}
}'
# 使用 tableName表名代替 tableId
lark-cli base +data-query \
--base-token MAGObxxxxx \
--dsl '{
"datasource": {"type": "table", "table": {"tableName": "销售数据"}},
"measures": [{"field_name": "金额", "aggregation": "sum", "alias": "total"}],
"shaper": {"format": "flat"}
}'
```
## 参数
| 参数 | 必填 | 说明 |
|------------------------|------|------|
| `--base-token <token>` | 是 | Base Tokenbase_token |
| `--dsl <json>` | 是 | LiteQuery Protocol JSON DSL 查询语句 |
## 如何从链接中提取参数
用户通常会提供如下 URL
```
https://example.feishu.cn/base/<base_token>?table=<table_id>
```
- `--base-token`:取 `/base/` 后面的字符串
- DSL 中的 `tableId`:取 `table=` 后面的值
## API 入参详情
**HTTP 方法和路径:**
```
POST /open-apis/base/v3/bases/:base_token/data/query
```
**Path 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `base_token` | 是 | Base Token |
**Request Body — DSL 结构:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `datasource` | object | 是 | 数据源,包含 `type`(固定 `"table"`)和 `table` 对象 |
| `datasource.table.tableId` | string | 二选一 | 目标数据表 ID |
| `datasource.table.tableName` | string | 二选一 | 目标数据表名称 |
| `dimensions` | Dimension[] | 否* | 分组维度字段GROUP BY |
| `measures` | Measure[] | 否* | 聚合度量字段 |
| `filters` | FilterGroup | 否 | 过滤条件WHERE |
| `sort` | Sort[] | 否 | 排序规则 |
| `pagination` | object | 否 | 限制返回行数,`{limit: N}`,最大 5000 |
| `shaper` | object | 否 | 结果格式,固定 `{format: "flat"}` |
> \* `dimensions``measures` 至少填写一个。
**Dimension 字段:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `field_name` | string | 是 | 字段名称 |
| `alias` | string | 否 | 输出列别名,需全局唯一 |
**Measure 字段:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `field_name` | string | 是 | 字段名称 |
| `aggregation` | string | 是 | 聚合函数:`sum`、`avg`、`min`、`max`、`count`、`count_all`、`distinct_count` |
| `alias` | string | 否 | 输出列别名,需全局唯一 |
**聚合函数适用字段类型:**
| 聚合函数 | 适用字段类型 |
|----------|-------------|
| `sum` / `avg` | `number` |
| `min` / `max` | `number`、`datetime` |
| `count` | 全字段适用,计数非空值 |
| `count_all` | 全字段适用,计数所有行 |
| `distinct_count` | 全字段适用 |
> `number` 包含 `style.type``progress` / `currency` / `rating` 等所有子类型。
**FilterGroup**
```json
{
"filters": {
"type": 1,
"conjunction": "and",
"conditions": [
{"field_name": "城市", "operator": "is", "value": ["北京"]}
]
}
}
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `type` | int | 是 | 固定填 `1` |
| `conjunction` | string | 否 | 条件组合逻辑:`"and"` 或 `"or"`,默认 `"and"` |
| `conditions` | Condition[] | 否 | 条件列表 |
**Condition**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `field_name` | string | 是 | 字段名称(必须与表中字段名精确匹配) |
| `operator` | string | 是 | 运算符(见下方运算符表) |
| `value` | string[] | 是 | 条件值数组;`isEmpty`/`isNotEmpty` 时**必须**传空数组 `[]` |
**运算符:**
| 运算符 | 说明 |
|--------|------|
| `is` | 等于 |
| `isNot` | 不等于 |
| `contains` | 包含 |
| `doesNotContain` | 不包含 |
| `isEmpty` | 为空 |
| `isNotEmpty` | 不为空 |
| `isGreater` | 大于 |
| `isGreaterEqual` | 大于等于 |
| `isLess` | 小于 |
| `isLessEqual` | 小于等于 |
> 各运算符的适用字段类型见下方「按各字段类型筛选时 value 格式详解」。
**按各字段类型筛选时 value 格式详解:**
*`text`*
| 运算符 | value 格式 | 元素个数 | 示例 |
|--------|-----------|---------|------|
| `is` / `isNot` / `contains` / `doesNotContain` | `["文本内容"]` | 仅 1 个 | `["Hello"]` |
| `isEmpty` / `isNotEmpty` | `[]` | 0 个 | `[]` |
> **不支持** `isGreater` / `isGreaterEqual` / `isLess` / `isLessEqual`:文本无自然顺序,比较运算无意义。
> `text` 也覆盖电话、超链接、邮箱、条码字段;通过 `style.type` 区分(`plain`(默认)/ `phone` / `url` / `email` / `barcode`),运算符集合一致。
> 当 `style.type=url`value 筛选的是链接显示名称,而不是 URL 本身。
*`number`*
| 运算符 | value 格式 | 元素个数 | 示例 |
|--------|-----------|---------|------|
| `is` / `isNot` / `isGreater` / `isGreaterEqual` / `isLess` / `isLessEqual` | `["数字字符串"]` | 仅 1 个 | `["23.4"]`、`["-100"]` |
| `isEmpty` / `isNotEmpty` | `[]` | 0 个 | `[]` |
> value 必须为合法数字的字符串形式。
> `number` 也覆盖货币、进度、评分字段;通过 `style.type` 区分(`plain`(默认)/ `currency` / `progress` / `rating`),运算符集合一致,仅 value 解释不同:
> - 当 `style.type=progress`34% 对应 0.34 而不是 34。
> - 当 `style.type=rating` 时,必须输入整数,代表评分。
*`auto_number`*
| 运算符 | value 格式 | 元素个数 | 示例 |
|--------|-----------|---------|------|
| `is` / `isNot` / `contains` / `doesNotContain` | `["编号字符串"]` | 仅 1 个 | `["00001"]` |
| `isGreater` / `isGreaterEqual` / `isLess` / `isLessEqual` | `["编号字符串"]` | 仅 1 个 | `["00010"]` |
| `isEmpty` / `isNotEmpty` | `[]` | 0 个 | `[]` |
*`select`*
| 运算符 | value 格式 | 元素个数 | 示例 |
|--------|-----------|---------|------|
| `is` / `isNot` | `["选项名"]` | **仅 1 个** | `["选项A"]` |
| `contains` / `doesNotContain` | `["选项A", "选项B"]` | 可多个 | `["选项A", "选项B"]` |
| `isEmpty` / `isNotEmpty` | `[]` | 0 个 | `[]` |
> **不支持** `isGreater` / `isGreaterEqual` / `isLess` / `isLessEqual`:选项为枚举值,无自然顺序。
> 通过 `multiple` 区分单选(`multiple=false`,默认)/ 多选(`multiple=true`)。
*`user` / `created_by` / `updated_by`*
| 运算符 | value 格式 | 元素个数 | 示例 |
|--------|-----------|---------|------------------------|
| `is` / `isNot` | `["用户ID1", "用户ID2"]` | **可多个** | `["ou_aaa", "ou_bbb"]` |
| `contains` / `doesNotContain` | `["用户ID1", "用户ID2"]` | 可多个 | `["ou_aaa", "ou_bbb"]` |
| `isEmpty` / `isNotEmpty` | `[]` | 0 个 | `[]` |
> **不支持** `isGreater` / `isGreaterEqual` / `isLess` / `isLessEqual`:人员无法比大小。
> 用户 ID 使用 `open_id``ou_` 前缀),接口层会自动做 ID 转换。
*`group_chat`*
| 运算符 | value 格式 | 元素个数 | 示例 |
|--------|-----------|---------|------|
| `is` / `isNot` | `["群组ID1", "群组ID2"]` | 可多个 | `["oc_aaa", "oc_bbb"]` |
| `contains` / `doesNotContain` | `["群组ID1", "群组ID2"]` | 可多个 | `["oc_aaa", "oc_bbb"]` |
| `isEmpty` / `isNotEmpty` | `[]` | 0 个 | `[]` |
> **不支持** `isGreater` / `isGreaterEqual` / `isLess` / `isLessEqual`:群组无法比大小。
*`link`*
| 运算符 | value 格式 | 元素个数 | 示例 |
|--------|-----------|---------|------|
| `is` / `isNot` | `["recId1", "recId2"]` | 可多个 | `["recAAA", "recBBB"]` |
| `contains` / `doesNotContain` | `["recId1", "recId2"]` | 可多个 | `["recAAA", "recBBB"]` |
| `isEmpty` / `isNotEmpty` | `[]` | 0 个 | `[]` |
> **不支持** `isGreater` / `isGreaterEqual` / `isLess` / `isLessEqual`:关联记录无法比大小。
> value 传关联表记录的 `record_id`
> 双向关联(创建时设 `bidirectional=true`)也属于 `link` 类型,运算符与单向关联一致。
*`location`*
| 运算符 | value 格式 | 元素个数 | 示例 |
|--------|-----------|---------|------|
| `is` / `isNot` / `contains` / `doesNotContain` | `["地址文本"]` | 仅 1 个 | `["北京市朝阳区..."]` |
| `isEmpty` / `isNotEmpty` | `[]` | 0 个 | `[]` |
> **不支持** `isGreater` / `isGreaterEqual` / `isLess` / `isLessEqual`:地理位置无自然顺序。
*`checkbox`*
| 运算符 | value 格式 | 元素个数 | 示例 |
|--------|-----------|---------|------|
| `is` | `["true"]``["false"]` | 仅 1 个 | `["true"]` |
> 仅支持 `is` 运算符,不支持其他运算符。
*`datetime` / `created_at` / `updated_at`*
日期字段仅支持 `is`、`isEmpty`、`isNotEmpty`、`isGreater`、`isLess` 五种运算符。
value 使用预定义关键字机制,第一个元素为字符串常量名称:
| 关键字 | 说明 | value 格式 | 支持的运算符 |
|--------|------|-----------|-------------|
| `ExactDate` | 精确日期 | `["ExactDate", "1773187200000"]`(毫秒时间戳) | `is`、`isGreater`、`isLess` |
| `Today` | 今天 | `["Today"]` | `is`、`isGreater`、`isLess` |
| `Tomorrow` | 明天 | `["Tomorrow"]` | `is`、`isGreater`、`isLess` |
| `Yesterday` | 昨天 | `["Yesterday"]` | `is`、`isGreater`、`isLess` |
| `CurrentWeek` | 本周 | `["CurrentWeek"]` | 仅 `is` |
| `LastWeek` | 上周 | `["LastWeek"]` | 仅 `is` |
| `CurrentMonth` | 本月 | `["CurrentMonth"]` | 仅 `is` |
| `LastMonth` | 上月 | `["LastMonth"]` | 仅 `is` |
| `TheLastWeek` | 过去七天 | `["TheLastWeek"]` | 仅 `is` |
| `TheNextWeek` | 未来七天 | `["TheNextWeek"]` | 仅 `is` |
| `TheLastMonth` | 过去三十天 | `["TheLastMonth"]` | 仅 `is` |
| `TheNextMonth` | 未来三十天 | `["TheNextMonth"]` | 仅 `is` |
> - **ExactDate 时区行为**:毫秒时间戳在实际筛选时会被转为**文档时区当天零点**,跨时区场景需注意日期可能偏移一天。
> - **范围型关键字**`CurrentWeek`、`LastWeek`、`CurrentMonth`、`LastMonth`、`TheLastWeek`、`TheNextWeek`、`TheLastMonth`、`TheNextMonth`)仅支持 `is` 运算符。
> - **关键字大小写敏感**`ExactDate`、`Today`、`CurrentWeek` 等首字母大写,写错大小写会导致校验失败。
*`attachment`*
| 运算符 | value 格式 | 元素个数 | 示例 |
|--------|-----------|---------|------|
| `isEmpty` / `isNotEmpty` | `[]` | 0 个 | `[]` |
> 附件字段仅支持 `isEmpty``isNotEmpty`,不支持其他运算符。
*`formula` / `lookup`*
公式和查找引用字段的运算符和 value 格式 **取决于其结果数据类型**,按结果类型参照上方对应字段类型的规则。例如:
- 公式结果为数字 → 按 `number` 规则
- 公式结果为日期 → 按 `datetime` 规则
- 公式结果为单选 → 按 `select` 规则
**Sort 字段:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `field_name` | string | 是 | 字段名称或 alias |
| `order` | string | 否 | `"asc"`(默认)或 `"desc"` |
**Pagination 字段:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `limit` | int | 否 | 返回记录数上限,必须为正整数,最大 5000不填时使用系统默认值。不支持 offset |
**Shaper 字段:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `format` | string | 是 | 固定为 `"flat"`,表示返回扁平化的对象数组 |
## API 出参详情
**成功时:**
```json
{"code": 0, "data": {"main_data": [{"dim_city": {"value": "北京"}, "total_amount": {"value": 12345.00}}, ...]}, "msg": ""}
```
**失败时:**
```json
{"code": 800004006, "data": {"error": {"code": 800004006, ...}}, "msg": "DSL validation failed"}
```
**Response 字段:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `code` | int | 状态码0 为成功 |
| `msg` | string | 错误信息 |
| `data.main_data` | []object | 查询结果数组,每个元素为一行数据 |
| `data.error` | object | 失败时的错误详情 |
每行数据的字段值封装在 CellValue 中:
```json
{
"dim_city": {
"value": "北京"
},
"total_amount": {
"value": 12345.00
}
}
```
- `value`:展示值(人员名称、选项名称、格式化日期等)
## 返回值
命令成功后输出 `data` 字段的内容:
```json
{
"main_data": [
{
"dim_city": {"value": "直营"},
"measure_count": {"value": 1}
},
{
"dim_city": {"value": "加盟"},
"measure_count": {"value": 2}
}
]
}
```
## 工作流
1. 确认 base-token 和 table-id
2. **先查表结构**:执行 `lark-cli base +field-list --base-token <base_token> --table-id <table_id>`
3. 从返回的字段列表中获取 field_nameDSL 中使用的字段名称)
4. 根据字段信息构造 DSL JSON
5. 执行 +data-query
6. 解读返回结果:
- 结果在 `data.main_data` 数组中,每个元素代表一行
- 每行对象的 key 为 DSL 中指定的 `alias`;未指定 alias 时key 为自动生成的列名
- 每个 value 是 CellValue 对象,实际值在 `value` 字段中,如 `{"value": "北京"}``{"value": 12345.00}`
- 失败时结果在 `data.error` 中,包含具体错误码和信息
## 坑点
- ⚠️ **必须先查表结构**DSL 的 `field_name` 必须与表中字段名称精确匹配(区分大小写),不能凭猜测构造。先用 `lark-cli base +field-list --base-token <base_token> --table-id <table_id>` 获取真实字段名
- ⚠️ **权限要求按文档类型分流**:普通多维表格只需文档**阅读权限**;高级权限多维表格必须是文档管理员(**FA / Full Access**),否则返回权限错误
- ⚠️ **alias 不支持中文**dimensions 和 measures 的 alias 必须使用英文(如 `dim_city`、`total_amount`),中文 alias 会导致错误
- ⚠️ **API 路径是 `base/v3`**:本接口路径为 `/open-apis/base/v3/bases/:base_token/data/query`,不是 `bitable/v1`。两者完全不同,用错版本号会返回 `[2200] Internal Error`
- ⚠️ **`dimensions``measures` 至少填一个**:两个都不填会返回 DSL 校验错误
- ⚠️ **`shaper` 必须为 `{"format": "flat"}`**:不填或填其他值会导致结果格式不可预期,建议始终显式指定
- ⚠️ **数据表标识 `tableId` vs `tableName`**datasource 中可以用 `tableId`(如 `tblXXX`)或 `tableName`(数据表的用户自定义显示名称),二选一,不要混用
- ⚠️ **`pagination.limit` 最大 5000**:超过会报错,且不支持 offset只支持 limit
- ⚠️ **所有 alias 必须全局唯一**dimensions 和 measures 之间的 alias 也不能重名
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数
- [lark-base-cell-value.md](lark-base-cell-value.md) — CellValue 格式规范
- [lark-base-shortcut-field-properties.md](lark-base-shortcut-field-properties.md) — shortcut 字段类型与 JSON 结构

View File

@ -0,0 +1,104 @@
# base +field-create
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
创建一个字段。
## Agent 最小工作流
1. 先判断是不是 `formula` / `lookup`
2. 如果是:先读对应 guide。
3. 没读 guide 前,不要直接创建 formula / lookup 字段。
4. 读完 guide 后,再构造 `--json` 并创建字段。
5. 如果是跨表 formula / lookup再补查**目标表**的结构。
## 推荐命令
```bash
lark-cli base +field-create \
--base-token <base_token> \
--table-id <table_id> \
--json '{"name":"预算","type":"number","style":{"type":"plain","precision":2}}'
lark-cli base +field-create \
--base-token <base_token> \
--table-id <table_id> \
--json '{"name":"状态","type":"select","multiple":false,"options":[{"name":"Todo","hue":"Blue","lightness":"Lighter"},{"name":"Done","hue":"Green","lightness":"Light"}]}'
lark-cli base +field-create \
--base-token <base_token> \
--table-id <table_id> \
--json '{"name":"负责人","type":"user","multiple":false,"description":"用于标记记录的直接负责人;协作约定可参考[团队字段约定](https://example.com/field-spec)"}'
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--json <body>` | 是 | 字段属性 JSON 对象 |
## API 入参详情
**HTTP 方法和路径:**
```
POST /open-apis/base/v3/bases/:base_token/tables/:table_id/fields
```
## JSON 值规范
- `--json` 必须是 **JSON 对象**,顶层直接传字段定义,不要再套一层。
- 顶层最少包含:`name`、`type`。
- 所有字段类型都支持可选 `description`;支持纯文本,也支持 Markdown 链接,如 `协作约定可参考[团队字段约定](https://example.com/field-spec)`
- `type` 不同,必填子字段不同:
- `select``multiple` 控制是否多选,`options` 定义静态选项,`dynamic_options_source` 定义动态选项来源。静态与动态选项配置二选一,不能同时传。
- `link`:必须有 `link_table`,可选 `bidirectional`、`bidirectional_link_field_name`。
- `formula`:必须有 `expression`;先读 formula guide再创建。
- `lookup`:必须有 `from`、`select`、`where`;先读 lookup guide再创建。
**正确base +field-create**
```json
{
"name": "状态",
"type": "select",
"multiple": false,
"options": [
{ "name": "Todo", "hue": "Blue", "lightness": "Lighter" },
{ "name": "Done", "hue": "Green", "lightness": "Light" }
]
}
```
**字段说明示例**
```json
{
"name": "负责人",
"type": "user",
"multiple": false,
"description": "用于标记记录的直接负责人;协作约定可参考[团队字段约定](https://example.com/field-spec)"
}
```
## 返回重点
- 返回 `field``created: true`
## 工作流
1. formula / lookup 字段必须先阅读对应指南;没读之前不要直接创建。
## 坑点
- ⚠️ 这是写入操作,执行前必须确认。
- ⚠️ 当 `type``formula``lookup` 时,先读对应 guide再创建。
## 参考
- [lark-base-field.md](lark-base-field.md) — field 索引页
- [lark-base-shortcut-field-properties.md](lark-base-shortcut-field-properties.md) — shortcut 字段 JSON 规范(推荐)
- [formula-field-guide.md](formula-field-guide.md) — formula 指南(创建公式必读)
- [lookup-field-guide.md](lookup-field-guide.md) — lookup 指南(创建查找引用必读)

View File

@ -0,0 +1,51 @@
# base +field-delete
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
删除一个字段。
## 推荐命令
```bash
lark-cli base +field-delete \
--base-token app_xxx \
--table-id tbl_xxx \
--field-id fld_xxx \
--yes
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--field-id <id_or_name>` | 是 | 字段 ID 或字段名 |
## API 入参详情
**HTTP 方法和路径:**
```
DELETE /open-apis/base/v3/bases/:base_token/tables/:table_id/fields/:field_id
```
## 返回重点
- 返回 `deleted: true` 和目标字段标识。
## 工作流
> 这是**高风险写入操作**。CLI 层要求显式传 `--yes`;如果用户已经明确要求删除且目标明确,直接执行并带上 `--yes`,不要再补一次确认。
1. 建议先用 `+field-get``+field-list` 确认目标字段。
2. 只有当字段目标仍不明确时,才继续追问;如果删除意图和目标都明确,直接执行。
## 坑点
- ⚠️ 高风险写操作,删除后不可恢复。
- ⚠️ 忘记带 `--yes` 会被 CLI 拦截。
## 参考
- [lark-base-field.md](lark-base-field.md) — field 索引页

View File

@ -0,0 +1,42 @@
# base +field-get
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
获取一个字段的完整配置。
## 推荐命令
```bash
lark-cli base +field-get \
--base-token app_xxx \
--table-id tbl_xxx \
--field-id fld_xxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--field-id <id_or_name>` | 是 | 字段 ID 或字段名 |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/tables/:table_id/fields/:field_id
```
## 返回重点
- 返回完整字段配置,适合做更新前的基线。
## 坑点
- ⚠️ 重名字段场景下,建议优先传 `fld_xxx`
## 参考
- [lark-base-field.md](lark-base-field.md) — field 索引页

View File

@ -0,0 +1,44 @@
# base +field-list
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
分页列出一张表下的字段。
## 推荐命令
```bash
lark-cli base +field-list \
--base-token app_xxx \
--table-id tbl_xxx \
--offset 0 \
--limit 100
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--offset <n>` | 否 | 分页偏移,默认 `0` |
| `--limit <n>` | 否 | 分页大小,默认 `100`,范围 `1-200` |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/tables/:table_id/fields
```
## 返回重点
- 返回字段列表及 `offset / limit / count / total`
## 坑点
- ⚠️ `+field-list` 禁止并发调用;批量列多个表字段时只能串行。
## 参考
- [lark-base-field.md](lark-base-field.md) — field 索引页

View File

@ -0,0 +1,48 @@
# base +field-search-options
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
搜索单选 / 多选字段的候选项。
## 推荐命令
```bash
lark-cli base +field-search-options \
--base-token app_xxx \
--table-id tbl_xxx \
--field-id fld_status \
--keyword 已完成 \
--offset 0 \
--limit 30
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--field-id <id_or_name>` | 是 | 字段 ID 或字段名 |
| `--keyword <text>` | 否 | 查询关键字;会映射到 API 的 `query` |
| `--offset <n>` | 否 | 分页偏移,默认 `0` |
| `--limit <n>` | 否 | 分页大小,默认 `30` |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/tables/:table_id/fields/:field_id/options
```
## 返回重点
- 返回 `options`、`total`、以及回显的 `field_id / field_name / keyword`
## 坑点
- ⚠️ 只适用于带选项集的字段;普通文本 / 数字字段没有可搜索选项。
## 参考
- [lark-base-field.md](lark-base-field.md) — field 索引页

View File

@ -0,0 +1,97 @@
# base +field-update
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
更新一个已有字段。
## 推荐命令
```bash
lark-cli base +field-update \
--base-token app_xxx \
--table-id tbl_xxx \
--field-id fld_xxx \
--json '{"name":"状态","type":"select","multiple":false,"options":[{"name":"Todo","hue":"Blue","lightness":"Lighter"},{"name":"Doing","hue":"Orange","lightness":"Light"},{"name":"Done","hue":"Green","lightness":"Light"}]}'
lark-cli base +field-update \
--base-token app_xxx \
--table-id tbl_xxx \
--field-id fld_xxx \
--json '{"name":"负责人","type":"user","multiple":false,"description":"用于标记记录的直接负责人;协作约定可参考[团队字段约定](https://example.com/field-spec)"}'
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--field-id <id_or_name>` | 是 | 字段 ID 或字段名 |
| `--json <body>` | 是 | 字段属性 JSON 对象 |
## API 入参详情
**HTTP 方法和路径:**
```
PUT /open-apis/base/v3/bases/:base_token/tables/:table_id/fields/:field_id
```
## JSON 值规范
- `--json` 必须是 **JSON 对象**,顶层直接传字段定义。
- 更新语义是 `PUT`(全量字段配置更新),不要只传零散片段;至少显式包含 `name`、`type`,并补齐该类型所需关键配置。
- 如需字段说明,直接传 `description`;支持纯文本,也支持 Markdown 链接,如 `协作约定可参考[团队字段约定](https://example.com/field-spec)`
- `select` 更新时:`options` 仍按对象数组传,避免混入无效字段。
- `link` 更新限制:
- 不能把非 `link` 字段改成 `link`,也不能把 `link` 改成非 `link`
- 现有 `link` 字段的 `bidirectional` 不能改。
**推荐更新示例**
```json
{
"name": "状态",
"type": "select",
"multiple": false,
"options": [
{ "name": "Todo", "hue": "Blue", "lightness": "Lighter" },
{ "name": "Doing", "hue": "Orange", "lightness": "Light" },
{ "name": "Done", "hue": "Green", "lightness": "Light" }
]
}
```
**字段说明示例**
```json
{
"name": "负责人",
"type": "user",
"multiple": false,
"description": "用于标记记录的直接负责人;协作约定可参考[团队字段约定](https://example.com/field-spec)"
}
```
## 返回重点
- 返回 `field``updated: true`
## 工作流
1. 建议先用 `+field-get` 拉现状,再做最小化修改。
2. `formula/lookup` 类型更新前先阅读对应指南。
## 坑点
- ⚠️ 这是全量字段属性更新语义,不是 patch。
- ⚠️ 这是写入操作,执行前必须确认。
- ⚠️ 当 `type``formula``lookup` 时,先阅读对应指南再执行。
## 参考
- [lark-base-field.md](lark-base-field.md) — field 索引页
- [lark-base-field-get.md](lark-base-field-get.md) — 查字段
- [lark-base-shortcut-field-properties.md](lark-base-shortcut-field-properties.md) — shortcut 字段 JSON 规范(推荐)
- [formula-field-guide.md](formula-field-guide.md) — formula 指南(更新公式前必读)
- [lookup-field-guide.md](lookup-field-guide.md) — lookup 指南(更新查找引用前必读)

View File

@ -0,0 +1,23 @@
# base field shortcuts
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
field 相关命令索引。
## 命令导航
| 文档 | 命令 | 说明 |
|------|------|------|
| [lark-base-field-list.md](lark-base-field-list.md) | `+field-list` | 分页列字段 |
| [lark-base-field-get.md](lark-base-field-get.md) | `+field-get` | 获取单字段配置 |
| [lark-base-field-create.md](lark-base-field-create.md) | `+field-create` | 创建字段 |
| [lark-base-field-update.md](lark-base-field-update.md) | `+field-update` | 更新字段 |
| [lark-base-field-search-options.md](lark-base-field-search-options.md) | `+field-search-options` | 搜索选项字段候选值 |
| [lark-base-field-delete.md](lark-base-field-delete.md) | `+field-delete` | 删除字段 |
## 说明
- 聚合页只保留目录职责;每个命令的详细说明请进入对应单命令文档。
- 所有 `+xxx-list` 调用都必须串行执行;若要批量跑多个 list 请求,只能串行执行。
- 写字段 JSON 前优先阅读 [lark-base-shortcut-field-properties.md](lark-base-shortcut-field-properties.md)。
- 涉及字段类型转换时,直接阅读 [lark-base-field-update.md](lark-base-field-update.md) 中的“字段类型变更规则”。

View File

@ -0,0 +1,87 @@
# base +form-create
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
在多维表格数据表中创建新表单。
## ⚠️ 注意事项
- **表格选择**:创建问卷前先考虑:这是新的业务领域吗?如果是,建议先用 `+table-create` 创建新表格
- **命名一致性**:问卷名称应与表格用途相关,避免在通用表格(如"收集表")中创建不相关的问卷
## 命令
```bash
# 创建表单(仅必填参数)
lark-cli base +form-create \
--base-token <base_token> \
--table-id <table_id> \
--name "用户调研问卷"
# 创建时附带描述(纯文本)
lark-cli base +form-create \
--base-token <base_token> \
--table-id <table_id> \
--name "用户调研问卷" \
--description "2024年度用户满意度调研"
# 创建时附带描述(含链接)
lark-cli base +form-create \
--base-token <base_token> \
--table-id <table_id> \
--name "用户调研问卷" \
--description "2024年度调研[详情请查看](https://example.com)"
# 使用应用身份bot
lark-cli base +form-create \
--base-token <base_token> \
--table-id <table_id> \
--name "用户调研问卷" \
--as bot
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Tokenbase_token |
| `--table-id <id>` | 是 | 数据表 ID |
| `--name <name>` | 是 | 表单名称 |
| `--description <string>` | 否 | 表单描述(纯文本或 Markdown 链接,如 `[文本](https://example.com)` |
| `--format` | 否 | 输出格式json默认\| pretty \| table \| ndjson \| csv |
| `--as` | 否 | 身份user默认\| bot |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 输出格式
| 字段 | 说明 |
|------|------|
| `id` | 新创建的表单 ID |
| `name` | 表单名称 |
| `description` | 表单描述 |
```json
{
"ok": true,
"data": {
"id": "vewX58te9D",
"name": "用户调研问卷",
"description": "2024年度用户满意度调研"
}
}
```
## 工作流
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。
1. 确认目标 `base_token``table_id`
2. 确认表单名称和描述
3. 执行命令
4. 报告返回的 `form_id`,后续可用于添加问题(`+form-questions-create`
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,64 @@
# base +form-delete
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
删除多维表格数据表中的指定表单。**不可逆操作**,执行前务必确认。
## 命令
```bash
# 删除表单
lark-cli base +form-delete \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id>
# 预览(不实际执行)
lark-cli base +form-delete \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--dry-run
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Tokenbase_token |
| `--table-id <id>` | 是 | 数据表 ID |
| `--form-id <id>` | 是 | 要删除的表单 ID |
| `--as` | 否 | 身份user默认\| bot |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 输出格式
```json
{
"ok": true,
"data": {
"deleted": true,
"form_id": "vewX58te9D"
}
}
```
## 工作流
> [!CAUTION]
> 这是**高风险写入操作(删除)** — 执行前必须明确向用户确认,告知此操作不可逆。
1. 先用 `+form-list``+form-get` 确认目标表单存在
2. 向用户展示将要删除的表单名称和 ID等待明确确认
3. 执行删除
4. 报告删除结果
## 提示
- 删除前建议先用 `+form-questions-list` 了解表单内容,避免误删
- `form_id` 可通过 `+form-list` 查询
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,68 @@
# base +form-get
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
获取多维表格数据表中指定表单的详情。只读操作,不修改任何数据。
## 命令
```bash
# 获取表单详情
lark-cli base +form-get \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id>
# 以 pretty 格式展示
lark-cli base +form-get \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--format pretty
# 使用应用身份bot
lark-cli base +form-get \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--as bot
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Tokenbase_token |
| `--table-id <id>` | 是 | 数据表 ID |
| `--form-id <id>` | 是 | 表单 ID |
| `--format` | 否 | 输出格式json默认\| pretty \| table \| ndjson \| csv |
| `--as` | 否 | 身份user默认\| bot |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 输出格式
| 字段 | 说明 |
|------|------|
| `id` | 表单 ID |
| `name` | 表单名称 |
| `description` | 表单描述 |
```json
{
"ok": true,
"data": {
"id": "vewX58te9D",
"name": "用户调研问卷",
"description": "2024年度用户满意度调研"
}
}
```
## 提示
- `form_id` 可通过 `lark-cli base +form-list --base-token <token> --table-id <id>` 获取
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,73 @@
# base +form-list
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
列出多维表格数据表中的所有表单。自动翻页,一次性返回全量结果。只读操作,不修改任何数据。
## 命令
```bash
# 列出指定数据表的所有表单
lark-cli base +form-list \
--base-token <base_token> \
--table-id <table_id>
# 以表格形式展示
lark-cli base +form-list \
--base-token <base_token> \
--table-id <table_id> \
--format table
# 使用应用身份bot
lark-cli base +form-list \
--base-token <base_token> \
--table-id <table_id> \
--as bot
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Tokenbase_token |
| `--table-id <id>` | 是 | 数据表 ID |
| `--page-size <n>` | 否 | 每次请求的分页大小,默认 100最大 100 |
| `--format` | 否 | 输出格式json默认\| pretty \| table \| ndjson \| csv |
| `--as` | 否 | 身份user默认\| bot |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 输出格式
每条表单包含以下字段:
| 字段 | 说明 |
|------|------|
| `id` | 表单 ID`vewX58te9D` |
| `name` | 表单名称 |
| `description` | 表单描述 |
JSON 输出示例(`--format json`,默认):
```json
{
"ok": true,
"data": {
"forms": [
{"id": "vewX58te9D", "name": "用户调研问卷", "description": "..."},
{"id": "form_yyyy", "name": "产品反馈表", "description": "..."}
],
"total": 2
}
}
```
## 提示
- `base_token` 在多维表格 URL 中可找到(形如 `bascnXXXX`
- `table_id` 可通过 `lark-cli base +table-list --base-token <base_token>` 获取
- 如无表单,输出 `forms: []`
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,118 @@
# base +form-questions-create
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
向多维表格表单/问卷中批量添加问题。
## 命令
```bash
# 添加一个文本必填问题
lark-cli base +form-questions-create \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--questions '[{"type":"text","title":"您的姓名是?","required":true}]'
# 添加多个问题(按顺序排列)
lark-cli base +form-questions-create \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--questions '[
{"type":"text","title":"您的姓名是?","required":true},
{"type":"text","title":"您的联系方式是?","required":false}
]'
# 添加单选题(带选项)
lark-cli base +form-questions-create \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--questions '[{"type":"select","title":"满意度评价","required":true,"multiple":false,"options":[{"name":"非常满意","hue":"Green"},{"name":"满意","hue":"Blue"},{"name":"一般","hue":"Yellow"}]}]'
# 添加评分题
lark-cli base +form-questions-create \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--questions '[{"type":"number","title":"服务评分","style":{"type":"rating","icon":"star","min":1,"max":5}}]'
# 添加带描述的问题(纯文本)
lark-cli base +form-questions-create \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--questions '[{"type":"text","title":"您的姓名","description":"请填写真实姓名"}]'
# 添加带描述的问题(含链接)
lark-cli base +form-questions-create \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--questions '[{"type":"text","title":"反馈建议","description":"更多详情请查看[帮助文档](https://example.com/help)"}]'
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Tokenbase_token |
| `--table-id <id>` | 是 | 数据表 ID |
| `--form-id <id>` | 是 | 表单 ID |
| `--questions <json>` | 是 | 问题 JSON 数组,最多 10 个(见下方格式) |
| `--format` | 否 | 输出格式json默认\| pretty \| table \| ndjson \| csv |
| `--as` | 否 | 身份user默认\| bot |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## `--questions` 格式
每个问题对象支持以下字段:
| 字段 | 必填 | 说明 |
|-----------------------|------|------|
| `title` | **是** | 问题标题(字段名) |
| `type` | **是** | 题目类型:`text`、`number`、`select`、`datetime`、`user`、`attachment`、`location` |
| `description` | 否 | 问题描述(纯文本或 Markdown 链接,如 `[文本](https://example.com)` |
| `required` | 否 | 是否必填true/false |
| `option_display_mode` | 否 | 选项展示方式(仅 `select` 有效):`0`=下拉,`1`=纵向(默认),`2`=横向 |
| `multiple` | 否 | 是否多选(`select`/`user` 类型有效bool |
| `options` | 否 | 选项列表(仅 `select` 有效):`[{"name":"选项1","hue":"Blue"}]`hue 可选:`Red`/`Orange`/`Yellow`/`Green`/`Blue`/`Purple`/`Gray` |
| `style` | 否 | 字段样式配置(见下方说明) |
### `style` 字段说明
| 类型 | style 结构 | 说明 |
|------|------|------|
| `text` | `{"type":"plain"}` | 当前仅支持 `plain` |
| `number` | `{"type":"plain","precision":2}` | precision 为小数位数 |
| `number`(评分) | `{"type":"rating","icon":"star","min":1,"max":5}` | icon 可选:`star`/`heart`/`thumbsup`/`fire`/`smile`/`lightning`/`flower`/`number` |
| `datetime` | `{"format":"yyyy/MM/dd"}` | format 可选:`yyyy/MM/dd`、`yyyy/MM/dd HH:mm`、`MM-dd`、`MM/dd/yyyy`、`dd/MM/yyyy` |
## 输出格式
返回创建成功的问题列表:
```json
{
"ok": true,
"data": {
"items": [
{"id": "q_001", "title": "您的姓名是?", "required": true}
]
}
}
```
## 工作流
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。
1. 先用 `+form-questions-list` 查看现有问题
2. 确认要添加的问题内容
3. 执行命令并报告新建的问题 ID
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,68 @@
# base +form-questions-delete
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
从多维表格表单中批量删除问题。**不可逆操作**,执行前务必确认。
## 命令
```bash
# 删除一个问题
lark-cli base +form-questions-delete \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--question-ids '["q_001"]'
# 批量删除多个问题
lark-cli base +form-questions-delete \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--question-ids '["q_001","q_002","q_003"]'
# 预览(不实际执行)
lark-cli base +form-questions-delete \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--question-ids '["q_001"]' \
--dry-run
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | 多维表格 App tokenbase_token |
| `--table-id <id>` | 是 | 数据表 ID |
| `--form-id <id>` | 是 | 表单 ID |
| `--question-ids <json>` | 是 | 要删除的问题 ID JSON 数组,最多 10 个,如 `'["q_001","q_002"]'` |
| `--as` | 否 | 身份user默认\| bot |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 输出格式
```json
{
"ok": true,
"data": {
"deleted": true,
"question_ids": ["q_001", "q_002"]
}
}
```
## 工作流
> [!CAUTION]
> 这是**高风险写入操作(删除)** — 执行前必须明确向用户确认,告知此操作不可逆。
1. 先用 `+form-questions-list` 查看问题列表,确认要删除的 `id`
2. 向用户展示将要删除的问题标题和 ID等待明确确认
3. 执行删除并报告结果
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,84 @@
# base +form-questions-list
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
列出多维表格表单/问卷中的所有问题。只读操作,不修改任何数据。
## 命令
```bash
# 列出表单所有问题
lark-cli base +form-questions-list \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id>
# 以表格形式展示
lark-cli base +form-questions-list \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--format table
# 使用应用身份bot
lark-cli base +form-questions-list \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--as bot
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Tokenbase_token |
| `--table-id <id>` | 是 | 数据表 ID |
| `--form-id <id>` | 是 | 表单 ID |
| `--format` | 否 | 输出格式json默认\| pretty \| table \| ndjson \| csv |
| `--as` | 否 | 身份user默认\| bot |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 输出格式
每条问题包含以下字段:
| 字段 | 说明 |
|------|------|
| `id` | 问题 ID即数据表的 field_id |
| `title` | 问题标题 |
| `description` | 问题描述 |
| `required` | 是否必填 |
```json
{
"ok": true,
"data": {
"questions": [
{
"id": "q_001",
"title": "您的姓名是?",
"description": "请填写真实姓名",
"required": true
},
{
"id": "q_002",
"title": "您的联系方式是?",
"description": "手机号或邮箱",
"required": false
}
],
"total": 2
}
}
```
## 提示
- 问题 `id` 与数据表的 `field_id` 相同
- 返回的问题列表已按顺序排列
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,92 @@
# base +form-questions-update
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
批量更新多维表格表单/问卷中的问题(标题、描述、是否必填)。
## 命令
```bash
# 更新一个问题的标题
lark-cli base +form-questions-update \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--questions '[{"id":"q_001","title":"您的真实姓名是?"}]'
# 同时更新多个问题
lark-cli base +form-questions-update \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--questions '[
{"id":"q_001","title":"姓名(必填)","required":true},
{"id":"q_002","title":"联系方式","required":false}
]'
# 更新问题描述(纯文本)
lark-cli base +form-questions-update \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--questions '[{"id":"q_001","description":"请填写您的真实姓名"}]'
# 更新问题描述(含链接)
lark-cli base +form-questions-update \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--questions '[{"id":"q_001","description":"更多说明请参考[帮助文档](https://example.com/help)"}]'
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Tokenbase_token |
| `--table-id <id>` | 是 | 数据表 ID |
| `--form-id <id>` | 是 | 表单 ID |
| `--questions <json>` | 是 | 问题更新 JSON 数组,最多 10 个(见下方格式) |
| `--format` | 否 | 输出格式json默认\| pretty \| table \| ndjson \| csv |
| `--as` | 否 | 身份user默认\| bot |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## `--questions` 格式
每个问题对象必须包含 `id`,其余字段按需传入:
| 字段 | 必填 | 说明 |
|------|------|------|
| `id` | **是** | 问题 IDfield_id不可修改 |
| `title` | 否 | 新的问题标题 |
| `description` | 否 | 新的问题描述(纯文本或 Markdown 链接,如 `[文本](https://example.com)` |
| `required` | 否 | 是否必填 |
| `option_display_mode` | 否 | 选项展示方式(仅 `select` 有效):`0`=下拉,`1`=纵向(默认),`2`=横向 |
## 输出格式
返回更新后的问题列表:
```json
{
"ok": true,
"data": {
"items": [
{"id": "q_001", "title": "姓名(必填)", "required": true}
]
}
}
```
## 工作流
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。
1. 先用 `+form-questions-list` 获取现有问题及其 `id`
2. 构造包含 `id` 的更新数组
3. 执行命令并报告更新结果
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,23 @@
# base form questions shortcuts
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
form questions 相关命令索引。
## 命令导航
| 文档 | 命令 | 说明 |
|------|------|------|
| [lark-base-form-questions-list.md](lark-base-form-questions-list.md) | `+form-questions-list` | 列出表单问题 |
| [lark-base-form-questions-create.md](lark-base-form-questions-create.md) | `+form-questions-create` | 创建表单问题 |
| [lark-base-form-questions-update.md](lark-base-form-questions-update.md) | `+form-questions-update` | 更新表单问题 |
| [lark-base-form-questions-delete.md](lark-base-form-questions-delete.md) | `+form-questions-delete` | 删除表单问题 |
## 相关
- [lark-base-form.md](lark-base-form.md) — 表单管理
## 说明
- 聚合页只保留目录职责;每个命令的详细说明请进入对应单命令文档。
- 所有 `+xxx-list` 调用都必须串行执行;若要批量跑多个 list 请求,只能串行执行。

View File

@ -0,0 +1,82 @@
# base +form-update
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
更新多维表格数据表中指定表单的名称或描述。
## 命令
```bash
# 更新表单名称
lark-cli base +form-update \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--name "新表单名称"
# 更新描述(纯文本)
lark-cli base +form-update \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--description "新的描述内容"
# 更新描述(含链接)
lark-cli base +form-update \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--description "新描述,[了解更多](https://example.com)"
# 同时更新名称和描述
lark-cli base +form-update \
--base-token <base_token> \
--table-id <table_id> \
--form-id <form_id> \
--name "新表单名称" \
--description "新的描述内容"
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Tokenbase_token |
| `--table-id <id>` | 是 | 数据表 ID |
| `--form-id <id>` | 是 | 表单 ID |
| `--name <name>` | 否 | 新的表单名称 |
| `--description <string>` | 否 | 新的表单描述(纯文本或 Markdown 链接,如 `[文本](https://example.com)` |
| `--format` | 否 | 输出格式json默认\| pretty \| table \| ndjson \| csv |
| `--as` | 否 | 身份user默认\| bot |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
> `--name``--description` 至少传一个,否则无实际变更。
## 输出格式
返回更新后的表单信息:
```json
{
"ok": true,
"data": {
"id": "vewX58te9D",
"name": "新表单名称",
"description": "新的描述内容"
}
}
```
## 工作流
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。
1. 确认 `form_id`(可通过 `+form-list` 获取)
2. 确认要修改的字段
3. 执行命令并报告更新后的值
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,24 @@
# base form shortcuts
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
form 相关命令索引。
## 命令导航
| 文档 | 命令 | 说明 |
|------|------|------|
| [lark-base-form-list.md](lark-base-form-list.md) | `+form-list` | 分页列出表单 |
| [lark-base-form-get.md](lark-base-form-get.md) | `+form-get` | 获取表单详情 |
| [lark-base-form-create.md](lark-base-form-create.md) | `+form-create` | 创建表单 |
| [lark-base-form-update.md](lark-base-form-update.md) | `+form-update` | 更新表单 |
| [lark-base-form-delete.md](lark-base-form-delete.md) | `+form-delete` | 删除表单 |
## 相关
- [lark-base-form-questions.md](lark-base-form-questions.md) — 表单问题(表单字段)管理
## 说明
- 聚合页只保留目录职责;每个命令的详细说明请进入对应单命令文档。
- 所有 `+xxx-list` 调用都必须串行执行;若要批量跑多个 list 请求,只能串行执行。

View File

@ -0,0 +1,16 @@
# base history shortcuts
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
history 相关命令索引。
## 命令导航
| 文档 | 命令 | 说明 |
|------|------|------|
| [lark-base-record-history-list.md](lark-base-record-history-list.md) | `+record-history-list` | 按 `table-id + record-id` 查询记录变更历史 |
## 说明
- 聚合页只保留目录职责;每个命令的详细说明请进入对应单命令文档。
- 所有 `+xxx-list` 调用都必须串行执行;若要批量跑多个 list 请求,只能串行执行。

View File

@ -0,0 +1,58 @@
# base +record-batch-create
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
批量创建记录。
## 适用场景(重点)
- 适合导入 CSV / Excel、外部系统一次性写入新数据。
- 先把输入数据映射到合适的字段类型,再组装 `fields + rows`
## 推荐命令
```bash
lark-cli base +record-batch-create --base-token <base_token> --table-id <table_id> \
--json '{"fields":["标题","状态"],"rows":[["任务 A","Open"],["任务 B","Done"]]}'
lark-cli base +record-batch-create --base-token <base_token> --table-id <table_id> --json @batch-create.json
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--json <body>` | 是 | 批量创建请求体,必须是 JSON 对象。支持直接传 JSON 字符串,或 `@<file_path>` 从文件读取 |
## API
`POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records/batch_create`
## `--json` 结构
本节只说明 `+record-batch-create` 的外层 JSON 形状CellValue 统一看 [lark-base-cell-value.md](lark-base-cell-value.md)。
对象形态:`{"fields":[...],"rows":[...]}`。
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `fields` | `string[]` | 是 | 字段 ID 或字段名数组 |
| `rows` | `CellValue[][]` | 是 | 二维数组,每一行按 `fields` 同序给 cell单次最多 200 行 |
## 返回重点
返回 `fields`、`field_id_list`、`record_id_list`、`data`,其中 `data``fields` 列顺序对齐。
## 坑点
- `fields` 与每行 `rows` 的列顺序必须一一对应。
- 空单元格必须显式用 `null` 填充。
- 单次最多 200 行,超出需分批写入。
- select 写入未知选项时平台可能自动新增选项;如果不是要新增选项,先确认真实选项名。
## 参考
- [lark-base-record.md](lark-base-record.md) — record 索引页
- [lark-base-cell-value.md](lark-base-cell-value.md) — CellValue 格式规范

View File

@ -0,0 +1,53 @@
# base +record-batch-update (batch update)
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
批量更新记录(将同一份 `patch` 批量应用到一批 `record_id_list`)。
## 推荐命令
```bash
lark-cli base +record-batch-update --base-token <base_token> --table-id <table_id> \
--json '{"record_id_list":["<record_id>"],"patch":{"状态":"完成"}}'
lark-cli base +record-batch-update --base-token <base_token> --table-id <table_id> --json @batch-update.json
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--json <body>` | 是 | 批量更新请求体,必须是 JSON 对象。支持直接传 JSON 字符串,或 `@<file_path>` 从文件读取 |
## API
`POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records/batch_update`
## `--json` 结构
本节只说明 `+record-batch-update` 的外层 JSON 形状CellValue 统一看 [lark-base-cell-value.md](lark-base-cell-value.md)。
对象形态:`{"record_id_list":[...],"patch":{...}}`。
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `record_id_list` | `string[]` | 是 | 要更新的记录 ID 列表(单次最多 200 条) |
| `patch` | `Map<FieldNameOrID, CellValue>` | 是 | 字段更新对象key 是字段名或字段 IDvalue 是 `CellValue`;同一份 `patch` 会应用到 `record_id_list` 内所有记录 |
## 返回重点
返回 `record_id_list`、`update`,可选返回 `ignored_fields``update` 可能为空对象。
## 坑点
- 这是“同值批量更新”:所有 `record_id_list` 都应用同一份 `patch`
- `record_id_list` 最大 200 条,超过会被接口校验拒绝。
- 命令不会自动做字段/行映射转换,传什么就发什么。
- 如果 `patch` 包含只读字段,返回里可能出现 `ignored_fields`;这些字段不会被更新。
## 参考
- [lark-base-record.md](lark-base-record.md) — record 索引页
- [lark-base-cell-value.md](lark-base-cell-value.md) — CellValue 格式规范

View File

@ -0,0 +1,62 @@
# base +record-delete
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
删除一条或多条记录。
## 推荐命令
```bash
lark-cli base +record-delete \
--base-token <base_token> \
--table-id <table_id> \
--record-id <record_id> \
--yes
```
```bash
lark-cli base +record-delete \
--base-token app_xxx \
--table-id tbl_xxx \
--record-id rec_001 \
--record-id rec_002 \
--yes
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--record-id <id>` | 否 | 与 `--json` 二选一;记录 ID可重复使用这是主推荐用法 |
| `--json <object>` | 否 | 与 `--record-id` 二选一;脚本/代理场景可传 `{"record_id_list":["rec_xxx"]}` |
## API 入参详情
**HTTP 方法和路径:**
```http
POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records/batch_delete
```
## 返回重点
- CLI 内部统一通过 `batch_delete` 删除记录;单个和多个 `--record-id` 使用相同的批量删除输出形态。
- 成功时直接返回接口 `data` 字段内容,通常包含 `record_id_list`
## 工作流
> 这是**高风险写入操作**。CLI 层要求显式传 `--yes`;如果用户已经明确要求删除且目标明确,直接执行并带上 `--yes`,不要再补一次确认。
1. 建议先用 `+record-get` 确认目标记录。
2. 只有当目标记录仍不明确时,才继续追问;如果删除意图和目标都明确,直接执行。
## 坑点
- ⚠️ 高风险写操作,删除后不可恢复。
- ⚠️ 忘记带 `--yes` 会被 CLI 拦截。
## 参考
- [lark-base-record.md](lark-base-record.md) — record 索引页

View File

@ -0,0 +1,86 @@
# base +record-history-list
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
查询指定记录的变更历史。当前可执行命令为 `+record-history-list`,无 `+history-list` 别名。
## 推荐命令
```bash
# 查询最新一页历史
lark-cli base +record-history-list \
--base-token <base_token> \
--table-id <table_id> \
--record-id <record_id>
# 指定分页大小,带游标翻页
lark-cli base +record-history-list \
--base-token <base_token> \
--table-id <table_id> \
--record-id <record_id> \
--page-size 30 \
--max-version 123456
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID |
| `--record-id <id>` | 是 | 记录 ID |
| `--page-size <n>` | 否 | 每页条数,默认 `30`,最大 `50` |
| `--max-version <n>` | 否 | 翻页游标,取上一页返回的 `next_max_version` 值 |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/record_history
```
- Query 参数:`table_id`、`record_id`、`page_size`、`max_version`。
## 返回重点
- 返回记录历史条目列表(按版本号降序,最新在前),而不是记录当前值。
- 每条历史包含:版本号(`rev`)、操作人(`operator`)、操作时间(`create_time`**秒级** Unix 时间戳)、操作类型(`activity_type`)、字段变更列表(`field_changes`)。
- 字段变更包含:字段 ID、字段名、字段类型、变更前值(`before`)、变更后值(`after`)。
- 适合定位谁改了这条记录、什么时候改的、改了哪些字段。
### activity_type 取值
| 值 | 含义 |
|------|------|
| `create` | 记录创建 |
| `update` | 记录编辑 |
| `delete` | 记录删除 |
### 不出现在历史中的字段类型
以下字段类型的变更**不会**出现在 `field_changes` 中:
- **计算字段**:公式(formula)、查找引用(lookup)
- **系统字段**:自动编号、创建时间、创建人、修改时间、修改人
## 翻页工作流
1. **首次请求**:不传 `--max-version`,获取最新一页。
2. **判断是否有下一页**:检查返回的 `has_more` 字段。
3. **翻页**:若 `has_more = true`,取返回的 `next_max_version` 值,传入下一次请求的 `--max-version`
4. **终止**:当 `has_more = false` 时停止。
## 工作流
1. 先确认 `table-id``record-id` 都来自同一张表。
2. 先查最新一页,再按翻页工作流向前翻页。
## 坑点
- ⚠️ `+record-history-list` 属于 `+xxx-list`,禁止并发调用;批量执行时只能串行。
- ⚠️ 当前不支持整表历史扫描,只支持单条记录历史。
## 参考
- [lark-base-history.md](lark-base-history.md) — history 索引页
- [lark-base-record.md](lark-base-record.md) — record 索引页

View File

@ -0,0 +1,86 @@
# base record read SOP
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、权限处理和全局参数。
记录读取由 6 个功能组合完成选路、字段投影、视图预处理、分页与范围、返回结构解释、link 关联读取。
## 1. 读取选路
| 场景 | 使用方式 | 规则 |
|------|------|------|
| 已知 `record_id` | `+record-get` | 只读单条记录,不要用 search/list 反查。 |
| 明确关键词检索 | `+record-search` | 只用于文本关键词检索;金额、状态、日期等结构化条件不要用 search。 |
| 普通明细读取 / 导出 / 查看前 N 条 | `+record-list` | 优先加 `--view-id` 时只读该视图可见记录与可见字段;或者加 `--field-id` 手动裁剪字段;不传 `--view-id` 时会读取全表。 |
| 明确筛选 / 排序 / Top N / Bottom N 且需要原始记录或 `record_id` | 创建带 filter + sort 的临时视图 + `+record-list --view-id` | 让视图完成 filter/sort projectionLLM 不擅长手工筛选排序,建议用视图完成。 |
| 统计 / 聚合结果且不需要 `record_id` | 转到 [`lark-base-data-query.md`](lark-base-data-query.md) | `data-query` 是特殊分析 DSL不是记录读取工具。 |
## 2. 字段投影
- `FieldListFirst`: 不清楚字段结构时先 `+field-list`,确认筛选字段、排序字段、展示字段、关联字段、业务唯一键字段。
- `UseRealField`: 字段名和字段 ID 必须来自 `+field-list` 返回,不要凭自然语言猜字段名。
- `MinimalProjection`: 每次读取只返回本次任务需要的字段;`+record-list` 用重复 `--field-id`,视图读取用 `+view-set-visible-fields`
- `FieldScopePriority`: 返回字段优先级为显式投影字段(`+record-list --field-id` / `record-search select_fields` > 视图可见字段 > 全表字段;需要稳定列范围时必须显式投影。
- `LongFieldAvoidance`: 默认不要读取 `trace`、`raw`、长文本、附件等高噪声字段,除非任务明确需要。
- `BusinessKey`: 后续要定位、更新或解释记录时投影中必须包含可识别业务字段例如订单号、日报ID、姓名、编号。
## 3. 视图预处理
适用于结构化筛选、排序、最高/最低、倒数、Top/Bottom N、按条件找记录等场景。
1. `+field-list` 获取字段 ID、字段名和字段类型。
2. `+view-create` 创建临时 `grid` 视图,名称带任务语义,例如 `tmp_query_销售额升序`
3. `+view-set-filter` 设置筛选条件;空值是否参与必须按用户语义判断。
4. `+view-set-sort` 设置排序条件;最高/最新用降序,最低/最早/倒数用升序。
5. `+view-set-visible-fields` 设置投影字段,只保留业务键、排序字段、筛选解释字段、需要展示或二跳的字段。
6. `+record-list --view-id <view_id> --limit <N>` 读取结果;不要再从未排序全表输出中手动挑选。
## 4. 分页与范围
- `ViewScope`: URL 带 `view_id` 时先判断用户是否要求“该视图下”;全表问题不要误用 URL 视图范围,应该根据需求创建合适的临时视图完成查询任务。
- `ViewIdScope`: `+record-list --view-id` 是作用域参数;仅用于用户指定的视图,或本次任务主动创建的临时筛选 / 排序 / 投影视图。
- `NeedAllPages`: 用户要求全部、导出、统计、最高/最低且未用视图/limit 限定时,必须检查 `has_more` 并串行翻页。
- `LimitWhenScoped`: 用户只要示例、前 N 条、Top/Bottom N使用 `--limit` 控制结果规模。
- `NoConcurrentList`: `+record-list` 禁止并发调用;分页和多表读取必须串行。
- `DataQueryScope`: `data-query` 的筛选 DSL 与视图筛选不是同一套语法;不要混用。
## 5. 返回结构解释
- `ColumnMapping`: `fields` / `field_id_list` 定义 `data` 每列含义;解释记录前先建立列到字段名的映射。
- `RowMapping`: `record_id_list[i]``data[i]` 是同一行;需要后续定位、更新或关联时,按下标整理成 `record_id + 字段名:值` 的小表。
- `BusinessMatch`: 后续引用目标记录时按业务字段匹配,不靠肉眼数行号。
- `FieldType`: 按字段类型解释值数字、货币、日期、人员、formula、lookup、attachment、link 不要当普通文本处理。
- `EmptyValue`: 空值参与筛选或排序前必须明确语义;不要默认把空值当 `0`、空字符串或有效状态。
- `AnswerCheck`: 最终回答前复核答案记录来自读取结果、筛选排序已应用、字段含义和 record_id 映射无误。
## 6. link 关联字段读取
link 字段是关联单元格;读取结果通常是关联表的 `record_id` 数组,不是用户可读名称。
| 步骤 | 做法 |
|------|------|
| 识别 link 字段 | 用 `+field-list` 查看字段类型为 `link`,并读取 `link_table` 确认关联目标表。 |
| 读取当前表 | 在当前表 `+record-list` / `+record-get` 中保留 link 字段和业务键字段。 |
| 解析单元格值 | link 单元格通常形如 `[{"id":"rec..."}]`;提取其中每个 `id` 作为关联表 `record_id`。 |
| 读取关联表 | 到 `link_table` 使用 `+record-get --record-id <rec...>` 或裁剪后的 `+record-list` 读取显示字段。 |
| 建立映射 | 形成 `关联record_id -> 显示字段值` 映射,再回填当前表结果。 |
| 多值处理 | 多个关联值保持原顺序;可去重批量读取,但回答时按原单元格顺序输出。 |
禁止事项:
- 不要把 link 单元格里的 `record_id` 当作最终答案。
- 不要用 `+record-search` 搜索 link `record_id` 来查关联记录。
- 不要凭关联 `record_id` 猜名称、负责人、门店等显示值。
- 不要只看当前表字段名推断关联表结构;跨表读取前必须拿关联表字段结构。
## 7. 命令 help
- `HelpFirst`: 参数、示例、JSON shape 和取值约束以 `lark-cli base +record-get --help`、`+record-search --help`、`+record-list --help` 为准。
- `RecordSearchJson`: 构造 `+record-search --json` 前先看 `+record-search --help`,确认 `keyword/search_fields/select_fields/view_id/offset/limit` 的结构和约束。
- `RecordListProjection`: 构造 `+record-list` 前先看 `+record-list --help`,确认 `--field-id`、`--view-id`、`--offset`、`--limit` 的语义。
## 参考
- [lark-base-view-set-filter.md](lark-base-view-set-filter.md)
- [lark-base-view-set-sort.md](lark-base-view-set-sort.md)
- [lark-base-view-set-visible-fields.md](lark-base-view-set-visible-fields.md)
- [lark-base-data-query.md](lark-base-data-query.md)

View File

@ -0,0 +1,72 @@
# base +record-share-link-create
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
为一条或多条记录生成分享链接(单次调用最多传入 100 条,内部调用批量接口)。
## 推荐命令
```bash
# 单条记录
lark-cli base +record-share-link-create \
--base-token <base_token> \
--table-id <table_id> \
--record-ids <record_id>
# 多条记录(使用 "," 分隔)
lark-cli base +record-share-link-create \
--base-token <base_token> \
--table-id <table_id> \
--record-ids rec001,rec002,rec003
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id>` | 是 | 表 ID |
| `--record-ids <ids...>` | 是 | 记录 ID 列表,逗号分隔或重复使用该标志,最多 100 条 |
## API 入参详情
**HTTP 方法和路径:**
```http
POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records/share_links/batch
```
**请求体:**
```json
{
"record_ids": ["rec001", "rec002", "rec003"]
}
```
> CLI 会自动对 `--record-ids` 去重后再调用接口。
## 返回重点
- 成功时直接返回接口 `data` 字段内容,包含 `record_share_links` 映射key 为 record_idvalue 为分享链接)。结构如下:
```json
{
"record_share_links": {
"rec001": "https://example.feishu.cn/record/TW2wrdbkoeoYXYcwvyIczJ2ZnFb"
}
}
```
- 若部分记录 ID 无权限/不存在,则 `record_share_links` 中只会包含有效记录对应的分享链接
- 若全部记录 ID 都无权限/不存在,则会返回错误信息 `records do not exist or no read permission`
## 坑点
- ⚠️ 单次最多 100 条记录,超出会被 CLI 校验拦截。
- ⚠️ 重复的 record_id 会在调用前自动去重。
- ⚠️ `--record-ids` 为空时会被校验拦截。
## 参考
- [lark-base-record.md](lark-base-record.md) — record 索引页

View File

@ -0,0 +1,50 @@
# base +record-upload-attachment
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
上传本地文件到当前 Base并把附件值写入指定记录的附件字段。
## 推荐命令
```bash
lark-cli base +record-upload-attachment \
--base-token <base_token> \
--table-id <table_id> \
--record-id <record_id> \
--field-id <field_id> \
--file ./report.pdf
lark-cli base +record-upload-attachment \
--base-token <base_token> \
--table-id <table_id> \
--record-id <record_id> \
--field-id "附件" \
--file ./report.pdf \
--name "Q1-final.pdf"
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--record-id <id>` | 是 | 记录 ID |
| `--field-id <id_or_name>` | 是 | 附件字段 ID 或字段名 |
| `--file <path>` | 是 | 本地文件路径,最大 2GB |
| `--name <name>` | 否 | 写入附件字段时显示的文件名,默认使用本地文件名 |
## 工作流
> [!CAUTION]
> 这是写入操作。用户已经明确要上传到某条记录的某个附件字段时可直接执行;如果 `record-id` 或目标字段仍有歧义,再先确认。
## 坑点
- ⚠️ 目标字段必须是 `attachment` 字段。
- ⚠️ 记录里的附件 `file_token` 属于 Drive media token下载时不要走 `lark-cli drive +download`,应使用 `lark-cli docs +media-download --token <file_token> --output <path>`
## 参考
- [lark-base-record.md](lark-base-record.md) — record 索引页

View File

@ -0,0 +1,64 @@
# base +record-upsert
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
创建记录,或在带 `--record-id` 时更新记录。
## 推荐命令
```bash
# 创建记录
lark-cli base +record-upsert --base-token <base_token> --table-id <table_id> \
--json '{"项目名称":"Apollo","状态":"进行中"}'
# 更新记录
lark-cli base +record-upsert --base-token <base_token> --table-id <table_id> --record-id <record_id> \
--json '{"项目名称":"Apollo","状态":"完成","完成时间":"2026-03-24 10:00:00"}'
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--record-id <id>` | 否 | 传入时走更新,不传时走创建 |
| `--json <body>` | 是 | 字段写入对象,类型 `Map<FieldNameOrID, CellValue>` |
## API
- 创建:`POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records`
- 更新:带 `--record-id` 时改走 `PATCH /records/:record_id`
## `--json` 结构
- `--json` 必须是 **JSON object map**,形状是 `Map<FieldNameOrID, CellValue>`
- key 是字段名或字段 IDvalue 是该字段的 `CellValue`
- 一次请求里同一字段只用一种标识,避免重复写入冲突。
- 写入前先 `+field-list` 确认字段类型和字段名/ID。
- CellValue 统一看 [lark-base-cell-value.md](lark-base-cell-value.md)。
```json
{
"项目名称": "Apollo",
"状态": "进行中",
"完成时间": "2026-03-24 10:00:00"
}
```
## 返回重点
- 创建时返回 `record``created: true`
- 更新时返回 `record``updated: true`
- 如果写入了 `formula / lookup / created_at / updated_at / created_by / updated_by` 等只读字段,返回里可能出现 `ignored_fields`,这些字段不会被更新。
## 坑点
- 有 `--record-id` 就一定更新;不传就一定创建,不会自动查重或按业务键 upsert。
- select 写入未知选项时平台可能自动新增选项;如果不是要新增选项,先用 `+field-list` / `+field-search-options` 确认真实选项名。
- 这是写入操作,执行前必须确认目标表和字段。
## 参考
- [lark-base-record.md](lark-base-record.md) — record 索引页
- [lark-base-cell-value.md](lark-base-cell-value.md) — CellValue 格式规范

View File

@ -0,0 +1,29 @@
# base record shortcuts
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
record 相关命令索引。
## 命令导航
| 文档 | 命令 | 说明 |
|------|------|------|
| [lark-base-record-read-sop.md](lark-base-record-read-sop.md) | `+record-get` / `+record-search` / `+record-list` | 记录读取统一选路、筛选排序投影 SOP |
| [lark-base-record-upsert.md](lark-base-record-upsert.md) | `+record-upsert` | 创建或更新记录 |
| [lark-base-record-batch-create.md](lark-base-record-batch-create.md) | `+record-batch-create` | 按 `fields/rows` 批量创建记录 |
| [lark-base-record-batch-update.md](lark-base-record-batch-update.md) | `+record-batch-update` | 批量更新记录 |
| [lark-base-record-upload-attachment.md](lark-base-record-upload-attachment.md) | `+record-upload-attachment` | 上传本地文件到附件字段并更新记录 |
| [`../../lark-doc/references/lark-doc-media-download.md`](../../lark-doc/references/lark-doc-media-download.md) | `lark-cli docs +media-download` | 下载 Base 附件到本地(附件的 `file_token` 来自 `+record-get` 的附件字段) |
| [lark-base-record-delete.md](lark-base-record-delete.md) | `+record-delete` | 删除一条或多条记录 |
| [lark-base-record-share-link-create.md](lark-base-record-share-link-create.md) | `+record-share-link-create` | 生成记录分享链接(支持单条或批量,最多 100 条)|
## 说明
- 读取记录前优先阅读 [lark-base-record-read-sop.md](lark-base-record-read-sop.md),它合并了 `+record-get` / `+record-search` / `+record-list` 的选路和 SOP。
- 聚合页只保留目录职责;写入、删除、历史等命令的详细说明请进入对应单命令文档。
- 所有 `+xxx-list` 调用都必须串行执行;若要批量跑多个 list 请求,只能串行执行。
- `+record-list` 支持重复传参 `--field-id` 做字段筛选。
- `+record-get` 支持重复 `--record-id``--json '{"record_id_list":[...]}'` 批量读取;也支持重复传参 `--field-id` 裁剪返回字段,避免返回全字段。
- 写记录 JSON 前优先阅读 [lark-base-cell-value.md](lark-base-cell-value.md)。
- 本地文件写入附件字段时,必须使用 `+record-upload-attachment`
- 从附件字段下载文件时,用 `lark-cli docs +media-download --token <file_token> --output <path>`,用法见 [`../../lark-doc/references/lark-doc-media-download.md`](../../lark-doc/references/lark-doc-media-download.md)。

View File

@ -0,0 +1,89 @@
# base +role-create
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
>
> **前置条件:** 需要base开启了高级权限如果调用这个接口返回没有开启高级权限可以参考 [`lark-base-advperm-enable.md`](lark-base-advperm-enable.md) 了解高级权限启用规则。
在指定 Base 中创建一个自定义角色,需传入完整的 AdvPermBaseRoleConfig 作为角色配置。
## 推荐命令
```bash
# 创建简单角色(仅设置名称和类型)
lark-cli base +role-create \
--base-token VwGhb**************fMnod \
--json '{"role_name":"财务审核员","role_type":"custom_role"}'
# 创建角色并配置表权限、字段权限、记录筛选
lark-cli base +role-create \
--base-token VwGhb**************fMnod \
--json '{"role_name":"财务审核员","role_type":"custom_role","base_rule_map":{"copy":false,"download":false},"table_rule_map":{"订单表":{"perm":"edit","record_rule":{"record_operations":["add"],"edit_filter_rule_group":{"conjunction":"and","filter_rules":[{"conjunction":"and","filters":[{"field_name":"部门","operator":"is","filter_values":["财务部"]}]}]},"other_record_all_read":true},"field_rule":{"field_perm_mode":"specify","field_perms":{"金额":"edit","备注":"read","密码":"no_perm"}}},"用户表":{"perm":"read_only"}},"dashboard_rule_map":{"销售看板":{"perm":"read_only"}}}'
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token27 位字母数字字符串 |
| `--json <body>` | 是 | AdvPermBaseRoleConfig JSON包含角色名称、类型、权限配置 |
## API 入参详情
**HTTP 方法和路径:**
```
POST /open-apis/base/v3/bases/:base_token/roles
```
**Path 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `base_token` | 是 | Base 的唯一标识27 位字母数字字符串 |
**Request BodyJSON — AdvPermBaseRoleConfig**
> 完整的权限配置结构详见 [role-config.md](role-config.md)
## API 出参详情
**Response**
| 字段 | 类型 | 说明 |
|------|------|------|
| `code` | int32 | 错误码0 表示成功 |
| `message` | string | 错误信息 |
| `data` | string | 创建成功时为空 |
## 返回值
命令成功后输出 JSON
```json
{
"ok": true,
"data": {
"success": true
}
}
```
## 工作流
1. 向用户确认 `--base-token` 和角色配置 JSON
2. 执行命令
3. 确认返回 `code: 0` 表示创建成功
## 坑点
- ⚠️ **role_type 必须为 custom_role**:创建接口仅支持 `custom_role`,传其他值会报业务错误
- ⚠️ **API 路径版本**:本接口使用 `base/v3`,路径必须从原始文档提取,不要用 WebSearch 补全
- ⚠️ **data 字段是 JSON 字符串**:响应中 `data` 是 string 类型(非 object需要双重解析
- ⚠️ **Filter 字段**`field_name`、`operator`、`filter_values` 由客户端传入,`field_type`、`field_ui_type`、`reference_type` 由服务端 filterFiller 自动补全
- ⚠️ **前置条件**Base 必须已开启高级权限,操作用户必须为 Base 管理员
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,83 @@
# base +role-delete
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
>
> **前置条件:** 需要base开启了高级权限如果调用这个接口返回没有开启高级权限可以参考 [`lark-base-advperm-enable.md`](lark-base-advperm-enable.md) 了解高级权限启用规则。
删除指定的自定义角色。系统角色editor / reader不可删除。
## 推荐命令
```bash
lark-cli base +role-delete \
--base-token VwGhb**************fMnod \
--role-id rolxxxxxx4
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token27 位字母数字字符串 |
| `--role-id <id>` | 是 | 角色 ID格式 `rol` + 8 位字母数字 |
## API 入参详情
**HTTP 方法和路径:**
```
DELETE /open-apis/base/v3/bases/:base_token/roles/:role_id
```
**Path 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `base_token` | 是 | Base 的唯一标识 |
| `role_id` | 是 | 角色 ID |
无 Query 参数,无 Request Body。
## API 出参详情
**Response**
| 字段 | 类型 | 说明 |
|------|------|------|
| `code` | int32 | 错误码0 表示成功 |
| `message` | string | 错误信息 |
| `data` | string | 删除成功时为空 |
## 返回值
命令成功后输出 JSON
```json
{
"ok": true,
"data": {
"success": true
}
}
```
## 工作流
> [!CAUTION]
> 这是**高风险不可逆操作** — 删除后无法恢复,执行前必须向用户二次确认。
1. 建议先用 `+role-list` 确认角色存在
2. 向用户确认 `--base-token``--role-id`
3. 执行命令
4. 确认返回 `code: 0`
## 坑点
- ⚠️ **仅支持删除自定义角色**系统角色editor / reader不可删除会报业务错误
- ⚠️ **不可逆操作**:删除后角色及其关联的成员配置无法恢复
- ⚠️ **role_id 来源**:可通过 `+role-list` 获取,格式为 `rol` + 8 位字母数字
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,87 @@
# base +role-get
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
>
> **前置条件:** 需要base开启了高级权限如果调用这个接口返回没有开启高级权限可以参考 [`lark-base-advperm-enable.md`](lark-base-advperm-enable.md) 了解高级权限启用规则。
获取指定角色的完整配置详情AdvPermBaseRole包含表权限、字段权限、记录筛选等全量配置。
## 推荐命令
```bash
lark-cli base +role-get \
--base-token VwGhb**************fMnod \
--role-id rolxxxxxx4
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token27 位字母数字字符串 |
| `--role-id <id>` | 是 | 角色 ID格式 `rol` + 8 位字母数字 |
| `--format <fmt>` | 否 | 输出格式json / pretty / table / csv / ndjson |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/roles/:role_id
```
**Path 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `base_token` | 是 | Base 的唯一标识 |
| `role_id` | 是 | 角色 ID |
无 Query 参数,无 Request Body。
## API 出参详情
**Response `data` 字段JSON 字符串,需双重解析):**
完整的 AdvPermBaseRole 对象(结构详见 [role-config.md](role-config.md)
| 字段 | 类型 | 说明 |
|------|------|------|
| `role_id` | string | 角色 ID |
| `role_name` | string | 角色名称 |
| `role_type` | string | 角色类型 |
| `base_rule_map` | map\<string, bool\> | Base 级权限copy/download |
| `table_rule_map` | map\<string, TableRule\> | 数据表权限配置 |
| `dashboard_rule_map` | map\<string, DashboardRule\> | 仪表盘权限配置 |
| `docx_rule_map` | map\<string, DocxRule\> | 文档权限配置 |
## 返回值
命令成功后输出完整角色配置 JSON
```json
{
"ok": true,
"data": {
"role_id": "rolxxxxxx4",
"role_name": "财务审核员",
"role_type": "custom_role",
"base_rule_map": {"copy": false, "download": false},
"table_rule_map": {
"订单表": {"perm": "edit", "record_rule": {...}, "field_rule": {...}},
"用户表": {"perm": "read_only"}
}
}
}
```
## 坑点
- ⚠️ **与 +role-list 的区别**`+role-list` 只返回摘要role_id/name/type`+role-get` 返回完整的权限配置
- ⚠️ **data 是 JSON 字符串**:响应 `data` 是 string 类型,需要双重解析
- ⚠️ **角色不存在时**data 为空字符串,不会报错
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,81 @@
# base +role-list
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
>
> **前置条件:** 需要base开启了高级权限如果调用这个接口返回没有开启高级权限可以参考 [`lark-base-advperm-enable.md`](lark-base-advperm-enable.md) 了解高级权限启用规则。。
获取指定 Base 下所有角色的摘要信息列表(包括系统角色和自定义角色),返回 role_id / role_name / role_type。
## 推荐命令
```bash
lark-cli base +role-list --base-token VwGhb**************fMnod
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token27 位字母数字字符串 |
| `--format <fmt>` | 否 | 输出格式json / pretty / table / csv / ndjson |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/roles
```
**Path 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `base_token` | 是 | Base 的唯一标识 |
无 Query 参数,无 Request Body。
## API 出参详情
**Response `data` 字段JSON 字符串,需双重解析):**
| 字段 | 类型 | 说明 |
|------|------|------|
| `base_roles` | []string | AIBaseRoleRef 的 JSON 字符串数组 |
| `total` | int32 | 角色总数 |
**AIBaseRoleRef 结构:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `role_id` | string | 角色 ID`rolxxxxxx1` |
| `role_name` | string | 角色名称 |
| `role_type` | string | 角色类型:`editor` / `reader` / `custom_role` |
## 返回值
命令成功后输出 JSON
```json
{
"ok": true,
"data": {
"base_roles": [
"{\"role_id\":\"rolxxxxxx1\",\"role_name\":\"技术管理员\",\"role_type\":\"custom_role\"}",
"{\"role_id\":\"rolxxxxxx2\",\"role_name\":\"编辑者\",\"role_type\":\"editor\"}"
],
"total": 4
}
}
```
## 坑点
- ⚠️ **data 是 JSON 字符串**:响应 `data` 是 string 类型,内部 `base_roles` 数组的每个元素也是 JSON 字符串,需要多层解析
- ⚠️ **返回摘要信息**:此接口只返回 role_id / role_name / role_type不含完整权限配置。要获取完整配置请用 `+role-get`
- ⚠️ **包含系统角色**:返回结果包含 editor / reader 两个系统角色
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,94 @@
# base +role-update
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
>
> **前置条件:** 需要base开启了高级权限如果调用这个接口返回没有开启高级权限可以参考 [`lark-base-advperm-enable.md`](lark-base-advperm-enable.md) 了解高级权限启用规则。
更新指定角色的权限配置。
## 推荐命令
```bash
# 仅修改角色名称
lark-cli base +role-update \
--base-token VwGhb**************fMnod \
--role-id rolxxxxxx4 \
--json '{"role_name":"高级审核员","role_type":"custom_role"}'
# 修改某个表的权限(其他表不受影响)
lark-cli base +role-update \
--base-token VwGhb**************fMnod \
--role-id rolxxxxxx4 \
--json '{"role_name":"财务审核员","role_type":"custom_role","table_rule_map":{"订单表":{"perm":"read_only"}}}'
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token27 位字母数字字符串 |
| `--role-id <id>` | 是 | 角色 ID格式 `rol` + 8 位字母数字 |
| `--json <body>` | 是 | 增量 AdvPermBaseRoleConfig JSON仅含需变更的字段 |
## API 入参详情
**HTTP 方法和路径:**
```
PUT /open-apis/base/v3/bases/:base_token/roles/:role_id
```
**Path 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `base_token` | 是 | Base 的唯一标识 |
| `role_id` | 是 | 角色 ID |
**Request BodyJSON — 增量 AdvPermBaseRoleConfig**
> 完整的权限配置结构详见 [role-config.md](role-config.md)**更新时需要在原始配置上修改传入的role配置信息尽量完整**。
## API 出参详情
**Response**
| 字段 | 类型 | 说明 |
|------|------|------|
| `code` | int32 | 错误码0 表示成功 |
| `message` | string | 错误信息 |
| `data` | string | 更新成功时为空 |
## 返回值
命令成功后输出 JSON
```json
{
"ok": true,
"data": {
"success": true
}
}
```
## 工作流
> [!CAUTION]
> 这是**高危写入操作** — 执行前必须向用户确认,需要 `--yes` 标志。
1. 建议先用 `+role-get` 获取当前配置,确认变更范围
2. 向用户确认 `--base-token`、`--role-id` 和变更 JSON对于变化的部分可以高亮提示用户
3. 执行命令
4. 确认返回 `code: 0`
## 坑点
- ⚠️ **Delta Merge 语义**:仅传入需变更的字段即可,未传入的保持不变。例如只传 `table_rule_map` 中某个表的 perm其他表不受影响
- ⚠️ **role_name 和 role_type 必填**:即使不修改也必须传入当前值
- ⚠️ **body 不能为 nil**PUT 请求必须传 body否则服务端返回错误
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,481 @@
# base shortcut field JSON 规范lark-base-shortcut-field-properties
> 适用命令:`lark-cli base +field-create`、`lark-cli base +field-update`
本文件定义 **shortcut 写字段**`--json` 的推荐格式,是字段类型与字段 JSON 结构的 source of truth。目标不是复刻完整 schema而是让 agent 稳定产出正确 payload。
## 1. 顶层规则(必须遵守)
- `--json` 必须是 JSON 对象。
- 顶层统一使用:`type` + `name` + 类型特有字段。
- 所有字段类型都支持可选 `description`;支持纯文本,也支持 Markdown 链接。
- 不要使用旧结构:`field_name`、`property`、`ui_type`、数字枚举 `type`
- `+field-update` 使用同样的字段 JSON 结构,但语义是 `PUT`;建议先 `+field-get` 再按目标状态全量提交。
- `type=formula``type=lookup` 创建/更新前,必须先读对应 guide。
推荐示例:
```json
{
"type": "text",
"name": "需求背景",
"description": "记录需求背景与已知约束"
}
```
## 2. 字段速查
| 类型 | 最小必填字段 | 常见补充字段 |
|------|--------------|-------------|
| `text` | `type` `name` | `style.type` |
| `number` | `type` `name` | `style` |
| `select` | `type` `name` | `multiple` + `options`,或 `multiple` + `dynamic_options_source` |
| `datetime` | `type` `name` | `style.format` |
| `created_at` / `updated_at` | `type` `name` | `style.format` |
| `user` / `group_chat` | `type` `name` | `multiple` |
| `created_by` / `updated_by` | `type` `name` | 无 |
| `link` | `type` `name` `link_table` | `bidirectional` `bidirectional_link_field_name` |
| `formula` | `type` `name` `expression` | 无 |
| `lookup` | `type` `name` `from` `select` `where` | `aggregate` |
| `auto_number` | `type` `name` | `style.rules` |
| `attachment` / `location` / `checkbox` | `type` `name` | 无 |
所有类型都可额外传 `description`;上表的“常见补充字段”只列类型特有配置。
## 3. 各类型写法
### 3.1 text
文本字段;电话、超链接、邮箱、条码也都属于 `text`,通过 `style.type` 区分。
最小写法(默认 `style.type``plain`
```json
{
"type": "text",
"name": "标题"
}
```
常用写法:
```json
{
"type": "text",
"name": "标题",
"description": "主标题字段"
}
```
```json
{
"type": "text",
"name": "联系电话",
"style": { "type": "phone" }
}
```
```json
{
"type": "text",
"name": "官网",
"style": { "type": "url" }
}
```
常用 `style.type``plain`(默认)、`phone`、`url`、`email`、`barcode`。
### 3.2 number
数字字段;货币、进度、评分都属于 `number`,通过 `style.type` 区分。
最小写法(默认 `style.type``plain`
```json
{
"type": "number",
"name": "工时"
}
```
`style` 是按 `type` 区分的对象;不同 `style.type` 的内部字段不一样,不要混传。
#### `plain`
支持字段:`precision`、`percentage`、`thousands_separator`
默认值 / 约束:
- `precision` 取值 `0..4`,默认 `2`
- `percentage` 默认 `false`
- `thousands_separator` 默认 `false`
```json
{
"type": "number",
"name": "工时",
"style": {
"type": "plain",
"precision": 2,
"percentage": false,
"thousands_separator": true
}
}
```
#### `currency`
支持字段:`precision`、`currency_code`
默认值 / 约束:
- `precision` 取值 `0..4`,默认 `2`
- `currency_code` 必填,如 `CNY`、`USD`、`EUR`
```json
{
"type": "number",
"name": "预算",
"style": { "type": "currency", "precision": 2, "currency_code": "CNY" }
}
```
#### `progress`
支持字段:`percentage`、`color`
默认值 / 约束:
- `percentage` 默认 `true`
- `color` 必填
- `color` 可用:`Blue`、`Purple`、`DarkGreen`、`Green`、`Cyan`、`Orange`、`Red`、`Gray`、`WhiteToBlueGradient`、`WhiteToPurpleGradient`、`WhiteToOrangeGradient`、`GreenToRedGradient`、`RedToGreenGradient`、`BlueToPinkGradient`、`PinkToBlueGradient`、`SpectralGradient`
```json
{
"type": "number",
"name": "完成度",
"style": { "type": "progress", "percentage": true, "color": "Blue" }
}
```
#### `rating`
支持字段:`icon`、`min`、`max`
默认值 / 约束:
- `icon` 默认 `star`
- `icon` 可用:`star`、`heart`、`thumbsup`、`fire`、`smile`、`lightning`、`flower`、`number`
- `min` 取值 `0..1`,默认 `1`
- `max` 取值 `1..10`,默认 `5`
```json
{
"type": "number",
"name": "评分",
"style": { "type": "rating", "icon": "star", "min": 1, "max": 5 }
}
```
### 3.3 select
单选和多选都使用 `select`;用 `multiple` 区分。`multiple` 默认 `false`。静态选项用 `options`,动态选项用 `dynamic_options_source`;两者不要同时传。
#### 静态选项
支持字段:`multiple`、`options`
默认值 / 约束:
- `multiple` 默认 `false`
- `options` 最多 `10000`
- `options[]` 结构是 `{name, hue?, lightness?}`
- `options[].name` 必填
- `options[].hue` 可用:`Red`、`Orange`、`Yellow`、`Lime`、`Green`、`Turquoise`、`Wathet`、`Blue`、`Carmine`、`Purple`、`Gray` 缺省值为 `Blue`
- `options[].lightness` 可用:`Lighter`、`Light`、`Standard`、`Dark`、`Darker` 缺省值为 `Lighter`
- 选项里没有 `id`,只有 `name`
```json
{
"type": "select",
"name": "状态",
"multiple": false,
"options": [
{ "name": "Todo", "hue": "Blue", "lightness": "Lighter" },
{ "name": "Done", "hue": "Green", "lightness": "Light" }
]
}
```
#### 动态选项
支持字段:`multiple`、`dynamic_options_source`
默认值 / 约束:
- `multiple` 默认 `false`
- `dynamic_options_source` 结构是 `{table_id, field_id}`
- `dynamic_options_source.table_id` 填来源表 id 或表名
- `dynamic_options_source.field_id` 填来源字段 id 或字段名
- `dynamic_options_source` 仅创建支持;更新已有字段时不要传
```json
{
"type": "select",
"name": "动态状态",
"multiple": false,
"dynamic_options_source": {
"table_id": "选项表",
"field_id": "候选状态"
}
}
```
### 3.4 datetime
手动填写的日期/时间字段。系统时间用 `created_at` / `updated_at`
最小写法:
```json
{
"type": "datetime",
"name": "截止时间"
}
```
支持字段:`style.format`
默认值 / 约束:
- `style.format` 默认 `yyyy/MM/dd` 可用格式:`yyyy/MM/dd`、`yyyy/MM/dd HH:mm`、`yyyy/MM/dd HH:mm Z`、`yyyy-MM-dd`、`yyyy-MM-dd HH:mm`、`yyyy-MM-dd HH:mm Z`、`MM-dd`、`MM/dd/yyyy`、`dd/MM/yyyy`
常用写法:
```json
{
"type": "datetime",
"name": "截止时间",
"style": { "format": "yyyy-MM-dd HH:mm" }
}
```
### 3.5 created_at / updated_at
系统创建时间 / 系统更新时间字段;可配显示格式,但记录写入时应视为只读。
支持字段:`style.format`
默认值 / 约束:
- `style.format` 默认 `yyyy/MM/dd`
- 可用格式:`yyyy/MM/dd`、`yyyy/MM/dd HH:mm`、`yyyy/MM/dd HH:mm Z`、`yyyy-MM-dd`、`yyyy-MM-dd HH:mm`、`yyyy-MM-dd HH:mm Z`、`MM-dd`、`MM/dd/yyyy`、`dd/MM/yyyy`
```json
{ "type": "created_at", "name": "创建时间" }
```
```json
{ "type": "updated_at", "name": "更新时间", "style": { "format": "yyyy/MM/dd HH:mm" } }
```
### 3.6 user / group_chat
人员字段和群字段都支持 `multiple`
默认值 / 约束:
- `multiple` 默认 `true`
```json
{ "type": "user", "name": "负责人", "multiple": true }
```
```json
{ "type": "group_chat", "name": "负责群", "multiple": true }
```
### 3.7 created_by / updated_by
系统创建人 / 系统修改人字段;记录写入时应视为只读。
```json
{ "type": "created_by", "name": "创建人" }
```
```json
{ "type": "updated_by", "name": "更新人" }
```
### 3.8 link
关联字段;`link_table` 必填。
支持字段:`link_table`、`bidirectional`、`bidirectional_link_field_name`
默认值 / 约束:
- `link_table` 必填
- `link` 字段的单元格表示“当前记录关联到的对侧表记录集合”
- `bidirectional` 默认 `false`
- `bidirectional=true` 时,会在被关联表自动创建一个反向关联字段。任一侧记录的关联关系发生变更时,另一侧对应记录会自动同步更新
- `bidirectional_link_field_name` 仅在 `bidirectional=true` 时使用
```json
{
"type": "link",
"name": "关联任务",
"link_table": "任务表"
}
```
双向关联:
```json
{
"type": "link",
"name": "关联任务",
"link_table": "任务表",
"bidirectional": true,
"bidirectional_link_field_name": "反向关联"
}
```
更新时注意:
- `link` 不允许转换为其他类型,其他类型也不能转换为 `link`
- 现有 `link` 字段的 `bidirectional` 不能改。
### 3.9 formula
公式字段;`expression` 必填。创建/更新前先读 [formula-field-guide.md](formula-field-guide.md) 学习公式语法。
```json
{
"type": "formula",
"name": "合计",
"expression": "1+1"
}
```
### 3.10 lookup
查找引用字段;`from`、`select`、`where` 必填,`aggregate` 可选。创建/更新前先读 [lookup-field-guide.md](lookup-field-guide.md)。
支持字段:`from`、`select`、`where`、`aggregate`
默认值 / 约束:
- `from`、`select`、`where` 必填
- `aggregate` 默认 `raw_value` 代表不进行聚合,直接返回 select 回的原始值
- `aggregate` 可用:`raw_value`、`sum`、`average`、`counta`、`unique_counta`、`max`、`min`、`unique`
- `where.logic` 默认 `and`,仅支持 `and` / `or`
- `where.conditions` 至少 1 条
- `conditions` 每项是三元组 `[field, op, value?]`
```json
{
"type": "lookup",
"name": "状态汇总",
"from": "任务表",
"select": "状态",
"where": {
"logic": "and",
"conditions": [
["负责人", "==", { "type": "field_ref", "field": "当前负责人" }],
["状态", "non_empty", null]
]
},
"aggregate": "raw_value"
}
```
### 3.11 auto_number
自动编号字段;不写 `style.rules` 时使用默认规则:`NO.001`。
最小写法:
```json
{
"type": "auto_number",
"name": "编号"
}
```
支持字段:`style.rules`
默认值 / 约束:
- `style.rules` 是规则数组,数量 `1..9`
- 默认规则:
```json
{
"style": {
"rules": [
{ "type": "text", "text": "NO." },
{ "type": "incremental_number", "length": 3 }
]
}
}
```
#### `text`
支持字段:`text`
```json
{ "type": "text", "text": "TASK-" }
```
#### `incremental_number`
支持字段:`length`
默认值 / 约束:
- `length` 取值 `1..9`
```json
{ "type": "incremental_number", "length": 4 }
```
#### `created_time`
支持字段:`date_format`
默认值 / 约束:
- `date_format` 可用:`yyyyMMdd`、`yyyyMM`、`yyMM`、`MMdd`、`yyyy`、`MM`、`dd`
```json
{ "type": "created_time", "date_format": "yyyyMMdd" }
```
自定义规则:
```json
{
"type": "auto_number",
"name": "编号",
"style": {
"rules": [
{ "type": "text", "text": "TASK-" },
{ "type": "created_time", "date_format": "yyyyMMdd" },
{ "type": "incremental_number", "length": 4 }
]
}
}
```
### 3.12 attachment / location / checkbox
```json
{ "type": "attachment", "name": "附件" }
```
```json
{ "type": "location", "name": "位置" }
```
```json
{ "type": "checkbox", "name": "完成" }
```
## 4. 创建与更新
- `+field-create`:按目标字段配置直接构造 `--json`
- `+field-update`:使用同样的 JSON 结构,但语义是 `PUT`;建议先 `+field-get`,再按目标完整状态提交。
## 5. 易错点
- `select` 只有一个类型;不要写 `single_select` / `multi_select`,用 `multiple` 控制是否多选。
- `number` 的精度、货币、进度、评分配置都放在 `style` 下,不要写顶层 `precision`
- `datetime` 是手动日期字段;系统时间请改用 `created_at` / `updated_at`
- `formula` / `lookup` 没读 guide 前不要直接写。

View File

@ -0,0 +1,62 @@
# base +table-create
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
创建数据表;可选地继续创建字段和视图。
## 推荐命令
```bash
lark-cli base +table-create \
--base-token app_xxx \
--name "客户名单"
lark-cli base +table-create \
--base-token app_xxx \
--name "项目管理" \
--fields '[{"name":"项目名称","type":"text"}]' \
--view '[{"name":"默认表格","type":"grid"}]'
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--name <name>` | 是 | 新表名称 |
| `--fields <json>` | 否 | 字段 JSON 数组 |
| `--view <json>` | 否 | 视图 JSON 对象或数组 |
## API 入参详情
**HTTP 方法和路径:**
```
POST /open-apis/base/v3/bases/:base_token/tables
```
- 如果传了 `--fields`CLI 会继续调用字段接口。
- 如果传了 `--view`CLI 会继续调用视图接口。
## 返回重点
- 至少返回 `table`
- 传了 `--fields` / `--view` 时,还会附带 `fields` / `views`
## 工作流
1. 先只传 `--name` 建空表。
2. 字段或视图参数较复杂时,先精简到最小必需字段,再以内联 JSON 传参。
## 坑点
- ⚠️ 这是写入操作,执行前必须确认。
- ⚠️ CLI 会用 `--fields` 的第一个元素更新系统默认首列,后续元素才是新增字段。
- ⚠️ 不要并行改同一张表,避免状态竞争。
## 参考
- [lark-base-table.md](lark-base-table.md) — table 索引页
- [lark-base-field-create.md](lark-base-field-create.md) — 建字段
- [lark-base-view-create.md](lark-base-view-create.md) — 建视图

View File

@ -0,0 +1,51 @@
# base +table-delete
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
删除一张表。
## 推荐命令
```bash
lark-cli base +table-delete \
--base-token app_xxx \
--table-id tbl_xxx \
--yes
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
## API 入参详情
**HTTP 方法和路径:**
```
DELETE /open-apis/base/v3/bases/:base_token/tables/:table_id
```
## 返回重点
- 返回 `deleted: true` 以及删除目标的 `table_id / table_name`
## 工作流
> 这是**高风险写入操作**。CLI 层要求显式传 `--yes`;如果用户已经明确要求删除且目标明确,直接执行并带上 `--yes`,不要再补一次确认。
1. 建议先用 `+table-list``+table-get` 确认目标。
2. 只有当目标表仍不明确时,才继续追问;如果删除意图和目标都明确,直接执行。
## 坑点
- ⚠️ 高风险不可逆操作。
- ⚠️ 删除场景强烈建议传 `tbl_xxx`,不要传表名。
- ⚠️ 忘记带 `--yes` 会被 CLI 拦截。
## 参考
- [lark-base-table.md](lark-base-table.md) — table 索引页
- [lark-base-table-list.md](lark-base-table-list.md) — 列表

View File

@ -0,0 +1,46 @@
# base +table-get
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
获取一张表的聚合信息:表基础信息、全部字段、全部视图。
## 推荐命令
```bash
lark-cli base +table-get \
--base-token app_xxx \
--table-id tbl_xxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID`id` 必须以 `tbl` 开头)或表名 |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/tables/:table_id
```
- CLI 内部还会继续查询 `/fields``/views`,并聚合输出。
## 返回重点
- 返回 `table`、`fields`、`views` 三段数据。
- 适合先摸清表结构,再继续字段或视图操作。
## 坑点
- ⚠️ 如果 `--table-id` 传的是 `id`,必须是 `tbl` 开头;不是的话先询问用户具体是哪张表,或先用 `+table-list` 查表列表再确认。
- ⚠️ `--table-id` 支持传表名,但重名场景下建议优先传 `tbl_xxx`
## 参考
- [lark-base-table.md](lark-base-table.md) — table 索引页
- [lark-base-field-list.md](lark-base-field-list.md) — 列字段
- [lark-base-view-list.md](lark-base-view-list.md) — 列视图

View File

@ -0,0 +1,43 @@
# base +table-list
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
分页列出一个 Base 下的数据表。
## 推荐命令
```bash
lark-cli base +table-list \
--base-token app_xxx \
--offset 0 \
--limit 50
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--offset <n>` | 否 | 分页偏移,默认 `0` |
| `--limit <n>` | 否 | 分页大小,默认 `50`,范围 `1-100` |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/tables
```
## 返回重点
- 返回 `items / offset / limit / count / total`
- `items` 会被简化为 `table_id``table_name`
## 坑点
- ⚠️ `+table-list` 禁止并发调用;批量列多个 Base 时必须串行。
## 参考
- [lark-base-table.md](lark-base-table.md) — table 索引页

View File

@ -0,0 +1,49 @@
# base +table-update
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
重命名一张表。
## 推荐命令
```bash
lark-cli base +table-update \
--base-token app_xxx \
--table-id tbl_xxx \
--name "重点客户名单"
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--name <name>` | 是 | 新表名 |
## API 入参详情
**HTTP 方法和路径:**
```
PATCH /open-apis/base/v3/bases/:base_token/tables/:table_id
```
## 返回重点
- 返回 `table``updated: true`
- 当前只支持更新名称。
## 工作流
1. 建议先用 `+table-get` 确认目标表。
## 坑点
- ⚠️ 这是写入操作,执行前必须确认。
## 参考
- [lark-base-table.md](lark-base-table.md) — table 索引页
- [lark-base-table-get.md](lark-base-table-get.md) — 查表详情

View File

@ -0,0 +1,20 @@
# base table shortcuts
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
table 相关命令索引。
## 命令导航
| 文档 | 命令 | 说明 |
|------|------|------|
| [lark-base-table-list.md](lark-base-table-list.md) | `+table-list` | 分页列出数据表 |
| [lark-base-table-get.md](lark-base-table-get.md) | `+table-get` | 获取单表概要、字段和视图 |
| [lark-base-table-create.md](lark-base-table-create.md) | `+table-create` | 创建数据表,可附带字段 / 视图 |
| [lark-base-table-update.md](lark-base-table-update.md) | `+table-update` | 重命名数据表 |
| [lark-base-table-delete.md](lark-base-table-delete.md) | `+table-delete` | 删除数据表 |
## 说明
- 聚合页只保留目录职责;调用任一 table 命令前,务必先阅读对应单命令文档(本页不提供调用细节)。
- 所有 `+xxx-list` 调用都必须串行执行;若要批量跑多个 list 请求,只能串行执行。

View File

@ -0,0 +1,50 @@
# base +view-create
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
创建一个视图。
## 1. 顶层规则
- --json 结构是 `{name, type?}`
- `name` 必填;同表内应唯一。
- `type` 可省略;省略时默认 `grid`
- 视图类型取值范围:`grid`、`kanban`、`gallery`、`calendar`、`gantt`。
- `+view-create` 不负责排序、分组、筛选、时间轴、卡片封面、可见字段顺序;这些配置需要创建后再调用对应命令。
- 表单视图不走 `+view-create`;使用表单相关命令。
## 2. 推荐命令
```bash
lark-cli base +view-create \
--base-token <base_token> \
--table-id <table_id> \
--json '{"name":"进行中","type":"grid"}'
```
## 3. JSON 写法
```json
{ "name": "进行中", "type": "grid" }
```
最小写法:
```json
{ "name": "默认视图" }
```
## 4. 使用建议
- 需要设置可见字段顺序时,创建后继续调用 [lark-base-view-set-visible-fields.md](lark-base-view-set-visible-fields.md)。
- 需要设置筛选、分组、排序、时间轴、卡片封面时,创建后继续调用对应 `+view-set-*` 命令。
## 5. 易错点
- 不要把 `form` 当成 `type` 传进来。
- 不要指望 `+view-create` 一次完成视图布局与属性配置。
## 6. 参考
- [lark-base-view.md](lark-base-view.md)
- [lark-base-view-set-visible-fields.md](lark-base-view-set-visible-fields.md)

View File

@ -0,0 +1,48 @@
# base +view-delete
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
删除一个视图。
## 推荐命令
```bash
lark-cli base +view-delete \
--base-token app_xxx \
--table-id tbl_xxx \
--view-id viw_xxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--view-id <id_or_name>` | 是 | 视图 ID 或视图名 |
## API 入参详情
**HTTP 方法和路径:**
```
DELETE /open-apis/base/v3/bases/:base_token/tables/:table_id/views/:view_id
```
## 返回重点
- 返回删除成功状态和目标视图标识。
## 工作流
1. 建议先用 `+view-get` 确认目标视图。
2. 删除前必须让用户确认。
## 坑点
- ⚠️ 高风险写操作,删除后不可恢复。
## 参考
- [lark-base-view.md](lark-base-view.md) — view 索引页

View File

@ -0,0 +1,38 @@
# base +view-get-card
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
获取卡片配置。
## 推荐命令
```bash
lark-cli base +view-get-card \
--base-token app_xxx \
--table-id tbl_xxx \
--view-id viw_xxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--view-id <id_or_name>` | 是 | 视图 ID 或视图名 |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/tables/:table_id/views/:view_id/card
```
## 返回重点
- 返回原始卡片配置。
## 参考
- [lark-base-view.md](lark-base-view.md) — view 索引页

View File

@ -0,0 +1,38 @@
# base +view-get-filter
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
获取筛选配置。
## 推荐命令
```bash
lark-cli base +view-get-filter \
--base-token app_xxx \
--table-id tbl_xxx \
--view-id viw_xxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--view-id <id_or_name>` | 是 | 视图 ID 或视图名 |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/tables/:table_id/views/:view_id/filter
```
## 返回重点
- 返回原始筛选配置。
## 参考
- [lark-base-view.md](lark-base-view.md) — view 索引页

View File

@ -0,0 +1,38 @@
# base +view-get-group
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
获取分组配置。
## 推荐命令
```bash
lark-cli base +view-get-group \
--base-token app_xxx \
--table-id tbl_xxx \
--view-id viw_xxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--view-id <id_or_name>` | 是 | 视图 ID 或视图名 |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/tables/:table_id/views/:view_id/group
```
## 返回重点
- 返回原始分组配置。
## 参考
- [lark-base-view.md](lark-base-view.md) — view 索引页

View File

@ -0,0 +1,38 @@
# base +view-get-sort
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
获取排序配置。
## 推荐命令
```bash
lark-cli base +view-get-sort \
--base-token app_xxx \
--table-id tbl_xxx \
--view-id viw_xxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--view-id <id_or_name>` | 是 | 视图 ID 或视图名 |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/tables/:table_id/views/:view_id/sort
```
## 返回重点
- 返回原始排序配置。
## 参考
- [lark-base-view.md](lark-base-view.md) — view 索引页

View File

@ -0,0 +1,38 @@
# base +view-get-timebar
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
获取时间轴配置。
## 推荐命令
```bash
lark-cli base +view-get-timebar \
--base-token app_xxx \
--table-id tbl_xxx \
--view-id viw_xxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--view-id <id_or_name>` | 是 | 视图 ID 或视图名 |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/tables/:table_id/views/:view_id/timebar
```
## 返回重点
- 返回原始时间轴配置。
## 参考
- [lark-base-view.md](lark-base-view.md) — view 索引页

View File

@ -0,0 +1,28 @@
# base +view-get-visible-fields
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
获取可见字段配置。
## 1. 顶层规则
- 读取当前视图的可见字段列表与顺序。
- 仅 `grid` / `kanban` / `gallery` / `calendar` / `gantt` 视图支持。
## 2. 推荐命令
```bash
lark-cli base +view-get-visible-fields \
--base-token <base_token> \
--table-id <table_id> \
--view-id <view_id>
```
## 3. 返回重点
- 返回当前视图可见字段列表。
- 返回结果中的主字段会位于第一位。
## 参考
- [lark-base-view.md](lark-base-view.md) — view 索引页

View File

@ -0,0 +1,38 @@
# base +view-get
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
获取一个视图的基本信息。
## 推荐命令
```bash
lark-cli base +view-get \
--base-token app_xxx \
--table-id tbl_xxx \
--view-id viw_xxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--view-id <id_or_name>` | 是 | 视图 ID 或视图名 |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/tables/:table_id/views/:view_id
```
## 返回重点
- 返回视图的基础配置。
## 参考
- [lark-base-view.md](lark-base-view.md) — view 索引页

View File

@ -0,0 +1,44 @@
# base +view-list
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
分页列出一张表下的视图。
## 推荐命令
```bash
lark-cli base +view-list \
--base-token app_xxx \
--table-id tbl_xxx \
--offset 0 \
--limit 100
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--offset <n>` | 否 | 分页偏移,默认 `0` |
| `--limit <n>` | 否 | 分页大小,默认 `100`,范围 `1-200` |
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/tables/:table_id/views
```
## 返回重点
- 返回 `views``total`
## 坑点
- ⚠️ `+view-list` 禁止并发调用;批量列多个表视图时只能串行。
## 参考
- [lark-base-view.md](lark-base-view.md) — view 索引页

View File

@ -0,0 +1,44 @@
# base +view-rename
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
重命名一个视图。
## 推荐命令
```bash
lark-cli base +view-rename \
--base-token app_xxx \
--table-id tbl_xxx \
--view-id viw_xxx \
--name "进行中客户"
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--view-id <id_or_name>` | 是 | 视图 ID 或视图名 |
| `--name <name>` | 是 | 新视图名 |
## API 入参详情
**HTTP 方法和路径:**
```
PATCH /open-apis/base/v3/bases/:base_token/tables/:table_id/views/:view_id
```
## 返回重点
- 返回更新后的 `view` 数据。
## 坑点
- ⚠️ 这是写入操作,执行前必须确认。
## 参考
- [lark-base-view.md](lark-base-view.md) — view 索引页

View File

@ -0,0 +1,55 @@
# base +view-set-card
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
更新卡片封面配置。
## 1. 顶层规则
- `--json` 必须是 JSON 对象。
- 仅 `gallery` / `kanban` 视图支持。
- `cover_field` 必填;可传 `attachment` 类型的字段 id、字段名`null`
## 2. 推荐命令
设置封面:
```bash
lark-cli base +view-set-card \
--base-token <base_token> \
--table-id <table_id> \
--view-id <view_id> \
--json '{"cover_field":"fld_cover"}'
```
## 3. JSON 写法
```json
{
"cover_field": "fld_cover"
}
```
```json
{
"cover_field": null
}
```
## 4. 使用建议
- 建议先用 [lark-base-view-get-card.md](lark-base-view-get-card.md) 读取现状,再改。
- 优先传字段 id不要依赖字段名。
- 普通文本、数字、选择字段不能作为封面字段。
## 5. 易错点
- 不要传空字符串;清空时传 `null`
- 不要在 `grid` / `calendar` / `gantt` 视图上调用。
- 不要假设任意字段都能做封面;稳定做法是先找 `attachment` 字段。
## 6. 参考
- [lark-base-view.md](lark-base-view.md)
- [lark-base-view-get-card.md](lark-base-view-get-card.md)

View File

@ -0,0 +1,181 @@
# base +view-set-filter
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
更新视图筛选配置。
## 1. 顶层规则
- `--json` 必须是 JSON 对象。
- 顶层结构是 `{logic?, conditions?}`
- `logic` 默认 `and`;推荐只用 canonical 值 `and` / `or`
- `conditions` 默认空数组。
- 每条条件写成 tuple`[field, operator, value?]`。
- `empty` / `non_empty` 可写成 2 项:`[field, "empty"]`、`[field, "non_empty"]`。
- 支持 `filter` 的视图类型:`grid`、`kanban`、`gallery`、`calendar`、`gantt`。
## 2. operator
可用 operator
- `==`
- `!=`
- `>`
- `>=`
- `<`
- `<=`
- `intersects`
- `disjoint`
- `empty`
- `non_empty`
## 3. value 写法
### `text` / `location`
用字符串:
```json
["标题", "intersects", "发布"]
```
### `number` / `auto_number`
用数字:
```json
["工时", ">=", 3.5]
```
### `select`
用选项名数组:
```json
["状态", "intersects", ["Doing", "Blocked"]]
```
### `user` / `created_by` / `updated_by`
用对象数组:
> **人员筛选:不要猜 ID。** 不知道 `open_id` 时,先用 `lark-contact` 查 id`lark-cli contact +search-user --query "<姓名/邮箱/手机号>" --as user`。
```json
["负责人", "intersects", [{ "id": "ou_xxx" }]]
```
### `group_chat`
用对象数组:
> **群组筛选:不要猜 ID。** 不知道 `chat_id` 时,先用 `lark-im` 搜群:`lark-cli im +chat-search --query "<群名关键词>" --as user`;取结果里的 `oc_xxx`
```json
["负责群", "intersects", [{ "id": "oc_xxx" }]]
```
### `link`
用记录 id 对象数组:
```json
["关联任务", "intersects", [{ "id": "rec_xxx" }]]
```
### `checkbox`
用布尔值:
```json
["完成", "==", true]
```
### `datetime` / `created_at` / `updated_at`
用相对时间关键字或 `ExactDate(...)`
```json
["截止时间", "==", "ExactDate(2026-01-01)"]
```
```json
["截止时间", "==", "ExactDate(2026-01-01 11:30)"]
```
```json
["截止时间", "==", "Today"]
```
可用关键字:
- `Today`
- `Yesterday`
- `Tomorrow`
### `formula` / `lookup`
- 筛选值类型由字段计算结果类型动态决定。
- 拿不准时,先把 `value` 当作单个字符串填入做一次尝试。
- 如果报错,再按错误提示把 `value` 改成对应类型。
字符串示例:
```json
["风险说明", "intersects", "高风险"]
```
数字示例:
```json
["汇总分", ">=", 80]
```
## 4. 推荐命令
```bash
lark-cli base +view-set-filter \
--base-token <base_token> \
--table-id <table_id> \
--view-id <view_id> \
--json '{"logic":"and","conditions":[["状态","intersects",["Doing"]],["负责人","intersects",[{"id":"ou_xxx"}]],["截止时间","empty"]]}'
```
## 5. JSON 写法
```json
{
"logic": "and",
"conditions": [
["状态", "intersects", ["Doing"]],
["负责人", "intersects", [{ "id": "ou_xxx" }]],
["截止时间", "empty"]
]
}
```
清空写法:
```json
{
"conditions": []
}
```
## 6. 使用建议
- 建议先用 [lark-base-view-get-filter.md](lark-base-view-get-filter.md) 读取现状,再改。
- 优先传字段 id不要依赖字段名。
- 需要清空全部筛选时,直接传 `{"conditions":[]}`
## 7. 易错点
- 不要再写旧对象风格:`{"field_name":...,"operator":...}`。
- `user` / `group_chat` / `link` 不要写成单个标量。
- `empty` / `non_empty` 不要硬塞无意义的 value。
- 日期条件稳定写法用 `ExactDate(...)``Today` / `Yesterday` / `Tomorrow`
- `formula` / `lookup` 的 value 形状不固定;拿不准时先读当前 filter 或字段定义,或根据错误提示修正类型。
## 8. 参考
- [lark-base-view.md](lark-base-view.md)
- [lark-base-view-get-filter.md](lark-base-view-get-filter.md)
- [lookup-field-guide.md](lookup-field-guide.md)

View File

@ -0,0 +1,65 @@
# base +view-set-group
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
更新视图分组配置。
## 1. 顶层规则
- `--json` 必须是 JSON 对象。
- 顶层写法固定为 `{"group_config":[...]}`
- 每项写 `{ "field": "<field_id_or_name>", "desc": false }`
- `desc` 可省略;省略时等价于 `false`
- 仅 `grid` / `kanban` / `gantt` 视图支持。
- `group_config` 传空数组 `[]` 表示清空分组。
- 分组字段必须是当前视图可分组的字段;字段存在也不代表一定可分组。
## 2. 推荐命令
设置分组:
```bash
lark-cli base +view-set-group \
--base-token <base_token> \
--table-id <table_id> \
--view-id <view_id> \
--json '{"group_config":[{"field":"fld_status","desc":false}]}'
```
清空分组:
```bash
lark-cli base +view-set-group \
--base-token <base_token> \
--table-id <table_id> \
--view-id <view_id> \
--json '{"group_config":[]}'
```
## 3. JSON 写法
```json
{
"group_config": [
{ "field": "fld_status", "desc": false }
]
}
```
## 4. 使用建议
- 优先传字段 id不要依赖字段名。
- 建议先用 [lark-base-view-get-group.md](lark-base-view-get-group.md) 读取现状。
- 只传对象;不要传 `[]``[{"field":"..."}]` 这类裸数组。
- 提交项数不要超过 3如果当前视图实际允许更少以错误提示为准收敛。
## 5. 易错点
- 不要把 `group_config` 写成对象。
- 不要拿当前视图不支持分组的字段去分组。
- 不要在 `gallery` / `calendar` 视图上调用。
## 6. 参考
- [lark-base-view.md](lark-base-view.md)
- [lark-base-view-get-group.md](lark-base-view-get-group.md)

View File

@ -0,0 +1,63 @@
# base +view-set-sort
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
更新视图排序配置。
## 1. 顶层规则
- `--json` 必须是 JSON 对象。
- 顶层写法固定为 `{"sort_config":[...]}`
- `sort_config` 最多 10 项。
- 每项写 `{ "field": "<field_id_or_name>", "desc": false }`
- `desc` 可省略;省略时等价于 `false`
- 仅 `grid` / `kanban` / `gallery` / `gantt` 视图支持。
## 2. 推荐命令
设置排序:
```bash
lark-cli base +view-set-sort \
--base-token <base_token> \
--table-id <table_id> \
--view-id <view_id> \
--json '{"sort_config":[{"field":"fld_priority","desc":true},{"field":"fld_created_at","desc":false}]}'
```
清空排序:
```bash
lark-cli base +view-set-sort \
--base-token <base_token> \
--table-id <table_id> \
--view-id <view_id> \
--json '{"sort_config":[]}'
```
## 3. JSON 写法
```json
{
"sort_config": [
{ "field": "fld_priority", "desc": true }
]
}
```
## 4. 使用建议
- 优先传字段 id不要依赖字段名。
- 如需覆盖已有排序,建议先用 [lark-base-view-get-sort.md](lark-base-view-get-sort.md) 读取现状。
- 只传对象;不要传 `[]``[{"field":"..."}]` 这类裸数组。
## 5. 易错点
- 不要把 `sort_config` 写成对象。
- 不要超过 10 项。
- 不要在 `calendar` 这类不支持排序配置的视图上调用。
## 6. 参考
- [lark-base-view.md](lark-base-view.md)
- [lark-base-view-get-sort.md](lark-base-view-get-sort.md)

View File

@ -0,0 +1,51 @@
# base +view-set-timebar
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
更新时间轴配置。
## 1. 顶层规则
- `--json` 必须是 JSON 对象。
- 顶层固定写 `start_time`、`end_time`、`title` 三个字段,三者都必填。
- `start_time` / `end_time` 必须是当前时间轴支持的日期字段。
- `title` 必须是当前表中已存在的字段。
- 仅 `calendar` / `gantt` 视图支持。
## 2. 推荐命令
```bash
lark-cli base +view-set-timebar \
--base-token <base_token> \
--table-id <table_id> \
--view-id <view_id> \
--json '{"start_time":"fld_start","end_time":"fld_end","title":"fld_title"}'
```
## 3. JSON 写法
```json
{
"start_time": "fld_start",
"end_time": "fld_end",
"title": "fld_title"
}
```
## 4. 使用建议
- 优先传字段 id不要依赖字段名。
- `start_time` / `end_time` 稳定做法优先使用日期时间字段。
- `title` 通常传主字段或文本标题字段。
- 建议先用 [lark-base-view-get-timebar.md](lark-base-view-get-timebar.md) 读取现状。
## 5. 易错点
- 不要把普通文本、选项、链接字段写到 `start_time` / `end_time`
- 不要漏传 `title`
- 不要在 `grid` / `gallery` / `kanban` 视图上调用。
## 6. 参考
- [lark-base-view.md](lark-base-view.md)
- [lark-base-view-get-timebar.md](lark-base-view-get-timebar.md)

View File

@ -0,0 +1,46 @@
# base +view-set-visible-fields
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
更新视图可见字段列表(同时控制视图中的字段顺序)。
## 1. 顶层规则
- `--json` 必须是 JSON 对象。
- 顶层固定写法:`{"visible_fields":[...]}`
- `visible_fields` 每项可传字段 id 或字段名。
- 仅 `grid` / `kanban` / `gallery` / `calendar` / `gantt` 视图支持。
## 2. 推荐命令
```bash
lark-cli base +view-set-visible-fields \
--base-token <base_token> \
--table-id <table_id> \
--view-id <view_id> \
--json '{"visible_fields":["标题","fld_status"]}'
```
## 3. JSON 写法
```json
{
"visible_fields": ["标题", "fld_status"]
}
```
## 4. 使用建议
- 优先传字段 id不要依赖字段名。
- 数组顺序用于控制视图字段顺序。
- 如果未包含主字段 `primaryField`,结果中会自动补到第一位。
## 5. 易错点
- 不要传裸数组:`["fld_a","fld_b"]`;必须包成 `{"visible_fields":[...]}`
- 如果传字段名,必须与当前表真实字段名精确匹配。
- 最终返回顺序可能与传入顺序不同,因为主字段会被强制置顶。
## 参考
- [lark-base-view.md](lark-base-view.md) — view 索引页

View File

@ -0,0 +1,44 @@
# base view shortcuts
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
view 相关命令索引。
## 命令导航
| 文档 | 命令 | 说明 |
|------|------|------|
| [lark-base-view-list.md](lark-base-view-list.md) | `+view-list` | 分页列视图 |
| [lark-base-view-get.md](lark-base-view-get.md) | `+view-get` | 获取视图基本信息 |
| [lark-base-view-create.md](lark-base-view-create.md) | `+view-create` | 创建视图 |
| [lark-base-view-delete.md](lark-base-view-delete.md) | `+view-delete` | 删除视图 |
| [lark-base-view-rename.md](lark-base-view-rename.md) | `+view-rename` | 重命名视图 |
| [lark-base-view-get-filter.md](lark-base-view-get-filter.md) | `+view-get-filter` | 读取筛选配置 |
| [lark-base-view-set-filter.md](lark-base-view-set-filter.md) | `+view-set-filter` | 更新筛选配置 |
| [lark-base-view-get-visible-fields.md](lark-base-view-get-visible-fields.md) | `+view-get-visible-fields` | 读取可见字段列表 |
| [lark-base-view-set-visible-fields.md](lark-base-view-set-visible-fields.md) | `+view-set-visible-fields` | 更新可见字段列表 |
| [lark-base-view-get-group.md](lark-base-view-get-group.md) | `+view-get-group` | 读取分组配置 |
| [lark-base-view-set-group.md](lark-base-view-set-group.md) | `+view-set-group` | 更新分组配置 |
| [lark-base-view-get-sort.md](lark-base-view-get-sort.md) | `+view-get-sort` | 读取排序配置 |
| [lark-base-view-set-sort.md](lark-base-view-set-sort.md) | `+view-set-sort` | 更新排序配置 |
| [lark-base-view-get-timebar.md](lark-base-view-get-timebar.md) | `+view-get-timebar` | 读取时间轴配置 |
| [lark-base-view-set-timebar.md](lark-base-view-set-timebar.md) | `+view-set-timebar` | 更新时间轴配置 |
| [lark-base-view-get-card.md](lark-base-view-get-card.md) | `+view-get-card` | 读取卡片配置 |
| [lark-base-view-set-card.md](lark-base-view-set-card.md) | `+view-set-card` | 更新卡片配置 |
## AI 决策前置
先判断视图类型,再选接口能力;不支持的能力直接不要调用。
| 视图类型 | 可用能力 |
|------|------|
| `grid` | `group` `sort` `filter` `visible_fields` |
| `kanban` | `group` `sort` `filter` `card` `visible_fields` |
| `gallery` | `sort` `filter` `card` `visible_fields` |
| `calendar` | `filter` `timebar` `visible_fields` |
| `gantt` | `group` `sort` `filter` `timebar` `visible_fields` |
## 说明
- 聚合页只保留目录职责;每个命令的详细说明请进入对应单命令文档。
- 所有 `+xxx-list` 调用都必须串行执行;若要批量跑多个 list 请求,只能串行执行。

View File

@ -0,0 +1,180 @@
# base +workflow-create
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
在 Base 中创建一个新的自动化工作流。新建后状态为 `disabled`,需调用 `+workflow-enable` 才能启用。
## ⚠️ 执行前必读
创建工作流前请按顺序完成:
1. **先读本文档**,了解 `--json` 参数格式和 `client_token` 的必填要求
2. **阅读 [workflow-guide.md](lark-base-workflow-guide.md)**,获取 Loop、IfElseBranch、SwitchBranch 等**完整示例**
3. **参考 [workflow-schema.md](lark-base-workflow-schema.md)**,查询具体字段定义
4. **不需要先调用 `+workflow-list`**,创建操作不依赖现有工作流列表
5. **按需调用 `+table-list` 和 `+field-list`** 获取表名和字段名(只在需要时调用)
6. **若遇到单多选字段,可调用`+field-get`命令**来获取选项详情
7. **调用命令时,请将 body 数据直接通过 --json 传入,禁止创建任何的临时文件,即禁止使用 --json @filename**
**常见错误**:
- ❌ 缺少 `client_token` → 报错: `client token is empty`
- ❌ 猜测 StepType → 报错: `unknown step type 'CreateTrigger'`(应该是 `AddRecordTrigger`
- ❌ 字段引用路径错误 → 报错: `prompt references an unknown reference`
> 💡 **提示**: 复杂场景(循环、分支、多步骤组合)的完整示例请直接阅读 [workflow-guide.md](lark-base-workflow-guide.md),本文档只包含基础用法。
## 推荐命令
```bash
lark-cli base +workflow-create \
--base-token BascXxxxxx \
--json '{"client_token":"1704067200","title":"新订单自动通知","steps":[{"id":"trigger_1","type":"AddRecordTrigger","title":"监控新订单","next":"action_1","data":{"table_name":"订单表","watched_field_name":"订单号"}},{"id":"action_1","type":"LarkMessageAction","title":"发送通知","next":null,"data":{"receiver":[{"value_type":"user","value":{"id":"ou_xxxx"}}],"send_to_everyone":false,"title":[{"value_type":"text","value":"新订单提醒"}],"content":[{"value_type":"text","value":"收到新订单"}],"btn_list":[]}}]}'
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | 多维表格 Base Token`Basc` 开头) |
| `--json <body>` | 是 | 工作流 body JSON包含 `title``steps` |
## 如何从链接中提取参数
用户通常会提供如下 URL
```
https://example.feishu.cn/base/<base_token>
```
- `--base-token`:取 `/base/` 后面的字符串(`Basc` 开头)
## API 入参详情
**HTTP 方法和路径:**
```
POST /open-apis/base/v3/bases/:base_token/workflows
```
**Path 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `base_token` | 是 | 多维表格 Base Token |
**Request BodyJSON**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `client_token` | string | **是** | 幂等键,每次创建必须传一个唯一随机值(如时间戳 `"1704067200"`),防重复提交,缺失会报错 |
| `title` | string | 否 | 工作流标题 |
| `steps` | WorkflowStep[] | 否 | 步骤列表 |
| `user_id_type` | string | 否 | 用户 ID 类型:`open_id` / `union_id` / `user_id`,默认 `open_id` |
> **步骤数据结构非常复杂,详见 [lark-base-workflow-schema.md](lark-base-workflow-schema.md)**
**Request Body 示例:**
```json
{
"client_token": "124131231421312312",
"title": "新订单自动通知",
"steps": [
{
"id": "trigger_1",
"type": "AddRecordTrigger",
"title": "监控新订单",
"next": "action_1",
"data": {
"table_name": "订单表",
"watched_field_name": "订单号"
}
},
{
"id": "action_1",
"type": "LarkMessageAction",
"title": "发送通知",
"next": null,
"data": {
"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" }
],
"btn_list": []
}
}
]
}
```
## API 出参详情
**Response `data` 字段:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `workflow_id` | string | 新建工作流的唯一 ID`wkf` 开头) |
| `title` | string | 工作流标题 |
| `status` | string | 工作流状态,新建固定为 `disabled` |
| `steps` | WorkflowStep[] | 完整步骤列表 |
| `creator_id` | string | 创建人 OpenID |
| `updater_id` | string | 最后修改人 OpenID |
| `create_time` | number | 创建时间Unix 秒级时间戳) |
| `update_time` | number | 更新时间Unix 秒级时间戳) |
## 返回值
```json
{
"ok": true,
"data": {
"workflow_id": "wkfosaYTS1V6rhjF",
"title": "新订单自动通知",
"status": "disabled",
"steps": [...],
"creator_id": "ou_xxxx",
"updater_id": "ou_xxxx",
"create_time": 1704067200,
"update_time": 1704067200
}
}
```
## 工作流
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。
1. 与用户确认 `--base-token` 和工作流定义(`--json` 内容)
2. 执行命令,报告返回的 `workflow_id``wkf` 开头)
3. 提示用户:新建工作流初始状态为 `disabled`,需调用 `+workflow-enable --workflow-id <返回的 workflow_id>` 才会生效
## 坑点
> ⚠️ **【重要】client_token 必传**:缺失会返回 `[code=800004006] client token is empty`,这**不是权限问题**,是 **JSON body 缺字段**。每次请求传唯一值即可(如 `"$(date +%s)"``"1743078000"`
- ⚠️ **新建后默认禁用**`status` 固定返回 `disabled`,需要额外调用 `+workflow-enable` 才能让工作流生效;不要误报"创建成功即启用"
- ⚠️ **steps 中 id 字段必须唯一**:每个步骤的 `id` 由调用方指定,且在工作流内必须唯一;`next` 和 `children.links[].to` 引用的 ID 必须在同一 steps 数组中存在,否则服务端返回 `[2200] Internal Error`
- ⚠️ **字段类型校验**:设置字段值时,`value_type` 必须与字段实际类型匹配:
- **select 类型字段**(单选/多选/流程):必须用 `option`,不能用 `text`
```json
// ✅ 正确
{ "field_name": "大区", "value": [{"value_type": "option", "value": {"name": "华东"}}] }
// ❌ 错误 - 会报错 valueType 'text' not allowed for fieldType '3'
{ "field_name": "大区", "value": [{"value_type": "text", "value": "华东"}] }
```
- **SetRecordTrigger 的 field_watch_info** 同样受此限制select 类型字段的 value 必须用 `option`
常见 action 输出:`FindRecordAction` → `$.step_id.recordNum`(记录数)、`$.step_id.fieldRecords`(查找到的记录列表);`AddRecordAction` → `$.step_id.recordId`
- ⚠️ **权限不足**:如遇 `permission denied`先确认当前身份bot 或 user是否对该 Base 有编辑权限,再检查 scope 是否已开通。参考 [lark-shared](../../lark-shared/SKILL.md) 中的权限不足处理流程
- ⚠️ **user_id_type**:涉及用户的 `value_type: "user"` 的 value 字段传 OpenID服务端会根据 `user_id_type`(默认 `open_id`)解析;如需传 `user_id` 格式需在 body 里显式声明 `"user_id_type": "user_id"`
## 参考
- [lark-base-workflow-schema.md](lark-base-workflow-schema.md) — 完整 Workflow 数据结构
- [lark-base-workflow-update](lark-base-workflow-update.md) — 全量更新工作流
- [lark-base-workflow-enable](lark-base-workflow-enable.md) — 启用工作流
- [lark-base-workflow-list](lark-base-workflow-list.md) — 列出全部工作流
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,94 @@
# base +workflow-disable
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
禁用 Base 中的一个自动化工作流。
## 推荐命令
```bash
lark-cli base +workflow-disable \
--base-token BascXxxxxx \
--workflow-id wkfxxxxxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | 多维表格 Base Token`Basc` 开头) |
| `--workflow-id <id>` | 是 | 工作流 ID`wkf` 开头) |
## 如何从链接中提取参数
用户通常会提供如下 URL
```
https://example.feishu.cn/base/<base_token>?table=<table_or_workflow_id>
```
- `--base-token`:取 `/base/` 后面的字符串(`Basc` 开头)
- `--workflow-id`:取 `?table=` 后面的值,当其以 `wkf` 开头时即为 workflow_id
> ⚠️ **注意区分 ID 前缀**table_id 以 `tbl` 开头workflow_id 以 `wkf` 开头,两者在 URL 的 `?table=` 参数里都会出现,需要根据前缀判断。
## API 入参详情
**HTTP 方法和路径:**
```
PATCH /open-apis/base/v3/bases/:base_token/workflows/:workflow_id/disable
```
**Path 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `base_token` | 是 | 多维表格 Base Token |
| `workflow_id` | 是 | 工作流唯一标识 |
**Request Body** 无
## API 出参详情
**Response `data` 字段:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `workflow_id` | string | 工作流唯一标识符 |
| `status` | string | 操作后的最新状态,固定为 `disabled` |
## 返回值
```json
{
"ok": true,
"data": {
"workflow_id": "wkfxxxxxx",
"status": "disabled"
}
}
```
## 工作流
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。
1. 向用户确认 `--base-token``--workflow-id`
2. 执行命令
3. 报告返回的 `status` 字段,确认值为 `disabled`
## 坑点
- ⚠️ **workflow_id 来源**workflow_id 以 `wkf` 开头,从 URL 的 `?table=wkf...` 参数提取,不是表格的 table_id`tbl` 开头),混淆会导致 `[2200] Internal Error`
- ⚠️ **API 路径末尾动词**`/disable` 是路径的最后一段,不是 body 字段;漏掉这个后缀会命中错误接口
- ⚠️ **PATCH body 不能为 nil**:接口虽无请求体,仍需传 `{}` 空对象,否则服务端可能返回 `server time out error`
- ⚠️ **scope 待确认**:内部文档未列出权限名称,代码中使用 `base:workflow:update`,如遇 `[230013] permission denied` 需核对实际 scope
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-base-workflow-enable](lark-base-workflow-enable.md) — 启用工作流
- [lark-base-workflow-list](lark-base-workflow-list.md) — 列出全部工作流
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,94 @@
# base +workflow-enable
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
启用 Base 中的一个自动化工作流。
## 推荐命令
```bash
lark-cli base +workflow-enable \
--base-token BascXxxxxx \
--workflow-id wkfxxxxxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | 多维表格 Base Token`Basc` 开头) |
| `--workflow-id <id>` | 是 | 工作流 ID`wkf` 开头) |
## 如何从链接中提取参数
用户通常会提供如下 URL
```
https://example.feishu.cn/base/<base_token>?table=<table_or_workflow_id>
```
- `--base-token`:取 `/base/` 后面的字符串(`Basc` 开头)
- `--workflow-id`:取 `?table=` 后面的值,当其以 `wkf` 开头时即为 workflow_id
> ⚠️ **注意区分 ID 前缀**table_id 以 `tbl` 开头workflow_id 以 `wkf` 开头,两者在 URL 的 `?table=` 参数里都会出现,需要根据前缀判断。
## API 入参详情
**HTTP 方法和路径:**
```
PATCH /open-apis/base/v3/bases/:base_token/workflows/:workflow_id/enable
```
**Path 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `base_token` | 是 | 多维表格 Base Token |
| `workflow_id` | 是 | 工作流唯一标识 |
**Request Body** 无
## API 出参详情
**Response `data` 字段:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `workflow_id` | string | 工作流唯一标识符 |
| `status` | string | 操作后的最新状态,固定为 `enabled` |
## 返回值
```json
{
"ok": true,
"data": {
"workflow_id": "wkfxxxxxx",
"status": "enabled"
}
}
```
## 工作流
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。
1. 向用户确认 `--base-token``--workflow-id`
2. 执行命令
3. 报告返回的 `status` 字段,确认值为 `enabled`
## 坑点
- ⚠️ **workflow_id 来源**workflow_id 以 `wkf` 开头,从 URL 的 `?table=wkf...` 参数提取,不是表格的 table_id`tbl` 开头),混淆会导致 `[2200] Internal Error`
- ⚠️ **API 路径末尾动词**`/enable` 是路径的最后一段,不是 body 字段;漏掉这个后缀会命中错误接口
- ⚠️ **PATCH body 不能为 nil**:接口虽无请求体,仍需传 `{}` 空对象,否则服务端可能返回 `server time out error`
- ⚠️ **scope 待确认**:内部文档未列出权限名称,代码中使用 `base:workflow:update`,如遇 `[230013] permission denied` 需核对实际 scope
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-base-workflow-disable](lark-base-workflow-disable.md) — 禁用工作流
- [lark-base-workflow-list](lark-base-workflow-list.md) — 列出全部工作流
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,147 @@
# base +workflow-get
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
> 💡 **按需查阅:** 如需深入理解返回的 `steps` 节点结构,可参考 [workflow-schema.md](lark-base-workflow-schema.md)。简单统计(如节点数量)无需阅读 schema。
获取一个 workflow 的完整定义包括标题、状态、所有步骤steps及其配置。
## 推荐命令
```bash
# 基本用法
lark-cli base +workflow-get \
--base-token BascXxxxxx \
--workflow-id wkfxxxxxx
# 指定用户 ID 类型creator_id / updater_id 字段的格式)
lark-cli base +workflow-get \
--base-token BascXxxxxx \
--workflow-id wkfxxxxxx \
--user-id-type open_id
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | 多维表格 Base Token`Basc` 开头 |
| `--workflow-id <id>` | 是 | Workflow ID`wkf` 开头 |
| `--user-id-type <type>` | 否 | 控制 `creator_id` / `updater_id` 字段返回的用户 ID 格式;枚举值:`open_id`(默认)、`union_id`、`user_id` |
## 如何从链接中提取参数
用户通常会提供如下 URL在 Base 的自动化管理页面复制):
```
https://xxx.feishu.cn/base/<base_token>?table=<table_id>&view=<view_id>
```
- `--base-token`:取 `/base/` 后面的字符串(`Basc` 开头)
- `--workflow-id`:以 `wkf` 开头,可从 `+workflow-list` 的输出中获取URL 上通常不直接暴露
## API 入参详情
**HTTP 方法和路径:**
```
GET /open-apis/base/v3/bases/:base_token/workflows/:workflow_id
```
**Path 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `base_token` | 是 | 多维表格 Base Token |
| `workflow_id` | 是 | Workflow ID`wkf` 开头) |
**Query 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `user_id_type` | 否 | 用户 ID 类型:`open_id` / `union_id` / `user_id` |
**Request Body** 无
## API 出参详情
**Response `data` 字段:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `workflow_id` | string | Workflow 唯一标识,`wkf` 开头 |
| `title` | string | Workflow 标题 |
| `status` | string | 状态:`enabled`(已启用)/ `disabled`(已停用) |
| `creator_id` | string | 创建人 ID格式受 `user_id_type` 控制) |
| `updater_id` | string | 最后修改人 ID |
| `create_time` | string | 创建时间Unix 时间戳,秒) |
| `update_time` | string | 最后更新时间Unix 时间戳,秒) |
| `steps` | []step | 步骤列表,见下方 |
**steps 元素字段:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | string | 步骤唯一 ID |
| `type` | string | 步骤类型,见 [workflow-schema.md](lark-base-workflow-schema.md) |
| `title` | string | 步骤标题 |
| `children` | object | 子关系边,`children.links[]` 承担分支/循环/slot 连线,结构见 schema |
| `children.links[].kind` | string | 关系类型:`if_true` / `if_false` / `case` / `loop_start` / `slot` |
| `children.links[].to` | string | 目标节点 ID |
| `children.links[].label` | string | 可选标签(如 `branch_1`、`tool` |
| `children.links[].desc` | string | 可选语义说明 |
| `next` | string \| null | 线性后继节点 ID`null` 表示流程结束 |
| `data` | object | 步骤详细配置,结构随 `type` 变化,见 [workflow-schema.md](lark-base-workflow-schema.md) |
| `meta` | object | 扩展元信息,仅 `APIHubAction` / `AIAgentLLMAction` / `AIAgentMCPAction` 等子节点有值 |
> 步骤类型(`type`)的完整枚举和每种类型的 `data` 字段结构,参见 [lark-base-workflow-schema.md](lark-base-workflow-schema.md)。
## 返回值
命令成功后输出 JSON
```json
{
"ok": true,
"data": {
"workflow_id": "wkfxxxxxx",
"title": "新订单自动通知",
"status": "enabled",
"creator_id": "ou_xxxx",
"updater_id": "ou_xxxx",
"create_time": "1704067200",
"update_time": "1704153600",
"steps": [
{
"id": "trigger_1",
"type": "AddRecordTrigger",
"title": "监控新订单",
"next": "action_1",
"data": { "table_name": "订单表", "watched_field_name": "订单号" }
},
{
"id": "action_1",
"type": "LarkMessageAction",
"title": "发送通知",
"next": null,
"data": { "..." : "..." }
}
]
}
}
```
## 坑点
- ⚠️ **workflow_id 来源**`workflow_id` 以 `wkf` 开头,从 `+workflow-list` 命令输出中获取URL 上通常拿不到,不要把 `table_id``tbl` 开头)误当成 `workflow_id`
- ⚠️ **steps 可能为空**:未配置任何步骤的 workflow 返回的 `steps` 为空数组,不代表接口异常
- ⚠️ **文档中 `title` 字段类型标注为 int**:这是文档笔误,实际为 string
- ⚠️ **API 路径版本**:本接口使用 `base/v3`,路径必须从原始文档提取,禁止用 WebSearch 补全,否则会拿到错误路径导致 `[2200] Internal Error`
- ⚠️ **只读接口GET 请求 body 传 nil**`baseV3Call` 的 body 参数传 `nil` 是正确的,不同于 PATCH/POST 写接口
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-base-workflow-schema.md](lark-base-workflow-schema.md) — Workflow 步骤数据结构完整参考
- [lark-base-workflow-list.md](lark-base-workflow-list.md) — 列出所有 workflow可用来获取 workflow_id
- [lark-base-workflow-create.md](lark-base-workflow-create.md) — 创建 workflow
- [lark-base-workflow-update.md](lark-base-workflow-update.md) — 更新 workflow

View File

@ -0,0 +1,718 @@
# Workflow 构造指南
本文档提供 Workflow 的完整构造示例、常见模式和错误避免指南。
> **配套文档**:
> - Workflow 的数据结构参考:[lark-base-workflow-schema.md](lark-base-workflow-schema.md)
> - 创建命令:[lark-base-workflow-create.md](lark-base-workflow-create.md)
> - 更新命令:[lark-base-workflow-update.md](lark-base-workflow-update.md)
---
## 快速开始
### 最简单的 Workflow
新增记录时发送消息通知:
```json
{
"client_token": "1704067200",
"title": "新订单自动通知",
"steps": [
{
"id": "trigger_1",
"type": "AddRecordTrigger",
"title": "监控新订单",
"next": "action_1",
"data": {
"table_name": "订单表",
"watched_field_name": "订单号"
}
},
{
"id": "action_1",
"type": "LarkMessageAction",
"title": "发送通知",
"next": null,
"data": {
"receiver": [{ "value_type": "user", "value": {"id": "ou_xxxx", "name": "张三"} }],
"send_to_everyone": false,
"title": [{ "value_type": "text", "value": "新订单提醒" }],
"content": [
{ "value_type": "text", "value": "收到新订单" }
],
"btn_list": []
}
}
]
}
```
---
## 场景速查表
| 场景 | 步骤组合 | 示例 |
|------|---------|------|
| 新增触发+通知 | AddRecordTrigger → LarkMessageAction | [下方](#示例1-新增记录触发--发送消息) |
| 定时+循环 | TimerTrigger → FindRecordAction → Loop → LarkMessageAction | [下方](#示例2-定时触发--查找记录--循环遍历--发送消息) |
| 条件判断 | ... → IfElseBranch → 分支处理 | [下方](#示例3-条件分支-ifelsebranch) |
| 多路分类 | ... → SwitchBranch → 多分支处理 | [下方](#示例4-多路分支-switchbranch) |
| 复杂组合 | 定时+查找+循环+分支+消息 | [下方](#示例5-组合场景-定时查找循环分支消息) |
---
## 完整示例
### 示例 1: 新增记录触发 + 发送消息
**场景**: 当订单表新增记录时,发送飞书消息通知负责人。
```json
{
"client_token": "1704067201",
"title": "新订单自动通知",
"steps": [
{
"id": "step_trigger",
"type": "AddRecordTrigger",
"title": "新增订单时触发",
"next": "step_notify",
"data": {
"table_name": "订单表",
"watched_field_name": "订单号",
"condition_list": null
}
},
{
"id": "step_notify",
"type": "LarkMessageAction",
"title": "发送订单通知",
"next": null,
"data": {
"receiver": [{ "value_type": "ref", "value": "$.step_trigger.fldManager" }],
"send_to_everyone": false,
"title": [{ "value_type": "text", "value": "新订单提醒" }],
"content": [
{ "value_type": "text", "value": "客户 " },
{ "value_type": "ref", "value": "$.step_trigger.fldCustomer" },
{ "value_type": "text", "value": " 创建了新订单,金额:¥" },
{ "value_type": "ref", "value": "$.step_trigger.fldAmount" }
],
"btn_list": [
{
"text": "查看订单",
"btn_action": "openLink",
"link": [{ "value_type": "ref", "value": "$.step_trigger.recordLink" }]
}
]
}
}
]
}
```
**关键点**:
- `AddRecordTrigger` 监控 `table_name` 表的 `watched_field_name` 字段
- 使用 `ref` 引用触发器输出的字段值(注意是 fieldId不是字段名
- `recordLink` 是触发器内置输出,表示记录链接
---
### 示例 2: 定时触发 + 查找记录 + 循环遍历 + 发送消息
**场景**: 每天早上 9 点,查找所有待处理订单,给每个客户发送提醒。
```json
{
"client_token": "1704067202",
"title": "每日待处理订单提醒",
"steps": [
{
"id": "step_timer",
"type": "TimerTrigger",
"title": "每天早上9点触发",
"next": "step_find_orders",
"data": {
"rule": "DAILY",
"start_time": "2025-01-01 09:00",
"is_never_end": true
}
},
{
"id": "step_find_orders",
"type": "FindRecordAction",
"title": "查找所有待处理订单",
"next": "step_loop_customers",
"data": {
"table_name": "订单表",
"field_names": ["客户名称", "订单金额", "客户联系方式"],
"should_proceed_when_no_results": false,
"filter_info": {
"conjunction": "and",
"conditions": [
{
"field_name": "状态",
"operator": "is",
"value": [{ "value_type": "option", "value": { "name": "待处理" } }]
}
]
}
}
},
{
"id": "step_loop_customers",
"type": "Loop",
"title": "遍历每个订单",
"children": {
"links": [
{ "kind": "loop_start", "to": "step_send_reminder" }
]
},
"next": null,
"data": {
"loop_mode": "continue",
"max_loop_times": 100,
"data": [{
"value_type": "ref",
"value": "$.step_find_orders.fieldRecords"
}]
}
},
{
"id": "step_send_reminder",
"type": "LarkMessageAction",
"title": "发送催办消息",
"next": null,
"data": {
"receiver": [{
"value_type": "ref",
"value": "$.step_loop_customers.item.fldContact"
}],
"send_to_everyone": false,
"title": [{ "value_type": "text", "value": "订单处理提醒" }],
"content": [
{ "value_type": "text", "value": "您好,您的订单 " },
{ "value_type": "ref", "value": "$.step_loop_customers.item.fldName" },
{ "value_type": "text", "value": " 金额 ¥" },
{ "value_type": "ref", "value": "$.step_loop_customers.item.fldAmount" },
{ "value_type": "text", "value": " 正在处理中。" }
],
"btn_list": []
}
}
]
}
```
**关键点**:
- `Loop.data` 必须传入 `ref` 类型的数据源(通常是 FindRecordAction 的 `fieldRecords`
- `Loop.children.links` 必须包含 `kind: "loop_start"` 的链接指向循环体
- 循环体内用 `$.{loopStepId}.item.{fieldId}` 引用当前遍历记录的字段
- `$.{loopStepId}.index` 获取当前索引(从 0 开始)
---
### 示例 3: 条件分支IfElseBranch
**场景**: 根据订单金额判断,大额订单通知主管审批,小额订单自动通过。
```json
{
"client_token": "1704067203",
"title": "订单金额自动判断",
"steps": [
{
"id": "step_trigger",
"type": "AddRecordTrigger",
"title": "新增订单时触发",
"next": "step_check_amount",
"data": {
"table_name": "订单表",
"watched_field_name": "订单金额"
}
},
{
"id": "step_check_amount",
"type": "IfElseBranch",
"title": "判断是否为大额订单",
"children": {
"links": [
{ "kind": "if_true", "to": "step_notify_manager", "label": "high", "desc": "金额>=10000" },
{ "kind": "if_false", "to": "step_auto_approve", "label": "normal", "desc": "金额<10000" }
]
},
"next": "step_log",
"data": {
"condition": {
"conjunction": "or",
"conditions": [
{
"conjunction": "and",
"conditions": [
{
"left_value": { "value_type": "ref", "value": "$.step_trigger.fldAmount" },
"operator": "isGreaterEqual",
"right_value": [{ "value_type": "number", "value": 10000 }]
}
]
}
]
}
}
},
{
"id": "step_notify_manager",
"type": "LarkMessageAction",
"title": "通知主管审批大额订单",
"next": "step_log",
"data": {
"receiver": [{ "value_type": "user", "value": {"id": "ou_manager", "name": "主管"} }],
"send_to_everyone": false,
"title": [{ "value_type": "text", "value": "大额订单待审批" }],
"content": [
{ "value_type": "text", "value": "有大额订单 ¥" },
{ "value_type": "ref", "value": "$.step_trigger.fldAmount" },
{ "value_type": "text", "value": " 需要您审批" }
],
"btn_list": []
}
},
{
"id": "step_auto_approve",
"type": "SetRecordAction",
"title": "自动标记小额订单为已审核",
"next": "step_log",
"data": {
"table_name": "订单表",
"ref_info": { "step_id": "step_trigger" },
"field_values": [
{
"field_name": "审批状态",
"value": [{ "value_type": "option", "value": { "name": "已自动审核" } }]
}
]
}
},
{
"id": "step_log",
"type": "GenerateAiTextAction",
"title": "生成订单处理日志",
"next": null,
"data": {
"prompt": [
{ "value_type": "text", "value": "请生成订单处理日志,金额:" },
{ "value_type": "ref", "value": "$.step_trigger.fldAmount" }
]
}
}
]
}
```
**关键点**:
- `IfElseBranch.children.links` 必须包含 `if_true``if_false` 两个分支
- `next` 指向两个分支汇合后的步骤(可选,为 null 则分支结束)
- `condition` 使用 OrGroup 结构,支持 `(A and B) or (C and D)` 的复杂条件
- 分支内可以用 `ref_info` 引用触发记录,用 `filter_info` 批量筛选记录
---
### 示例 4: 多路分支SwitchBranch
**场景**: 根据订单优先级P0/P1/P2执行不同的处理流程。
```json
{
"client_token": "1704067204",
"title": "按优先级分类处理订单",
"steps": [
{
"id": "step_trigger",
"type": "AddRecordTrigger",
"title": "新增订单时触发",
"next": "step_classify",
"data": {
"table_name": "订单表",
"watched_field_name": "优先级"
}
},
{
"id": "step_classify",
"type": "SwitchBranch",
"title": "按优先级分类",
"children": {
"links": [
{ "kind": "case", "to": "step_p0_handler", "label": "p0", "desc": "P0-紧急" },
{ "kind": "case", "to": "step_p1_handler", "label": "p1", "desc": "P1-高优先级" },
{ "kind": "case", "to": "step_p2_handler", "label": "p2", "desc": "P2-普通" },
{ "kind": "case", "to": "step_other_handler", "label": "other", "desc": "其他" }
]
},
"next": null,
"data": {
"mode": "exclusive",
"no_match_action": "classifyToOther",
"child_branch_list": [
{
"name": "P0-紧急",
"condition": {
"conjunction": "or",
"conditions": [
{
"conjunction": "and",
"conditions": [
{
"left_value": { "value_type": "ref", "value": "$.step_trigger.fldPriority" },
"operator": "is",
"right_value": [{ "value_type": "option", "value": { "name": "P0" } }]
}
]
}
]
}
},
{
"name": "P1-高优先级",
"condition": {
"conjunction": "or",
"conditions": [
{
"conjunction": "and",
"conditions": [
{
"left_value": { "value_type": "ref", "value": "$.step_trigger.fldPriority" },
"operator": "is",
"right_value": [{ "value_type": "option", "value": { "name": "P1" } }]
}
]
}
]
}
},
{
"name": "P2-普通",
"condition": {
"conjunction": "or",
"conditions": [
{
"conjunction": "and",
"conditions": [
{
"left_value": { "value_type": "ref", "value": "$.step_trigger.fldPriority" },
"operator": "is",
"right_value": [{ "value_type": "option", "value": { "name": "P2" } }]
}
]
}
]
}
}
]
}
},
{
"id": "step_p0_handler",
"type": "LarkMessageAction",
"title": "P0紧急处理",
"next": null,
"data": {
"receiver": [{ "value_type": "user", "value": {"id": "ou_director", "name": "总监"} }],
"send_to_everyone": false,
"title": [{ "value_type": "text", "value": "🚨 P0 紧急订单" }],
"content": [{ "value_type": "text", "value": "有新的 P0 紧急订单需要立即处理" }],
"btn_list": []
}
},
{
"id": "step_p1_handler",
"type": "SetRecordAction",
"title": "标记高优先级",
"next": null,
"data": {
"table_name": "订单表",
"ref_info": { "step_id": "step_trigger" },
"field_values": [
{ "field_name": "处理状态", "value": [{ "value_type": "text", "value": "高优先级待处理" }] }
]
}
},
{
"id": "step_p2_handler",
"type": "Delay",
"title": "普通订单延迟处理",
"next": null,
"data": { "duration": 60 }
},
{
"id": "step_other_handler",
"type": "SetRecordAction",
"title": "标记其他订单",
"next": null,
"data": {
"table_name": "订单表",
"ref_info": { "step_id": "step_trigger" },
"field_values": [
{ "field_name": "处理状态", "value": [{ "value_type": "text", "value": "待分类" }] }
]
}
}
]
}
```
**关键点**:
- `SwitchBranch` 适合 3 路及以上的分支场景(少于 3 路用 `IfElseBranch` 更简洁)
- `children.links``kind: "case"``label` 对应 `child_branch_list` 中的条件
- `mode: "exclusive"` 表示排他执行(第一个匹配的分支执行后停止)
- `no_match_action: "classifyToOther"` 表示无匹配时走最后一个 `case`(兜底分支)
---
### 示例 5: 组合场景(定时+查找+循环+分支+消息)
**场景**: 每天早上 9 点,查找昨天的订单,按金额分级,给不同级别的销售发送不同的通知。
```json
{
"client_token": "1704067205",
"title": "每日订单分级通知",
"steps": [
{
"id": "step_timer",
"type": "TimerTrigger",
"title": "每天早上9点触发",
"next": "step_find_orders",
"data": {
"rule": "DAILY",
"start_time": "2025-01-01 09:00",
"is_never_end": true
}
},
{
"id": "step_find_orders",
"type": "FindRecordAction",
"title": "查找昨天所有订单",
"next": "step_loop",
"data": {
"table_name": "订单表",
"field_names": ["订单号", "客户名称", "金额", "销售负责人"],
"should_proceed_when_no_results": false,
"filter_info": {
"conjunction": "and",
"conditions": [
{ "field_name": "创建时间", "operator": "isGreaterEqual", "value": [{ "value_type": "date", "value": "yesterday" }] }
]
}
}
},
{
"id": "step_loop",
"type": "Loop",
"title": "遍历每个订单",
"children": {
"links": [
{ "kind": "loop_start", "to": "step_classify" }
]
},
"next": "step_summary",
"data": {
"loop_mode": "continue",
"max_loop_times": 500,
"data": [{ "value_type": "ref", "value": "$.step_find_orders.fieldRecords" }]
}
},
{
"id": "step_classify",
"type": "SwitchBranch",
"title": "按金额分类",
"children": {
"links": [
{ "kind": "case", "to": "step_vip_notify", "label": "vip", "desc": "VIP >= 10万" },
{ "kind": "case", "to": "step_normal_notify", "label": "normal", "desc": "普通 < 10万" }
]
},
"next": null,
"data": {
"mode": "exclusive",
"no_match_action": "fail",
"child_branch_list": [
{
"name": "VIP订单",
"condition": {
"conjunction": "or",
"conditions": [
{
"conjunction": "and",
"conditions": [
{
"left_value": { "value_type": "ref", "value": "$.step_loop.item.fldAmount" },
"operator": "isGreaterEqual",
"right_value": [{ "value_type": "number", "value": 100000 }]
}
]
}
]
}
},
{
"name": "普通订单",
"condition": {
"conjunction": "or",
"conditions": [
{
"conjunction": "and",
"conditions": [
{
"left_value": { "value_type": "ref", "value": "$.step_loop.item.fldAmount" },
"operator": "isLess",
"right_value": [{ "value_type": "number", "value": 100000 }]
}
]
}
]
}
}
]
}
},
{
"id": "step_vip_notify",
"type": "LarkMessageAction",
"title": "VIP订单通知",
"next": null,
"data": {
"receiver": [{ "value_type": "ref", "value": "$.step_loop.item.fldSales" }],
"send_to_everyone": false,
"title": [{ "value_type": "text", "value": "🌟 VIP大额订单" }],
"content": [
{ "value_type": "text", "value": "恭喜!您有一笔 VIP 订单 ¥" },
{ "value_type": "ref", "value": "$.step_loop.item.fldAmount" },
{ "value_type": "text", "value": ",客户:" },
{ "value_type": "ref", "value": "$.step_loop.item.fldCustomer" }
],
"btn_list": []
}
},
{
"id": "step_normal_notify",
"type": "LarkMessageAction",
"title": "普通订单通知",
"next": null,
"data": {
"receiver": [{ "value_type": "ref", "value": "$.step_loop.item.fldSales" }],
"send_to_everyone": false,
"title": [{ "value_type": "text", "value": "新订单通知" }],
"content": [
{ "value_type": "text", "value": "您有一笔新订单 ¥" },
{ "value_type": "ref", "value": "$.step_loop.item.fldAmount" }
],
"btn_list": []
}
},
{
"id": "step_summary",
"type": "GenerateAiTextAction",
"title": "生成日报",
"next": null,
"data": {
"prompt": [
{ "value_type": "text", "value": "请生成昨日订单处理日报" }
]
}
}
]
}
```
---
## 构造技巧
### Loop 构造要点
1. **数据源**: `Loop.data` 必须传入 `ref` 类型,通常是 `FindRecordAction``fieldRecords`
2. **循环体**: `children.links` 必须包含 `kind: "loop_start"` 指向循环体入口
3. **引用**: 循环体内用 `$.{loopStepId}.item.{fieldId}` 引用当前元素
4. **索引**: 用 `$.{loopStepId}.index` 获取当前索引(从 0 开始)
### 分支构造要点
1. **IfElseBranch**:
- 适合二元判断(是/否、大于/小于)
- `children.links` 必须包含 `if_true``if_false`
- 可以用 `next` 指向汇合点
2. **SwitchBranch**:
- 适合多路分类3路及以上
- `label` 对应 `child_branch_list` 中的条件顺序
- 建议加一个兜底分支(其他)
### 字段值构造
| 字段类型 | value_type | 示例 |
|---------|------------|------|
| 文本 | `text` | `{"value_type": "text", "value": "张三"}` |
| 数字 | `number` | `{"value_type": "number", "value": 100}` |
| 单选 | `option` | `{"value_type": "option", "value": {"name": "已完成"}}` |
| 人员 | `user` | `{"value_type": "user", "value": {"id": "ou_xxxx"}}` |
| 引用 | `ref` | `{"value_type": "ref", "value": "$.step_1.fldxxx"}` |
---
## 常见错误避免
### Top 10 高频错误
| # | 错误信息 | 原因 | 解决方案 |
|---|---------|------|---------|
| 1 | `path "xxx" does not exist in the output path tree` | ref 引用路径错误或 stepId 不存在 | 检查 stepId 是否在 steps 数组中;使用 fieldId 而非字段名;确保路径以 `$.` 开头 |
| 2 | `recordInfo.conditions must be non-empty` | `condition_list` 为空数组 `[]` | 改用 `null` 或省略该字段 |
| 3 | `At least one of filter info and ref info is required` | SetRecordAction/FindRecordAction 缺少定位条件 | 必须提供 `filter_info``ref_info` 之一 |
| 4 | `client token is empty` | 缺少 `client_token` | 每次请求传入唯一值(时间戳或随机字符串) |
| 5 | `valueType 'text' not allowed for fieldType '3'` | select 类型字段值格式错误 | 改用 `option` 类型 |
| 6 | `Undefined Step Type` | 使用了不支持的 StepType | 使用 `AddRecordTrigger` 而非 `CreateRecordTrigger` |
| 7 | `prompt references an unknown reference from step` | 引用的 stepId 不存在 | 确保引用的 step 在同一 workflow 的 steps 数组中 |
| 8 | `[2200] Internal Error` | 1. steps[].id 重复 2. next/children.links 引用了不存在的 step | 确保所有 step id 唯一;检查引用关系 |
| 9 | 工作流结构不完整 | Branch/Loop 节点缺少 `children` | 仅 BranchIfElseBranch/SwitchBranch和 Loop 节点需要 `children`Trigger/Action 节点无需设置 |
| 10 | 嵌套分支过于复杂 | 多层 IfElseBranch 嵌套 | 3+ 路分支用 SwitchBranch 替代嵌套 IfElseBranch |
### 其他常见错误
**1. condition_list 为空数组**
```json
// ❌ 错误
{ "condition_list": [] }
// ✅ 正确
{ "condition_list": null }
// 或省略该字段
```
**2. filter_info 和 ref_info 同时提供**
```json
// ❌ 错误
{ "filter_info": {...}, "ref_info": {...} }
// ✅ 正确(二选一)
{ "filter_info": {...}, "ref_info": null }
{ "filter_info": null, "ref_info": {...} }
```
**3. 使用字段名而非 fieldId**
```json
// ❌ 错误
{ "value": "$.step_1.客户名称" }
// ✅ 正确
{ "value": "$.step_1.fldXXXXXXXX" }
```
---
## 参考
- [lark-base-workflow-schema.md](lark-base-workflow-schema.md) — 字段定义参考
- [lark-base-workflow-create.md](lark-base-workflow-create.md) — 创建命令
- [lark-base-workflow-update.md](lark-base-workflow-update.md) — 更新命令

View File

@ -0,0 +1,124 @@
# base +workflow-list
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
列出 Base 中所有自动化工作流,自动分页获取全量数据。
## 推荐命令
```bash
# 列出全部工作流
lark-cli base +workflow-list \
--base-token BascXxxxxx
# 只看已启用的工作流
lark-cli base +workflow-list \
--base-token BascXxxxxx \
--status enabled
# 只看已禁用的工作流
lark-cli base +workflow-list \
--base-token BascXxxxxx \
--status disabled
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | 多维表格 Base Token`Basc` 开头) |
| `--status <value>` | 否 | 过滤状态:`enabled` 或 `disabled`;不传则返回全部 |
| `--page-size <n>` | 否 | 每页大小,默认 100最大 100 |
## API 入参详情
**HTTP 方法和路径:**
```
POST /open-apis/base/v3/bases/:base_token/workflows/list
```
> ⚠️ **注意:列表接口用 POST**,不是 GET。
**Path 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `base_token` | 是 | 多维表格 Base Token |
**Request BodyJSON**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `page_size` | int | 否 | 分页大小,默认 20最大 100 |
| `page_token` | string | 否 | 分页标记,首次请求不填,翻页时传上一页返回的 `page_token` |
| `status` | string | 否 | 过滤状态:`enabled` / `disabled` |
## API 出参详情
**Response `data` 字段:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `has_more` | boolean | 是否还有更多数据 |
| `page_token` | string | 下一页 token`has_more` 为 true 时返回) |
| `total` | int | 符合条件的工作流总数 |
| `items` | []object | 工作流列表 |
| `items[].workflow_id` | string | 工作流唯一标识(`wkf` 开头) |
| `items[].title` | string | 工作流标题 |
| `items[].status` | string | 当前状态:`enabled` / `disabled` |
| `items[].trigger_type` | string | 触发器类型,如 `AddRecordTrigger` |
| `items[].creator_id` | string | 创建人的 open_id |
| `items[].updater_id` | string | 最后修改人的 open_id |
| `items[].create_time` | int | 创建时间Unix 秒级时间戳 |
| `items[].update_time` | int | 最后更新时间Unix 秒级时间戳 |
## 返回值
命令成功后输出 JSON已合并所有分页
```json
{
"ok": true,
"data": {
"items": [
{
"workflow_id": "wkfxxxxxx",
"title": "自动发送通知",
"status": "enabled",
"trigger_type": "AddRecordTrigger",
"creator_id": "ou_xxxxx",
"updater_id": "ou_yyyyy",
"create_time": 1704067200,
"update_time": 1704153600
}
],
"total": 1
}
}
```
## ⚡ 性能提示
### 场景适用性
**✅ 需要先 list 的场景**:
- 批量操作:启用/停用多个工作流(先 list再批量 enable/disable
- 查询统计:统计定时触发的工作流数量(先 list再筛选
- 修改操作:修改指定名称的工作流(先 list ,从列表中找到对应名称工作流的 workflow_id再 get/update
**❌ 不需要先 list 的场景**:
- **创建工作流**:直接调用 `+workflow-create`,不需要先 list
- 查看指定工作流详情:如果已知 workflow_id直接 `+workflow-get`
### 缓存策略
同一会话中处理多个工作流时,只需调用一次 `+workflow-list` 获取全部结果,然后从中筛选所需的工作流,避免重复查询。
## 坑点
- ⚠️ **列表用 POST 不用 GET**`/workflows/list` 是 POST 接口,`page_token` 放在 Request Body 里而不是 Query 参数,常见误区
- ⚠️ **workflow_id 前缀 `wkf`**:返回的 `workflow_id``wkf` 开头,传给 `+workflow-enable` / `+workflow-disable` 时直接使用即可,不要和 table_id`tbl` 开头)混淆
- ⚠️ **scope 待确认**:内部文档未列出权限名,代码使用 `base:workflow:read`,如遇 `[230013] permission denied` 需核对实际 scope
## 参考
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-base-workflow-enable](lark-base-workflow-enable.md) — 启用工作流
- [lark-base-workflow-disable](lark-base-workflow-disable.md) — 禁用工作流
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,935 @@
# Workflow 数据结构参考
本文档定义 Workflow 的完整数据结构,适用于:
- **查询场景**:理解 `+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` | 日期提醒触发 |
| `LarkMessageTrigger` | 接收飞书消息触发 |
> 所有 Trigger 节点**请勿设置** `children` ,通过 `next` 串联后继。
### 触发器选型指南
| 需求描述 | 触发器 |
|---------|--------|
| 新增记录时 | `AddRecordTrigger` |
| 字段变为特定值时(**仅修改** | `SetRecordTrigger` |
| **新增或修改**都触发 | `ChangeRecordTrigger` |
| 拿不准用哪个 | `ChangeRecordTrigger` |
> ⚠️ `SetRecordTrigger` 仅监听修改,`ChangeRecordTrigger` 同时监听新增 + 修改。
### Action 类型
| type | 说明 |
|------|------|
| `AddRecordAction` | 新增记录 |
| `SetRecordAction` | 更新记录 |
| `FindRecordAction` | 查找记录 |
| `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 / CreatedTime / 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 关系 |
### 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` 互斥) |
### 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}`。
##### 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` |
##### 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 / CreatedUser / ModifiedUser | 姓名 | `name` | `string` | 用户姓名 |
| **日期字段**DateTime / CreatedTime / ModifiedTime | 时间戳 | `timestamp` | `number` | 时间戳数值 |
| **附件字段**Attachment | 文件名 | `fileName` | `string` | 附件文件名 |
| | 文件类型 | `fileType` | `string` | MIME 类型 |
| | 文件大小 | `size` | `number` | 文件字节数 |
| | 文件 Token | `fileToken` | `string` | 附件 token |
| **超链接字段**URL | 文本 | `text` | `string` | 链接文本部分 |
| | 链接 | `link` | `string` | 链接 URL 部分 |
| **自动编号字段**AutoNumber | 序号 | `sequence` | `number` | 编号的纯数字序号 |
| **关联字段**SingleLink / DuplexLink | 字段下钻 | `{fieldId}` | - | 可下钻到关联表的字段 |
> 其他字段类型(如文本、数字、复选框、单选/多选、电话、地理位置、进度、公式、引用查找等)仅支持 `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 | 触发器 | ✅ | 动态(表字段 + 记录属性) |
| TimerTrigger | 触发器 | ✅ | 静态(仅 scheduleTime |
| LarkMessageTrigger | 触发器 | ✅ | 静态(消息属性列表) |
| FindRecordAction | 动作 | ✅ | 动态(用户选择的字段) |
| AddRecordAction | 动作 | ✅ | 动态(用户配置的字段) |
| SetRecordAction | 动作 | ✅ | 动态(用户配置的字段) |
| 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 / MultiSelect 字段多值匹配
| 操作 | 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-create](lark-base-workflow-create.md) — 创建工作流命令
- [lark-base-workflow-update](lark-base-workflow-update.md) — 更新工作流命令
- [lark-base-workflow-list](lark-base-workflow-list.md) — 列出工作流命令

View File

@ -0,0 +1,167 @@
# base +workflow-update
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
全量替换 Base 中一个已有工作流的定义(`title` 和/或 `steps`)。使用 PUT 语义,传入的内容会完整覆盖原有定义。
## ⚠️ 执行前必读
更新工作流前请按顺序完成:
1. **先读本文档**,了解 `--json` 参数格式和 PUT 全量覆盖的语义
2. **阅读 [workflow-guide.md](lark-base-workflow-guide.md)**,获取 Loop、IfElseBranch、SwitchBranch 等**完整示例**(与创建场景共用相同的步骤结构)
3. **参考 [workflow-schema.md](lark-base-workflow-schema.md)**,查询具体字段定义
4. **按需调用 `+workflow-list`** 获取工作流 ID`wkf` 开头)
5. **如需基于现有工作流修改**,先调用 `+workflow-get` 导出当前定义,在此基础上修改
## 推荐命令
```bash
lark-cli base +workflow-update \
--base-token BascXxxxxx \
--workflow-id wkfosaYTS1V6rhjF \
--json '{"title":"新标题","steps":[{"id":"trigger_1","type":"AddRecordTrigger","title":"监控新订单","next":"action_1","data":{"table_name":"订单表","watched_field_name":"订单号"}},{"id":"action_1","type":"LarkMessageAction","title":"发送通知","next":null,"data":{"receiver":[{"value_type":"user","value":{"id":"ou_xxxx"}}],"send_to_everyone":false,"title":[{"value_type":"text","value":"新订单提醒"}],"content":[{"value_type":"text","value":"收到新订单"}],"btn_list":[]}}]}'
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--base-token <token>` | 是 | 多维表格 Base Token`Basc` 开头) |
| `--workflow-id <id>` | 是 | 工作流 ID`wkf` 开头),可从 `+workflow-list` 获取 |
| `--json <body>` | 是 | 工作流 body JSON包含 `title` 和/或 `steps`|
## 如何从链接中提取参数
用户通常会提供如下 URL
```
https://example.feishu.cn/base/<base_token>?table=<table_or_workflow_id>
```
- `--base-token`:取 `/base/` 后面的字符串(`Basc` 开头)
- `--workflow-id`:取 `?table=` 后面的值,当其以 `wkf` 开头时即为 workflow_id
> ⚠️ **注意区分 ID 前缀**table_id 以 `tbl` 开头workflow_id 以 `wkf` 开头。
## API 入参详情
**HTTP 方法和路径:**
```
PUT /open-apis/base/v3/bases/:base_token/workflows/:workflow_id
```
**Path 参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `base_token` | 是 | 多维表格 Base Token |
| `workflow_id` | 是 | 工作流唯一标识(`wkf` 开头) |
**Request BodyJSON**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `title` | string | 否 | 工作流标题 |
| `steps` | WorkflowStep[] | 否 | 步骤列表PUT 语义,全量替换) |
| `user_id_type` | string | 否 | 用户 ID 类型:`open_id` / `union_id` / `user_id`,默认 `open_id` |
> **步骤数据结构非常复杂,详见 [lark-base-workflow-schema.md](lark-base-workflow-schema.md)**
**Request Body 示例:**
```json
{
"title": "新订单自动通知(更新版)",
"steps": [
{
"id": "trigger_1",
"type": "AddRecordTrigger",
"title": "监控新订单",
"next": "action_1",
"data": {
"table_name": "订单表",
"watched_field_name": "订单号"
}
},
{
"id": "action_1",
"type": "LarkMessageAction",
"title": "发送通知",
"next": null,
"data": {
"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" }
],
"btn_list": []
}
}
]
}
```
> **注意**Trigger 和 Action 节点不应设置 `children` 字段,只有 Branch如 IfElseBranch、SwitchBranch和 System如 Loop节点才需要 `children` 来描述分支或循环结构。
## API 出参详情
**Response `data` 字段:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `workflow_id` | string | 工作流唯一 ID`wkf` 开头) |
| `title` | string | 更新后的工作流标题 |
| `status` | string | 当前状态:`enabled` / `disabled` |
| `steps` | WorkflowStep[] | 完整步骤列表(更新后) |
| `creator_id` | string | 创建人 OpenID |
| `updater_id` | string | 最后修改人 OpenID |
| `create_time` | number | 创建时间Unix 秒级时间戳) |
| `update_time` | number | 更新时间Unix 秒级时间戳) |
## 返回值
```json
{
"ok": true,
"data": {
"workflow_id": "wkfosaYTS1V6rhjF",
"title": "新订单自动通知(更新版)",
"status": "disabled",
"steps": [...],
"creator_id": "ou_xxxx",
"updater_id": "ou_yyyy",
"create_time": 1704067200,
"update_time": 1704153600
}
}
```
## 工作流
> [!CAUTION]
> 这是**写入操作** — 执行前必须向用户确认。PUT 语义,会**完整覆盖**原有工作流定义。
1. 确认 `--base-token``--workflow-id`(建议先用 `+workflow-list` 查出 ID
2. 确认 `--json` 的完整内容 — PUT 会全量覆盖,漏传 `steps` 会清空所有步骤
3. 执行命令,报告返回的 `workflow_id``update_time`
## 坑点
- ⚠️ **PUT 是全量覆盖**:传什么就写什么;如果只传 `title` 不传 `steps`,原有 steps 会被清空;如需只改标题,使用 PATCH 接口(目前无对应 shortcut可参考 API 文档直接调用)
- ⚠️ **workflow_id 前缀**:以 `wkf` 开头,从 URL 的 `?table=wkf...` 提取;和 table_id`tbl` 开头)混淆会导致 `[2200] Internal Error`
- ⚠️ **steps 中 id 字段必须唯一**:每个步骤的 `id` 在同一工作流内必须唯一;`next` 和 `children.links[].to` 引用的 ID 必须在 steps 数组中存在
- ⚠️ **更新不影响 enabled 状态**`+workflow-update` 不会改变工作流的 `enabled/disabled` 状态;需要另外调用 `+workflow-enable` / `+workflow-disable`
## 参考
- [lark-base-workflow-schema.md](lark-base-workflow-schema.md) — Workflow 数据结构参考
- [lark-base-workflow-guide.md](lark-base-workflow-guide.md) — **完整示例、构造技巧、常见错误**
- [lark-base-workflow-create](lark-base-workflow-create.md) — 创建工作流
- [lark-base-workflow-enable](lark-base-workflow-enable.md) — 启用工作流
- [lark-base-workflow-list](lark-base-workflow-list.md) — 列出全部工作流
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数

View File

@ -0,0 +1,23 @@
# base workflow shortcuts
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
workflow 相关命令索引。
## 命令导航
| 文档 | 命令 | 说明 |
|------|------|------|
| [lark-base-workflow-list.md](lark-base-workflow-list.md) | `+workflow-list` | 列出 Base 中所有自动化工作流 |
| [lark-base-workflow-get.md](lark-base-workflow-get.md) | `+workflow-get` | 获取单工作流的详情和完整结构 |
| [lark-base-workflow-create.md](lark-base-workflow-create.md) | `+workflow-create` | 创建一个新的自动化工作流 |
| [lark-base-workflow-update.md](lark-base-workflow-update.md) | `+workflow-update` | 全量替换已有工作流的定义 |
| [lark-base-workflow-enable.md](lark-base-workflow-enable.md) | `+workflow-enable` | 启用一个自动化工作流 |
| [lark-base-workflow-disable.md](lark-base-workflow-disable.md) | `+workflow-disable` | 禁用一个自动化工作流 |
| [lark-base-workflow-schema.md](lark-base-workflow-schema.md) | (数据结构参考) | Workflow 的创建和更新结构规范 |
## 说明
- 聚合页只保留目录职责;每个命令的详细说明请进入对应单命令文档。
- **配置前置依赖**:在创建或更新 workflow 之前大概率需要获取数据表table或字段field的真实 ID 等信息作为工作流节点的参数。建议按需先查阅 [table 命令索引](lark-base-table.md) 或 [field 命令索引](lark-base-field.md) 获取结构。
- **Schema 结构必读**:在创建或更新 workflow 前,必须仔细阅读 [lark-base-workflow-schema.md](lark-base-workflow-schema.md) 了解各触发器和节点组件的具体结构。

View File

@ -0,0 +1,18 @@
# base / workspace shortcuts
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
base / workspace 相关命令索引。
## 命令导航
| 文档 | 命令 | 说明 |
|------|------|------|
| [lark-base-base-create.md](lark-base-base-create.md) | `+base-create` | 创建 Base |
| [lark-base-base-get.md](lark-base-base-get.md) | `+base-get` | 获取 Base 详情 |
| [lark-base-base-copy.md](lark-base-base-copy.md) | `+base-copy` | 复制 Base |
## 说明
- 聚合页只保留目录职责;每个命令的详细说明请进入对应单命令文档。
- `+base-*` 负责 Base 生命周期动作。

View File

@ -0,0 +1,510 @@
# Base Lookup Field Configuration Guide
## Mandatory Read Acknowledgement
When creating or updating a lookup field with `lark-cli base +field-create/+field-update --json ...` and `type` is `lookup`, you should read this guide first and only then add `--i-have-read-guide` to the command.
Do **not** proactively add `--i-have-read-guide` before reading this guide. Without it, the CLI will fail fast and direct you back to this guide.
## Default strategy
**Use Formula fields by default for cross-table references and aggregations.** Only use Lookup fields when the user explicitly requests a Lookup field. Formula is a strict superset of Lookup — anything Lookup can do, Formula can do with a single expression.
## Usage
When creating a lookup field, the Agent should:
1. Get all table names: `lark-cli base +table-list --base-token <base>` — returns `items[].table_name`
2. Get table structure: `lark-cli base +table-get --base-token <base> --table-id <table>` — returns `fields[]`
3. If the lookup references other tables, also get those tables' structures
4. Determine the four elements: from (source table), select (source field), where (filter), aggregate (aggregation)
5. Construct the Lookup field JSON and submit it to create or update the field
**Key constraints**:
- Table names and field names must **exactly match** those returned by `+table-list` / `+table-get`
- The `from` table must be in the same Base
---
## Section 1: Core Concepts — Four-Element Model
A Lookup field is defined by five fields:
| Field | Meaning | JSON key | Required |
|-------|---------|----------|----------|
| **type** | Must be `"lookup"` | `type` | Yes |
| **from** | Source table to pull data from | `from` | Yes |
| **select** | Field in the source table to retrieve | `select` | Yes |
| **where** | Filter conditions on the source table | `where` | Yes (at least one condition) |
| **aggregate** | How to aggregate multiple matching records | `aggregate` | No (default: `raw_value`) |
**SQL analogy**:
```
SELECT [select field]
FROM [from table]
WHERE [filter conditions]
GROUP BY [aggregate function]
```
**Row-level matching (most important concept)**:
A Lookup field is computed row-by-row — for each row in the current table, it filters the source table to find "related" records. **The filter defines what "related" means.**
```
Current table row 1 → filter source table → matching records → select field → aggregate → result
Current table row 2 → filter source table → matching records → select field → aggregate → result
...
```
**Rule: Whenever the current table and the source table have a row-level correspondence (matching by some field value), you must specify a filter.**
---
## Section 2: Lookup vs Link vs Formula
Lookup and Link serve **different purposes**. Creating a Lookup does NOT require a Link field to exist first.
| Dimension | Link | Lookup | Formula |
|-----------|------|--------|---------|
| Purpose | Establish record relationships (read-write) | Pull and aggregate data from another table (read-only) | Compute values from expressions (read-only) |
| When to use | "link" / "associate" / "bind" two tables | "look up" / "reference" / "aggregate" / "count" from another table | Calculations, text manipulation, conditional logic |
**Common mistake**: Creating a Link field just to create a Lookup. If two tables share a matching text/number field, Lookup can match directly — no Link required.
**Selection decision tree**:
```
What does the user need?
├─ "Link"/"associate"/"bind" records between tables → Link
├─ "Look up"/"reference"/"aggregate"/"count" from another table → Lookup
│ ├─ Needs aggregation (sum/count/average)? → Lookup + aggregate
│ └─ Just reference a value? → Lookup (aggregate = null)
├─ Calculations/text manipulation within current table → Formula
└─ Access linked record's field → Prefer Lookup (more intuitive), or Formula chain access
```
---
## Section 3: Filter Condition Rules
**You must provide a `where` with at least one condition.** Improper conditions cause every row to pull all records from the source table.
### The Iron Rule: field belongs to source table
```
filter condition:
field → must be a field in the FROM table (source table)
value → constant or reference to a field in the CURRENT table
```
### How to find the matching field pair
**With a Link field (most common)**: The match is between the **Link field** and the **target table's primary field**.
```
Link is in the source table → source.linkField matches current.primaryField
Link is in the current table → source.primaryField matches current.linkField
```
**Without a Link field**: Two tables share a field with the same meaning — match directly.
### Where condition structure
Each condition is a **tuple** (array) of 2 or 3 elements: `[field, operator, value?]`
```json
{
"logic": "and",
"conditions": [
["<source table field>", "<operator>", { "type": "constant", "value": "<val>" }]
]
}
```
For `empty` / `non_empty`, the value can be omitted (2-element tuple):
```json
["<source table field>", "empty"]
```
### Two value formats
**Constant value** — for fixed conditions (e.g., "status is completed"):
```json
["状态", "==", { "type": "constant", "value": "已完成" }]
```
**Field reference** — for dynamic per-row matching (e.g., "match current row's project"):
```json
["项目名", "==", { "type": "field_ref", "field": "项目名" }]
```
**Decision guide**: Fixed condition (e.g., "status is completed") → `constant`. Dynamic condition (e.g., "match current record's project ID") → `field_ref`.
### Constant value format by field type
The `value` inside `{ "type": "constant", "value": ... }` varies by field type:
| Field type | Constant value format | Example |
|-----------|----------------------|---------|
| `text` | String | `"已完成"` |
| `number` | Number | `100`, `0.8` |
| `datetime` / `created_at` / `updated_at` | String | `"ExactDate(2025-01-01)"`, `"ExactDate(2025-01-01 09:30)"`, `"Today"`, `"Yesterday"`, `"Tomorrow"` |
| `select` (`multiple=false/true`) | Option name array | `["Todo"]`, `["Todo", "Done"]` |
| `link` | Record reference array | `[{ "id": "rec_xxx" }]`, `[{ "id": "rec_xxx" }, { "id": "rec_yyy" }]` |
| `user` / `created_by` / `updated_by` | User reference array | `[{ "id": "ou_xxx" }]`, `[{ "id": "ou_xxx" }, { "id": "ou_yyy" }]` |
| `checkbox` | Boolean | `true`, `false` |
| `attachment` / `location` | Only `empty` / `non_empty` | value must be `null` or omitted |
| `auto_number` | Not supported for constant comparison | Use dynamic field\_ref instead |
| `formula` / `lookup` (exact type) | Follow the underlying type rules | — |
| `formula` / `lookup` (fuzzy type) | String | `"some text"` |
**`datetime` notes**:
- Supported datetime constant values are `ExactDate(...)`, `Today`, `Yesterday`, `Tomorrow`
- Date-only fields use `ExactDate(YYYY-MM-DD)`
- Fields that include time use `ExactDate(YYYY-MM-DD HH:mm)`
- For complex or relative date filtering, consider using a Formula field instead
### Dynamic field reference — set comparison semantics
When using `{ "type": "field_ref", "field": "..." }`, values from both sides are first **converted to sets** at runtime, then compared using set operations:
- **`==`**: Sets are exactly equal (strict matching)
- **`intersects`**: Sets have a non-empty intersection (most commonly used)
**Conversion rules by field type**:
| Field type | Converted to |
|-----------|-------------|
| `text` | Single-element string set |
| `number` / `auto_number` / `datetime` | Single-element number set |
| `select` (`multiple=false/true`) | Set of option name strings |
| `user` / `created_by` / `updated_by` | Set of user name strings |
| `link` | Set of linked records' primary field string representations |
| `formula` / `lookup` | The computed value set |
**Examples**:
- User field `["name1", "name2"]` **intersects** text `"name1"` → true; **==** text `"name1"` → false (sets not equal)
- User field `["name1"]` **==** text `"name1"` → true (single-element sets are equal)
- Link field referencing records → converted to primary field strings, then compared
### Supported operators
| Operator | Meaning | Applicable field types |
|----------|---------|-----------------|
| `==` | Equal (exact match) | All types |
| `!=` | Not equal | All types |
| `>` | Greater than | `number`, `datetime` |
| `>=` | Greater than or equal | `number`, `datetime` |
| `<` | Less than | `number`, `datetime` |
| `<=` | Less than or equal | `number`, `datetime` |
| `intersects` | Has intersection (non-empty overlap) | All types (most commonly used for dynamic field\_ref) |
| `disjoint` | No intersection | All types |
| `empty` | Field is empty | All types (value must be null or omitted) |
| `non_empty` | Field is not empty | All types (value must be null or omitted) |
### Constraints
- **Only one level of and/or** — nesting (e.g., `{ and: [{ or: [...] }] }`) is not supported
- **At least one condition** — empty conditions array will error
---
## Section 4: Aggregate Rules
| Aggregate | Common user phrasing | Select field should be | Result type |
|-----------|---------------------|----------------------|-------------|
| `sum` | "total" / "sum" / "cumulative amount" | `number` field (e.g., amount) | Number |
| `average` | "average" / "mean" | `number` field | Number |
| `max` | "maximum" / "latest" / "most recent" | `number` / `datetime` field | Same as source |
| `min` | "minimum" / "earliest" | `number` / `datetime` field | Same as source |
| `counta` | "count" / "how many" / "total number" | Any field | Number |
| `unique_counta` | "count distinct" / "how many different" | Field to deduplicate | Number |
| `unique` | "list distinct" / "which ones" / "show different" | Field to display | List |
| `raw_value` | "list all" / "show all values" (default) | Field to display | List |
**Common confusion**: `unique` returns a **deduplicated list**, `unique_counta` returns a **count**. "Which categories are involved" → `unique`; "How many categories" → `unique_counta`.
**Important**:
- Enum values are **snake_case lowercase**: `sum` not `Sum`, `average` not `Average`
- **Count is `counta`, NOT `count`** — this is the most common enum mistake
---
## Section 5: Hard Constraints
1. **Always write a filter**: The `where` field is required with at least one condition. Whenever the current table and source table have row-level correspondence, the condition should express that relationship.
2. **Lookup fields are read-only**: Cell values cannot be manually set.
3. **Create Lookup after all dependent fields exist**: The source table and referenced fields must exist before creating the Lookup field.
4. **Source table must be in the same Base**: Cross-Base lookups are not supported.
5. **Changing `from` requires changing `select`**: Updating the source table without updating the select field will error.
---
## Section 6: Decision Trees
### How to build the filter
```
Step 1: Analyze the filtering semantics in the user's request
"Count artworks per exhibition" → filter: belongs to exhibition = current exhibition
"Sum completed order amounts" → filter: status = completed AND project = current project
Step 2: Find the matching field pair
├─ Tables have a Link relationship?
│ ├─ Link is in source table → source.linkField matches current.primaryField
│ └─ Link is in current table → source.primaryField matches current.linkField
├─ Tables share same-meaning text/number field? → source.field matches current.field
└─ Also need constant filtering? → AND combination
```
### Which aggregate?
```
How to handle multiple matching records?
├─ Show all values as-is → raw_value (default)
├─ Show deduplicated list → unique
├─ Sum → sum
├─ Average → average
├─ Maximum / minimum → max / min
├─ Count records → counta
└─ Count distinct → unique_counta
```
---
## Section 7: Common Configuration Patterns
> Patterns are categorized by **filter matching method**. Aggregate choice is independent — see Section 4.
### Pattern 1: Aggregate from a linked table (Link is in the source table)
**Scenario**: "Count artworks per exhibition", "Sum order amounts per project"
When the source table has a Link pointing to the current table:
```
Exhibition table: ExhibitionName (primaryField) ← current table
Artwork table: ArtworkName (primaryField), ← source table (Link is here)
Exhibition (Link → Exhibition table)
```
```json
{
"type": "lookup",
"name": "Artwork Count",
"from": "Artwork table",
"select": "ArtworkName",
"aggregate": "counta",
"where": {
"logic": "and",
"conditions": [
["Exhibition", "intersects", { "type": "field_ref", "field": "ExhibitionName" }]
]
}
}
```
### Pattern 2: Reference a linked record's field (Link is in the current table)
**Scenario**: "Show supplier's contact person", "Display warehouse manager"
When the current table has a Link pointing to the source table:
```
Supplier table: SupplierName (primaryField), Contact (Text) ← source table
Inventory table: ProductName (primaryField), ← current table (Link is here)
Supplier (Link → Supplier table)
```
```json
{
"type": "lookup",
"name": "Supplier Contact",
"from": "Supplier table",
"select": "Contact",
"where": {
"logic": "and",
"conditions": [
["SupplierName", "intersects", { "type": "field_ref", "field": "Supplier" }]
]
}
}
```
### Pattern 3: Match by same-meaning field (no Link)
**Scenario**: "Sum order amounts per project" (tables share a "ProjectName" field but no Link)
```
Project table: ProjectName (primaryField) ← current table
Order table: OrderID (primaryField), ProjectName (Text), ← source table
Amount (Number)
```
```json
{
"type": "lookup",
"name": "Order Total",
"from": "Order table",
"select": "Amount",
"aggregate": "sum",
"where": {
"logic": "and",
"conditions": [
["ProjectName", "==", { "type": "field_ref", "field": "ProjectName" }]
]
}
}
```
### Pattern 4: Dynamic matching + constant filtering
**Scenario**: "Only count completed orders", "Only sum approved budgets"
Combine row-level matching with fixed-value filtering using `logic: "and"`:
```json
{
"type": "lookup",
"name": "Completed Order Amount",
"from": "Order table",
"select": "Amount",
"aggregate": "sum",
"where": {
"logic": "and",
"conditions": [
["Manager", "==", { "type": "field_ref", "field": "EmployeeName" }],
["Status", "==", { "type": "constant", "value": "Completed" }]
]
}
}
```
### Pattern 5: Date filtering with constant value
**Scenario**: "Look up orders created after 2025-01-01", "Sum today's sales"
```json
{
"type": "lookup",
"name": "Recent Orders",
"from": "Order table",
"select": "Amount",
"aggregate": "sum",
"where": {
"logic": "and",
"conditions": [
["ProjectName", "==", { "type": "field_ref", "field": "ProjectName" }],
["CreatedDate", ">=", { "type": "constant", "value": "ExactDate(2025-01-01)" }]
]
}
}
```
---
## Section 8: Anti-Pattern Collection
### Mistake 1: Omitting where (most common)
```json
// Wrong: no where, every row pulls all records
{ "type": "lookup", "name": "Artwork Count", "from": "Artwork table", "select": "ArtworkName", "aggregate": "counta" }
// Correct: where with Link relationship
{ "type": "lookup", "name": "Artwork Count", "from": "Artwork table", "select": "ArtworkName", "aggregate": "counta",
"where": { "logic": "and", "conditions": [
["Exhibition", "intersects", { "type": "field_ref", "field": "ExhibitionName" }]
]}}
```
### Mistake 2: Wrong value type — confusing constant vs field_ref
```json
// Wrong: using constant for a dynamic join
["ProjectName", "==", { "type": "constant", "value": "ProjectName" }]
// Correct: use field_ref for dynamic per-row matching
["ProjectName", "==", { "type": "field_ref", "field": "ProjectName" }]
```
### Mistake 3: Using `count` instead of `counta`
```json
// Wrong
{ "aggregate": "count" }
// Correct
{ "aggregate": "counta" }
```
### Mistake 4: Wrong case for aggregate values
```json
// Wrong
{ "aggregate": "SUM" }
{ "aggregate": "Sum" }
// Correct — snake_case lowercase
{ "aggregate": "sum" }
{ "aggregate": "average" }
```
### Mistake 5: Nested where conditions
```json
// Wrong: nesting not supported
{ "logic": "and", "conditions": [
{ "logic": "or", "conditions": [...] }
]}
// Correct: only one level
{ "logic": "and", "conditions": [cond1, cond2, cond3] }
```
### Mistake 6: Confusing Lookup with Link
The user says "aggregate order amounts" — use Lookup, not Link. Link establishes relationships; Lookup retrieves and aggregates data.
### Mistake 7: Using object format instead of tuple for conditions
```json
// Wrong: object format
{ "fieldRef": "Status", "operator": "is", "value": { "type": "constant", "value": "Done" } }
// Correct: tuple format [field, operator, value?]
["Status", "==", { "type": "constant", "value": "Done" }]
```
### Mistake 8: Missing `type` field
```json
// Wrong: no type field
{ "name": "Total", "from": "Orders", "select": "Amount", "aggregate": "sum", "where": { ... } }
// Correct: must include type
{ "type": "lookup", "name": "Total", "from": "Orders", "select": "Amount", "aggregate": "sum", "where": { ... } }
```
---
## Section 9: Constraint Summary
- `type` must be `"lookup"` — this field is required in the request body
- `where` is required with at least one condition — always specify a filter
- Conditions use **tuple format**: `[field, operator, value?]` — NOT object format
- Lookup fields are read-only — values cannot be manually set
- Source table and referenced fields must exist before creating the Lookup
- Condition field (first element of tuple) must reference a field in the source table, not the current table
- Where supports only one level of and/or — no nesting
- Aggregate values are snake_case lowercase: `sum`, `counta`, `unique_counta` (NOT `count`)
- Operators: `==`, `!=`, `>`, `>=`, `<`, `<=`, `intersects`, `disjoint`, `empty`, `non_empty`
- Table and field names must exactly match `+table-get` output
- `datetime` constant values use string format: `ExactDate(YYYY-MM-DD)` / `ExactDate(YYYY-MM-DD HH:mm)` / `Today` / `Yesterday` / `Tomorrow`
- `select` constant values use option names;
- `link` / `user` constant values use `{id}` object arrays

View File

@ -0,0 +1,539 @@
# 飞书多维表格角色权限配置详解
> **返回**: [SKILL.md](../SKILL.md) | **相关**: [role-create](lark-base-role-create.md) · [role-update](lark-base-role-update.md) · [role-get](lark-base-role-get.md)
本文档详细说明角色权限AdvPermBaseRoleConfig的完整 JSON 结构,供 `+role-create``+role-update` 构造 `--json` 参数时参考。
## 📋 目录
- [顶层结构 (AdvPermBaseRoleConfig)](#顶层结构-advpermbaseroleconfig)
- [角色类型 (RoleType)](#角色类型-roletype)
- [Base 级权限 (BaseRuleMap)](#base-级权限-baserulemap)
- [仪表盘权限 (DashboardRule)](#仪表盘权限-dashboardrule)
- [文档权限 (DocxRule)](#文档权限-docxrule)
- [数据表权限 (TableRule)](#数据表权限-tablerule)
- [表级权限 (TablePerm)](#表级权限-tableperm)
- [视图权限 (ViewRule)](#视图权限-viewrule)
- [字段权限 (FieldRule)](#字段权限-fieldrule)
- [记录权限 (RecordRule)](#记录权限-recordrule)
- [筛选条件 (FilterRuleGroup)](#筛选条件-filterrulegroup)
- [默认权限策略与风控规则](#默认权限策略与风控规则)
- [默认关闭项](#默认关闭项)
- [权限对象选择](#权限对象选择)
- [记录操作默认策略](#记录操作默认策略)
- [field_perms 构造 SOP](#field_perms-构造-sop)
- [视图权限默认策略](#视图权限默认策略)
---
## 顶层结构 (AdvPermBaseRoleConfig)
```json
{
"role_name": "财务审核员",
"role_type": "custom_role",
"base_rule_map": { "copy": false, "download": false },
"table_rule_map": { "订单表": { "perm": "edit", "...": "..." } },
"dashboard_rule_map": { "销售看板": { "perm": "read_only" } },
"docx_rule_map": { "文档A": { "perm": "edit", "allow_download": true } }
}
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|----|------|
| `role_name` | string | 是 | 角色名称,不能为空 |
| `role_type` | string | 是 | 角色类型,见 [RoleType](#角色类型-roletype) |
| `base_rule_map` | map\<string, bool\> | 是 | Base 级权限,见 [BaseRuleMap](#base-级权限-baserulemap) |
| `table_rule_map` | map\<string, TableRule\> | 否 | 数据表权限key 为表名 |
| `dashboard_rule_map` | map\<string, DashboardRule\> | 否 | 仪表盘权限key 为仪表盘名称 |
| `docx_rule_map` | map\<string, DocxRule\> | 否 | 文档权限仅单品模式key 为文档名称 |
---
## 角色类型 (RoleType)
| 值 | 说明 |
|------|------|
| `editor` | 系统角色:编辑者 |
| `reader` | 系统角色:阅读者 |
| `custom_role` | 自定义角色 |
**注意**:
- 创建接口(`+role-create`)仅支持 `custom_role`
- 更新接口(`+role-update`)支持 `editor` / `reader` / `custom_role`
---
## Base 级权限 (BaseRuleMap)
1. 默认值均为 `false`,当需要启用时设置为 `true`
2. 在新增角色和修改角色时需要默认带上这个字段,**严禁**在用户未明确要求的情况下将其设置为 `true`
```json
{
"base_rule_map": {
"copy": true,
"download": false
}
}
```
| Key | 说明 |
|-----|------|
| `copy` | 允许复制多维表格内容 |
| `download` | 允许创建副本、下载、打印多维表格 |
---
## 仪表盘权限 (DashboardRule)
```json
{
"dashboard_rule_map": {
"销售看板": { "perm": "read_only" },
"内部数据": { "perm": "no_perm" }
}
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `perm` | string | 仪表盘权限 |
**perm 可选值**:
| 值 | 说明 |
|----|------|
| `read_only` | 仅可阅读 |
| `no_perm` | 无权限 |
---
## 文档权限 (DocxRule)
> ⚠️ 仅在单品模式(`is_base_solo = true`)下可用。
```json
{
"docx_rule_map": {
"文档A": { "perm": "edit", "allow_download": true },
"文档B": { "perm": "read_only" }
}
}
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `perm` | string | 是 | 文档权限 |
| `allow_download` | bool | 否 | 是否允许下载/导出 |
**perm 可选值**:
| 值 | 说明 |
|----|------|
| `edit` | 可编辑 |
| `read_only` | 仅可阅读 |
| `no_perm` | 无权限 |
---
## 数据表权限 (TableRule)
```json
{
"table_rule_map": {
"订单表": {
"perm": "edit",
"view_rule": { "..." : "..." },
"record_rule": { "..." : "..." },
"field_rule": { "..." : "..." }
},
"用户表": {
"perm": "read_only"
}
}
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `perm` | string | 表级权限,见 [TablePerm](#表级权限-tableperm) |
| `view_rule` | ViewRule | 视图权限配置 |
| `record_rule` | RecordRule | 记录权限配置 |
| `field_rule` | FieldRule | 字段权限配置 |
**注意**: 当 `perm``no_perm` 时,`view_rule`、`record_rule`、`field_rule` 均无须再设置。
---
### 表级权限 (TablePerm)
| 值 | 说明 |
|----|------|
| `manage` | 可管理 |
| `edit` | 可编辑 |
| `read_only` | 仅可阅读 |
| `no_perm` | 无权限(此时不能再设置视图、记录和字段的权限) |
---
### 视图权限 (ViewRule)
```json
{
"view_rule": {
"allow_edit": true,
"visibility": {
"all_visible": false,
"visible_views": ["表格视图", "看板视图"]
}
}
}
```
| 字段 | 类型 | 说明 |
|------|------|----------------------------|
| `allow_edit` | bool | 可新增、删除、修改视图;表权限为 `edit` 时默认为 `true`,表权限为 `read_only` 或用户明确限制时为 `false` |
| `visibility` | object | 可见的视图配置 |
| `visibility.all_visible` | bool | 是否全部可见 |
| `visibility.visible_views` | []string | 可见视图名称 列表 |
**⚠️ 核心规则:`view_rule` 必须同时包含 `allow_edit``visibility` 两个字段,缺一不可。**
输出 `view_rule` 时,**必须**使用以下完整结构,根据场景选择对应模板:
```json
// 情况 A表权限为 edit 且用户未明确限制 → allow_edit 默认为 true全部可见
{
"view_rule": {
"allow_edit": true,
"visibility": {
"all_visible": true
}
}
}
// 情况 B表权限为 read_only或用户明确说不可编辑视图 → 全部可见、不可编辑
{
"view_rule": {
"allow_edit": false,
"visibility": {
"all_visible": true
}
}
}
// 情况 C用户提及了具体视图 → 仅指定视图可见allow_edit 仍按 A/B 规则判断)
{
"view_rule": {
"allow_edit": true,
"visibility": {
"all_visible": false,
"visible_views": ["表格视图", "看板视图"]
}
}
}
```
**注意**:
- 当 `all_visible``false` 时,`visible_views` 不可为空,必须指定至少一个可见视图
- `biz_type``query_form_view` 的视图不可放在 `visible_views` 中(不能配置可见性)
---
### 字段权限 (FieldRule)
```json
{
"field_rule": {
"field_perm_mode": "specify",
"field_perms": {
"金额": "edit",
"备注": "read",
"密码": "no_perm"
},
"allow_edit_and_modify_option_fields": [],
"allow_edit_and_download_file_fields": []
}
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `field_perm_mode` | string | 字段权限模式 |
| `field_perms` | map\<string, string\> | 字段名 → 权限,仅 `field_perm_mode``specify` 时有效 |
| `allow_edit_and_modify_option_fields` | []string | 允许增删改选项的字段名列表 |
| `allow_edit_and_download_file_fields` | []string | 允许下载附件的字段名列表 |
**field_perm_mode 可选值**:
| 值 | 说明 |
|----|------|
| `all_edit` | 所有字段可编辑,但选项不可增删改 |
| `all_read` | 所有字段可读 |
| `specify` | 指定字段权限(可进一步设置 `field_perms` 和选项增删改权限) |
| `no_perm` | 无权限 |
**field_perms 中单个字段的权限值**:
| 值 | 说明 |
|----|------|
| `edit` | 可编辑(含新增和阅读权限) |
| `create` | 可新增(含阅读权限) |
| `read` | 可阅读 |
| `no_perm` | 无权限 |
**⚠️ field_perms 重要规则**:
1. 写入前必须先查看字段的 `type`
2. `formula` / `lookup` / `auto_number` 类型字段**必须强制**降级为 `read``no_perm`**严禁**设为 `edit`
3. 必须输出除 4 个系统字段外的所有字段
4. `allow_edit_and_modify_option_fields`:仅当用户明确要求"允许增删改选项"时才配置,否则必须为空数组 `[]`。仅支持 `select` 类型字段
5. `allow_edit_and_download_file_fields`:用户没有要求时不要设置,且仅 `field_perm_mode``specify` 时才能设置
---
### 记录权限 (RecordRule)
```json
{
"record_rule": {
"record_operations": ["add"],
"edit_filter_rule_group": {
"conjunction": "and",
"filter_rules": [
{
"conjunction": "and",
"filters": [
{
"field_name": "部门",
"operator": "is",
"filter_values": ["财务部"]
}
]
}
]
},
"other_record_all_read": true
}
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `record_operations` | []string | 记录操作权限,仅 `TablePerm = edit` 时有效 |
| `edit_filter_rule_group` | FilterRuleGroup | 可编辑记录的筛选条件,范围为所有记录时此字段为空 |
| `other_record_all_read` | bool | 是否可阅读所有记录。都可读时为 `true`,其他情况为 `false` |
| `read_filter_rule_group` | FilterRuleGroup | 可阅读记录的额外筛选规则。仅当可阅读范围与可编辑范围不一致时设置(依赖 `other_record_all_read = false` |
**record_operations 可选值**:
| 值 | 说明 |
|----|------|
| `add` | 可新增记录 |
| `delete` | 可删除记录 |
---
### 筛选条件 (FilterRuleGroup)
```json
{
"conjunction": "and",
"filter_rules": [
{
"conjunction": "and",
"filters": [
{
"field_name": "部门",
"operator": "is",
"filter_values": ["财务部"]
}
]
}
]
}
```
**FilterRuleGroup 结构**:
| 字段 | 类型 | 说明 |
|------|------|------|
| `conjunction` | string | 逻辑连接词:`and` / `or` |
| `filter_rules` | []FilterRule | 筛选规则数组 |
**FilterRule 结构**:
| 字段 | 类型 | 说明 |
|------|------|------|
| `conjunction` | string | 逻辑连接词,默认 `and` |
| `filters` | []Filter | 筛选条件数组 |
**Filter 结构**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `field_name` | string | 是 | 字段名。仅限 `can_filter``true` 的字段。若服务端要求当前用户类条件,可按 API 返回结构处理 |
| `operator` | string | 是 | 操作符,见下表 |
| `field_type` | string | 否 | 通常由服务端 filterFiller 补全Agent 判断字段类型时以 `+field-list` / 字段操作接口的 `type` 为准,常见可筛选类型包括 `select`、`user`、`created_by`、`number` 及部分 `formula` / `lookup` |
| `reference_type` | string | 条件 | 引用类型。`field_type` 为公式或引用字段时必须赋值,其他情况不能赋值 |
| `filter_values` | []string | 条件 | 筛选值。`operator` 为 `isEmpty` / `isNotEmpty` 时不设置,字段类型为 `user` 时也无需设置,其他情况必须设置。值为选项的 `name` |
| `field_ui_type` | string | 条件 | 该字段有值时一定要填 |
| `is_invalid` | bool | 否 | 判断筛选条件是否有效 |
**operator 可选值**:
| 值 | 说明 |
|----|------|
| `is` | 等于 |
| `isNot` | 不等于 |
| `contains` | 包含 |
| `doesNotContain` | 不包含 |
| `isEmpty` | 为空 |
| `isNotEmpty` | 不为空 |
| `isGreater` | 大于 |
| `isGreaterEqual` | 大于等于 |
| `isLess` | 小于 |
| `isLessEqual` | 小于等于 |
**注意**:
- `field_type`、`field_ui_type`、`reference_type` 在创建/更新角色时由服务端 filterFiller 自动补全,客户端通常只需传 `field_name`、`operator`、`filter_values`
---
## 默认权限策略与风控规则
构造角色配置 JSON 时,采用 **默认拒绝与权限最小化** 策略。用户未明确提及的权限一律不开放,不因"合理猜测""常见做法"主动扩展权限范围。
### 默认关闭项
以下能力在用户未明确说明时**默认关闭**
| 能力 | 默认值 | 开启条件 |
|------|--------|----------|
| 未提及的数据表的任何访问 | `no_perm` | 用户明确提及该表 |
| 仪表盘访问 | 不配置 | 用户明确提及该仪表盘 |
| `base_rule_map.copy` | `false` | 用户明确要求"允许复制" |
| `base_rule_map.download` | `false` | 用户明确要求"允许下载/打印/副本" |
### 默认开启项(条件性)
以下能力在特定条件下**默认开启**,用户明确限制时才排除:
| 能力 | 默认值 | 排除条件 |
|------|--------|----------|
| `record_operations` 中的 `delete` | **包含**`perm = edit` 时) | 用户明确限制时才排除 |
| `view_rule.allow_edit` | **`true`**`perm = edit` 时) | 用户明确限制"不可编辑视图"或 `perm = read_only` 时设为 `false` |
---
### Editor / Reader 的权限上限规则
1. 对 Editor 与 Reader系统允许修改其权限配置但同时施加以下封顶约束
2. Reader 的任一权限项 不允许超过「仅可阅读」
3. Reader 不允许拥有任何可编辑、可新增、可删除相关权限; Editor 的权限可被修改,但其能力范围受高级权限能力封顶。
### 权限对象选择
**注意**:
- 仅对用户明确指向的权限对象生成配置(明确提及的表名、仪表盘名,或可解析为唯一对象的指代如"当前表""这张表"
- **严禁**基于业务常识、岗位职责、名称相似性或其他角色的历史配置推断或扩展权限对象
- 用户未明确提及的对象不生成任何权限配置,视为 `no_perm`
---
### 记录操作默认策略
**注意**:
- 用户未提及时,表权限为 `edit` 时默认同时包含 `add``delete`,默认不包含 `delete` 的情况仅适用于用户明确限制操作的场景
- 阅读范围默认对齐编辑范围:用户仅描述可编辑范围、未说明阅读范围时,可阅读范围与可编辑范围保持一致,不主动扩大
- 当可读范围与可编辑范围一致时,**不得**生成 `read_filter_rule_group`;应设置 `other_record_all_read = false``read_filter_rule_group = null`
**⚠️ 记录操作限制**:
1. `perm``read_only` 时,`record_rule.record_operations` **只能为空**
2. 同步表(`is_sync = true`**严禁**新增和删除记录
---
### field_perms 构造 SOP
在生成 `field_perms` 时,**严禁**依赖模糊的"继承"概念,必须按以下步骤执行:
| 步骤 | 操作 | 说明 |
|------|------|------|
| 1. 基准设定 | `perm = edit` → 全部字段预设 `"edit"``perm = read_only` → 全部预设 `"read"` | 基于 `base_table_info` 中的全量字段 |
| 2. 物理降级 | `formula` / `lookup` / `auto_number` 及系统字段 → 强制降级为 `"read"` | 不可变字段严禁设为 `edit` |
| 3. 用户覆盖 | 仅对用户**显式指定**了特定权限的字段应用 `no_perm` / `read` / `create` | 未显式指定的保持基准值 |
| 4. 反筛选误判 | 用于 `filter_rules` 的字段,若基准为 `"edit"` 且用户未要求降级 → **保持 `"edit"`** | 筛选条件不影响字段可编辑性 |
| 5. 筛选依赖兜底 | 出现在 `filter_rules` 中的字段**不允许**遗漏,权限至少为 `"read"` | 最终校验步骤 |
**⚠️ field_perm_mode 选择规则**:
1. 用户以"所有字段""全字段"等整体性表述描述且不要求选项增删改时,**必须**使用 `all_edit` / `all_read`**严禁**变为逐字段 `specify`
2. 仅在以下情况使用 `specify`:用户明确提出字段级差异需求、不同字段权限目标存在显著差异、或明确要求配置选项增删改权限
3. 系统字段硬性约束导致的自动降级**不视为**差异,不触发 `specify`
4. 对"仅""只能""部分"等约束定语,范围外的字段按定语的反方向设置
**⚠️ 同步表限制**: `is_sync = true` 的表**严禁**设置字段为 `edit``create`
---
### 视图权限默认策略
**判断流程(必须按顺序执行,命中即停)**:
1. **先判断用户是否提及了具体视图名称**(如"看板视图可见""甘特图不可编辑"等)
- **是**`all_visible = false``visible_views` 仅包含用户明确提及为"可见"的视图名称(非 viewID未提及的视图视为不可见
- **否**(用户完全未提及任何视图)→ `all_visible = true`
2. `allow_edit` 在表权限为 `edit` 时**默认为 `true`**;仅当用户明确限制"不可编辑视图"时才设为 `false`。设为 `true` 时仍**必须**包含 `visibility` 字段(参考视图权限 情况 A
3. `all_visible``false` 时,`visible_views` **不可为空**,必须至少包含一个视图
**❌ 常见错误 — 缺少 `visibility` 字段:**
```json
// 错误!缺少 visibility
{ "view_rule": { "allow_edit": false } }
```
**✅ 正确写法:**
```json
// 即使全部可见,也必须显式写出 visibility
{ "view_rule": { "allow_edit": false, "visibility": { "all_visible": true } } }
```
---
### 字段类型与筛选算子的强约束关系
当字段被用于记录筛选条件时,字段操作接口返回的 `type` 与可用算子存在固定绑定关系:
**`user` / `created_by` 类型字段:**
- 仅允许使用 `contains` 算子
- 不允许使用 `is`、`isNot` 等精确匹配算子
- 筛选条件中无需填写具体值(由系统自动匹配当前成员)
**`select` (`multiple=false`) 类型字段:**
- `is``isNot` 算子仅允许用于匹配**单一选项**,不得用于多个值
- 当用户表达"字段值等于/不等于某一个具体选项"(如"出勤状态不等于出勤"Agent 必须使用 `is` / `isNot`,且 filter_values 仅包含单一值。
- 当用户表达"字段值等于/不等于多个选项集合"(如"学历不是专科和其他"Agent 必须使用 `contains` / `doesNotContain`,并将多个选项填入 filter_values。
- `contains` / `doesNotContain`中的filter_values可包含多个值表示或关系
**`select` (`multiple=true`) 类型字段:**
- `is` / `isNot`filter_values 允许填写多个选项
- 当 operator = is 且勾选 A、B 时,语义为该字段**同时包含** A 和 BA&B不是"等于 A 或等于 B"
- 当用户表达"包含任一选项"时,除了可以使用 contains 实现外,也可以使用 is 并且配套通过 filter_rules.conjunction = or 实现
- `contains` / `doesNotContain`:用于表达"包含任一选项/不包含任一选项"filter_values 可填写多个选项(系统按"任一匹配"处理);若要表达"等于 A 或等于 B",应拆成多条筛选条件并用「或」组合。
**百分比字段**
- 对于 query 中“数字”的筛选条件时,如果涉及到百分比,要原封不动地还原用户给你的数值(百分比都变成小数)。比如“大于 20%”则变成“大于 0.2”、“xx 率小于 60”则变成“小于 0.6”。
### 被用于筛选的字段的 field_perms 权限强制要求
当某字段(系统字段没有此要求)被用于「满足特定条件的记录」中的筛选条件时,系统将根据当前数据表权限与记录权限,自动施加以下**不可变约束**
**筛选字段的读写一致性:**
- 若表权限为 edit且字段类型属于【可编辑字段】则筛选字段必须保持 edit 权限,除非用户显式要求降级。
- 严禁因为字段被用作筛选条件而将其降级为 read。筛选条件仅要求字段可见不要求字段只读。
**新增记录时的字段最低权限:**
- 当且仅当记录权限包含「可新增记录」时字段至少为可新增create用于保证在新增记录时筛选条件字段可被正确写入。
- 若当前记录权限为「仅可阅读」,则不触发该约束。
**字段是否可编辑edit不作强制要求**,由具体权限方案决定,不属于 infra 强制约束范围。
上述由系统自动施加的字段权限,不可被手动取消或降级。

View File

@ -0,0 +1,154 @@
---
name: lark-calendar
version: 1.0.0
description: "飞书日历calendar提供日历与日程会议的全面管理能力。核心场景包括查看/搜索日程、创建/更新日程、管理参会人、查询忙闲状态及推荐空闲时段、查询/搜索与预定会议室。注意:涉及【预约日程/会议】或【查询/预定会议室】时,必须先读取 references/lark-calendar-schedule-meeting.md 工作流!高频操作请优先使用 Shortcuts+agenda快速概览今日/近期行程)、+create创建日程并按需邀请参会人及预定会议室、+update更新既有日程字段或独立增删参会人/会议室)、+freebusy查询用户主日历的忙闲信息和rsvp的状态、+rsvp回复日程邀请"
metadata:
requires:
bins: ["lark-cli"]
cliHelp: "lark-cli calendar --help"
---
# calendar (v4)
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md),其中包含认证、权限处理**
**CRITICAL — 所有的 Shortcuts 在执行之前,务必先使用 Read 工具读取其对应的说明文档,禁止直接盲目调用命令。**
**CRITICAL — 凡涉及【预约日程/会议】或【查询/搜索会议室】,第一步 MUST 强制使用 Read 工具读取 [`references/lark-calendar-schedule-meeting.md`](references/lark-calendar-schedule-meeting.md)。禁止跳过此步直接调用 API 或 Shortcut**
**CRITICAL — 术语约束:用户日常表达中常说的“帮我约个日历”、“查一下今天的日历”等,其实际意图通常是针对 日程Event 的创建或查询,而非操作 日历Calendar 容器本身。请自动将口语化的“日历”意图映射为“日程”操作(如 `+create`, `+agenda`)。**
**CRITICAL — 会议与日程的意图路由:**
- **查询过去时间的会议**:如果用户明确查询过去时间的会议(如“昨天的会议”、“上周的会议”),**优先使用 [`../lark-vc/SKILL.md`](../lark-vc/SKILL.md) 搜索会议记录**。因为会议数据不仅包含从日程发起的视频会议,还包含即时会议,仅查询日程数据会导致结果不全。
- **查询日历/日程或未来时间的会议**:如果用户明确表达的是“日历”、“日程”,或者涉及**未来时间**的安排则属于本技能lark-calendar的业务域请继续使用本技能处理。
**CRITICAL — 任务类型分流:处理“预约/改约日程、添加/移除参会人、添加/更换会议室、调整时间”时,必须先判断用户是在“新建日程”还是“编辑已有日程”。**
- **编辑已有日程的强信号**:用户明确提到某个已存在的日程锚点(如标题、时间段、`这个日程`、`这场会`)并表达修改动作(如“添加”“移除”“改到”“换会议室”“调整时间”)。这类请求默认走**编辑已有日程**,绝不能直接按新建处理。
- **编辑已有日程的前置步骤**一旦判定为编辑MUST 先定位目标日程或具体实例的 `event_id`再继续后续流程。若是重复性日程MUST 先定位到对应实例的 `event_id`
- **新建日程**:只有当用户表达的是“新约一个会/创建一个日程/安排一次会议”等新增意图,且没有指向某个既有日程的修改动作时,才进入新建流程。
**CRITICAL — 验证与同步延迟在涉及删除日程delete、修改日程patch或者涉及添加移除参与人/会议室之后如果需要进行二次查询验证操作结果MUST 等待至少 2 秒后再进行查询,以防止因数据同步延迟导致查不到最新数据。注意:不要向用户提及你等待了这 2 秒钟的事情。**
**CRITICAL — 重复性日程的实例操作:目前已经完全具备对重复性日程的某个具体实例进行操作的能力(例如:编辑某个实例、删除某个实例、为某个实例添加/删除参与人、为某个实例添加/移除会议室)。只要在对应的操作中传递对应实例的 `event_id` 即可。因此MUST 先定位到对应的那次实例的 `event_id`(可通过 `events search_event` 搜索日程,或 `+agenda` 查看对应时间范围的日程等相关查询获取),绝对禁止直接使用原重复性日程的 `event_id` 进行操作。**
**时间与日期推断规范:**
为确保准确性,在涉及时间推断时,请严格遵循以下规则:
- **星期的定义**:周一是一周的第一天,周日是一周的最后一天。计算`下周一`等相对日期时,务必基于当前真实日期和星期基准进行推算,避免算错日期。
- **一天的范围**:当用户提到`明天`、`今天`等泛指某一天时,时间范围应默认覆盖整天时间范围。**切勿**自行缩减查询范围,以免遗漏晚上的时间安排。
- **历史时间约束**:不能预约已经完全过去的时间。唯一的例外情况是“跨越当前时间”的日程,即日程的开始时间在过去,但结束时间在未来。
## 核心场景
### 1. 预约新日程/会议、编辑已有日程、查询/搜索可用会议室
**BLOCKING REQUIREMENT (阻塞性要求): 只要用户的意图包含“预约日程/会议”或“查询/搜索可用会议室”,你必须立即停止其他思考,优先使用 Read 工具完整读取 [`references/lark-calendar-schedule-meeting.md`](references/lark-calendar-schedule-meeting.md)!未读取该文件前,绝对禁止执行任何日程创建或会议室查询操作。**
**CRITICAL: 必须严格按照上述文档中定义的工作流Workflow执行后续操作。处理该场景时默认做“智能助理”不要做“表单填写机”。能补全的默认值先补全只有在时间冲突、结果无法唯一确定、时间语义存在歧义时才主动追问。**
**CRITICAL: 执行顺序必须固定为:先判断任务类型(新建/编辑);若为编辑先定位目标日程 `event_id`;再补默认值或继承已定位日程的已知信息;再判断时间是否明确;最后进入“明确时间”或“模糊时间/无时间信息”分支。不要跳步。**
**CRITICAL: 明确时间且需要会议室时,先基于最终确定的时间块执行 `+room-find`,再按需执行 `+freebusy`;模糊时间或无时间信息时,先 `+suggestion`,如需会议室再批量 `+room-find`。如果是编辑已有日程且不改时间,只新增会议室,则必须基于已定位日程的原始时间执行 `+room-find`,且最终落地时默认保留已存在的会议室;只有用户明确表达“更换会议室”或“移除会议室”时,才删除原会议室。**
**CRITICAL: 当用户说“查会议室”“找会议室”“搜可用会议室”或“推荐常用会议室”时,默认是查会议室可用性,不是查会议室资源名录,更严禁拉取历史日程做统计分析。完整规则以 [lark-calendar-schedule-meeting.md](references/lark-calendar-schedule-meeting.md) 为准。**
**BLOCKING REQUIREMENT: 即使用户的核心诉求是“查会议室”,只要【没有提供明确的起止时间】,绝对禁止直接调用 `+room-find`!必须先进入【无时间/模糊时间】分支,调用 `+suggestion` 拿到候选时间块后,再将时间块传给 `+room-find`。**
**BLOCKING REQUIREMENT: 只要面临时间方案或会议室方案的选择(如模糊时间、无时间或需要会议室),在最终执行创建新日程或更新既有日程之前,必须先向用户展示候选方案并等待用户明确确认。绝对禁止擅自替用户做决定。**
## 核心概念
- **日历Calendar**日程的容器。每个用户有一个主日历primary calendar也可以创建或订阅共享日历。
- **日程Event**日历中的单个日程包含起止时间、地点、标题、参与人等属性。支持单次日程和重复日程遵循RFC5545 iCalendar国际标准。
- ***全天日程All-day Event***: 只按日期占用、没有具体起止时刻的日程,结束日期是包含在日程时间内的。
- **日程实例Instance**日程的具体时间实例本质是对日程的展开。普通日程和例外日程对应1个Instance重复性日程对应N个Instance。在按时间段查询时可通过实例视图将重复日程展开为独立的实例返回以便在时间线上准确展示和管理。
- **重复规则Rrule/Recurrence Rule**:定义重复性日程的重复规则,比如`FREQ=DAILY;UNTIL=20230307T155959Z;INTERVAL=14`表示每14天重复一次。
- **例外日程Exception**:重复性日程中与原重复性日程不一致的日程。
- **参会人Attendee**日程的参与者可以是用户、群、会议室资源、外部邮箱地址等。每个参与人有独立的RSVP状态。
- **响应状态RSVP**:参与人对日程邀请的回复状态(接受/拒绝/待定)。
- **忙闲时间FreeBusy**:查询用户在指定时间段的忙闲状态,用于会议时间协调。
- **会议室Room**“room”不是“房间”是“会议室”。请在理解和处理意图时将“room”和“房间”准确映射为“会议室”及其相关操作。
- **时间块Time Slot / Time Block**:指一个**具体且确定**的连续时间段(如 `14:00~15:00`)。在文档中,它与泛指的“时间范围/区间”(如“今天下午”、“下周”)有严格区别。在调用预定、查询可用会议室等确切操作时,必须基于确定的“时间块”而非模糊的“时间范围”。
## 资源关系
```
Calendar (日历)
└── Event (日程)
├── Attendee (参会人)
└── Reminder (提醒)
```
## Shortcuts推荐优先使用
Shortcut 是对常用操作的高级封装(`lark-cli calendar +<verb> [flags]`)。有 Shortcut 的操作优先使用。
| Shortcut | 说明 |
|----------|------|
| [`+agenda`](references/lark-calendar-agenda.md) | 查看日程安排(默认今天) |
| [`+create`](references/lark-calendar-create.md) | 创建日程并邀请参会人ISO 8601 时间) |
| [`+update`](references/lark-calendar-update.md) | 更新既有日程字段,或独立增量添加/移除参会人和会议室 |
| [`+freebusy`](references/lark-calendar-freebusy.md) | 查询用户主日历的忙闲信息和rsvp的状态 |
| [`+room-find`](references/lark-calendar-room-find.md) | 针对一个或多个**明确的**时间块查找可用会议室(**无明确时间时禁止直接调用,需先走 +suggestion** |
| [`+rsvp`](references/lark-calendar-rsvp.md) | 回复日程(接受/拒绝/待定) |
| [`+suggestion`](references/lark-calendar-suggestion.md) | 根据非明确时间或一段时间范围,推荐多个可用时间块方案 |
## 会议室相关规则
- **会议室是日程的一种参与人resource attendee不能脱离日程单独存在或单独预定。**
- **凡是用户意图是“预定/查询/搜索可用会议室”时,都必须进入 `references/lark-calendar-schedule-meeting.md` 工作流处理。**
- `+room-find` 的时间输入必须是**确定时间块**,不能是时间区间搜索。
- **强制约束:如果用户仅要求“查询会议室”但未提供明确时间,必须先调用 `+suggestion` 获取可用时间块,然后再将时间块交给 `+room-find` 批量查询。严禁直接猜测时间并盲目调用 `+room-find`。**
- **编辑已有日程时,如果用户表达的是“添加会议室/再加一个会议室”,默认语义是增量添加,必须保留已有会议室;只有在用户明确表达“更换会议室”“把原会议室换掉”“移除会议室”时,才执行旧会议室删除。**
## API Resources
```bash
lark-cli schema calendar.<resource>.<method> # 调用 API 前必须先查看参数结构
lark-cli calendar <resource> <method> [flags] # 调用 API
```
> **重要**:使用原生 API 时,必须先运行 `schema` 查看 `--data` / `--params` 参数结构,不要猜测字段格式。
### calendars
- `create` — 创建共享日历
- `delete` — 删除共享日历
- `get` — 查询日历信息
- `list` — 查询日历列表
- `patch` — 更新日历信息
- `primary` — 查询用户主日历
- `search` — 搜索日历
### event.attendees
- `batch_delete` — 删除日程参与人
- `create` — 添加日程参与人
- `list` — 获取日程参与人列表
### events
- `create` — 创建日程
- `delete` — 删除日程
- `get` — 获取日程
- `instance_view` — 查询日程视图
- `patch` — 更新日程
- `search_event` — 搜索日程目前只会返回日程id、日程主题、日程时间的信息需要更多的日程详情需要走 `events get` 命令)
- `share_info` — 获取日程分享链接
### freebusys
- `list` — 查询主日历日程忙闲信息
## 权限表
| 方法 | 所需 scope |
|------|-----------|
| `calendars.create` | `calendar:calendar:create` |
| `calendars.delete` | `calendar:calendar:delete` |
| `calendars.get` | `calendar:calendar:read` |
| `calendars.list` | `calendar:calendar:read` |
| `calendars.patch` | `calendar:calendar:update` |
| `calendars.primary` | `calendar:calendar:read` |
| `calendars.search` | `calendar:calendar:read` |
| `event.attendees.batch_delete` | `calendar:calendar.event:update` |
| `event.attendees.create` | `calendar:calendar.event:update` |
| `event.attendees.list` | `calendar:calendar.event:read` |
| `events.create` | `calendar:calendar.event:create` |
| `events.delete` | `calendar:calendar.event:delete` |
| `events.get` | `calendar:calendar.event:read` |
| `events.instance_view` | `calendar:calendar.event:read` |
| `events.patch` | `calendar:calendar.event:update` |
| `events.search_event` | `calendar:calendar.event:read` |
| `events.share_info` | `calendar:calendar.event:read` |
| `freebusys.list` | `calendar:calendar.free_busy:read` |
**注意(强制性):**
- 涉及日期(时间)字符串与时间戳的相互转换时,务必调用系统命令或脚本代码等外部工具进行处理,以确保转换的绝对准确。违者将导致严重的逻辑错误!

View File

@ -0,0 +1,78 @@
# calendar +agenda
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
查看近期日程安排。只读操作,不修改任何日程。
需要的scopes: ["calendar:calendar.event:read"]
## 命令
```bash
# 查看今天日程(默认)
lark-cli calendar +agenda
# 自定义时间范围ISO 8601
lark-cli calendar +agenda --start "2026-03-10T00:00+08:00" --end "2026-03-17T00:00+08:00"
# 自定义时间范围(仅日期)
lark-cli calendar +agenda --start 2026-03-10 --end 2026-03-17
# 人类可读格式输出
lark-cli calendar +agenda --format pretty
# 指定日历
lark-cli calendar +agenda --calendar-id cal_xxx
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--start <time>` | 否 | 开始时间ISO 8601 或仅日期,默认当天) |
| `--end <time>` | 否 | 结束时间(默认与 `--start` 属于同一天,自动取当天结束时间) |
| `--calendar-id <id>` | 否 | 日历 ID省略则使用主日历 |
| `--format` | 否 | 输出格式json默认 \| pretty |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 时间格式
`--start``--end` 支持以下格式:
| 格式 | 示例 | 说明 |
|------|------|------|
| ISO 8601 | `2026-03-10T14:00:00+08:00` | 完整格式 |
| 日期+时间 | `2026-03-10 14:00:00` | 自动补全时区 |
| 仅日期 | `2026-03-10` | start 取 00:00:00end 取 23:59:59 |
| Unix 时间戳 | `1741564800` | 秒级时间戳 |
## 输出格式
**将结果整理为易读的日程表:**
```
## 2026-03-10 周一
09:00 - 09:30 站会
10:00 - 11:00 产品评审
14:00 - 15:00 与 Alice 1:1
## 2026-03-11 周二
(无日程)
```
**注意:按日期分组,并严格按照开始时间升序(从早到晚的时间线)排序输出。** 显示标题、时长
## 提示
- 已取消的日程会自动过滤,无需额外处理。
- 如无日程,告知用户"日程清空"。
- 大于 40 天的时间范围会自动拆分查询并合并结果。
- 查看多个日历:先用 `lark-cli calendar calendars list --page-all` 列出日历列表,再逐个查询。
## 参考
- [lark-calendar](../SKILL.md) -- 日历全部命令
- [lark-shared](../../lark-shared/SKILL.md) -- 认证和全局参数

View File

@ -0,0 +1,109 @@
# calendar +create
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
创建日程并按需邀请参会人。
需要的scopes: ["calendar:calendar.event:create","calendar:calendar.event:update"]
## 推荐命令
```bash
# 创建日程 + 邀请参会人ISO 8601 时间)
lark-cli calendar +create \
--summary "产品评审" \
--start "2026-03-12T14:00+08:00" \
--end "2026-03-12T15:00+08:00" \
--attendee-ids ou_aaa,ou_bbb
# 无参会人
lark-cli calendar +create \
--summary "午餐" \
--start "2026-03-12T12:00+08:00" \
--end "2026-03-12T13:00+08:00"
# 指定日历
lark-cli calendar +create --summary "..." --start "..." --end "..." \
--calendar-id cal_xxx
```
参数:
| 参数 | 必填 | 说明 |
|------|------|------|
| `--summary <text>` | 否 | 日程标题。注意:标题中不应该出现时间、地点、人物信息 |
| `--start <time>` | 是 | 开始时间ISO 8601`2026-03-12T14:00+08:00` |
| `--end <time>` | 是 | 结束时间ISO 8601 |
| `--description <text>` | 否 | 日程详细描述。提供会议议程、活动内容、注意事项或链接等。与 summary 配合使用,仅关注当前日程信息 |
| `--attendee-ids <id_list>` | 否 | 参与人 ID 列表(逗号分隔)。支持用户(`ou_`)、群组(`oc_`)和会议室(`omm_`。AI 提取时请务必保留对应前缀 |
| `--calendar-id <id>` | 否 | 日历 ID省略则使用主日历 |
| `--rrule <rrule>` | 否 | 重复日程的重复性规则规则设置方式参考rfc5545。**【⚠️注意:系统绝对不支持 COUNT如需限制重复次数必须转为 UNTIL】**。示例值:"FREQ=DAILY;INTERVAL=1" |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
> **⚠️ `rrule` 规则限制:飞书日历系统不支持 `COUNT` 参数。遇到限制重复次数的需求,必须根据开始时间和频率自行推算并转换成 `UNTIL=<具体日期>` 格式。**
> 自动设置 `attendee_ability: "can_modify_event"`,参会人可查看彼此并编辑日程。
> 自动设置 `free_busy_status: "busy"`,默认日程忙闲状态为忙碌。
> 自动设置 `reminders: [{"minutes": 5}]`,默认日程开始前 5 分钟提醒。
> 自动设置 `vchat: {"vc_type": "vc"}`,默认日程包含飞书视频会议。如需其他视频会议类型或不含视频会议,请使用完整 API 命令。
> 失败保护:若添加参会人失败(如 open_id 错误CLI 会自动删除刚创建的空日程(回滚,不通知参会人)。
## 高级用法(完整 API 命令)
如需配置 `location`(地理位置,不含会议室位置)、`visibility`(日程公开范围)、自定义 `reminders`(提醒设置)、自定义 `attendee_ability`(参与人权限)、自定义 `free_busy_status`(日程忙闲状态)、参与人可选参加状态或全天日程等高级参数,请使用完整的 API 命令:
**注意**
- 全天日程的开始日期和结束日期必须分别是日程开始的第一天和结束的最后一天。如果只有一天的话,开始日期和结束日期是相同。
```bash
# 第一步:创建日程(含高级参数)
## 查看完整参数定义
lark-cli schema calendar.events.create
## 创建日程
lark-cli calendar events create --calendar-id primary --data '{
"summary": "产品评审",
"description": "本周分享主题CLI 架构设计",
"start_time": { "timestamp": "1741586400" },
"end_time": { "timestamp": "1741593600" },
"location": { "name": "5F-大会议室" },
"attendee_ability": "can_modify_event",
"reminders": [{ "minutes": 15 }]
}'
# 第二步:添加参会人(使用第一步返回的 calendar_id 和 event_id
## 查看完整参数定义
lark-cli schema calendar.event.attendees.create
## 添加参会人
lark-cli calendar event.attendees create \
--calendar-id <CALENDAR_ID> --event-id <EVENT_ID> \
--data '{"attendees": [{"type": "user", "user_id": "ou_xxx"}]}'
# 可选第三步(推荐):若第二步失败,回滚删除空日程
## 查看完整参数定义
lark-cli schema calendar.events.delete
## 删除空日程
lark-cli calendar events delete \
--calendar-id <CALENDAR_ID> --event-id <EVENT_ID> \
--params '{"need_notification":false}'
```
> 完整 API 命令的时间参数是 **Unix 秒字符串**(非 ISO 8601
> 当你手动拆成两步执行时,建议保留“失败后回滚删除”的第三步,避免遗留空日程。
## 参会人类型
| `type` | `user_id` 格式 | 说明 |
|--------|---------------|------|
| `user` | `ou_xxx`open_id | 飞书用户 |
| `group` | `oc_xxx` | 飞书群组 |
| `resource` | `omm_xxx` | 会议室 |
| `third_party` | 邮箱地址 | 外部参会人 |
> [!CAUTION]
> 这是**写入操作** -- 执行前必须确认用户意图。
## 参考
- [lark-calendar](../SKILL.md) -- 日历全部命令
- [lark-shared](../../lark-shared/SKILL.md) -- 认证和全局参数
- [lark-calendar-suggestion](lark-calendar-suggestion.md) -- 根据非明确时间或一段时间范围,推荐多个可用时间块方案

View File

@ -0,0 +1,124 @@
# calendar +freebusy
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md)。
查询用户主日历的忙闲信息返回指定时间范围内的忙碌时段列表和rsvp的状态。
需要的scopes: ["calendar:calendar.free_busy:read"]
## 命令
```bash
# 查询当前用户今天的忙闲(默认)
lark-cli calendar +freebusy
# 自定义时间范围(仅日期)
lark-cli calendar +freebusy --start 2026-03-11 --end 2026-03-12
# 自定义时间范围(完整 ISO 8601
lark-cli calendar +freebusy --start "2026-03-11T08:00:00+08:00" --end "2026-03-11T18:00:00+08:00"
# 查询指定用户的忙闲信息
lark-cli calendar +freebusy --start 2026-03-11 --end 2026-03-12 --user-id ou_xxx
# 人类可读格式输出
lark-cli calendar +freebusy --format pretty
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--start <time>` | 否 | 查询开始时间ISO 8601 或仅日期,默认当天) |
| `--end <time>` | 否 | 查询结束时间(默认与 `--start` 属于同一天,自动取当天结束时间) |
| `--user-id <open_id>` | 否 | 目标查询用户 ID`ou_` 前缀。省略时默认查询当前登录用户bot 身份调用时必须明确指定 |
| `--format` | 否 | 输出格式json默认 \| pretty |
| `--dry-run` | 否 | 预览 API 调用,不执行 |
## 时间格式
`--start``--end` 支持以下格式:
| 格式 | 示例 | 说明 |
|------|------|------|
| ISO 8601 | `2026-03-11T09:00:00+08:00` | 完整格式 |
| 日期+时间 | `2026-03-11 09:00:00` | 自动补全时区 |
| 仅日期 | `2026-03-11` | start 取 00:00:00end 取 23:59:59 |
| Unix 时间戳 | `1741564800` | 秒级时间戳 |
## 输出示例
### 表格格式
```
start end rsvp_status
---------------- ---------------- -----------
2026-03-11 10:00 2026-03-11 10:30 接受
2026-03-11 14:00 2026-03-11 15:00 待定
共 2 个忙碌时段
```
### JSON 格式
```json
[
{
"start_time": "2026-03-11T10:00:00+08:00",
"end_time": "2026-03-11T10:30:00+08:00",
"rsvp_status": "accept"
},
{
"start_time": "2026-03-11T14:00:00+08:00",
"end_time": "2026-03-11T15:00:00+08:00",
"rsvp_status": "tentative"
}
]
```
## 典型场景
### 1. 查找日程会议空闲时段
```bash
# 查询今天的忙碌时段
lark-cli calendar +freebusy
# 查询工作时间段
lark-cli calendar +freebusy \
--start "2026-03-11T08:00:00+08:00" \
--end "2026-03-11T18:00:00+08:00"
```
### 2. 检查团队成员可用性
```bash
# 查询多个成员,对比找出共同空闲时间
lark-cli calendar +freebusy --start 2026-03-12 --user-id ou_member_a
lark-cli calendar +freebusy --start 2026-03-12 --user-id ou_member_b
```
## 注意事项
1. **只查询主日历** — 此命令只返回用户主日历的忙闲信息,不包括其他订阅日历
2. **隐私保护** — 只返回忙碌时段的起止时间,不包含日程标题、描述等详细信息
3. **bot 身份** — bot 必须通过 `--user-id` 指定要查询的用户
## 与其他命令对比
| 命令 | 用途 | 输出内容 |
|------|------|----------|
| `calendar +freebusy` | 查询忙闲时段 | 只返回忙碌时段列表(无日程详情) |
| `calendar +agenda` | 查看日程安排 | 返回完整日程列表(含标题、描述等) |
**选择建议**
- **仅需了解是否有空** → 使用 `+freebusy`(更快,隐私保护)
- **需要查看日程详情** → 使用 `+agenda`
## 参考
- [lark-calendar-agenda](lark-calendar-agenda.md) — 查看日程安排
- [lark-calendar-create](lark-calendar-create.md) — 创建日程
- [lark-calendar-suggestion](lark-calendar-suggestion.md) — 根据非明确时间或一段时间范围,推荐多个可用时间块方案
- [lark-calendar](../SKILL.md) — 日历完整 API

View File

@ -0,0 +1,89 @@
# calendar +room-find
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md)。
针对一个或多个时间块查找/搜索可用会议室。会议室是日程的一种资源型参与人,不能脱离日程单独预定。
需要的 scopes: ["calendar:calendar.free_busy:read"]
## 适用场景
- 已知一个或多个待选时间块,需要查找可用会议室
## 命令
```bash
lark-cli calendar +room-find \
--slot "2026-03-27T14:00:00+08:00~2026-03-27T15:00:00+08:00" \
--slot "2026-03-27T16:00:00+08:00~2026-03-27T17:00:00+08:00" \
--attendee-ids "ou_xxx,ou_yyy" \
--city "北京" \
--building "学清嘉创大厦B座" \
--floor "F2" \
--event-rrule "FREQ=DAILY;INTERVAL=1"
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--slot <start~end>` | 是 | 期望查询的时间块,格式需遵循 `开始时间~结束时间`。若存在多个候选时间块,可重复传入此参数。 |
| `--city <text>` | 否 | 会议室所在城市强约束。**仅当**用户明确说出具体城市(如北京、上海)时才提取,**严禁**根据园区或楼宇名称自行联想或补全。 |
| `--building <text>` | 否 | 会议室所在楼宇强约束,承载城市以下、楼层以上的办公区/园区/楼栋描述。|
| `--floor <text>` | 否 | 仅用于筛选会议室所在楼层。应先做归一化,再传递规范值;例如 `2楼` / `二楼` / `2F` 统一为 `F2`。注意此参数只筛选楼层不可混入区域定位如“A区”或具体会议室号。 |
| `--room-name <text>` | 否 | 会议室名强约束。仅当用户明确提到会议室专名或会议室号如“木星”“02”时使用。应优先传递去后缀、去冗余后的规范名例如 `木星会议室``木星``会议室 02` / `02会议室``02`。 |
| `--min-capacity <n>` | 否 | 会议室最小容纳人数。当用户明确参会人数或提出“至少容纳N人”等要求时提取数字放入此参数必须为正整数。 |
| `--max-capacity <n>` | 否 | 会议室最大容纳人数。用于过滤过大空间,必须为正整数。 |
| `--attendee-ids <id_list>` | 否 | 参会对象 ID 列表。支持用户 ID`ou_` 前缀)和群组 ID`oc_` 前缀),多个 ID 以逗号分隔。 |
| `--event-rrule <rrule>` | 否 | 重复日程的重复性规则规则设置方式参考rfc5545。**【⚠️注意:系统绝对不支持 COUNT如需限制重复次数必须转为 UNTIL】**。示例值:"FREQ=DAILY;INTERVAL=1" |
| `--timezone <tz>` | 否 | 对话中明确提及的预约日程所使用的时区(默认取用户设备时区,例如 `Asia/Shanghai` |
## 规则
- 多个 `--slot` 会由 CLI 内部并发调用单时间块接口,再聚合成一次输出
- `+room-find` 的时间输入必须是**确定时间块**,不是时间区间搜索。
- 如果是重复性日程,必须校验返回中的 `reserve_until_time`(该会议室最晚可预约时间)是否覆盖 `event-rrule` 对应的重复范围。
- `--city` 仅在用户明确说出城市时才提取;不要仅凭 `望京办公室`、`漕河泾园区`、`南山办公室` 这类位置名自动补城市。
- 若已经提取了 `--city`,则 `--building` 中不要再重复携带城市前缀。例如用户说 `北京学清嘉创大厦B座` 时,应提取为 `--city "北京"``--building "学清嘉创大厦B座"`,不要把 `北京学清嘉创大厦B座` 原样整体传入 `--building`
- 同一语义槽位只保留一个规范值。例如用户说“2楼”应转换为 `--floor "F2"`**禁止**同时传 `2楼 F2` 这类重复楼层信息。
- 参数归类顺序应为:`city/building/floor` > `floor + room-name` 复合表达 > `room-name`。若短词更像楼层/区域定位(如 `2L`、`2F`),优先落到 `--floor`,不要默认落到 `--room-name`。像 `学清2层` 这种表达,通常拆为 `--building "学清"``--floor "F2"`
- 对会议室名要做轻量归一化:`木星会议室` 应提取为 `--room-name "木星"``会议室 02` / `02会议室` 应提取为 `--room-name "02"`
- 对复合会议室号要优先拆分结构化信息:`F3-05` / `F5-07` / `3楼-08` 这类表达,若可稳定识别楼层与会议室号,应优先提取为 `--floor "F3"` + `--room-name "05"`、`--floor "F5"` + `--room-name "07"`、`--floor "F3"` + `--room-name "08"`,不要把整段直接作为 `--room-name`
- 当提供了会议室搜索筛选条件时,返回结果也**不保证**与搜索词完全字面匹配。底层可能会结合邻近楼层做推荐,例如用户搜索 `2层`,即使 `2层` 没有空闲会议室,也可能返回相近的 `3层` 候选。这不应被误判为接口返回异常。
## 输出格式
**将返回的候选会议室整理为易读的结构化排版向用户展示。严禁将时间和会议室名称放在同一行展示,必须分行并使用编号列表呈现可用会议室,严禁揉成一团纯文本堆叠。**
```text
## 2026-03-27 周五
[选项 1] 14:00 - 15:00
可用会议室:
1. 学清嘉创大厦B座-F2-02🎦(7人)
2. 学清嘉创大厦B座-F3-05🎦(11人)
💡 请回复您倾向的选项编号以及对应的会议室序号,我来为您完成预定。
```
> **AI 行为指导:**
> - **结构化展示时间块与会议室**:默认按“时间块 -> 会议室候选”的层级结构展示。**严禁将时间与会议室名称输出在同一行**。以清晰的分行列表呈现可用会议室,并直接询问用户意向。默认原样展示完整 `room_name`;不要擅自缩写、截断、改写,或仅提取楼层及会议室号替代完整名称。
> - **`room_name` 必须逐字透传**:展示给用户的会议室名称,必须直接使用 CLI/API 返回的 `room_name` 原值。禁止提取楼层、会议室号、容量、视频能力后重组成新的名称,禁止意译、缩写、去前缀、去后缀,或仅保留“便于阅读”的摘要名。
> - **重复日程要明确阻断原因与自动缩短**:若某候选会议室的 `reserve_until_time` 无法覆盖重复性日程,**必须**向用户明确说明该会议室最长可约至何时。若用户确认继续选用该会议室,你必须**自动将日程的重复规则结束时间缩短**至该 `reserve_until_time`,以防止会议室预约失败。不能直接按原规则继续。
> - **正确解释推荐结果**:如果返回结果与用户输入条件不完全字面一致,先说明底层可能返回邻近位置或相近条件的推荐候选,不要直接将其判定为异常。
> - **默认减少用户输入成本**:应主动引导用户不必一开始就提供很详细的会议室搜索条件。只要时间块已明确,用户直接表达“想约会议室”即可,先基于当前信息查询候选;只有在用户对结果不满意时,再引导其补充更具体的楼宇、楼层、会议室名或容量条件。
**字段说明:**
| 字段名 | 说明 |
| :--- | :--- |
| `room_id` | 会议室唯一标识,用于后续创建日程时添加为会议室参与人使用。 |
| `room_name` | 会议室名称,默认原样完整展示给用户,不要自行缩写、截断、改写,也不要用楼层及会议室号摘要替代原值。 |
| `capacity` | 会议室最大容纳人数。 |
| `reserve_until_time` | 该会议室当前允许被预约到的最晚时间点,用于校验重复性日程是否超期。 |
## 参考
- [lark-calendar-create](lark-calendar-create.md)
- [lark-calendar-suggestion](lark-calendar-suggestion.md)
- [lark-calendar](../SKILL.md) — 日历完整 API

Some files were not shown because too many files have changed in this diff Show More