diff --git a/shortcuts/base/base_shortcuts_test.go b/shortcuts/base/base_shortcuts_test.go index ab711a59..6e13c986 100644 --- a/shortcuts/base/base_shortcuts_test.go +++ b/shortcuts/base/base_shortcuts_test.go @@ -132,7 +132,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-visible-fields", "+view-set-visible-fields", "+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-search", "+record-get", "+record-upsert", "+record-batch-create", "+record-batch-update", "+record-upload-attachment", "+record-delete", + "+record-list", "+record-search", "+record-get", "+record-upsert", "+record-batch-create", "+record-batch-update", "+record-share-link-create", "+record-share-link-batch-create", "+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_ops.go b/shortcuts/base/record_ops.go index f91ca579..26b6a40a 100644 --- a/shortcuts/base/record_ops.go +++ b/shortcuts/base/record_ops.go @@ -112,6 +112,73 @@ func dryRunRecordHistoryList(_ context.Context, runtime *common.RuntimeContext) Set("base_token", runtime.Str("base-token")) } +func dryRunRecordShareBatch(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { + recordIDs := deduplicateRecordIDs(runtime) + return common.NewDryRunAPI(). + POST("/open-apis/base/v3/bases/:base_token/tables/:table_id/records/share_links/batch"). + Body(map[string]interface{}{"records": recordIDs}). + Set("base_token", runtime.Str("base-token")). + Set("table_id", baseTableID(runtime)) +} + +func dryRunRecordShare(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { + return common.NewDryRunAPI(). + POST("/open-apis/base/v3/bases/:base_token/tables/:table_id/records/:record_id/share_links"). + Set("base_token", runtime.Str("base-token")). + Set("table_id", baseTableID(runtime)). + Set("record_id", runtime.Str("record-id")) +} + +func validateRecordShareBatch(runtime *common.RuntimeContext) error { + recordIDs := runtime.StrSlice("record-ids") + if len(recordIDs) == 0 { + return common.FlagErrorf("--record-ids is required and must not be empty") + } + if len(recordIDs) > maxShareBatchSize { + return common.FlagErrorf("--record-ids exceeds maximum limit of %d (got %d)", maxShareBatchSize, len(recordIDs)) + } + return nil +} + +func deduplicateRecordIDs(runtime *common.RuntimeContext) []string { + raw := runtime.StrSlice("record-ids") + seen := make(map[string]bool, len(raw)) + result := make([]string, 0, len(raw)) + for _, id := range raw { + if id != "" && !seen[id] { + seen[id] = true + result = append(result, id) + } + } + return result +} + +func executeRecordShareBatch(runtime *common.RuntimeContext) error { + recordIDs := deduplicateRecordIDs(runtime) + body := map[string]interface{}{ + "records": recordIDs, + } + data, err := baseV3Call(runtime, "POST", + baseV3Path("bases", runtime.Str("base-token"), "tables", baseTableID(runtime), "records", "share_links", "batch"), + nil, body) + if err != nil { + return err + } + runtime.Out(data, nil) + return nil +} + +func executeRecordShare(runtime *common.RuntimeContext) error { + data, err := baseV3Call(runtime, "POST", + baseV3Path("bases", runtime.Str("base-token"), "tables", baseTableID(runtime), "records", runtime.Str("record-id"), "share_links"), + nil, nil) + if err != nil { + return err + } + runtime.Out(data, nil) + return nil +} + func validateRecordJSON(runtime *common.RuntimeContext) error { return nil } diff --git a/shortcuts/base/record_share_link_batch_create.go b/shortcuts/base/record_share_link_batch_create.go new file mode 100644 index 00000000..717d9d53 --- /dev/null +++ b/shortcuts/base/record_share_link_batch_create.go @@ -0,0 +1,36 @@ +// Copyright (c) 2026 Lark Technologies Pte. Ltd. +// SPDX-License-Identifier: MIT + +package base + +import ( + "context" + + "github.com/larksuite/cli/shortcuts/common" +) + +const maxShareBatchSize = 100 + +var BaseRecordShareLinkBatchCreate = common.Shortcut{ + Service: "base", + Command: "+record-share-link-batch-create", + Description: "Batch generate record share links (max 100 records per request)", + Risk: "read", + Scopes: []string{"base:record:read"}, + AuthTypes: authTypes(), + Flags: []common.Flag{ + baseTokenFlag(true), + tableRefFlag(true), + {Name: "record-ids", Type: "string_slice", Desc: "record IDs to generate share links for (comma-separated or repeatable, max 100)", Required: true}, + }, + Tips: []string{ + `Example: --base-token xxx --table-id tblxxx --record-ids rec001,rec002,rec003`, + }, + Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { + return validateRecordShareBatch(runtime) + }, + DryRun: dryRunRecordShareBatch, + Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { + return executeRecordShareBatch(runtime) + }, +} diff --git a/shortcuts/base/record_share_link_create.go b/shortcuts/base/record_share_link_create.go new file mode 100644 index 00000000..4e3adc3e --- /dev/null +++ b/shortcuts/base/record_share_link_create.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 BaseRecordShareLinkCreate = common.Shortcut{ + Service: "base", + Command: "+record-share-link-create", + Description: "Generate a share link for a single record", + Risk: "read", + Scopes: []string{"base:record:read"}, + AuthTypes: authTypes(), + Flags: []common.Flag{ + baseTokenFlag(true), + tableRefFlag(true), + recordRefFlag(true), + }, + Tips: []string{ + `Example: --base-token xxx --table-id tblxxx --record-id recxxx`, + }, + DryRun: dryRunRecordShare, + Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { + return executeRecordShare(runtime) + }, +} diff --git a/shortcuts/base/shortcuts.go b/shortcuts/base/shortcuts.go index 32ca8406..491137aa 100644 --- a/shortcuts/base/shortcuts.go +++ b/shortcuts/base/shortcuts.go @@ -42,6 +42,8 @@ func Shortcuts() []common.Shortcut { BaseRecordUpsert, BaseRecordBatchCreate, BaseRecordBatchUpdate, + BaseRecordShareLinkCreate, + BaseRecordShareLinkBatchCreate, BaseRecordUploadAttachment, BaseRecordDelete, BaseRecordHistoryList, diff --git a/shortcuts/common/runner.go b/shortcuts/common/runner.go index 1ddb69e5..ae957418 100644 --- a/shortcuts/common/runner.go +++ b/shortcuts/common/runner.go @@ -181,6 +181,12 @@ func (ctx *RuntimeContext) StrArray(name string) []string { return v } +// StrSlice returns a string-slice flag value (supports CSV splitting and repeated flags). +func (ctx *RuntimeContext) StrSlice(name string) []string { + v, _ := ctx.Cmd.Flags().GetStringSlice(name) + return v +} + // ── API helpers ── // CallAPI uses an internal HTTP wrapper with limited control over request/response. @@ -849,6 +855,8 @@ func registerShortcutFlags(cmd *cobra.Command, s *Shortcut) { cmd.Flags().Int(fl.Name, d, desc) case "string_array": cmd.Flags().StringArray(fl.Name, nil, desc) + case "string_slice": + cmd.Flags().StringSlice(fl.Name, nil, desc) default: cmd.Flags().String(fl.Name, fl.Default, desc) } diff --git a/skills/lark-base/references/lark-base-record-share-link-batch-create.md b/skills/lark-base/references/lark-base-record-share-link-batch-create.md new file mode 100644 index 00000000..a9f66225 --- /dev/null +++ b/skills/lark-base/references/lark-base-record-share-link-batch-create.md @@ -0,0 +1,71 @@ +# base +record-share-link-batch-create + +> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。 + +批量生成记录分享链接(单次调用最多传入 100 条)。 + +## 适用场景(重点) + +- 适合需要一次性获取多条记录分享链接的场景,例如批量导出分享链接、批量发送通知等。 +- 当需要处理的记录数超过 100 条时,需分批调用。 + +## 推荐命令 + +```bash +# 请使用“,”来分隔多个 record id +lark-cli base +record-share-link-batch-create \ + --base-token xxx \ + --table-id tbl_xxx \ + --record-ids rec001,rec002,rec003 +``` + +## 参数 + +| 参数 | 必填 | 说明 | +|------|------|----| +| `--base-token ` | 是 | Base Token | +| `--table-id ` | 是 | 表 ID | +| `--record-ids ` | 是 | 记录 ID 列表,需要使用逗号分隔,最多 100 条 | + +## API 入参详情 + +**HTTP 方法和路径:** + +```http +POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records/share_links/batch +``` + +**请求体:** + +```json +{ + "records": ["rec001", "rec002", "rec003"] +} +``` + +> CLI 会自动对 `--record-ids` 去重后再调用接口。 + +## 返回重点 + +- 成功时直接返回接口 `data` 字段内容,包含 `record_share_links` 映射(key 为 record_id,value 为分享链接)。结构如下: + +```json +{ + "record_share_links": { + "rec001": "https://example.feishu.cn/record/TW2wrdbkoeoYXYcwvyIczJ2ZnFb", + "rec002": "https://example.feishu.cn/record/aB3xKmNpQrStUvWxYz123456789", + "rec003": "https://example.feishu.cn/record/cD4yLmNoPqRsTuVwXz987654321" + } +} +``` + +## 坑点 + +- ⚠️ 单次最多 100 条记录,超出会被 CLI 校验拦截。 +- ⚠️ 重复的 record_id 会在调用前自动去重。 +- ⚠️ `--record-ids` 为空时会被校验拦截。 + +## 参考 + +- [lark-base-record.md](lark-base-record.md) — record 索引页 +- [lark-base-record-share-link-create.md](lark-base-record-share-link-create.md) — 单条生成分享链接 diff --git a/skills/lark-base/references/lark-base-record-share-link-create.md b/skills/lark-base/references/lark-base-record-share-link-create.md new file mode 100644 index 00000000..a1c6ff3e --- /dev/null +++ b/skills/lark-base/references/lark-base-record-share-link-create.md @@ -0,0 +1,44 @@ +# base +record-share-link-create + +> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。 + +为单条记录生成分享链接。 + +## 推荐命令 + +```bash +lark-cli base +record-share-link-create \ + --base-token xxx \ + --table-id tbl_xxx \ + --record-id rec_xxx +``` + +## 参数 + +| 参数 | 必填 | 说明 | +|------|------|------| +| `--base-token ` | 是 | Base Token | +| `--table-id ` | 是 | 表 ID | +| `--record-id ` | 是 | 记录 ID | + +## API 入参详情 + +**HTTP 方法和路径:** + +``` +POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records/:record_id/share_links +``` + +## 返回重点 + +- 成功时直接返回接口 `data` 字段内容,包含该记录的分享链接信息。结构如下: +```json +{ + "record_share_link": "https://example.feishu.cn/record/TW2wrdbkoeoYXYcwvyIczJ2ZnFb" +} +``` + +## 参考 + +- [lark-base-record.md](lark-base-record.md) — record 索引页 +- [lark-base-record-share-link-batch-create.md](lark-base-record-share-link-batch-create.md) — 批量生成分享链接 diff --git a/skills/lark-base/references/lark-base-record.md b/skills/lark-base/references/lark-base-record.md index 078f8499..edd57e50 100644 --- a/skills/lark-base/references/lark-base-record.md +++ b/skills/lark-base/references/lark-base-record.md @@ -14,6 +14,8 @@ record 相关命令索引。 | [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-share-link-create.md](lark-base-record-share-link-create.md) | `+record-share-link-create` | 为单条记录生成分享链接 | +| [lark-base-record-share-link-batch-create.md](lark-base-record-share-link-batch-create.md) | `+record-share-link-batch-create` | 批量生成记录分享链接(最多 100 条) | | [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` | 删除记录 |