docs(base): refine record cell value guidance (#636)

* refactor(base): enforce field-map record upsert input

1. Reject top-level fields wrappers in base +record-upsert input and keep request bodies as field maps.

2. Replace record-upsert tests with Map<FieldNameOrID, CellValue> input and assert the outgoing body has no fields wrapper.

3. Consolidate Base record value documentation around lark-base-cell-value and update record command references.

* refactor(base): use common record JSON parsing for upsert

1. Remove the dedicated record-upsert parser and restore the shared record JSON object validation path.

2. Keep record-upsert dry-run and execution as raw JSON object passthrough.

3. Drop the test assertion that rejected a top-level fields key for record-upsert.

* docs(base): refine record cell value guidance

1. Align record CellValue examples with live behavior for date, URL, user, link, select, numeric styles, and readonly fields.

2. Remove misleading user_id_type and execution identity prompts from record-writing guidance.

3. Keep record JSON file input guidance generic and avoid documenting environment-specific stdin or path limits.
This commit is contained in:
zgz2048
2026-04-23 19:18:14 +08:00
committed by GitHub
parent 10f1f2e2ea
commit f52ea47163
24 changed files with 277 additions and 409 deletions

View File

@@ -584,17 +584,25 @@ func TestBaseTableExecuteUpdate(t *testing.T) {
func TestBaseRecordExecuteUpsertUpdate(t *testing.T) {
factory, stdout, reg := newExecuteFactory(t)
reg.Register(&httpmock.Stub{
updateStub := &httpmock.Stub{
Method: "PATCH",
URL: "/open-apis/base/v3/bases/app_x/tables/tbl_x/records/rec_x",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"record_id": "rec_x", "fields": map[string]interface{}{"Name": "Alice"}},
},
})
if err := runShortcut(t, BaseRecordUpsert, []string{"+record-upsert", "--base-token", "app_x", "--table-id", "tbl_x", "--record-id", "rec_x", "--json", `{"fields":{"Name":"Alice"}}`}, factory, stdout); err != nil {
}
reg.Register(updateStub)
if err := runShortcut(t, BaseRecordUpsert, []string{"+record-upsert", "--base-token", "app_x", "--table-id", "tbl_x", "--record-id", "rec_x", "--json", `{"Name":"Alice"}`}, factory, stdout); err != nil {
t.Fatalf("err=%v", err)
}
body := decodeCapturedJSONBody(t, updateStub)
if body["Name"] != "Alice" {
t.Fatalf("request body=%v", body)
}
if _, ok := body["fields"]; ok {
t.Fatalf("request body must not contain fields wrapper: %v", body)
}
if got := stdout.String(); !strings.Contains(got, `"updated": true`) || !strings.Contains(got, `"rec_x"`) {
t.Fatalf("stdout=%s", got)
}
@@ -1018,17 +1026,25 @@ func TestBaseRecordExecuteReadCreateDelete(t *testing.T) {
t.Run("create", func(t *testing.T) {
factory, stdout, reg := newExecuteFactory(t)
reg.Register(&httpmock.Stub{
createStub := &httpmock.Stub{
Method: "POST",
URL: "/open-apis/base/v3/bases/app_x/tables/tbl_x/records",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"record_id": "rec_new", "fields": map[string]interface{}{"Name": "Alice"}},
},
})
if err := runShortcut(t, BaseRecordUpsert, []string{"+record-upsert", "--base-token", "app_x", "--table-id", "tbl_x", "--json", `{"fields":{"Name":"Alice"}}`}, factory, stdout); err != nil {
}
reg.Register(createStub)
if err := runShortcut(t, BaseRecordUpsert, []string{"+record-upsert", "--base-token", "app_x", "--table-id", "tbl_x", "--json", `{"Name":"Alice"}`}, factory, stdout); err != nil {
t.Fatalf("err=%v", err)
}
body := decodeCapturedJSONBody(t, createStub)
if body["Name"] != "Alice" {
t.Fatalf("request body=%v", body)
}
if _, ok := body["fields"]; ok {
t.Fatalf("request body must not contain fields wrapper: %v", body)
}
if got := stdout.String(); !strings.Contains(got, `"created": true`) || !strings.Contains(got, `"rec_new"`) {
t.Fatalf("stdout=%s", got)
}

View File

@@ -252,6 +252,7 @@ func TestBaseTableValidate(t *testing.T) {
}
func TestBaseRecordValidate(t *testing.T) {
ctx := context.Background()
if BaseRecordList.Validate != nil {
t.Fatalf("record list validate should be nil for repeatable --field-id")
}
@@ -264,6 +265,9 @@ func TestBaseRecordValidate(t *testing.T) {
if BaseRecordUpsert.Validate == nil {
t.Fatalf("record upsert validate should reject invalid JSON before dry-run")
}
if err := BaseRecordUpsert.Validate(ctx, newBaseTestRuntime(map[string]string{"base-token": "b", "table-id": "tbl_1", "json": `{"Name":"Alice"}`}, nil, nil)); err != nil {
t.Fatalf("record upsert map validate err=%v", err)
}
}
func TestBaseViewValidate(t *testing.T) {

View File

@@ -572,30 +572,6 @@ func resolveViewRef(views []map[string]interface{}, ref string) (map[string]inte
return nil, fmt.Errorf("view %q not found", ref)
}
func normalizeRecordInputs(raw string) ([]map[string]interface{}, error) {
var records []interface{}
if err := common.ParseJSON([]byte(raw), &records); err != nil {
return nil, fmt.Errorf("--records invalid JSON, must be a record array")
}
result := make([]map[string]interface{}, 0, len(records))
for idx, item := range records {
record, ok := item.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("record %d must be an object", idx+1)
}
if fields, ok := record["fields"].(map[string]interface{}); ok {
normalized := map[string]interface{}{"fields": fields}
if recordID, ok := record["record_id"].(string); ok && recordID != "" {
normalized["record_id"] = recordID
}
result = append(result, normalized)
continue
}
result = append(result, map[string]interface{}{"fields": record})
}
return result, nil
}
func chunkRecords(records []map[string]interface{}, size int) [][]map[string]interface{} {
if size <= 0 {
size = 1

View File

@@ -189,13 +189,7 @@ func TestBaseV3Helpers(t *testing.T) {
}
func TestRecordAndChunkHelpers(t *testing.T) {
records, err := normalizeRecordInputs(`[{"record_id":"rec_1","fields":{"Name":"Alice"}},{"Name":"Bob"}]`)
if err != nil || len(records) != 2 {
t.Fatalf("records=%v err=%v", records, err)
}
if _, err := normalizeRecordInputs(`[1]`); err == nil || !strings.Contains(err.Error(), "must be an object") {
t.Fatalf("err=%v", err)
}
records := []map[string]interface{}{{"record_id": "rec_1"}, {"record_id": "rec_2"}}
if len(chunkRecords(records, 1)) != 2 || len(chunkStringIDs([]string{"a", "b", "c"}, 2)) != 2 {
t.Fatalf("chunk helpers mismatch")
}

View File

@@ -24,6 +24,7 @@ var BaseRecordBatchCreate = common.Shortcut{
Tips: []string{
`Example: --json '{"fields":["Title","Status"],"rows":[["Task A","Open"],["Task B","Done"]]}'`,
"Agent hint: use the lark-base skill's record-batch-create guide for usage and limits.",
"Agent hint: use lark-base-cell-value.md as the source of truth for each CellValue.",
},
Validate: func(ctx context.Context, runtime *common.RuntimeContext) error {
return validateRecordJSON(runtime)

View File

@@ -24,6 +24,7 @@ var BaseRecordBatchUpdate = common.Shortcut{
Tips: []string{
`Example: --json '{"record_id_list":["recXXX"],"patch":{"Status":"Done"}}'`,
"Agent hint: use the lark-base skill's record-batch-update guide for usage and limits.",
"Agent hint: use lark-base-cell-value.md as the source of truth for each patch CellValue.",
},
Validate: func(ctx context.Context, runtime *common.RuntimeContext) error {
return validateRecordJSON(runtime)

View File

@@ -20,7 +20,7 @@ var BaseRecordUpsert = common.Shortcut{
baseTokenFlag(true),
tableRefFlag(true),
recordRefFlag(false),
{Name: "json", Desc: "record JSON object", Required: true},
{Name: "json", Desc: "record JSON object: Map<FieldNameOrID, CellValue>", Required: true},
},
Tips: []string{
`Example: --json '{"Name":"Alice"}'`,

View File

@@ -7,10 +7,10 @@
- `lark-cli base table.records create`
- `lark-cli base records create`
2. **优先使用 Shortcut** — 有 Shortcut 的操作不要手拼原生 API
3. **写记录前** — 先调用 `table.fields list` 获取字段 `type/ui_type`,再读 [lark-base-shortcut-record-value.md](../../skills/lark-base/references/lark-base-shortcut-record-value.md) 确认每个字段类型的值格式
3. **写记录前** — 先调用 `table.fields list` 获取字段 `type/ui_type`,再读 [lark-base-cell-value.md](../../skills/lark-base/references/lark-base-cell-value.md);该文档是 CellValue 的 source of truth
4. **写字段前** — 先读 [lark-base-shortcut-field-properties.md](../../skills/lark-base/references/lark-base-shortcut-field-properties.md) 确认字段类型的 `property` 结构
5. **筛选查询前** — 先读 [lark-base-view-set-filter.md](../../skills/lark-base/references/lark-base-view-set-filter.md),当前 `base/v3` 通过 `view.filter update + table.records list` 组合完成筛选读取
6. **批量上限 500 条/次** — 同一表建议串行写入,并在批次间延迟 0.51 秒
6. **批量上限 200 条/次** — 同一表建议串行写入,并在批次间延迟 0.51 秒
7. **改名和删除按明确意图执行** — 视图重命名这类低风险改名操作,目标和新名称明确时可直接执行;删除记录 / 字段 / 表时,只要用户已经明确要求删除且目标明确,也可直接执行,不需要再补一次确认
8. **不要走旧 bitable 路径** — Base 场景不要调用 `lark-cli api GET /open-apis/bitable/v1/...`;即使 wiki 解析结果是 `obj_type=bitable`,后续也应继续使用 `lark-cli base ...`
9. **不要把本地文件导入误判成 Base 表内操作** — 如果目标是“把 Excel / CSV / `.base` 快照导入成 Base / 多维表格”,必须先走 `lark-cli drive +import --type bitable`;只有导入完成后,才回到 `lark-cli base ...`
@@ -34,13 +34,11 @@
- **Base token 口径统一**:无论 Shortcut 还是原生 API都统一使用 `base_token`
- **附件字段**:上传本地文件时只能走 `lark-cli base +record-upload-attachment`
- **人员字段 / 用户字段**:调试时注意 `user_id_type` 与执行身份user / bot差异
- **能力边界**:当前 `base/v3` 原生 spec 以单表 / 单记录 / 视图筛选配置为主,批量写入和旧 `search` 场景优先走 unified Shortcut 组合能力
- **视图重命名确认规则**:用户已经明确“把哪个视图改成什么名字”时,执行 `table.views patch` / 对应 shortcut 直接改名即可,不需要再补一句确认
- **删除确认规则(记录 / 字段 / 表)**:执行 `table.records delete / table.fields delete / tables delete` 或对应 shortcut 时,如果用户已经明确要求删除且目标明确,可以直接执行;只有目标不明确时才先追问
- **创建 / 复制 Base 的友好性规则**:创建或复制 Base 时,`folder_token``time_zone`、复制时的新名称都属于可选项;用户没特别要求时不要为这些参数额外打断
- **创建 / 复制 Base 的结果返回规范**:成功后必须主动返回新 Base 的 token如果返回结果里带可访问链接`base.url`),也要一并返回
- **附件字段本地文件上传**:只能使用 `lark-cli base +record-upload-attachment`
## Wiki 链接特殊处理(特别关键!)
@@ -103,18 +101,18 @@ lark-cli wiki spaces.get_node --params '{"token":"Pgrrwvr***********UnRb"}'
| 错误码 | 含义 | 解决方案 |
|--------|------|---------|
| 1254064 | 日期格式错误 | 用毫秒时间戳,非字符串 / 秒级 |
| 1254068 | 超链接格式错误 | 用 `{text, link}` 对象 |
| 1254066 | 人员字段错误 | `[{id:"ou_xxx"}]`,并确认 `user_id_type` |
| 1254064 | 日期格式错误 | 传 `YYYY-MM-DD HH:mm:ss` 字符串,不要写相对时间 |
| 1254068 | 超链接格式错误 | `"https://example.com"` 或 `"[文本](https://example.com)"` |
| 1254066 | 人员字段错误 | `[{ "id": "ou_xxx" }]` |
| 1254045 | 字段名不存在 | 检查字段名(含空格、大小写) |
| 1254015 | 字段值类型不匹配 | 先 list 字段,再按类型构造 |
| `param baseToken is invalid` / `base_token invalid` | 把 wiki token、workspace token 或其他 token 当成了 `base_token` | 如果输入来自 `/wiki/...`,先查 `wiki.spaces.get_node`;当 `obj_type=bitable` 时,用 `node.obj_token` 作为 `base_token` 重试,不要改走 `bitable/v1` |
| 1254104 | 批量超 500 条 | 分批调用 |
| 1254104 | 批量超 200 条 | 分批调用 |
| 1254291 | 并发写冲突 | 串行写入 + 批次间延迟 |
## 参考文档
- [lark-base-shortcut-field-properties.md](../../skills/lark-base/references/lark-base-shortcut-field-properties.md) — 字段类型 property 配置
- [lark-base-shortcut-record-value.md](../../skills/lark-base/references/lark-base-shortcut-record-value.md) — 记录值格式详解
- [lark-base-shortcut-field-properties.md](../../skills/lark-base/references/lark-base-shortcut-field-properties.md) — 字段类型 JSON 配置
- [lark-base-cell-value.md](../../skills/lark-base/references/lark-base-cell-value.md) — CellValue source of truth
- [lark-base-view-set-filter.md](../../skills/lark-base/references/lark-base-view-set-filter.md) — 查询筛选指南filter / operator / sort / 分页)
- [examples.md](../../skills/lark-base/references/examples.md) — 完整操作示例(建表、筛选、更新)

View File

@@ -103,7 +103,7 @@ metadata:
| 命令 | 用途 / 何时使用 | 必读 reference | 路由提醒 |
|------|------------------|----------------|----------|
| `+record-search / +record-list / +record-get` | 按关键词检索记录、读取记录明细 / 分页导出,或获取单条记录详情 | [`lark-base-record-search.md`](references/lark-base-record-search.md)、[`lark-base-record-list.md`](references/lark-base-record-list.md)、[`lark-base-record-get.md`](references/lark-base-record-get.md) | 默认优先 `+record-list`;仅当用户提供明确搜索关键词时使用 `+record-search`;取数不用来做聚合分析;`--limit` 最大 `200`;仅在用户明确需要时继续翻页;`+record-list` 只能串行执行 |
| `+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-shortcut-record-value.md`](references/lark-base-shortcut-record-value.md) | 写前先 `+field-list`;只写存储字段;`+record-batch-update` 为同值更新(同一 patch 应用到多条记录);批量单次不超过 `200` 条;附件不要走这里 |
| `+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 / +record-history-list` | 删除记录,或查询某条记录的变更历史 | [`lark-base-record-delete.md`](references/lark-base-record-delete.md)、[`lark-base-record-history-list.md`](references/lark-base-record-history-list.md) | 删除时用户已明确目标可直接执行并带 `--yes`;历史查询按 `table-id + record-id`,不支持整表扫描;`+record-history-list` 只能串行执行 |
@@ -273,10 +273,6 @@ lark-cli auth login --domain base
4. 若 bot 身份仍然返回权限错误,**立即停止重试**,根据错误响应按 `lark-shared` 流程引导用户解决(引导去开发者后台开通 scope 或确认资源访问权限)。
5. 只有在用户明确要求"用应用身份 / bot 身份操作",才跳过 user 直接使用 `--as bot`
**补充说明**
- 人员字段 / 用户字段:注意 `user_id_type` 与执行身份user / bot差异。
## 4. 执行规则
### 4.1 标准执行顺序
@@ -293,7 +289,7 @@ lark-cli auth login --domain base
1. 先拿结构,再写命令;至少先拿当前表结构,跨表时还要拿目标表结构。
2. 不要猜表名、字段名、表达式引用,一律以真实返回为准。
3. 只使用原子命令;不要回退到旧的聚合式 `+table / +field / +record / +view / +history / +workspace`
4. 写记录前先读字段结构;先 `+field-list`,再按字段类型构造写入值
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`
@@ -323,15 +319,15 @@ lark-cli auth login --domain base
| 错误 / 现象 | 含义 | 恢复动作 |
|-------------|------|----------|
| `1254064` | 日期格式错误 | 用毫秒时间戳,非字符串 / 秒级 |
| `1254068` | 超链接格式错误 | `{text, link}` 对象 |
| `1254066` | 人员字段错误 | `[{id:"ou_xxx"}]`,并确认 `user_id_type` |
| `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 重建请求 |
| 系统字段 / 公式字段写入失败 | 只读字段被当成可写字段 | 改为写存储字段计算结果交给 formula / lookup / 系统字段自动产出 |
| `ignored_fields` / `READONLY` | 只读字段被当成可写字段常见于系统字段、formula、lookup | 移除只读字段,只写存储字段计算结果交给 formula / lookup / 系统字段自动产出 |
| `1254104` | 批量超 200 条 | 分批调用 |
| `1254291` | 并发写冲突 | 串行写入 + 批次间延迟 |
| `91403` | 无权限访问该 Base | **不要重试**。按 `lark-shared` 权限不足处理流程引导用户解决权限问题 |

View File

@@ -2,7 +2,7 @@
本文档提供基于 `lark-cli base ...` 的完整示例,覆盖 unified Shortcut 与当前 `base/v3` 原生 API 的常见组合方式。
> **返回**: [SKILL.md](../SKILL.md) | **参考**: [shortcut 字段 JSON 规范](lark-base-shortcut-field-properties.md) · [shortcut 记录值规范](lark-base-shortcut-record-value.md)
> **返回**: [SKILL.md](../SKILL.md) | **参考**: [shortcut 字段 JSON 规范](lark-base-shortcut-field-properties.md) · [CellValue 规范](lark-base-cell-value.md)
---

View File

@@ -0,0 +1,144 @@
# 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
用对象数组,元素至少包含 `id`。单选/多选人员字段都使用数组;`id` 必须是可被当前 Base 识别的用户 ID写入前确认字段是否允许多选人员。
```json
{
"负责人": [
{ "id": "ou_xxx" },
{ "id": "ou_xxx2" }
]
}
```
### 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

@@ -371,5 +371,5 @@ value 使用预定义关键字机制,第一个元素为字符串常量名称
- [lark-base](../SKILL.md) — 多维表格全部命令
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数
- [lark-base-shortcut-record-value.md](lark-base-shortcut-record-value.md) — shortcut 字段值格式规范
- [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

@@ -6,23 +6,16 @@
## 适用场景(重点)
- 适合大量创建写入场景,例如导入 CSV / Excel、外部系统一次性写入新数据。
- 当输入是长表格或长文本数据时,先按 [lark-base-shortcut-record-value.md](lark-base-shortcut-record-value.md) 做字段映射和类型规范化,再组装 `fields + rows` 调用本命令写入
- 适合导入 CSV / Excel、外部系统一次性写入新数据。
- 先把输入数据映射到合适的字段类型,再组装 `fields + rows`
## 推荐命令
```bash
lark-cli base +record-batch-create \
--base-token XXXXXX \
--table-id tblXXX \
lark-cli base +record-batch-create --base-token <base_token> --table-id <table_id> \
--json '{"fields":["标题","状态"],"rows":[["任务 A","Open"],["任务 B","Done"]]}'
```
```bash
lark-cli base +record-batch-create \
--base-token XXXXXX \
--table-id tblXXX \
--json @batch-create.json
lark-cli base +record-batch-create --base-token <base_token> --table-id <table_id> --json @batch-create.json
```
## 参数
@@ -33,44 +26,33 @@ lark-cli base +record-batch-create \
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--json <body>` | 是 | 批量创建请求体,必须是 JSON 对象。支持直接传 JSON 字符串,或 `@<file_path>` 从文件读取 |
## API 入参详情
## API
**HTTP 方法和路径:**
```http
POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records/batch_create
```
`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` | `any[][]` | 是 | 二维数组,每一行按 `fields` 同序给;单次最多 200 行 |
| `rows` | `CellValue[][]` | 是 | 二维数组,每一行按 `fields` 同序给 cell;单次最多 200 行 |
## 返回重点
`data` 为多行二维数组,与 `+record-list` 返回的多行数据结构一致(按 `fields` 列顺序对齐
| 字段 | 类型 | 说明 |
|------|------|------|
| `fields` | `string[]` | 返回的字段名数组 |
| `field_id_list` | `string[]` | 返回的字段 ID 数组 |
| `record_id_list` | `string[]` | 新创建记录 ID 列表 |
| `data` | `any[][]` | 与 `fields` 对齐的多行数据 |
| `ignored_fields` | `array` | 可选,表示被忽略的字段信息 |
返回 `fields``field_id_list``record_id_list``data`,其中 `data` `fields` 列顺序对齐。
## 坑点
- ⚠️ `--json` 必须是对象
- ⚠️ 写 `rows` 前必须先阅读 [lark-base-shortcut-record-value.md](lark-base-shortcut-record-value.md),按字段类型填值,禁止按自然语言猜测 value 结构
- ⚠️ `fields``rows` 列顺序必须一一对应
- ⚠️ 空单元格可以显式用 `null` 填充
- ⚠️ 单次最多 200 行,超出需分批写入。
- `fields` 与每行 `rows` 的列顺序必须一一对应
- 空单元格必须显式用 `null` 填充
- 单次最多 200 行,超出需分批写入
- select 写入未知选项时平台可能自动新增选项;如果不是要新增选项,先确认真实选项名
## 参考
- [lark-base-record.md](lark-base-record.md) — record 索引页
- [lark-base-shortcut-record-value.md](lark-base-shortcut-record-value.md) — 记录值格式规范
- [lark-base-cell-value.md](lark-base-cell-value.md) — CellValue 格式规范

View File

@@ -7,17 +7,10 @@
## 推荐命令
```bash
lark-cli base +record-batch-update \
--base-token XXXXXX \
--table-id tblXXX \
--json '{"record_id_list":["recXXX"],"patch":{"field_id_or_name":"value"}}'
```
lark-cli base +record-batch-update --base-token <base_token> --table-id <table_id> \
--json '{"record_id_list":["<record_id>"],"patch":{"状态":"完成"}}'
```bash
lark-cli base +record-batch-update \
--base-token XXXXXX \
--table-id tblXXX \
--json @batch-update.json
lark-cli base +record-batch-update --base-token <base_token> --table-id <table_id> --json @batch-update.json
```
## 参数
@@ -28,44 +21,33 @@ lark-cli base +record-batch-update \
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--json <body>` | 是 | 批量更新请求体,必须是 JSON 对象。支持直接传 JSON 字符串,或 `@<file_path>` 从文件读取 |
## 生成 `patch` 前必看
## API
- 先阅读 [lark-base-shortcut-record-value.md](lark-base-shortcut-record-value.md),按字段类型构造 `patch` 的 value避免类型不匹配。
## API 入参详情
**HTTP 方法和路径:**
```http
POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records/batch_update
```
`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` | `object` | 是 | 同一份字段更新对象会应用到 `record_id_list` 内所有记录 |
| `patch` | `Map<FieldNameOrID, CellValue>` | 是 | 字段更新对象key 是字段名或字段 IDvalue 是 `CellValue`;同一份 `patch` 会应用到 `record_id_list` 内所有记录 |
## 返回重点
返回结构如下(其中 `update` 可与 `+record-list` 的单行字段对象结构对齐):
| 字段 | 类型 | 说明 |
|------|------|------|
| `record_id_list` | `string[]` | 本次更新到的记录 ID 列表 |
| `update` | `object` | 本次应用的字段更新结果;可能为空对象 |
| `ignored_fields` | `{id,name,reason}[]` | 可选,被忽略字段信息 |
返回 `record_id_list``update`,可选返回 `ignored_fields``update` 可能为空对象。
## 坑点
- ⚠️ `--json` 必须是对象
- ⚠️ 该接口是“同值批量更新”:同一请求内所有 `record_id_list` 都会应用同一份 `patch`
- ⚠️ `record_id_list` 最大 200 条,超过会被接口校验拒绝
- ⚠️ 命令不会自动做字段/行映射转换,传什么就发什么
- 这是“同值批量更新”:所有 `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

@@ -8,9 +8,9 @@
```bash
lark-cli base +record-delete \
--base-token app_xxx \
--table-id tbl_xxx \
--record-id rec_xxx \
--base-token <base_token> \
--table-id <table_id> \
--record-id <record_id> \
--yes
```

View File

@@ -8,9 +8,9 @@
```bash
lark-cli base +record-get \
--base-token app_xxx \
--table-id tbl_xxx \
--record-id rec_xxx
--base-token <base_token> \
--table-id <table_id> \
--record-id <record_id>
```
## 参数

View File

@@ -9,15 +9,15 @@
```bash
# 查询最新一页历史
lark-cli base +record-history-list \
--base-token app_xxx \
--table-id tbl_xxx \
--record-id rec_xxx
--base-token <base_token> \
--table-id <table_id> \
--record-id <record_id>
# 指定分页大小,带游标翻页
lark-cli base +record-history-list \
--base-token app_xxx \
--table-id tbl_xxx \
--record-id rec_xxx \
--base-token <base_token> \
--table-id <table_id> \
--record-id <record_id> \
--page-size 30 \
--max-version 123456
```

View File

@@ -32,15 +32,15 @@
```bash
lark-cli base +record-list \
--base-token XXXXXX \
--table-id tblXXX \
--base-token <base_token> \
--table-id <table_id> \
--offset 0 \
--limit 100
lark-cli base +record-list \
--base-token XXXXXX \
--table-id tblXXX \
--view-id vewXXX \
--base-token <base_token> \
--table-id <table_id> \
--view-id <view_id> \
--field-id fldStatus \
--field-id 项目名称 \
--offset 0 \

View File

@@ -15,14 +15,14 @@
```bash
lark-cli base +record-search \
--base-token XXXXXX \
--table-id tblXXX \
--json '{"keyword":"Created","search_fields":["Title","fld_owner"],"offset":0,"limit":100}'
--base-token <base_token> \
--table-id <table_id> \
--json '{"keyword":"Created","search_fields":["Title","<field_id>"],"offset":0,"limit":100}'
lark-cli base +record-search \
--base-token XXXXXX \
--table-id tblXXX \
--json '{"view_id":"vewXXX","keyword":"Alice","search_fields":["Title","Owner"],"select_fields":["Title","Owner","Status"],"offset":0,"limit":50}'
--base-token <base_token> \
--table-id <table_id> \
--json '{"view_id":"<view_id>","keyword":"Alice","search_fields":["Title","Owner"],"select_fields":["Title","Owner","Status"],"offset":0,"limit":50}'
```
## 参数

View File

@@ -9,14 +9,14 @@
```bash
# 单条记录
lark-cli base +record-share-link-create \
--base-token xxx \
--table-id tbl_xxx \
--record-ids rec_xxx
--base-token <base_token> \
--table-id <table_id> \
--record-ids <record_id>
# 多条记录(使用 "," 分隔)
lark-cli base +record-share-link-create \
--base-token xxx \
--table-id tbl_xxx \
--base-token <base_token> \
--table-id <table_id> \
--record-ids rec001,rec002,rec003
```

View File

@@ -8,16 +8,16 @@
```bash
lark-cli base +record-upload-attachment \
--base-token app_xxx \
--table-id tbl_xxx \
--record-id rec_xxx \
--field-id fld_attach \
--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 app_xxx \
--table-id tbl_xxx \
--record-id rec_xxx \
--base-token <base_token> \
--table-id <table_id> \
--record-id <record_id> \
--field-id "附件" \
--file ./report.pdf \
--name "Q1-final.pdf"
@@ -48,4 +48,3 @@ lark-cli base +record-upload-attachment \
## 参考
- [lark-base-record.md](lark-base-record.md) — record 索引页
- [lark-base-shortcut-record-value.md](lark-base-shortcut-record-value.md) — 记录值格式详解

View File

@@ -7,16 +7,13 @@
## 推荐命令
```bash
lark-cli base +record-upsert \
--base-token app_xxx \
--table-id tbl_xxx \
--json '{"项目名称":"Apollo","状态":"进行中"}'
# 创建记录
lark-cli base +record-upsert --base-token <base_token> --table-id <table_id> \
--json '{"项目名称":"Apollo","状态":"进行中"}'
lark-cli base +record-upsert \
--base-token app_xxx \
--table-id tbl_xxx \
--record-id rec_xxx \
--json '{"项目名称":"Apollo","状态":"进行中","标签":["高优","外部依赖"],"截止日期":"2026-03-24 10:00:00"}'
# 更新记录
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"}'
```
## 参数
@@ -26,44 +23,26 @@ lark-cli base +record-upsert \
| `--base-token <token>` | 是 | Base Token |
| `--table-id <id_or_name>` | 是 | 表 ID 或表名 |
| `--record-id <id>` | 否 | 传入时走更新,不传时走创建 |
| `--json <body>` | 是 | 记录 JSON 对象 |
| `--json <body>` | 是 | 字段写入对象,类型 `Map<FieldNameOrID, CellValue>` |
## API 入参详情
## API
**HTTP 方法和路径:**
- 创建:`POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records`
- 更新:带 `--record-id` 时改走 `PATCH /records/:record_id`
```
POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records
```
## `--json` 结构
- `--record-id` 时内部改走 `PATCH /records/:record_id`
## JSON 值规范
- `--json` 必须是 **JSON 对象**
- key 可用字段名或字段 ID但一次请求里同一字段只用一种标识避免重复写入冲突。
- value 必须匹配字段类型;先 `+field-list` 再构造值。
- 推荐值形状(常用):
- 文本:`"标题"`
- 数字:`12.5`
- 单选:`"Todo"`
- 多选:`["A","B"]`
- 复选框:`true`
- 人员:`[{"id":"ou_xxx"}]`
- 关联记录:`[{"id":"rec_xxx"}]`
- 日期:`"YYYY-MM-DD HH:mm:ss"`(如 `"2026-03-24 10:00:00"`
- 需要清空字段时优先传 `null`(字段允许清空时)。
- 公式、查找引用、创建/更新时间、创建/修改人等只读字段不要写入。
**正确base +record-upsert**
- `--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",
"状态": "进行中",
"标签": ["高优", "外部依赖"],
"负责人": [{ "id": "ou_xxx" }],
"截止日期": "2026-03-24 10:00:00"
"完成时间": "2026-03-24 10:00:00"
}
```
@@ -71,19 +50,15 @@ POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records
- 创建时返回 `record``created: true`
- 更新时返回 `record``updated: true`
## 工作流
1. 先确定是新增还是修改。
- 如果写入了 `formula / lookup / created_at / updated_at / created_by / updated_by` 等只读字段,返回里可能出现 `ignored_fields`,这些字段不会被更新。
## 坑点
- ⚠️ `--json` 必须是对象,不支持数组
- ⚠️ 有 `--record-id` 就一定走更新;不传就一定走创建,不会自动查重
- ⚠️ 这是写入操作,执行前必须确认。
- `--record-id` 就一定更新;不传就一定创建,不会自动查重或按业务键 upsert
- select 写入未知选项时平台可能自动新增选项;如果不是要新增选项,先用 `+field-list` / `+field-search-options` 确认真实选项名
- 这是写入操作,执行前必须确认目标表和字段
## 参考
- [lark-base-record.md](lark-base-record.md) — record 索引页
- [lark-base-shortcut-record-value.md](lark-base-shortcut-record-value.md) — shortcut 记录值格式规范(推荐)
- [lark-base-cell-value.md](lark-base-cell-value.md) — CellValue 格式规范

View File

@@ -24,6 +24,6 @@ record 相关命令索引。
- 聚合页只保留目录职责;每个命令的详细说明请进入对应单命令文档。
- 所有 `+xxx-list` 调用都必须串行执行;若要批量跑多个 list 请求,只能串行执行。
- `+record-list` 支持重复传参 `--field-id` 做字段筛选。
- 写记录 JSON 前优先阅读 [lark-base-shortcut-record-value.md](lark-base-shortcut-record-value.md)。
- 写记录 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

@@ -1,200 +0,0 @@
# base shortcut record JSON 规范lark-base-shortcut-record-value
> 适用命令:`lark-cli base +record-upsert`
本文件定义 **shortcut 写记录**`--json` 的推荐格式,目标是让 AI 一次写对。
## 1. 顶层规则(必须遵守)
- `--json` 必须是 JSON 对象。
- 顶层直接传字段映射:`{"字段名或字段ID": 值}`
- 一次 payload 里同一字段只用一种 key字段名或字段 ID不要重复。
- 写入前先 `+field-list` 获取字段 `type/style/multiple`,再构造值。
## 2. 各类型值格式与示例
### 2.1 text / phone / url
**推荐值**:字符串。
```json
{
"标题": "Hello",
"联系电话": "1380000000000",
"官网": "https://example.com"
}
```
**Schema**
```json
{ "type": "string", "description": "text field cell, example: \"one string and [one url](https://foo.bar)\"" }
```
### 2.2 number
**推荐值**:数字。
```json
{
"工时": 12.5,
"预算": 3000
}
```
**Schema**
```json
{ "type": "number", "description": "number field cell, can be any float64 value" }
```
### 2.3 select单选/多选/阶段)
**推荐值**
- 单选:字符串
- 多选:字符串数组
```json
{
"状态": "Todo",
"标签": ["后端", "高优"]
}
```
**Schema**
```json
{ "type": "array", "items": { "type": "string", "description": "option name" }, "description": "select field cell, example: [\"option_1\", \"option_2\"]" }
```
### 2.4 datetime
**推荐值**`YYYY-MM-DD HH:mm:ss` 字符串(稳妥写法)。
```json
{
"截止时间": "2026-03-24 10:00:00"
}
```
**Schema**
```json
{ "type": "string", "description": "datetime field cell. accepts common datetime strings and timestamp-like values. Prefer \"YYYY-MM-DD HH:mm:ss\" in requests because it is the most stable format and matches the API output. Example: \"2026-01-01 19:30:00\"" }
```
### 2.5 checkbox
**推荐值**:布尔值。
```json
{
"已完成": true
}
```
**Schema**
```json
{ "type": "boolean", "description": "checkbox field cell" }
```
### 2.6 user
**推荐值**:对象数组,元素至少有 `id`
```json
{
"负责人": [
{ "id": "ou_xxx" }
]
}
```
**Schema**
```json
{ "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "user id" } }, "required": ["id"], "additionalProperties": false }, "description": "user field cell, example: [{\"id\": \"ou_123\"}]" }
```
### 2.7 link
**推荐值**:对象数组,元素至少有 `id`
```json
{
"关联任务": [
{ "id": "rec_xxx" }
]
}
```
**Schema**
```json
{ "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "record id" } }, "required": ["id"], "additionalProperties": false }, "description": "link field cell, example: [{\"id\": \"rec_123\"}]" }
```
### 2.8 location
**推荐值**:对象 `{lng, lat}`
```json
{
"坐标": {
"lng": 116.397428,
"lat": 39.90923
}
}
```
**Schema**
```json
{
"type": "object",
"properties": { "lng": { "type": "number", "description": "Longitude" }, "lat": { "type": "number", "description": "Latitude" } },
"required": ["lng", "lat"],
"additionalProperties": false,
"description": "location field cell, example: {\"lng\": 113.94765, \"lat\": 22.528533}"
}
```
### 2.9 attachment
对 agent 而言,附件上传是**特殊链路**:如果用户要把本地文件加到记录里,必须使用 `lark-cli base +record-upload-attachment` 上传到已有记录。
**Schema - attachment**
```json
{
"type": "array",
"items": { "type": "object", "properties": { "file_token": { "type": "string", "minLength": 0, "maxLength": 50 }, "name": { "type": "string", "minLength": 1, "maxLength": 255 } }, "required": ["file_token", "name"], "additionalProperties": false },
"description": "attachment field cell. For agent, do not synthesize this payload via +record-upsert; must use +record-upload-attachment to upload files."
}
```
## 3. 只读字段(不要写)
以下字段在写记录时应视为只读:
- `auto_number`
- `lookup`
- `formula`
- `created_time` / `modified_time`
- `created_by` / `modified_by`
## 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 }
}
```