467 lines
20 KiB
Go
467 lines
20 KiB
Go
package helpers
|
||
|
||
import (
|
||
"strings"
|
||
|
||
"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/spf13/cobra"
|
||
)
|
||
|
||
// boolPtr returns a pointer to v, for ParamDecl.Required declarations.
|
||
func boolPtr(v bool) *bool { return &v }
|
||
|
||
// 这些是原有的宽松兜底名,不属于本次 Primary 迁移;保持原生 hidden
|
||
// 注册,不给它们新增 alias_of/origin 元数据。
|
||
var aisearchPersonGuessFlags = []string{"name", "q", "text"}
|
||
|
||
// 历史解析优先级必须独立于新旧 Primary:双传时旧 --keyword 始终获胜。
|
||
var aisearchQueryResolutionOrder = []string{"keyword", "name", "q", "query", "text"}
|
||
|
||
// flagValue 安全地读取 flag 值:先查 local,再查自身 PersistentFlags,
|
||
// 再查 parents 的 PersistentFlags。比 cmd.Flags().GetString 更鲁棒,
|
||
// 因为后者只在 cobra Execute 完成 mergePersistentFlags 后才包含继承的 flag。
|
||
func flagValue(cmd *cobra.Command, name string) string {
|
||
if f := cmd.Flag(name); f != nil {
|
||
return f.Value.String()
|
||
}
|
||
return ""
|
||
}
|
||
|
||
func changedFlagValue(cmd *cobra.Command, name string) (string, bool) {
|
||
if f := cmd.Flag(name); f != nil && f.Changed {
|
||
return f.Value.String(), true
|
||
}
|
||
return "", false
|
||
}
|
||
|
||
func aisearchFlagOrFallback(cmd *cobra.Command, primary string, aliases ...string) string {
|
||
if v := flagValue(cmd, primary); v != "" {
|
||
return v
|
||
}
|
||
for _, alias := range aliases {
|
||
if v := flagValue(cmd, alias); v != "" {
|
||
return v
|
||
}
|
||
}
|
||
return ""
|
||
}
|
||
|
||
func aisearchFlagOrDefault(cmd *cobra.Command, primary, def string, aliases ...string) string {
|
||
names := append([]string{primary}, aliases...)
|
||
for _, name := range names {
|
||
if v, ok := changedFlagValue(cmd, name); ok && v != "" {
|
||
return v
|
||
}
|
||
}
|
||
if v := flagValue(cmd, primary); v != "" {
|
||
return v
|
||
}
|
||
return def
|
||
}
|
||
|
||
// resolveAisearchKeyword 按历史顺序解析人员搜索词。虽然公开 Primary 已改为
|
||
// --query,但兼容期内显式旧 --keyword 的值仍优先。
|
||
func resolveAisearchKeyword(cmd *cobra.Command) string {
|
||
for _, name := range aisearchQueryResolutionOrder {
|
||
if v := flagValue(cmd, name); v != "" {
|
||
return v
|
||
}
|
||
}
|
||
return ""
|
||
}
|
||
|
||
func addAisearchPersonFlags(cmd *cobra.Command) {
|
||
// Alias evidence must be emitted by corecmd.FlagSpec.Aliases. Build the pair
|
||
// on a temporary declaration command so the legacy alias can retain its
|
||
// historical -w shorthand without extending the global alias mechanism or
|
||
// assigning that shorthand to the new --query Primary.
|
||
declaration := &cobra.Command{Use: "aisearch-query-flags"}
|
||
corecmd.RegisterFlags(declaration, []corecmd.FlagSpec{{
|
||
Name: "query",
|
||
Usage: "搜索关键词 (必填,如人名、技能关键词等)",
|
||
Aliases: []string{"keyword"},
|
||
}})
|
||
queryFlag := declaration.Flags().Lookup("query")
|
||
keywordFlag := declaration.Flags().Lookup("keyword")
|
||
keywordFlag.Shorthand = "w"
|
||
cmd.Flags().AddFlag(queryFlag)
|
||
cmd.Flags().AddFlag(keywordFlag)
|
||
cmd.Flags().StringP("dimension", "d", "all", "查询维度: all/name/department/position/duty/supervisor/subordinate/phone/jobNumber,多个用逗号分隔")
|
||
for _, alias := range aisearchPersonGuessFlags {
|
||
cmd.Flags().String(alias, "", "")
|
||
_ = cmd.Flags().MarkHidden(alias)
|
||
}
|
||
cmd.Flags().String("type", "", "兼容选择器;person/search 路径仅接受 person/user/people")
|
||
_ = cmd.Flags().MarkHidden("type")
|
||
}
|
||
|
||
func addAisearchKeywordCompatibilityFlag(cmd *cobra.Command) {
|
||
cmd.Flags().String("keyword", "", "--queries 的兼容别名")
|
||
_ = cmd.Flags().MarkHidden("keyword")
|
||
cmd.Flags().String("query", "", "--queries 的兼容别名")
|
||
_ = cmd.Flags().MarkHidden("query")
|
||
}
|
||
|
||
// runAisearchPerson 是 aisearch person 的实际执行体,被 personCmd 和 root
|
||
// 的智能 RunE(裸调兜底)共享调用。
|
||
func runAisearchPerson(cmd *cobra.Command, _ []string) error {
|
||
if selector := strings.ToLower(strings.TrimSpace(flagValue(cmd, "type"))); selector != "" && selector != "person" && selector != "user" && selector != "people" {
|
||
return apperrors.NewValidation("aisearch person/search 的 --type 仅接受 person、user 或 people")
|
||
}
|
||
keyword := resolveAisearchKeyword(cmd)
|
||
if keyword == "" {
|
||
return validateRequiredFlags(cmd, "query")
|
||
}
|
||
dimensions := parseDimensions(flagValue(cmd, "dimension"))
|
||
return callMCPTool("enterprise_person_search", map[string]any{
|
||
"keyword": keyword,
|
||
"dimension": dimensions,
|
||
})
|
||
}
|
||
|
||
// runAisearchEnterprise 调用企业内部知识搜索工具。它关注内容本身,
|
||
// 参数只包含内容关键词、内容类型和显式时间范围。
|
||
func runAisearchEnterprise(cmd *cobra.Command, _ []string) error {
|
||
queries := parseCSVValues(aisearchFlagOrFallback(cmd, "queries", "query", "keyword"))
|
||
searchTypes := normalizeAisearchSearchTypes(parseCSVValues(aisearchFlagOrDefault(cmd, "types", "all", "search-types", "searchTypes")))
|
||
if len(searchTypes) == 0 {
|
||
searchTypes = []string{"all"}
|
||
}
|
||
|
||
toolArgs := map[string]any{
|
||
"queries": queries,
|
||
"searchTypes": searchTypes,
|
||
}
|
||
if v := aisearchFlagOrFallback(cmd, "time-range", "timeRange"); v != "" {
|
||
toolArgs["timeRange"] = v
|
||
}
|
||
return callMCPTool("search_enterprise", toolArgs)
|
||
}
|
||
|
||
// runAisearchBehavior 调用企业内部行为记录搜索工具。该能力和 person 同属
|
||
// aisearch server,但参数空间不同,因此独立成 behavior 子命令,避免复用
|
||
// search/query 这类已经被 person 兜底占用的路径。
|
||
func runAisearchBehavior(cmd *cobra.Command, _ []string) error {
|
||
queries := parseCSVValues(aisearchFlagOrFallback(cmd, "queries", "query", "keyword"))
|
||
searchTypes := normalizeAisearchSearchTypes(parseCSVValues(aisearchFlagOrDefault(cmd, "types", "all", "search-types", "searchTypes")))
|
||
if len(searchTypes) == 0 {
|
||
searchTypes = []string{"all"}
|
||
}
|
||
|
||
toolArgs := map[string]any{
|
||
"queries": queries,
|
||
"searchTypes": searchTypes,
|
||
}
|
||
if v := aisearchFlagOrFallback(cmd, "chat-scope", "chatScope"); v != "" {
|
||
toolArgs["chatScope"] = v
|
||
}
|
||
if v := aisearchFlagOrDefault(cmd, "behavior-type", "all", "behaviorType"); v != "" {
|
||
toolArgs["behaviorType"] = v
|
||
}
|
||
if v := aisearchFlagOrFallback(cmd, "time-range", "timeRange"); v != "" {
|
||
toolArgs["timeRange"] = v
|
||
}
|
||
if v := flagValue(cmd, "direction"); v != "" {
|
||
toolArgs["direction"] = v
|
||
}
|
||
return callMCPTool("search_enterprise_behavior", toolArgs)
|
||
}
|
||
|
||
func normalizeAisearchSearchTypes(values []string) []string {
|
||
normalized := make([]string, 0, len(values))
|
||
for _, v := range values {
|
||
switch strings.ToLower(strings.TrimSpace(v)) {
|
||
case "doc", "document":
|
||
normalized = append(normalized, "document")
|
||
default:
|
||
normalized = append(normalized, v)
|
||
}
|
||
}
|
||
return normalized
|
||
}
|
||
|
||
func newAisearchCommand() *cobra.Command {
|
||
// Product-level Agent routing Decl (migrated from selection/aisearch.json
|
||
// products.aisearch). Catalog assembly stamps provenance contract_final.
|
||
contract.RegisterProductDecl(contract.ProductDecl{
|
||
ID: "aisearch",
|
||
Selection: contract.ProductSelectionDecl{
|
||
AgentSummary: "企业内智能搜人、搜知识内容与搜行为记录",
|
||
UseWhen: []string{
|
||
"语义找人、按主题搜企业知识,或追溯发送/创建/分享等行为",
|
||
},
|
||
AvoidWhen: []string{
|
||
"已有明确资源 ID 要读写时改用对应产品;普通 OAuth 登录不用 aisearch",
|
||
},
|
||
},
|
||
})
|
||
root := &cobra.Command{
|
||
Use: "aisearch",
|
||
Short: "AI 搜问",
|
||
Long: `AI 搜问:搜索企业人员信息、企业内部知识内容与企业内部行为记录。`,
|
||
// 智能 root:模型常漏 person 子命令直接 dws aisearch --query xxx,
|
||
// 检测到 query 就自动等价于 person;否则退回 group help。
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
if resolveAisearchKeyword(cmd) != "" {
|
||
return runAisearchPerson(cmd, args)
|
||
}
|
||
return groupRunE(cmd, args)
|
||
},
|
||
}
|
||
|
||
// root 和 person 各自定义同一组本地 flag,这样:
|
||
// - dws aisearch --query xxx ← root 自己能解析
|
||
// - dws aisearch person --query xxx ← person 本地 flag
|
||
// - dws aisearch search --query xxx ← search 是 person 的 alias
|
||
// 不能放在 product PersistentFlags:否则 enterprise/behavior 会公开接受
|
||
// 它们不拥有的 person-only dimension,破坏 Help ↔ Schema 完整性。
|
||
addAisearchPersonFlags(root)
|
||
|
||
personCmd := &cobra.Command{
|
||
Use: "person",
|
||
// alias 列表覆盖真实瞎猜模式(按图里调用频次降序):
|
||
// A 类同义瞎猜:search(196) / search-person(74) / user-search(2) /
|
||
// user(隐含) / query(5) / people(4) / ask(1) / search-user(2)
|
||
// B 类跨模块混淆:contact(2 + 路径变体共 5)
|
||
// 所有 alias 透明等价于 person,对外文档/help 仍只展示 person。
|
||
Aliases: []string{
|
||
"search", "search-person", "search-user",
|
||
"user", "user-search",
|
||
"query", "people", "ask", "find", "lookup",
|
||
"contact",
|
||
},
|
||
// 显式声明允许任意位置参数:模型可能写 dws aisearch person search --query xxx,
|
||
// 此时 "search" 会作为 positional arg 被忽略,不报错。
|
||
Args: cobra.ArbitraryArgs,
|
||
Short: "搜索企业人员",
|
||
Long: `通过关键词搜索企业内人员信息,支持按维度筛选。
|
||
|
||
可选维度 (--dimension):
|
||
all 全部维度 (默认)
|
||
name 姓名
|
||
department 部门
|
||
position 职位
|
||
duty 职责/技能
|
||
supervisor 上级
|
||
subordinate 下级
|
||
phone 手机号
|
||
jobNumber 工号
|
||
|
||
多个维度用逗号分隔。`,
|
||
Example: ` dws aisearch person --query "张三" --dimension department
|
||
dws aisearch person --query "产品部" --dimension department
|
||
dws aisearch person --query "五道" --dimension supervisor
|
||
dws aisearch person --query "AI搜问" --dimension duty
|
||
dws aisearch person --query "李四" --dimension name,department
|
||
dws aisearch person --query "13800138000" --dimension phone
|
||
dws aisearch person --query "W12345" --dimension jobNumber`,
|
||
RunE: runAisearchPerson,
|
||
}
|
||
DeclareLeafMetadata(personCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "aisearch",
|
||
Name: "enterprise_person_search",
|
||
CanonicalPath: "aisearch.enterprise_person_search",
|
||
CLIPath: "aisearch person",
|
||
PrimaryCLIPath: "aisearch person",
|
||
},
|
||
Description: "企业内找人:按姓名/部门/职位/职责/上下级/手机号/工号筛选",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "aisearch", RPCName: "enterprise_person_search"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "企业内找人:按姓名/部门/职位/职责/上下级/手机号/工号筛选",
|
||
UseWhen: []string{
|
||
"找人、谁负责某事、查上级/下级、按手机号或工号定位人员",
|
||
"需要把维度词映射到 --dimension,关键词只保留目标实体",
|
||
},
|
||
AvoidWhen: []string{
|
||
"已有 userId 只需详情时用 contact user get",
|
||
"精确通讯录关键词搜同事/好友且不涉及职责语义时可用 contact user search",
|
||
"搜企业知识内容时用 aisearch enterprise;搜行为记录时用 aisearch behavior",
|
||
},
|
||
Examples: []string{
|
||
"dws aisearch person --query \"张三\" --dimension name --format json",
|
||
"dws aisearch person --query \"五道\" --dimension supervisor --format json",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "query", Property: "keyword", Required: boolPtr(true)},
|
||
},
|
||
},
|
||
})
|
||
addAisearchPersonFlags(personCmd)
|
||
|
||
enterpriseCmd := &cobra.Command{
|
||
Use: "enterprise",
|
||
Aliases: []string{"knowledge", "content", "search-enterprise", "search_enterprise"},
|
||
Short: "搜索企业内部知识内容和相关消息",
|
||
Long: `检索企业内部知识内容,如文档、消息、日程、待办、听记、日志、图片、链接、AI 表格、企业百科、邮件等。
|
||
|
||
普通“XX 相关消息/文档/邮件/日程/待办/纪要有哪些”属于企业内容搜索,使用本命令;queries 只放内容关键词,时间放到 --time-range,所有类型词放到 --types。汇总类场景可不传 queries,使用 --types all。
|
||
|
||
不要把“最近搜索问题相关消息”截断成 --query "搜索问题",也不要把“最近 OKR 相关邮件”写成 --query "OKR 邮件";这会丢失时间和类型槽位。应显式写成 --queries + --types + --time-range。`,
|
||
Example: ` dws aisearch enterprise --queries "智能化方案" --types document
|
||
dws aisearch enterprise --queries "搜索问题" --types im --time-range "最近"
|
||
dws aisearch enterprise --queries "OKR" --types mail --time-range "最近"
|
||
dws aisearch enterprise --queries "AI搜问" --types calendar --time-range "本周"
|
||
dws aisearch enterprise --queries "项目" --types todo,minute --time-range "最近"
|
||
dws aisearch enterprise --queries "发版" --types im --time-range "本周"
|
||
dws aisearch enterprise --types all --time-range "本周"
|
||
dws aisearch enterprise --queries "OKR" --types document,im,mail`,
|
||
RunE: runAisearchEnterprise,
|
||
}
|
||
// Register flags that carry ParamDecl before DeclareLeafMetadata so
|
||
// AttachContract can emit their dws.schema.* annotations.
|
||
enterpriseCmd.Flags().String("queries", "", "内容关键词列表,多个用逗号分隔;汇总类场景可留空")
|
||
enterpriseCmd.Flags().String("time-range", "", "时间范围,仅当用户显式给出时间词时填写,如 今天/本周/9月/过去一周")
|
||
DeclareLeafMetadata(enterpriseCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "aisearch",
|
||
Name: "search_enterprise",
|
||
CanonicalPath: "aisearch.search_enterprise",
|
||
CLIPath: "aisearch enterprise",
|
||
PrimaryCLIPath: "aisearch enterprise",
|
||
},
|
||
Description: "搜索企业内部知识与相关内容(文档/IM/日历/待办/纪要/日志/邮件等)",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "aisearch", RPCName: "search_enterprise"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "搜索企业内部知识与相关内容(文档/IM/日历/待办/纪要/日志/邮件等)",
|
||
UseWhen: []string{
|
||
"按主题找资料、方案、文档、消息、邮件等内容,且关注“有什么内容”",
|
||
"用户显式给出时间词或类型词时分别映射到 --time-range / --types",
|
||
},
|
||
AvoidWhen: []string{
|
||
"问“我发给谁/谁发给我/我创建过”等行为追溯时用 aisearch behavior",
|
||
"企业找人时用 aisearch person",
|
||
"已知具体资源 ID 要读写时改用对应产品命令",
|
||
},
|
||
Examples: []string{
|
||
"dws aisearch enterprise --queries \"智能化方案\" --types document --format json",
|
||
"dws aisearch enterprise --queries \"OKR\" --types mail --time-range \"最近\" --format json",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "queries", Required: boolPtr(false)},
|
||
{Name: "time-range", Required: boolPtr(false)},
|
||
{Name: "types", Property: "searchTypes"},
|
||
},
|
||
},
|
||
})
|
||
enterpriseCmd.Flags().String("types", "all", "搜索类型: all/document/im/calendar/todo/minute/report/image/link/notable/baike/mail,多个用逗号分隔")
|
||
enterpriseCmd.Flags().String("search-types", "", "--types 的别名")
|
||
_ = enterpriseCmd.Flags().MarkHidden("search-types")
|
||
enterpriseCmd.Flags().String("searchTypes", "", "--types 的别名")
|
||
_ = enterpriseCmd.Flags().MarkHidden("searchTypes")
|
||
enterpriseCmd.Flags().String("timeRange", "", "--time-range 的别名")
|
||
_ = enterpriseCmd.Flags().MarkHidden("timeRange")
|
||
addAisearchKeywordCompatibilityFlag(enterpriseCmd)
|
||
|
||
behaviorCmd := &cobra.Command{
|
||
Use: "behavior",
|
||
Short: "搜索明确的发送/创建/接收等行为记录",
|
||
Long: `仅当用户明确询问“我/某人发过、发给、收到、创建、分享、编辑过什么”等行为动作时,检索企业内部行为记录。
|
||
|
||
普通“XX 相关消息/文档/邮件有哪些”不是行为记录,应使用 aisearch enterprise。behavior 的 queries 只放内容关键词;时间放到 --time-range,所有类型词放到 --types,行为动作放到 --behavior-type,人与人之间的流向放到 --direction。`,
|
||
Example: ` dws aisearch behavior --types mail --behavior-type send --direction "我->汐峰"
|
||
dws aisearch behavior --types im,mail --behavior-type send --direction "我->汐峰"
|
||
dws aisearch behavior --types document --behavior-type receive --direction "汐峰->我"
|
||
dws aisearch behavior --types all --behavior-type create --time-range "本周"
|
||
dws aisearch behavior --types im --chat-scope "scrum群" --behavior-type send --time-range "今天"`,
|
||
RunE: runAisearchBehavior,
|
||
}
|
||
DeclareLeafMetadata(behaviorCmd, LeafSpec{
|
||
Safety: contract.SafetySpec{
|
||
Effect: "read", Risk: "low",
|
||
Confirmation: "not_required", Idempotency: "idempotent",
|
||
},
|
||
Contract: LeafContract{
|
||
Identity: contract.ToolIdentitySpec{
|
||
ProductID: "aisearch",
|
||
Name: "search_enterprise_behavior",
|
||
CanonicalPath: "aisearch.search_enterprise_behavior",
|
||
CLIPath: "aisearch behavior",
|
||
PrimaryCLIPath: "aisearch behavior",
|
||
},
|
||
Description: "搜索发送/创建/分享/编辑/接收等明确行为记录",
|
||
Interface: &contract.InterfaceSpec{
|
||
Mode: "mcp",
|
||
Availability: "available",
|
||
Ref: &contract.InterfaceRefSpec{ProductID: "aisearch", RPCName: "search_enterprise_behavior"},
|
||
},
|
||
Selection: contract.SelectionSpec{
|
||
AgentSummary: "搜索发送/创建/分享/编辑/接收等明确行为记录",
|
||
UseWhen: []string{"用户明确问我/某人发过、发给、收到、创建、分享、编辑过什么"},
|
||
AvoidWhen: []string{
|
||
"只按主题找内容本身时用 aisearch enterprise",
|
||
"没有行为动作词时不要选用本工具",
|
||
},
|
||
Examples: []string{
|
||
"dws aisearch behavior --types mail --behavior-type send --direction \"我->汐峰\" --format json",
|
||
"dws aisearch behavior --queries \"智能化方案\" --types document --behavior-type create --format json",
|
||
},
|
||
},
|
||
Parameters: []contract.ParamDecl{
|
||
{Name: "types", Property: "searchTypes"},
|
||
},
|
||
},
|
||
})
|
||
behaviorCmd.Flags().String("queries", "", "内容关键词列表,多个用逗号分隔;汇总类场景可留空")
|
||
behaviorCmd.Flags().String("types", "all", "搜索类型: all/document/im/calendar/todo/minute/report/image/link/notable/baike/mail,多个用逗号分隔")
|
||
behaviorCmd.Flags().String("search-types", "", "--types 的别名")
|
||
_ = behaviorCmd.Flags().MarkHidden("search-types")
|
||
behaviorCmd.Flags().String("searchTypes", "", "--types 的别名")
|
||
_ = behaviorCmd.Flags().MarkHidden("searchTypes")
|
||
behaviorCmd.Flags().String("chat-scope", "", "消息所在会话/群范围,仅 IM 类型且用户明确指定群名时填写")
|
||
behaviorCmd.Flags().String("chatScope", "", "--chat-scope 的别名")
|
||
_ = behaviorCmd.Flags().MarkHidden("chatScope")
|
||
behaviorCmd.Flags().String("behavior-type", "all", "行为类型: all/send/create/share/edit/receive")
|
||
behaviorCmd.Flags().String("behaviorType", "", "--behavior-type 的别名")
|
||
_ = behaviorCmd.Flags().MarkHidden("behaviorType")
|
||
behaviorCmd.Flags().String("time-range", "", "时间范围,仅当用户显式给出时间词时填写,如 今天/本周/9月/过去一周")
|
||
behaviorCmd.Flags().String("timeRange", "", "--time-range 的别名")
|
||
_ = behaviorCmd.Flags().MarkHidden("timeRange")
|
||
behaviorCmd.Flags().String("direction", "", `交互方向,如 "我->汐峰"、"汐峰->我"、"我<->汐峰"`)
|
||
addAisearchKeywordCompatibilityFlag(behaviorCmd)
|
||
|
||
root.AddCommand(personCmd, enterpriseCmd, behaviorCmd)
|
||
return root
|
||
}
|
||
|
||
// parseDimensions 将逗号分隔的维度字符串解析为 []string。
|
||
func parseDimensions(s string) []string {
|
||
s = strings.TrimSpace(s)
|
||
if s == "" {
|
||
return []string{"all"}
|
||
}
|
||
parts := strings.Split(s, ",")
|
||
dims := make([]string, 0, len(parts))
|
||
for _, p := range parts {
|
||
if d := strings.TrimSpace(p); d != "" {
|
||
dims = append(dims, d)
|
||
}
|
||
}
|
||
if len(dims) == 0 {
|
||
return []string{"all"}
|
||
}
|
||
return dims
|
||
}
|