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
master
Keson 4 years ago committed by GitHub
parent fe0d0687f5
commit 41964f9d52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,6 +1,8 @@
package gogen
import (
"fmt"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/urfave/cli"
)
@ -27,3 +29,27 @@ var templates = map[string]string{
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 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
}

@ -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))
}

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

@ -3,6 +3,7 @@ package main
import (
"fmt"
"os"
"runtime"
"github.com/tal-tech/go-zero/core/logx"
"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/configgen"
"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"
rpc "github.com/tal-tech/go-zero/tools/goctl/rpc/command"
"github.com/tal-tech/go-zero/tools/goctl/tpl"
"github.com/urfave/cli"
)
var (
BuildTime = "not set"
commands = []cli.Command{
BuildVersion = "20201021"
commands = []cli.Command{
{
Name: "api",
Usage: "generate api related files",
@ -328,14 +329,46 @@ var (
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.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
// cli already print error messages
if err := app.Run(os.Args); err != nil {

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

@ -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)
}

@ -4,4 +4,4 @@
goctl model mysql ddl -src="./sql/user.sql" -dir="./sql/model" -c
# 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"

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

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

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

@ -10,7 +10,12 @@ import (
)
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
camelTableName := table.Name.ToCamel()
for _, field := range table.Fields {
@ -33,10 +38,16 @@ func genFindOneByField(table Table, withCache bool) (string, string, error) {
if err != nil {
return "", "", err
}
list = append(list, output.String())
}
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,
"primaryKeyLeft": table.CacheKey[table.PrimaryKey.Name.Source()].Left,
"lowerStartCamelObject": stringx.From(camelTableName).UnTitle(),
@ -45,6 +56,7 @@ func genFindOneByField(table Table, withCache bool) (string, string, error) {
if err != nil {
return "", "", err
}
return strings.Join(list, "\n"), out.String(), nil
}
return strings.Join(list, "\n"), "", nil

@ -7,20 +7,32 @@ import (
func genImports(withCache, timeImport bool) (string, error) {
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,
})
if err != nil {
return "", err
}
return buffer.String(), nil
} 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,
})
if err != nil {
return "", err
}
return buffer.String(), nil
}
}

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

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

@ -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)
}
}

@ -6,8 +6,13 @@ import (
)
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").
Parse(template.New).
Parse(text).
Execute(map[string]interface{}{
"withCache": withCache,
"upperStartCamelObject": table.Name.ToCamel(),
@ -15,5 +20,6 @@ func genNew(table Table, withCache bool) (string, error) {
if err != nil {
return "", err
}
return output.String(), nil
}

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

@ -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
}

@ -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))
}

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

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

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

@ -17,11 +17,9 @@ func TestParseSelect(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.Equal(t, "user_snake", table.Name.Source())
assert.Equal(t, "id", table.PrimaryKey.Name.Source())
assert.Equal(t, true, table.ContainsTime())
}

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

@ -1,15 +1,15 @@
package template
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}}
_, 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}})` " + `
return conn.Exec(query, {{.expressionValues}})
}, {{.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}})` " + `
_,err:=m.conn.Exec(query, {{.expressionValues}}){{end}}
return err
ret,err:=m.conn.Exec(query, {{.expressionValues}}){{end}}
return ret,err
}
`

@ -1,13 +1,13 @@
package template
var Update = `
func (m *{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}) error {
func (m *{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}) (sql.Result,error) {
{{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}} = ?` + "`" + `
return conn.Exec(query, {{.expressionValues}})
}, {{.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
}
`

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

@ -32,7 +32,7 @@ func NewDefaultRpcGenerator(ctx *ctx.RpcContext) *defaultRpcGenerator {
}
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 ...")
defer func() {
if err == nil {

@ -123,7 +123,11 @@ func (g *defaultRpcGenerator) genCall() error {
filename := filepath.Join(callPath, typesFilename)
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,
"const": constLit,
"filePackage": service.Name.Lower(),
@ -145,8 +149,11 @@ func (g *defaultRpcGenerator) genCall() error {
if err != nil {
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(),
"head": head,
"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)))
for _, method := range service.Funcs {
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(),
"method": method.Name.Title(),
"package": pkgName,
@ -189,7 +200,12 @@ func (g *defaultRpcGenerator) getInterfaceFuncs(service *parser.RpcService) ([]s
functions := make([]string, 0)
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{}{
"hasComment": method.HaveDoc(),
"comment": method.GetDoc(),

@ -23,5 +23,11 @@ func (g *defaultRpcGenerator) genConfig() error {
if util.FileExists(fileName) {
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)
}

@ -22,7 +22,12 @@ func (g *defaultRpcGenerator) genEtc() error {
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(),
}, fileName, false)
}

@ -61,7 +61,11 @@ func (g *defaultRpcGenerator) genLogic() error {
svcImport := fmt.Sprintf(`"%v"`, g.mustGetPackage(dirSvc))
imports.AddStr(svcImport)
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()),
"functions": functions,
"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.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()),
"method": method.Name.Title(),
"request": method.ParameterIn.StarExpression,
@ -94,6 +103,7 @@ func (g *defaultRpcGenerator) genLogicFunction(packageName string, method *parse
if err != nil {
return "", nil, err
}
functions = append(functions, buffer.String())
return strings.Join(functions, util.NL), imports.KeysStr(), nil
}

@ -58,7 +58,12 @@ func (g *defaultRpcGenerator) genMain() error {
imports = append(imports, configImport, pbImport, remoteImport, svcImport)
srv, registers := g.genServer(pkg, file.Service)
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,
"package": pkg,
"serviceName": g.Ctx.ServiceName.Lower(),

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

@ -59,8 +59,14 @@ func (g *defaultRpcGenerator) genHandler() error {
if err != nil {
return err
}
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,
"types": fmt.Sprintf(typeFmt, 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.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(),
"logicName": fmt.Sprintf("%sLogic", method.Name.Title()),
"method": method.Name.Title(),
@ -98,6 +109,7 @@ func (g *defaultRpcGenerator) genFunctions(service *parser.RpcService) ([]string
if err != nil {
return nil, nil, err
}
functionList = append(functionList, buffer.String())
}
return functionList, imports.KeysStr(), nil

@ -25,7 +25,12 @@ func NewServiceContext(c config.Config) *ServiceContext {
func (g *defaultRpcGenerator) genSvc() error {
svcPath := g.dirM[dirSvc]
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)),
}, fileName, false)
}

@ -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.")
}
}

@ -1,54 +1,69 @@
package gen
import (
"path/filepath"
"strings"
"fmt"
"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
}

@ -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))
}

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

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

@ -6,6 +6,8 @@ import (
"github.com/logrusorgru/aurora"
"github.com/tal-tech/go-zero/core/errorx"
"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/urfave/cli"
)
@ -17,6 +19,12 @@ func GenTemplates(ctx *cli.Context) error {
func() error {
return gogen.GenTemplates(ctx)
},
func() error {
return modelgen.GenTemplates(ctx)
},
func() error {
return rpcgen.GenTemplates(ctx)
},
); err != nil {
return err
}
@ -31,3 +39,64 @@ func GenTemplates(ctx *cli.Context) error {
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
}
}

@ -28,7 +28,7 @@ func InitTemplates(category string, templates map[string]string) error {
}
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
}
}
@ -36,6 +36,22 @@ func InitTemplates(category string, templates map[string]string) error {
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) {
dir, err := GetTemplateDir(category)
if err != nil {
@ -55,9 +71,8 @@ func LoadTemplate(category, file, builtin string) (string, error) {
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
}

Loading…
Cancel
Save