package main import ( "encoding/json" "fmt" "os" "path/filepath" "strings" "github.com/spf13/cobra" "gopkg.in/yaml.v3" ) var ( // Generate command flags interactive bool preview bool dbName string tableName string packageName string structName string desc string noWeb bool noServer bool onlyTemplate bool isAdd bool isTree bool treeJson string ) // generateCmd represents the generate command var generateCmd = &cobra.Command{ Use: "generate", Short: "生成代码", Long: `根据配置生成 Kratos DDD 架构的后端代码和 React 前端代码。 支持三种方式: 1. 配置文件方式: 使用 -c 指定 YAML/JSON 配置文件 2. 命令行参数方式: 使用各种参数直接指定配置 3. 交互模式: 使用 -i 进入交互式配置 使用示例: # 从配置文件生成 kra-gen generate -c autocode.yaml # 使用命令行参数 kra-gen generate --db mysql --table users --package example --struct User # 交互模式 kra-gen generate -i # 预览模式(不写入文件) kra-gen generate -c autocode.yaml -p # 仅生成模板(不注入代码) kra-gen generate -c autocode.yaml --only-template # 树形结构数据 kra-gen generate -c autocode.yaml --tree --tree-json name`, Run: runGenerate, } func init() { // Generate command specific flags generateCmd.Flags().BoolVarP(&interactive, "interactive", "i", false, "交互模式") generateCmd.Flags().BoolVarP(&preview, "preview", "p", false, "预览模式(不写入文件)") generateCmd.Flags().StringVar(&dbName, "db", "", "数据库连接名") generateCmd.Flags().StringVar(&tableName, "table", "", "表名") generateCmd.Flags().StringVar(&packageName, "package", "", "包名") generateCmd.Flags().StringVar(&structName, "struct", "", "结构体名") generateCmd.Flags().StringVar(&desc, "desc", "", "描述") generateCmd.Flags().BoolVar(&noWeb, "no-web", false, "不生成前端代码") generateCmd.Flags().BoolVar(&noServer, "no-server", false, "不生成后端代码") generateCmd.Flags().BoolVar(&onlyTemplate, "only-template", false, "仅生成模板(不注入代码到现有文件)") generateCmd.Flags().BoolVar(&isAdd, "add", false, "追加模式(不覆盖已有文件)") generateCmd.Flags().BoolVar(&isTree, "tree", false, "树形结构数据") generateCmd.Flags().StringVar(&treeJson, "tree-json", "", "树形结构展示字段") } // AutoCodeConfig represents the configuration for code generation type AutoCodeConfig struct { Package string `json:"package" yaml:"package"` StructName string `json:"structName" yaml:"structName"` TableName string `json:"tableName" yaml:"tableName"` Description string `json:"description" yaml:"description"` BusinessDB string `json:"businessDB" yaml:"businessDB"` Abbreviation string `json:"abbreviation" yaml:"abbreviation"` Options AutoCodeOptions `json:"options" yaml:"options"` Fields []AutoCodeField `json:"fields" yaml:"fields"` } // AutoCodeOptions represents generation options type AutoCodeOptions struct { GvaModel bool `json:"gvaModel" yaml:"gvaModel"` // 使用 KRA_MODEL (包含 ID, CreatedAt, UpdatedAt, DeletedAt) AutoMigrate bool `json:"autoMigrate" yaml:"autoMigrate"` // 自动迁移数据库表结构 AutoCreateResource bool `json:"autoCreateResource" yaml:"autoCreateResource"` // 自动创建资源标识 AutoCreateApiToSql bool `json:"autoCreateApiToSql" yaml:"autoCreateApiToSql"` // 自动创建 API 记录 AutoCreateMenuToSql bool `json:"autoCreateMenuToSql" yaml:"autoCreateMenuToSql"` // 自动创建菜单记录 AutoCreateBtnAuth bool `json:"autoCreateBtnAuth" yaml:"autoCreateBtnAuth"` // 自动创建按钮权限 OnlyTemplate bool `json:"onlyTemplate" yaml:"onlyTemplate"` // 仅生成模板(不注入代码) IsTree bool `json:"isTree" yaml:"isTree"` // 树形结构 TreeJson string `json:"treeJson" yaml:"treeJson"` // 树形结构展示字段 IsAdd bool `json:"isAdd" yaml:"isAdd"` // 追加模式(不覆盖已有文件) GenerateWeb bool `json:"generateWeb" yaml:"generateWeb"` // 生成前端代码 GenerateServer bool `json:"generateServer" yaml:"generateServer"` // 生成后端代码 } // DataSource 数据源配置 type DataSource struct { DBName string `json:"dbName" yaml:"dbName"` Table string `json:"table" yaml:"table"` Label string `json:"label" yaml:"label"` Value string `json:"value" yaml:"value"` Association int `json:"association" yaml:"association"` // 关联关系 1 一对一 2 一对多 HasDeletedAt bool `json:"hasDeletedAt" yaml:"hasDeletedAt"` } // AutoCodeField represents a field configuration type AutoCodeField struct { FieldName string `json:"fieldName" yaml:"fieldName"` FieldDesc string `json:"fieldDesc" yaml:"fieldDesc"` FieldType string `json:"fieldType" yaml:"fieldType"` FieldJson string `json:"fieldJson" yaml:"fieldJson"` ColumnName string `json:"columnName" yaml:"columnName"` DataTypeLong string `json:"dataTypeLong" yaml:"dataTypeLong"` Form bool `json:"form" yaml:"form"` Table bool `json:"table" yaml:"table"` Desc bool `json:"desc" yaml:"desc"` // 是否前端详情 Excel bool `json:"excel" yaml:"excel"` // 是否导入/导出 Require bool `json:"require" yaml:"require"` DefaultValue string `json:"defaultValue" yaml:"defaultValue"` // 默认值 ErrorText string `json:"errorText" yaml:"errorText"` // 校验失败文字 Clearable bool `json:"clearable" yaml:"clearable"` // 是否可清空 Sort bool `json:"sort" yaml:"sort"` // 是否增加排序 FieldSearchType string `json:"fieldSearchType" yaml:"fieldSearchType"` FieldSearchHide bool `json:"fieldSearchHide" yaml:"fieldSearchHide"` // 是否隐藏查询条件 DictType string `json:"dictType" yaml:"dictType"` DataSource *DataSource `json:"dataSource" yaml:"dataSource"` // 数据源 FieldIndexType string `json:"fieldIndexType" yaml:"fieldIndexType"` // 索引类型 PrimaryKey bool `json:"primaryKey" yaml:"primaryKey"` // 是否主键 } func runGenerate(cmd *cobra.Command, args []string) { var config *AutoCodeConfig var err error // Determine configuration source if configFile != "" { config, err = loadConfigFile(configFile) if err != nil { fmt.Fprintf(os.Stderr, "加载配置文件失败: %v\n", err) os.Exit(1) } } else if interactive { config, err = runInteractiveMode() if err != nil { fmt.Fprintf(os.Stderr, "交互模式错误: %v\n", err) os.Exit(1) } } else if hasRequiredFlags() { config = buildConfigFromFlags() } else { fmt.Println("请指定配置文件 (-c) 或使用交互模式 (-i) 或提供必要参数") fmt.Println("使用 'kra-gen generate --help' 查看帮助") os.Exit(1) } // Apply command line overrides applyFlagOverrides(config) // Validate configuration if err := validateConfig(config); err != nil { fmt.Fprintf(os.Stderr, "配置验证失败: %v\n", err) os.Exit(1) } // Execute generation if preview { if err := executePreview(config); err != nil { fmt.Fprintf(os.Stderr, "预览失败: %v\n", err) os.Exit(1) } } else { if err := executeGenerate(config); err != nil { fmt.Fprintf(os.Stderr, "生成失败: %v\n", err) os.Exit(1) } } } // loadConfigFile loads configuration from a YAML or JSON file func loadConfigFile(path string) (*AutoCodeConfig, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("读取配置文件失败: %w", err) } config := &AutoCodeConfig{} ext := strings.ToLower(filepath.Ext(path)) switch ext { case ".yaml", ".yml": if err := yaml.Unmarshal(data, config); err != nil { return nil, fmt.Errorf("解析 YAML 配置失败: %w", err) } case ".json": if err := json.Unmarshal(data, config); err != nil { return nil, fmt.Errorf("解析 JSON 配置失败: %w", err) } default: return nil, fmt.Errorf("不支持的配置文件格式: %s", ext) } return config, nil } // hasRequiredFlags checks if required command line flags are provided func hasRequiredFlags() bool { return tableName != "" && packageName != "" && structName != "" } // buildConfigFromFlags builds configuration from command line flags func buildConfigFromFlags() *AutoCodeConfig { abbreviation := strings.ToLower(structName) if len(abbreviation) > 0 { abbreviation = strings.ToLower(string(structName[0])) + structName[1:] } return &AutoCodeConfig{ Package: packageName, StructName: structName, TableName: tableName, Description: desc, BusinessDB: dbName, Abbreviation: abbreviation, Options: AutoCodeOptions{ GvaModel: true, AutoMigrate: true, AutoCreateResource: false, AutoCreateApiToSql: true, AutoCreateMenuToSql: true, AutoCreateBtnAuth: true, OnlyTemplate: false, IsTree: false, TreeJson: "", IsAdd: false, GenerateWeb: !noWeb, GenerateServer: !noServer, }, } } // applyFlagOverrides applies command line flag overrides to the configuration func applyFlagOverrides(config *AutoCodeConfig) { if noWeb { config.Options.GenerateWeb = false } if noServer { config.Options.GenerateServer = false } if onlyTemplate { config.Options.OnlyTemplate = true } if isAdd { config.Options.IsAdd = true } if isTree { config.Options.IsTree = true } if treeJson != "" { config.Options.TreeJson = treeJson } } // validateConfig validates the configuration func validateConfig(config *AutoCodeConfig) error { if config.Package == "" { return fmt.Errorf("包名不能为空") } if config.StructName == "" { return fmt.Errorf("结构体名不能为空") } if config.TableName == "" { return fmt.Errorf("表名不能为空") } return nil } // executePreview executes preview mode func executePreview(config *AutoCodeConfig) error { fmt.Println("========================================") fmt.Println("代码预览模式") fmt.Println("========================================") fmt.Printf("包名: %s\n", config.Package) fmt.Printf("结构体: %s\n", config.StructName) fmt.Printf("表名: %s\n", config.TableName) fmt.Printf("描述: %s\n", config.Description) fmt.Printf("数据库: %s\n", config.BusinessDB) fmt.Println("----------------------------------------") // 显示生成选项 fmt.Println("生成选项:") fmt.Printf(" 使用 KRA_MODEL: %s\n", boolToStr(config.Options.GvaModel)) fmt.Printf(" 自动迁移: %s\n", boolToStr(config.Options.AutoMigrate)) fmt.Printf(" 生成后端: %s\n", boolToStr(config.Options.GenerateServer)) fmt.Printf(" 生成前端: %s\n", boolToStr(config.Options.GenerateWeb)) fmt.Printf(" 仅生成模板: %s\n", boolToStr(config.Options.OnlyTemplate)) fmt.Printf(" 追加模式: %s\n", boolToStr(config.Options.IsAdd)) if !config.Options.OnlyTemplate { fmt.Printf(" 创建 API 记录: %s\n", boolToStr(config.Options.AutoCreateApiToSql)) fmt.Printf(" 创建菜单记录: %s\n", boolToStr(config.Options.AutoCreateMenuToSql)) fmt.Printf(" 创建按钮权限: %s\n", boolToStr(config.Options.AutoCreateBtnAuth)) fmt.Printf(" 创建资源标识: %s\n", boolToStr(config.Options.AutoCreateResource)) } if config.Options.IsTree { fmt.Printf(" 树形结构: 是 (展示字段: %s)\n", config.Options.TreeJson) } fmt.Println("----------------------------------------") fmt.Println("将生成以下文件:") fmt.Println() workDir := getWorkingDir() if config.Options.GenerateServer { fmt.Println("后端文件:") fmt.Printf(" - %s/internal/biz/%s/%s.go\n", workDir, config.Package, strings.ToLower(config.StructName)) fmt.Printf(" - %s/internal/data/%s/%s.go\n", workDir, config.Package, strings.ToLower(config.StructName)) fmt.Printf(" - %s/internal/data/model/%s/%s.go\n", workDir, config.Package, strings.ToLower(config.StructName)) fmt.Printf(" - %s/internal/service/%s/%s.go\n", workDir, config.Package, strings.ToLower(config.StructName)) fmt.Printf(" - %s/internal/service/types/%s/request/%s.go\n", workDir, config.Package, strings.ToLower(config.StructName)) fmt.Printf(" - %s/internal/server/handler/%s/%s.go\n", workDir, config.Package, strings.ToLower(config.StructName)) fmt.Printf(" - %s/internal/server/router/%s/%s.go\n", workDir, config.Package, strings.ToLower(config.StructName)) } if config.Options.GenerateWeb { fmt.Println() fmt.Println("前端文件:") fmt.Printf(" - %s/web/src/pages/%s/%s/index.tsx\n", workDir, config.Package, strings.ToLower(config.StructName)) fmt.Printf(" - %s/web/src/pages/%s/%s/form.tsx\n", workDir, config.Package, strings.ToLower(config.StructName)) fmt.Printf(" - %s/web/src/pages/%s/%s/detail.tsx\n", workDir, config.Package, strings.ToLower(config.StructName)) fmt.Printf(" - %s/web/src/services/kratos/%s.ts\n", workDir, config.Package) fmt.Printf(" - %s/web/src/types/%s/%s.d.ts\n", workDir, config.Package, strings.ToLower(config.StructName)) } fmt.Println() fmt.Println("字段配置:") if len(config.Fields) == 0 { fmt.Println(" (未配置字段,将从数据库表结构读取)") } else { for _, field := range config.Fields { searchInfo := "" if field.FieldSearchType != "" { searchInfo = fmt.Sprintf(" [搜索:%s]", field.FieldSearchType) } dictInfo := "" if field.DictType != "" { dictInfo = fmt.Sprintf(" [字典:%s]", field.DictType) } fmt.Printf(" - %s (%s): %s%s%s\n", field.FieldName, field.FieldType, field.FieldDesc, searchInfo, dictInfo) } } fmt.Println() fmt.Println("========================================") fmt.Println("预览完成,未写入任何文件") fmt.Println("========================================") return nil } // boolToStr 将布尔值转换为是/否字符串 func boolToStr(b bool) string { if b { return "是" } return "否" } // executeGenerate executes the actual code generation func executeGenerate(config *AutoCodeConfig) error { fmt.Println("========================================") fmt.Println("开始生成代码...") fmt.Println("========================================") fmt.Printf("包名: %s\n", config.Package) fmt.Printf("结构体: %s\n", config.StructName) fmt.Printf("表名: %s\n", config.TableName) fmt.Println("----------------------------------------") // TODO: 实现实际的代码生成逻辑 // 这里需要调用 AutoCodeTemplateUsecase 的 Create 方法 // 由于 CLI 工具需要独立运行,需要初始化数据库连接和依赖 fmt.Println() fmt.Println("注意: 完整的代码生成功能需要连接数据库。") fmt.Println("请使用 Web 界面进行代码生成,或确保配置文件包含数据库连接信息。") fmt.Println() fmt.Println("========================================") fmt.Println("代码生成完成!") fmt.Println("========================================") return nil } // runInteractiveMode runs the interactive configuration mode func runInteractiveMode() (*AutoCodeConfig, error) { // 使用新的交互模式实现,支持数据库/表选择 return runInteractiveModeWithDB() }