Files
dingtalk-workspace-cli/internal/shortcut/chat/batch_write.go

93 lines
2.7 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
package chat
import (
"fmt"
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut"
)
type shortcutBatchWrite struct {
target string
arguments map[string]any
}
// executeShortcutBatchWrite gives batch-style IM mutations one shared result
// contract. Dry-run publishes executable per-item actions without calling a
// write tool. Real execution preserves input order and records every failure
// instead of aborting the remaining items.
func executeShortcutBatchWrite(rt *shortcut.RuntimeContext, product, tool string, items []shortcutBatchWrite) error {
if rt.DryRun() {
actions := make([]map[string]any, 0, len(items))
for _, item := range items {
actions = append(actions, map[string]any{
"target": item.target,
"tool": tool,
"arguments": item.arguments,
})
}
return rt.Output(map[string]any{
"contractVersion": "im.batch-write.v1",
"dry_run": true,
"executed": false,
"preview_kind": "plan",
"tool": tool,
"actionCount": len(actions),
"failedCount": 0,
"actions": actions,
"requestedCount": len(items),
})
}
succeeded := make([]map[string]any, 0, len(items))
failures := make([]map[string]any, 0)
for _, item := range items {
data, err := rt.CallMCPWriteData(product, tool, item.arguments)
if err != nil {
failures = append(failures, map[string]any{
"target": item.target,
"error": err.Error(),
})
continue
}
entry := map[string]any{"target": item.target}
if len(data) > 0 {
entry["result"] = data
}
succeeded = append(succeeded, entry)
}
result := map[string]any{
"contractVersion": "im.batch-write.v1",
"ok": len(failures) == 0,
"partial": len(succeeded) > 0 && len(failures) > 0,
"requestedCount": len(items),
"succeededCount": len(succeeded),
"failedCount": len(failures),
"succeeded": succeeded,
"failures": failures,
}
if err := rt.Output(result); err != nil {
return err
}
if len(failures) > 0 {
return apperrors.NewAPI(
fmt.Sprintf("批量执行 %s 失败:%d/%d 个目标未完成", tool, len(failures), len(items)),
apperrors.WithOperation(product+"/"+tool),
apperrors.WithReason("batch_write_failed"),
apperrors.WithExecutionStarted(true),
apperrors.WithRetryable(false),
apperrors.WithDetails(map[string]any{
"requestedCount": len(items),
"succeededCount": len(succeeded),
"failedCount": len(failures),
"partial": len(succeeded) > 0,
}),
)
}
return nil
}