From 21502336a717cde380007f98ff3202566db78ca8 Mon Sep 17 00:00:00 2001 From: kongenpei Date: Mon, 6 Apr 2026 20:23:38 +0800 Subject: [PATCH] feat(base): add record batch add/set shortcuts --- shortcuts/base/base_dryrun_ops_test.go | 2 + shortcuts/base/base_execute_test.go | 72 +++++++++++++++++++ shortcuts/base/base_shortcuts_test.go | 2 +- shortcuts/base/record_batch_add.go | 31 ++++++++ shortcuts/base/record_batch_set.go | 31 ++++++++ shortcuts/base/record_ops.go | 46 ++++++++++++ shortcuts/base/shortcuts.go | 2 + skills/lark-base/SKILL.md | 14 ++-- .../references/lark-base-record-batch-add.md | 55 ++++++++++++++ .../references/lark-base-record-batch-set.md | 62 ++++++++++++++++ .../lark-base/references/lark-base-record.md | 2 + 11 files changed, 313 insertions(+), 6 deletions(-) create mode 100644 shortcuts/base/record_batch_add.go create mode 100644 shortcuts/base/record_batch_set.go create mode 100644 skills/lark-base/references/lark-base-record-batch-add.md create mode 100644 skills/lark-base/references/lark-base-record-batch-set.md diff --git a/shortcuts/base/base_dryrun_ops_test.go b/shortcuts/base/base_dryrun_ops_test.go index 3898826b..d6039b95 100644 --- a/shortcuts/base/base_dryrun_ops_test.go +++ b/shortcuts/base/base_dryrun_ops_test.go @@ -75,6 +75,8 @@ func TestDryRunRecordOps(t *testing.T) { nil, nil, ) assertDryRunContains(t, dryRunRecordUpsert(ctx, upsertCreateRT), "POST /open-apis/base/v3/bases/app_x/tables/tbl_1/records") + assertDryRunContains(t, dryRunRecordBatchAdd(ctx, upsertCreateRT), "POST /open-apis/base/v3/bases/app_x/tables/tbl_1/records/batch") + assertDryRunContains(t, dryRunRecordBatchSet(ctx, upsertCreateRT), "PATCH /open-apis/base/v3/bases/app_x/tables/tbl_1/records/batch") rt := newBaseTestRuntime( map[string]string{"base-token": "app_x", "table-id": "tbl_1", "record-id": "rec_1", "json": `{"Name":"B"}`}, diff --git a/shortcuts/base/base_execute_test.go b/shortcuts/base/base_execute_test.go index 34128a93..640a1ee8 100644 --- a/shortcuts/base/base_execute_test.go +++ b/shortcuts/base/base_execute_test.go @@ -588,6 +588,78 @@ func TestBaseRecordExecuteReadCreateDelete(t *testing.T) { } }) + t.Run("batch add", func(t *testing.T) { + factory, stdout, reg := newExecuteFactory(t) + registerTokenStub(reg) + reg.Register(&httpmock.Stub{ + Method: "POST", + URL: "/open-apis/base/v3/bases/app_x/tables/tbl_x/records/batch", + Body: map[string]interface{}{ + "code": 0, + "data": map[string]interface{}{ + "fields": []interface{}{"Name"}, + "record_id_list": []interface{}{"rec_1", "rec_2"}, + "data": []interface{}{[]interface{}{"Alice"}, []interface{}{"Bob"}}, + }, + }, + }) + if err := runShortcut(t, BaseRecordBatchAdd, []string{"+record-batch-add", "--base-token", "app_x", "--table-id", "tbl_x", "--json", `{"fields":["Name"],"rows":[["Alice"],["Bob"]]}`}, factory, stdout); err != nil { + t.Fatalf("err=%v", err) + } + if got := stdout.String(); !strings.Contains(got, `"record_id_list"`) || !strings.Contains(got, `"rec_1"`) || !strings.Contains(got, `"Alice"`) { + t.Fatalf("stdout=%s", got) + } + }) + + t.Run("batch set", func(t *testing.T) { + factory, stdout, reg := newExecuteFactory(t) + registerTokenStub(reg) + reg.Register(&httpmock.Stub{ + Method: "PATCH", + URL: "/open-apis/base/v3/bases/app_x/tables/tbl_x/records/batch", + Body: map[string]interface{}{ + "code": 0, + "data": map[string]interface{}{ + "has_more": false, + "record_id_list": []interface{}{"rec_1"}, + "update": map[string]interface{}{"Status": "Done"}, + }, + }, + }) + if err := runShortcut(t, BaseRecordBatchSet, []string{"+record-batch-set", "--base-token", "app_x", "--table-id", "tbl_x", "--json", `{"patch":{"Status":"Done"},"filter":{"logic":"and","conditions":[["Status","is","Open"]]},"offset":0,"limit":200}`}, factory, stdout); err != nil { + t.Fatalf("err=%v", err) + } + if got := stdout.String(); !strings.Contains(got, `"record_id_list"`) || !strings.Contains(got, `"update"`) || !strings.Contains(got, `"Done"`) { + t.Fatalf("stdout=%s", got) + } + }) + + t.Run("batch set matrix passthrough", func(t *testing.T) { + factory, stdout, reg := newExecuteFactory(t) + registerTokenStub(reg) + updateStub := &httpmock.Stub{ + Method: "PATCH", + URL: "/open-apis/base/v3/bases/app_x/tables/tbl_x/records/batch", + Body: map[string]interface{}{ + "code": 0, + "data": map[string]interface{}{ + "record_id_list": []interface{}{"rec_1"}, + }, + }, + } + reg.Register(updateStub) + if err := runShortcut(t, BaseRecordBatchSet, []string{"+record-batch-set", "--base-token", "app_x", "--table-id", "tbl_x", "--json", `{"records":["rec_1"],"fields":["fld_name","fld_status"],"cells":[["Alice","Done"]]}`}, factory, stdout); err != nil { + t.Fatalf("err=%v", err) + } + if got := stdout.String(); !strings.Contains(got, `"record_id_list"`) || !strings.Contains(got, `"rec_1"`) { + t.Fatalf("stdout=%s", got) + } + body := string(updateStub.CapturedBody) + if !strings.Contains(body, `"records":["rec_1"]`) || !strings.Contains(body, `"fields":["fld_name","fld_status"]`) || !strings.Contains(body, `"cells":[["Alice","Done"]]`) { + t.Fatalf("request body=%s", body) + } + }) + t.Run("delete", func(t *testing.T) { factory, stdout, reg := newExecuteFactory(t) registerTokenStub(reg) diff --git a/shortcuts/base/base_shortcuts_test.go b/shortcuts/base/base_shortcuts_test.go index a6f1c61d..cda960be 100644 --- a/shortcuts/base/base_shortcuts_test.go +++ b/shortcuts/base/base_shortcuts_test.go @@ -114,7 +114,7 @@ func TestShortcutsCatalog(t *testing.T) { "+table-list", "+table-get", "+table-create", "+table-update", "+table-delete", "+field-list", "+field-get", "+field-create", "+field-update", "+field-delete", "+field-search-options", "+view-list", "+view-get", "+view-create", "+view-delete", "+view-get-filter", "+view-set-filter", "+view-get-group", "+view-set-group", "+view-get-sort", "+view-set-sort", "+view-get-timebar", "+view-set-timebar", "+view-get-card", "+view-set-card", "+view-rename", - "+record-list", "+record-get", "+record-upsert", "+record-upload-attachment", "+record-delete", + "+record-list", "+record-get", "+record-upsert", "+record-batch-add", "+record-batch-set", "+record-upload-attachment", "+record-delete", "+record-history-list", "+base-get", "+base-copy", "+base-create", "+role-create", "+role-delete", "+role-update", "+role-list", "+role-get", "+advperm-enable", "+advperm-disable", diff --git a/shortcuts/base/record_batch_add.go b/shortcuts/base/record_batch_add.go new file mode 100644 index 00000000..1c1fb03d --- /dev/null +++ b/shortcuts/base/record_batch_add.go @@ -0,0 +1,31 @@ +// Copyright (c) 2026 Lark Technologies Pte. Ltd. +// SPDX-License-Identifier: MIT + +package base + +import ( + "context" + + "github.com/larksuite/cli/shortcuts/common" +) + +var BaseRecordBatchAdd = common.Shortcut{ + Service: "base", + Command: "+record-batch-add", + Description: "Batch add records", + Risk: "write", + Scopes: []string{"base:record:create"}, + AuthTypes: authTypes(), + Flags: []common.Flag{ + baseTokenFlag(true), + tableRefFlag(true), + {Name: "json", Desc: "batch add JSON object, e.g. {\"fields\":[],\"rows\":[]}", Required: true}, + }, + Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { + return validateRecordJSON(runtime) + }, + DryRun: dryRunRecordBatchAdd, + Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { + return executeRecordBatchAdd(runtime) + }, +} diff --git a/shortcuts/base/record_batch_set.go b/shortcuts/base/record_batch_set.go new file mode 100644 index 00000000..19075cb8 --- /dev/null +++ b/shortcuts/base/record_batch_set.go @@ -0,0 +1,31 @@ +// Copyright (c) 2026 Lark Technologies Pte. Ltd. +// SPDX-License-Identifier: MIT + +package base + +import ( + "context" + + "github.com/larksuite/cli/shortcuts/common" +) + +var BaseRecordBatchSet = common.Shortcut{ + Service: "base", + Command: "+record-batch-set", + Description: "Batch set records", + Risk: "write", + Scopes: []string{"base:record:update"}, + AuthTypes: authTypes(), + Flags: []common.Flag{ + baseTokenFlag(true), + tableRefFlag(true), + {Name: "json", Desc: "batch set JSON object, passed through as request body, e.g. {\"patch\":{},\"filter\":{},\"offset\":0,\"limit\":200} or {\"records\":[],\"fields\":[],\"cells\":[]}", Required: true}, + }, + Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { + return validateRecordJSON(runtime) + }, + DryRun: dryRunRecordBatchSet, + Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { + return executeRecordBatchSet(runtime) + }, +} diff --git a/shortcuts/base/record_ops.go b/shortcuts/base/record_ops.go index 280b1c58..40b29cfe 100644 --- a/shortcuts/base/record_ops.go +++ b/shortcuts/base/record_ops.go @@ -51,6 +51,24 @@ func dryRunRecordUpsert(_ context.Context, runtime *common.RuntimeContext) *comm Set("table_id", baseTableID(runtime)) } +func dryRunRecordBatchAdd(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { + body, _ := parseJSONObject(runtime.Str("json"), "json") + return common.NewDryRunAPI(). + POST("/open-apis/base/v3/bases/:base_token/tables/:table_id/records/batch"). + Body(body). + Set("base_token", runtime.Str("base-token")). + Set("table_id", baseTableID(runtime)) +} + +func dryRunRecordBatchSet(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { + body, _ := parseJSONObject(runtime.Str("json"), "json") + return common.NewDryRunAPI(). + PATCH("/open-apis/base/v3/bases/:base_token/tables/:table_id/records/batch"). + Body(body). + Set("base_token", runtime.Str("base-token")). + Set("table_id", baseTableID(runtime)) +} + func dryRunRecordDelete(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). DELETE("/open-apis/base/v3/bases/:base_token/tables/:table_id/records/:record_id"). @@ -128,6 +146,34 @@ func executeRecordUpsert(runtime *common.RuntimeContext) error { return nil } +func executeRecordBatchAdd(runtime *common.RuntimeContext) error { + body, err := parseJSONObject(runtime.Str("json"), "json") + if err != nil { + return err + } + result, err := baseV3Raw(runtime, "POST", baseV3Path("bases", runtime.Str("base-token"), "tables", baseTableID(runtime), "records", "batch"), nil, body) + data, err := handleBaseAPIResult(result, err, "batch add records") + if err != nil { + return err + } + runtime.Out(data, nil) + return nil +} + +func executeRecordBatchSet(runtime *common.RuntimeContext) error { + body, err := parseJSONObject(runtime.Str("json"), "json") + if err != nil { + return err + } + result, err := baseV3Raw(runtime, "PATCH", baseV3Path("bases", runtime.Str("base-token"), "tables", baseTableID(runtime), "records", "batch"), nil, body) + data, err := handleBaseAPIResult(result, err, "batch set records") + if err != nil { + return err + } + runtime.Out(data, nil) + return nil +} + func executeRecordDelete(runtime *common.RuntimeContext) error { _, err := baseV3Call(runtime, "DELETE", baseV3Path("bases", runtime.Str("base-token"), "tables", baseTableID(runtime), "records", runtime.Str("record-id")), nil, nil) if err != nil { diff --git a/shortcuts/base/shortcuts.go b/shortcuts/base/shortcuts.go index fea4ebd7..43909059 100644 --- a/shortcuts/base/shortcuts.go +++ b/shortcuts/base/shortcuts.go @@ -37,6 +37,8 @@ func Shortcuts() []common.Shortcut { BaseRecordList, BaseRecordGet, BaseRecordUpsert, + BaseRecordBatchAdd, + BaseRecordBatchSet, BaseRecordUploadAttachment, BaseRecordDelete, BaseRecordHistoryList, diff --git a/skills/lark-base/SKILL.md b/skills/lark-base/SKILL.md index 80d81d3f..72bebe49 100644 --- a/skills/lark-base/SKILL.md +++ b/skills/lark-base/SKILL.md @@ -36,17 +36,17 @@ metadata: - 不要把 `+record-list` 当聚合分析引擎 - 不要没读 guide 就直接创建 formula / lookup 字段 - 不要凭自然语言猜表名、字段名、公式表达式里的字段引用 -- 不要把系统字段、formula 字段、lookup 字段当成 `+record-upsert` 的写入目标 +- 不要把系统字段、formula 字段、lookup 字段当成 `+record-upsert / +record-batch-add / +record-batch-set` 的写入目标 - 不要在 Base 场景改走 `lark-cli api GET /open-apis/bitable/v1/...` - 不要因为 wiki 解析结果里的 `obj_type=bitable` 就去找 `bitable.*`;在本 CLI 里应继续使用 `lark-cli base +...` ## Base 基本心智模型 1. **Base 字段分三类** - - **存储字段**:真实存用户输入的数据,通常适合 `+record-upsert` 写入,例如文本、数字、日期、单选、多选、人员、关联。**附件字段例外**:对 agent 而言,文件上传必须走 `+record-upload-attachment`。 + - **存储字段**:真实存用户输入的数据,通常适合 `+record-upsert / +record-batch-add / +record-batch-set` 写入,例如文本、数字、日期、单选、多选、人员、关联。**附件字段例外**:对 agent 而言,文件上传必须走 `+record-upload-attachment`。 - **系统字段**:平台自动维护,只读,典型包括创建时间、最后更新时间、创建人、修改人、自动编号。 - **计算字段**:通过表达式或跨表规则推导,只读,典型包括 **公式字段(formula)** 和 **查找引用字段(lookup)**。 -2. **写记录前先判断字段类别** — 只有存储字段可直接写;公式 / lookup / 创建时间 / 更新时间 / 创建人 / 修改人 / 自动编号都应视为只读输出字段,不能拿来做 `+record-upsert` 入参。 +2. **写记录前先判断字段类别** — 只有存储字段可直接写;公式 / lookup / 创建时间 / 更新时间 / 创建人 / 修改人 / 自动编号都应视为只读输出字段,不能拿来做 `+record-upsert / +record-batch-add / +record-batch-set` 入参。 3. **Base 不只是存表数据,也能内建计算** — 用户提出“统计、比较、排名、文本拼接、日期差、跨表汇总、状态判断”等需求时,不能默认导出数据后手算;要先判断是否应通过 `+data-query` 或公式字段在 Base 内完成。 ## 分析路径决策 @@ -101,7 +101,7 @@ metadata: ## 核心规则 -1. **只使用原子命令** — 使用 `+table-list / +table-get / +field-create / +record-upsert / +view-set-filter / +record-history-list / +base-get` 这类一命令一动作的写法,不使用旧聚合式 `+table / +field / +record / +view / +history / +workspace` +1. **只使用原子命令** — 使用 `+table-list / +table-get / +field-create / +record-upsert / +record-batch-add / +record-batch-set / +view-set-filter / +record-history-list / +base-get` 这类一命令一动作的写法,不使用旧聚合式 `+table / +field / +record / +view / +history / +workspace` 2. **写记录前先读字段结构** — 先调用 `+field-list` 获取字段结构,再读 [lark-base-shortcut-record-value.md](references/lark-base-shortcut-record-value.md) 确认各字段类型的写入值格式 3. **写字段前先看字段属性规范** — 先读 [lark-base-shortcut-field-properties.md](references/lark-base-shortcut-field-properties.md) 确认 `+field-create/+field-update` 的 JSON 结构 4. **筛选查询按视图能力执行** — 先读 [lark-base-view-set-filter.md](references/lark-base-view-set-filter.md) 和 [lark-base-record-list.md](references/lark-base-record-list.md),通过 `+view-set-filter` + `+record-list` 组合完成筛选读取 @@ -134,6 +134,8 @@ metadata: | 创建 / 更新 lookup 字段 | `lark-cli base +field-create` / `+field-update` | `type=lookup`;先读 lookup guide,再创建 / 更新,默认先判断 formula 是否更合适 | | 列表 / 获取记录 | `lark-cli base +record-list` / `+record-get` | 原子命令,如果需要`聚合计算`,`分组统计` 推荐走 `+data-query` | | 创建 / 更新记录 | `lark-cli base +record-upsert` | `--table-id [--record-id] --json` | +| 批量新增记录 | `lark-cli base +record-batch-add` | `--table-id --json`(`json.fields + json.rows`) | +| 按条件批量更新记录 | `lark-cli base +record-batch-set` | `--table-id --json`(`json.patch + json.filter + json.offset + json.limit`) | | 聚合分析 / 比较排序 / 求最值 / 筛选统计 | `lark-cli base +data-query` | 不要用 `+record-list` 拉全量数据再手动计算,需使用 `+data-query` 走服务端计算 | | 配置 / 查询视图 | `lark-cli base +view-*` | `list/get/create/delete/get-*/set-*/rename` | | 查看记录历史 | `lark-cli base +record-history-list` | 按表和记录查询变更历史 | @@ -267,6 +269,8 @@ https://{domain}/base/{base-token}?table={table-id}&view={view-id} - [lark-base-shortcut-field-properties.md](references/lark-base-shortcut-field-properties.md) — `+field-create/+field-update` JSON 规范(推荐) - [role-config.md](references/role-config.md) — 角色权限配置详解 - [lark-base-shortcut-record-value.md](references/lark-base-shortcut-record-value.md) — `+record-upsert` 值格式规范(推荐) +- [lark-base-record-batch-add.md](references/lark-base-record-batch-add.md) — `+record-batch-add` JSON 结构与 raw schema +- [lark-base-record-batch-set.md](references/lark-base-record-batch-set.md) — `+record-batch-set` JSON 结构与 raw schema - [formula-field-guide.md](references/formula-field-guide.md) — formula 字段写法、函数约束、CurrentValue 规则、跨表计算模式(强烈推荐) - [lookup-field-guide.md](references/lookup-field-guide.md) — lookup 字段配置规则、where/aggregate 约束、与 formula 的取舍 - [lark-base-view-set-filter.md](references/lark-base-view-set-filter.md) — 视图筛选配置 @@ -293,7 +297,7 @@ https://{domain}/base/{base-token}?table={table-id}&view={view-id} |----------|------| | [`table commands`](references/lark-base-table.md) | `+table-list / +table-get / +table-create / +table-update / +table-delete` | | [`field commands`](references/lark-base-field.md) | `+field-list / +field-get / +field-create / +field-update / +field-delete / +field-search-options` | -| [`record commands`](references/lark-base-record.md) | `+record-list / +record-get / +record-upsert / +record-upload-attachment / +record-delete` | +| [`record commands`](references/lark-base-record.md) | `+record-list / +record-get / +record-upsert / +record-batch-add / +record-batch-set / +record-upload-attachment / +record-delete` | | [`view commands`](references/lark-base-view.md) | `+view-list / +view-get / +view-create / +view-delete / +view-get-* / +view-set-* / +view-rename` | | [`data-query commands`](references/lark-base-data-query.md) | `+data-query` | | [`history commands`](references/lark-base-history.md) | `+record-history-list` | diff --git a/skills/lark-base/references/lark-base-record-batch-add.md b/skills/lark-base/references/lark-base-record-batch-add.md new file mode 100644 index 00000000..30394e4f --- /dev/null +++ b/skills/lark-base/references/lark-base-record-batch-add.md @@ -0,0 +1,55 @@ +# base +record-batch-add + +> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。 + +批量新增记录。 + +## 推荐命令 + +```bash +lark-cli base +record-batch-add \ + --base-token app_xxx \ + --table-id tbl_xxx \ + --json '{"fields":["标题","状态"],"rows":[["任务 A","Open"],["任务 B","Done"]]}' +``` + +## 参数 + +| 参数 | 必填 | 说明 | +|------|------|------| +| `--base-token ` | 是 | Base Token | +| `--table-id ` | 是 | 表 ID 或表名 | +| `--json ` | 是 | 批量新增请求体,必须是 JSON 对象 | + +## API 入参详情 + +**HTTP 方法和路径:** + +``` +POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records/batch +``` + +## `--json` Raw JSON Schema + +```json +{"type":"object","properties":{"fields":{"type":"array","items":{"type":"string","minLength":1,"maxLength":100,"description":"Field id or name"},"minItems":1,"maxItems":200},"rows":{"type":"array","items":{"type":"array","items":{"anyOf":[{"anyOf":[{"type":"string","description":"text field cell, example: \"one string and [one url](https://foo.bar)\""},{"type":"number","description":"number field cell, can be any float64 value"},{"type":"array","items":{"type":"string","description":"option name"},"description":"select field cell, example: [\"option_1\", \"option_2\"]"},{"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\""},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"record id"}},"required":["id"],"additionalProperties":false},"description":"link field cell, example: [{\"id\": \"rec_123\"}]"},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"user id"}},"required":["id"],"additionalProperties":false},"description":"user field cell, example: [{\"id\": \"ou_123\"}]"},{"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}"},{"type":"boolean","description":"checkbox field cell"},{"type":"array","items":{"type":"object","properties":{"file_token":{"type":"string","minLength":0,"maxLength":50},"name":{"type":"string","minLength":1,"maxLength":255},"mime_type":{"type":"string","maxLength":255,"description":"deprecated field"},"size":{"type":"integer","minimum":0,"description":"deprecated field"},"image_width":{"type":"integer","minimum":0,"description":"deprecated field"},"image_height":{"type":"integer","minimum":0,"description":"deprecated field"},"deprecated_set_attachment":{"type":"boolean","description":"deprecated field"}},"required":["file_token","name"],"additionalProperties":false},"description":"attachment field cell. temporary compatibility for attachment writes."},{"type":"null"}]},{"type":"null"}]}},"minItems":1,"maxItems":200}},"required":["fields","rows"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"} +``` + +## 返回重点 + +- 返回对象键: + - `fields` + - `field_id_list` + - `record_id_list` + - `data` + - `ignored_fields`(可选) + +## 坑点 + +- ⚠️ `--json` 必须是对象。 +- ⚠️ `fields` 与 `rows` 列顺序必须一一对应。 + +## 参考 + +- [lark-base-record.md](lark-base-record.md) — record 索引页 +- [lark-base-shortcut-record-value.md](lark-base-shortcut-record-value.md) — 记录值格式规范 diff --git a/skills/lark-base/references/lark-base-record-batch-set.md b/skills/lark-base/references/lark-base-record-batch-set.md new file mode 100644 index 00000000..5a1874a5 --- /dev/null +++ b/skills/lark-base/references/lark-base-record-batch-set.md @@ -0,0 +1,62 @@ +# base +record-batch-set + +> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。 + +批量更新记录。 + +## 推荐命令 + +```bash +lark-cli base +record-batch-set \ + --base-token app_xxx \ + --table-id tbl_xxx \ + --json '{"patch":{"状态":"Done"},"filter":{"logic":"and","conditions":[["状态","==","Open"]]},"offset":0,"limit":200}' +``` + +```bash +lark-cli base +record-batch-set \ + --base-token app_xxx \ + --table-id tbl_xxx \ + --json '{"records":["rec_xxx"],"fields":["fld_xxx","fld_yyy"],"cells":[["xx","yy"]]}' +``` + +## 参数 + +| 参数 | 必填 | 说明 | +|------|------|------| +| `--base-token ` | 是 | Base Token | +| `--table-id ` | 是 | 表 ID 或表名 | +| `--json ` | 是 | 批量更新请求体,必须是 JSON 对象;CLI 不改写内容,直接发送给 API | + +## API 入参详情 + +**HTTP 方法和路径:** + +``` +PATCH /open-apis/base/v3/bases/:base_token/tables/:table_id/records/batch +``` + +## `--json` Raw JSON Schema + +```json +{"type":"object","properties":{"filter":{"type":"object","properties":{"logic":{"type":"string","enum":["and","or"],"default":"and","description":"Filter Condition Logic"},"conditions":{"type":"array","items":{"type":"array","minItems":3,"maxItems":3,"items":[{"type":"string","minLength":1,"maxLength":100,"description":"Field id or name"},{"type":"string","enum":["==","!=",">",">=","<","<=","intersects","disjoint","empty","non_empty"],"description":"Condition operator"},{"anyOf":[{"not":{}},{"anyOf":[{"anyOf":[{"type":"string","description":"text & formula & location field support string as filter value"},{"type":"number","description":"number & auto_number(the underfly incremental_number) field support number as filter value"},{"type":"array","items":{"type":"string","description":"option name"},"description":"select field support one option: [\"option1\"] or multiple options: `[\"option1\", \"option2\"]` as filter value."},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"record id"}},"required":["id"],"additionalProperties":false},"description":"link field support record id list as filter value"},{"type":"string","description":"\ndatetime & create_at & updated_at field support relative and absolute filter value.\nabsolute:\n- \"ExactDate(yyyy-MM-dd)\"\nrelative:\n- Today\n- Tomorrow\n- Yesterday\n"},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"user id"}},"required":["id"],"additionalProperties":false},"description":"user field support user id list as filter value"},{"type":"boolean","description":"checkbox field support boolean as filter value"}]},{"type":"null"}]}]}],"description":"one condition expression. shape: [field_id, filter_operator, value]. when operator is \"empty\" or \"non_empty\", the value is not required."},"default":[]}},"additionalProperties":false},"offset":{"type":"integer","minimum":0,"default":0},"limit":{"type":"integer","minimum":1,"maximum":200},"patch":{"type":"object","additionalProperties":{"anyOf":[{"anyOf":[{"type":"string","description":"text field cell, example: \"one string and [one url](https://foo.bar)\""},{"type":"number","description":"number field cell, can be any float64 value"},{"type":"array","items":{"type":"string","description":"option name"},"description":"select field cell, example: [\"option_1\", \"option_2\"]"},{"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\""},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"record id"}},"required":["id"],"additionalProperties":false},"description":"link field cell, example: [{\"id\": \"rec_123\"}]"},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"user id"}},"required":["id"],"additionalProperties":false},"description":"user field cell, example: [{\"id\": \"ou_123\"}]"},{"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}"},{"type":"boolean","description":"checkbox field cell"},{"type":"array","items":{"type":"object","properties":{"file_token":{"type":"string","minLength":0,"maxLength":50},"name":{"type":"string","minLength":1,"maxLength":255},"mime_type":{"type":"string","maxLength":255,"description":"deprecated field"},"size":{"type":"integer","minimum":0,"description":"deprecated field"},"image_width":{"type":"integer","minimum":0,"description":"deprecated field"},"image_height":{"type":"integer","minimum":0,"description":"deprecated field"},"deprecated_set_attachment":{"type":"boolean","description":"deprecated field"}},"required":["file_token","name"],"additionalProperties":false},"description":"attachment field cell. temporary compatibility for attachment writes."},{"type":"null"}]},{"type":"null"}]},"propertyNames":{"minLength":1,"maxLength":100}}},"required":["filter","limit","patch"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"} +``` + +## 返回重点 + +- 返回对象键: + - `has_more` + - `record_id_list` + - `update` + - `ignored_fields`(可选) + +## 坑点 + +- ⚠️ `--json` 必须是对象。 +- ⚠️ `limit` 超过 `200` 会被 OpenAPI 拒绝。 +- ⚠️ 命令不会自动做字段/行映射转换,传什么就发什么。 + +## 参考 + +- [lark-base-record.md](lark-base-record.md) — record 索引页 +- [lark-base-view-set-filter.md](lark-base-view-set-filter.md) — 视图筛选配置参考 diff --git a/skills/lark-base/references/lark-base-record.md b/skills/lark-base/references/lark-base-record.md index 15b29393..85c3295e 100644 --- a/skills/lark-base/references/lark-base-record.md +++ b/skills/lark-base/references/lark-base-record.md @@ -11,6 +11,8 @@ record 相关命令索引。 | [lark-base-record-list.md](lark-base-record-list.md) | `+record-list` | 分页列记录 | | [lark-base-record-get.md](lark-base-record-get.md) | `+record-get` | 获取单条记录 | | [lark-base-record-upsert.md](lark-base-record-upsert.md) | `+record-upsert` | 创建或更新记录 | +| [lark-base-record-batch-add.md](lark-base-record-batch-add.md) | `+record-batch-add` | 按 `fields/rows` 批量新增记录 | +| [lark-base-record-batch-set.md](lark-base-record-batch-set.md) | `+record-batch-set` | 批量更新记录 | | [lark-base-record-upload-attachment.md](lark-base-record-upload-attachment.md) | `+record-upload-attachment` | 上传本地文件到附件字段并更新记录 | | [lark-base-record-delete.md](lark-base-record-delete.md) | `+record-delete` | 删除记录 |