1011 lines
44 KiB
Go
1011 lines
44 KiB
Go
package helpers
|
||
|
||
import (
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"github.com/spf13/cobra"
|
||
)
|
||
|
||
// ──────────────────────────────────────────────────────────
|
||
// dws contact — 通讯录
|
||
// ──────────────────────────────────────────────────────────
|
||
|
||
func parseCSVValues(raw string) []string {
|
||
raw = strings.TrimSpace(raw)
|
||
if raw == "" {
|
||
return nil
|
||
}
|
||
if strings.HasPrefix(raw, "[") && strings.HasSuffix(raw, "]") {
|
||
var values []string
|
||
if err := json.Unmarshal([]byte(raw), &values); err == nil {
|
||
return cleanStringValues(values)
|
||
}
|
||
raw = strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(raw, "["), "]"))
|
||
}
|
||
parts := strings.Split(raw, ",")
|
||
return cleanStringValues(parts)
|
||
}
|
||
|
||
func cleanStringValues(parts []string) []string {
|
||
values := make([]string, 0, len(parts))
|
||
for _, p := range parts {
|
||
v := strings.Trim(strings.TrimSpace(p), `"'`)
|
||
if v != "" {
|
||
values = append(values, v)
|
||
}
|
||
}
|
||
return values
|
||
}
|
||
|
||
// contactUserIDFlagKeys 汇总 contact user get 支持的所有 flag 名(含 camelCase 派生与全小写写法),
|
||
// 在 RunE 中统一引用,避免每个调用点重复维护别名列表。
|
||
// camelCase 版本 --userId / --userIds 由 RegisterCamelCaseAliases 自动派生,--userid 为手写全小写别名。
|
||
var contactUserIDFlagKeys = []string{"ids", "user-id", "user-ids", "userId", "userIds", "userid"}
|
||
|
||
// contactRootDeptLikeTokens 是用户/模型常写错的"根部门占位符"。钉钉根部门 deptId 恒为 1。
|
||
// 在 contactParseInt64WithAliases 与 list-members 的 CSV 解析里命中这类值时,给出就近提示,避免调用方再去猜。
|
||
var contactRootDeptLikeTokens = map[string]struct{}{
|
||
"self": {}, "me": {}, "root": {}, "0": {},
|
||
}
|
||
|
||
// contactFirstSetFlagName 返回 names 中第一个被用户显式传入的 flag 名(Changed=true)。
|
||
// 用于让报错文案显示用户实际输入的 flag 名,而不是主 flag 名,避免
|
||
// 出现 "用户传 --ids me 却被报 flag --id 不合法" 的错位。
|
||
func contactFirstSetFlagName(cmd *cobra.Command, names ...string) string {
|
||
for _, n := range names {
|
||
if f := cmd.Flag(n); f != nil && f.Changed {
|
||
return n
|
||
}
|
||
}
|
||
if len(names) > 0 {
|
||
return names[0]
|
||
}
|
||
return ""
|
||
}
|
||
|
||
func contactAnyFlagChanged(cmd *cobra.Command, names ...string) bool {
|
||
for _, n := range names {
|
||
if f := cmd.Flag(n); f != nil && f.Changed {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
// contactParseInt64WithAliases 先在主 flag 与全部别名中找出用户实际传入的值(空则报 missing),
|
||
// 再走根部门占位符警告 + int64 解析,避免用户传别名时 RunE 读不到。
|
||
// 报错文案中使用用户实际输入的 flag 名(比如用户传 --ids me,错误里显示 --ids 而不是主 flag --id),
|
||
// 防止用户/LLM 被"我明明没传这个 flag 为啥报它"的错位文案带偏。
|
||
func contactParseInt64WithAliases(cmd *cobra.Command, primary string, aliases ...string) (int64, error) {
|
||
if err := validateRequiredFlagWithAliases(cmd, primary, aliases...); err != nil {
|
||
return 0, err
|
||
}
|
||
raw := strings.TrimSpace(flagOrFallback(cmd, primary, aliases...))
|
||
setName := contactFirstSetFlagName(cmd, append([]string{primary}, aliases...)...)
|
||
if _, ok := contactRootDeptLikeTokens[strings.ToLower(raw)]; ok {
|
||
return 0, fmt.Errorf(
|
||
"flag --%s 必须是整数;钉钉根部门 deptId=1,请使用 --%s 1", setName, setName)
|
||
}
|
||
v, err := strconv.ParseInt(raw, 10, 64)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("flag --%s must be an integer: %w", setName, err)
|
||
}
|
||
return v, nil
|
||
}
|
||
|
||
func newContactCommand() *cobra.Command {
|
||
root := &cobra.Command{
|
||
Use: "contact",
|
||
Short: "通讯录 / 用户 / 部门 / 人员关系",
|
||
Long: `查询钉钉通讯录:用户搜索、手机号查找、部门搜索、子部门 / 成员列表、人员关系;用户花名册档案信息(学历、家庭、银行卡、合同等)与离职员工信息。
|
||
|
||
通讯录功能:
|
||
- contact user get-self/search/search-mobile/get: 通讯录用户查询
|
||
- contact user invite: 邀请员工加入企业
|
||
- contact dept search/get-info/list-children/list-members: 部门查询
|
||
- contact relation list-my-followings: 特别关注人查询
|
||
|
||
企业管理功能:
|
||
- contact org create: 创建企业
|
||
- contact account create: 创建企业专属账号
|
||
|
||
基础人事功能(HR 花名册):
|
||
- contact user profile fields/get: 员工花名册档案查询(学历、家庭、银行卡等)
|
||
- contact user dismission search: 离职员工列表查询`,
|
||
RunE: groupRunE,
|
||
}
|
||
|
||
userCmd := &cobra.Command{
|
||
Use: "user",
|
||
Short: "人员管理",
|
||
Long: `人员管理:通讯录用户查询、邀请员工加入企业、用户档案(花名册)查询、离职员工查询。
|
||
|
||
【何时用哪个命令】
|
||
- 查询用户的部门、主管、管理员权限 → contact user get
|
||
- 邀请员工加入企业 → contact user invite
|
||
- 查询用户的学历、家庭、银行卡、合同等档案 → contact user profile get
|
||
- 查询离职员工列表 → contact user dismission search`,
|
||
RunE: groupRunE,
|
||
}
|
||
|
||
contactUserGetSelfCmd := &cobra.Command{
|
||
Use: "get-self",
|
||
Aliases: []string{"self", "me", "whoami", "current"},
|
||
Short: "获取当前用户信息(我是谁 / 本人)",
|
||
Long: "获取当前登录用户的 userId 与基本信息。\n\n触发词:我是谁 / 我的信息 / 我的 userId / 当前用户 / 本人 / self / me / whoami / current。\n别名:self / me / whoami / current 均等价于 get-self。\n无需参数;禁止用 `dws contact user get --ids me/self` 代替(会返回空数据的假成功)。",
|
||
Example: ` dws contact user get-self
|
||
dws contact user self # 别名(等价)
|
||
dws contact user me # 别名(等价)`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
return callMCPTool("get_current_user_profile", nil)
|
||
},
|
||
}
|
||
|
||
relationCmd := &cobra.Command{Use: "relation",
|
||
Short: "人员关系查询",
|
||
Long: `查询钉钉人员关系:特别关注人。`,
|
||
RunE: groupRunE}
|
||
|
||
contactRelationListMyFollowingsCmd := &cobra.Command{
|
||
Use: "list-my-followings",
|
||
Short: "获取当前用户的特别关注列表",
|
||
Example: ` dws contact relation list-my-followings`,
|
||
Args: cobra.NoArgs,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
return callMCPTool("list_my_followings", nil)
|
||
},
|
||
}
|
||
|
||
contactUserSearchCmd := &cobra.Command{
|
||
Use: "search",
|
||
Short: "按关键词搜索用户",
|
||
Example: ` dws contact user search --query "张三"`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
// 兼容 --query / --keyword / --name 三种写法(--name 为隐藏别名,对齐 dept search)。
|
||
if err := validateRequiredFlagWithAliases(cmd, "query", "keyword", "name"); err != nil {
|
||
return err
|
||
}
|
||
kw := flagOrFallback(cmd, "query", "keyword", "name")
|
||
return callMCPTool("search_contact_by_key_word", map[string]any{
|
||
"keyword": kw,
|
||
})
|
||
},
|
||
}
|
||
|
||
contactUserSearchMobileCmd := &cobra.Command{
|
||
Use: "search-mobile",
|
||
Short: "按手机号搜索用户",
|
||
Example: ` dws contact user search-mobile --mobile 13800138000`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "mobile"); err != nil {
|
||
return err
|
||
}
|
||
return callMCPTool("search_user_by_mobile", map[string]any{
|
||
"mobile": mustGetFlag(cmd, "mobile"),
|
||
})
|
||
},
|
||
}
|
||
|
||
contactUserGetCmd := &cobra.Command{
|
||
Use: "get",
|
||
Short: "批量获取用户详情(组织管理信息)",
|
||
Long: `批量获取用户详情,返回用户的组织管理信息(来自通讯录领域)。
|
||
|
||
返回字段:
|
||
- isAdmin: 是否为管理员
|
||
- orgEmployeeModel.orgUserId / orgUserName: 用户 ID / 姓名
|
||
- orgEmployeeModel.orgName / orgId: 所属组织名称 / ID
|
||
- orgEmployeeModel.orgMasterUserId / orgMasterDisplayName: 直属主管
|
||
- orgEmployeeModel.depts: 所属部门列表(含 deptId、deptName)
|
||
- orgEmployeeModel.labels: 角色列表
|
||
|
||
【适用场景】
|
||
- 想知道某个用户在哪个部门、上级是谁、是不是管理员
|
||
|
||
【不适用场景】
|
||
- 查询学历、家庭、银行卡、合同、紧急联系人等档案信息 → 请用 contact user profile get`,
|
||
Example: ` dws contact user get --ids userId1,userId2 # 查询 userId: dws contact user search --query "姓名"`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlagWithAliases(cmd, contactUserIDFlagKeys[0], contactUserIDFlagKeys[1:]...); err != nil {
|
||
return err
|
||
}
|
||
raw := flagOrFallback(cmd, contactUserIDFlagKeys[0], contactUserIDFlagKeys[1:]...)
|
||
// 拦截“假 userId”:me/self/current/whoami/i/me 代替真实 userId 会得到空数据的假成功。
|
||
for _, part := range parseCSVValues(raw) {
|
||
switch strings.ToLower(strings.TrimSpace(part)) {
|
||
case "me", "self", "current", "whoami", "i":
|
||
return fmt.Errorf("--ids 需要真实的 userId,不接受 %q 这类占位符\n hint: 获取当前用户用: dws contact user get-self", part)
|
||
}
|
||
}
|
||
return callMCPTool("get_user_info_by_user_ids", map[string]any{
|
||
"user_id_list": parseCSVValues(raw),
|
||
})
|
||
},
|
||
}
|
||
|
||
// ── label 角色 ──────────────────────────────────────────────────
|
||
|
||
contactLabelCmd := &cobra.Command{
|
||
Use: "label",
|
||
Aliases: []string{"role"},
|
||
Short: "角色查询",
|
||
Long: `角色查询:获取企业所有角色列表、根据角色名称查询角色ID、根据角色ID查询角色下的成员。
|
||
|
||
【何时用哪个命令】
|
||
- 获取企业所有角色列表 → contact label list
|
||
- 根据角色名称查询角色ID → contact label get
|
||
- 根据角色ID查询角色下的成员 → contact label list-members
|
||
|
||
【典型场景:查询某类角色的人员(如主管、管理员、财务等)】
|
||
1. contact label list → 获取企业全部角色列表
|
||
2. 从返回结果中匹配目标角色名称及 labelId
|
||
3. contact label list-members --id <labelId> → 获取该角色下的成员`,
|
||
RunE: groupRunE,
|
||
}
|
||
|
||
runContactLabelList := func(cmd *cobra.Command, args []string) error {
|
||
if len(args) > 0 {
|
||
return fmt.Errorf("contact label list 不接受位置参数: %s", strings.Join(args, " "))
|
||
}
|
||
return callMCPTool("get_org_labels", map[string]any{})
|
||
}
|
||
runContactLabelGet := func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlagWithAliases(cmd, "names", "name", "query", "keyword"); err != nil {
|
||
return err
|
||
}
|
||
raw := flagOrFallback(cmd, "names", "name", "query", "keyword")
|
||
return callMCPTool("search_label_by_name", map[string]any{
|
||
"labelNames": parseCSVValues(raw),
|
||
})
|
||
}
|
||
runContactLabelMembers := func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlagWithAliases(cmd, "id", "label-id", "role-id"); err != nil {
|
||
return err
|
||
}
|
||
return callMCPTool("get_label_members_by_labelId", map[string]any{
|
||
"labelId": flagOrFallback(cmd, "id", "label-id", "role-id"),
|
||
})
|
||
}
|
||
|
||
contactLabelGetCmd := &cobra.Command{
|
||
Use: "get",
|
||
Short: "根据角色名称查询角色",
|
||
Long: `根据角色名称精确匹配查询角色信息(角色ID、名称等)。支持同时查询多个角色名称,逗号分隔。无需分页。
|
||
|
||
注意:精确匹配可能无结果(如用户输入"管理员"但企业只有"主管理员"和"子管理员"),
|
||
此时应降级使用 label list 获取全部角色列表,从中模糊匹配包含关键词的角色。`,
|
||
Example: ` dws contact label get --names "管理员"
|
||
dws contact label get --names "管理员,财务"`,
|
||
RunE: runContactLabelGet,
|
||
}
|
||
|
||
contactLabelListMembersCmd := &cobra.Command{
|
||
Use: "list-members",
|
||
Short: "查询角色下的成员",
|
||
Long: `根据角色ID查询该角色下的成员列表。`,
|
||
Example: ` dws contact label list-members --id 12345 # 查询 labelId: dws contact label get --names "角色名"`,
|
||
RunE: runContactLabelMembers,
|
||
}
|
||
|
||
contactLabelGetCmd.Flags().String("names", "", "角色名称,逗号分隔 (必填)")
|
||
contactLabelGetCmd.Flags().String("name", "", "--names 的别名")
|
||
contactLabelGetCmd.Flags().String("query", "", "--names 的别名")
|
||
contactLabelGetCmd.Flags().String("keyword", "", "--names 的别名")
|
||
_ = contactLabelGetCmd.Flags().MarkHidden("name")
|
||
_ = contactLabelGetCmd.Flags().MarkHidden("query")
|
||
_ = contactLabelGetCmd.Flags().MarkHidden("keyword")
|
||
|
||
contactLabelListMembersCmd.Flags().String("id", "", "角色 ID (必填)")
|
||
contactLabelListMembersCmd.Flags().String("label-id", "", "--id 的别名")
|
||
contactLabelListMembersCmd.Flags().String("role-id", "", "--id 的别名")
|
||
_ = contactLabelListMembersCmd.Flags().MarkHidden("label-id")
|
||
_ = contactLabelListMembersCmd.Flags().MarkHidden("role-id")
|
||
|
||
contactLabelListAllCmd := &cobra.Command{
|
||
Use: "list",
|
||
Short: "获取企业所有角色列表",
|
||
Long: `获取当前企业的所有角色(标签)列表,返回角色ID、角色名称等信息。无需参数。
|
||
|
||
用于不知道准确角色名称时,先列出全部角色,再根据需要选择目标角色查询成员。
|
||
|
||
【典型场景】
|
||
- 用户说"企业所有主管/查所有管理员/财务人员有哪些"
|
||
→ 先 label list 浏览全部角色,匹配目标角色后 label list-members 获取成员`,
|
||
Example: ` dws contact label list`,
|
||
RunE: runContactLabelList,
|
||
}
|
||
|
||
contactLabelCmd.AddCommand(contactLabelListAllCmd, contactLabelGetCmd, contactLabelListMembersCmd)
|
||
|
||
contactDeptCmd := &cobra.Command{Use: "dept", Short: "部门查询", RunE: groupRunE}
|
||
|
||
contactDeptSearchCmd := &cobra.Command{
|
||
Use: "search",
|
||
Short: "搜索部门",
|
||
Example: ` dws contact dept search --query "技术部"`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlagWithAliases(cmd, "query", "keyword", "name"); err != nil {
|
||
return err
|
||
}
|
||
return callMCPTool("search_dept_by_keyword", map[string]any{
|
||
"query": flagOrFallback(cmd, "query", "keyword", "name"),
|
||
})
|
||
},
|
||
}
|
||
|
||
contactDeptListChildrenCmd := &cobra.Command{
|
||
Use: "list-children",
|
||
Short: "查看子部门",
|
||
Example: ` dws contact dept list-children --dept 12345`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
// list-children 主 flag 为 --dept;接受 --id / --ids / --dept-id / --dept-ids 作为别名。
|
||
deptID, err := contactParseInt64WithAliases(cmd, "dept", "id", "ids", "dept-id", "dept-ids", "deptId", "deptIds")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return callMCPTool("get_sub_depts_by_dept_id", map[string]any{
|
||
"deptId": deptID,
|
||
})
|
||
},
|
||
}
|
||
|
||
contactDeptGetInfoCmd := &cobra.Command{
|
||
Use: "get-info",
|
||
Short: "获取部门详情(部门ID、名称、人数)",
|
||
Example: ` dws contact dept get-info --dept 12345 # 查询 deptId: dws contact dept search`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
// get-info 主 flag 为 --dept;接受 --id / --ids / --dept-id / --dept-ids 作为别名。
|
||
deptID, err := contactParseInt64WithAliases(cmd, "dept", "id", "ids", "dept-id", "dept-ids", "deptId", "deptIds")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return callMCPTool("get_dept_info_by_dept_id", map[string]any{
|
||
"deptId": deptID,
|
||
})
|
||
},
|
||
}
|
||
|
||
contactDeptListMembersCmd := &cobra.Command{
|
||
Use: "list-members",
|
||
Short: "查看部门成员(仅本部门,不含下级)",
|
||
Long: "查看指定部门的成员列表。\n\n范围:仅返回传入 deptId 的**本部门**直接成员,**不递归下级部门**。\n跨层级需求:先 'dws contact dept list-children --dept <父deptId>' 枚举子部门,再对子 deptId 分别或合并调用本命令。",
|
||
Example: ` dws contact dept list-members --depts 12345,67890 # 查询 deptId: dws contact dept search 或 dws contact dept list-children`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
// list-members 主 flag 为 --depts;接受 --ids / --id / --dept-id / --dept-ids 作为别名。
|
||
if err := validateRequiredFlagWithAliases(cmd, "depts", "ids", "id", "dept-id", "dept-ids", "deptId", "deptIds"); err != nil {
|
||
return err
|
||
}
|
||
raw := flagOrFallback(cmd, "depts", "ids", "id", "dept-id", "dept-ids", "deptId", "deptIds")
|
||
// 拦截逗号分隔列表中的根部门占位符(self/me/root/0),提示应用 --depts 1。
|
||
// 报错里显示用户实际输入的 flag 名,避免出现 "用户传 --id self 却被报 --depts 不合法" 的错位。
|
||
setName := contactFirstSetFlagName(cmd, "depts", "ids", "id", "dept-id", "dept-ids", "deptId", "deptIds")
|
||
for _, t := range parseCSVValues(raw) {
|
||
if _, ok := contactRootDeptLikeTokens[strings.ToLower(strings.TrimSpace(t))]; ok {
|
||
return fmt.Errorf(
|
||
"flag --%s 包含非法占位符 %q;钉钉根部门 deptId=1,请使用 --%s 1", setName, t, setName)
|
||
}
|
||
}
|
||
return callMCPTool("get_dept_members_by_deptId", map[string]any{
|
||
"deptIds": parseCSVValues(raw),
|
||
})
|
||
},
|
||
}
|
||
|
||
// ── user profile 用户档案(花名册) ────────────────────────────────────
|
||
contactUserProfileCmd := &cobra.Command{
|
||
Use: "profile",
|
||
Short: "用户档案(花名册)",
|
||
Long: `用户档案(花名册):查询花名册字段列表、查询员工花名册字段信息。
|
||
|
||
花名册字段包含:试用/转正信息、个人/家庭信息、学历信息、银行卡/合同信息、
|
||
紧急联系人和其他企业自定义信息。
|
||
|
||
【与 contact user get 的区别】
|
||
- contact user get: 组织管理信息(部门、主管、管理员权限)
|
||
- contact user profile get: 个人档案信息(学历、家庭、银行卡等)`,
|
||
RunE: groupRunE,
|
||
}
|
||
|
||
contactUserProfileFieldsCmd := &cobra.Command{
|
||
Use: "fields",
|
||
Short: "查询花名册有权限的字段列表",
|
||
Long: `查询花名册有权限的字段列表,根据当前用户查询花名册有权限的字段列表。
|
||
|
||
花名册字段包含:试用/转正信息、个人/家庭信息、学历信息、银行卡/合同信息、
|
||
紧急联系人和其他企业自定义信息。
|
||
|
||
认证信息(corpId、optUserId)由系统自动注入,无需手动传入。
|
||
|
||
【典型用法】
|
||
通常作为 contact user profile get 的前置步骤,用于获取可查询的字段 code 列表。`,
|
||
Example: ` dws contact user profile fields`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
return callMCPToolOnServer("hrmregister", "list_authorized_roster_fields", map[string]any{})
|
||
},
|
||
}
|
||
|
||
contactUserProfileGetCmd := &cobra.Command{
|
||
Use: "get",
|
||
Short: "查询员工花名册字段信息(个人档案)",
|
||
Long: `查询员工花名册字段信息,根据当前用户指定员工和字段列表,查询相应管理范围内员工的字段值信息。
|
||
|
||
花名册字段包含:试用/转正信息、个人/家庭信息、学历信息、银行卡/合同信息、
|
||
紧急联系人和其他企业自定义信息。
|
||
|
||
返回字段枚举说明:
|
||
- employeeType 员工类型:0 无类型,1 全职,2 兼职,3 实习,4 劳务派遣,5 退休返聘,6 劳务外包
|
||
- employeeStatus 员工状态:-1 无状态,1 待入职,2 试用,3 正式,4 离职,5 待离职,6 试岗,7 已退休
|
||
|
||
认证信息(corpId、optUserId)由系统自动注入,无需手动传入。
|
||
--staff-id 为查询员工 ID,--fields 为指定字段集合(逗号分隔),可通过
|
||
contact user profile fields 获取可用字段列表。
|
||
|
||
【适用场景】
|
||
- 查询某员工的学历、家庭、银行卡、紧急联系人、合同等档案信息
|
||
|
||
【不适用场景】
|
||
- 查询用户的部门、主管、管理员权限 → 请用 contact user get`,
|
||
Example: ` dws contact user profile get --staff-id STAFF_ID
|
||
dws contact user profile get --staff-id STAFF_ID --fields fieldCode1,fieldCode2`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
params := map[string]any{}
|
||
if v := mustGetFlag(cmd, "staff-id"); v != "" {
|
||
params["staffId"] = v
|
||
}
|
||
if v, _ := cmd.Flags().GetString("fields"); v != "" {
|
||
fieldCodes := parseCSVValues(v)
|
||
if len(fieldCodes) > 0 {
|
||
params["fieldCodeList"] = fieldCodes
|
||
}
|
||
}
|
||
return callMCPToolOnServer("hrmregister", "get_authorized_emp_rosterInfo", params)
|
||
},
|
||
}
|
||
contactUserProfileGetCmd.Flags().String("staff-id", "", "查询员工 ID(可选)")
|
||
contactUserProfileGetCmd.Flags().String("fields", "", "指定字段集合, 逗号分隔, 可通过 profile fields 获取(可选)")
|
||
|
||
contactUserProfileCmd.AddCommand(contactUserProfileFieldsCmd, contactUserProfileGetCmd)
|
||
|
||
// ── user dismission 离职员工 ───────────────────────────────────────────
|
||
contactUserDismissionCmd := &cobra.Command{
|
||
Use: "dismission",
|
||
Short: "离职员工查询",
|
||
Long: `离职员工查询:分页获取离职员工列表,支持按员工姓名、离职时间范围、部门进行过滤。`,
|
||
RunE: groupRunE,
|
||
}
|
||
|
||
contactUserDismissionSearchCmd := &cobra.Command{
|
||
Use: "search",
|
||
Short: "分页获取离职员工列表",
|
||
Long: `分页获取离职员工列表,支持按员工姓名、离职时间范围、部门进行过滤。
|
||
|
||
认证信息(corpId、optUserId)由系统自动注入,无需手动传入。
|
||
--name 员工姓名,模糊搜索(可选)
|
||
--start 离职日期查询范围开始,格式 YYYY-MM-DD(可选)
|
||
--end 离职日期查询范围结束,格式 YYYY-MM-DD(可选)
|
||
--depts 部门 ID 列表,逗号分隔(可选)
|
||
--hide-retirement 是否隐藏退休,默认 true(可选)
|
||
--hide-partner 是否隐藏合作伙伴,默认 false(可选)
|
||
--page 页码,从 1 开始(可选,默认 1)
|
||
--limit 页大小,200 以内(可选,默认 20)
|
||
|
||
注意:--start 和 --end 必须同时设置或同时不设置,不允许只设置其中一个。
|
||
|
||
【适用场景】
|
||
- 查询公司离职员工名单
|
||
- 按时间范围/部门/姓名筛选离职员工
|
||
|
||
【不适用场景】
|
||
- 查询在职员工 → 使用 contact user search`,
|
||
Example: ` dws contact user dismission search
|
||
dws contact user dismission search --name "张三"
|
||
dws contact user dismission search --start 2026-01-01 --end 2026-03-31
|
||
dws contact user dismission search --depts 123456,789012 --page 1 --limit 50`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
startStr, _ := cmd.Flags().GetString("start")
|
||
endStr, _ := cmd.Flags().GetString("end")
|
||
if (startStr == "") != (endStr == "") {
|
||
return fmt.Errorf("--start 和 --end 必须同时设置或同时不设置")
|
||
}
|
||
searchVO := map[string]any{}
|
||
if v, _ := cmd.Flags().GetString("name"); v != "" {
|
||
searchVO["empName"] = v
|
||
}
|
||
if startStr != "" {
|
||
ts, err := parseDateToTimestamp(startStr, "start")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
searchVO["startDate"] = ts
|
||
}
|
||
if endStr != "" {
|
||
ts, err := parseDateToTimestamp(endStr, "end")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
searchVO["endDate"] = ts
|
||
}
|
||
if v, _ := cmd.Flags().GetString("depts"); v != "" {
|
||
searchVO["depts"] = parseCSVInts(v)
|
||
}
|
||
if cmd.Flags().Changed("hide-retirement") {
|
||
v, _ := cmd.Flags().GetBool("hide-retirement")
|
||
searchVO["hideRetirement"] = v
|
||
}
|
||
if cmd.Flags().Changed("hide-partner") {
|
||
v, _ := cmd.Flags().GetBool("hide-partner")
|
||
searchVO["hidePartner"] = v
|
||
}
|
||
params := map[string]any{
|
||
"searchVO": searchVO,
|
||
}
|
||
if v, _ := cmd.Flags().GetInt("page"); v > 0 {
|
||
params["pageNum"] = v
|
||
}
|
||
if v, _ := cmd.Flags().GetInt("limit"); v > 0 {
|
||
params["pageSize"] = v
|
||
}
|
||
return callMCPToolOnServer("hrmregister", "query_dismission_employee_list", params)
|
||
},
|
||
}
|
||
contactUserDismissionSearchCmd.Flags().String("name", "", "员工姓名,模糊搜索(可选)")
|
||
contactUserDismissionSearchCmd.Flags().String("start", "", "离职日期查询范围开始,格式 YYYY-MM-DD(可选),与end要么都不填要么都填")
|
||
contactUserDismissionSearchCmd.Flags().String("end", "", "离职日期查询范围结束,格式 YYYY-MM-DD(可选),与start要么都不填要么都填")
|
||
contactUserDismissionSearchCmd.Flags().String("depts", "", "部门 ID 列表,逗号分隔(可选)")
|
||
contactUserDismissionSearchCmd.Flags().Bool("hide-retirement", true, "是否隐藏退休,默认 true(可选)")
|
||
contactUserDismissionSearchCmd.Flags().Bool("hide-partner", false, "是否隐藏合作伙伴,默认 false(可选)")
|
||
contactUserDismissionSearchCmd.Flags().Int("page", 1, "页码,从 1 开始(可选)")
|
||
contactUserDismissionSearchCmd.Flags().Int("limit", 20, "页大小,200 以内(可选)")
|
||
|
||
contactUserDismissionCmd.AddCommand(contactUserDismissionSearchCmd)
|
||
|
||
contactUserInviteCmd := &cobra.Command{
|
||
Use: "invite",
|
||
Short: "邀请员工加入企业",
|
||
Long: `通过手机号邀请单个员工加入当前企业。
|
||
|
||
参数:
|
||
--org-user-name 员工在企业内的名称
|
||
--org-user-mobile 员工手机号
|
||
--depts 员工所属部门列表 JSON 数组,格式: [{"deptId":1}]
|
||
|
||
认证信息(corpId、optUserId)由系统自动注入,无需手动传入。`,
|
||
Example: ` dws contact user invite --org-user-name "张三" --org-user-mobile "13800138000" --depts '[{"deptId":1}]'`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "org-user-name", "org-user-mobile"); err != nil {
|
||
return err
|
||
}
|
||
deptsJSON := mustGetFlag(cmd, "depts")
|
||
var depts []map[string]any
|
||
if deptsJSON != "" {
|
||
if err := json.Unmarshal([]byte(deptsJSON), &depts); err != nil {
|
||
return fmt.Errorf("--depts JSON 解析失败: %w\n hint: 正确格式: [{\"deptId\":1}]", err)
|
||
}
|
||
}
|
||
name := strings.TrimSpace(mustGetFlag(cmd, "org-user-name"))
|
||
mobile := strings.TrimSpace(mustGetFlag(cmd, "org-user-mobile"))
|
||
if name == "" || mobile == "" {
|
||
return fmt.Errorf("--org-user-name 和 --org-user-mobile 不能为空")
|
||
}
|
||
return callMCPTool("add_employee", map[string]any{
|
||
"orgUserName": name,
|
||
"orgUserMobile": mobile,
|
||
"depts": depts,
|
||
})
|
||
},
|
||
}
|
||
contactUserInviteCmd.Flags().String("org-user-name", "", "员工在企业内的名称 (必填)")
|
||
contactUserInviteCmd.Flags().String("org-user-mobile", "", "员工手机号 (必填)")
|
||
contactUserInviteCmd.Flags().String("depts", "", "员工所属部门列表 JSON 数组(可选),格式: [{\"deptId\":1}]")
|
||
|
||
// ── flags 注册 ───────────────────────────────────────────────
|
||
contactUserSearchCmd.Flags().String("query", "", "搜索关键词 (必填)")
|
||
contactUserSearchCmd.Flags().String("keyword", "", "--query 的别名")
|
||
contactUserSearchCmd.Flags().String("name", "", "--query 的别名")
|
||
_ = contactUserSearchCmd.Flags().MarkHidden("keyword")
|
||
_ = contactUserSearchCmd.Flags().MarkHidden("name")
|
||
contactUserSearchMobileCmd.Flags().String("mobile", "", "手机号 (必填)")
|
||
contactUserGetCmd.Flags().String("ids", "", "用户 ID 列表 (必填)")
|
||
contactUserGetCmd.Flags().String("user-id", "", "--ids 的别名")
|
||
contactUserGetCmd.Flags().String("user-ids", "", "--ids 的别名")
|
||
contactUserGetCmd.Flags().String("userid", "", "--ids 的别名(全小写)")
|
||
_ = contactUserGetCmd.Flags().MarkHidden("user-id")
|
||
_ = contactUserGetCmd.Flags().MarkHidden("user-ids")
|
||
_ = contactUserGetCmd.Flags().MarkHidden("userid")
|
||
userCmd.AddCommand(
|
||
contactUserGetSelfCmd, contactUserSearchCmd, contactUserSearchMobileCmd, contactUserGetCmd,
|
||
contactUserInviteCmd, // 邀请员工加入企业
|
||
contactUserProfileCmd, // 花名册档案
|
||
contactUserDismissionCmd, // 离职员工
|
||
)
|
||
|
||
contactDeptSearchCmd.Flags().String("query", "", "搜索关键词 (必填)")
|
||
contactDeptSearchCmd.Flags().String("keyword", "", "--query 的别名")
|
||
contactDeptSearchCmd.Flags().String("name", "", "--query 的别名")
|
||
_ = contactDeptSearchCmd.Flags().MarkHidden("keyword")
|
||
_ = contactDeptSearchCmd.Flags().MarkHidden("name")
|
||
// 主 flag 与 RunE 读取保持一致:get-info / list-children 用 --dept,list-members 用 --depts。
|
||
// 历史上主 flag 曾误注册为 --id/--ids,导致 RunE 读的 --dept/--depts 未注册、命令行传入报 unknown flag。
|
||
contactDeptGetInfoCmd.Flags().String("dept", "", "部门 ID (必填)")
|
||
contactDeptListChildrenCmd.Flags().String("dept", "", "部门 ID (必填)")
|
||
contactDeptListMembersCmd.Flags().String("depts", "", "部门 ID 列表 (必填)")
|
||
|
||
// dept 系列命令统一接受 --id / --ids / --dept-id / --dept-ids 别名(集中注册避免逐命令重复写)。
|
||
// camelCase --deptId / --deptIds 由 RegisterCamelCaseAliases 自动派生,无需手写。
|
||
type deptIDAliasSpec struct {
|
||
cmd *cobra.Command
|
||
aliases []string
|
||
}
|
||
for _, s := range []deptIDAliasSpec{
|
||
{contactDeptGetInfoCmd, []string{"id", "dept-id", "ids", "dept-ids"}},
|
||
{contactDeptListChildrenCmd, []string{"id", "ids", "dept-id", "dept-ids"}},
|
||
{contactDeptListMembersCmd, []string{"ids", "id", "dept-id", "dept-ids"}},
|
||
} {
|
||
for _, name := range s.aliases {
|
||
s.cmd.Flags().String(name, "", "部门 ID 别名(等价于当前命令的主 flag)")
|
||
_ = s.cmd.Flags().MarkHidden(name)
|
||
}
|
||
}
|
||
contactDeptCmd.AddCommand(contactDeptSearchCmd, contactDeptGetInfoCmd, contactDeptListChildrenCmd, contactDeptListMembersCmd)
|
||
|
||
// ── org 企业管理 ──────────────────────────────────────────────────
|
||
|
||
contactOrgCmd := &cobra.Command{
|
||
Use: "org",
|
||
Short: "企业管理",
|
||
Long: `企业管理:创建企业。
|
||
|
||
【何时用哪个命令】
|
||
- 创建新企业 → contact org create
|
||
- 创建企业专属账号 → contact account create
|
||
- 邀请员工加入企业 → contact user invite`,
|
||
RunE: groupRunE,
|
||
}
|
||
|
||
contactOrgCreateCmd := &cobra.Command{
|
||
Use: "create",
|
||
Short: "创建企业",
|
||
Long: `创建一个新的钉钉企业。需提供企业名称和当前用户在企业内的名称(作为创建者)。
|
||
|
||
认证信息(corpId、optUserId)由系统自动注入,无需手动传入。`,
|
||
Example: ` dws contact org create --org-name "我的企业" --creator-username "张三"`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "org-name", "creator-username"); err != nil {
|
||
return err
|
||
}
|
||
orgName := strings.TrimSpace(mustGetFlag(cmd, "org-name"))
|
||
creatorUsername := strings.TrimSpace(mustGetFlag(cmd, "creator-username"))
|
||
if orgName == "" || creatorUsername == "" {
|
||
return fmt.Errorf("--org-name 和 --creator-username 不能为空")
|
||
}
|
||
return callMCPTool("org_create", map[string]any{
|
||
"orgName": orgName,
|
||
"creatorUsername": creatorUsername,
|
||
})
|
||
},
|
||
}
|
||
contactOrgCreateCmd.Flags().String("org-name", "", "企业名称 (必填)")
|
||
contactOrgCreateCmd.Flags().String("creator-username", "", "创建者在企业内的名称,对应 creatorUsername (必填)")
|
||
contactOrgCmd.AddCommand(contactOrgCreateCmd)
|
||
|
||
// ── account 企业账号管理 ──────────────────────────────────────────
|
||
|
||
contactAccountCmd := &cobra.Command{
|
||
Use: "account",
|
||
Short: "企业账号管理",
|
||
Long: "企业账号管理:创建企业专属账号。",
|
||
RunE: groupRunE,
|
||
}
|
||
|
||
contactAccountCreateCmd := &cobra.Command{
|
||
Use: "create",
|
||
Short: "创建企业专属账号",
|
||
Long: `为当前企业创建一个专属登录账号。
|
||
|
||
必填:--org-user-name、--login-id
|
||
可选:--org-user-mobile、--email、--dept-ids、--send-pwd-via-sms
|
||
|
||
注意:
|
||
- 登录号(--login-id)请勿包含手机号等联系方式,否则可能被运营商拦截短信
|
||
- --send-pwd-via-sms 控制是否通过手机短信/邮件发送登录邀请
|
||
|
||
认证信息(corpId、optUserId)由系统自动注入,无需手动传入。`,
|
||
Example: ` dws contact account create --org-user-name "张三" --login-id "zhangsan001" --org-user-mobile "13800138000" --email "zhangsan@example.com" --dept-ids "1,2,3" --send-pwd-via-sms`,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlags(cmd, "org-user-name", "login-id"); err != nil {
|
||
return err
|
||
}
|
||
orgUserName := strings.TrimSpace(mustGetFlag(cmd, "org-user-name"))
|
||
loginID := strings.TrimSpace(mustGetFlag(cmd, "login-id"))
|
||
if orgUserName == "" || loginID == "" {
|
||
return fmt.Errorf("--org-user-name 和 --login-id 不能为空")
|
||
}
|
||
toolArgs := map[string]any{
|
||
"orgUserName": orgUserName,
|
||
"loginId": loginID,
|
||
}
|
||
if cmd.Flags().Changed("send-pwd-via-sms") {
|
||
sendPwdViaSMS, _ := cmd.Flags().GetBool("send-pwd-via-sms")
|
||
toolArgs["sendPwdViaSms"] = sendPwdViaSMS
|
||
}
|
||
if mobile := strings.TrimSpace(mustGetFlag(cmd, "org-user-mobile")); mobile != "" {
|
||
toolArgs["orgUserMobile"] = mobile
|
||
}
|
||
if email := strings.TrimSpace(mustGetFlag(cmd, "email")); email != "" {
|
||
toolArgs["email"] = email
|
||
}
|
||
if cmd.Flags().Changed("dept-ids") {
|
||
ids, err := parseCSVIntsStrict(mustGetFlag(cmd, "dept-ids"))
|
||
if err != nil {
|
||
return fmt.Errorf("--dept-ids 解析失败: %w", err)
|
||
}
|
||
toolArgs["deptIds"] = ids
|
||
}
|
||
return callMCPTool("exclusive_account_create", toolArgs)
|
||
},
|
||
}
|
||
contactAccountCreateCmd.Flags().String("org-user-name", "", "员工在企业内的名称 (必填)")
|
||
contactAccountCreateCmd.Flags().String("login-id", "", "登录号 (必填),请勿包含手机号")
|
||
contactAccountCreateCmd.Flags().String("org-user-mobile", "", "员工手机号(可选)")
|
||
contactAccountCreateCmd.Flags().String("email", "", "邮箱(可选)")
|
||
contactAccountCreateCmd.Flags().String("dept-ids", "", "要加入的部门 ID 列表,逗号分隔(可选)")
|
||
contactAccountCreateCmd.Flags().Bool("send-pwd-via-sms", false, "是否通过手机短信/邮件发送登录邀请(可选)")
|
||
contactAccountCmd.AddCommand(contactAccountCreateCmd)
|
||
|
||
relationCmd.AddCommand(contactRelationListMyFollowingsCmd)
|
||
root.AddCommand(userCmd, contactDeptCmd, contactLabelCmd, relationCmd, contactOrgCmd, contactAccountCmd)
|
||
|
||
addQueryFlags := func(cmd *cobra.Command) {
|
||
cmd.Flags().String("query", "", "搜索关键词 (必填)")
|
||
cmd.Flags().String("keyword", "", "--query 的别名")
|
||
cmd.Flags().String("name", "", "--query 的别名")
|
||
_ = cmd.Flags().MarkHidden("keyword")
|
||
_ = cmd.Flags().MarkHidden("name")
|
||
}
|
||
addUserIDFlags := func(cmd *cobra.Command) {
|
||
cmd.Flags().String("ids", "", "用户 ID 列表")
|
||
cmd.Flags().String("user-id", "", "--ids 的别名")
|
||
cmd.Flags().String("user-ids", "", "--ids 的别名")
|
||
cmd.Flags().String("userid", "", "--ids 的别名(全小写)")
|
||
_ = cmd.Flags().MarkHidden("user-id")
|
||
_ = cmd.Flags().MarkHidden("user-ids")
|
||
_ = cmd.Flags().MarkHidden("userid")
|
||
}
|
||
addLabelNameFlags := func(cmd *cobra.Command) {
|
||
cmd.Flags().String("names", "", "角色名称,逗号分隔")
|
||
cmd.Flags().String("name", "", "--names 的别名")
|
||
cmd.Flags().String("query", "", "--names 的别名")
|
||
cmd.Flags().String("keyword", "", "--names 的别名")
|
||
_ = cmd.Flags().MarkHidden("name")
|
||
_ = cmd.Flags().MarkHidden("query")
|
||
_ = cmd.Flags().MarkHidden("keyword")
|
||
}
|
||
addLabelIDFlags := func(cmd *cobra.Command) {
|
||
cmd.Flags().String("id", "", "角色 ID")
|
||
cmd.Flags().String("label-id", "", "--id 的别名")
|
||
cmd.Flags().String("role-id", "", "--id 的别名")
|
||
_ = cmd.Flags().MarkHidden("label-id")
|
||
_ = cmd.Flags().MarkHidden("role-id")
|
||
}
|
||
addDeptIDFlags := func(cmd *cobra.Command) {
|
||
cmd.Flags().String("dept", "", "部门 ID")
|
||
cmd.Flags().String("id", "", "--dept 的别名")
|
||
cmd.Flags().String("dept-id", "", "--dept 的别名")
|
||
cmd.Flags().String("dept-ids", "", "--dept 的别名")
|
||
_ = cmd.Flags().MarkHidden("id")
|
||
_ = cmd.Flags().MarkHidden("dept-id")
|
||
_ = cmd.Flags().MarkHidden("dept-ids")
|
||
}
|
||
runContactUserGet := func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlagWithAliases(cmd, contactUserIDFlagKeys[0], contactUserIDFlagKeys[1:]...); err != nil {
|
||
return err
|
||
}
|
||
raw := flagOrFallback(cmd, contactUserIDFlagKeys[0], contactUserIDFlagKeys[1:]...)
|
||
for _, part := range parseCSVValues(raw) {
|
||
switch strings.ToLower(strings.TrimSpace(part)) {
|
||
case "me", "self", "current", "whoami", "i":
|
||
return fmt.Errorf("--ids 需要真实的 userId,不接受 %q 这类占位符\n hint: 获取当前用户用: dws contact user get-self", part)
|
||
}
|
||
}
|
||
return callMCPTool("get_user_info_by_user_ids", map[string]any{
|
||
"user_id_list": parseCSVValues(raw),
|
||
})
|
||
}
|
||
runContactUserSearch := func(cmd *cobra.Command, args []string) error {
|
||
if err := validateRequiredFlagWithAliases(cmd, "query", "keyword", "name"); err != nil {
|
||
return err
|
||
}
|
||
return callMCPTool("search_contact_by_key_word", map[string]any{
|
||
"keyword": flagOrFallback(cmd, "query", "keyword", "name"),
|
||
})
|
||
}
|
||
|
||
contactRootSearchCmd := &cobra.Command{
|
||
Use: "search",
|
||
Short: "按关键词搜索用户(兼容入口)",
|
||
Hidden: true,
|
||
RunE: runContactUserSearch,
|
||
}
|
||
contactRootFindCmd := &cobra.Command{
|
||
Use: "find",
|
||
Short: "按关键词搜索用户(兼容入口)",
|
||
Hidden: true,
|
||
RunE: runContactUserSearch,
|
||
}
|
||
addQueryFlags(contactRootSearchCmd)
|
||
addQueryFlags(contactRootFindCmd)
|
||
|
||
contactRootGetCmd := &cobra.Command{
|
||
Use: "get",
|
||
Short: "获取用户/部门/角色详情(兼容入口)",
|
||
Hidden: true,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
switch {
|
||
case contactAnyFlagChanged(cmd, contactUserIDFlagKeys...):
|
||
return runContactUserGet(cmd, args)
|
||
case contactAnyFlagChanged(cmd, "dept", "id", "dept-id", "dept-ids"):
|
||
deptID, err := contactParseInt64WithAliases(cmd, "dept", "id", "dept-id", "dept-ids")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return callMCPTool("get_dept_info_by_dept_id", map[string]any{"deptId": deptID})
|
||
case contactAnyFlagChanged(cmd, "names", "name", "query", "keyword"):
|
||
return runContactLabelGet(cmd, args)
|
||
case contactAnyFlagChanged(cmd, "label-id", "role-id"):
|
||
return runContactLabelMembers(cmd, args)
|
||
default:
|
||
return fmt.Errorf("contact get 需要指定 --ids <userId>、--dept <deptId>、--names <角色名> 或 --label-id <角色ID>")
|
||
}
|
||
},
|
||
}
|
||
addUserIDFlags(contactRootGetCmd)
|
||
addDeptIDFlags(contactRootGetCmd)
|
||
addLabelNameFlags(contactRootGetCmd)
|
||
contactRootGetCmd.Flags().String("label-id", "", "角色 ID")
|
||
contactRootGetCmd.Flags().String("role-id", "", "--label-id 的别名")
|
||
_ = contactRootGetCmd.Flags().MarkHidden("role-id")
|
||
|
||
contactRootListCmd := &cobra.Command{
|
||
Use: "list",
|
||
Short: "列出角色/部门成员/用户详情(兼容入口)",
|
||
Hidden: true,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
switch {
|
||
case contactAnyFlagChanged(cmd, "depts", "id", "dept-id", "dept-ids"):
|
||
if err := validateRequiredFlagWithAliases(cmd, "depts", "id", "dept-id", "dept-ids"); err != nil {
|
||
return err
|
||
}
|
||
raw := flagOrFallback(cmd, "depts", "id", "dept-id", "dept-ids")
|
||
setName := contactFirstSetFlagName(cmd, "depts", "id", "dept-id", "dept-ids")
|
||
for _, t := range parseCSVValues(raw) {
|
||
if _, ok := contactRootDeptLikeTokens[strings.ToLower(strings.TrimSpace(t))]; ok {
|
||
return fmt.Errorf("flag --%s 包含非法占位符 %q;钉钉根部门 deptId=1,请使用 --%s 1", setName, t, setName)
|
||
}
|
||
}
|
||
return callMCPTool("get_dept_members_by_deptId", map[string]any{"deptIds": parseCSVValues(raw)})
|
||
case contactAnyFlagChanged(cmd, contactUserIDFlagKeys...):
|
||
return runContactUserGet(cmd, args)
|
||
case contactAnyFlagChanged(cmd, "names", "name", "query", "keyword"):
|
||
return runContactLabelGet(cmd, args)
|
||
default:
|
||
return runContactLabelList(cmd, args)
|
||
}
|
||
},
|
||
}
|
||
contactRootListCmd.Flags().String("depts", "", "部门 ID 列表")
|
||
contactRootListCmd.Flags().String("id", "", "--depts 的别名")
|
||
contactRootListCmd.Flags().String("dept-id", "", "--depts 的别名")
|
||
contactRootListCmd.Flags().String("dept-ids", "", "--depts 的别名")
|
||
_ = contactRootListCmd.Flags().MarkHidden("id")
|
||
_ = contactRootListCmd.Flags().MarkHidden("dept-id")
|
||
_ = contactRootListCmd.Flags().MarkHidden("dept-ids")
|
||
addUserIDFlags(contactRootListCmd)
|
||
addLabelNameFlags(contactRootListCmd)
|
||
|
||
for _, use := range []string{"self", "me", "whoami", "get-self", "user-self", "current-user"} {
|
||
root.AddCommand(&cobra.Command{
|
||
Use: use,
|
||
Short: "获取当前用户信息(兼容入口)",
|
||
Hidden: true,
|
||
RunE: contactUserGetSelfCmd.RunE,
|
||
})
|
||
}
|
||
root.AddCommand(contactRootSearchCmd, contactRootFindCmd, contactRootGetCmd, contactRootListCmd)
|
||
|
||
contactHintSubCmd := func(use, suggestion string) *cobra.Command {
|
||
c := hintSubCmd(use, suggestion)
|
||
runHint := c.RunE
|
||
c.DisableFlagParsing = true
|
||
c.RunE = func(cmd *cobra.Command, args []string) error {
|
||
for _, arg := range args {
|
||
if arg == "--help" || arg == "-h" {
|
||
return cmd.Help()
|
||
}
|
||
}
|
||
return runHint(cmd, args)
|
||
}
|
||
return c
|
||
}
|
||
|
||
root.AddCommand(contactHintSubCmd("department", "use: dws contact dept [search|list-members|list-children|get-info]"))
|
||
|
||
// hint: dws contact user find/list/info/detail → 指向 user search / user get
|
||
userCmd.AddCommand(contactHintSubCmd("find", "use: dws contact user search --query <关键词>"))
|
||
userCmd.AddCommand(contactHintSubCmd("list", "use: dws contact user search --query <关键词>"))
|
||
// 针对 LLM 常混淆的 REST 风格子命名:user info / user detail / user get-info
|
||
userCmd.AddCommand(contactHintSubCmd("info", "use: dws contact user get --ids <用户ID> or dws contact user get-self"))
|
||
userCmd.AddCommand(contactHintSubCmd("detail", "use: dws contact user get --ids <用户ID>"))
|
||
userCmd.AddCommand(contactHintSubCmd("get-info", "use: dws contact user get --ids <用户ID>"))
|
||
// 注:me / whoami / current 现已是 get-self 的真别名(Aliases),不再注册 hintSubCmd(会与真别名冲突)。
|
||
|
||
// hint: dws contact dept list / dept info / dept detail → 指向 list-members / list-children / get-info
|
||
contactDeptCmd.AddCommand(contactHintSubCmd("list", "use: dws contact dept list-members --depts <部门ID> or dws contact dept list-children --dept <父部门ID>"))
|
||
contactDeptCmd.AddCommand(contactHintSubCmd("info", "use: dws contact dept get-info --dept <部门ID>"))
|
||
contactDeptCmd.AddCommand(contactHintSubCmd("detail", "use: dws contact dept get-info --dept <部门ID>"))
|
||
|
||
// dws contact label find/search/info/detail/list-all → 真实兼容入口。
|
||
// 注:list 已是真命令(label list),不再注册 hintSubCmd(会与真命令冲突)。
|
||
for _, use := range []string{"find", "search", "info"} {
|
||
cmd := &cobra.Command{Use: use, Hidden: true, RunE: runContactLabelGet}
|
||
addLabelNameFlags(cmd)
|
||
contactLabelCmd.AddCommand(cmd)
|
||
}
|
||
contactLabelDetailCmd := &cobra.Command{Use: "detail", Hidden: true, RunE: runContactLabelMembers}
|
||
addLabelIDFlags(contactLabelDetailCmd)
|
||
contactLabelCmd.AddCommand(contactLabelDetailCmd)
|
||
contactLabelCmd.AddCommand(&cobra.Command{Use: "list-all", Hidden: true, RunE: runContactLabelList})
|
||
|
||
// contact 子树统一错误兜底:任何 flag 解析失败均在尾部追加 "See '<CommandPath> --help' for usage."
|
||
// 与 docker / kubectl / gh 的 UX 一致。unknown subcommand 由 cobra 自带 Did-You-Mean 处理。
|
||
var attachContactHelpHint func(c *cobra.Command)
|
||
attachContactHelpHint = func(c *cobra.Command) {
|
||
c.SetFlagErrorFunc(func(cc *cobra.Command, err error) error {
|
||
// 与 root 级 flagErrorWithSuggestions 保持同款尾部 hint 格式(句号结尾为全树 UX 约定)。
|
||
msg := fmt.Sprintf("%s\nSee '%s --help' for usage.", err.Error(), cc.CommandPath())
|
||
return errors.New(msg)
|
||
})
|
||
for _, sub := range c.Commands() {
|
||
attachContactHelpHint(sub)
|
||
}
|
||
}
|
||
attachContactHelpHint(root)
|
||
|
||
return root
|
||
}
|
||
|
||
// parseCSVInts 解析逗号分隔的整数字符串为 []int64 切片,
|
||
// 去除空白并过滤无法解析的项。
|
||
func parseCSVInts(s string) []int64 {
|
||
parts := strings.Split(s, ",")
|
||
result := make([]int64, 0, len(parts))
|
||
for _, p := range parts {
|
||
if trimmed := strings.TrimSpace(p); trimmed != "" {
|
||
if n, err := strconv.ParseInt(trimmed, 10, 64); err == nil {
|
||
result = append(result, n)
|
||
}
|
||
}
|
||
}
|
||
return result
|
||
}
|
||
|
||
func parseCSVIntsStrict(s string) ([]int64, error) {
|
||
parts := strings.Split(s, ",")
|
||
result := make([]int64, 0, len(parts))
|
||
for i, p := range parts {
|
||
trimmed := strings.TrimSpace(p)
|
||
if trimmed == "" {
|
||
return nil, fmt.Errorf("第 %d 项为空", i+1)
|
||
}
|
||
n, err := strconv.ParseInt(trimmed, 10, 64)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("第 %d 项 %q 不是整数: %w", i+1, trimmed, err)
|
||
}
|
||
result = append(result, n)
|
||
}
|
||
return result, nil
|
||
}
|