package cmd import ( "fmt" "io/fs" "os" "path/filepath" "strings" "text/template" "github.com/samber/lo" "github.com/spf13/cobra" "go.ipao.vip/atomctl/v2/pkg/utils/gomod" "go.ipao.vip/atomctl/v2/templates" ) // CommandNewProvider 注册 new_provider 命令 func CommandNewEvent(root *cobra.Command) { cmd := &cobra.Command{ Use: "event", Aliases: []string{"e"}, Short: "创建新的 event publish & subscriber", Long: `生成事件的发布者与订阅者模板,并在 app/events/topics.go 中维护事件常量。 行为: - 根据名称转换:驼峰 -> snake 作为 topic,Pascal 作为导出名 - 生成 publishers/.go 与 subscribers/.go(可通过 --only 选择一侧) - 若 topics.go 不存在会自动创建,并追加 const Topic{Name}=""(避免重复) - --dry-run 仅打印渲染与写入动作;--dir 指定输出基目录(默认 .) 示例: atomctl new event UserCreated atomctl new event UserCreated --only=publisher`, Args: cobra.ExactArgs(1), RunE: commandNewEventE, } cmd.Flags().String("only", "", "仅生成: publisher 或 subscriber") root.AddCommand(cmd) } func commandNewEventE(cmd *cobra.Command, args []string) error { snakeName := lo.SnakeCase(args[0]) camelName := lo.PascalCase(args[0]) // shared flags dryRun, _ := cmd.Flags().GetBool("dry-run") baseDir, _ := cmd.Flags().GetString("dir") only, _ := cmd.Flags().GetString("only") publisherPath := filepath.Join(baseDir, "app/events/publishers") subscriberPath := filepath.Join(baseDir, "app/events/subscribers") path, err := os.Getwd() if err != nil { return err } path, _ = filepath.Abs(path) err = gomod.Parse(filepath.Join(path, "go.mod")) if err != nil { return err } if dryRun { fmt.Printf("[dry-run] mkdir -p %s\n", publisherPath) fmt.Printf("[dry-run] mkdir -p %s\n", subscriberPath) } else { if err := os.MkdirAll(publisherPath, os.ModePerm); err != nil { return err } if err := os.MkdirAll(subscriberPath, os.ModePerm); err != nil { return err } } err = fs.WalkDir(templates.Events, "events", func(path string, d fs.DirEntry, err error) error { if err != nil { return err } if d.IsDir() { return nil } relPath, err := filepath.Rel("events", path) if err != nil { return err } var destPath string if relPath == "publisher.go.tpl" { if only == "subscriber" { return nil } destPath = filepath.Join(publisherPath, snakeName+".go") } else if relPath == "subscriber.go.tpl" { if only == "publisher" { return nil } destPath = filepath.Join(subscriberPath, snakeName+".go") } else { return nil } tmpl, err := template.ParseFS(templates.Events, path) if err != nil { return err } if dryRun { fmt.Printf("[dry-run] render > %s\n", destPath) return nil } destFile, err := os.Create(destPath) if err != nil { return err } defer destFile.Close() return tmpl.Execute(destFile, map[string]string{ "Name": camelName, "ModuleName": gomod.GetModuleName(), }) }) // 写入或追加 topic 常量,避免重复。 topicsPath := filepath.Join(baseDir, "app/events/topics.go") topicLine := fmt.Sprintf("const Topic%s = %q\n", camelName, "event:"+snakeName) if dryRun { fmt.Printf("[dry-run] ensure topics file and add constant > %s\n", topicsPath) } else { // ensure file exists with basic header if _, statErr := os.Stat(topicsPath); os.IsNotExist(statErr) { if err := os.MkdirAll(filepath.Dir(topicsPath), os.ModePerm); err != nil { return err } header := "package events\n\n// topics generated by atomctl\n\n" if err := os.WriteFile(topicsPath, []byte(header), 0o644); err != nil { return err } } // check duplicate content, _ := os.ReadFile(topicsPath) if !strings.Contains(string(content), "Topic"+camelName+" ") && !strings.Contains(string(content), topicLine) { f, err := os.OpenFile(topicsPath, os.O_APPEND|os.O_WRONLY, 0o644) if err != nil { return err } defer f.Close() if _, err := f.WriteString(topicLine); err != nil { return err } } } fmt.Printf("event 已创建: %s\n", snakeName) return nil }