606 lines
26 KiB
Go
606 lines
26 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.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Package oa registers declarative shortcuts for the DingTalk OA approval
|
|
// service, wrapping the raw MCP tools exposed by the dws oa helper.
|
|
package oa
|
|
|
|
import (
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut"
|
|
)
|
|
|
|
// ListPending — 查询待我处理的审批 (list_pending_approvals)
|
|
var ListPending = shortcut.Shortcut{
|
|
Service: "oa",
|
|
Command: "+list-pending",
|
|
Product: "oa",
|
|
Description: "查询待我处理的审批(时间范围为 epoch 毫秒)",
|
|
Intent: "当你想知道自己当前有哪些审批还没处理、需要清理审批待办或按时间段/关键字盘点待审批单时使用;传入起止时间(epoch 毫秒,可选关键字与分页),返回待我审批的实例列表,是后续 +get 查详情、+approve/+reject 处理的入口。",
|
|
Risk: shortcut.RiskRead,
|
|
Safety: contract.SafetySpec{
|
|
Effect: "read", Risk: "low",
|
|
Confirmation: "not_required", Idempotency: "idempotent",
|
|
},
|
|
Contract: corecmd.ContractDecl{
|
|
Identity: contract.ToolIdentitySpec{
|
|
ProductID: "oa",
|
|
Name: "shortcut_list_pending",
|
|
CanonicalPath: "oa.shortcut_list_pending",
|
|
CLIPath: "oa +list-pending",
|
|
PrimaryCLIPath: "oa +list-pending",
|
|
},
|
|
Description: "查询待我处理的审批(时间范围为 epoch 毫秒)",
|
|
Interface: &contract.InterfaceSpec{
|
|
Mode: "composite",
|
|
Availability: "available",
|
|
Reason: "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
|
|
},
|
|
Selection: contract.SelectionSpec{
|
|
AgentSummary: "查询待我处理的审批(时间范围为 epoch 毫秒)",
|
|
UseWhen: []string{"当你想知道自己当前有哪些审批还没处理、需要清理审批待办或按时间段/关键字盘点待审批单时使用;传入起止时间(epoch 毫秒,可选关键字与分页),返回待我审批的实例列表,是后续 +get 查详情、+approve/+reject 处理的入口。"},
|
|
AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
|
|
Examples: []string{"dws oa +list-pending --start 1741536000000 --end 1741622399000 --query 报销"},
|
|
},
|
|
},
|
|
Flags: []shortcut.Flag{
|
|
{Name: "start", Type: shortcut.FlagInt, Desc: "开始时间(epoch 毫秒)", Required: true},
|
|
{Name: "end", Type: shortcut.FlagInt, Desc: "结束时间(epoch 毫秒)", Required: true},
|
|
{Name: "page", Type: shortcut.FlagString, Desc: "分页页码"},
|
|
{Name: "limit", Type: shortcut.FlagString, Desc: "每页大小"},
|
|
{Name: "query", Type: shortcut.FlagString, Desc: "关键字搜索"},
|
|
},
|
|
Tips: []string{`dws oa +list-pending --start 1741536000000 --end 1741622399000 --query 报销`},
|
|
Execute: func(rt *shortcut.RuntimeContext) error {
|
|
params := map[string]any{
|
|
"starTime": rt.Int("start"),
|
|
"endTime": rt.Int("end"),
|
|
}
|
|
if rt.Changed("page") {
|
|
params["pageNum"] = rt.Str("page")
|
|
}
|
|
if rt.Changed("limit") {
|
|
params["pageSize"] = rt.Str("limit")
|
|
}
|
|
if rt.Changed("query") {
|
|
params["query"] = rt.Str("query")
|
|
}
|
|
data, err := rt.CallMCPData("oa", "list_pending_approvals", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
instances := listPendingProject(data)
|
|
return rt.Output(map[string]any{"count": len(instances), "instances": instances})
|
|
},
|
|
}
|
|
|
|
// listPendingProject reshapes the raw list_pending_approvals response into the
|
|
// same clean {processInstanceId, title, status, createTime} approval list as
|
|
// +list-executed/+list-submitted, so all approval-instance listings project
|
|
// identically. It reuses the shared oaInstance* defensive probes, tolerating
|
|
// response-shape and key-spelling drift.
|
|
func listPendingProject(data map[string]any) []map[string]any {
|
|
raw := oaInstanceResolveList(data)
|
|
out := make([]map[string]any, 0, len(raw))
|
|
for _, item := range raw {
|
|
m, ok := item.(map[string]any)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if row := oaInstanceProjectItem(m); len(row) > 0 {
|
|
out = append(out, row)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Detail — 获取审批实例详情 (get_processInstance_detail)
|
|
// Approve — 同意审批 (approve_processInstance)
|
|
// Reject — 拒绝审批 (reject_processInstance)
|
|
// Revoke — 撤销已发起的审批 (revoke_processInstance)
|
|
// Records — 获取审批操作记录 (get_processInstance_records)
|
|
// ListInitiated — 查询审批模板下已发起的审批记录 (list_initiated_instances)
|
|
// ListTasks — 查询待我审批的任务 ID (list_pending_tasks)
|
|
// ListForms — 获取当前用户可见的审批表单列表 (list_user_visible_process)
|
|
var ListForms = shortcut.Shortcut{
|
|
Service: "oa",
|
|
Command: "+list-forms",
|
|
Product: "oa",
|
|
Description: "获取当前用户可见的审批表单列表",
|
|
Intent: "当你想浏览自己有权限发起哪些审批模板、或需要为 +list-initiated 等操作枚举 processCode 时使用;无需关键字,按游标分页返回当前用户可见的全部审批表单,适合不确定表单名称时先整体看一遍。",
|
|
Risk: shortcut.RiskRead,
|
|
Safety: contract.SafetySpec{
|
|
Effect: "read", Risk: "low",
|
|
Confirmation: "not_required", Idempotency: "idempotent",
|
|
},
|
|
Contract: corecmd.ContractDecl{
|
|
Identity: contract.ToolIdentitySpec{
|
|
ProductID: "oa",
|
|
Name: "shortcut_list_forms",
|
|
CanonicalPath: "oa.shortcut_list_forms",
|
|
CLIPath: "oa +list-forms",
|
|
PrimaryCLIPath: "oa +list-forms",
|
|
},
|
|
Description: "获取当前用户可见的审批表单列表",
|
|
Interface: &contract.InterfaceSpec{
|
|
Mode: "composite",
|
|
Availability: "available",
|
|
Reason: "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
|
|
},
|
|
Selection: contract.SelectionSpec{
|
|
AgentSummary: "获取当前用户可见的审批表单列表",
|
|
UseWhen: []string{"当你想浏览自己有权限发起哪些审批模板、或需要为 +list-initiated 等操作枚举 processCode 时使用;无需关键字,按游标分页返回当前用户可见的全部审批表单,适合不确定表单名称时先整体看一遍。"},
|
|
AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
|
|
Examples: []string{"dws oa +list-forms --cursor 0 --limit 100"},
|
|
},
|
|
},
|
|
Flags: []shortcut.Flag{
|
|
{Name: "cursor", Type: shortcut.FlagInt, Default: "0", Desc: "分页游标,首次传 0"},
|
|
{Name: "limit", Type: shortcut.FlagInt, Default: "100", Desc: "每页大小,最大 100"},
|
|
},
|
|
Tips: []string{`dws oa +list-forms --cursor 0 --limit 100`},
|
|
Execute: func(rt *shortcut.RuntimeContext) error {
|
|
data, err := rt.CallMCPData("oa", "list_user_visible_process", map[string]any{
|
|
"cursor": rt.Int("cursor"),
|
|
"pageSize": rt.Int("limit"),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
forms := listFormsProject(data)
|
|
return rt.Output(map[string]any{"count": len(forms), "forms": forms})
|
|
},
|
|
}
|
|
|
|
// listFormsProject reshapes the raw list_user_visible_process response into a
|
|
// clean approval-form list ({processCode, name, iconUrl}) — the output-projection
|
|
// fidelity the framework applies to every list command. The list container and
|
|
// per-item field names are probed defensively across candidate keys so the
|
|
// projection tolerates response-shape drift rather than crashing or fabricating.
|
|
func listFormsProject(data map[string]any) []map[string]any {
|
|
raw := oaFormResolveList(data)
|
|
out := make([]map[string]any, 0, len(raw))
|
|
for _, item := range raw {
|
|
m, ok := item.(map[string]any)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if row := oaFormProjectItem(m); len(row) > 0 {
|
|
out = append(out, row)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// oaFormResolveList locates the form list inside a response, tolerating a bare
|
|
// top-level array or nesting one level under a common envelope key.
|
|
func oaFormResolveList(data map[string]any) []any {
|
|
if data == nil {
|
|
return []any{}
|
|
}
|
|
// list_user_visible_process nests the forms under result.processCodeList;
|
|
// probing must include that exact key (both at the top level in case the
|
|
// envelope is already unwrapped, and one level deeper under result/data) or
|
|
// the whole list silently projects to empty.
|
|
for _, key := range []string{"result", "data", "list", "items", "processList", "processCodeList", "forms"} {
|
|
v, ok := data[key]
|
|
if !ok {
|
|
continue
|
|
}
|
|
if arr, ok := v.([]any); ok {
|
|
return arr
|
|
}
|
|
if inner, ok := v.(map[string]any); ok {
|
|
for _, ik := range []string{"list", "items", "processList", "processCodeList", "forms", "result", "data"} {
|
|
if arr, ok := inner[ik].([]any); ok {
|
|
return arr
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return []any{}
|
|
}
|
|
|
|
// oaFormProjectItem picks the stable identity/label fields of a single approval
|
|
// form, probing candidate key spellings so it survives snake/camel drift.
|
|
func oaFormProjectItem(m map[string]any) map[string]any {
|
|
row := map[string]any{}
|
|
if v, ok := oaFormFirst(m, "processCode", "process_code", "code"); ok {
|
|
row["processCode"] = v
|
|
}
|
|
if v, ok := oaFormFirst(m, "name", "processName", "process_name", "flowTitle"); ok {
|
|
row["name"] = v
|
|
}
|
|
if v, ok := oaFormFirst(m, "iconUrl", "icon_url", "iconName"); ok {
|
|
row["iconUrl"] = v
|
|
}
|
|
return row
|
|
}
|
|
|
|
// oaFormFirst returns the first present candidate key's value.
|
|
func oaFormFirst(m map[string]any, keys ...string) (any, bool) {
|
|
for _, k := range keys {
|
|
if v, ok := m[k]; ok {
|
|
return v, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// SearchForms — 按关键字模糊搜索可见审批表单 (search_form)
|
|
var SearchForms = shortcut.Shortcut{
|
|
Service: "oa",
|
|
Command: "+search-forms",
|
|
Product: "oa",
|
|
Description: "按关键字模糊搜索当前用户可见的审批表单",
|
|
Intent: "当你已知想找的审批大致名称(如「报销」「请假」)、想快速定位对应表单及其 processCode 时使用,比 +list-forms 全量列举更高效;传入关键字,返回名称或 processCode 匹配的表单,供后续 +list-initiated 按模板查询。",
|
|
Risk: shortcut.RiskRead,
|
|
Safety: contract.SafetySpec{
|
|
Effect: "read", Risk: "low",
|
|
Confirmation: "not_required", Idempotency: "idempotent",
|
|
},
|
|
Contract: corecmd.ContractDecl{
|
|
Identity: contract.ToolIdentitySpec{
|
|
ProductID: "oa",
|
|
Name: "shortcut_search_forms",
|
|
CanonicalPath: "oa.shortcut_search_forms",
|
|
CLIPath: "oa +search-forms",
|
|
PrimaryCLIPath: "oa +search-forms",
|
|
},
|
|
Description: "按关键字模糊搜索当前用户可见的审批表单",
|
|
Interface: &contract.InterfaceSpec{
|
|
Mode: "composite",
|
|
Availability: "available",
|
|
Reason: "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
|
|
},
|
|
Selection: contract.SelectionSpec{
|
|
AgentSummary: "按关键字模糊搜索当前用户可见的审批表单",
|
|
UseWhen: []string{"当你已知想找的审批大致名称(如「报销」「请假」)、想快速定位对应表单及其 processCode 时使用,比 +list-forms 全量列举更高效;传入关键字,返回名称或 processCode 匹配的表单,供后续 +list-initiated 按模板查询。"},
|
|
AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
|
|
Examples: []string{"dws oa +search-forms --query 报销"},
|
|
},
|
|
},
|
|
Flags: []shortcut.Flag{
|
|
{Name: "query", Type: shortcut.FlagString, Desc: "关键字(匹配 processCode 或表单名称)", Required: true},
|
|
},
|
|
Tips: []string{`dws oa +search-forms --query 报销`},
|
|
Execute: func(rt *shortcut.RuntimeContext) error {
|
|
data, err := rt.CallMCPData("oa", "search_form", map[string]any{
|
|
"query": rt.Str("query"),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
forms := searchFormsProject(data)
|
|
return rt.Output(map[string]any{"count": len(forms), "forms": forms})
|
|
},
|
|
}
|
|
|
|
// searchFormsProject reshapes the raw search_form response into the same clean
|
|
// {processCode, name, iconUrl} list as +list-forms, so both approval-form
|
|
// listings project identically. It reuses the shared oaForm* defensive probes,
|
|
// tolerating response-shape and key-spelling drift.
|
|
func searchFormsProject(data map[string]any) []map[string]any {
|
|
raw := oaFormResolveList(data)
|
|
out := make([]map[string]any, 0, len(raw))
|
|
for _, item := range raw {
|
|
m, ok := item.(map[string]any)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if row := oaFormProjectItem(m); len(row) > 0 {
|
|
out = append(out, row)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// DingInfo — 获取审批任务的被催办人 userId (oa_ding_user)
|
|
// ListExecuted — 获取当前用户已处理过的审批单列表 (get_done_tasks)
|
|
var ListExecuted = shortcut.Shortcut{
|
|
Service: "oa",
|
|
Command: "+list-executed",
|
|
Product: "oa",
|
|
Description: "获取当前用户已经处理过的审批单列表",
|
|
Intent: "当你想回顾自己历史上审批过(已同意/拒绝等)的单子、做复盘或查找某条已办审批时使用,区别于 +list-pending 的待办;按页码/关键字分页返回我已处理的审批单。",
|
|
Risk: shortcut.RiskRead,
|
|
Safety: contract.SafetySpec{
|
|
Effect: "read", Risk: "low",
|
|
Confirmation: "not_required", Idempotency: "idempotent",
|
|
},
|
|
Contract: corecmd.ContractDecl{
|
|
Identity: contract.ToolIdentitySpec{
|
|
ProductID: "oa",
|
|
Name: "shortcut_list_executed",
|
|
CanonicalPath: "oa.shortcut_list_executed",
|
|
CLIPath: "oa +list-executed",
|
|
PrimaryCLIPath: "oa +list-executed",
|
|
},
|
|
Description: "获取当前用户已经处理过的审批单列表",
|
|
Interface: &contract.InterfaceSpec{
|
|
Mode: "composite",
|
|
Availability: "available",
|
|
Reason: "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
|
|
},
|
|
Selection: contract.SelectionSpec{
|
|
AgentSummary: "获取当前用户已经处理过的审批单列表",
|
|
UseWhen: []string{"当你想回顾自己历史上审批过(已同意/拒绝等)的单子、做复盘或查找某条已办审批时使用,区别于 +list-pending 的待办;按页码/关键字分页返回我已处理的审批单。"},
|
|
AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
|
|
Examples: []string{"dws oa +list-executed --limit 20 --page 1 --query 报销"},
|
|
},
|
|
},
|
|
Flags: []shortcut.Flag{
|
|
{Name: "page", Type: shortcut.FlagString, Default: "1", Desc: "分页页码"},
|
|
{Name: "limit", Type: shortcut.FlagString, Default: "20", Desc: "每页大小"},
|
|
{Name: "query", Type: shortcut.FlagString, Desc: "关键字搜索"},
|
|
},
|
|
Tips: []string{`dws oa +list-executed --limit 20 --page 1 --query 报销`},
|
|
Execute: func(rt *shortcut.RuntimeContext) error {
|
|
params := map[string]any{
|
|
"pageNumber": rt.Str("page"),
|
|
"pageSize": rt.Str("limit"),
|
|
}
|
|
if rt.Changed("query") {
|
|
params["query"] = rt.Str("query")
|
|
}
|
|
data, err := rt.CallMCPData("oa", "get_done_tasks", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
instances := listExecutedProject(data)
|
|
return rt.Output(map[string]any{"count": len(instances), "instances": instances})
|
|
},
|
|
}
|
|
|
|
// listExecutedProject reshapes the raw get_done_tasks response into a clean
|
|
// approval-instance list ({processInstanceId, title, status, createTime}) — the
|
|
// the clean output projection applied to every list command. The
|
|
// list container and per-item field names are probed defensively across
|
|
// candidate keys so the projection tolerates response-shape drift rather than
|
|
// crashing or fabricating data.
|
|
func listExecutedProject(data map[string]any) []map[string]any {
|
|
raw := oaInstanceResolveList(data)
|
|
out := make([]map[string]any, 0, len(raw))
|
|
for _, item := range raw {
|
|
m, ok := item.(map[string]any)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if row := oaInstanceProjectItem(m); len(row) > 0 {
|
|
out = append(out, row)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// oaInstanceResolveList locates the approval-instance list inside a response,
|
|
// tolerating a bare top-level array or nesting one level under a common
|
|
// envelope key.
|
|
func oaInstanceResolveList(data map[string]any) []any {
|
|
if data == nil {
|
|
return []any{}
|
|
}
|
|
// The approval instance tools (list_pending_approvals, get_done_tasks,
|
|
// get_submitted_instances, get_noticed_instances) all nest the instance list
|
|
// under result.values; "values" MUST be in the probe set or every one of
|
|
// +list-pending / +list-executed / +list-submitted / +list-cc silently
|
|
// projects to empty despite the backend returning records.
|
|
for _, key := range []string{"result", "data", "list", "items", "values", "instances", "tasks"} {
|
|
v, ok := data[key]
|
|
if !ok {
|
|
continue
|
|
}
|
|
if arr, ok := v.([]any); ok {
|
|
return arr
|
|
}
|
|
if inner, ok := v.(map[string]any); ok {
|
|
for _, ik := range []string{"list", "items", "values", "instances", "tasks", "result", "data"} {
|
|
if arr, ok := inner[ik].([]any); ok {
|
|
return arr
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return []any{}
|
|
}
|
|
|
|
// oaInstanceProjectItem picks the stable identity/label fields of a single
|
|
// approval instance, probing candidate key spellings so it survives
|
|
// snake/camel drift.
|
|
func oaInstanceProjectItem(m map[string]any) map[string]any {
|
|
row := map[string]any{}
|
|
if v, ok := oaFormFirst(m, "processInstanceId", "process_instance_id", "instanceId", "id"); ok {
|
|
row["processInstanceId"] = v
|
|
}
|
|
if v, ok := oaFormFirst(m, "title", "processTitle", "process_title", "name"); ok {
|
|
row["title"] = v
|
|
}
|
|
if v, ok := oaFormFirst(m, "status", "result", "processResult"); ok {
|
|
row["status"] = v
|
|
}
|
|
if v, ok := oaFormFirst(m, "createTime", "create_time", "gmtCreate", "createdTime"); ok {
|
|
row["createTime"] = v
|
|
}
|
|
return row
|
|
}
|
|
|
|
// ListSubmitted — 获取当前用户已发起的审批单列表 (get_submitted_instances)
|
|
var ListSubmitted = shortcut.Shortcut{
|
|
Service: "oa",
|
|
Command: "+list-submitted",
|
|
Product: "oa",
|
|
Description: "获取当前用户已发起的审批单列表",
|
|
Intent: "当你想查看自己提交发起的审批单及其审批进度(如某笔报销/请假审到哪一步)时使用;按页码/关键字分页返回我发起的审批单,可据此决定是否 +revoke 撤销或催办。",
|
|
Risk: shortcut.RiskRead,
|
|
Safety: contract.SafetySpec{
|
|
Effect: "read", Risk: "low",
|
|
Confirmation: "not_required", Idempotency: "idempotent",
|
|
},
|
|
Contract: corecmd.ContractDecl{
|
|
Identity: contract.ToolIdentitySpec{
|
|
ProductID: "oa",
|
|
Name: "shortcut_list_submitted",
|
|
CanonicalPath: "oa.shortcut_list_submitted",
|
|
CLIPath: "oa +list-submitted",
|
|
PrimaryCLIPath: "oa +list-submitted",
|
|
},
|
|
Description: "获取当前用户已发起的审批单列表",
|
|
Interface: &contract.InterfaceSpec{
|
|
Mode: "composite",
|
|
Availability: "available",
|
|
Reason: "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
|
|
},
|
|
Selection: contract.SelectionSpec{
|
|
AgentSummary: "获取当前用户已发起的审批单列表",
|
|
UseWhen: []string{"当你想查看自己提交发起的审批单及其审批进度(如某笔报销/请假审到哪一步)时使用;按页码/关键字分页返回我发起的审批单,可据此决定是否 +revoke 撤销或催办。"},
|
|
AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
|
|
Examples: []string{"dws oa +list-submitted --limit 20 --page 1 --query 报销"},
|
|
},
|
|
},
|
|
Flags: []shortcut.Flag{
|
|
{Name: "page", Type: shortcut.FlagString, Default: "1", Desc: "分页页码"},
|
|
{Name: "limit", Type: shortcut.FlagString, Default: "20", Desc: "每页大小"},
|
|
{Name: "query", Type: shortcut.FlagString, Desc: "关键字搜索"},
|
|
},
|
|
Tips: []string{`dws oa +list-submitted --limit 20 --page 1 --query 报销`},
|
|
Execute: func(rt *shortcut.RuntimeContext) error {
|
|
params := map[string]any{
|
|
"pageNumber": rt.Str("page"),
|
|
"pageSize": rt.Str("limit"),
|
|
}
|
|
if rt.Changed("query") {
|
|
params["query"] = rt.Str("query")
|
|
}
|
|
data, err := rt.CallMCPData("oa", "get_submitted_instances", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
instances := listSubmittedProject(data)
|
|
return rt.Output(map[string]any{"count": len(instances), "instances": instances})
|
|
},
|
|
}
|
|
|
|
// listSubmittedProject reshapes the raw get_submitted_instances response into
|
|
// the same clean {processInstanceId, title, status, createTime} approval list
|
|
// as +list-executed, so both instance listings project identically. It reuses
|
|
// the shared oaInstance* defensive probes, tolerating response-shape and
|
|
// key-spelling drift.
|
|
func listSubmittedProject(data map[string]any) []map[string]any {
|
|
raw := oaInstanceResolveList(data)
|
|
out := make([]map[string]any, 0, len(raw))
|
|
for _, item := range raw {
|
|
m, ok := item.(map[string]any)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if row := oaInstanceProjectItem(m); len(row) > 0 {
|
|
out = append(out, row)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// ListCc — 获取抄送当前用户的审批单列表 (get_noticed_instances)
|
|
var ListCc = shortcut.Shortcut{
|
|
Service: "oa",
|
|
Command: "+list-cc",
|
|
Product: "oa",
|
|
Description: "获取抄送当前用户的审批单列表",
|
|
Intent: "当你想查看抄送给自己、需要知悉但无需审批的单子时使用;按页码/关键字分页返回抄送我的审批单列表,适合了解与自己相关但不用自己动手处理的审批动态。",
|
|
Risk: shortcut.RiskRead,
|
|
Safety: contract.SafetySpec{
|
|
Effect: "read", Risk: "low",
|
|
Confirmation: "not_required", Idempotency: "idempotent",
|
|
},
|
|
Contract: corecmd.ContractDecl{
|
|
Identity: contract.ToolIdentitySpec{
|
|
ProductID: "oa",
|
|
Name: "shortcut_list_cc",
|
|
CanonicalPath: "oa.shortcut_list_cc",
|
|
CLIPath: "oa +list-cc",
|
|
PrimaryCLIPath: "oa +list-cc",
|
|
},
|
|
Description: "获取抄送当前用户的审批单列表",
|
|
Interface: &contract.InterfaceSpec{
|
|
Mode: "composite",
|
|
Availability: "available",
|
|
Reason: "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
|
|
},
|
|
Selection: contract.SelectionSpec{
|
|
AgentSummary: "获取抄送当前用户的审批单列表",
|
|
UseWhen: []string{"当你想查看抄送给自己、需要知悉但无需审批的单子时使用;按页码/关键字分页返回抄送我的审批单列表,适合了解与自己相关但不用自己动手处理的审批动态。"},
|
|
AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
|
|
Examples: []string{"dws oa +list-cc --limit 20 --page 1 --query 报销"},
|
|
},
|
|
},
|
|
Flags: []shortcut.Flag{
|
|
{Name: "page", Type: shortcut.FlagString, Default: "1", Desc: "分页页码"},
|
|
{Name: "limit", Type: shortcut.FlagString, Default: "20", Desc: "每页大小"},
|
|
{Name: "query", Type: shortcut.FlagString, Desc: "关键字搜索"},
|
|
},
|
|
Tips: []string{`dws oa +list-cc --limit 20 --page 1 --query 报销`},
|
|
Execute: func(rt *shortcut.RuntimeContext) error {
|
|
params := map[string]any{
|
|
"pageNumber": rt.Str("page"),
|
|
"pageSize": rt.Str("limit"),
|
|
}
|
|
if rt.Changed("query") {
|
|
params["query"] = rt.Str("query")
|
|
}
|
|
data, err := rt.CallMCPData("oa", "get_noticed_instances", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
instances := listCcProject(data)
|
|
return rt.Output(map[string]any{"count": len(instances), "instances": instances})
|
|
},
|
|
}
|
|
|
|
// listCcProject reshapes the raw get_noticed_instances response into the same
|
|
// clean {processInstanceId, title, status, createTime} approval list as the
|
|
// other instance listings, so all approval-instance listings project
|
|
// identically. It reuses the shared oaInstance* defensive probes, tolerating
|
|
// response-shape and key-spelling drift.
|
|
func listCcProject(data map[string]any) []map[string]any {
|
|
raw := oaInstanceResolveList(data)
|
|
out := make([]map[string]any, 0, len(raw))
|
|
for _, item := range raw {
|
|
m, ok := item.(map[string]any)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if row := oaInstanceProjectItem(m); len(row) > 0 {
|
|
out = append(out, row)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// RedirectTask — 转交审批任务给其他人 (redirect_task)
|
|
// Comment — 对审批实例添加评论 (dingflow_comments)
|
|
// CcNotice — 对审批实例进行抄送 (oa_cc_noticer)
|
|
// AppendTask — 对审批任务进行加签 (append_task)
|
|
// RevertActivities — 获取审批任务可回退的节点信息 (get_inst_revert_activities)
|
|
// RevertTask — 退回审批任务到指定节点 (revert_task)
|
|
func init() {
|
|
shortcut.Register(
|
|
ListPending,
|
|
ListForms,
|
|
SearchForms,
|
|
ListExecuted,
|
|
ListSubmitted,
|
|
ListCc,
|
|
)
|
|
}
|