Files

413 lines
17 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package helpers
import (
"fmt"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
"github.com/spf13/cobra"
)
func newFloatImageCmds() []*cobra.Command {
createFloatImageCmd := &cobra.Command{
Use: "create-float-image",
Short: "创建浮动图片",
Long: `在钉钉表格的指定工作表上创建一个浮动图片。
浮动图片悬浮于单元格之上,不占用单元格内容,可自由定位和调整大小。
直接传 --file 可在一个命令中上传本地图片并创建浮动图片。
已经通过 media-upload 获得 resourceUrl 时,也可以改传 --src;--file 与 --src 二选一。
--range 指定浮动图片锚定的单元格位置,使用 A1 表示法(如 A1、B3)。
--width / --height 为必填,单位像素,必须为正整数。
--offset-x / --offset-y 可选,表示相对锚点单元格左上角的偏移量(像素),默认 0。`,
Example: ` # 直接从本地文件创建
dws sheet create-float-image --node NODE_ID --sheet-id SHEET_ID \
--file ./chart.png --range A1 --width 400 --height 300
# 高级用法:先上传图片获取 resourceUrl
dws sheet media-upload --node NODE_ID --file ./chart.png
# 输出: resourceUrl: /core/api/resources/img/xxxx...
# 再通过 --src 创建浮动图片
dws sheet create-float-image --node NODE_ID --sheet-id SHEET_ID \
--src "/core/api/resources/img/xxxx..." --range A1 --width 400 --height 300
# 带偏移量
dws sheet create-float-image --node NODE_ID --sheet-id SHEET_ID \
--src "/core/api/resources/img/xxxx..." --range B2 --width 200 --height 150 --offset-x 10 --offset-y 20`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := validateRequiredFlags(cmd, "node", "sheet-id", "range"); err != nil {
return err
}
filePath, src, err := validateFloatImageCreateInput(cmd)
if err != nil {
return err
}
width, err := cmd.Flags().GetInt("width")
if err != nil {
return fmt.Errorf("--width 解析失败: %w", err)
}
if width <= 0 {
return fmt.Errorf("--width 必须为正整数,当前值: %d", width)
}
height, err := cmd.Flags().GetInt("height")
if err != nil {
return fmt.Errorf("--height 解析失败: %w", err)
}
if height <= 0 {
return fmt.Errorf("--height 必须为正整数,当前值: %d", height)
}
toolArgs := map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
"sheetId": mustGetFlag(cmd, "sheet-id"),
"range": mustGetFlag(cmd, "range"),
"width": width,
"height": height,
}
if cmd.Flags().Changed("offset-x") {
ox, _ := cmd.Flags().GetInt("offset-x")
if ox < 0 {
return fmt.Errorf("--offset-x 不能为负数,当前值: %d", ox)
}
toolArgs["offsetX"] = ox
}
if cmd.Flags().Changed("offset-y") {
oy, _ := cmd.Flags().GetInt("offset-y")
if oy < 0 {
return fmt.Errorf("--offset-y 不能为负数,当前值: %d", oy)
}
toolArgs["offsetY"] = oy
}
if filePath != "" {
return runFloatImageFileMode(cmd, "create_float_image", filePath, toolArgs)
}
toolArgs["src"] = src
return callMCPToolContext(cmd.Context(), "create_float_image", toolArgs)
},
}
DeclareLeafMetadata(createFloatImageCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "not_required", Idempotency: "unknown",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "create_float_image",
CanonicalPath: "sheet.create_float_image",
CLIPath: "sheet create-float-image",
PrimaryCLIPath: "sheet create-float-image",
},
Description: "从本地文件或已上传的 resourceUrl 创建浮动图片。",
DryRun: &contract.DryRunSpec{PreviewKind: "request", RemoteReads: false},
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "create_float_image"},
},
Selection: contract.SelectionSpec{
AgentSummary: "从本地文件或已上传的 resourceUrl 创建浮动图片。",
UseWhen: []string{"需要在单元格上方悬浮图片、不占用单元格内容时"},
AvoidWhen: []string{"单元格内嵌图片用 write-image;更新/删除浮动图用 update/delete-float-image"},
Examples: []string{"dws sheet create-float-image --node <NODE_ID> --sheet-id <SHEET_ID> --src \"/core/api/resources/img/...\" --range A1 --width 400 --height 300"},
},
Parameters: []contract.ParamDecl{
{Name: "node", Property: "nodeId"},
{Name: "file", Required: boolPtr(false), RequiredWhen: "exactly one of --file or --src must be provided"},
{Name: "src", Property: "src", Required: boolPtr(false)},
},
},
})
createFloatImageCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
createFloatImageCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
createFloatImageCmd.Flags().String("file", "", "本地图片文件路径,与 --src 二选一")
createFloatImageCmd.Flags().String("src", "", "通过 media-upload 获取的 resourceUrl,与 --file 二选一")
createFloatImageCmd.Flags().String("range", "", "锚点单元格,A1 表示法,如 A1、B3 (必填)")
createFloatImageCmd.Flags().Int("width", 0, "图片宽度,像素 (必填)")
createFloatImageCmd.Flags().Int("height", 0, "图片高度,像素 (必填)")
createFloatImageCmd.Flags().Int("offset-x", 0, "水平偏移量,像素 (默认 0)")
createFloatImageCmd.Flags().Int("offset-y", 0, "垂直偏移量,像素 (默认 0)")
getFloatImageCmd := &cobra.Command{
Use: "get-float-image",
Short: "获取浮动图片详情",
Long: `获取钉钉表格指定工作表中某个浮动图片的详细信息。
返回浮动图片的 ID、图片 URL、锚点位置、尺寸和偏移量等信息。
floatImageId 可通过 list-float-images 获取。`,
Example: ` dws sheet get-float-image --node NODE_ID --sheet-id SHEET_ID --float-image-id FI_ID`,
RunE: func(cmd *cobra.Command, args []string) error {
return callMCPTool("get_float_image", map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
"sheetId": mustGetFlag(cmd, "sheet-id"),
"floatImageId": mustGetFlag(cmd, "float-image-id"),
})
},
}
DeclareLeafMetadata(getFloatImageCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "read", Risk: "low",
Confirmation: "not_required", Idempotency: "idempotent",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "get_float_image",
CanonicalPath: "sheet.get_float_image",
CLIPath: "sheet get-float-image",
PrimaryCLIPath: "sheet get-float-image",
},
Description: "获取单张浮动图片详情。",
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "get_float_image"},
},
Selection: contract.SelectionSpec{
AgentSummary: "获取单张浮动图片详情。",
UseWhen: []string{"已知 float-image-id,需要查看锚点/尺寸/src 时"},
AvoidWhen: []string{"列出全部浮动图用 list-float-images"},
Examples: []string{"dws sheet get-float-image --node <NODE_ID> --sheet-id <SHEET_ID> --float-image-id <FI_ID>"},
},
Parameters: []contract.ParamDecl{
{Name: "node", Property: "nodeId"},
},
},
})
getFloatImageCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
getFloatImageCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
getFloatImageCmd.Flags().String("float-image-id", "", "浮动图片 ID (必填)")
listFloatImagesCmd := &cobra.Command{
Use: "list-float-images",
Short: "列出工作表所有浮动图片",
Long: `列出钉钉表格指定工作表中所有浮动图片。
返回每个浮动图片的 ID、图片 URL、锚点位置、尺寸和偏移量等信息,以及总数。`,
Example: ` dws sheet list-float-images --node NODE_ID --sheet-id SHEET_ID`,
RunE: func(cmd *cobra.Command, args []string) error {
return callMCPTool("list_float_images", map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
"sheetId": mustGetFlag(cmd, "sheet-id"),
})
},
}
DeclareLeafMetadata(listFloatImagesCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "read", Risk: "low",
Confirmation: "not_required", Idempotency: "idempotent",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "list_float_images",
CanonicalPath: "sheet.list_float_images",
CLIPath: "sheet list-float-images",
PrimaryCLIPath: "sheet list-float-images",
},
Description: "列出工作表全部浮动图片。",
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "list_float_images"},
},
Selection: contract.SelectionSpec{
AgentSummary: "列出工作表全部浮动图片。",
UseWhen: []string{"需要枚举浮动图 ID 以便后续 get/update/delete 时"},
AvoidWhen: []string{"查单张详情用 get-float-image"},
Examples: []string{"dws sheet list-float-images --node <NODE_ID> --sheet-id <SHEET_ID>"},
},
Parameters: []contract.ParamDecl{
{Name: "node", Property: "nodeId"},
},
},
})
listFloatImagesCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
listFloatImagesCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
updateFloatImageCmd := &cobra.Command{
Use: "update-float-image",
Short: "更新浮动图片属性",
Long: `更新钉钉表格指定工作表中浮动图片的属性。
可直接传 --file 上传本地图片并替换浮动图片,也可传已上传图片的 --src。
至少需要传入一个更新字段(--file / --src / --range / --width / --height / --offset-x / --offset-y),且 --file 与 --src 不能同时使用。
floatImageId 可通过 list-float-images 获取。`,
Example: ` # 移动浮动图片到新位置
dws sheet update-float-image --node NODE_ID --sheet-id SHEET_ID --float-image-id FI_ID --range C5
# 调整尺寸
dws sheet update-float-image --node NODE_ID --sheet-id SHEET_ID --float-image-id FI_ID --width 600 --height 400
# 直接用本地文件替换图片
dws sheet update-float-image --node NODE_ID --sheet-id SHEET_ID --float-image-id FI_ID \
--file ./replacement.png
# 高级用法:用 media-upload 返回的 resourceUrl 替换图片
dws sheet update-float-image --node NODE_ID --sheet-id SHEET_ID --float-image-id FI_ID \
--src "/core/api/resources/img/xxxx..."`,
RunE: func(cmd *cobra.Command, args []string) error {
fileChanged := cmd.Flags().Changed("file")
srcChanged := cmd.Flags().Changed("src")
rangeChanged := cmd.Flags().Changed("range")
widthChanged := cmd.Flags().Changed("width")
heightChanged := cmd.Flags().Changed("height")
oxChanged := cmd.Flags().Changed("offset-x")
oyChanged := cmd.Flags().Changed("offset-y")
if !fileChanged && !srcChanged && !rangeChanged && !widthChanged && !heightChanged && !oxChanged && !oyChanged {
return fmt.Errorf("%s", floatImageUpdateFieldsError)
}
filePath, src, err := validateFloatImageUpdateInput(cmd)
if err != nil {
return err
}
if fileChanged {
if err := validateRequiredFlags(cmd, "node", "sheet-id", "float-image-id"); err != nil {
return err
}
}
toolArgs := map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
"sheetId": mustGetFlag(cmd, "sheet-id"),
"floatImageId": mustGetFlag(cmd, "float-image-id"),
}
if srcChanged {
toolArgs["src"] = src
}
if rangeChanged {
toolArgs["range"], _ = cmd.Flags().GetString("range")
}
if widthChanged {
w, _ := cmd.Flags().GetInt("width")
if w <= 0 {
return fmt.Errorf("--width 必须为正整数,当前值: %d", w)
}
toolArgs["width"] = w
}
if heightChanged {
h, _ := cmd.Flags().GetInt("height")
if h <= 0 {
return fmt.Errorf("--height 必须为正整数,当前值: %d", h)
}
toolArgs["height"] = h
}
if oxChanged {
ox, _ := cmd.Flags().GetInt("offset-x")
if ox < 0 {
return fmt.Errorf("--offset-x 不能为负数,当前值: %d", ox)
}
toolArgs["offsetX"] = ox
}
if oyChanged {
oy, _ := cmd.Flags().GetInt("offset-y")
if oy < 0 {
return fmt.Errorf("--offset-y 不能为负数,当前值: %d", oy)
}
toolArgs["offsetY"] = oy
}
if fileChanged {
return runFloatImageFileMode(cmd, "update_float_image", filePath, toolArgs)
}
return callMCPToolContext(cmd.Context(), "update_float_image", toolArgs)
},
}
DeclareLeafMetadata(updateFloatImageCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "not_required", Idempotency: "unknown",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "update_float_image",
CanonicalPath: "sheet.update_float_image",
CLIPath: "sheet update-float-image",
PrimaryCLIPath: "sheet update-float-image",
},
Description: "更新浮动图片锚点、尺寸、偏移或资源路径。",
DryRun: &contract.DryRunSpec{PreviewKind: "request", RemoteReads: false},
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "update_float_image"},
},
Selection: contract.SelectionSpec{
AgentSummary: "更新浮动图片锚点、尺寸、偏移或资源路径。",
UseWhen: []string{"需要移动/缩放/替换已有浮动图片时"},
AvoidWhen: []string{"创建用 create-float-image;删除用 delete-float-image"},
Examples: []string{"dws sheet update-float-image --node <NODE_ID> --sheet-id <SHEET_ID> --float-image-id <FI_ID> --range C5"},
},
Parameters: []contract.ParamDecl{
{Name: "node", Property: "nodeId"},
{Name: "file", Required: boolPtr(false)},
{Name: "src", Property: "src", Required: boolPtr(false)},
},
},
})
updateFloatImageCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
updateFloatImageCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
updateFloatImageCmd.Flags().String("float-image-id", "", "浮动图片 ID (必填)")
updateFloatImageCmd.Flags().String("file", "", "用于替换浮动图片的本地图片路径,与 --src 不能同时使用")
updateFloatImageCmd.Flags().String("src", "", "新的图片资源路径,通过 media-upload 获取的 resourceUrl")
updateFloatImageCmd.Flags().String("range", "", "新的锚点单元格,A1 表示法")
updateFloatImageCmd.Flags().Int("width", 0, "新的图片宽度,像素")
updateFloatImageCmd.Flags().Int("height", 0, "新的图片高度,像素")
updateFloatImageCmd.Flags().Int("offset-x", 0, "新的水平偏移量,像素")
updateFloatImageCmd.Flags().Int("offset-y", 0, "新的垂直偏移量,像素")
deleteFloatImageCmd := &cobra.Command{
Use: "delete-float-image",
Short: "删除浮动图片",
Long: `删除钉钉表格指定工作表中的浮动图片。
操作不可恢复,删除后图片将从工作表中移除。
floatImageId 可通过 list-float-images 获取。`,
Example: ` dws sheet delete-float-image --node NODE_ID --sheet-id SHEET_ID --float-image-id FI_ID --yes`,
RunE: func(cmd *cobra.Command, args []string) error {
return callMCPTool("delete_float_image", map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
"sheetId": mustGetFlag(cmd, "sheet-id"),
"floatImageId": mustGetFlag(cmd, "float-image-id"),
})
},
}
DeclareLeafMetadata(deleteFloatImageCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "user_required", Idempotency: "unknown",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "delete_float_image",
CanonicalPath: "sheet.delete_float_image",
CLIPath: "sheet delete-float-image",
PrimaryCLIPath: "sheet delete-float-image",
},
Description: "删除浮动图片(需确认后加 --yes)。",
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "delete_float_image"},
},
Selection: contract.SelectionSpec{
AgentSummary: "删除浮动图片(需确认后加 --yes)。",
UseWhen: []string{"用户明确要求删除某张浮动图片时"},
AvoidWhen: []string{"删除单元格内嵌图需改写单元格;列目录用 list-float-images"},
Examples: []string{"dws sheet delete-float-image --node <NODE_ID> --sheet-id <SHEET_ID> --float-image-id <FI_ID>"},
},
Parameters: []contract.ParamDecl{
{Name: "node", Property: "nodeId"},
},
},
})
deleteFloatImageCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
deleteFloatImageCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
deleteFloatImageCmd.Flags().String("float-image-id", "", "浮动图片 ID (必填)")
return []*cobra.Command{createFloatImageCmd, getFloatImageCmd, listFloatImagesCmd, updateFloatImageCmd, deleteFloatImageCmd}
}