Parcourir la source

gozero template (#147)

* model/rpc generate code from template cache

* delete unused(deprecated) code

* support template init|update|clean|revert

* model: return the execute result for insert and update operation

* // deprecated: containsAny

* add template test

* add default buildVersion

* update build version
Keson il y a 4 ans
Parent
commit
41964f9d52
43 fichiers modifiés avec 904 ajouts et 115 suppressions
  1. 26 0
      tools/goctl/api/gogen/template.go
  2. 92 0
      tools/goctl/api/gogen/template_test.go
  3. 0 18
      tools/goctl/feature/feature.go
  4. 45 12
      tools/goctl/goctl.go
  5. 4 0
      tools/goctl/model/sql/CHANGELOG.md
  6. 20 0
      tools/goctl/model/sql/converter/types_test.go
  7. 1 1
      tools/goctl/model/sql/example/generator.sh
  8. 6 1
      tools/goctl/model/sql/gen/delete.go
  9. 7 1
      tools/goctl/model/sql/gen/field.go
  10. 6 1
      tools/goctl/model/sql/gen/findone.go
  11. 14 2
      tools/goctl/model/sql/gen/findonebyfield.go
  12. 14 2
      tools/goctl/model/sql/gen/imports.go
  13. 7 1
      tools/goctl/model/sql/gen/insert.go
  14. 1 1
      tools/goctl/model/sql/gen/keys.go
  15. 77 0
      tools/goctl/model/sql/gen/keys_test.go
  16. 7 1
      tools/goctl/model/sql/gen/new.go
  17. 6 1
      tools/goctl/model/sql/gen/tag.go
  18. 72 0
      tools/goctl/model/sql/gen/template.go
  19. 93 0
      tools/goctl/model/sql/gen/template_test.go
  20. 8 1
      tools/goctl/model/sql/gen/types.go
  21. 7 1
      tools/goctl/model/sql/gen/update.go
  22. 6 1
      tools/goctl/model/sql/gen/vars.go
  23. 4 6
      tools/goctl/model/sql/parser/parser_test.go
  24. 1 0
      tools/goctl/model/sql/template/import.go
  25. 5 5
      tools/goctl/model/sql/template/insert.go
  26. 4 4
      tools/goctl/model/sql/template/update.go
  27. 4 0
      tools/goctl/rpc/CHANGELOG.md
  28. 1 1
      tools/goctl/rpc/gen/gen.go
  29. 21 5
      tools/goctl/rpc/gen/gencall.go
  30. 7 1
      tools/goctl/rpc/gen/genconfig.go
  31. 6 1
      tools/goctl/rpc/gen/genetc.go
  32. 12 2
      tools/goctl/rpc/gen/genlogic.go
  33. 6 1
      tools/goctl/rpc/gen/genmain.go
  34. 1 0
      tools/goctl/rpc/gen/genpb.go
  35. 14 2
      tools/goctl/rpc/gen/genserver.go
  36. 6 1
      tools/goctl/rpc/gen/gensvc.go
  37. 59 0
      tools/goctl/rpc/gen/rpctemplate.go
  38. 49 34
      tools/goctl/rpc/gen/template.go
  39. 92 0
      tools/goctl/rpc/gen/template_test.go
  40. 1 0
      tools/goctl/rpc/parser/pbast.go
  41. 4 3
      tools/goctl/rpc/parser/proto.go
  42. 69 0
      tools/goctl/tpl/templates.go
  43. 19 4
      tools/goctl/util/files.go

+ 26 - 0
tools/goctl/api/gogen/template.go

@@ -1,6 +1,8 @@
 package gogen
 package gogen
 
 
 import (
 import (
+	"fmt"
+
 	"github.com/tal-tech/go-zero/tools/goctl/util"
 	"github.com/tal-tech/go-zero/tools/goctl/util"
 	"github.com/urfave/cli"
 	"github.com/urfave/cli"
 )
 )
@@ -27,3 +29,27 @@ var templates = map[string]string{
 func GenTemplates(_ *cli.Context) error {
 func GenTemplates(_ *cli.Context) error {
 	return util.InitTemplates(category, templates)
 	return util.InitTemplates(category, templates)
 }
 }
+
+func RevertTemplate(name string) error {
+	content, ok := templates[name]
+	if !ok {
+		return fmt.Errorf("%s: no such file name", name)
+	}
+	return util.CreateTemplate(category, name, content)
+}
+
+func Update(category string) error {
+	err := Clean()
+	if err != nil {
+		return err
+	}
+	return util.InitTemplates(category, templates)
+}
+
+func Clean() error {
+	return util.Clean(category)
+}
+
+func GetCategory() string {
+	return category
+}

+ 92 - 0
tools/goctl/api/gogen/template_test.go

@@ -0,0 +1,92 @@
+package gogen
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/tal-tech/go-zero/tools/goctl/util"
+)
+
+func TestGenTemplates(t *testing.T) {
+	err := util.InitTemplates(category, templates)
+	assert.Nil(t, err)
+	dir, err := util.GetTemplateDir(category)
+	assert.Nil(t, err)
+	file := filepath.Join(dir, "main.tpl")
+	data, err := ioutil.ReadFile(file)
+	assert.Nil(t, err)
+	assert.Equal(t, string(data), mainTemplate)
+}
+
+func TestRevertTemplate(t *testing.T) {
+	name := "main.tpl"
+	err := util.InitTemplates(category, templates)
+	assert.Nil(t, err)
+
+	dir, err := util.GetTemplateDir(category)
+	assert.Nil(t, err)
+
+	file := filepath.Join(dir, name)
+	data, err := ioutil.ReadFile(file)
+	assert.Nil(t, err)
+
+	modifyData := string(data) + "modify"
+	err = util.CreateTemplate(category, name, modifyData)
+	assert.Nil(t, err)
+
+	data, err = ioutil.ReadFile(file)
+	assert.Nil(t, err)
+
+	assert.Equal(t, string(data), modifyData)
+
+	assert.Nil(t, RevertTemplate(name))
+
+	data, err = ioutil.ReadFile(file)
+	assert.Nil(t, err)
+	assert.Equal(t, mainTemplate, string(data))
+}
+
+func TestClean(t *testing.T) {
+	name := "main.tpl"
+	err := util.InitTemplates(category, templates)
+	assert.Nil(t, err)
+
+	assert.Nil(t, Clean())
+
+	dir, err := util.GetTemplateDir(category)
+	assert.Nil(t, err)
+
+	file := filepath.Join(dir, name)
+	_, err = ioutil.ReadFile(file)
+	assert.NotNil(t, err)
+}
+
+func TestUpdate(t *testing.T) {
+	name := "main.tpl"
+	err := util.InitTemplates(category, templates)
+	assert.Nil(t, err)
+
+	dir, err := util.GetTemplateDir(category)
+	assert.Nil(t, err)
+
+	file := filepath.Join(dir, name)
+	data, err := ioutil.ReadFile(file)
+	assert.Nil(t, err)
+
+	modifyData := string(data) + "modify"
+	err = util.CreateTemplate(category, name, modifyData)
+	assert.Nil(t, err)
+
+	data, err = ioutil.ReadFile(file)
+	assert.Nil(t, err)
+
+	assert.Equal(t, string(data), modifyData)
+
+	assert.Nil(t, Update(category))
+
+	data, err = ioutil.ReadFile(file)
+	assert.Nil(t, err)
+	assert.Equal(t, mainTemplate, string(data))
+}

+ 0 - 18
tools/goctl/feature/feature.go

@@ -1,18 +0,0 @@
-package feature
-
-import (
-	"fmt"
-
-	"github.com/logrusorgru/aurora"
-	"github.com/urfave/cli"
-)
-
-var feature = `
-1、增加goctl model支持
-`
-
-func Feature(_ *cli.Context) error {
-	fmt.Println(aurora.Blue("\nFEATURE:"))
-	fmt.Println(aurora.Blue(feature))
-	return nil
-}

+ 45 - 12
tools/goctl/goctl.go

@@ -3,6 +3,7 @@ package main
 import (
 import (
 	"fmt"
 	"fmt"
 	"os"
 	"os"
+	"runtime"
 
 
 	"github.com/tal-tech/go-zero/core/logx"
 	"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/apigen"
@@ -17,15 +18,15 @@ import (
 	"github.com/tal-tech/go-zero/tools/goctl/api/validate"
 	"github.com/tal-tech/go-zero/tools/goctl/api/validate"
 	"github.com/tal-tech/go-zero/tools/goctl/configgen"
 	"github.com/tal-tech/go-zero/tools/goctl/configgen"
 	"github.com/tal-tech/go-zero/tools/goctl/docker"
 	"github.com/tal-tech/go-zero/tools/goctl/docker"
-	"github.com/tal-tech/go-zero/tools/goctl/feature"
 	model "github.com/tal-tech/go-zero/tools/goctl/model/sql/command"
 	model "github.com/tal-tech/go-zero/tools/goctl/model/sql/command"
 	rpc "github.com/tal-tech/go-zero/tools/goctl/rpc/command"
 	rpc "github.com/tal-tech/go-zero/tools/goctl/rpc/command"
+	"github.com/tal-tech/go-zero/tools/goctl/tpl"
 	"github.com/urfave/cli"
 	"github.com/urfave/cli"
 )
 )
 
 
 var (
 var (
-	BuildTime = "not set"
-	commands  = []cli.Command{
+	BuildVersion = "20201021"
+	commands     = []cli.Command{
 		{
 		{
 			Name:  "api",
 			Name:  "api",
 			Usage: "generate api related files",
 			Usage: "generate api related files",
@@ -328,14 +329,46 @@ var (
 			Action: configgen.GenConfigCommand,
 			Action: configgen.GenConfigCommand,
 		},
 		},
 		{
 		{
-			Name:   "feature",
-			Usage:  "the features of the latest version",
-			Action: feature.Feature,
-		},
-		{
-			Name:   "template",
-			Usage:  "initialize the api templates",
-			Action: gogen.GenTemplates,
+			Name:  "template",
+			Usage: "template operation",
+			Subcommands: []cli.Command{
+				{
+					Name:   "init",
+					Usage:  "initialize the all templates(force update)",
+					Action: tpl.GenTemplates,
+				},
+				{
+					Name:   "clean",
+					Usage:  "clean the all cache templates",
+					Action: tpl.CleanTemplates,
+				},
+				{
+					Name:  "update",
+					Usage: "update template of the target category to the latest",
+					Flags: []cli.Flag{
+						cli.StringFlag{
+							Name:  "category,c",
+							Usage: "the category of template, enum [api,rpc,model]",
+						},
+					},
+					Action: tpl.UpdateTemplates,
+				},
+				{
+					Name:  "revert",
+					Usage: "revert the target template to the latest",
+					Flags: []cli.Flag{
+						cli.StringFlag{
+							Name:  "category,c",
+							Usage: "the category of template, enum [api,rpc,model]",
+						},
+						cli.StringFlag{
+							Name:  "name,n",
+							Usage: "the target file name of template",
+						},
+					},
+					Action: tpl.RevertTemplates,
+				},
+			},
 		},
 		},
 	}
 	}
 )
 )
@@ -345,7 +378,7 @@ func main() {
 
 
 	app := cli.NewApp()
 	app := cli.NewApp()
 	app.Usage = "a cli tool to generate code"
 	app.Usage = "a cli tool to generate code"
-	app.Version = BuildTime
+	app.Version = fmt.Sprintf("%s %s/%s", BuildVersion, runtime.GOOS, runtime.GOARCH)
 	app.Commands = commands
 	app.Commands = commands
 	// cli already print error messages
 	// cli already print error messages
 	if err := app.Run(os.Args); err != nil {
 	if err := app.Run(os.Args); err != nil {

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

@@ -1,5 +1,9 @@
 # Change log
 # Change log
 
 
+## 2020-10-19
+
+* 增加template
+
 ## 2020-08-20
 ## 2020-08-20
 
 
 * 新增支持通过连接数据库生成model
 * 新增支持通过连接数据库生成model

+ 20 - 0
tools/goctl/model/sql/converter/types_test.go

@@ -0,0 +1,20 @@
+package converter
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestConvertDataType(t *testing.T) {
+	v, err := ConvertDataType("tinyint")
+	assert.Nil(t, err)
+	assert.Equal(t, "int64", v)
+
+	v, err = ConvertDataType("timestamp")
+	assert.Nil(t, err)
+	assert.Equal(t, "time.Time", v)
+
+	_, err = ConvertDataType("float32")
+	assert.NotNil(t, err)
+}

+ 1 - 1
tools/goctl/model/sql/example/generator.sh

@@ -4,4 +4,4 @@
 goctl model mysql ddl -src="./sql/user.sql" -dir="./sql/model" -c
 goctl model mysql ddl -src="./sql/user.sql" -dir="./sql/model" -c
 
 
 # generate model with cache from data source
 # 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"
+#goctl model mysql datasource -url="user:password@tcp(127.0.0.1:3306)/database" -table="table1,table2"  -dir="./model"

+ 6 - 1
tools/goctl/model/sql/gen/delete.go

@@ -22,8 +22,13 @@ func genDelete(table Table, withCache bool) (string, error) {
 	}
 	}
 
 
 	camel := table.Name.ToCamel()
 	camel := table.Name.ToCamel()
+	text, err := util.LoadTemplate(category, deleteTemplateFile, template.Delete)
+	if err != nil {
+		return "", err
+	}
+
 	output, err := util.With("delete").
 	output, err := util.With("delete").
-		Parse(template.Delete).
+		Parse(text).
 		Execute(map[string]interface{}{
 		Execute(map[string]interface{}{
 			"upperStartCamelObject":     camel,
 			"upperStartCamelObject":     camel,
 			"withCache":                 withCache,
 			"withCache":                 withCache,

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

@@ -25,8 +25,14 @@ func genField(field parser.Field) (string, error) {
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
+
+	text, err := util.LoadTemplate(category, fieldTemplateFile, template.Field)
+	if err != nil {
+		return "", err
+	}
+
 	output, err := util.With("types").
 	output, err := util.With("types").
-		Parse(template.Field).
+		Parse(text).
 		Execute(map[string]interface{}{
 		Execute(map[string]interface{}{
 			"name":       field.Name.ToCamel(),
 			"name":       field.Name.ToCamel(),
 			"type":       field.DataType,
 			"type":       field.DataType,

+ 6 - 1
tools/goctl/model/sql/gen/findone.go

@@ -8,8 +8,13 @@ import (
 
 
 func genFindOne(table Table, withCache bool) (string, error) {
 func genFindOne(table Table, withCache bool) (string, error) {
 	camel := table.Name.ToCamel()
 	camel := table.Name.ToCamel()
+	text, err := util.LoadTemplate(category, findOneTemplateFile, template.FindOne)
+	if err != nil {
+		return "", err
+	}
+
 	output, err := util.With("findOne").
 	output, err := util.With("findOne").
-		Parse(template.FindOne).
+		Parse(text).
 		Execute(map[string]interface{}{
 		Execute(map[string]interface{}{
 			"withCache":                 withCache,
 			"withCache":                 withCache,
 			"upperStartCamelObject":     camel,
 			"upperStartCamelObject":     camel,

+ 14 - 2
tools/goctl/model/sql/gen/findonebyfield.go

@@ -10,7 +10,12 @@ import (
 )
 )
 
 
 func genFindOneByField(table Table, withCache bool) (string, string, error) {
 func genFindOneByField(table Table, withCache bool) (string, string, error) {
-	t := util.With("findOneByField").Parse(template.FindOneByField)
+	text, err := util.LoadTemplate(category, findOneByFieldTemplateFile, template.FindOneByField)
+	if err != nil {
+		return "", "", err
+	}
+
+	t := util.With("findOneByField").Parse(text)
 	var list []string
 	var list []string
 	camelTableName := table.Name.ToCamel()
 	camelTableName := table.Name.ToCamel()
 	for _, field := range table.Fields {
 	for _, field := range table.Fields {
@@ -33,10 +38,16 @@ func genFindOneByField(table Table, withCache bool) (string, string, error) {
 		if err != nil {
 		if err != nil {
 			return "", "", err
 			return "", "", err
 		}
 		}
+
 		list = append(list, output.String())
 		list = append(list, output.String())
 	}
 	}
 	if withCache {
 	if withCache {
-		out, err := util.With("findOneByFieldExtraMethod").Parse(template.FindOneByFieldExtraMethod).Execute(map[string]interface{}{
+		text, err := util.LoadTemplate(category, findOneByFieldExtraMethodTemplateFile, template.FindOneByFieldExtraMethod)
+		if err != nil {
+			return "", "", err
+		}
+
+		out, err := util.With("findOneByFieldExtraMethod").Parse(text).Execute(map[string]interface{}{
 			"upperStartCamelObject": camelTableName,
 			"upperStartCamelObject": camelTableName,
 			"primaryKeyLeft":        table.CacheKey[table.PrimaryKey.Name.Source()].Left,
 			"primaryKeyLeft":        table.CacheKey[table.PrimaryKey.Name.Source()].Left,
 			"lowerStartCamelObject": stringx.From(camelTableName).UnTitle(),
 			"lowerStartCamelObject": stringx.From(camelTableName).UnTitle(),
@@ -45,6 +56,7 @@ func genFindOneByField(table Table, withCache bool) (string, string, error) {
 		if err != nil {
 		if err != nil {
 			return "", "", err
 			return "", "", err
 		}
 		}
+
 		return strings.Join(list, "\n"), out.String(), nil
 		return strings.Join(list, "\n"), out.String(), nil
 	}
 	}
 	return strings.Join(list, "\n"), "", nil
 	return strings.Join(list, "\n"), "", nil

+ 14 - 2
tools/goctl/model/sql/gen/imports.go

@@ -7,20 +7,32 @@ import (
 
 
 func genImports(withCache, timeImport bool) (string, error) {
 func genImports(withCache, timeImport bool) (string, error) {
 	if withCache {
 	if withCache {
-		buffer, err := util.With("import").Parse(template.Imports).Execute(map[string]interface{}{
+		text, err := util.LoadTemplate(category, importsTemplateFile, template.Imports)
+		if err != nil {
+			return "", err
+		}
+
+		buffer, err := util.With("import").Parse(text).Execute(map[string]interface{}{
 			"time": timeImport,
 			"time": timeImport,
 		})
 		})
 		if err != nil {
 		if err != nil {
 			return "", err
 			return "", err
 		}
 		}
+
 		return buffer.String(), nil
 		return buffer.String(), nil
 	} else {
 	} else {
-		buffer, err := util.With("import").Parse(template.ImportsNoCache).Execute(map[string]interface{}{
+		text, err := util.LoadTemplate(category, importsWithNoCacheTemplateFile, template.ImportsNoCache)
+		if err != nil {
+			return "", err
+		}
+
+		buffer, err := util.With("import").Parse(text).Execute(map[string]interface{}{
 			"time": timeImport,
 			"time": timeImport,
 		})
 		})
 		if err != nil {
 		if err != nil {
 			return "", err
 			return "", err
 		}
 		}
+
 		return buffer.String(), nil
 		return buffer.String(), nil
 	}
 	}
 }
 }

+ 7 - 1
tools/goctl/model/sql/gen/insert.go

@@ -34,8 +34,13 @@ func genInsert(table Table, withCache bool) (string, error) {
 		expressionValues = append(expressionValues, "data."+camel)
 		expressionValues = append(expressionValues, "data."+camel)
 	}
 	}
 	camel := table.Name.ToCamel()
 	camel := table.Name.ToCamel()
+	text, err := util.LoadTemplate(category, insertTemplateFile, template.Insert)
+	if err != nil {
+		return "", err
+	}
+
 	output, err := util.With("insert").
 	output, err := util.With("insert").
-		Parse(template.Insert).
+		Parse(text).
 		Execute(map[string]interface{}{
 		Execute(map[string]interface{}{
 			"withCache":             withCache,
 			"withCache":             withCache,
 			"containsIndexCache":    table.ContainsUniqueKey,
 			"containsIndexCache":    table.ContainsUniqueKey,
@@ -49,5 +54,6 @@ func genInsert(table Table, withCache bool) (string, error) {
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
+
 	return output.String(), nil
 	return output.String(), nil
 }
 }

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

@@ -12,7 +12,7 @@ type (
 	// {{prefix}}=cache
 	// {{prefix}}=cache
 	// key:id
 	// key:id
 	Key struct {
 	Key struct {
-		VarExpression     string // cacheUserIdPrefix="cache#user#id#"
+		VarExpression     string // cacheUserIdPrefix = "cache#User#id#"
 		Left              string // cacheUserIdPrefix
 		Left              string // cacheUserIdPrefix
 		Right             string // cache#user#id#
 		Right             string // cache#user#id#
 		Variable          string // userIdKey
 		Variable          string // userIdKey

+ 77 - 0
tools/goctl/model/sql/gen/keys_test.go

@@ -0,0 +1,77 @@
+package gen
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/tal-tech/go-zero/tools/goctl/model/sql/parser"
+	"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
+)
+
+func TestGenCacheKeys(t *testing.T) {
+	m, err := genCacheKeys(parser.Table{
+		Name: stringx.From("user"),
+		PrimaryKey: parser.Primary{
+			Field: parser.Field{
+				Name:         stringx.From("id"),
+				DataBaseType: "bigint",
+				DataType:     "int64",
+				IsKey:        false,
+				IsPrimaryKey: true,
+				IsUniqueKey:  false,
+				Comment:      "自增id",
+			},
+			AutoIncrement: true,
+		},
+		Fields: []parser.Field{
+			{
+				Name:         stringx.From("mobile"),
+				DataBaseType: "varchar",
+				DataType:     "string",
+				IsKey:        false,
+				IsPrimaryKey: false,
+				IsUniqueKey:  true,
+				Comment:      "手机号",
+			},
+			{
+				Name:         stringx.From("name"),
+				DataBaseType: "varchar",
+				DataType:     "string",
+				IsKey:        false,
+				IsPrimaryKey: false,
+				IsUniqueKey:  true,
+				Comment:      "姓名",
+			},
+			{
+				Name:         stringx.From("createTime"),
+				DataBaseType: "timestamp",
+				DataType:     "time.Time",
+				IsKey:        false,
+				IsPrimaryKey: false,
+				IsUniqueKey:  false,
+				Comment:      "创建时间",
+			},
+			{
+				Name:         stringx.From("updateTime"),
+				DataBaseType: "timestamp",
+				DataType:     "time.Time",
+				IsKey:        false,
+				IsPrimaryKey: false,
+				IsUniqueKey:  false,
+				Comment:      "更新时间",
+			},
+		},
+	})
+	assert.Nil(t, err)
+
+	for fieldName, key := range m {
+		name := stringx.From(fieldName)
+		assert.Equal(t, fmt.Sprintf(`cacheUser%sPrefix = "cache#User#%s#"`, name.ToCamel(), name.UnTitle()), key.VarExpression)
+		assert.Equal(t, fmt.Sprintf(`cacheUser%sPrefix`, name.ToCamel()), key.Left)
+		assert.Equal(t, fmt.Sprintf(`cache#User#%s#`, name.UnTitle()), key.Right)
+		assert.Equal(t, fmt.Sprintf(`user%sKey`, name.ToCamel()), key.Variable)
+		assert.Equal(t, `user`+name.ToCamel()+`Key := fmt.Sprintf("%s%v", cacheUser`+name.ToCamel()+`Prefix,`+name.UnTitle()+`)`, key.KeyExpression)
+	}
+
+}

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

@@ -6,8 +6,13 @@ import (
 )
 )
 
 
 func genNew(table Table, withCache bool) (string, error) {
 func genNew(table Table, withCache bool) (string, error) {
+	text, err := util.LoadTemplate(category, modelNewTemplateFile, template.New)
+	if err != nil {
+		return "", err
+	}
+
 	output, err := util.With("new").
 	output, err := util.With("new").
-		Parse(template.New).
+		Parse(text).
 		Execute(map[string]interface{}{
 		Execute(map[string]interface{}{
 			"withCache":             withCache,
 			"withCache":             withCache,
 			"upperStartCamelObject": table.Name.ToCamel(),
 			"upperStartCamelObject": table.Name.ToCamel(),
@@ -15,5 +20,6 @@ func genNew(table Table, withCache bool) (string, error) {
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
+
 	return output.String(), nil
 	return output.String(), nil
 }
 }

+ 6 - 1
tools/goctl/model/sql/gen/tag.go

@@ -9,8 +9,13 @@ func genTag(in string) (string, error) {
 	if in == "" {
 	if in == "" {
 		return in, nil
 		return in, nil
 	}
 	}
+	text, err := util.LoadTemplate(category, tagTemplateFile, template.Tag)
+	if err != nil {
+		return "", err
+	}
+
 	output, err := util.With("tag").
 	output, err := util.With("tag").
-		Parse(template.Tag).
+		Parse(text).
 		Execute(map[string]interface{}{
 		Execute(map[string]interface{}{
 			"field": in,
 			"field": in,
 		})
 		})

+ 72 - 0
tools/goctl/model/sql/gen/template.go

@@ -0,0 +1,72 @@
+package gen
+
+import (
+	"fmt"
+
+	"github.com/tal-tech/go-zero/tools/goctl/model/sql/template"
+	"github.com/tal-tech/go-zero/tools/goctl/util"
+	"github.com/urfave/cli"
+)
+
+const (
+	category                              = "model"
+	deleteTemplateFile                    = "delete.tpl"
+	fieldTemplateFile                     = "filed.tpl"
+	findOneTemplateFile                   = "find-one.tpl"
+	findOneByFieldTemplateFile            = "find-one-by-field.tpl"
+	findOneByFieldExtraMethodTemplateFile = "find-one-by-filed-extra-method.tpl"
+	importsTemplateFile                   = "import.tpl"
+	importsWithNoCacheTemplateFile        = "import-no-cache.tpl"
+	insertTemplateFile                    = "insert.tpl"
+	modelTemplateFile                     = "model.tpl"
+	modelNewTemplateFile                  = "model-new.tpl"
+	tagTemplateFile                       = "tag.tpl"
+	typesTemplateFile                     = "types.tpl"
+	updateTemplateFile                    = "update.tpl"
+	varTemplateFile                       = "var.tpl"
+)
+
+var templates = map[string]string{
+	deleteTemplateFile:                    template.Delete,
+	fieldTemplateFile:                     template.Field,
+	findOneTemplateFile:                   template.FindOne,
+	findOneByFieldTemplateFile:            template.FindOneByField,
+	findOneByFieldExtraMethodTemplateFile: template.FindOneByFieldExtraMethod,
+	importsTemplateFile:                   template.Imports,
+	importsWithNoCacheTemplateFile:        template.ImportsNoCache,
+	insertTemplateFile:                    template.Insert,
+	modelTemplateFile:                     template.Model,
+	modelNewTemplateFile:                  template.New,
+	tagTemplateFile:                       template.Tag,
+	typesTemplateFile:                     template.Types,
+	updateTemplateFile:                    template.Update,
+	varTemplateFile:                       template.Vars,
+}
+
+func GenTemplates(_ *cli.Context) error {
+	return util.InitTemplates(category, templates)
+}
+
+func RevertTemplate(name string) error {
+	content, ok := templates[name]
+	if !ok {
+		return fmt.Errorf("%s: no such file name", name)
+	}
+	return util.CreateTemplate(category, name, content)
+}
+
+func Clean() error {
+	return util.Clean(category)
+}
+
+func Update(category string) error {
+	err := Clean()
+	if err != nil {
+		return err
+	}
+	return util.InitTemplates(category, templates)
+}
+
+func GetCategory() string {
+	return category
+}

+ 93 - 0
tools/goctl/model/sql/gen/template_test.go

@@ -0,0 +1,93 @@
+package gen
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/tal-tech/go-zero/tools/goctl/model/sql/template"
+	"github.com/tal-tech/go-zero/tools/goctl/util"
+)
+
+func TestGenTemplates(t *testing.T) {
+	err := util.InitTemplates(category, templates)
+	assert.Nil(t, err)
+	dir, err := util.GetTemplateDir(category)
+	assert.Nil(t, err)
+	file := filepath.Join(dir, "model-new.tpl")
+	data, err := ioutil.ReadFile(file)
+	assert.Nil(t, err)
+	assert.Equal(t, string(data), template.New)
+}
+
+func TestRevertTemplate(t *testing.T) {
+	name := "model-new.tpl"
+	err := util.InitTemplates(category, templates)
+	assert.Nil(t, err)
+
+	dir, err := util.GetTemplateDir(category)
+	assert.Nil(t, err)
+
+	file := filepath.Join(dir, name)
+	data, err := ioutil.ReadFile(file)
+	assert.Nil(t, err)
+
+	modifyData := string(data) + "modify"
+	err = util.CreateTemplate(category, name, modifyData)
+	assert.Nil(t, err)
+
+	data, err = ioutil.ReadFile(file)
+	assert.Nil(t, err)
+
+	assert.Equal(t, string(data), modifyData)
+
+	assert.Nil(t, RevertTemplate(name))
+
+	data, err = ioutil.ReadFile(file)
+	assert.Nil(t, err)
+	assert.Equal(t, template.New, string(data))
+}
+
+func TestClean(t *testing.T) {
+	name := "model-new.tpl"
+	err := util.InitTemplates(category, templates)
+	assert.Nil(t, err)
+
+	assert.Nil(t, Clean())
+
+	dir, err := util.GetTemplateDir(category)
+	assert.Nil(t, err)
+
+	file := filepath.Join(dir, name)
+	_, err = ioutil.ReadFile(file)
+	assert.NotNil(t, err)
+}
+
+func TestUpdate(t *testing.T) {
+	name := "model-new.tpl"
+	err := util.InitTemplates(category, templates)
+	assert.Nil(t, err)
+
+	dir, err := util.GetTemplateDir(category)
+	assert.Nil(t, err)
+
+	file := filepath.Join(dir, name)
+	data, err := ioutil.ReadFile(file)
+	assert.Nil(t, err)
+
+	modifyData := string(data) + "modify"
+	err = util.CreateTemplate(category, name, modifyData)
+	assert.Nil(t, err)
+
+	data, err = ioutil.ReadFile(file)
+	assert.Nil(t, err)
+
+	assert.Equal(t, string(data), modifyData)
+
+	assert.Nil(t, Update(category))
+
+	data, err = ioutil.ReadFile(file)
+	assert.Nil(t, err)
+	assert.Equal(t, template.New, string(data))
+}

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

@@ -11,8 +11,14 @@ func genTypes(table Table, withCache bool) (string, error) {
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
+
+	text, err := util.LoadTemplate(category, typesTemplateFile, template.Types)
+	if err != nil {
+		return "", err
+	}
+
 	output, err := util.With("types").
 	output, err := util.With("types").
-		Parse(template.Types).
+		Parse(text).
 		Execute(map[string]interface{}{
 		Execute(map[string]interface{}{
 			"withCache":             withCache,
 			"withCache":             withCache,
 			"upperStartCamelObject": table.Name.ToCamel(),
 			"upperStartCamelObject": table.Name.ToCamel(),
@@ -21,5 +27,6 @@ func genTypes(table Table, withCache bool) (string, error) {
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
+
 	return output.String(), nil
 	return output.String(), nil
 }
 }

+ 7 - 1
tools/goctl/model/sql/gen/update.go

@@ -22,8 +22,13 @@ func genUpdate(table Table, withCache bool) (string, error) {
 	}
 	}
 	expressionValues = append(expressionValues, "data."+table.PrimaryKey.Name.ToCamel())
 	expressionValues = append(expressionValues, "data."+table.PrimaryKey.Name.ToCamel())
 	camelTableName := table.Name.ToCamel()
 	camelTableName := table.Name.ToCamel()
+	text, err := util.LoadTemplate(category, updateTemplateFile, template.Update)
+	if err != nil {
+		return "", err
+	}
+
 	output, err := util.With("update").
 	output, err := util.With("update").
-		Parse(template.Update).
+		Parse(text).
 		Execute(map[string]interface{}{
 		Execute(map[string]interface{}{
 			"withCache":             withCache,
 			"withCache":             withCache,
 			"upperStartCamelObject": camelTableName,
 			"upperStartCamelObject": camelTableName,
@@ -36,5 +41,6 @@ func genUpdate(table Table, withCache bool) (string, error) {
 	if err != nil {
 	if err != nil {
 		return "", nil
 		return "", nil
 	}
 	}
+
 	return output.String(), nil
 	return output.String(), nil
 }
 }

+ 6 - 1
tools/goctl/model/sql/gen/vars.go

@@ -14,8 +14,13 @@ func genVars(table Table, withCache bool) (string, error) {
 		keys = append(keys, v.VarExpression)
 		keys = append(keys, v.VarExpression)
 	}
 	}
 	camel := table.Name.ToCamel()
 	camel := table.Name.ToCamel()
+	text, err := util.LoadTemplate(category, varTemplateFile, template.Vars)
+	if err != nil {
+		return "", err
+	}
+
 	output, err := util.With("var").
 	output, err := util.With("var").
-		Parse(template.Vars).
+		Parse(text).
 		GoFmt(true).
 		GoFmt(true).
 		Execute(map[string]interface{}{
 		Execute(map[string]interface{}{
 			"lowerStartCamelObject": stringx.From(camel).UnTitle(),
 			"lowerStartCamelObject": stringx.From(camel).UnTitle(),

+ 4 - 6
tools/goctl/model/sql/parser/parser_test.go

@@ -17,11 +17,9 @@ func TestParseSelect(t *testing.T) {
 }
 }
 
 
 func TestParseCreateTable(t *testing.T) {
 func TestParseCreateTable(t *testing.T) {
-	_, err := Parse("CREATE TABLE `user_snake` (\n  `id` bigint(10) NOT NULL AUTO_INCREMENT,\n  `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',\n  `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',\n  `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',\n  `gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开',\n  `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称',\n  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `name_index` (`name`),\n  KEY `mobile_index` (`mobile`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;")
-	assert.Nil(t, err)
-}
-
-func TestParseCreateTable2(t *testing.T) {
-	_, err := Parse("create table `user_snake` (\n  `id` bigint(10) NOT NULL AUTO_INCREMENT,\n  `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',\n  `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',\n  `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',\n  `gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开',\n  `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称',\n  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `name_index` (`name`),\n  KEY `mobile_index` (`mobile`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;")
+	table, err := Parse("CREATE TABLE `user_snake` (\n  `id` bigint(10) NOT NULL AUTO_INCREMENT,\n  `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',\n  `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',\n  `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',\n  `gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开',\n  `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称',\n  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `name_index` (`name`),\n  KEY `mobile_index` (`mobile`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;")
 	assert.Nil(t, err)
 	assert.Nil(t, err)
+	assert.Equal(t, "user_snake", table.Name.Source())
+	assert.Equal(t, "id", table.PrimaryKey.Name.Source())
+	assert.Equal(t, true, table.ContainsTime())
 }
 }

+ 1 - 0
tools/goctl/model/sql/template/import.go

@@ -15,6 +15,7 @@ var (
 )
 )
 `
 `
 	ImportsNoCache = `import (
 	ImportsNoCache = `import (
+	"database/sql"
 	"strings"
 	"strings"
 	{{if .time}}"time"{{end}}
 	{{if .time}}"time"{{end}}
 
 

+ 5 - 5
tools/goctl/model/sql/template/insert.go

@@ -1,15 +1,15 @@
 package template
 package template
 
 
 var Insert = `
 var Insert = `
-func (m *{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}) error {
+func (m *{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}) (sql.Result,error) {
 	{{if .withCache}}{{if .containsIndexCache}}{{.keys}}
 	{{if .withCache}}{{if .containsIndexCache}}{{.keys}}
-    _, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
+    ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
 		query := ` + "`" + `insert into ` + "`" + ` + m.table + ` + "` (` + " + `{{.lowerStartCamelObject}}RowsExpectAutoSet` + " + `) values ({{.expression}})` " + `
 		query := ` + "`" + `insert into ` + "`" + ` + m.table + ` + "` (` + " + `{{.lowerStartCamelObject}}RowsExpectAutoSet` + " + `) values ({{.expression}})` " + `
 		return conn.Exec(query, {{.expressionValues}})
 		return conn.Exec(query, {{.expressionValues}})
 	}, {{.keyValues}}){{else}}query := ` + "`" + `insert into ` + "`" + ` + m.table + ` + "` (` + " + `{{.lowerStartCamelObject}}RowsExpectAutoSet` + " + `) values ({{.expression}})` " + `
 	}, {{.keyValues}}){{else}}query := ` + "`" + `insert into ` + "`" + ` + m.table + ` + "` (` + " + `{{.lowerStartCamelObject}}RowsExpectAutoSet` + " + `) values ({{.expression}})` " + `
-    _,err:=m.ExecNoCache(query, {{.expressionValues}})
+    ret,err:=m.ExecNoCache(query, {{.expressionValues}})
 	{{end}}{{else}}query := ` + "`" + `insert into ` + "`" + ` + m.table + ` + "` (` + " + `{{.lowerStartCamelObject}}RowsExpectAutoSet` + " + `) values ({{.expression}})` " + `
 	{{end}}{{else}}query := ` + "`" + `insert into ` + "`" + ` + m.table + ` + "` (` + " + `{{.lowerStartCamelObject}}RowsExpectAutoSet` + " + `) values ({{.expression}})` " + `
-    _,err:=m.conn.Exec(query, {{.expressionValues}}){{end}}
-	return err
+    ret,err:=m.conn.Exec(query, {{.expressionValues}}){{end}}
+	return ret,err
 }
 }
 `
 `

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

@@ -1,13 +1,13 @@
 package template
 package template
 
 
 var Update = `
 var Update = `
-func (m *{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}) error {
+func (m *{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}) (sql.Result,error) {
 	{{if .withCache}}{{.primaryCacheKey}}
 	{{if .withCache}}{{.primaryCacheKey}}
-    _, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
+    ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
 		query := ` + "`" + `update ` + "` +" + `m.table +` + "` " + `set ` + "` +" + `{{.lowerStartCamelObject}}RowsWithPlaceHolder` + " + `" + ` where {{.originalPrimaryKey}} = ?` + "`" + `
 		query := ` + "`" + `update ` + "` +" + `m.table +` + "` " + `set ` + "` +" + `{{.lowerStartCamelObject}}RowsWithPlaceHolder` + " + `" + ` where {{.originalPrimaryKey}} = ?` + "`" + `
 		return conn.Exec(query, {{.expressionValues}})
 		return conn.Exec(query, {{.expressionValues}})
 	}, {{.primaryKeyVariable}}){{else}}query := ` + "`" + `update ` + "` +" + `m.table +` + "` " + `set ` + "` +" + `{{.lowerStartCamelObject}}RowsWithPlaceHolder` + " + `" + ` where {{.originalPrimaryKey}} = ?` + "`" + `
 	}, {{.primaryKeyVariable}}){{else}}query := ` + "`" + `update ` + "` +" + `m.table +` + "` " + `set ` + "` +" + `{{.lowerStartCamelObject}}RowsWithPlaceHolder` + " + `" + ` where {{.originalPrimaryKey}} = ?` + "`" + `
-    _,err:=m.conn.Exec(query, {{.expressionValues}}){{end}}
-	return err
+    ret,err:=m.conn.Exec(query, {{.expressionValues}}){{end}}
+	return ret,err
 }
 }
 `
 `

+ 4 - 0
tools/goctl/rpc/CHANGELOG.md

@@ -1,5 +1,9 @@
 # Change log
 # Change log
 
 
+## 2020-10-19
+
+* 增加template
+
 ## 2020-09-10
 ## 2020-09-10
 
 
 * rpc greet服务一键生成
 * rpc greet服务一键生成

+ 1 - 1
tools/goctl/rpc/gen/gen.go

@@ -32,7 +32,7 @@ func NewDefaultRpcGenerator(ctx *ctx.RpcContext) *defaultRpcGenerator {
 }
 }
 
 
 func (g *defaultRpcGenerator) Generate() (err error) {
 func (g *defaultRpcGenerator) Generate() (err error) {
-	g.Ctx.Info(aurora.Blue("-> goctl rpc reference documents: ").String() + "「https://github.com/tal-tech/go-zero/blob/master/doc/goctl-rpc.md」")
+	g.Ctx.Info(aurora.Blue("-> goctl rpc reference documents: ").String() + "「https://github.com/tal-tech/zero-doc/blob/main/doc/goctl-rpc.md」")
 	g.Ctx.Warning("-> generating rpc code ...")
 	g.Ctx.Warning("-> generating rpc code ...")
 	defer func() {
 	defer func() {
 		if err == nil {
 		if err == nil {

+ 21 - 5
tools/goctl/rpc/gen/gencall.go

@@ -123,7 +123,11 @@ func (g *defaultRpcGenerator) genCall() error {
 
 
 	filename := filepath.Join(callPath, typesFilename)
 	filename := filepath.Join(callPath, typesFilename)
 	head := util.GetHead(g.Ctx.ProtoSource)
 	head := util.GetHead(g.Ctx.ProtoSource)
-	err = util.With("types").GoFmt(true).Parse(callTemplateTypes).SaveTo(map[string]interface{}{
+	text, err := util.LoadTemplate(category, callTypesTemplateFile, callTemplateTypes)
+	if err != nil {
+		return err
+	}
+	err = util.With("types").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
 		"head":                  head,
 		"head":                  head,
 		"const":                 constLit,
 		"const":                 constLit,
 		"filePackage":           service.Name.Lower(),
 		"filePackage":           service.Name.Lower(),
@@ -145,8 +149,11 @@ func (g *defaultRpcGenerator) genCall() error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-
-	err = util.With("shared").GoFmt(true).Parse(callTemplateText).SaveTo(map[string]interface{}{
+	text, err = util.LoadTemplate(category, callTemplateFile, callTemplateText)
+	if err != nil {
+		return err
+	}
+	err = util.With("shared").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
 		"name":        service.Name.Lower(),
 		"name":        service.Name.Lower(),
 		"head":        head,
 		"head":        head,
 		"filePackage": service.Name.Lower(),
 		"filePackage": service.Name.Lower(),
@@ -166,7 +173,11 @@ func (g *defaultRpcGenerator) genFunction(service *parser.RpcService) ([]string,
 	imports.AddStr(fmt.Sprintf(`%v "%v"`, pkgName, g.mustGetPackage(dirPb)))
 	imports.AddStr(fmt.Sprintf(`%v "%v"`, pkgName, g.mustGetPackage(dirPb)))
 	for _, method := range service.Funcs {
 	for _, method := range service.Funcs {
 		imports.AddStr(g.ast.Imports[method.ParameterIn.Package])
 		imports.AddStr(g.ast.Imports[method.ParameterIn.Package])
-		buffer, err := util.With("sharedFn").Parse(callFunctionTemplate).Execute(map[string]interface{}{
+		text, err := util.LoadTemplate(category, callFunctionTemplateFile, callFunctionTemplate)
+		if err != nil {
+			return nil, nil, err
+		}
+		buffer, err := util.With("sharedFn").Parse(text).Execute(map[string]interface{}{
 			"rpcServiceName": service.Name.Title(),
 			"rpcServiceName": service.Name.Title(),
 			"method":         method.Name.Title(),
 			"method":         method.Name.Title(),
 			"package":        pkgName,
 			"package":        pkgName,
@@ -189,7 +200,12 @@ func (g *defaultRpcGenerator) getInterfaceFuncs(service *parser.RpcService) ([]s
 	functions := make([]string, 0)
 	functions := make([]string, 0)
 
 
 	for _, method := range service.Funcs {
 	for _, method := range service.Funcs {
-		buffer, err := util.With("interfaceFn").Parse(callInterfaceFunctionTemplate).Execute(
+		text, err := util.LoadTemplate(category, callInterfaceFunctionTemplateFile, callInterfaceFunctionTemplate)
+		if err != nil {
+			return nil, err
+		}
+
+		buffer, err := util.With("interfaceFn").Parse(text).Execute(
 			map[string]interface{}{
 			map[string]interface{}{
 				"hasComment": method.HaveDoc(),
 				"hasComment": method.HaveDoc(),
 				"comment":    method.GetDoc(),
 				"comment":    method.GetDoc(),

+ 7 - 1
tools/goctl/rpc/gen/genconfig.go

@@ -23,5 +23,11 @@ func (g *defaultRpcGenerator) genConfig() error {
 	if util.FileExists(fileName) {
 	if util.FileExists(fileName) {
 		return nil
 		return nil
 	}
 	}
-	return ioutil.WriteFile(fileName, []byte(configTemplate), os.ModePerm)
+
+	text, err := util.LoadTemplate(category, configTemplateFileFile, configTemplate)
+	if err != nil {
+		return err
+	}
+
+	return ioutil.WriteFile(fileName, []byte(text), os.ModePerm)
 }
 }

+ 6 - 1
tools/goctl/rpc/gen/genetc.go

@@ -22,7 +22,12 @@ func (g *defaultRpcGenerator) genEtc() error {
 		return nil
 		return nil
 	}
 	}
 
 
-	return util.With("etc").Parse(etcTemplate).SaveTo(map[string]interface{}{
+	text, err := util.LoadTemplate(category, etcTemplateFileFile, etcTemplate)
+	if err != nil {
+		return err
+	}
+
+	return util.With("etc").Parse(text).SaveTo(map[string]interface{}{
 		"serviceName": g.Ctx.ServiceName.Lower(),
 		"serviceName": g.Ctx.ServiceName.Lower(),
 	}, fileName, false)
 	}, fileName, false)
 }
 }

+ 12 - 2
tools/goctl/rpc/gen/genlogic.go

@@ -61,7 +61,11 @@ func (g *defaultRpcGenerator) genLogic() error {
 			svcImport := fmt.Sprintf(`"%v"`, g.mustGetPackage(dirSvc))
 			svcImport := fmt.Sprintf(`"%v"`, g.mustGetPackage(dirSvc))
 			imports.AddStr(svcImport)
 			imports.AddStr(svcImport)
 			imports.AddStr(importList...)
 			imports.AddStr(importList...)
-			err = util.With("logic").GoFmt(true).Parse(logicTemplate).SaveTo(map[string]interface{}{
+			text, err := util.LoadTemplate(category, logicTemplateFileFile, logicTemplate)
+			if err != nil {
+				return err
+			}
+			err = util.With("logic").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
 				"logicName": fmt.Sprintf("%sLogic", method.Name.Title()),
 				"logicName": fmt.Sprintf("%sLogic", method.Name.Title()),
 				"functions": functions,
 				"functions": functions,
 				"imports":   strings.Join(imports.KeysStr(), util.NL),
 				"imports":   strings.Join(imports.KeysStr(), util.NL),
@@ -82,7 +86,12 @@ func (g *defaultRpcGenerator) genLogicFunction(packageName string, method *parse
 	}
 	}
 	imports.AddStr(g.ast.Imports[method.ParameterIn.Package])
 	imports.AddStr(g.ast.Imports[method.ParameterIn.Package])
 	imports.AddStr(g.ast.Imports[method.ParameterOut.Package])
 	imports.AddStr(g.ast.Imports[method.ParameterOut.Package])
-	buffer, err := util.With("fun").Parse(logicFunctionTemplate).Execute(map[string]interface{}{
+	text, err := util.LoadTemplate(category, logicFuncTemplateFileFile, logicFunctionTemplate)
+	if err != nil {
+		return "", nil, err
+	}
+
+	buffer, err := util.With("fun").Parse(text).Execute(map[string]interface{}{
 		"logicName":    fmt.Sprintf("%sLogic", method.Name.Title()),
 		"logicName":    fmt.Sprintf("%sLogic", method.Name.Title()),
 		"method":       method.Name.Title(),
 		"method":       method.Name.Title(),
 		"request":      method.ParameterIn.StarExpression,
 		"request":      method.ParameterIn.StarExpression,
@@ -94,6 +103,7 @@ func (g *defaultRpcGenerator) genLogicFunction(packageName string, method *parse
 	if err != nil {
 	if err != nil {
 		return "", nil, err
 		return "", nil, err
 	}
 	}
+
 	functions = append(functions, buffer.String())
 	functions = append(functions, buffer.String())
 	return strings.Join(functions, util.NL), imports.KeysStr(), nil
 	return strings.Join(functions, util.NL), imports.KeysStr(), nil
 }
 }

+ 6 - 1
tools/goctl/rpc/gen/genmain.go

@@ -58,7 +58,12 @@ func (g *defaultRpcGenerator) genMain() error {
 	imports = append(imports, configImport, pbImport, remoteImport, svcImport)
 	imports = append(imports, configImport, pbImport, remoteImport, svcImport)
 	srv, registers := g.genServer(pkg, file.Service)
 	srv, registers := g.genServer(pkg, file.Service)
 	head := util.GetHead(g.Ctx.ProtoSource)
 	head := util.GetHead(g.Ctx.ProtoSource)
-	return util.With("main").GoFmt(true).Parse(mainTemplate).SaveTo(map[string]interface{}{
+	text, err := util.LoadTemplate(category, mainTemplateFile, mainTemplate)
+	if err != nil {
+		return err
+	}
+
+	return util.With("main").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
 		"head":        head,
 		"head":        head,
 		"package":     pkg,
 		"package":     pkg,
 		"serviceName": g.Ctx.ServiceName.Lower(),
 		"serviceName": g.Ctx.ServiceName.Lower(),

+ 1 - 0
tools/goctl/rpc/gen/genpb.go

@@ -18,6 +18,7 @@ const (
 
 
 func (g *defaultRpcGenerator) genPb() error {
 func (g *defaultRpcGenerator) genPb() error {
 	pbPath := g.dirM[dirPb]
 	pbPath := g.dirM[dirPb]
+	// deprecated: containsAny will be removed in the feature
 	imports, containsAny, err := parser.ParseImport(g.Ctx.ProtoFileSrc)
 	imports, containsAny, err := parser.ParseImport(g.Ctx.ProtoFileSrc)
 	if err != nil {
 	if err != nil {
 		return err
 		return err

+ 14 - 2
tools/goctl/rpc/gen/genserver.go

@@ -59,8 +59,14 @@ func (g *defaultRpcGenerator) genHandler() error {
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
+
 		imports.AddStr(importList...)
 		imports.AddStr(importList...)
-		err = util.With("server").GoFmt(true).Parse(serverTemplate).SaveTo(map[string]interface{}{
+		text, err := util.LoadTemplate(category, serverTemplateFile, serverTemplate)
+		if err != nil {
+			return err
+		}
+
+		err = util.With("server").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
 			"head":    head,
 			"head":    head,
 			"types":   fmt.Sprintf(typeFmt, service.Name.Title()),
 			"types":   fmt.Sprintf(typeFmt, service.Name.Title()),
 			"server":  service.Name.Title(),
 			"server":  service.Name.Title(),
@@ -85,7 +91,12 @@ func (g *defaultRpcGenerator) genFunctions(service *parser.RpcService) ([]string
 		}
 		}
 		imports.AddStr(g.ast.Imports[method.ParameterIn.Package])
 		imports.AddStr(g.ast.Imports[method.ParameterIn.Package])
 		imports.AddStr(g.ast.Imports[method.ParameterOut.Package])
 		imports.AddStr(g.ast.Imports[method.ParameterOut.Package])
-		buffer, err := util.With("func").Parse(functionTemplate).Execute(map[string]interface{}{
+		text, err := util.LoadTemplate(category, serverFuncTemplateFile, functionTemplate)
+		if err != nil {
+			return nil, nil, err
+		}
+
+		buffer, err := util.With("func").Parse(text).Execute(map[string]interface{}{
 			"server":     service.Name.Title(),
 			"server":     service.Name.Title(),
 			"logicName":  fmt.Sprintf("%sLogic", method.Name.Title()),
 			"logicName":  fmt.Sprintf("%sLogic", method.Name.Title()),
 			"method":     method.Name.Title(),
 			"method":     method.Name.Title(),
@@ -98,6 +109,7 @@ func (g *defaultRpcGenerator) genFunctions(service *parser.RpcService) ([]string
 		if err != nil {
 		if err != nil {
 			return nil, nil, err
 			return nil, nil, err
 		}
 		}
+
 		functionList = append(functionList, buffer.String())
 		functionList = append(functionList, buffer.String())
 	}
 	}
 	return functionList, imports.KeysStr(), nil
 	return functionList, imports.KeysStr(), nil

+ 6 - 1
tools/goctl/rpc/gen/gensvc.go

@@ -25,7 +25,12 @@ func NewServiceContext(c config.Config) *ServiceContext {
 func (g *defaultRpcGenerator) genSvc() error {
 func (g *defaultRpcGenerator) genSvc() error {
 	svcPath := g.dirM[dirSvc]
 	svcPath := g.dirM[dirSvc]
 	fileName := filepath.Join(svcPath, fileServiceContext)
 	fileName := filepath.Join(svcPath, fileServiceContext)
-	return util.With("svc").GoFmt(true).Parse(svcTemplate).SaveTo(map[string]interface{}{
+	text, err := util.LoadTemplate(category, svcTemplateFile, svcTemplate)
+	if err != nil {
+		return err
+	}
+
+	return util.With("svc").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
 		"imports": fmt.Sprintf(`"%v"`, g.mustGetPackage(dirConfig)),
 		"imports": fmt.Sprintf(`"%v"`, g.mustGetPackage(dirConfig)),
 	}, fileName, false)
 	}, fileName, false)
 }
 }

+ 59 - 0
tools/goctl/rpc/gen/rpctemplate.go

@@ -0,0 +1,59 @@
+package gen
+
+import (
+	"path/filepath"
+	"strings"
+
+	"github.com/logrusorgru/aurora"
+	"github.com/tal-tech/go-zero/tools/goctl/util"
+	"github.com/tal-tech/go-zero/tools/goctl/util/console"
+	"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
+)
+
+const rpcTemplateText = `syntax = "proto3";
+
+package {{.package}};
+
+message Request {
+  string ping = 1;
+}
+
+message Response {
+  string pong = 1;
+}
+
+service {{.serviceName}} {
+  rpc Ping(Request) returns(Response);
+}
+`
+
+type rpcTemplate struct {
+	out string
+	console.Console
+}
+
+func NewRpcTemplate(out string, idea bool) *rpcTemplate {
+	return &rpcTemplate{
+		out:     out,
+		Console: console.NewConsole(idea),
+	}
+}
+
+func (r *rpcTemplate) MustGenerate(showState bool) {
+	r.Info(aurora.Blue("-> goctl rpc reference documents: ").String() + "「https://github.com/tal-tech/zero-doc/blob/main/doc/goctl-rpc.md」")
+	r.Info("-> generating template...")
+	protoFilename := filepath.Base(r.out)
+	serviceName := stringx.From(strings.TrimSuffix(protoFilename, filepath.Ext(protoFilename)))
+	text, err := util.LoadTemplate(category, rpcTemplateFile, rpcTemplateText)
+	r.Must(err)
+
+	err = util.With("t").Parse(text).SaveTo(map[string]string{
+		"package":     serviceName.UnTitle(),
+		"serviceName": serviceName.Title(),
+	}, r.out, false)
+	r.Must(err)
+
+	if showState {
+		r.Success("Done.")
+	}
+}

+ 49 - 34
tools/goctl/rpc/gen/template.go

@@ -1,54 +1,69 @@
 package gen
 package gen
 
 
 import (
 import (
-	"path/filepath"
-	"strings"
+	"fmt"
 
 
 	"github.com/tal-tech/go-zero/tools/goctl/util"
 	"github.com/tal-tech/go-zero/tools/goctl/util"
-	"github.com/tal-tech/go-zero/tools/goctl/util/console"
-	"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
+	"github.com/urfave/cli"
 )
 )
 
 
-const rpcTemplateText = `syntax = "proto3";
-
-package {{.package}};
+const (
+	category                          = "rpc"
+	callTemplateFile                  = "call.tpl"
+	callTypesTemplateFile             = "call-types.tpl"
+	callInterfaceFunctionTemplateFile = "call-interface-func.tpl"
+	callFunctionTemplateFile          = "call-func.tpl"
+	configTemplateFileFile            = "config.tpl"
+	etcTemplateFileFile               = "etc.tpl"
+	logicTemplateFileFile             = "logic.tpl"
+	logicFuncTemplateFileFile         = "logic-func.tpl"
+	mainTemplateFile                  = "main.tpl"
+	serverTemplateFile                = "server.tpl"
+	serverFuncTemplateFile            = "server-func.tpl"
+	svcTemplateFile                   = "svc.tpl"
+	rpcTemplateFile                   = "template.tpl"
+)
 
 
-message Request {
-  string ping = 1;
+var templates = map[string]string{
+	callTemplateFile:                  callTemplateText,
+	callTypesTemplateFile:             callTemplateTypes,
+	callInterfaceFunctionTemplateFile: callInterfaceFunctionTemplate,
+	callFunctionTemplateFile:          callFunctionTemplate,
+	configTemplateFileFile:            configTemplate,
+	etcTemplateFileFile:               etcTemplate,
+	logicTemplateFileFile:             logicTemplate,
+	logicFuncTemplateFileFile:         logicFunctionTemplate,
+	mainTemplateFile:                  mainTemplate,
+	serverTemplateFile:                serverTemplate,
+	serverFuncTemplateFile:            functionTemplate,
+	svcTemplateFile:                   svcTemplate,
+	rpcTemplateFile:                   rpcTemplateText,
 }
 }
 
 
-message Response {
-  string pong = 1;
+func GenTemplates(_ *cli.Context) error {
+	return util.InitTemplates(category, templates)
 }
 }
 
 
-service {{.serviceName}} {
-  rpc Ping(Request) returns(Response);
+func RevertTemplate(name string) error {
+	content, ok := templates[name]
+	if !ok {
+		return fmt.Errorf("%s: no such file name", name)
+	}
+	return util.CreateTemplate(category, name, content)
 }
 }
-`
 
 
-type rpcTemplate struct {
-	out string
-	console.Console
+func Clean() error {
+	return util.Clean(category)
 }
 }
 
 
-func NewRpcTemplate(out string, idea bool) *rpcTemplate {
-	return &rpcTemplate{
-		out:     out,
-		Console: console.NewConsole(idea),
+func Update(category string) error {
+	err := Clean()
+	if err != nil {
+		return err
 	}
 	}
+	return util.InitTemplates(category, templates)
 }
 }
 
 
-func (r *rpcTemplate) MustGenerate(showState bool) {
-	r.Info("查看rpc生成请移步至「https://github.com/tal-tech/zero-doc/blob/main/doc/goctl-rpc.md」")
-	r.Info("generating template...")
-	protoFilename := filepath.Base(r.out)
-	serviceName := stringx.From(strings.TrimSuffix(protoFilename, filepath.Ext(protoFilename)))
-	err := util.With("t").Parse(rpcTemplateText).SaveTo(map[string]string{
-		"package":     serviceName.UnTitle(),
-		"serviceName": serviceName.Title(),
-	}, r.out, false)
-	r.Must(err)
-	if showState {
-		r.Success("Done.")
-	}
+func GetCategory() string {
+	return category
 }
 }

+ 92 - 0
tools/goctl/rpc/gen/template_test.go

@@ -0,0 +1,92 @@
+package gen
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/tal-tech/go-zero/tools/goctl/util"
+)
+
+func TestGenTemplates(t *testing.T) {
+	err := util.InitTemplates(category, templates)
+	assert.Nil(t, err)
+	dir, err := util.GetTemplateDir(category)
+	assert.Nil(t, err)
+	file := filepath.Join(dir, "main.tpl")
+	data, err := ioutil.ReadFile(file)
+	assert.Nil(t, err)
+	assert.Equal(t, string(data), mainTemplate)
+}
+
+func TestRevertTemplate(t *testing.T) {
+	name := "main.tpl"
+	err := util.InitTemplates(category, templates)
+	assert.Nil(t, err)
+
+	dir, err := util.GetTemplateDir(category)
+	assert.Nil(t, err)
+
+	file := filepath.Join(dir, name)
+	data, err := ioutil.ReadFile(file)
+	assert.Nil(t, err)
+
+	modifyData := string(data) + "modify"
+	err = util.CreateTemplate(category, name, modifyData)
+	assert.Nil(t, err)
+
+	data, err = ioutil.ReadFile(file)
+	assert.Nil(t, err)
+
+	assert.Equal(t, string(data), modifyData)
+
+	assert.Nil(t, RevertTemplate(name))
+
+	data, err = ioutil.ReadFile(file)
+	assert.Nil(t, err)
+	assert.Equal(t, mainTemplate, string(data))
+}
+
+func TestClean(t *testing.T) {
+	name := "main.tpl"
+	err := util.InitTemplates(category, templates)
+	assert.Nil(t, err)
+
+	assert.Nil(t, Clean())
+
+	dir, err := util.GetTemplateDir(category)
+	assert.Nil(t, err)
+
+	file := filepath.Join(dir, name)
+	_, err = ioutil.ReadFile(file)
+	assert.NotNil(t, err)
+}
+
+func TestUpdate(t *testing.T) {
+	name := "main.tpl"
+	err := util.InitTemplates(category, templates)
+	assert.Nil(t, err)
+
+	dir, err := util.GetTemplateDir(category)
+	assert.Nil(t, err)
+
+	file := filepath.Join(dir, name)
+	data, err := ioutil.ReadFile(file)
+	assert.Nil(t, err)
+
+	modifyData := string(data) + "modify"
+	err = util.CreateTemplate(category, name, modifyData)
+	assert.Nil(t, err)
+
+	data, err = ioutil.ReadFile(file)
+	assert.Nil(t, err)
+
+	assert.Equal(t, string(data), modifyData)
+
+	assert.Nil(t, Update(category))
+
+	data, err = ioutil.ReadFile(file)
+	assert.Nil(t, err)
+	assert.Equal(t, mainTemplate, string(data))
+}

+ 1 - 0
tools/goctl/rpc/parser/pbast.go

@@ -97,6 +97,7 @@ type (
 	}
 	}
 	// parsing for rpc
 	// parsing for rpc
 	PbAst struct {
 	PbAst struct {
+		// deprecated: containsAny will be removed in the feature
 		ContainsAny bool
 		ContainsAny bool
 		Imports     map[string]string
 		Imports     map[string]string
 		Structure   map[string]*Struct
 		Structure   map[string]*Struct

+ 4 - 3
tools/goctl/rpc/parser/proto.go

@@ -47,9 +47,10 @@ type (
 	}
 	}
 
 
 	Proto struct {
 	Proto struct {
-		Package     string
-		Import      []*Import
-		PbSrc       string
+		Package string
+		Import  []*Import
+		PbSrc   string
+		// deprecated: containsAny will be removed in the feature
 		ContainsAny bool
 		ContainsAny bool
 		Message     map[string]lang.PlaceholderType
 		Message     map[string]lang.PlaceholderType
 		Enum        map[string]*Enum
 		Enum        map[string]*Enum

+ 69 - 0
tools/goctl/tpl/templates.go

@@ -6,6 +6,8 @@ import (
 	"github.com/logrusorgru/aurora"
 	"github.com/logrusorgru/aurora"
 	"github.com/tal-tech/go-zero/core/errorx"
 	"github.com/tal-tech/go-zero/core/errorx"
 	"github.com/tal-tech/go-zero/tools/goctl/api/gogen"
 	"github.com/tal-tech/go-zero/tools/goctl/api/gogen"
+	modelgen "github.com/tal-tech/go-zero/tools/goctl/model/sql/gen"
+	rpcgen "github.com/tal-tech/go-zero/tools/goctl/rpc/gen"
 	"github.com/tal-tech/go-zero/tools/goctl/util"
 	"github.com/tal-tech/go-zero/tools/goctl/util"
 	"github.com/urfave/cli"
 	"github.com/urfave/cli"
 )
 )
@@ -17,6 +19,12 @@ func GenTemplates(ctx *cli.Context) error {
 		func() error {
 		func() error {
 			return gogen.GenTemplates(ctx)
 			return gogen.GenTemplates(ctx)
 		},
 		},
+		func() error {
+			return modelgen.GenTemplates(ctx)
+		},
+		func() error {
+			return rpcgen.GenTemplates(ctx)
+		},
 	); err != nil {
 	); err != nil {
 		return err
 		return err
 	}
 	}
@@ -31,3 +39,64 @@ func GenTemplates(ctx *cli.Context) error {
 
 
 	return nil
 	return nil
 }
 }
+
+func CleanTemplates(_ *cli.Context) error {
+	err := errorx.Chain(
+		func() error {
+			return gogen.Clean()
+		},
+		func() error {
+			return modelgen.Clean()
+		},
+		func() error {
+			return rpcgen.Clean()
+		},
+	)
+	if err != nil {
+		return err
+	}
+
+	fmt.Printf("%s\n", aurora.Green("template are clean!"))
+	return nil
+}
+
+func UpdateTemplates(ctx *cli.Context) (err error) {
+	category := ctx.String("category")
+	defer func() {
+		if err == nil {
+			fmt.Println(aurora.Green(fmt.Sprintf("%s template are update!", category)).String())
+		}
+	}()
+	switch category {
+	case gogen.GetCategory():
+		return gogen.Update(category)
+	case rpcgen.GetCategory():
+		return rpcgen.Update(category)
+	case modelgen.GetCategory():
+		return modelgen.Update(category)
+	default:
+		err = fmt.Errorf("unexpected category: %s", category)
+		return
+	}
+}
+
+func RevertTemplates(ctx *cli.Context) (err error) {
+	category := ctx.String("category")
+	filename := ctx.String("name")
+	defer func() {
+		if err == nil {
+			fmt.Println(aurora.Green(fmt.Sprintf("%s template are reverted!", filename)).String())
+		}
+	}()
+	switch category {
+	case gogen.GetCategory():
+		return gogen.RevertTemplate(filename)
+	case rpcgen.GetCategory():
+		return rpcgen.RevertTemplate(filename)
+	case modelgen.GetCategory():
+		return modelgen.RevertTemplate(filename)
+	default:
+		err = fmt.Errorf("unexpected category: %s", category)
+		return
+	}
+}

+ 19 - 4
tools/goctl/util/files.go

@@ -28,7 +28,7 @@ func InitTemplates(category string, templates map[string]string) error {
 	}
 	}
 
 
 	for k, v := range templates {
 	for k, v := range templates {
-		if err := createTemplate(filepath.Join(dir, k), v); err != nil {
+		if err := createTemplate(filepath.Join(dir, k), v, false); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -36,6 +36,22 @@ func InitTemplates(category string, templates map[string]string) error {
 	return nil
 	return nil
 }
 }
 
 
+func CreateTemplate(category, name, content string) error {
+	dir, err := GetTemplateDir(category)
+	if err != nil {
+		return err
+	}
+	return createTemplate(filepath.Join(dir, name), content, true)
+}
+
+func Clean(category string) error {
+	dir, err := GetTemplateDir(category)
+	if err != nil {
+		return err
+	}
+	return os.RemoveAll(dir)
+}
+
 func LoadTemplate(category, file, builtin string) (string, error) {
 func LoadTemplate(category, file, builtin string) (string, error) {
 	dir, err := GetTemplateDir(category)
 	dir, err := GetTemplateDir(category)
 	if err != nil {
 	if err != nil {
@@ -55,9 +71,8 @@ func LoadTemplate(category, file, builtin string) (string, error) {
 	return string(content), nil
 	return string(content), nil
 }
 }
 
 
-func createTemplate(file, content string) error {
-	if FileExists(file) {
-		println(1)
+func createTemplate(file, content string, force bool) error {
+	if FileExists(file) && !force {
 		return nil
 		return nil
 	}
 	}