- add success and failure outcomes for three attachment commands - define business data schemas and mark downloadUri as sensitive - migrate attachment commands to unified result output - verify compact and full Schema result projections - cover success, malformed response, and tool error paths to #666
1666 lines
71 KiB
Go
1666 lines
71 KiB
Go
package helpers
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"io"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
|
||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd"
|
||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
|
||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/output"
|
||
"github.com/spf13/cobra"
|
||
)
|
||
|
||
func decodeOARequest(raw string) (map[string]any, error) {
|
||
dec := json.NewDecoder(bytes.NewBufferString(raw))
|
||
dec.UseNumber()
|
||
var request map[string]any
|
||
if err := dec.Decode(&request); err != nil || request == nil {
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return nil, fmt.Errorf("JSON 请求不能为 null")
|
||
}
|
||
if err := dec.Decode(new(any)); !errors.Is(err, io.EOF) {
|
||
return nil, fmt.Errorf("JSON 请求包含多余内容")
|
||
}
|
||
return request, nil
|
||
}
|
||
|
||
func oaFormValues(raw string) ([]map[string]string, error) {
|
||
var values map[string]string
|
||
if err := json.Unmarshal([]byte(raw), &values); err != nil {
|
||
return nil, err
|
||
}
|
||
result := make([]map[string]string, 0, len(values))
|
||
for name, value := range values {
|
||
result = append(result, map[string]string{"name": name, "value": value})
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
func parseOAAttachmentFileInfos(raw string) ([]map[string]any, error) {
|
||
decoder := json.NewDecoder(strings.NewReader(raw))
|
||
decoder.UseNumber()
|
||
var items []map[string]any
|
||
if err := decoder.Decode(&items); err != nil {
|
||
return nil, fmt.Errorf("--file-infos JSON 解析失败: %w", err)
|
||
}
|
||
if err := rejectTrailingOAAttachmentJSON(decoder); err != nil {
|
||
return nil, err
|
||
}
|
||
if len(items) < 1 || len(items) > 10 {
|
||
return nil, fmt.Errorf("--file-infos 必须包含 1 至 10 个文件")
|
||
}
|
||
|
||
infos := make([]map[string]any, 0, len(items))
|
||
for index, item := range items {
|
||
for name := range item {
|
||
if name != "spaceId" && name != "fileId" {
|
||
return nil, fmt.Errorf("--file-infos 第 %d 项包含未知字段 %q", index+1, name)
|
||
}
|
||
}
|
||
|
||
spaceValue, ok := item["spaceId"]
|
||
if !ok {
|
||
return nil, fmt.Errorf("--file-infos 第 %d 项缺少 spaceId", index+1)
|
||
}
|
||
spaceID, ok := spaceValue.(json.Number)
|
||
if !ok {
|
||
return nil, fmt.Errorf("--file-infos 第 %d 项 spaceId 必须是数字", index+1)
|
||
}
|
||
|
||
fileValue, ok := item["fileId"]
|
||
if !ok {
|
||
return nil, fmt.Errorf("--file-infos 第 %d 项缺少 fileId", index+1)
|
||
}
|
||
fileID, ok := fileValue.(string)
|
||
if !ok {
|
||
return nil, fmt.Errorf("--file-infos 第 %d 项 fileId 必须是字符串", index+1)
|
||
}
|
||
fileID = strings.TrimSpace(fileID)
|
||
if fileID == "" {
|
||
return nil, fmt.Errorf("--file-infos 第 %d 项 fileId 不能为空", index+1)
|
||
}
|
||
|
||
infos = append(infos, map[string]any{"spaceId": spaceID, "fileId": fileID})
|
||
}
|
||
return infos, nil
|
||
}
|
||
|
||
func rejectTrailingOAAttachmentJSON(decoder *json.Decoder) error {
|
||
var extra any
|
||
if err := decoder.Decode(&extra); err != io.EOF {
|
||
if err == nil {
|
||
return fmt.Errorf("--file-infos JSON 包含多余内容")
|
||
}
|
||
return fmt.Errorf("--file-infos JSON 解析失败: %w", err)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func validateOAAttachmentFileInfos(cmd *cobra.Command, _ []string) error {
|
||
raw, _ := cmd.Flags().GetString("file-infos")
|
||
_, err := parseOAAttachmentFileInfos(raw)
|
||
return err
|
||
}
|
||
|
||
func validateOAPreviewFileIDs(cmd *cobra.Command, _ []string) error {
|
||
fileIDs, _ := cmd.Flags().GetStringSlice("file-ids")
|
||
if len(fileIDs) > 20 {
|
||
return fmt.Errorf("--file-ids 最多包含 20 个附件 ID")
|
||
}
|
||
for index, fileID := range fileIDs {
|
||
if strings.TrimSpace(fileID) == "" {
|
||
return fmt.Errorf("--file-ids 第 %d 项不能为空", index+1)
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func callOAAttachmentResult(cmd *cobra.Command, tool string, args map[string]any) (output.CommandResult, error) {
|
||
data, err := CallMCPToolDataOnServer(cmd.Context(), "oa", tool, args)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
response, ok := data.(map[string]any)
|
||
if !ok {
|
||
return nil, apperrors.NewInternal(fmt.Sprintf("oa/%s 返回值不是 JSON 对象", tool))
|
||
}
|
||
result, ok := response["result"]
|
||
if !ok {
|
||
return nil, apperrors.NewInternal(fmt.Sprintf("oa/%s 返回值缺少 result", tool))
|
||
}
|
||
return output.Success(result), nil
|
||
}
|
||
|
||
func newOAAttachmentCommand() *cobra.Command {
|
||
attachmentCmd := &cobra.Command{
|
||
Use: "attachment",
|
||
Short: "审批附件授权与下载链接",
|
||
RunE: groupRunE,
|
||
}
|
||
|
||
downloadURLCmd := NewLeafCommand(LeafSpec{
|
||
Use: "download-url",
|
||
Short: "获取审批附件下载链接",
|
||
Example: " dws oa approval attachment download-url --instance-id <processInstanceId> --file-id <fileId>",
|
||
Server: "oa",
|
||
Tool: "get_attachment_download_url",
|
||
OutputRollout: output.RolloutUnifiedActive,
|
||
ResultCall: callOAAttachmentResult,
|
||
Flags: []LeafFlag{
|
||
{Name: "instance-id", Usage: "审批实例 ID (必填)", Bind: "processInstanceId", Trim: true, Required: true, MarkRequired: true},
|
||
{Name: "file-id", Usage: "审批附件文件 ID (必填)", Bind: "fileId", Trim: true, Required: true, MarkRequired: true},
|
||
{Name: "with-comment-attachment", Usage: "是否包含评论中的附件", Kind: LeafBool, Bind: "withCommentAttachment"},
|
||
},
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "get_attachment_download_url",
|
||
CanonicalPath: "oa.get_attachment_download_url",
|
||
CLIPath: "oa approval attachment download-url",
|
||
PrimaryCLIPath: "oa approval attachment download-url",
|
||
},
|
||
Description: "获取审批附件下载授权并生成临时下载链接",
|
||
Result: &contract.ResultSpec{
|
||
Outcomes: []contract.ResultOutcome{contract.ResultOutcomeSuccess, contract.ResultOutcomeFailure},
|
||
DataSchema: json.RawMessage(`{"type":"object","description":"审批附件临时下载信息","properties":{"spaceId":{"type":"integer","description":"审批附件所在钉盘空间 ID"},"agentId":{"type":"integer","description":"审批应用 Agent ID"},"downloadUri":{"type":"string","description":"带临时授权签名的附件下载链接"},"class":{"type":"string","description":"服务端响应类型标识"},"fileId":{"type":"string","description":"审批附件文件 ID"}},"required":["spaceId","agentId","downloadUri","fileId"],"additionalProperties":true}`),
|
||
SensitivePaths: []string{"downloadUri"},
|
||
},
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "get_attachment_download_url"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "获取审批实例中指定附件的临时下载链接",
|
||
UseWhen: []string{"已从审批详情获得 processInstanceId 和 fileId,需要获取附件下载链接时"},
|
||
AvoidWhen: []string{
|
||
"只需查看审批表单和附件元数据时使用 dws oa approval detail",
|
||
"需要将附件真正保存到本地时不要误认为本命令会下载文件;它只返回链接",
|
||
},
|
||
Examples: []string{"dws oa approval attachment download-url --instance-id <processInstanceId> --file-id <fileId>"},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "instance-id", Property: "processInstanceId", InterfaceType: "string"},
|
||
{Name: "file-id", Property: "fileId", InterfaceType: "string"},
|
||
{Name: "with-comment-attachment", Property: "withCommentAttachment", InterfaceType: "boolean"},
|
||
},
|
||
},
|
||
})
|
||
|
||
authorizeDownloadCmd := NewLeafCommand(LeafSpec{
|
||
Use: "authorize-download",
|
||
Short: "授权当前用户下载审批钉盘文件",
|
||
Long: "批量授权当前用户下载指定的审批钉盘文件。",
|
||
Example: ` dws oa approval attachment authorize-download --file-infos '[{"spaceId":27827223951,"fileId":"232271651278"}]'`,
|
||
Server: "oa",
|
||
Tool: "auth_download_file",
|
||
OutputRollout: output.RolloutUnifiedActive,
|
||
ResultCall: callOAAttachmentResult,
|
||
Flags: []LeafFlag{
|
||
{
|
||
Name: "file-infos", Usage: "审批钉盘文件信息 JSON 数组 (必填)",
|
||
Bind: "fileInfos", Trim: true, Required: true, MarkRequired: true,
|
||
Transform: func(raw string) (any, error) {
|
||
return parseOAAttachmentFileInfos(raw)
|
||
},
|
||
},
|
||
},
|
||
Constraints: []LeafConstraint{{
|
||
Kind: corecmd.Custom, Flags: []string{"file-infos"},
|
||
Description: "文件信息列表必须包含 1 至 10 项",
|
||
}},
|
||
Validate: validateOAAttachmentFileInfos,
|
||
Safety: contract.SafetySpec{
|
||
Effect: "write", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "auth_download_file",
|
||
CanonicalPath: "oa.auth_download_file",
|
||
CLIPath: "oa approval attachment authorize-download",
|
||
PrimaryCLIPath: "oa approval attachment authorize-download",
|
||
},
|
||
Description: "批量授权当前用户下载指定的审批钉盘文件",
|
||
Result: &contract.ResultSpec{
|
||
Outcomes: []contract.ResultOutcome{contract.ResultOutcomeSuccess, contract.ResultOutcomeFailure},
|
||
DataSchema: json.RawMessage(`{"type":"boolean","description":"是否成功为当前用户授予审批钉盘文件下载权限"}`),
|
||
},
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "auth_download_file"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "使用审批钉盘 spaceId/fileId 批量授权当前用户下载文件",
|
||
UseWhen: []string{"已有一个或多个审批钉盘文件的 spaceId 和 fileId,需要为当前用户取得下载权限时"},
|
||
AvoidWhen: []string{
|
||
"需要生成单个审批附件下载链接时使用 attachment download-url",
|
||
"需要授权在审批单内预览附件时使用 attachment authorize-preview",
|
||
},
|
||
Examples: []string{`dws oa approval attachment authorize-download --file-infos '[{"spaceId":27827223951,"fileId":"232271651278"}]'`},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "file-infos", Property: "fileInfos", InterfaceType: "array"},
|
||
},
|
||
},
|
||
})
|
||
|
||
authorizePreviewCmd := NewLeafCommand(LeafSpec{
|
||
Use: "authorize-preview",
|
||
Short: "授权当前用户预览审批附件",
|
||
Long: "批量授权当前用户预览审批单中的附件。",
|
||
Example: " dws oa approval attachment authorize-preview --instance-id <processInstanceId> --file-ids <fileId1>,<fileId2>",
|
||
Server: "oa",
|
||
Tool: "auth_preview_attachment",
|
||
OutputRollout: output.RolloutUnifiedActive,
|
||
ResultCall: callOAAttachmentResult,
|
||
Flags: []LeafFlag{
|
||
{Name: "instance-id", Usage: "审批实例 ID (必填)", Bind: "processInstanceId", Trim: true, Required: true, MarkRequired: true},
|
||
{Name: "file-ids", Usage: "附件 ID 列表,多个用逗号分隔 (必填)", Kind: LeafStringSlice, Bind: "fileIdList", Required: true, MarkRequired: true},
|
||
{Name: "with-comment-attachment", Usage: "是否包含评论中的附件", Kind: LeafBool, Bind: "withCommentAttachment"},
|
||
},
|
||
Constraints: []LeafConstraint{{
|
||
Kind: corecmd.Custom, Flags: []string{"file-ids"},
|
||
Description: "附件 ID 列表最多包含 20 项且每项不能为空",
|
||
}},
|
||
Validate: validateOAPreviewFileIDs,
|
||
Safety: contract.SafetySpec{
|
||
Effect: "write", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "auth_preview_attachment",
|
||
CanonicalPath: "oa.auth_preview_attachment",
|
||
CLIPath: "oa approval attachment authorize-preview",
|
||
PrimaryCLIPath: "oa approval attachment authorize-preview",
|
||
},
|
||
Description: "批量授权当前用户预览审批单中的附件",
|
||
Result: &contract.ResultSpec{
|
||
Outcomes: []contract.ResultOutcome{contract.ResultOutcomeSuccess, contract.ResultOutcomeFailure},
|
||
DataSchema: json.RawMessage(`{"type":"object","description":"审批附件预览授权信息","properties":{"spaceId":{"type":"integer","description":"审批附件所在钉盘空间 ID"},"agentId":{"type":"integer","description":"审批应用 Agent ID"},"class":{"type":"string","description":"服务端响应类型标识"}},"required":["spaceId","agentId"],"additionalProperties":true}`),
|
||
},
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "auth_preview_attachment"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "按审批实例和附件 ID 列表授权当前用户预览附件",
|
||
UseWhen: []string{"已有 processInstanceId 和附件 fileId 列表,需要在审批场景中批量取得预览权限时"},
|
||
AvoidWhen: []string{
|
||
"需要下载权限而不是预览权限时使用 attachment authorize-download",
|
||
"需要直接获得单个附件临时下载链接时使用 attachment download-url",
|
||
},
|
||
Examples: []string{"dws oa approval attachment authorize-preview --instance-id <processInstanceId> --file-ids <fileId1>,<fileId2>"},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "instance-id", Property: "processInstanceId", InterfaceType: "string"},
|
||
{Name: "file-ids", Property: "fileIdList", InterfaceType: "array"},
|
||
{Name: "with-comment-attachment", Property: "withCommentAttachment", InterfaceType: "boolean"},
|
||
},
|
||
},
|
||
})
|
||
|
||
attachmentCmd.AddCommand(downloadURLCmd, authorizeDownloadCmd, authorizePreviewCmd)
|
||
return attachmentCmd
|
||
}
|
||
|
||
// ──────────────────────────────────────────────────────────
|
||
// dws oa — OA 审批
|
||
// MCP tools(tools/list): list_pending_approvals, get_processInstance_detail,
|
||
// approve_processInstance, reject_processInstance, revoke_processInstance,
|
||
// get_processInstance_records, list_initiated_instances, list_pending_tasks,
|
||
// list_user_visible_process, append_task, search_form, oa_ding_user, revert_task,
|
||
// get_inst_revert_activities, get_process_schema, forecast_process,
|
||
// start_process_instance, get_attachment_download_url, auth_download_file,
|
||
// auth_preview_attachment
|
||
// ──────────────────────────────────────────────────────────
|
||
|
||
func newOaCommand() *cobra.Command {
|
||
// Product-level Agent routing Decl (migrated from selection/oa.json
|
||
// products.oa). Catalog assembly stamps provenance contract_final.
|
||
contract.RegisterProductDecl(contract.ProductDecl{
|
||
ID: "oa",
|
||
Selection: contract.ProductSelectionDecl{
|
||
AgentSummary: "查询和处理 OA 审批实例、任务、记录、抄送、评论与附件授权",
|
||
UseWhen: []string{
|
||
"查看待审、已办、已发起或抄送审批,并执行同意、拒绝、撤销、转交等审批动作时",
|
||
"获取审批附件下载链接,或为当前用户授权下载、预览审批附件时",
|
||
},
|
||
AvoidWhen: []string{
|
||
"不要用于普通待办任务或工作日志;需要实时监听未来的审批任务/实例事件时使用 event consume",
|
||
},
|
||
},
|
||
})
|
||
root := &cobra.Command{
|
||
Use: "oa",
|
||
Short: "OA 审批 / 同意 / 拒绝 / 撤销",
|
||
Long: `管理钉钉 OA 审批:待办查询、审批详情、同意、拒绝、撤销、操作记录、已发起列表、表单列表与附件授权。`,
|
||
RunE: groupRunE,
|
||
}
|
||
|
||
approvalCmd := &cobra.Command{Use: "approval", Short: "审批管理", RunE: groupRunE}
|
||
|
||
approvalListPendingCmd := &cobra.Command{
|
||
Use: "list-pending",
|
||
Short: "查询待我处理的审批",
|
||
Example: ` dws oa approval list-pending --start "2026-03-10T00:00:00+08:00" --end "2026-03-10T23:59:59+08:00" --query 关键词`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
startMs, err := parseISOTimeToMillis("start", mustGetFlag(cmd, "start"))
|
||
if err != nil {
|
||
return err
|
||
}
|
||
endMs, err := parseISOTimeToMillis("end", mustGetFlag(cmd, "end"))
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if err := validateTimeRange(startMs, endMs); err != nil {
|
||
return err
|
||
}
|
||
argsMap := map[string]any{
|
||
"starTime": float64(startMs),
|
||
"endTime": float64(endMs),
|
||
}
|
||
if v, _ := cmd.Flags().GetString("page"); v != "" {
|
||
if n, err := strconv.ParseFloat(v, 64); err == nil {
|
||
argsMap["pageNum"] = n
|
||
}
|
||
}
|
||
if v := flagOrFallback(cmd, "limit", "size"); v != "" {
|
||
if n, err := strconv.ParseFloat(v, 64); err == nil {
|
||
argsMap["pageSize"] = n
|
||
}
|
||
}
|
||
if v, _ := cmd.Flags().GetString("query"); v != "" {
|
||
argsMap["query"] = v
|
||
}
|
||
return callMCPTool("list_pending_approvals", argsMap)
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalListPendingCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "list_pending_approvals",
|
||
CanonicalPath: "oa.list_pending_approvals",
|
||
CLIPath: "oa approval list-pending",
|
||
PrimaryCLIPath: "oa approval list-pending",
|
||
},
|
||
Description: "查询当前用户待处理的审批单列表",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "list_pending_approvals"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "查询当前用户待处理的审批单列表",
|
||
UseWhen: []string{"需要查看待我处理的审批单并提取 processInstanceId / 跳转链接时"},
|
||
AvoidWhen: []string{
|
||
"已知实例只要 taskId 时改用 dws oa approval tasks",
|
||
"不要用本命令执行同意/拒绝",
|
||
},
|
||
Examples: []string{
|
||
"dws oa approval list-pending --start \"2026-03-10T00:00:00+08:00\" --end \"2026-03-10T23:59:59+08:00\"",
|
||
"dws oa approval list-pending --start \"2026-03-10T00:00:00+08:00\" --end \"2026-03-10T23:59:59+08:00\" --query 关键词",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "end", Property: "endTime"},
|
||
{Name: "limit", Property: "pageSize"},
|
||
{Name: "page", Property: "pageNum"},
|
||
{Name: "start", Property: "starTime"},
|
||
},
|
||
},
|
||
})
|
||
|
||
approvalDetailCmd := &cobra.Command{
|
||
Use: "detail",
|
||
Short: "获取审批实例详情",
|
||
Example: ` dws oa approval detail --instance-id <processInstanceId> # 查询 instanceId: dws oa approval list-pending`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "instance-id"); err != nil {
|
||
return err
|
||
}
|
||
return callMCPTool("get_processInstance_detail", map[string]any{
|
||
"processInstanceId": mustGetFlag(cmd, "instance-id"),
|
||
})
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalDetailCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "get_processInstance_detail",
|
||
CanonicalPath: "oa.get_processInstance_detail",
|
||
CLIPath: "oa approval detail",
|
||
PrimaryCLIPath: "oa approval detail",
|
||
},
|
||
Description: "获取指定审批实例的详情信息",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "get_processInstance_detail"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "获取指定审批实例的详情信息",
|
||
UseWhen: []string{"已知 processInstanceId,需要查看表单内容与当前状态详情时"},
|
||
AvoidWhen: []string{
|
||
"需要操作历史而非表单详情时改用 dws oa approval records",
|
||
"该命令不会同意/拒绝/撤销",
|
||
},
|
||
Examples: []string{
|
||
"dws oa approval detail --instance-id <processInstanceId>",
|
||
"dws oa approval detail --instance-id <processInstanceId> --format json",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "instance-id", Property: "processInstanceId"},
|
||
},
|
||
},
|
||
})
|
||
|
||
approvalApproveCmd := &cobra.Command{
|
||
Use: "approve",
|
||
Short: "同意审批",
|
||
Example: ` dws oa approval approve --instance-id <id> --task-id <taskId> # 查询 instanceId: dws oa approval list-pending; taskId 来自 dws oa approval tasks
|
||
dws oa approval approve --instance-id <id> --task-id <taskId> --remark "同意"`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "instance-id", "task-id"); err != nil {
|
||
return err
|
||
}
|
||
taskIdNum, _ := strconv.ParseFloat(mustGetFlag(cmd, "task-id"), 64)
|
||
argsMap := map[string]any{
|
||
"processInstanceId": mustGetFlag(cmd, "instance-id"),
|
||
"taskId": taskIdNum,
|
||
}
|
||
if v, _ := cmd.Flags().GetString("remark"); v != "" {
|
||
argsMap["remark"] = v
|
||
}
|
||
return callMCPTool("approve_processInstance", argsMap)
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalApproveCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "write", Risk: "medium",
|
||
Confirmation: "not_required", Idempotency: "unknown",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "approve_processInstance",
|
||
CanonicalPath: "oa.approve_processInstance",
|
||
CLIPath: "oa approval approve",
|
||
PrimaryCLIPath: "oa approval approve",
|
||
},
|
||
Description: "同意审批",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "approve_processInstance"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "同意审批",
|
||
UseWhen: []string{"已知 processInstanceId 与待办 taskId,用户明确要求同意该审批任务时"},
|
||
AvoidWhen: []string{
|
||
"实例或 taskId 未确认时不要同意;先用 list-pending / tasks 取 ID",
|
||
"要拒绝时改用 dws oa approval reject;要撤销自己发起的单时改用 revoke",
|
||
},
|
||
Examples: []string{
|
||
"dws oa approval approve --instance-id <id> --task-id <taskId>",
|
||
"dws oa approval approve --instance-id <id> --task-id <taskId> --remark \"同意\"",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "instance-id", Property: "processInstanceId"},
|
||
},
|
||
},
|
||
})
|
||
|
||
approvalRejectCmd := &cobra.Command{
|
||
Use: "reject",
|
||
Short: "拒绝审批",
|
||
Example: ` dws oa approval reject --instance-id <id> --task-id <taskId> --remark "不同意" # 查询 instanceId: dws oa approval list-pending; taskId 来自 dws oa approval tasks`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "instance-id", "task-id"); err != nil {
|
||
return err
|
||
}
|
||
taskIdNum, _ := strconv.ParseFloat(mustGetFlag(cmd, "task-id"), 64)
|
||
argsMap := map[string]any{
|
||
"processInstanceId": mustGetFlag(cmd, "instance-id"),
|
||
"taskId": taskIdNum,
|
||
}
|
||
if v, _ := cmd.Flags().GetString("remark"); v != "" {
|
||
argsMap["remark"] = v
|
||
}
|
||
return callMCPTool("reject_processInstance", argsMap)
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalRejectCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "write", Risk: "medium",
|
||
Confirmation: "not_required", Idempotency: "unknown",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "reject_processInstance",
|
||
CanonicalPath: "oa.reject_processInstance",
|
||
CLIPath: "oa approval reject",
|
||
PrimaryCLIPath: "oa approval reject",
|
||
},
|
||
Description: "拒绝审批",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "reject_processInstance"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "拒绝审批",
|
||
UseWhen: []string{"已知 processInstanceId 与 taskId,用户明确要求拒绝/驳回该审批任务时"},
|
||
AvoidWhen: []string{
|
||
"要同意时改用 dws oa approval approve",
|
||
"实例、taskId 或拒绝原因未确认时不要拒绝",
|
||
},
|
||
Examples: []string{
|
||
"dws oa approval reject --instance-id <id> --task-id <taskId> --remark \"不同意\"",
|
||
"dws oa approval reject --instance-id <id> --task-id <taskId> --remark \"不符合要求\" --format json",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "instance-id", Property: "processInstanceId"},
|
||
},
|
||
},
|
||
})
|
||
|
||
approvalRevokeCmd := &cobra.Command{
|
||
Use: "revoke",
|
||
Short: "撤销已发起的审批",
|
||
Example: ` dws oa approval revoke --instance-id <id> --yes # 查询 instanceId: dws oa approval list-pending
|
||
dws oa approval revoke --instance-id <id> --remark "误发起" --yes`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "instance-id"); err != nil {
|
||
return err
|
||
}
|
||
instanceId := mustGetFlag(cmd, "instance-id")
|
||
argsMap := map[string]any{
|
||
"processInstanceId": instanceId,
|
||
}
|
||
if v, _ := cmd.Flags().GetString("remark"); v != "" {
|
||
argsMap["remark"] = v
|
||
}
|
||
return callMCPTool("revoke_processInstance", argsMap)
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalRevokeCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "write", Risk: "high",
|
||
Confirmation: "user_required", Idempotency: "unknown",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "revoke_processInstance",
|
||
CanonicalPath: "oa.revoke_processInstance",
|
||
CLIPath: "oa approval revoke",
|
||
PrimaryCLIPath: "oa approval revoke",
|
||
},
|
||
Description: "撤销当前用户已发起的审批实例",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "revoke_processInstance"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "撤销当前用户已发起的审批实例",
|
||
UseWhen: []string{"用户明确要求撤销自己已发起的审批实例,且 processInstanceId 已确认时"},
|
||
AvoidWhen: []string{
|
||
"不是自己发起的单或 instanceId 未确认时不要撤销",
|
||
"要拒绝别人的待办时改用 reject,不要用 revoke",
|
||
},
|
||
Examples: []string{"dws oa approval revoke --instance-id <instance-id>"},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "instance-id", Property: "processInstanceId"},
|
||
},
|
||
},
|
||
})
|
||
|
||
approvalRecordsCmd := &cobra.Command{
|
||
Use: "records",
|
||
Short: "获取审批操作记录",
|
||
Example: ` dws oa approval records --instance-id <processInstanceId> # 查询 instanceId: dws oa approval list-pending`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "instance-id"); err != nil {
|
||
return err
|
||
}
|
||
return callMCPTool("get_processInstance_records", map[string]any{
|
||
"processInstanceId": mustGetFlag(cmd, "instance-id"),
|
||
})
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalRecordsCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "get_processInstance_records",
|
||
CanonicalPath: "oa.get_processInstance_records",
|
||
CLIPath: "oa approval records",
|
||
PrimaryCLIPath: "oa approval records",
|
||
},
|
||
Description: "获取审批操作记录",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "get_processInstance_records"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "获取审批操作记录",
|
||
UseWhen: []string{"已知 processInstanceId,需要查看谁做了什么审批操作及结果时"},
|
||
AvoidWhen: []string{
|
||
"需要当前表单详情时改用 dws oa approval detail",
|
||
"该命令只读历史,不处理审批",
|
||
},
|
||
Examples: []string{
|
||
"dws oa approval records --instance-id <processInstanceId>",
|
||
"dws oa approval records --instance-id <processInstanceId> --format json",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "instance-id", Property: "processInstanceId"},
|
||
},
|
||
},
|
||
})
|
||
|
||
approvalListInitiatedCmd := &cobra.Command{
|
||
Use: "list-initiated",
|
||
Short: "查询审批模板下已发起的审批记录",
|
||
Example: ` dws oa approval list-initiated --process-code <code> --start "2026-03-10T00:00:00+08:00" --end "2026-03-10T23:59:59+08:00" --cursor 0 --limit 20 # processCode 来自管理后台配置`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "process-code"); err != nil {
|
||
return err
|
||
}
|
||
startMs, err := parseISOTimeToMillis("start", mustGetFlag(cmd, "start"))
|
||
if err != nil {
|
||
return err
|
||
}
|
||
endMs, err := parseISOTimeToMillis("end", mustGetFlag(cmd, "end"))
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if err := validateTimeRange(startMs, endMs); err != nil {
|
||
return err
|
||
}
|
||
nextToken, _ := strconv.ParseFloat(flagOrFallback(cmd, "cursor", "next-token"), 64)
|
||
maxResults, _ := strconv.ParseFloat(flagOrFallback(cmd, "limit", "max-results"), 64)
|
||
return callMCPTool("list_initiated_instances", map[string]any{
|
||
"processCode": mustGetFlag(cmd, "process-code"),
|
||
"startTime": float64(startMs),
|
||
"endTime": float64(endMs),
|
||
"nextToken": nextToken,
|
||
"maxResults": maxResults,
|
||
})
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalListInitiatedCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "list_initiated_instances",
|
||
CanonicalPath: "oa.list_initiated_instances",
|
||
CLIPath: "oa approval list-initiated",
|
||
PrimaryCLIPath: "oa approval list-initiated",
|
||
},
|
||
Description: "查询当前用户已发起的审批实例列表",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "list_initiated_instances"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "查询当前用户已发起的审批实例列表",
|
||
UseWhen: []string{"已知 processCode,需要按起止时间查询自己发起的审批实例基础信息时"},
|
||
AvoidWhen: []string{
|
||
"不知道 processCode 时先用 dws oa approval list-forms",
|
||
"要撤销实例时先确认 instanceId 再改用 revoke",
|
||
},
|
||
Examples: []string{
|
||
"dws oa approval list-initiated --process-code <code> --start \"2026-03-10T00:00:00+08:00\" --end \"2026-03-10T23:59:59+08:00\" --cursor 0 --limit 20",
|
||
"dws oa approval list-initiated --process-code <code> --start \"2026-03-10T00:00:00+08:00\" --end \"2026-03-10T23:59:59+08:00\" --cursor 0 --limit 20 --format json",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "cursor", Property: "nextToken"},
|
||
{Name: "end", Property: "endTime"},
|
||
{Name: "limit", Property: "maxResults"},
|
||
{Name: "start", Property: "startTime"},
|
||
},
|
||
},
|
||
})
|
||
|
||
approvalTasksCmd := &cobra.Command{
|
||
Use: "tasks",
|
||
Short: "查询待我审批的任务 ID",
|
||
Example: ` dws oa approval tasks --instance-id <processInstanceId> # 查询 instanceId: dws oa approval list-pending`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "instance-id"); err != nil {
|
||
return err
|
||
}
|
||
return callMCPTool("list_pending_tasks", map[string]any{
|
||
"processInstanceId": mustGetFlag(cmd, "instance-id"),
|
||
})
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalTasksCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "list_pending_tasks",
|
||
CanonicalPath: "oa.list_pending_tasks",
|
||
CLIPath: "oa approval tasks",
|
||
PrimaryCLIPath: "oa approval tasks",
|
||
},
|
||
Description: "查询待我审批的任务Id",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "list_pending_tasks"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "查询待我审批的任务Id",
|
||
UseWhen: []string{"已知 processInstanceId,需要取得当前用户待办 taskId 以便同意或拒绝时"},
|
||
AvoidWhen: []string{
|
||
"还不知道实例列表时先用 dws oa approval list-pending",
|
||
"本命令只取 taskId,不执行审批",
|
||
},
|
||
Examples: []string{
|
||
"dws oa approval tasks --instance-id <processInstanceId>",
|
||
"dws oa approval tasks --instance-id <processInstanceId> --format json",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "instance-id", Property: "processInstanceId"},
|
||
},
|
||
},
|
||
})
|
||
|
||
approvalListFormsCmd := &cobra.Command{
|
||
Use: "list-forms",
|
||
Short: "获取当前用户可见的审批表单列表",
|
||
Example: ` dws oa approval list-forms --cursor 0 --limit 100`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
cursor, _ := strconv.ParseFloat(mustGetFlag(cmd, "cursor"), 64)
|
||
pageSize, _ := strconv.ParseFloat(flagOrFallback(cmd, "limit", "size"), 64)
|
||
return callMCPTool("list_user_visible_process", map[string]any{
|
||
"cursor": cursor,
|
||
"pageSize": pageSize,
|
||
})
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalListFormsCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "list_user_visible_process",
|
||
CanonicalPath: "oa.list_user_visible_process",
|
||
CLIPath: "oa approval list-forms",
|
||
PrimaryCLIPath: "oa approval list-forms",
|
||
},
|
||
Description: "获取当前用户可见的审批表单列表",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "list_user_visible_process"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "获取当前用户可见的审批表单列表",
|
||
UseWhen: []string{"需要列出可见审批模板并取得 processCode 时"},
|
||
AvoidWhen: []string{"需要实例、任务或操作记录时不要使用;该命令只列可见模板"},
|
||
Examples: []string{
|
||
"dws oa approval list-forms --cursor 0 --limit 100",
|
||
"dws oa approval list-forms --cursor 0 --limit 100 --format json",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "limit", Property: "pageSize"},
|
||
},
|
||
},
|
||
})
|
||
|
||
// 模糊搜索表单(按 processCode 或 name 关键字)
|
||
approvalSearchFormsCmd := &cobra.Command{
|
||
Use: "search-forms",
|
||
Short: "按关键字模糊搜索当前用户可见的审批表单",
|
||
Example: ` dws oa approval search-forms --query AI
|
||
dws oa approval search-forms --query 报销`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "query"); err != nil {
|
||
return err
|
||
}
|
||
return callMCPTool("search_form", map[string]any{
|
||
"query": mustGetFlag(cmd, "query"),
|
||
})
|
||
},
|
||
}
|
||
|
||
// 获取审批任务的被催办人 userId
|
||
// 仅返回 userId;发 DING 的 robotCode 由 $DINGTALK_DING_ROBOT_CODE / --robot-code 提供,content 由 agent 撰写。
|
||
approvalDingInfoCmd := &cobra.Command{
|
||
Use: "ding-info",
|
||
Short: "获取审批任务的被催办人 userId(需与 ding message send 串联使用)",
|
||
Example: ` dws oa approval ding-info --task-id <taskId>
|
||
# 返回的 userId 作为 --users 传入 dws ding message send:
|
||
# dws ding message send --robot-code $DINGTALK_DING_ROBOT_CODE --users <userId逗号拼接> --content "请尽快审批"`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "task-id"); err != nil {
|
||
return err
|
||
}
|
||
return callMCPTool("oa_ding_user", map[string]any{
|
||
"taskId": mustGetFlag(cmd, "task-id"),
|
||
})
|
||
},
|
||
}
|
||
|
||
// 已经审批过的
|
||
approvalExecutedListCmd := &cobra.Command{
|
||
Use: "list-executed",
|
||
Short: "获取当前用户已经处理过的审批单列表",
|
||
Example: ` dws oa approval list-executed --limit 20 --page 1 --query 关键词`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
pageSize, _ := strconv.ParseFloat(mustGetFlag(cmd, "limit"), 64)
|
||
pageNumber, _ := strconv.ParseFloat(mustGetFlag(cmd, "page"), 64)
|
||
argsMap := map[string]any{
|
||
"pageNumber": pageNumber,
|
||
"pageSize": pageSize,
|
||
}
|
||
if v, _ := cmd.Flags().GetString("query"); v != "" {
|
||
argsMap["query"] = v
|
||
}
|
||
return callMCPTool("get_done_tasks", argsMap)
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalExecutedListCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "get_done_tasks",
|
||
CanonicalPath: "oa.get_done_tasks",
|
||
CLIPath: "oa approval list-executed",
|
||
PrimaryCLIPath: "oa approval list-executed",
|
||
},
|
||
Description: "获取员工已处理任务列表",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "get_done_tasks"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "获取员工已处理任务列表",
|
||
UseWhen: []string{"需要查看当前用户已经处理过的审批单列表时"},
|
||
AvoidWhen: []string{
|
||
"要看待处理单时改用 dws oa approval list-pending",
|
||
"要看自己发起或抄送时改用 list-submitted / list-cc",
|
||
},
|
||
Examples: []string{
|
||
"dws oa approval list-executed --limit <pageSize> --page <pageNumber> --query 关键词",
|
||
"dws oa approval list-executed --limit <pageSize> --page <pageNumber> --query 关键词 --format json",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "limit", Property: "pageSize"},
|
||
{Name: "page", Property: "pageNumber"},
|
||
},
|
||
},
|
||
})
|
||
// 已发起
|
||
approvalSubmittedListCmd := &cobra.Command{
|
||
Use: "list-submitted",
|
||
Short: "获取当前用户已发起的审批单列表",
|
||
Example: ` dws oa approval list-submitted --limit 20 --page 1 --query 关键词`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
pageSize, _ := strconv.ParseFloat(mustGetFlag(cmd, "limit"), 64)
|
||
pageNumber, _ := strconv.ParseFloat(mustGetFlag(cmd, "page"), 64)
|
||
argsMap := map[string]any{
|
||
"pageNumber": pageNumber,
|
||
"pageSize": pageSize,
|
||
}
|
||
if v, _ := cmd.Flags().GetString("query"); v != "" {
|
||
argsMap["query"] = v
|
||
}
|
||
return callMCPTool("get_submitted_instances", argsMap)
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalSubmittedListCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "get_submitted_instances",
|
||
CanonicalPath: "oa.get_submitted_instances",
|
||
CLIPath: "oa approval list-submitted",
|
||
PrimaryCLIPath: "oa approval list-submitted",
|
||
},
|
||
Description: "获取已提交实例列表",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "get_submitted_instances"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "获取已提交实例列表",
|
||
UseWhen: []string{"需要查看当前用户已提交/发起相关的审批单列表时"},
|
||
AvoidWhen: []string{
|
||
"要按 processCode 与时间窗列已发起实例时也可对照 list-initiated",
|
||
"要处理待办时改用 list-pending / tasks",
|
||
},
|
||
Examples: []string{
|
||
"dws oa approval list-submitted --limit <pageSize> --page <pageNumber> --query 关键词",
|
||
"dws oa approval list-submitted --limit <pageSize> --page <pageNumber> --query 关键词 --format json",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "limit", Property: "pageSize"},
|
||
{Name: "page", Property: "pageNumber"},
|
||
},
|
||
},
|
||
})
|
||
// 抄送
|
||
approvalCcListCmd := &cobra.Command{
|
||
Use: "list-cc",
|
||
Short: "获取抄送当前用户的审批单列表",
|
||
Example: ` dws oa approval list-cc --limit 20 --page 1 --query 关键词`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
pageSize, _ := strconv.ParseFloat(mustGetFlag(cmd, "limit"), 64)
|
||
pageNumber, _ := strconv.ParseFloat(mustGetFlag(cmd, "page"), 64)
|
||
argsMap := map[string]any{
|
||
"pageNumber": pageNumber,
|
||
"pageSize": pageSize,
|
||
}
|
||
if v, _ := cmd.Flags().GetString("query"); v != "" {
|
||
argsMap["query"] = v
|
||
}
|
||
return callMCPTool("get_noticed_instances", argsMap)
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalCcListCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "get_noticed_instances",
|
||
CanonicalPath: "oa.get_noticed_instances",
|
||
CLIPath: "oa approval list-cc",
|
||
PrimaryCLIPath: "oa approval list-cc",
|
||
},
|
||
Description: "获取抄送用户的列表",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "get_noticed_instances"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "获取抄送用户的列表",
|
||
UseWhen: []string{"需要查看抄送给当前用户的审批单列表时"},
|
||
AvoidWhen: []string{
|
||
"要看待处理或自己发起的审批时不要使用",
|
||
"该命令只列抄送实例,不执行审批动作",
|
||
},
|
||
Examples: []string{
|
||
"dws oa approval list-cc --limit <pageSize> --page <pageNumber> --query 关键词",
|
||
"dws oa approval list-cc --limit <pageSize> --page <pageNumber> --query 关键词 --format json",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "limit", Property: "pageSize"},
|
||
{Name: "page", Property: "pageNumber"},
|
||
},
|
||
},
|
||
})
|
||
|
||
// 转交任务
|
||
approvalTransferCmd := &cobra.Command{
|
||
Use: "redirect-task",
|
||
Short: "转交审批任务给其他人",
|
||
Example: ` dws oa approval redirect-task --task-id <taskId> --to-actioner-id <userId>
|
||
dws oa approval redirect-task --task-id <taskId> --to-actioner-id <userId> --remark "请帮忙处理"`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "task-id", "to-actioner-id"); err != nil {
|
||
return err
|
||
}
|
||
argsMap := map[string]any{
|
||
"taskId": mustGetFlag(cmd, "task-id"),
|
||
"toActionerId": mustGetFlag(cmd, "to-actioner-id"),
|
||
}
|
||
if v, _ := cmd.Flags().GetString("remark"); v != "" {
|
||
argsMap["remark"] = v
|
||
}
|
||
return callMCPTool("redirect_task", argsMap)
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalTransferCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "write", Risk: "medium",
|
||
Confirmation: "not_required", Idempotency: "unknown",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "redirect_task",
|
||
CanonicalPath: "oa.redirect_task",
|
||
CLIPath: "oa approval redirect-task",
|
||
PrimaryCLIPath: "oa approval redirect-task",
|
||
},
|
||
Description: "转交审批任务",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "redirect_task"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "转交审批任务",
|
||
UseWhen: []string{"已知 taskId 与接收人 toActionerId,用户明确要求转交审批任务时"},
|
||
AvoidWhen: []string{
|
||
"任务、接收人或转交意图未确认时不要转交",
|
||
"要自己处理时改用 approve/reject",
|
||
},
|
||
Examples: []string{
|
||
"dws oa approval redirect-task --task-id <taskId> --to-actioner-id <userId>",
|
||
"dws oa approval redirect-task --task-id <taskId> --to-actioner-id <userId> --remark \"请帮忙处理\"",
|
||
},
|
||
},
|
||
},
|
||
})
|
||
|
||
// 评论审批实例
|
||
approvalCommentCmd := &cobra.Command{
|
||
Use: "oa-comments",
|
||
Short: "对审批实例添加评论",
|
||
Example: ` dws oa approval oa-comments --instance-id <processInstanceId> --content "同意,请尽快处理"`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "instance-id"); err != nil {
|
||
return err
|
||
}
|
||
commentText := flagOrFallback(cmd, "content", "text")
|
||
if commentText == "" {
|
||
return fmt.Errorf("--content is required")
|
||
}
|
||
return callMCPTool("dingflow_comments", map[string]any{
|
||
"processInstanceId": mustGetFlag(cmd, "instance-id"),
|
||
"text": commentText,
|
||
})
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalCommentCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "write", Risk: "medium",
|
||
Confirmation: "not_required", Idempotency: "unknown",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "dingflow_comments",
|
||
CanonicalPath: "oa.dingflow_comments",
|
||
CLIPath: "oa approval oa-comments",
|
||
PrimaryCLIPath: "oa approval oa-comments",
|
||
},
|
||
Description: "用户添加审批评论",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "dingflow_comments"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "用户添加审批评论",
|
||
UseWhen: []string{"已知 processInstanceId,需要为审批实例添加评论文本时"},
|
||
AvoidWhen: []string{
|
||
"只需同意/拒绝/转交时改用对应写命令",
|
||
"评论内容或实例未确认时不要添加",
|
||
},
|
||
Examples: []string{
|
||
"dws oa approval oa-comments --instance-id <processInstanceId> --content \"同意,请尽快处理\"",
|
||
"dws oa approval oa-comments --instance-id <processInstanceId> --content \"同意,请尽快处理\" --format json",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "content", Property: "text"},
|
||
{Name: "instance-id", Property: "processInstanceId"},
|
||
},
|
||
},
|
||
})
|
||
|
||
// 审批抄送
|
||
approvalCcCmd := &cobra.Command{
|
||
Use: "oa-cc-noticer",
|
||
Short: "对审批实例进行抄送",
|
||
Example: ` dws oa approval oa-cc-noticer --instance-id <processInstanceId> --users "68674200835816"
|
||
dws oa approval oa-cc-noticer --instance-id <processInstanceId> --users "userId1,userId2" --operator-id "123123"`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "instance-id"); err != nil {
|
||
return err
|
||
}
|
||
userListStr := flagOrFallback(cmd, "users", "user-list")
|
||
if userListStr == "" {
|
||
return fmt.Errorf("--users is required")
|
||
}
|
||
userList := strings.Split(userListStr, ",")
|
||
argsMap := map[string]any{
|
||
"processInstanceId": mustGetFlag(cmd, "instance-id"),
|
||
"userList": userList,
|
||
}
|
||
return callMCPTool("oa_cc_noticer", argsMap)
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalCcCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "write", Risk: "medium",
|
||
Confirmation: "not_required", Idempotency: "unknown",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "oa_cc_noticer",
|
||
CanonicalPath: "oa.oa_cc_noticer",
|
||
CLIPath: "oa approval oa-cc-noticer",
|
||
PrimaryCLIPath: "oa approval oa-cc-noticer",
|
||
},
|
||
Description: "抄送审批人",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "oa_cc_noticer"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "抄送审批人",
|
||
UseWhen: []string{"已知 processInstanceId,需要为审批实例追加抄送人时"},
|
||
AvoidWhen: []string{
|
||
"只需查看抄送列表时改用 dws oa approval list-cc",
|
||
"实例与抄送人未确认时不要添加",
|
||
},
|
||
Examples: []string{
|
||
"dws oa approval oa-cc-noticer --instance-id <processInstanceId> --users \"68674200835816\"",
|
||
"dws oa approval oa-cc-noticer --instance-id <processInstanceId> --users \"userId1,userId2\" --operator-id \"123123\"",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "instance-id", Property: "processInstanceId"},
|
||
{Name: "users", Property: "userList"},
|
||
},
|
||
},
|
||
})
|
||
|
||
// 加签任务
|
||
approvalAppendTaskCmd := &cobra.Command{
|
||
Use: "append-task",
|
||
Short: "对审批任务进行加签",
|
||
Example: ` dws oa approval append-task --instance-id <processInstanceId> --task-id <taskId> --type before --appender-user-ids "userId1,userId2" --activate-type ALL --agree-all true
|
||
dws oa approval append-task --instance-id <processInstanceId> --task-id <taskId> --type Parallel --appender-user-ids "userId1" --activate-type ONE_BY_ONE --agree-all false`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "instance-id", "task-id", "type", "appender-user-ids", "activate-type", "agree-all"); err != nil {
|
||
return err
|
||
}
|
||
typeVal := mustGetFlag(cmd, "type")
|
||
if typeVal != "before" && typeVal != "after" && typeVal != "Parallel" {
|
||
return fmt.Errorf("--type must be one of: before, after, Parallel, got: %s", typeVal)
|
||
}
|
||
activateTypeVal := mustGetFlag(cmd, "activate-type")
|
||
if activateTypeVal != "ALL" && activateTypeVal != "ONE_BY_ONE" {
|
||
return fmt.Errorf("--activate-type must be one of: ALL, ONE_BY_ONE, got: %s", activateTypeVal)
|
||
}
|
||
appenderUserIdsStr := mustGetFlag(cmd, "appender-user-ids")
|
||
appenderUserIds := strings.Split(appenderUserIdsStr, ",")
|
||
agreeAll, err := strconv.ParseBool(mustGetFlag(cmd, "agree-all"))
|
||
if err != nil {
|
||
return fmt.Errorf("--agree-all must be 'true' or 'false', got: %s", mustGetFlag(cmd, "agree-all"))
|
||
}
|
||
return callMCPTool("append_task", map[string]any{
|
||
"processInstanceId": mustGetFlag(cmd, "instance-id"),
|
||
"taskId": mustGetFlag(cmd, "task-id"),
|
||
"type": typeVal,
|
||
"appenderUserIds": appenderUserIds,
|
||
"activateType": activateTypeVal,
|
||
"agreeAll": agreeAll,
|
||
})
|
||
},
|
||
}
|
||
|
||
// 获取任务可回退的节点信息
|
||
approvalRevertActivitiesCmd := &cobra.Command{
|
||
Use: "revert-activities",
|
||
Short: "获取审批任务可回退的节点信息(退回前必须调用,获取可回退节点列表)",
|
||
Example: ` dws oa approval revert-activities --task-id <taskId>
|
||
# 返回可回退节点列表,从中选择 targetActivityId 和 action`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "task-id"); err != nil {
|
||
return err
|
||
}
|
||
taskIdNum, err := strconv.ParseFloat(mustGetFlag(cmd, "task-id"), 64)
|
||
if err != nil {
|
||
return fmt.Errorf("--task-id must be a number, got: %s", mustGetFlag(cmd, "task-id"))
|
||
}
|
||
return callMCPTool("get_inst_revert_activities", map[string]any{
|
||
"taskId": taskIdNum,
|
||
})
|
||
},
|
||
}
|
||
|
||
// 退回任务(退回到审批人或发起人)
|
||
approvalRevertTaskCmd := &cobra.Command{
|
||
Use: "revert-task",
|
||
Short: "退回审批任务到指定节点(审批人或发起人)",
|
||
Example: ` # 退回到发起人(targetActivityId 固定传 sid-startevent)
|
||
dws oa approval revert-task --instance-id <processInstanceId> --task-id <taskId> --target-activity-id sid-startevent --action REVERT_FOR_RESUBMIT --remark "补充说明后重提"
|
||
# 退回到某个审批节点(targetActivityId 从审批流程节点信息中获取 activityId)
|
||
dws oa approval revert-task --instance-id <processInstanceId> --task-id <taskId> --target-activity-id <activityId> --action REVERT_FOR_APPROVAL --remark "重新审批"`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "instance-id", "task-id", "target-activity-id", "action"); err != nil {
|
||
return err
|
||
}
|
||
action := mustGetFlag(cmd, "action")
|
||
if action != "REVERT_FOR_APPROVAL" && action != "REVERT_FOR_RESUBMIT" {
|
||
return fmt.Errorf("--action must be one of: REVERT_FOR_APPROVAL, REVERT_FOR_RESUBMIT, got: %s", action)
|
||
}
|
||
targetActivityId := mustGetFlag(cmd, "target-activity-id")
|
||
// 退回发起人时,targetActivityId 固定为 sid-startevent
|
||
if action == "REVERT_FOR_RESUBMIT" && targetActivityId != "sid-startevent" {
|
||
return fmt.Errorf("--action=REVERT_FOR_RESUBMIT 时 --target-activity-id 必须为 sid-startevent,got: %s", targetActivityId)
|
||
}
|
||
taskIdNum, err := strconv.ParseFloat(mustGetFlag(cmd, "task-id"), 64)
|
||
if err != nil {
|
||
return fmt.Errorf("--task-id must be a number, got: %s", mustGetFlag(cmd, "task-id"))
|
||
}
|
||
inner := map[string]any{
|
||
"processInstanceId": mustGetFlag(cmd, "instance-id"),
|
||
"taskId": taskIdNum,
|
||
"targetActivityId": targetActivityId,
|
||
"revertAction": action,
|
||
}
|
||
if v, _ := cmd.Flags().GetString("remark"); v != "" {
|
||
inner["remark"] = v
|
||
}
|
||
return callMCPTool("revert_task", map[string]any{
|
||
"RevertTaskRequest": inner,
|
||
})
|
||
},
|
||
}
|
||
|
||
approvalFormSchemaCmd := &cobra.Command{
|
||
Use: "form-schema", Short: "查询审批模板的表单 Schema",
|
||
Example: "dws oa approval form-schema --process-code <processCode>",
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "process-code"); err != nil {
|
||
return err
|
||
}
|
||
return callMCPTool("get_process_schema", map[string]any{"processCode": mustGetFlag(cmd, "process-code")})
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalFormSchemaCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "get_process_schema",
|
||
CanonicalPath: "oa.get_process_schema",
|
||
CLIPath: "oa approval form-schema",
|
||
PrimaryCLIPath: "oa approval form-schema",
|
||
},
|
||
Description: "查询审批模板的表单 Schema",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "get_process_schema"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "查询审批模板的表单 Schema",
|
||
UseWhen: []string{"已从 list-forms 或 search-forms 获得 processCode,需要读取字段、选项和必填规则后再填写审批时"},
|
||
AvoidWhen: []string{"只需列出可用模板时使用 list-forms;不要把返回的 Schema 当作可直接提交的实例请求"},
|
||
Examples: []string{"dws oa approval form-schema --process-code <processCode>"},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "process-code", Property: "processCode"},
|
||
},
|
||
},
|
||
})
|
||
approvalForecastCmd := &cobra.Command{
|
||
Use: "forecast-process", Short: "根据表单值预测审批流程与自选节点",
|
||
Example: "dws oa approval forecast-process --process-code <processCode> --dept-id -1 --form-values '{\"金额\":\"100\"}'",
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if raw, _ := cmd.Flags().GetString("request"); raw != "" {
|
||
request, err := decodeOARequest(raw)
|
||
if err != nil {
|
||
return fmt.Errorf("--request JSON 解析失败: %w", err)
|
||
}
|
||
return callMCPTool("forecast_process", map[string]any{"ProcessForecastPopRequest": request})
|
||
}
|
||
if err := validateRequiredFlags(cmd, "process-code", "dept-id", "form-values"); err != nil {
|
||
return err
|
||
}
|
||
deptID, err := strconv.ParseInt(mustGetFlag(cmd, "dept-id"), 10, 64)
|
||
if err != nil {
|
||
return fmt.Errorf("--dept-id 必须为整数: %w", err)
|
||
}
|
||
values, err := oaFormValues(mustGetFlag(cmd, "form-values"))
|
||
if err != nil {
|
||
return fmt.Errorf("--form-values JSON 解析失败: %w", err)
|
||
}
|
||
return callMCPTool("forecast_process", map[string]any{"ProcessForecastPopRequest": map[string]any{"processCode": mustGetFlag(cmd, "process-code"), "deptId": deptID, "formComponentValues": [][]map[string]string{values}}})
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalForecastCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "forecast_process",
|
||
CanonicalPath: "oa.forecast_process",
|
||
CLIPath: "oa approval forecast-process",
|
||
PrimaryCLIPath: "oa approval forecast-process",
|
||
},
|
||
Description: "根据表单值预测审批流程与自选节点",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "forecast_process"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "预测审批流程与自选审批节点",
|
||
UseWhen: []string{"已知道 processCode 且已根据表单 Schema 组装字段,需要在发起前确认审批路径或自选节点时"},
|
||
AvoidWhen: []string{"需要真正创建审批单时改用 create-instance;未获得字段定义时先用 form-schema"},
|
||
Examples: []string{
|
||
"dws oa approval forecast-process --process-code <processCode> --dept-id -1 --form-values '{\"金额\":\"100\"}'",
|
||
"dws oa approval forecast-process --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[[{\"name\":\"金额\",\"value\":\"100\"}]]}'",
|
||
},
|
||
},
|
||
// Simple-mode flags are mapping exclusions (encoded inside ProcessForecastPopRequest).
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "request", Property: "ProcessForecastPopRequest", InterfaceType: "object"},
|
||
},
|
||
},
|
||
})
|
||
approvalCreateCmd := &cobra.Command{
|
||
Use: "create-instance", Short: "发起审批实例(需要 --yes 确认)",
|
||
Example: "dws oa approval create-instance --process-code <processCode> --form-values '{\"事由\":\"测试\"}' --yes",
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if !commandDryRun(cmd) {
|
||
yes, _ := cmd.Flags().GetBool("yes")
|
||
if !yes {
|
||
return fmt.Errorf("发起审批实例会创建真实业务数据;请先核对参数,然后添加 --yes 确认执行")
|
||
}
|
||
}
|
||
var request map[string]any
|
||
if raw, _ := cmd.Flags().GetString("request"); raw != "" {
|
||
var err error
|
||
request, err = decodeOARequest(raw)
|
||
if err != nil {
|
||
return fmt.Errorf("--request JSON 解析失败: %w", err)
|
||
}
|
||
} else {
|
||
if err := validateRequiredFlags(cmd, "process-code", "form-values"); err != nil {
|
||
return err
|
||
}
|
||
values, err := oaFormValues(mustGetFlag(cmd, "form-values"))
|
||
if err != nil {
|
||
return fmt.Errorf("--form-values JSON 解析失败: %w", err)
|
||
}
|
||
request = map[string]any{"processCode": mustGetFlag(cmd, "process-code"), "formComponentValues": values}
|
||
if dept, _ := cmd.Flags().GetString("dept-id"); dept != "" {
|
||
value, err := strconv.ParseInt(dept, 10, 64)
|
||
if err != nil {
|
||
return fmt.Errorf("--dept-id 必须为整数: %w", err)
|
||
}
|
||
request["deptId"] = value
|
||
}
|
||
if userID, _ := cmd.Flags().GetString("originator-user-id"); userID != "" {
|
||
request["originatorUserId"] = userID
|
||
}
|
||
if rawApprovers, _ := cmd.Flags().GetString("approvers"); rawApprovers != "" {
|
||
action, _ := cmd.Flags().GetString("approvers-action-type")
|
||
if action != "AND" && action != "OR" && action != "NONE" {
|
||
return fmt.Errorf("--approvers-action-type 必须为 AND、OR 或 NONE")
|
||
}
|
||
request["approvers"] = []map[string]any{{"actionType": action, "userIds": strings.Split(rawApprovers, ",")}}
|
||
}
|
||
if rawCC, _ := cmd.Flags().GetString("cc-list"); rawCC != "" {
|
||
position, _ := cmd.Flags().GetString("cc-position")
|
||
if position != "START" && position != "FINISH" && position != "START_FINISH" {
|
||
return fmt.Errorf("--cc-position 必须为 START、FINISH 或 START_FINISH")
|
||
}
|
||
request["ccList"] = strings.Split(rawCC, ",")
|
||
request["ccPosition"] = position
|
||
}
|
||
}
|
||
return callMCPTool("start_process_instance", map[string]any{"ProcessInstanceCreationPopRequest": request})
|
||
},
|
||
}
|
||
DeclareLeafMetadata(approvalCreateCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "write", Risk: "high",
|
||
Confirmation: "user_required", Idempotency: "non_idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "oa",
|
||
Name: "start_process_instance",
|
||
CanonicalPath: "oa.start_process_instance",
|
||
CLIPath: "oa approval create-instance",
|
||
PrimaryCLIPath: "oa approval create-instance",
|
||
},
|
||
Description: "发起审批实例(需要 --yes 确认)",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "oa", RPCName: "start_process_instance"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "发起新的审批实例",
|
||
UseWhen: []string{"用户确认要发起审批,且已查询表单 Schema、核对字段及审批路径后使用"},
|
||
AvoidWhen: []string{"只需预测流程时使用 forecast-process;用户尚未确认或字段未按 Schema 核对时不要发起"},
|
||
Examples: []string{
|
||
"dws oa approval create-instance --process-code <processCode> --form-values '{\"事由\":\"测试\"}'",
|
||
"dws oa approval create-instance --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[{\"name\":\"事由\",\"value\":\"测试\"}],\"targetSelectActioners\":[{\"actionerKey\":\"manual-node\",\"actionerStaffIds\":[\"user-id\"]}]}'",
|
||
},
|
||
},
|
||
// Simple-mode flags are mapping exclusions (encoded inside ProcessInstanceCreationPopRequest).
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "request", Property: "ProcessInstanceCreationPopRequest", InterfaceType: "object"},
|
||
},
|
||
},
|
||
})
|
||
|
||
approvalListPendingCmd.Flags().String("start", "", "开始时间 ISO-8601 (如 2026-03-10T00:00:00+08:00) (必填)")
|
||
approvalListPendingCmd.Flags().String("end", "", "结束时间 ISO-8601 (如 2026-03-10T23:59:59+08:00) (必填)")
|
||
approvalListPendingCmd.Flags().String("page", "", "分页页码 (可选)")
|
||
approvalListPendingCmd.Flags().String("limit", "", "每页大小 (可选)")
|
||
approvalListPendingCmd.Flags().String("size", "", "每页大小 (可选)")
|
||
approvalListPendingCmd.Flags().Lookup("size").Hidden = true
|
||
approvalListPendingCmd.Flags().String("query", "", "关键字搜索(可选)")
|
||
|
||
approvalDetailCmd.Flags().String("instance-id", "", "审批实例 ID (必填)")
|
||
approvalApproveCmd.Flags().String("instance-id", "", "审批实例 ID (必填)")
|
||
approvalApproveCmd.Flags().String("task-id", "", "审批任务 ID (必填)")
|
||
approvalApproveCmd.Flags().String("remark", "", "审批意见 (可选)")
|
||
approvalRejectCmd.Flags().String("instance-id", "", "审批实例 ID (必填)")
|
||
approvalRejectCmd.Flags().String("task-id", "", "审批任务 ID (必填)")
|
||
approvalRejectCmd.Flags().String("remark", "", "审批意见 (可选)")
|
||
approvalRevokeCmd.Flags().String("instance-id", "", "审批实例 ID (必填)")
|
||
approvalRevokeCmd.Flags().String("remark", "", "撤销说明 (可选)")
|
||
approvalRecordsCmd.Flags().String("instance-id", "", "审批实例 ID (必填)")
|
||
approvalListInitiatedCmd.Flags().String("process-code", "", "表单 processCode (必填)")
|
||
approvalListInitiatedCmd.Flags().String("start", "", "开始时间 ISO-8601 (如 2026-03-10T00:00:00+08:00) (必填)")
|
||
approvalListInitiatedCmd.Flags().String("end", "", "结束时间 ISO-8601 (如 2026-03-10T23:59:59+08:00) (必填)")
|
||
approvalListInitiatedCmd.Flags().String("cursor", "0", "分页游标,首次传 0")
|
||
approvalListInitiatedCmd.Flags().String("next-token", "0", "分页游标,首次传 0")
|
||
approvalListInitiatedCmd.Flags().Lookup("next-token").Hidden = true
|
||
approvalListInitiatedCmd.Flags().String("limit", "20", "每页大小,最大 20")
|
||
approvalListInitiatedCmd.Flags().String("max-results", "20", "每页大小,最大 20")
|
||
approvalListInitiatedCmd.Flags().Lookup("max-results").Hidden = true
|
||
approvalTasksCmd.Flags().String("instance-id", "", "审批实例 ID (必填)")
|
||
approvalListFormsCmd.Flags().String("cursor", "0", "分页游标(默认 0,翻页传返回的 cursor)")
|
||
approvalListFormsCmd.Flags().String("limit", "100", "每页大小(默认 100,最大 100)")
|
||
approvalListFormsCmd.Flags().String("size", "100", "每页大小(默认 100,最大 100)")
|
||
approvalListFormsCmd.Flags().Lookup("size").Hidden = true
|
||
approvalSearchFormsCmd.Flags().String("query", "", "关键字(匹配 processCode 或表单名称)(必填)")
|
||
approvalDingInfoCmd.Flags().String("task-id", "", "审批任务 ID (必填)")
|
||
approvalExecutedListCmd.Flags().String("page", "1", "分页页码(可选)")
|
||
approvalExecutedListCmd.Flags().String("limit", "20", "每页大小(可选)")
|
||
approvalExecutedListCmd.Flags().String("query", "", "关键字搜索(可选)")
|
||
approvalSubmittedListCmd.Flags().String("page", "1", "分页页码(可选)")
|
||
approvalSubmittedListCmd.Flags().String("limit", "20", "每页大小(可选)")
|
||
approvalSubmittedListCmd.Flags().String("query", "", "关键字搜索(可选)")
|
||
approvalCcListCmd.Flags().String("page", "1", "分页页码(可选)")
|
||
approvalCcListCmd.Flags().String("limit", "20", "每页大小(可选)")
|
||
approvalCcListCmd.Flags().String("query", "", "关键字搜索(可选)")
|
||
approvalTransferCmd.Flags().String("task-id", "", "审批任务 ID (必填)")
|
||
approvalTransferCmd.Flags().String("to-actioner-id", "", "转交目标用户 ID (必填)")
|
||
approvalTransferCmd.Flags().String("remark", "", "转交说明 (可选)")
|
||
approvalCommentCmd.Flags().String("instance-id", "", "审批实例 ID (必填)")
|
||
approvalCommentCmd.Flags().String("content", "", "评论内容 (必填)")
|
||
approvalCommentCmd.Flags().String("text", "", "评论内容 (必填)")
|
||
approvalCommentCmd.Flags().Lookup("text").Hidden = true
|
||
approvalCcCmd.Flags().String("instance-id", "", "审批实例 ID (必填)")
|
||
approvalCcCmd.Flags().String("users", "", "抄送用户 ID 列表,多个用逗号分隔 (必填)")
|
||
approvalCcCmd.Flags().String("user-list", "", "抄送用户 ID 列表,多个用逗号分隔 (必填)")
|
||
approvalCcCmd.Flags().Lookup("user-list").Hidden = true
|
||
approvalCcCmd.Flags().String("operator-id", "", "操作人 ID (可选)")
|
||
|
||
approvalAppendTaskCmd.Flags().String("instance-id", "", "审批实例 ID (必填)")
|
||
approvalAppendTaskCmd.Flags().String("task-id", "", "审批任务 ID (必填)")
|
||
approvalAppendTaskCmd.Flags().String("type", "", "加签类型:before(前加签),after(后加签),Parallel(并加签)(必填)")
|
||
approvalAppendTaskCmd.Flags().String("appender-user-ids", "", "被加签用户 ID 列表,多个用逗号分隔 (必填)")
|
||
approvalAppendTaskCmd.Flags().String("activate-type", "", "任务激活类型:ALL(或签),ONE_BY_ONE(依次审批)(必填)")
|
||
approvalAppendTaskCmd.Flags().String("agree-all", "", "是否需要全部同意,true 或 false (必填)")
|
||
|
||
approvalRevertActivitiesCmd.Flags().String("task-id", "", "审批任务 ID (必填)")
|
||
|
||
approvalRevertTaskCmd.Flags().String("instance-id", "", "审批实例 ID (必填)")
|
||
approvalRevertTaskCmd.Flags().String("task-id", "", "审批任务 ID (必填)")
|
||
approvalRevertTaskCmd.Flags().String("target-activity-id", "", "退回到的节点 ID(退回发起人固定传 sid-startevent)(必填)")
|
||
approvalRevertTaskCmd.Flags().String("action", "", "退回方式:REVERT_FOR_APPROVAL(退回到审批人)/ REVERT_FOR_RESUBMIT(退回到发起人)(必填)")
|
||
approvalRevertTaskCmd.Flags().String("remark", "", "退回说明 (可选)")
|
||
approvalFormSchemaCmd.Flags().String("process-code", "", "审批模板 processCode (必填)")
|
||
approvalForecastCmd.Flags().String("process-code", "", "审批模板 processCode(简单模式使用;与 --request 互斥)")
|
||
approvalForecastCmd.Flags().String("dept-id", "", "发起人部门 ID(简单模式使用;与 --request 互斥)")
|
||
approvalForecastCmd.Flags().String("form-values", "", "表单值 JSON(简单模式使用;与 --request 互斥)")
|
||
approvalForecastCmd.Flags().String("request", "", "完整请求 JSON(高级模式;与简单模式参数互斥)")
|
||
approvalForecastCmd.MarkFlagsOneRequired("request", "process-code")
|
||
approvalForecastCmd.MarkFlagsRequiredTogether("process-code", "dept-id", "form-values")
|
||
forecastMutuallyExclusive := make([][]string, 0, 3)
|
||
for _, name := range []string{"process-code", "dept-id", "form-values"} {
|
||
approvalForecastCmd.MarkFlagsMutuallyExclusive("request", name)
|
||
forecastMutuallyExclusive = append(forecastMutuallyExclusive, []string{"request", name})
|
||
}
|
||
cli.AnnotateRuntimeConstraints(approvalForecastCmd, cli.RuntimeSchemaConstraints{
|
||
MutuallyExclusive: forecastMutuallyExclusive,
|
||
RequireOneOf: [][]string{{"request", "process-code"}},
|
||
RequireTogether: [][]string{{"process-code", "dept-id", "form-values"}},
|
||
})
|
||
|
||
approvalCreateCmd.Flags().String("process-code", "", "审批模板 processCode(简单模式使用;与 --request 互斥)")
|
||
approvalCreateCmd.Flags().String("dept-id", "-1", "发起人部门 ID")
|
||
approvalCreateCmd.Flags().String("form-values", "", "表单值 JSON(简单模式使用;与 --request 互斥)")
|
||
approvalCreateCmd.Flags().String("request", "", "完整请求 JSON(高级模式;与简单模式参数互斥)")
|
||
approvalCreateCmd.Flags().String("originator-user-id", "", "审批发起人 userId")
|
||
approvalCreateCmd.Flags().String("approvers", "", "审批人 userId 列表,多个用逗号分隔")
|
||
approvalCreateCmd.Flags().String("approvers-action-type", "OR", "审批类型:AND、OR 或 NONE")
|
||
approvalCreateCmd.Flags().String("cc-list", "", "抄送人 userId 列表,多个用逗号分隔")
|
||
approvalCreateCmd.Flags().String("cc-position", "START", "抄送时点:START、FINISH 或 START_FINISH")
|
||
approvalCreateCmd.MarkFlagsOneRequired("request", "process-code")
|
||
approvalCreateCmd.MarkFlagsRequiredTogether("process-code", "form-values")
|
||
createSimpleFlags := []string{"process-code", "dept-id", "form-values", "originator-user-id", "approvers", "approvers-action-type", "cc-list", "cc-position"}
|
||
createMutuallyExclusive := make([][]string, 0, len(createSimpleFlags))
|
||
for _, name := range createSimpleFlags {
|
||
approvalCreateCmd.MarkFlagsMutuallyExclusive("request", name)
|
||
createMutuallyExclusive = append(createMutuallyExclusive, []string{"request", name})
|
||
}
|
||
cli.AnnotateRuntimeConstraints(approvalCreateCmd, cli.RuntimeSchemaConstraints{
|
||
MutuallyExclusive: createMutuallyExclusive,
|
||
RequireOneOf: [][]string{{"request", "process-code"}},
|
||
RequireTogether: [][]string{{"process-code", "form-values"}},
|
||
})
|
||
|
||
approvalCmd.AddCommand(
|
||
approvalListPendingCmd,
|
||
approvalDetailCmd,
|
||
approvalApproveCmd,
|
||
approvalRejectCmd,
|
||
approvalRevokeCmd,
|
||
approvalRecordsCmd,
|
||
approvalListInitiatedCmd,
|
||
approvalTasksCmd,
|
||
approvalListFormsCmd,
|
||
approvalSearchFormsCmd,
|
||
approvalDingInfoCmd,
|
||
approvalExecutedListCmd,
|
||
approvalSubmittedListCmd,
|
||
approvalCcListCmd,
|
||
approvalTransferCmd,
|
||
approvalCommentCmd,
|
||
approvalCcCmd,
|
||
approvalAppendTaskCmd,
|
||
approvalRevertActivitiesCmd,
|
||
approvalRevertTaskCmd,
|
||
approvalFormSchemaCmd,
|
||
approvalForecastCmd,
|
||
approvalCreateCmd,
|
||
)
|
||
approvalCmd.AddCommand(newOAAttachmentCommand())
|
||
root.AddCommand(approvalCmd)
|
||
|
||
return root
|
||
}
|