瀏覽代碼

gocctl model v20200819 (#18)

* rename snake、came method

* new: generate model from data source

* add change log md

* update model doc

* update  doc

* beauty code
Keson 4 年之前
父節點
當前提交
db83843558

+ 8 - 1
doc/goctl.md

@@ -195,6 +195,13 @@ ts需要指定webapi所在目录
 goctl api dart -api user/user.api -dir ./src
 ```
 
+## 根据mysql ddl或者datasource生成model文件
+
+```shell script
+$ goctl model mysql -src={filename} -dir={dir} -cache={true|false}
+```
+详情参考[model文档](https://github.com/tal-tech/go-zero/blob/master/tools/goctl/model/sql/README.MD)
+
 ## 根据定义好的简单go文件生成mongo代码文件(仅限golang使用)  
 ```shell
 goctl model mongo -src {{yourDir}}/xiao/service/xhb/user/model/usermodel.go -cache yes
@@ -218,7 +225,7 @@ type User struct {
      o是改字段需要生产的操作函数 可以取得get,find,set 分别表示生成返回单个对象的查询方法,返回多个对象的查询方法,设置该字段方法  
      生成的目标文件会覆盖该简单go文件  
 
-## goctl rpc生成
+## goctl rpc生成(业务剥离中,暂未开放)
 
   命令 `goctl rpc proto -proto ${proto} -service ${serviceName} -project ${projectName} -dir ${directory} -shared ${shared}`  
   如: `goctl rpc proto -proto test.proto  -service test -project xjy  -dir .`  

+ 58 - 18
tools/goctl/goctl.go

@@ -4,6 +4,8 @@ import (
 	"fmt"
 	"os"
 
+	"github.com/urfave/cli"
+
 	"github.com/tal-tech/go-zero/core/logx"
 	"github.com/tal-tech/go-zero/tools/goctl/api/apigen"
 	"github.com/tal-tech/go-zero/tools/goctl/api/dartgen"
@@ -18,7 +20,6 @@ import (
 	"github.com/tal-tech/go-zero/tools/goctl/docker"
 	"github.com/tal-tech/go-zero/tools/goctl/feature"
 	"github.com/tal-tech/go-zero/tools/goctl/model/sql/command"
-	"github.com/urfave/cli"
 )
 
 var (
@@ -191,25 +192,64 @@ var (
 		{
 			Name:  "model",
 			Usage: "generate model code",
-			Flags: []cli.Flag{
-				cli.StringFlag{
-					Name:  "src, s",
-					Usage: "the file path of the ddl source file",
-				},
-				cli.StringFlag{
-					Name:  "dir, d",
-					Usage: "the target dir",
-				},
-				cli.BoolFlag{
-					Name:  "cache, c",
-					Usage: "generate code with cache [optional]",
-				},
-				cli.BoolFlag{
-					Name:  "idea",
-					Usage: "for idea plugin [optional]",
+			Subcommands: []cli.Command{
+				{
+					Name:  "mysql",
+					Usage: `generate mysql model"`,
+					Subcommands: []cli.Command{
+						{
+							Name:  "ddl",
+							Usage: `generate mysql model from ddl"`,
+							Flags: []cli.Flag{
+								cli.StringFlag{
+									Name:  "src, s",
+									Usage: "the file path of the ddl source file",
+								},
+								cli.StringFlag{
+									Name:  "dir, d",
+									Usage: "the target dir",
+								},
+								cli.BoolFlag{
+									Name:  "cache, c",
+									Usage: "generate code with cache [optional]",
+								},
+								cli.BoolFlag{
+									Name:  "idea",
+									Usage: "for idea plugin [optional]",
+								},
+							},
+							Action: command.MysqlDDL,
+						},
+						{
+							Name:  "datasource",
+							Usage: `generate model from datasource"`,
+							Flags: []cli.Flag{
+								cli.StringFlag{
+									Name:  "url",
+									Usage: `the data source of database,like "root:password@tcp(127.0.0.1:3306)/database"`,
+								},
+								cli.StringFlag{
+									Name:  "table, t",
+									Usage: `source table,tables separated by commas,like "user,course"`,
+								},
+								cli.BoolFlag{
+									Name:  "cache, c",
+									Usage: "generate code with cache [optional]",
+								},
+								cli.StringFlag{
+									Name:  "dir, d",
+									Usage: "the target dir",
+								},
+								cli.BoolFlag{
+									Name:  "idea",
+									Usage: "for idea plugin [optional]",
+								},
+							},
+							Action: command.MyDataSource,
+						},
+					},
 				},
 			},
-			Action: command.Mysql,
 		},
 		{
 			Name:  "config",

+ 10 - 0
tools/goctl/model/sql/CHANGELOG.md

@@ -0,0 +1,10 @@
+# Change log
+
+# 2020-08-20
+* 新增支持通过连接数据库生成model
+* 支持数据库多表生成
+* 优化stringx
+
+# 2020-08-19
+* 重构model代码生成逻辑
+* 实现从ddl解析表信息生成代码

+ 57 - 33
tools/goctl/model/sql/README.MD

@@ -4,21 +4,28 @@ goctl model 为go-zero下的工具模块中的组件之一,目前支持识别m
 
 # 快速开始
 
-```
-$ goctl model -src ./sql/user.sql -dir ./model -c true
-```
+* 通过ddl生成
 
-详情用法请参考[example](https://github.com/tal-tech/go-zero/tools/goctl/model/sql/example)
+    ```shell script
+    $ goctl model mysql ddl -src="./sql/user.sql" -dir="./sql/model" -c=true
+    ```
 
-执行上述命令后即可快速生成CURD代码。
+    执行上述命令后即可快速生成CURD代码。
 
-```
-model
-│   ├── error.go
-│   └── usermodel.go
-```
+    ```
+    model
+    │   ├── error.go
+    │   └── usermodel.go
+    ```
+* 通过datasource生成
+
+    ```shell script
+    $ goctl model mysql datasource -url="user:password@tcp(127.0.0.1:3306)/database" -table="table1,table2"  -dir="./model"
+    ```
+    
+
+> 详情用法请参考[example](https://github.com/tal-tech/go-zero/tree/master/tools/goctl/model/sql/example)
 
-> 注意:这里的目录结构中有usercoursemodel.go目录,在example中我为了体现带cache与不带cache代码的区别,因此将sql文件分别使用了独立的sql文件(user.sql&course.sql),在实际项目开发中你可以将ddl建表语句放在一个sql文件中,`goctl model`会自动解析并分割,最终按照每个ddl建表语句为单位生成独立的go文件。
 
 * 生成代码示例
   
@@ -174,22 +181,22 @@ model
 # 用法
 
 ```
-$ goctl model -h
+$ goctl model mysql -h
 ```
 
 ```
 NAME:
-   goctl model - generate model code
+   goctl model mysql - generate mysql model"
 
 USAGE:
-   goctl model [command options] [arguments...]
+   goctl model mysql command [command options] [arguments...]
 
-OPTIONS:
-   --src value, -s value  the file path of the ddl source file
-   --dir value, -d value  the target dir
-   --cache, -c            generate code with cache [optional]
-   --idea                 for idea plugin [optional]
+COMMANDS:
+   ddl         generate mysql model from ddl"
+   datasource  generate model from datasource"
 
+OPTIONS:
+   --help, -h  show help
 ```
 
 # 生成规则
@@ -198,22 +205,43 @@ OPTIONS:
   
   我们默认用户在建表时会创建createTime、updateTime字段(忽略大小写、下划线命名风格)且默认值均为`CURRENT_TIMESTAMP`,而updateTime支持`ON UPDATE CURRENT_TIMESTAMP`,对于这两个字段生成`insert`、`update`时会被移除,不在赋值范畴内,当然,如果你不需要这两个字段那也无大碍。
 * 带缓存模式
-
-    ```
-    $ goctl model -src {filename} -dir {dir} -cache true
-    ```
+    * ddl
+    
+        ```shell script
+        $ goctl model mysql -src={filename} -dir={dir} -cache=true
+        ```
+    * datasource
+    
+        ```shell script
+        $ goctl model mysql datasource -url={datasource} -table={tables}  -dir={dir} -cache=true
+        ```
   
   目前仅支持redis缓存,如果选择带缓存模式,即生成的`FindOne(ByXxx)`&`Delete`代码会生成带缓存逻辑的代码,目前仅支持单索引字段(除全文索引外),对于联合索引我们默认认为不需要带缓存,且不属于通用型代码,因此没有放在代码生成行列,如example中user表中的`id`、`name`、`mobile`字段均属于单字段索引。
 
 * 不带缓存模式
   
-  ```
-    $ goctl model -src {filename} -dir {dir} 
-  ```
+  * ddl
+  
+      ```shell script
+        $ goctl model -src={filename} -dir={dir} 
+      ```
+  * datasource
+  
+      ```shell script
+          $ goctl model mysql datasource -url={datasource} -table={tables}  -dir={dir}
+      ```
   or
-  ```
-    $ goctl model -src {filename} -dir {dir} -cache false
-  ```
+  * ddl
+      
+      ```shell script
+        $ goctl model -src={filename} -dir={dir} -cache=false
+      ```
+  * datasource
+  
+      ```shell script
+        $ goctl model mysql datasource -url={datasource} -table={tables}  -dir={dir} -cache=false
+      ```
+  
   生成代码仅基本的CURD结构。
 
 # 缓存
@@ -238,10 +266,6 @@ OPTIONS:
 
 # QA
 
-* goctl model支持根据数据库连接后选择表生成代码吗?
-  
-  目前暂时不支持,在后面会向这个方向扩展。
-
 * goctl model除了命令行模式,支持插件模式吗?
 
   很快支持idea插件。

+ 75 - 14
tools/goctl/model/sql/command/command.go

@@ -1,24 +1,85 @@
 package command
 
 import (
+	"io/ioutil"
+	"path/filepath"
+	"strings"
+
+	"github.com/urfave/cli"
+
+	"github.com/tal-tech/go-zero/core/collection"
+	"github.com/tal-tech/go-zero/core/logx"
+	"github.com/tal-tech/go-zero/core/stores/sqlx"
 	"github.com/tal-tech/go-zero/tools/goctl/model/sql/gen"
+	"github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
 	"github.com/tal-tech/go-zero/tools/goctl/util/console"
-	"github.com/urfave/cli"
 )
 
-func Mysql(ctx *cli.Context) error {
-	src := ctx.String("src")
-	dir := ctx.String("dir")
-	cache := ctx.Bool("cache")
-	idea := ctx.Bool("idea")
-	var log console.Console
-	if idea {
-		log = console.NewIdeaConsole()
-	} else {
-		log = console.NewColorConsole()
-	}
-	generator := gen.NewDefaultGenerator(src, dir, gen.WithConsoleOption(log))
-	err := generator.Start(cache)
+const (
+	flagSrc   = "src"
+	flagDir   = "dir"
+	flagCache = "cache"
+	flagIdea  = "idea"
+	flagUrl   = "url"
+	flagTable = "table"
+)
+
+func MysqlDDL(ctx *cli.Context) error {
+	src := ctx.String(flagSrc)
+	dir := ctx.String(flagDir)
+	cache := ctx.Bool(flagCache)
+	idea := ctx.Bool(flagIdea)
+	log := console.NewConsole(idea)
+	fileSrc, err := filepath.Abs(src)
+	if err != nil {
+		return err
+	}
+	data, err := ioutil.ReadFile(fileSrc)
+	if err != nil {
+		return err
+	}
+	source := string(data)
+	generator := gen.NewDefaultGenerator(source, dir, gen.WithConsoleOption(log))
+	err = generator.Start(cache)
+	if err != nil {
+		log.Error("%v", err)
+	}
+	return nil
+}
+
+func MyDataSource(ctx *cli.Context) error {
+	url := strings.TrimSpace(ctx.String(flagUrl))
+	dir := strings.TrimSpace(ctx.String(flagDir))
+	cache := ctx.Bool(flagCache)
+	idea := ctx.Bool(flagIdea)
+	table := strings.TrimSpace(ctx.String(flagTable))
+	log := console.NewConsole(idea)
+	if len(url) == 0 {
+		log.Error("%v", "expected data source of mysql, but is empty")
+		return nil
+	}
+	if len(table) == 0 {
+		log.Error("%v", "expected table(s), but nothing found")
+		return nil
+	}
+	logx.Disable()
+	conn := sqlx.NewMysql(url)
+	m := model.NewDDLModel(conn)
+	tables := collection.NewSet()
+	for _, item := range strings.Split(table, ",") {
+		item = strings.TrimSpace(item)
+		if len(item) == 0 {
+			continue
+		}
+		tables.AddStr(item)
+	}
+	ddl, err := m.ShowDDL(tables.KeysStr()...)
+	if err != nil {
+		log.Error("%v", err)
+		return nil
+	}
+	generator := gen.NewDefaultGenerator(strings.Join(ddl, "\n"), dir, gen.WithConsoleOption(log))
+	err = generator.Start(cache)
 	if err != nil {
 		log.Error("%v", err)
 	}

+ 5 - 2
tools/goctl/model/sql/example/generator.sh

@@ -1,4 +1,7 @@
 #!/bin/bash
 
-# generate usermodel with cache
-goctl model -src ./sql/user.sql -dir ./model -c true
+# generate model with cache from ddl
+ goctl model mysql ddl -src="./sql/user.sql" -dir="./sql/model" -c=true
+
+# generate model with cache from data source
+ goctl model mysql datasource -url="user:password@tcp(127.0.0.1:3306)/database" -table="table1,table2"  -dir="./model"

+ 2 - 2
tools/goctl/model/sql/gen/delete.go

@@ -27,14 +27,14 @@ func genDelete(table Table, withCache bool) (string, error) {
 			break
 		}
 	}
-	camel := table.Name.Snake2Camel()
+	camel := table.Name.ToCamel()
 	output, err := templatex.With("delete").
 		Parse(template.Delete).
 		Execute(map[string]interface{}{
 			"upperStartCamelObject":     camel,
 			"withCache":                 withCache,
 			"containsIndexCache":        containsIndexCache,
-			"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.Snake2Camel()).LowerStart(),
+			"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).UnTitle(),
 			"dataType":                  table.PrimaryKey.DataType,
 			"keys":                      strings.Join(keySet.KeysStr(), "\n"),
 			"originalPrimaryKey":        table.PrimaryKey.Name.Source(),

+ 1 - 1
tools/goctl/model/sql/gen/field.go

@@ -28,7 +28,7 @@ func genField(field parser.Field) (string, error) {
 	output, err := templatex.With("types").
 		Parse(template.Field).
 		Execute(map[string]interface{}{
-			"name":       field.Name.Snake2Camel(),
+			"name":       field.Name.ToCamel(),
 			"type":       field.DataType,
 			"tag":        tag,
 			"hasComment": field.Comment != "",

+ 3 - 3
tools/goctl/model/sql/gen/findone.go

@@ -7,15 +7,15 @@ import (
 )
 
 func genFindOne(table Table, withCache bool) (string, error) {
-	camel := table.Name.Snake2Camel()
+	camel := table.Name.ToCamel()
 	output, err := templatex.With("findOne").
 		Parse(template.FindOne).
 		Execute(map[string]interface{}{
 			"withCache":                 withCache,
 			"upperStartCamelObject":     camel,
-			"lowerStartCamelObject":     stringx.From(camel).LowerStart(),
+			"lowerStartCamelObject":     stringx.From(camel).UnTitle(),
 			"originalPrimaryKey":        table.PrimaryKey.Name.Source(),
-			"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.Snake2Camel()).LowerStart(),
+			"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).UnTitle(),
 			"dataType":                  table.PrimaryKey.DataType,
 			"cacheKey":                  table.CacheKey[table.PrimaryKey.Name.Source()].KeyExpression,
 			"cacheKeyVariable":          table.CacheKey[table.PrimaryKey.Name.Source()].Variable,

+ 6 - 6
tools/goctl/model/sql/gen/fineonebyfield.go

@@ -12,23 +12,23 @@ import (
 func genFineOneByField(table Table, withCache bool) (string, error) {
 	t := templatex.With("findOneByField").Parse(template.FindOneByField)
 	var list []string
-	camelTableName := table.Name.Snake2Camel()
+	camelTableName := table.Name.ToCamel()
 	for _, field := range table.Fields {
 		if field.IsPrimaryKey || !field.IsKey {
 			continue
 		}
-		camelFieldName := field.Name.Snake2Camel()
+		camelFieldName := field.Name.ToCamel()
 		output, err := t.Execute(map[string]interface{}{
 			"upperStartCamelObject":     camelTableName,
 			"upperField":                camelFieldName,
-			"in":                        fmt.Sprintf("%s %s", stringx.From(camelFieldName).LowerStart(), field.DataType),
+			"in":                        fmt.Sprintf("%s %s", stringx.From(camelFieldName).UnTitle(), field.DataType),
 			"withCache":                 withCache,
 			"cacheKey":                  table.CacheKey[field.Name.Source()].KeyExpression,
 			"cacheKeyVariable":          table.CacheKey[field.Name.Source()].Variable,
 			"primaryKeyLeft":            table.CacheKey[table.PrimaryKey.Name.Source()].Left,
-			"lowerStartCamelObject":     stringx.From(camelTableName).LowerStart(),
-			"lowerStartCamelField":      stringx.From(camelFieldName).LowerStart(),
-			"upperStartCamelPrimaryKey": table.PrimaryKey.Name.Snake2Camel(),
+			"lowerStartCamelObject":     stringx.From(camelTableName).UnTitle(),
+			"lowerStartCamelField":      stringx.From(camelFieldName).UnTitle(),
+			"upperStartCamelPrimaryKey": table.PrimaryKey.Name.ToCamel(),
 			"originalField":             field.Name.Source(),
 			"originalPrimaryField":      table.PrimaryKey.Name.Source(),
 		})

+ 4 - 17
tools/goctl/model/sql/gen/gen.go

@@ -23,21 +23,17 @@ const (
 type (
 	defaultGenerator struct {
 		source string
-		src    string
 		dir    string
 		console.Console
 	}
 	Option func(generator *defaultGenerator)
 )
 
-func NewDefaultGenerator(src, dir string, opt ...Option) *defaultGenerator {
-	if src == "" {
-		src = pwd
-	}
+func NewDefaultGenerator(source, dir string, opt ...Option) *defaultGenerator {
 	if dir == "" {
 		dir = pwd
 	}
-	generator := &defaultGenerator{src: src, dir: dir}
+	generator := &defaultGenerator{source: source, dir: dir}
 	var optionList []Option
 	optionList = append(optionList, newDefaultOption())
 	optionList = append(optionList, opt...)
@@ -60,10 +56,6 @@ func newDefaultOption() Option {
 }
 
 func (g *defaultGenerator) Start(withCache bool) error {
-	fileSrc, err := filepath.Abs(g.src)
-	if err != nil {
-		return err
-	}
 	dirAbs, err := filepath.Abs(g.dir)
 	if err != nil {
 		return err
@@ -72,21 +64,16 @@ func (g *defaultGenerator) Start(withCache bool) error {
 	if err != nil {
 		return err
 	}
-	data, err := ioutil.ReadFile(fileSrc)
-	if err != nil {
-		return err
-	}
-	g.source = string(data)
 	modelList, err := g.genFromDDL(withCache)
 	if err != nil {
 		return err
 	}
 
 	for tableName, code := range modelList {
-		name := fmt.Sprintf("%smodel.go", strings.ToLower(stringx.From(tableName).Snake2Camel()))
+		name := fmt.Sprintf("%smodel.go", strings.ToLower(stringx.From(tableName).ToCamel()))
 		filename := filepath.Join(dirAbs, name)
 		if util.FileExists(filename) {
-			g.Warning("%s already exists,ignored.", name)
+			g.Warning("%s already exists, ignored.", name)
 			continue
 		}
 		err = ioutil.WriteFile(filename, []byte(code), os.ModePerm)

+ 3 - 3
tools/goctl/model/sql/gen/insert.go

@@ -12,7 +12,7 @@ func genInsert(table Table, withCache bool) (string, error) {
 	expressions := make([]string, 0)
 	expressionValues := make([]string, 0)
 	for _, filed := range table.Fields {
-		camel := filed.Name.Snake2Camel()
+		camel := filed.Name.ToCamel()
 		if camel == "CreateTime" || camel == "UpdateTime" {
 			continue
 		}
@@ -22,13 +22,13 @@ func genInsert(table Table, withCache bool) (string, error) {
 		expressions = append(expressions, "?")
 		expressionValues = append(expressionValues, "data."+camel)
 	}
-	camel := table.Name.Snake2Camel()
+	camel := table.Name.ToCamel()
 	output, err := templatex.With("insert").
 		Parse(template.Insert).
 		Execute(map[string]interface{}{
 			"withCache":             withCache,
 			"upperStartCamelObject": camel,
-			"lowerStartCamelObject": stringx.From(camel).LowerStart(),
+			"lowerStartCamelObject": stringx.From(camel).UnTitle(),
 			"expression":            strings.Join(expressions, ", "),
 			"expressionValues":      strings.Join(expressionValues, ", "),
 		})

+ 4 - 4
tools/goctl/model/sql/gen/keys.go

@@ -26,14 +26,14 @@ type (
 func genCacheKeys(table parser.Table) (map[string]Key, error) {
 	fields := table.Fields
 	m := make(map[string]Key)
-	camelTableName := table.Name.Snake2Camel()
-	lowerStartCamelTableName := stringx.From(camelTableName).LowerStart()
+	camelTableName := table.Name.ToCamel()
+	lowerStartCamelTableName := stringx.From(camelTableName).UnTitle()
 	for _, field := range fields {
 		if !field.IsKey {
 			continue
 		}
-		camelFieldName := field.Name.Snake2Camel()
-		lowerStartCamelFieldName := stringx.From(camelFieldName).LowerStart()
+		camelFieldName := field.Name.ToCamel()
+		lowerStartCamelFieldName := stringx.From(camelFieldName).UnTitle()
 		left := fmt.Sprintf("cache%s%sPrefix", camelTableName, camelFieldName)
 		right := fmt.Sprintf("cache#%s#%s#", camelTableName, lowerStartCamelFieldName)
 		variable := fmt.Sprintf("%s%sKey", lowerStartCamelTableName, camelFieldName)

+ 1 - 1
tools/goctl/model/sql/gen/new.go

@@ -10,7 +10,7 @@ func genNew(table Table, withCache bool) (string, error) {
 		Parse(template.New).
 		Execute(map[string]interface{}{
 			"withCache":             withCache,
-			"upperStartCamelObject": table.Name.Snake2Camel(),
+			"upperStartCamelObject": table.Name.ToCamel(),
 		})
 	if err != nil {
 		return "", err

+ 1 - 1
tools/goctl/model/sql/gen/types.go

@@ -15,7 +15,7 @@ func genTypes(table Table, withCache bool) (string, error) {
 		Parse(template.Types).
 		Execute(map[string]interface{}{
 			"withCache":             withCache,
-			"upperStartCamelObject": table.Name.Snake2Camel(),
+			"upperStartCamelObject": table.Name.ToCamel(),
 			"fields":                fieldsString,
 		})
 	if err != nil {

+ 4 - 4
tools/goctl/model/sql/gen/update.go

@@ -11,7 +11,7 @@ import (
 func genUpdate(table Table, withCache bool) (string, error) {
 	expressionValues := make([]string, 0)
 	for _, filed := range table.Fields {
-		camel := filed.Name.Snake2Camel()
+		camel := filed.Name.ToCamel()
 		if camel == "CreateTime" || camel == "UpdateTime" {
 			continue
 		}
@@ -20,8 +20,8 @@ func genUpdate(table Table, withCache bool) (string, error) {
 		}
 		expressionValues = append(expressionValues, "data."+camel)
 	}
-	expressionValues = append(expressionValues, "data."+table.PrimaryKey.Name.Snake2Camel())
-	camelTableName := table.Name.Snake2Camel()
+	expressionValues = append(expressionValues, "data."+table.PrimaryKey.Name.ToCamel())
+	camelTableName := table.Name.ToCamel()
 	output, err := templatex.With("update").
 		Parse(template.Update).
 		Execute(map[string]interface{}{
@@ -29,7 +29,7 @@ func genUpdate(table Table, withCache bool) (string, error) {
 			"upperStartCamelObject": camelTableName,
 			"primaryCacheKey":       table.CacheKey[table.PrimaryKey.Name.Source()].DataKeyExpression,
 			"primaryKeyVariable":    table.CacheKey[table.PrimaryKey.Name.Source()].Variable,
-			"lowerStartCamelObject": stringx.From(camelTableName).LowerStart(),
+			"lowerStartCamelObject": stringx.From(camelTableName).UnTitle(),
 			"originalPrimaryKey":    table.PrimaryKey.Name.Source(),
 			"expressionValues":      strings.Join(expressionValues, ", "),
 		})

+ 2 - 2
tools/goctl/model/sql/gen/vars.go

@@ -13,12 +13,12 @@ func genVars(table Table, withCache bool) (string, error) {
 	for _, v := range table.CacheKey {
 		keys = append(keys, v.VarExpression)
 	}
-	camel := table.Name.Snake2Camel()
+	camel := table.Name.ToCamel()
 	output, err := templatex.With("var").
 		Parse(template.Vars).
 		GoFmt(true).
 		Execute(map[string]interface{}{
-			"lowerStartCamelObject": stringx.From(camel).LowerStart(),
+			"lowerStartCamelObject": stringx.From(camel).UnTitle(),
 			"upperStartCamelObject": camel,
 			"cacheKeys":             strings.Join(keys, "\r\n"),
 			"autoIncrement":         table.PrimaryKey.AutoIncrement,

+ 33 - 0
tools/goctl/model/sql/model/ddlmodel.go

@@ -0,0 +1,33 @@
+package model
+
+import (
+	"github.com/tal-tech/go-zero/core/stores/sqlx"
+)
+
+type (
+	DDLModel struct {
+		conn sqlx.SqlConn
+	}
+	DDL struct {
+		Table string `db:"Table"`
+		DDL   string `db:"Create Table"`
+	}
+)
+
+func NewDDLModel(conn sqlx.SqlConn) *DDLModel {
+	return &DDLModel{conn: conn}
+}
+
+func (m *DDLModel) ShowDDL(table ...string) ([]string, error) {
+	var ddl []string
+	for _, t := range table {
+		query := `show create table ` + t
+		var resp DDL
+		err := m.conn.QueryRow(&resp, query)
+		if err != nil {
+			return nil, err
+		}
+		ddl = append(ddl, resp.DDL)
+	}
+	return ddl, nil
+}

+ 1 - 1
tools/goctl/model/sql/parser/parser.go

@@ -81,7 +81,7 @@ func Parse(ddl string) (*Table, error) {
 		}
 		column := index.Columns[0]
 		columnName := column.Column.String()
-		camelColumnName := stringx.From(columnName).Snake2Camel()
+		camelColumnName := stringx.From(columnName).ToCamel()
 		// by default, createTime|updateTime findOne is not used.
 		if camelColumnName == "CreateTime" || camelColumnName == "UpdateTime" {
 			continue

+ 7 - 0
tools/goctl/util/console/console.go

@@ -19,6 +19,13 @@ type (
 	}
 )
 
+func NewConsole(idea bool) Console {
+	if idea {
+		return NewIdeaConsole()
+	}
+	return NewColorConsole()
+}
+
 func NewColorConsole() *colorConsole {
 	return &colorConsole{}
 }

+ 3 - 19
tools/goctl/util/stringx/string.go

@@ -6,10 +6,6 @@ import (
 	"unicode"
 )
 
-const (
-	emptyString = ""
-)
-
 type (
 	String struct {
 		source string
@@ -31,15 +27,9 @@ func (s String) IsEmptyOrSpace() bool {
 }
 
 func (s String) Lower() string {
-	if s.IsEmptyOrSpace() {
-		return s.source
-	}
 	return strings.ToLower(s.source)
 }
 func (s String) Upper() string {
-	if s.IsEmptyOrSpace() {
-		return s.source
-	}
 	return strings.ToUpper(s.source)
 }
 func (s String) Title() string {
@@ -50,10 +40,7 @@ func (s String) Title() string {
 }
 
 // snake->camel(upper start)
-func (s String) Snake2Camel() string {
-	if s.IsEmptyOrSpace() {
-		return s.source
-	}
+func (s String) ToCamel() string {
 	list := s.splitBy(func(r rune) bool {
 		return r == '_'
 	}, true)
@@ -65,10 +52,7 @@ func (s String) Snake2Camel() string {
 }
 
 // camel->snake
-func (s String) Camel2Snake() string {
-	if s.IsEmptyOrSpace() {
-		return s.source
-	}
+func (s String) ToSnake() string {
 	list := s.splitBy(func(r rune) bool {
 		return unicode.IsUpper(r)
 	}, false)
@@ -80,7 +64,7 @@ func (s String) Camel2Snake() string {
 }
 
 // return original string if rune is not letter at index 0
-func (s String) LowerStart() string {
+func (s String) UnTitle() string {
 	if s.IsEmptyOrSpace() {
 		return s.source
 	}

+ 7 - 7
tools/goctl/util/stringx/string_test.go

@@ -17,26 +17,26 @@ func TestString_IsEmptyOrSpace(t *testing.T) {
 }
 
 func TestString_Snake2Camel(t *testing.T) {
-	ret := From("____this_is_snake").Snake2Camel()
+	ret := From("____this_is_snake").ToCamel()
 	assert.Equal(t, "ThisIsSnake", ret)
 
-	ret2 := From("测试_test_Data").Snake2Camel()
+	ret2 := From("测试_test_Data").ToCamel()
 	assert.Equal(t, "测试TestData", ret2)
 
-	ret3 := From("___").Snake2Camel()
+	ret3 := From("___").ToCamel()
 	assert.Equal(t, "", ret3)
 
-	ret4 := From("testData_").Snake2Camel()
+	ret4 := From("testData_").ToCamel()
 	assert.Equal(t, "TestData", ret4)
 
-	ret5 := From("testDataTestData").Snake2Camel()
+	ret5 := From("testDataTestData").ToCamel()
 	assert.Equal(t, "TestDataTestData", ret5)
 }
 
 func TestString_Camel2Snake(t *testing.T) {
-	ret := From("ThisIsCCCamel").Camel2Snake()
+	ret := From("ThisIsCCCamel").ToSnake()
 	assert.Equal(t, "this_is_c_c_camel", ret)
 
-	ret2 := From("测试Test_Data_test_data").Camel2Snake()
+	ret2 := From("测试Test_Data_test_data").ToSnake()
 	assert.Equal(t, "测试_test__data_test_data", ret2)
 }