409 lines
15 KiB
Go
409 lines
15 KiB
Go
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()
|
|
}
|