Compare commits

...

12 Commits

Author SHA1 Message Date
Kevin Wan
e5c560e8ba simplify code, format makefile (#233) 2020-11-28 22:27:58 +08:00
xuezonggui
bed494d904 optimization (#221) 2020-11-28 19:43:39 +08:00
Keson
2dfecda465 modify the service name from proto (#230) 2020-11-28 11:48:44 +08:00
voidint
3ebb1e0221 Improve Makefile robustness (#224) 2020-11-27 23:40:07 +08:00
kingxt
348184904c set default handler value (#228)
* set default value

* set default value
2020-11-26 11:57:02 +08:00
Keson
7a27fa50a1 update version (#226) 2020-11-25 12:04:22 +08:00
Kevin Wan
8d4951c990 check go.mod before build docker image (#225) 2020-11-24 23:19:31 +08:00
Keson
6e57f6c527 feature model interface (#222)
* make variable declaration more concise

* add model interface

* optimize interface methods

* fix: go test failed

* warp returns

* optimize
2020-11-24 22:36:23 +08:00
kingxt
b9ac51b6c3 feature: file namestyle (#223)
* add api filename style

* new feature: config.yaml

* optimize

* optimize logic generation

* check hanlder valid

* optimize

* reactor naming style

* optimize

* optimize test

* optimize gen middleware

* format

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-24 15:11:18 +08:00
kevin
702e8d79ce fix doc errors 2020-11-24 10:39:38 +08:00
kevin
95a9dabf8b format import 2020-11-23 16:35:39 +08:00
Chris
bae66c49c2 1.use local variable i; 2.make sure limiter larger than timer period (#218)
Co-authored-by: chris <feilee1987@163.com>
2020-11-23 16:34:51 +08:00
67 changed files with 1338 additions and 547 deletions

View File

@@ -76,7 +76,6 @@ func (rw *RollingWindow) updateOffset() {
span := rw.span() span := rw.span()
if span > 0 { if span > 0 {
offset := rw.offset offset := rw.offset
// reset expired buckets
start := offset + 1 start := offset + 1
steps := start + span steps := start + span
var remainder int var remainder int
@@ -84,15 +83,16 @@ func (rw *RollingWindow) updateOffset() {
remainder = steps - rw.size remainder = steps - rw.size
steps = rw.size steps = rw.size
} }
// reset expired buckets
for i := start; i < steps; i++ { for i := start; i < steps; i++ {
rw.win.resetBucket(i) rw.win.resetBucket(i)
offset = i
} }
for i := 0; i < remainder; i++ { for i := 0; i < remainder; i++ {
rw.win.resetBucket(i) rw.win.resetBucket(i)
offset = i
} }
rw.offset = offset
rw.offset = (offset + span) % rw.size
rw.lastTime = timex.Now() rw.lastTime = timex.Now()
} }
} }

View File

@@ -41,6 +41,7 @@ func main() {
var allowed, denied int32 var allowed, denied int32
var wait sync.WaitGroup var wait sync.WaitGroup
for i := 0; i < *threads; i++ { for i := 0; i < *threads; i++ {
i := i
wait.Add(1) wait.Add(1)
go func() { go func() {
for { for {

View File

@@ -174,7 +174,7 @@ goctl api -o greet.api
you can check it by curl: you can check it by curl:
```shell ```shell
curl -i http://localhost:8888/greet/from/you curl -i http://localhost:8888/from/you
``` ```
the response looks like: the response looks like:

View File

@@ -120,7 +120,7 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
默认侦听在 8888 端口(可以在配置文件里修改),可以通过 curl 请求: 默认侦听在 8888 端口(可以在配置文件里修改),可以通过 curl 请求:
```shell ```shell
curl -i http://localhost:8888/greet/from/you curl -i http://localhost:8888/from/you
``` ```
返回如下: 返回如下:

View File

@@ -2,13 +2,13 @@ version := $(shell /bin/date "+%Y-%m-%d %H:%M")
build: build:
go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" goctl.go go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" goctl.go
command -v upx &> /dev/null && upx goctl $(if $(shell command -v upx), upx goctl)
mac: mac:
GOOS=darwin go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl-darwin goctl.go GOOS=darwin go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl-darwin goctl.go
command -v upx &> /dev/null && upx goctl-darwin $(if $(shell command -v upx), upx goctl-darwin)
win: win:
GOOS=windows go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl.exe goctl.go GOOS=windows go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl.exe goctl.go
command -v upx &> /dev/null && upx goctl.exe $(if $(shell command -v upx), upx goctl.exe)
linux: linux:
GOOS=linux go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl-linux goctl.go GOOS=linux go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl-linux goctl.go
command -v upx &> /dev/null && upx goctl-linux $(if $(shell command -v upx), upx goctl-linux)

View File

@@ -28,10 +28,10 @@ type response struct {
} }
service {{.serviceName}} { service {{.serviceName}} {
@handler // TODO: set handler name and delete this comment @handler GetUser // TODO: set handler name and delete this comment
get /users/id/:userId(request) returns(response) get /users/id/:userId(request) returns(response)
@handler // TODO: set handler name and delete this comment @handler CreateUser // TODO: set handler name and delete this comment
post /users/create(request) post /users/create(request)
} }
` `

View File

@@ -12,13 +12,13 @@ import (
"time" "time"
"github.com/logrusorgru/aurora" "github.com/logrusorgru/aurora"
"github.com/urfave/cli"
"github.com/tal-tech/go-zero/core/logx" "github.com/tal-tech/go-zero/core/logx"
apiformat "github.com/tal-tech/go-zero/tools/goctl/api/format" apiformat "github.com/tal-tech/go-zero/tools/goctl/api/format"
"github.com/tal-tech/go-zero/tools/goctl/api/parser" "github.com/tal-tech/go-zero/tools/goctl/api/parser"
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util" apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/urfave/cli"
) )
const tmpFile = "%s-%d" const tmpFile = "%s-%d"
@@ -28,6 +28,8 @@ var tmpDir = path.Join(os.TempDir(), "goctl")
func GoCommand(c *cli.Context) error { func GoCommand(c *cli.Context) error {
apiFile := c.String("api") apiFile := c.String("api")
dir := c.String("dir") dir := c.String("dir")
namingStyle := c.String("style")
if len(apiFile) == 0 { if len(apiFile) == 0 {
return errors.New("missing -api") return errors.New("missing -api")
} }
@@ -35,10 +37,10 @@ func GoCommand(c *cli.Context) error {
return errors.New("missing -dir") return errors.New("missing -dir")
} }
return DoGenProject(apiFile, dir) return DoGenProject(apiFile, dir, namingStyle)
} }
func DoGenProject(apiFile, dir string) error { func DoGenProject(apiFile, dir, style string) error {
p, err := parser.NewParser(apiFile) p, err := parser.NewParser(apiFile)
if err != nil { if err != nil {
return err return err
@@ -48,15 +50,21 @@ func DoGenProject(apiFile, dir string) error {
return err return err
} }
cfg, err := config.NewConfig(style)
if err != nil {
return err
}
logx.Must(util.MkdirIfNotExist(dir)) logx.Must(util.MkdirIfNotExist(dir))
logx.Must(genEtc(dir, api)) logx.Must(genEtc(dir, cfg, api))
logx.Must(genConfig(dir, api)) logx.Must(genConfig(dir, cfg, api))
logx.Must(genMain(dir, api)) logx.Must(genMain(dir, cfg, api))
logx.Must(genServiceContext(dir, api)) logx.Must(genServiceContext(dir, cfg, api))
logx.Must(genTypes(dir, api)) logx.Must(genTypes(dir, cfg, api))
logx.Must(genHandlers(dir, api)) logx.Must(genRoutes(dir, cfg, api))
logx.Must(genRoutes(dir, api)) logx.Must(genHandlers(dir, cfg, api))
logx.Must(genLogic(dir, api)) logx.Must(genLogic(dir, cfg, api))
logx.Must(genMiddleware(dir, cfg, api))
if err := backupAndSweep(apiFile); err != nil { if err := backupAndSweep(apiFile); err != nil {
return err return err

View File

@@ -534,6 +534,7 @@ func TestHasImportApi(t *testing.T) {
} }
} }
assert.True(t, hasInline) assert.True(t, hasInline)
validate(t, filename) validate(t, filename)
} }
@@ -558,15 +559,30 @@ func TestNestTypeApi(t *testing.T) {
err := ioutil.WriteFile(filename, []byte(nestTypeApi), os.ModePerm) err := ioutil.WriteFile(filename, []byte(nestTypeApi), os.ModePerm)
assert.Nil(t, err) assert.Nil(t, err)
defer os.Remove(filename) defer os.Remove(filename)
_, err = parser.NewParser(filename) _, err = parser.NewParser(filename)
assert.NotNil(t, err) assert.NotNil(t, err)
} }
func TestCamelStyle(t *testing.T) {
filename := "greet.api"
err := ioutil.WriteFile(filename, []byte(testApiTemplate), os.ModePerm)
assert.Nil(t, err)
defer os.Remove(filename)
_, err = parser.NewParser(filename)
assert.Nil(t, err)
validateWithCamel(t, filename, "GoZero")
}
func validate(t *testing.T, api string) { func validate(t *testing.T, api string) {
validateWithCamel(t, api, "gozero")
}
func validateWithCamel(t *testing.T, api, camel string) {
dir := "_go" dir := "_go"
os.RemoveAll(dir) os.RemoveAll(dir)
err := DoGenProject(api, dir) err := DoGenProject(api, dir, camel)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
assert.Nil(t, err) assert.Nil(t, err)
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {

View File

@@ -8,12 +8,14 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
"github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util" ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars" "github.com/tal-tech/go-zero/tools/goctl/vars"
) )
const ( const (
configFile = "config.go" configFile = "config"
configTemplate = `package config configTemplate = `package config
import {{.authImport}} import {{.authImport}}
@@ -31,8 +33,13 @@ type Config struct {
` `
) )
func genConfig(dir string, api *spec.ApiSpec) error { func genConfig(dir string, cfg *config.Config, api *spec.ApiSpec) error {
fp, created, err := util.MaybeCreateFile(dir, configDir, configFile) filename, err := format.FileNamingFormat(cfg.NamingFormat, configFile)
if err != nil {
return err
}
fp, created, err := util.MaybeCreateFile(dir, configDir, filename+".go")
if err != nil { if err != nil {
return err return err
} }

View File

@@ -8,7 +8,9 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
"github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util" ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
) )
const ( const (
@@ -20,8 +22,13 @@ Port: {{.port}}
` `
) )
func genEtc(dir string, api *spec.ApiSpec) error { func genEtc(dir string, cfg *config.Config, api *spec.ApiSpec) error {
fp, created, err := util.MaybeCreateFile(dir, etcDir, fmt.Sprintf("%s.yaml", api.Service.Name)) filename, err := format.FileNamingFormat(cfg.NamingFormat, api.Service.Name)
if err != nil {
return err
}
fp, created, err := util.MaybeCreateFile(dir, etcDir, fmt.Sprintf("%s.yaml", filename))
if err != nil { if err != nil {
return err return err
} }

View File

@@ -2,14 +2,18 @@ package gogen
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"path" "path"
"strings" "strings"
"text/template" "text/template"
"unicode"
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util" apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
"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/format"
"github.com/tal-tech/go-zero/tools/goctl/vars" "github.com/tal-tech/go-zero/tools/goctl/vars"
) )
@@ -50,13 +54,8 @@ type Handler struct {
HasRequest bool HasRequest bool
} }
func genHandler(dir string, group spec.Group, route spec.Route) error { func genHandler(dir string, cfg *config.Config, group spec.Group, route spec.Route) error {
handler, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler") handler := getHandlerName(route)
if !ok {
return fmt.Errorf("missing handler annotation for %q", route.Path)
}
handler = getHandlerName(handler)
if getHandlerFolderPath(group, route) != handlerDir { if getHandlerFolderPath(group, route) != handlerDir {
handler = strings.Title(handler) handler = strings.Title(handler)
} }
@@ -65,27 +64,24 @@ func genHandler(dir string, group spec.Group, route spec.Route) error {
return err return err
} }
return doGenToFile(dir, handler, group, route, Handler{ return doGenToFile(dir, handler, cfg, group, route, Handler{
ImportPackages: genHandlerImports(group, route, parentPkg), ImportPackages: genHandlerImports(group, route, parentPkg),
HandlerName: handler, HandlerName: handler,
RequestType: util.Title(route.RequestType.Name), RequestType: util.Title(route.RequestType.Name),
LogicType: strings.TrimSuffix(strings.Title(handler), "Handler") + "Logic", LogicType: strings.Title(getLogicName(route)),
Call: strings.Title(strings.TrimSuffix(handler, "Handler")), Call: strings.Title(strings.TrimSuffix(handler, "Handler")),
HasResp: len(route.ResponseType.Name) > 0, HasResp: len(route.ResponseType.Name) > 0,
HasRequest: len(route.RequestType.Name) > 0, HasRequest: len(route.RequestType.Name) > 0,
}) })
} }
func doGenToFile(dir, handler string, group spec.Group, route spec.Route, handleObj Handler) error { func doGenToFile(dir, handler string, cfg *config.Config, group spec.Group, route spec.Route, handleObj Handler) error {
if getHandlerFolderPath(group, route) != handlerDir { filename, err := format.FileNamingFormat(cfg.NamingFormat, handler)
handler = strings.Title(handler) if err != nil {
} return err
filename := strings.ToLower(handler)
if strings.HasSuffix(filename, "handler") {
filename = filename + ".go"
} else {
filename = filename + "handler.go"
} }
filename = filename + ".go"
fp, created, err := apiutil.MaybeCreateFile(dir, getHandlerFolderPath(group, route), filename) fp, created, err := apiutil.MaybeCreateFile(dir, getHandlerFolderPath(group, route), filename)
if err != nil { if err != nil {
return err return err
@@ -111,10 +107,10 @@ func doGenToFile(dir, handler string, group spec.Group, route spec.Route, handle
return err return err
} }
func genHandlers(dir string, api *spec.ApiSpec) error { func genHandlers(dir string, cfg *config.Config, api *spec.ApiSpec) error {
for _, group := range api.Service.Groups { for _, group := range api.Service.Groups {
for _, route := range group.Routes { for _, route := range group.Routes {
if err := genHandler(dir, group, route); err != nil { if err := genHandler(dir, cfg, group, route); err != nil {
return err return err
} }
} }
@@ -136,14 +132,23 @@ func genHandlerImports(group spec.Group, route spec.Route, parentPkg string) str
return strings.Join(imports, "\n\t") return strings.Join(imports, "\n\t")
} }
func getHandlerBaseName(handler string) string { func getHandlerBaseName(route spec.Route) (string, error) {
handlerName := util.Untitle(handler) handler, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler")
if strings.HasSuffix(handlerName, "handler") { if !ok {
handlerName = strings.ReplaceAll(handlerName, "handler", "") return "", fmt.Errorf("missing handler annotation for %q", route.Path)
} else if strings.HasSuffix(handlerName, "Handler") {
handlerName = strings.ReplaceAll(handlerName, "Handler", "")
} }
return handlerName
for _, char := range handler {
if !unicode.IsDigit(char) && !unicode.IsLetter(char) {
return "", errors.New(fmt.Sprintf("route [%s] handler [%s] invalid, handler name should only contains letter or digit",
route.Path, handler))
}
}
handler = strings.TrimSpace(handler)
handler = strings.TrimSuffix(handler, "handler")
handler = strings.TrimSuffix(handler, "Handler")
return handler, nil
} }
func getHandlerFolderPath(group spec.Group, route spec.Route) string { func getHandlerFolderPath(group spec.Group, route spec.Route) string {
@@ -159,6 +164,20 @@ func getHandlerFolderPath(group spec.Group, route spec.Route) string {
return path.Join(handlerDir, folder) return path.Join(handlerDir, folder)
} }
func getHandlerName(handler string) string { func getHandlerName(route spec.Route) string {
return getHandlerBaseName(handler) + "Handler" handler, err := getHandlerBaseName(route)
if err != nil {
panic(err)
}
return handler + "Handler"
}
func getLogicName(route spec.Route) string {
handler, err := getHandlerBaseName(route)
if err != nil {
panic(err)
}
return handler + "Logic"
} }

View File

@@ -9,7 +9,9 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
"github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util" ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars" "github.com/tal-tech/go-zero/tools/goctl/vars"
) )
@@ -40,10 +42,10 @@ func (l *{{.logic}}) {{.function}}({{.request}}) {{.responseType}} {
} }
` `
func genLogic(dir string, api *spec.ApiSpec) error { func genLogic(dir string, cfg *config.Config, api *spec.ApiSpec) error {
for _, g := range api.Service.Groups { for _, g := range api.Service.Groups {
for _, r := range g.Routes { for _, r := range g.Routes {
err := genLogicByRoute(dir, g, r) err := genLogicByRoute(dir, cfg, g, r)
if err != nil { if err != nil {
return err return err
} }
@@ -52,16 +54,14 @@ func genLogic(dir string, api *spec.ApiSpec) error {
return nil return nil
} }
func genLogicByRoute(dir string, group spec.Group, route spec.Route) error { func genLogicByRoute(dir string, cfg *config.Config, group spec.Group, route spec.Route) error {
handler, ok := util.GetAnnotationValue(route.Annotations, "server", "handler") logic := getLogicName(route)
if !ok { goFile, err := format.FileNamingFormat(cfg.NamingFormat, logic)
return fmt.Errorf("missing handler annotation for %q", route.Path) if err != nil {
return err
} }
handler = strings.TrimSuffix(handler, "handler") goFile = goFile + ".go"
handler = strings.TrimSuffix(handler, "Handler")
filename := strings.ToLower(handler)
goFile := filename + "logic.go"
fp, created, err := util.MaybeCreateFile(dir, getLogicFolderPath(group, route), goFile) fp, created, err := util.MaybeCreateFile(dir, getLogicFolderPath(group, route), goFile)
if err != nil { if err != nil {
return err return err
@@ -102,8 +102,8 @@ func genLogicByRoute(dir string, group spec.Group, route spec.Route) error {
buffer := new(bytes.Buffer) buffer := new(bytes.Buffer)
err = t.Execute(fp, map[string]string{ err = t.Execute(fp, map[string]string{
"imports": imports, "imports": imports,
"logic": strings.Title(handler) + "Logic", "logic": strings.Title(logic),
"function": strings.Title(strings.TrimSuffix(handler, "Handler")), "function": strings.Title(strings.TrimSuffix(logic, "Logic")),
"responseType": responseString, "responseType": responseString,
"returnString": returnString, "returnString": returnString,
"request": requestString, "request": requestString,

View File

@@ -8,7 +8,9 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
"github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util" ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars" "github.com/tal-tech/go-zero/tools/goctl/vars"
) )
@@ -40,12 +42,17 @@ func main() {
} }
` `
func genMain(dir string, api *spec.ApiSpec) error { func genMain(dir string, cfg *config.Config, api *spec.ApiSpec) error {
name := strings.ToLower(api.Service.Name) name := strings.ToLower(api.Service.Name)
if strings.HasSuffix(name, "-api") { if strings.HasSuffix(name, "-api") {
name = strings.ReplaceAll(name, "-api", "") name = strings.ReplaceAll(name, "-api", "")
} }
goFile := name + ".go" filename, err := format.FileNamingFormat(cfg.NamingFormat, name)
if err != nil {
return err
}
goFile := filename + ".go"
fp, created, err := util.MaybeCreateFile(dir, "", goFile) fp, created, err := util.MaybeCreateFile(dir, "", goFile)
if err != nil { if err != nil {
return err return err

View File

@@ -5,7 +5,10 @@ import (
"strings" "strings"
"text/template" "text/template"
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
"github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
) )
var middlewareImplementCode = ` var middlewareImplementCode = `
@@ -30,9 +33,16 @@ func (m *{{.name}})Handle(next http.HandlerFunc) http.HandlerFunc {
} }
` `
func genMiddleware(dir string, middlewares []string) error { func genMiddleware(dir string, cfg *config.Config, api *spec.ApiSpec) error {
var middlewares = getMiddleware(api)
for _, item := range middlewares { for _, item := range middlewares {
filename := strings.TrimSuffix(strings.ToLower(item), "middleware") + "middleware" + ".go" middlewareFilename := strings.TrimSuffix(strings.ToLower(item), "middleware") + "_middleware"
formatName, err := format.FileNamingFormat(cfg.NamingFormat, middlewareFilename)
if err != nil {
return err
}
filename := formatName + ".go"
fp, created, err := util.MaybeCreateFile(dir, middlewareDir, filename) fp, created, err := util.MaybeCreateFile(dir, middlewareDir, filename)
if err != nil { if err != nil {
return err return err

View File

@@ -12,12 +12,14 @@ import (
"github.com/tal-tech/go-zero/core/collection" "github.com/tal-tech/go-zero/core/collection"
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util" apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
"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/format"
"github.com/tal-tech/go-zero/tools/goctl/vars" "github.com/tal-tech/go-zero/tools/goctl/vars"
) )
const ( const (
routesFilename = "routes.go" routesFilename = "routes"
routesTemplate = `// Code generated by goctl. DO NOT EDIT. routesTemplate = `// Code generated by goctl. DO NOT EDIT.
package handler package handler
@@ -62,7 +64,7 @@ type (
} }
) )
func genRoutes(dir string, api *spec.ApiSpec) error { func genRoutes(dir string, cfg *config.Config, api *spec.ApiSpec) error {
var builder strings.Builder var builder strings.Builder
groups, err := getRoutes(api) groups, err := getRoutes(api)
if err != nil { if err != nil {
@@ -121,10 +123,16 @@ func genRoutes(dir string, api *spec.ApiSpec) error {
return err return err
} }
filename := path.Join(dir, handlerDir, routesFilename) routeFilename, err := format.FileNamingFormat(cfg.NamingFormat, routesFilename)
if err != nil {
return err
}
routeFilename = routeFilename + ".go"
filename := path.Join(dir, handlerDir, routeFilename)
os.Remove(filename) os.Remove(filename)
fp, created, err := apiutil.MaybeCreateFile(dir, handlerDir, routesFilename) fp, created, err := apiutil.MaybeCreateFile(dir, handlerDir, routeFilename)
if err != nil { if err != nil {
return err return err
} }
@@ -176,11 +184,8 @@ func getRoutes(api *spec.ApiSpec) ([]group, error) {
for _, g := range api.Service.Groups { for _, g := range api.Service.Groups {
var groupedRoutes group var groupedRoutes group
for _, r := range g.Routes { for _, r := range g.Routes {
handler, ok := apiutil.GetAnnotationValue(r.Annotations, "server", "handler") handler := getHandlerName(r)
if !ok { handler = handler + "(serverCtx)"
return nil, fmt.Errorf("missing handler annotation for route %q", r.Path)
}
handler = getHandlerBaseName(handler) + "Handler(serverCtx)"
folder, ok := apiutil.GetAnnotationValue(r.Annotations, "server", groupProperty) folder, ok := apiutil.GetAnnotationValue(r.Annotations, "server", groupProperty)
if ok { if ok {
handler = toPrefix(folder) + "." + strings.ToUpper(handler[:1]) + handler[1:] handler = toPrefix(folder) + "." + strings.ToUpper(handler[:1]) + handler[1:]

View File

@@ -8,12 +8,14 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
"github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util" ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars" "github.com/tal-tech/go-zero/tools/goctl/vars"
) )
const ( const (
contextFilename = "servicecontext.go" contextFilename = "service_context"
contextTemplate = `package svc contextTemplate = `package svc
import ( import (
@@ -35,8 +37,13 @@ func NewServiceContext(c {{.config}}) *ServiceContext {
` `
) )
func genServiceContext(dir string, api *spec.ApiSpec) error { func genServiceContext(dir string, cfg *config.Config, api *spec.ApiSpec) error {
fp, created, err := util.MaybeCreateFile(dir, contextDir, contextFilename) filename, err := format.FileNamingFormat(cfg.NamingFormat, contextFilename)
if err != nil {
return err
}
fp, created, err := util.MaybeCreateFile(dir, contextDir, filename+".go")
if err != nil { if err != nil {
return err return err
} }
@@ -64,10 +71,6 @@ func genServiceContext(dir string, api *spec.ApiSpec) error {
var middlewareStr string var middlewareStr string
var middlewareAssignment string var middlewareAssignment string
var middlewares = getMiddleware(api) var middlewares = getMiddleware(api)
err = genMiddleware(dir, middlewares)
if err != nil {
return err
}
for _, item := range middlewares { for _, item := range middlewares {
middlewareStr += fmt.Sprintf("%s rest.Middleware\n", item) middlewareStr += fmt.Sprintf("%s rest.Middleware\n", item)

View File

@@ -12,11 +12,13 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util" apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
"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/format"
) )
const ( const (
typesFile = "types.go" typesFile = "types"
typesTemplate = `// Code generated by goctl. DO NOT EDIT. typesTemplate = `// Code generated by goctl. DO NOT EDIT.
package types{{if .containsTime}} package types{{if .containsTime}}
import ( import (
@@ -43,19 +45,25 @@ func BuildTypes(types []spec.Type) (string, error) {
return builder.String(), nil return builder.String(), nil
} }
func genTypes(dir string, api *spec.ApiSpec) error { func genTypes(dir string, cfg *config.Config, api *spec.ApiSpec) error {
val, err := BuildTypes(api.Types) val, err := BuildTypes(api.Types)
if err != nil { if err != nil {
return err return err
} }
filename := path.Join(dir, typesDir, typesFile) typeFilename, err := format.FileNamingFormat(cfg.NamingFormat, typesFile)
os.Remove(filename)
fp, created, err := apiutil.MaybeCreateFile(dir, typesDir, typesFile)
if err != nil { if err != nil {
return err return err
} }
typeFilename = typeFilename + ".go"
filename := path.Join(dir, typesDir, typeFilename)
os.Remove(filename)
fp, created, err := apiutil.MaybeCreateFile(dir, typesDir, typeFilename)
if err != nil {
return err
}
if !created { if !created {
return nil return nil
} }

View File

@@ -7,6 +7,7 @@ import (
"text/template" "text/template"
"github.com/tal-tech/go-zero/tools/goctl/api/gogen" "github.com/tal-tech/go-zero/tools/goctl/api/gogen"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"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"
) )
@@ -60,6 +61,6 @@ func NewService(c *cli.Context) error {
return err return err
} }
err = gogen.DoGenProject(apiFilePath, abs) err = gogen.DoGenProject(apiFilePath, abs, conf.DefaultFormat)
return err return err
} }

View File

@@ -0,0 +1,122 @@
package config
import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/tal-tech/go-zero/tools/goctl/util"
"gopkg.in/yaml.v2"
)
const (
configFile = "config.yaml"
configFolder = "config"
DefaultFormat = "gozero"
)
const defaultYaml = `# namingFormat is used to define the naming format of the generated file name.
# just like time formatting, you can specify the formatting style through the
# two format characters go, and zero. for example: snake format you can
# define as go_zero, camel case format you can it is defined as goZero,
# and even split characters can be specified, such as go#zero. in theory,
# any combination can be used, but the prerequisite must meet the naming conventions
# of each operating system file name. if you want to independently control the file
# naming style of the api, rpc, and model layers, you can set it through apiNamingFormat,
# rpcNamingFormat, modelNamingFormat, and independent control is not enabled by default.
# for more information, please see #{apiNamingFormat},#{rpcNamingFormat},#{modelNamingFormat}
# Note: namingFormat is based on snake or camel string
namingFormat: gozero
`
type Config struct {
// NamingFormat is used to define the naming format of the generated file name.
// just like time formatting, you can specify the formatting style through the
// two format characters go, and zero. for example: snake format you can
// define as go_zero, camel case format you can it is defined as goZero,
// and even split characters can be specified, such as go#zero. in theory,
// any combination can be used, but the prerequisite must meet the naming conventions
// of each operating system file name.
// Note: NamingFormat is based on snake or camel string
NamingFormat string `yaml:"namingFormat"`
}
func NewConfig(format string) (*Config, error) {
if len(format) == 0 {
format = DefaultFormat
}
cfg := &Config{NamingFormat: format}
err := validate(cfg)
return cfg, err
}
func InitOrGetConfig() (*Config, error) {
var (
defaultConfig Config
)
err := yaml.Unmarshal([]byte(defaultYaml), &defaultConfig)
if err != nil {
return nil, err
}
goctlHome, err := util.GetGoctlHome()
if err != nil {
return nil, err
}
configDir := filepath.Join(goctlHome, configFolder)
configFilename := filepath.Join(configDir, configFile)
if util.FileExists(configFilename) {
data, err := ioutil.ReadFile(configFilename)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(data, &defaultConfig)
if err != nil {
return nil, err
}
err = validate(&defaultConfig)
if err != nil {
return nil, err
}
return &defaultConfig, nil
}
err = util.MkdirIfNotExist(configDir)
if err != nil {
return nil, err
}
f, err := os.Create(configFilename)
if err != nil {
return nil, err
}
defer func() {
_ = f.Close()
}()
_, err = f.WriteString(defaultYaml)
if err != nil {
return nil, err
}
err = validate(&defaultConfig)
if err != nil {
return nil, err
}
return &defaultConfig, nil
}
func validate(cfg *Config) error {
if len(strings.TrimSpace(cfg.NamingFormat)) == 0 {
return errors.New("missing namingFormat")
}
return nil
}

View File

@@ -0,0 +1,50 @@
# 配置项管理
| 名称 | 是否可选 | 说明 |
|-------------------|----------|-----------------------------------------------|
| namingFormat | YES | 文件名称格式化符 |
# naming-format
`namingFormat`可以用于对生成代码的文件名称进行格式化和日期格式化符yyyy-MM-dd类似在代码生成时可以根据这些配置项的格式化符进行格式化。
## 格式化符(gozero)
格式化符有`go`,`zero`组成,如常见的三种格式化风格你可以这样编写:
* lower: `gozero`
* camel: `goZero`
* snake: `go_zero`
常见格式化符生成示例
源字符welcome_to_go_zero
| 格式化符 | 格式化结果 | 说明 |
|------------|-----------------------|---------------------------|
| gozero | welcometogozero | 小写 |
| goZero | welcomeToGoZero | 驼峰 |
| go_zero | welcome_to_go_zero | snake |
| Go#zero | Welcome#to#go#zero | #号分割Title类型 |
| GOZERO | WELCOMETOGOZERO | 大写 |
| \_go#zero_ | \_welcome#to#go#zero_ | 下划线做前后缀,并且#分割 |
错误格式化符示例
* go
* gOZero
* zero
* goZEro
* goZERo
* goZeRo
* tal
# 使用方法
目前可通过在生成api、rpc、model时通过`--style`参数指定format格式
```shell script
goctl api go test.api -dir . -style gozero
```
```shell script
goctl rpc proto -src test.proto -dir . -style go_zero
```
```shell script
goctl model mysql datasource -url="" -table="*" -dir ./snake -style GoZero
```
# 默认值
当不指定-style时默认值为`gozero`

View File

@@ -18,6 +18,7 @@ ENV GOPROXY https://goproxy.cn,direct
WORKDIR /build/zero WORKDIR /build/zero
COPY . . COPY . .
RUN sh -c "[ -f go.mod ]" || exit
COPY {{.goRelPath}}/etc /app/etc COPY {{.goRelPath}}/etc /app/etc
RUN go build -ldflags="-s -w" -o /app/{{.exeFile}} {{.goRelPath}}/{{.goFile}} RUN go build -ldflags="-s -w" -o /app/{{.exeFile}} {{.goRelPath}}/{{.goFile}}

View File

@@ -25,7 +25,7 @@ import (
) )
var ( var (
BuildVersion = "20201108" BuildVersion = "20201125"
commands = []cli.Command{ commands = []cli.Command{
{ {
Name: "api", Name: "api",
@@ -98,6 +98,11 @@ var (
Name: "api", Name: "api",
Usage: "the api file", Usage: "the api file",
}, },
cli.StringFlag{
Name: "style",
Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
},
}, },
Action: gogen.GoCommand, Action: gogen.GoCommand,
}, },
@@ -202,8 +207,9 @@ var (
Usage: `generate rpc demo service`, Usage: `generate rpc demo service`,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "style", Name: "style",
Usage: "the file naming style, lower|camel|snake,default is lower", Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "idea", Name: "idea",
@@ -240,8 +246,9 @@ var (
Usage: `the target path of the code`, Usage: `the target path of the code`,
}, },
cli.StringFlag{ cli.StringFlag{
Name: "style", Name: "style",
Usage: "the file naming style, lower|camel|snake,default is lower", Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "idea", Name: "idea",
@@ -273,8 +280,9 @@ var (
Usage: "the target dir", Usage: "the target dir",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "style", Name: "style",
Usage: "the file naming style, lower|camel|snake,default is lower", Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "cache, c", Name: "cache, c",
@@ -308,8 +316,9 @@ var (
Usage: "the target dir", Usage: "the target dir",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "style", Name: "style",
Usage: "the file naming style, lower|camel|snake, default is lower", Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "idea", Name: "idea",

View File

@@ -7,7 +7,7 @@ goctl model 为go-zero下的工具模块中的组件之一目前支持识别m
* 通过ddl生成 * 通过ddl生成
```shell script ```shell script
goctl model mysql ddl -src="./*.sql" -dir="./sql/model" -c=true goctl model mysql ddl -src="./*.sql" -dir="./sql/model" -c
``` ```
执行上述命令后即可快速生成CURD代码。 执行上述命令后即可快速生成CURD代码。
@@ -29,156 +29,191 @@ goctl model 为go-zero下的工具模块中的组件之一目前支持识别m
```go ```go
package model package model
import (
"database/sql"
"fmt"
"strings"
"time"
"github.com/tal-tech/go-zero/core/stores/cache"
"github.com/tal-tech/go-zero/core/stores/sqlc"
"github.com/tal-tech/go-zero/core/stores/sqlx"
"github.com/tal-tech/go-zero/core/stringx"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx"
)
var (
userFieldNames = builderx.FieldNames(&User{})
userRows = strings.Join(userFieldNames, ",")
userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), ",")
userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), "=?,") + "=?"
cacheUserPrefix = "cache#User#user#"
cacheUserNamePrefix = "cache#User#name#"
cacheUserMobilePrefix = "cache#User#mobile#"
cacheUserIdPrefix = "cache#User#id#"
)
type (
UserModel interface {
Insert(data User) (sql.Result, error)
FindOne(id int64) (*User, error)
FindOneByUser(user string) (*User, error)
FindOneByName(name string) (*User, error)
FindOneByMobile(mobile string) (*User, error)
Update(data User) error
Delete(id int64) error
}
defaultUserModel struct {
sqlc.CachedConn
table string
}
User struct {
Id int64 `db:"id"`
User string `db:"user"` // 用户
Name string `db:"name"` // 用户名称
Password string `db:"password"` // 用户密码
Mobile string `db:"mobile"` // 手机号
Gender string `db:"gender"` // 男|女|未公开
Nickname string `db:"nickname"` // 用户昵称
CreateTime time.Time `db:"create_time"`
UpdateTime time.Time `db:"update_time"`
}
)
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel {
return &defaultUserModel{
CachedConn: sqlc.NewConn(conn, c),
table: "user",
}
}
func (m *defaultUserModel) Insert(data User) (sql.Result, error) {
userKey := fmt.Sprintf("%s%v", cacheUserPrefix, data.User)
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet)
return conn.Exec(query, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname)
}, userMobileKey, userKey, userNameKey)
return ret, err
}
func (m *defaultUserModel) FindOne(id int64) (*User, error) {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
var resp User
err := m.QueryRow(&resp, userIdKey, func(conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
return conn.QueryRow(v, query, id)
})
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserModel) FindOneByUser(user string) (*User, error) {
userKey := fmt.Sprintf("%s%v", cacheUserPrefix, user)
var resp User
err := m.QueryRowIndex(&resp, userKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where user = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, user); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserModel) FindOneByName(name string) (*User, error) {
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, name)
var resp User
err := m.QueryRowIndex(&resp, userNameKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where name = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, name); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserModel) FindOneByMobile(mobile string) (*User, error) {
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)
var resp User
err := m.QueryRowIndex(&resp, userMobileKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where mobile = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, mobile); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserModel) Update(data User) error {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where id = ?", m.table, userRowsWithPlaceHolder)
return conn.Exec(query, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id)
}, userIdKey)
return err
}
func (m *defaultUserModel) Delete(id int64) error {
data, err := m.FindOne(id)
if err != nil {
return err
}
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
userKey := fmt.Sprintf("%s%v", cacheUserPrefix, data.User)
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
_, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where id = ?", m.table)
return conn.Exec(query, id)
}, userIdKey, userKey, userNameKey, userMobileKey)
return err
}
func (m *defaultUserModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary)
}
func (m *defaultUserModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
return conn.QueryRow(v, query, primary)
}
import (
"database/sql"
"fmt"
"strings"
"time"
"github.com/tal-tech/go-zero/core/stores/cache"
"github.com/tal-tech/go-zero/core/stores/sqlc"
"github.com/tal-tech/go-zero/core/stores/sqlx"
"github.com/tal-tech/go-zero/core/stringx"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx"
)
var (
userFieldNames = builderx.FieldNames(&User{})
userRows = strings.Join(userFieldNames, ",")
userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), ",")
userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), "=?,") + "=?"
cacheUserIdPrefix = "cache#User#id#"
cacheUserNamePrefix = "cache#User#name#"
cacheUserMobilePrefix = "cache#User#mobile#"
)
type (
UserModel struct {
sqlc.CachedConn
table string
}
User struct {
Id int64 `db:"id"`
Name string `db:"name"` // 用户名称
Password string `db:"password"` // 用户密码
Mobile string `db:"mobile"` // 手机号
Gender string `db:"gender"` // 男|女|未公开
Nickname string `db:"nickname"` // 用户昵称
CreateTime time.Time `db:"create_time"`
UpdateTime time.Time `db:"update_time"`
}
)
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) *UserModel {
return &UserModel{
CachedConn: sqlc.NewConn(conn, c),
table: "user",
}
}
func (m *UserModel) Insert(data User) (sql.Result, error) {
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet)
return conn.Exec(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname)
}, userNameKey, userMobileKey)
return ret, err
}
func (m *UserModel) FindOne(id int64) (*User, error) {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
var resp User
err := m.QueryRow(&resp, userIdKey, func(conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
return conn.QueryRow(v, query, id)
})
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *UserModel) FindOneByName(name string) (*User, error) {
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, name)
var resp User
err := m.QueryRowIndex(&resp, userNameKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where name = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, name); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *UserModel) FindOneByMobile(mobile string) (*User, error) {
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)
var resp User
err := m.QueryRowIndex(&resp, userMobileKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where mobile = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, mobile); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *UserModel) Update(data User) error {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where id = ?", m.table, userRowsWithPlaceHolder)
return conn.Exec(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id)
}, userIdKey)
return err
}
func (m *UserModel) Delete(id int64) error {
data, err := m.FindOne(id)
if err != nil {
return err
}
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
_, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where id = ?", m.table)
return conn.Exec(query, id)
}, userMobileKey, userIdKey, userNameKey)
return err
}
func (m *UserModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary)
}
func (m *UserModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
return conn.QueryRow(v, query, primary)
}
``` ```
## 用法 ## 用法
@@ -211,25 +246,24 @@ OPTIONS:
* ddl * ddl
```shell script ```shell script
goctl model mysql -src={patterns} -dir={dir} -cache=true goctl model mysql -src={patterns} -dir={dir} -cache
``` ```
help help
``` ```
NAME: NAME:
goctl model mysql ddl - generate mysql model from ddl goctl model mysql ddl - generate mysql model from ddl
USAGE: USAGE:
goctl model mysql ddl [command options] [arguments...] goctl model mysql ddl [command options] [arguments...]
OPTIONS: OPTIONS:
--src value, -s value the path or path globbing patterns of the ddl --src value, -s value the path or path globbing patterns of the ddl
--dir value, -d value the target dir --dir value, -d value the target dir
--style value the file naming style, lower|camel|underline,default is lower --style value the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]
--cache, -c generate code with cache [optional] --cache, -c generate code with cache [optional]
--idea for idea plugin [optional] --idea for idea plugin [optional]
``` ```
* datasource * datasource
@@ -242,18 +276,19 @@ OPTIONS:
``` ```
NAME: NAME:
goctl model mysql datasource - generate model from datasource goctl model mysql datasource - generate model from datasource
USAGE:
goctl model mysql datasource [command options] [arguments...]
OPTIONS:
--url value the data source of database,like "root:password@tcp(127.0.0.1:3306)/database
--table value, -t value the table or table globbing patterns in the database
--cache, -c generate code with cache [optional]
--dir value, -d value the target dir
--style value the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]
--idea for idea plugin [optional]
USAGE:
goctl model mysql datasource [command options] [arguments...]
OPTIONS:
--url value the data source of database,like "root:password@tcp(127.0.0.1:3306)/database
--table value, -t value the table or table globbing patterns in the database
--cache, -c generate code with cache [optional]
--dir value, -d value the target dir
--style value the file naming style, lower|camel|snake, default is lower
--idea for idea plugin [optional]
``` ```
@@ -281,13 +316,13 @@ OPTIONS:
* ddl * ddl
```shell script ```shell script
goctl model -src={patterns} -dir={dir} -cache=false goctl model -src={patterns} -dir={dir}
``` ```
* datasource * datasource
```shell script ```shell script
goctl model mysql datasource -url={datasource} -table={patterns} -dir={dir} -cache=false goctl model mysql datasource -url={datasource} -table={patterns} -dir={dir}
``` ```
生成代码仅基本的CURD结构。 生成代码仅基本的CURD结构。

View File

@@ -2,7 +2,6 @@ package command
import ( import (
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -10,6 +9,7 @@ import (
"github.com/go-sql-driver/mysql" "github.com/go-sql-driver/mysql"
"github.com/tal-tech/go-zero/core/logx" "github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stores/sqlx" "github.com/tal-tech/go-zero/core/stores/sqlx"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/gen" "github.com/tal-tech/go-zero/tools/goctl/model/sql/gen"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/model" "github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/util" "github.com/tal-tech/go-zero/tools/goctl/model/sql/util"
@@ -24,9 +24,9 @@ const (
flagDir = "dir" flagDir = "dir"
flagCache = "cache" flagCache = "cache"
flagIdea = "idea" flagIdea = "idea"
flagStyle = "style"
flagUrl = "url" flagUrl = "url"
flagTable = "table" flagTable = "table"
flagStyle = "style"
) )
func MysqlDDL(ctx *cli.Context) error { func MysqlDDL(ctx *cli.Context) error {
@@ -34,8 +34,12 @@ func MysqlDDL(ctx *cli.Context) error {
dir := ctx.String(flagDir) dir := ctx.String(flagDir)
cache := ctx.Bool(flagCache) cache := ctx.Bool(flagCache)
idea := ctx.Bool(flagIdea) idea := ctx.Bool(flagIdea)
namingStyle := strings.TrimSpace(ctx.String(flagStyle)) style := ctx.String(flagStyle)
return fromDDl(src, dir, namingStyle, cache, idea) cfg, err := config.NewConfig(style)
if err != nil {
return err
}
return fromDDl(src, dir, cfg, cache, idea)
} }
func MyDataSource(ctx *cli.Context) error { func MyDataSource(ctx *cli.Context) error {
@@ -43,26 +47,23 @@ func MyDataSource(ctx *cli.Context) error {
dir := strings.TrimSpace(ctx.String(flagDir)) dir := strings.TrimSpace(ctx.String(flagDir))
cache := ctx.Bool(flagCache) cache := ctx.Bool(flagCache)
idea := ctx.Bool(flagIdea) idea := ctx.Bool(flagIdea)
namingStyle := strings.TrimSpace(ctx.String(flagStyle)) style := ctx.String(flagStyle)
pattern := strings.TrimSpace(ctx.String(flagTable)) pattern := strings.TrimSpace(ctx.String(flagTable))
return fromDataSource(url, pattern, dir, namingStyle, cache, idea) cfg, err := config.NewConfig(style)
if err != nil {
return err
}
return fromDataSource(url, pattern, dir, cfg, cache, idea)
} }
func fromDDl(src, dir, namingStyle string, cache, idea bool) error { func fromDDl(src, dir string, cfg *config.Config, cache, idea bool) error {
log := console.NewConsole(idea) log := console.NewConsole(idea)
src = strings.TrimSpace(src) src = strings.TrimSpace(src)
if len(src) == 0 { if len(src) == 0 {
return errors.New("expected path or path globbing patterns, but nothing found") return errors.New("expected path or path globbing patterns, but nothing found")
} }
switch namingStyle {
case gen.NamingLower, gen.NamingCamel, gen.NamingSnake:
case "":
namingStyle = gen.NamingLower
default:
return fmt.Errorf("unexpected naming style: %s", namingStyle)
}
files, err := util.MatchFiles(src) files, err := util.MatchFiles(src)
if err != nil { if err != nil {
return err return err
@@ -81,7 +82,7 @@ func fromDDl(src, dir, namingStyle string, cache, idea bool) error {
source = append(source, string(data)) source = append(source, string(data))
} }
generator, err := gen.NewDefaultGenerator(dir, namingStyle, gen.WithConsoleOption(log)) generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log))
if err != nil { if err != nil {
return err return err
} }
@@ -90,7 +91,7 @@ func fromDDl(src, dir, namingStyle string, cache, idea bool) error {
return err return err
} }
func fromDataSource(url, pattern, dir, namingStyle string, cache, idea bool) error { func fromDataSource(url, pattern, dir string, cfg *config.Config, cache, idea bool) error {
log := console.NewConsole(idea) log := console.NewConsole(idea)
if len(url) == 0 { if len(url) == 0 {
log.Error("%v", "expected data source of mysql, but nothing found") log.Error("%v", "expected data source of mysql, but nothing found")
@@ -102,25 +103,17 @@ func fromDataSource(url, pattern, dir, namingStyle string, cache, idea bool) err
return nil return nil
} }
switch namingStyle { dsn, err := mysql.ParseDSN(url)
case gen.NamingLower, gen.NamingCamel, gen.NamingSnake:
case "":
namingStyle = gen.NamingLower
default:
return fmt.Errorf("unexpected naming style: %s", namingStyle)
}
cfg, err := mysql.ParseDSN(url)
if err != nil { if err != nil {
return err return err
} }
logx.Disable() logx.Disable()
databaseSource := strings.TrimSuffix(url, "/"+cfg.DBName) + "/information_schema" databaseSource := strings.TrimSuffix(url, "/"+dsn.DBName) + "/information_schema"
db := sqlx.NewMysql(databaseSource) db := sqlx.NewMysql(databaseSource)
im := model.NewInformationSchemaModel(db) im := model.NewInformationSchemaModel(db)
tables, err := im.GetAllTables(cfg.DBName) tables, err := im.GetAllTables(dsn.DBName)
if err != nil { if err != nil {
return err return err
} }
@@ -135,7 +128,7 @@ func fromDataSource(url, pattern, dir, namingStyle string, cache, idea bool) err
if !match { if !match {
continue continue
} }
columns, err := im.FindByTableName(cfg.DBName, item) columns, err := im.FindByTableName(dsn.DBName, item)
if err != nil { if err != nil {
return err return err
} }
@@ -146,11 +139,11 @@ func fromDataSource(url, pattern, dir, namingStyle string, cache, idea bool) err
return errors.New("no tables matched") return errors.New("no tables matched")
} }
generator, err := gen.NewDefaultGenerator(dir, namingStyle, gen.WithConsoleOption(log)) generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log))
if err != nil { if err != nil {
return err return err
} }
err = generator.StartFromInformationSchema(cfg.DBName, matchTables, cache) err = generator.StartFromInformationSchema(dsn.DBName, matchTables, cache)
return err return err
} }

View File

@@ -7,19 +7,22 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/gen" "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
) )
var sql = "-- 用户表 --\nCREATE TABLE `user` (\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 UNIQUE KEY `mobile_index` (`mobile`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n" var sql = "-- 用户表 --\nCREATE TABLE `user` (\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 UNIQUE KEY `mobile_index` (`mobile`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n"
var cfg = &config.Config{
NamingFormat: "gozero",
}
func TestFromDDl(t *testing.T) { func TestFromDDl(t *testing.T) {
err := fromDDl("./user.sql", t.TempDir(), gen.NamingCamel, true, false) err := fromDDl("./user.sql", t.TempDir(), cfg, true, false)
assert.Equal(t, errNotMatched, err) assert.Equal(t, errNotMatched, err)
// case dir is not exists // case dir is not exists
unknownDir := filepath.Join(t.TempDir(), "test", "user.sql") unknownDir := filepath.Join(t.TempDir(), "test", "user.sql")
err = fromDDl(unknownDir, t.TempDir(), gen.NamingCamel, true, false) err = fromDDl(unknownDir, t.TempDir(), cfg, true, false)
assert.True(t, func() bool { assert.True(t, func() bool {
switch err.(type) { switch err.(type) {
case *os.PathError: case *os.PathError:
@@ -30,18 +33,11 @@ func TestFromDDl(t *testing.T) {
}()) }())
// case empty src // case empty src
err = fromDDl("", t.TempDir(), gen.NamingCamel, true, false) err = fromDDl("", t.TempDir(), cfg, true, false)
if err != nil { if err != nil {
assert.Equal(t, "expected path or path globbing patterns, but nothing found", err.Error()) assert.Equal(t, "expected path or path globbing patterns, but nothing found", err.Error())
} }
// case unknown naming style
tmp := filepath.Join(t.TempDir(), "user.sql")
err = fromDDl(tmp, t.TempDir(), "lower1", true, false)
if err != nil {
assert.Equal(t, "unexpected naming style: lower1", err.Error())
}
tempDir := filepath.Join(t.TempDir(), "test") tempDir := filepath.Join(t.TempDir(), "test")
err = util.MkdirIfNotExist(tempDir) err = util.MkdirIfNotExist(tempDir)
if err != nil { if err != nil {
@@ -67,7 +63,7 @@ func TestFromDDl(t *testing.T) {
_, err = os.Stat(user2Sql) _, err = os.Stat(user2Sql)
assert.Nil(t, err) assert.Nil(t, err)
err = fromDDl(filepath.Join(tempDir, "user*.sql"), tempDir, gen.NamingLower, true, false) err = fromDDl(filepath.Join(tempDir, "user*.sql"), tempDir, cfg, true, false)
assert.Nil(t, err) assert.Nil(t, err)
_, err = os.Stat(filepath.Join(tempDir, "usermodel.go")) _, err = os.Stat(filepath.Join(tempDir, "usermodel.go"))

View File

@@ -2,7 +2,7 @@
# generate model with cache from ddl # generate model with cache from ddl
fromDDL: fromDDL:
goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/user" -c goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/user" -cache
# generate model with cache from data source # generate model with cache from data source
@@ -12,4 +12,4 @@ datasource=127.0.0.1:3306
database=gozero database=gozero
fromDataSource: fromDataSource:
goctl model mysql datasource -url="$(user):$(password)@tcp($(datasource))/$(database)" -table="*" -dir ./model/cache -c -style camel goctl model mysql datasource -url="$(user):$(password)@tcp($(datasource))/$(database)" -table="*" -dir ./model/cache -c -style gozero

View File

@@ -1,6 +1,7 @@
-- 用户表 -- -- 用户表 --
CREATE TABLE `user` ( CREATE TABLE `user` (
`id` bigint(10) NOT NULL AUTO_INCREMENT, `id` bigint(10) NOT NULL AUTO_INCREMENT,
`user` varchar(50) NOT NULL DEFAULT '' COMMENT '用户',
`name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称', `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',
`password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码', `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',
`mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号', `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',
@@ -10,6 +11,7 @@ CREATE TABLE `user` (
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `name_index` (`name`), UNIQUE KEY `name_index` (`name`),
UNIQUE KEY `user_index` (`user`),
UNIQUE KEY `mobile_index` (`mobile`) UNIQUE KEY `mobile_index` (`mobile`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

View File

@@ -9,7 +9,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
func genDelete(table Table, withCache bool) (string, error) { func genDelete(table Table, withCache bool) (string, string, error) {
keySet := collection.NewSet() keySet := collection.NewSet()
keyVariableSet := collection.NewSet() keyVariableSet := collection.NewSet()
for fieldName, key := range table.CacheKey { for fieldName, key := range table.CacheKey {
@@ -24,7 +24,7 @@ func genDelete(table Table, withCache bool) (string, error) {
camel := table.Name.ToCamel() camel := table.Name.ToCamel()
text, err := util.LoadTemplate(category, deleteTemplateFile, template.Delete) text, err := util.LoadTemplate(category, deleteTemplateFile, template.Delete)
if err != nil { if err != nil {
return "", err return "", "", err
} }
output, err := util.With("delete"). output, err := util.With("delete").
@@ -40,8 +40,23 @@ func genDelete(table Table, withCache bool) (string, error) {
"keyValues": strings.Join(keyVariableSet.KeysStr(), ", "), "keyValues": strings.Join(keyVariableSet.KeysStr(), ", "),
}) })
if err != nil { if err != nil {
return "", err return "", "", err
} }
return output.String(), nil // interface method
text, err = util.LoadTemplate(category, deleteMethodTemplateFile, template.DeleteMethod)
if err != nil {
return "", "", err
}
deleteMethodOut, err := util.With("deleteMethod").
Parse(text).
Execute(map[string]interface{}{
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).UnTitle(),
"dataType": table.PrimaryKey.DataType,
})
if err != nil {
return "", "", err
}
return output.String(), deleteMethodOut.String(), nil
} }

View File

@@ -6,11 +6,11 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
func genFindOne(table Table, withCache bool) (string, error) { func genFindOne(table Table, withCache bool) (string, string, error) {
camel := table.Name.ToCamel() camel := table.Name.ToCamel()
text, err := util.LoadTemplate(category, findOneTemplateFile, template.FindOne) text, err := util.LoadTemplate(category, findOneTemplateFile, template.FindOne)
if err != nil { if err != nil {
return "", err return "", "", err
} }
output, err := util.With("findOne"). output, err := util.With("findOne").
@@ -26,8 +26,23 @@ func genFindOne(table Table, withCache bool) (string, error) {
"cacheKeyVariable": table.CacheKey[table.PrimaryKey.Name.Source()].Variable, "cacheKeyVariable": table.CacheKey[table.PrimaryKey.Name.Source()].Variable,
}) })
if err != nil { if err != nil {
return "", err return "", "", err
} }
return output.String(), nil text, err = util.LoadTemplate(category, findOneMethodTemplateFile, template.FindOneMethod)
if err != nil {
return "", "", err
}
findOneMethod, err := util.With("findOneMethod").
Parse(text).
Execute(map[string]interface{}{
"upperStartCamelObject": camel,
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).UnTitle(),
"dataType": table.PrimaryKey.DataType,
})
if err != nil {
return "", "", err
}
return output.String(), findOneMethod.String(), nil
} }

View File

@@ -9,10 +9,16 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
func genFindOneByField(table Table, withCache bool) (string, string, error) { type findOneCode struct {
findOneMethod string
findOneInterfaceMethod string
cacheExtra string
}
func genFindOneByField(table Table, withCache bool) (*findOneCode, error) {
text, err := util.LoadTemplate(category, findOneByFieldTemplateFile, template.FindOneByField) text, err := util.LoadTemplate(category, findOneByFieldTemplateFile, template.FindOneByField)
if err != nil { if err != nil {
return "", "", err return nil, err
} }
t := util.With("findOneByField").Parse(text) t := util.With("findOneByField").Parse(text)
@@ -36,15 +42,40 @@ func genFindOneByField(table Table, withCache bool) (string, string, error) {
"originalField": field.Name.Source(), "originalField": field.Name.Source(),
}) })
if err != nil { if err != nil {
return "", "", err return nil, err
} }
list = append(list, output.String()) list = append(list, output.String())
} }
text, err = util.LoadTemplate(category, findOneByFieldMethodTemplateFile, template.FindOneByFieldMethod)
if err != nil {
return nil, err
}
t = util.With("findOneByFieldMethod").Parse(text)
var listMethod []string
for _, field := range table.Fields {
if field.IsPrimaryKey || !field.IsUniqueKey {
continue
}
camelFieldName := field.Name.ToCamel()
output, err := t.Execute(map[string]interface{}{
"upperStartCamelObject": camelTableName,
"upperField": camelFieldName,
"in": fmt.Sprintf("%s %s", stringx.From(camelFieldName).UnTitle(), field.DataType),
})
if err != nil {
return nil, err
}
listMethod = append(listMethod, output.String())
}
if withCache { if withCache {
text, err := util.LoadTemplate(category, findOneByFieldExtraMethodTemplateFile, template.FindOneByFieldExtraMethod) text, err := util.LoadTemplate(category, findOneByFieldExtraMethodTemplateFile, template.FindOneByFieldExtraMethod)
if err != nil { if err != nil {
return "", "", err return nil, err
} }
out, err := util.With("findOneByFieldExtraMethod").Parse(text).Execute(map[string]interface{}{ out, err := util.With("findOneByFieldExtraMethod").Parse(text).Execute(map[string]interface{}{
@@ -54,11 +85,18 @@ func genFindOneByField(table Table, withCache bool) (string, string, error) {
"originalPrimaryField": table.PrimaryKey.Name.Source(), "originalPrimaryField": table.PrimaryKey.Name.Source(),
}) })
if err != nil { if err != nil {
return "", "", err return nil, err
} }
return strings.Join(list, "\n"), out.String(), nil return &findOneCode{
findOneMethod: strings.Join(list, util.NL),
findOneInterfaceMethod: strings.Join(listMethod, util.NL),
cacheExtra: out.String(),
}, nil
} }
return strings.Join(list, "\n"), "", nil
return &findOneCode{
findOneMethod: strings.Join(list, util.NL),
findOneInterfaceMethod: strings.Join(listMethod, util.NL),
}, nil
} }

View File

@@ -7,11 +7,14 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/model" "github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/parser" "github.com/tal-tech/go-zero/tools/goctl/model/sql/parser"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/template" "github.com/tal-tech/go-zero/tools/goctl/model/sql/template"
modelutil "github.com/tal-tech/go-zero/tools/goctl/model/sql/util"
"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/console"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
@@ -28,13 +31,13 @@ type (
//source string //source string
dir string dir string
console.Console console.Console
pkg string pkg string
namingStyle string cfg *config.Config
} }
Option func(generator *defaultGenerator) Option func(generator *defaultGenerator)
) )
func NewDefaultGenerator(dir, namingStyle string, opt ...Option) (*defaultGenerator, error) { func NewDefaultGenerator(dir string, cfg *config.Config, opt ...Option) (*defaultGenerator, error) {
if dir == "" { if dir == "" {
dir = pwd dir = pwd
} }
@@ -50,7 +53,7 @@ func NewDefaultGenerator(dir, namingStyle string, opt ...Option) (*defaultGenera
return nil, err return nil, err
} }
generator := &defaultGenerator{dir: dir, namingStyle: namingStyle, pkg: pkg} generator := &defaultGenerator{dir: dir, cfg: cfg, pkg: pkg}
var optionList []Option var optionList []Option
optionList = append(optionList, newDefaultOption()) optionList = append(optionList, newDefaultOption())
optionList = append(optionList, opt...) optionList = append(optionList, opt...)
@@ -114,13 +117,12 @@ func (g *defaultGenerator) createFile(modelList map[string]string) error {
for tableName, code := range modelList { for tableName, code := range modelList {
tn := stringx.From(tableName) tn := stringx.From(tableName)
name := fmt.Sprintf("%smodel.go", strings.ToLower(tn.ToCamel())) modelFilename, err := format.FileNamingFormat(g.cfg.NamingFormat, fmt.Sprintf("%s_model", tn.Source()))
switch g.namingStyle { if err != nil {
case NamingCamel: return err
name = fmt.Sprintf("%sModel.go", tn.ToCamel())
case NamingSnake:
name = fmt.Sprintf("%s_model.go", tn.ToSnake())
} }
name := modelFilename + ".go"
filename := filepath.Join(dirAbs, name) filename := filepath.Join(dirAbs, name)
if util.FileExists(filename) { if util.FileExists(filename) {
g.Warning("%s already exists, ignored.", name) g.Warning("%s already exists, ignored.", name)
@@ -132,10 +134,12 @@ func (g *defaultGenerator) createFile(modelList map[string]string) error {
} }
} }
// generate error file // generate error file
filename := filepath.Join(dirAbs, "vars.go") varFilename, err := format.FileNamingFormat(g.cfg.NamingFormat, "vars")
if g.namingStyle == NamingCamel { if err != nil {
filename = filepath.Join(dirAbs, "Vars.go") return err
} }
filename := filepath.Join(dirAbs, varFilename+".go")
text, err := util.LoadTemplate(category, errTemplateFile, template.Error) text, err := util.LoadTemplate(category, errTemplateFile, template.Error)
if err != nil { if err != nil {
return err return err
@@ -219,39 +223,41 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
return "", err return "", err
} }
typesCode, err := genTypes(table, withCache) insertCode, insertCodeMethod, err := genInsert(table, withCache)
if err != nil {
return "", err
}
newCode, err := genNew(table, withCache)
if err != nil {
return "", err
}
insertCode, err := genInsert(table, withCache)
if err != nil { if err != nil {
return "", err return "", err
} }
var findCode = make([]string, 0) var findCode = make([]string, 0)
findOneCode, err := genFindOne(table, withCache) findOneCode, findOneCodeMethod, err := genFindOne(table, withCache)
if err != nil { if err != nil {
return "", err return "", err
} }
findOneByFieldCode, extraMethod, err := genFindOneByField(table, withCache) ret, err := genFindOneByField(table, withCache)
if err != nil { if err != nil {
return "", err return "", err
} }
findCode = append(findCode, findOneCode, findOneByFieldCode) findCode = append(findCode, findOneCode, ret.findOneMethod)
updateCode, err := genUpdate(table, withCache) updateCode, updateCodeMethod, err := genUpdate(table, withCache)
if err != nil { if err != nil {
return "", err return "", err
} }
deleteCode, err := genDelete(table, withCache) deleteCode, deleteCodeMethod, err := genDelete(table, withCache)
if err != nil {
return "", err
}
var list []string
list = append(list, insertCodeMethod, findOneCodeMethod, ret.findOneInterfaceMethod, updateCodeMethod, deleteCodeMethod)
typesCode, err := genTypes(table, strings.Join(modelutil.TrimStringSlice(list), util.NL), withCache)
if err != nil {
return "", err
}
newCode, err := genNew(table, withCache)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -266,7 +272,7 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
"find": strings.Join(findCode, "\n"), "find": strings.Join(findCode, "\n"),
"update": updateCode, "update": updateCode,
"delete": deleteCode, "delete": deleteCode,
"extraMethod": extraMethod, "extraMethod": ret.cacheExtra,
}) })
if err != nil { if err != nil {
return "", err return "", err

View File

@@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx" "github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/tools/goctl/config"
) )
var ( var (
@@ -22,7 +23,9 @@ func TestCacheModel(t *testing.T) {
defer func() { defer func() {
_ = os.RemoveAll(dir) _ = os.RemoveAll(dir)
}() }()
g, err := NewDefaultGenerator(cacheDir, NamingCamel) g, err := NewDefaultGenerator(cacheDir, &config.Config{
NamingFormat: "GoZero",
})
assert.Nil(t, err) assert.Nil(t, err)
err = g.StartFromDDL(source, true) err = g.StartFromDDL(source, true)
@@ -31,7 +34,9 @@ func TestCacheModel(t *testing.T) {
_, err := os.Stat(filepath.Join(cacheDir, "TestUserInfoModel.go")) _, err := os.Stat(filepath.Join(cacheDir, "TestUserInfoModel.go"))
return err == nil return err == nil
}()) }())
g, err = NewDefaultGenerator(noCacheDir, NamingLower) g, err = NewDefaultGenerator(noCacheDir, &config.Config{
NamingFormat: "gozero",
})
assert.Nil(t, err) assert.Nil(t, err)
err = g.StartFromDDL(source, false) err = g.StartFromDDL(source, false)
@@ -51,7 +56,9 @@ func TestNamingModel(t *testing.T) {
defer func() { defer func() {
_ = os.RemoveAll(dir) _ = os.RemoveAll(dir)
}() }()
g, err := NewDefaultGenerator(camelDir, NamingCamel) g, err := NewDefaultGenerator(camelDir, &config.Config{
NamingFormat: "GoZero",
})
assert.Nil(t, err) assert.Nil(t, err)
err = g.StartFromDDL(source, true) err = g.StartFromDDL(source, true)
@@ -60,7 +67,9 @@ func TestNamingModel(t *testing.T) {
_, err := os.Stat(filepath.Join(camelDir, "TestUserInfoModel.go")) _, err := os.Stat(filepath.Join(camelDir, "TestUserInfoModel.go"))
return err == nil return err == nil
}()) }())
g, err = NewDefaultGenerator(snakeDir, NamingSnake) g, err = NewDefaultGenerator(snakeDir, &config.Config{
NamingFormat: "go_zero",
})
assert.Nil(t, err) assert.Nil(t, err)
err = g.StartFromDDL(source, true) err = g.StartFromDDL(source, true)

View File

@@ -9,7 +9,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
func genInsert(table Table, withCache bool) (string, error) { func genInsert(table Table, withCache bool) (string, string, error) {
keySet := collection.NewSet() keySet := collection.NewSet()
keyVariableSet := collection.NewSet() keyVariableSet := collection.NewSet()
for fieldName, key := range table.CacheKey { for fieldName, key := range table.CacheKey {
@@ -36,7 +36,7 @@ func genInsert(table Table, withCache bool) (string, error) {
camel := table.Name.ToCamel() camel := table.Name.ToCamel()
text, err := util.LoadTemplate(category, insertTemplateFile, template.Insert) text, err := util.LoadTemplate(category, insertTemplateFile, template.Insert)
if err != nil { if err != nil {
return "", err return "", "", err
} }
output, err := util.With("insert"). output, err := util.With("insert").
@@ -52,8 +52,23 @@ func genInsert(table Table, withCache bool) (string, error) {
"keyValues": strings.Join(keyVariableSet.KeysStr(), ", "), "keyValues": strings.Join(keyVariableSet.KeysStr(), ", "),
}) })
if err != nil { if err != nil {
return "", err return "", "", err
} }
return output.String(), nil // interface method
text, err = util.LoadTemplate(category, insertTemplateMethodFile, template.InsertMethod)
if err != nil {
return "", "", err
}
insertMethodOutput, err := util.With("insertMethod").
Parse(text).
Execute(map[string]interface{}{
"upperStartCamelObject": camel,
})
if err != nil {
return "", "", err
}
return output.String(), insertMethodOutput.String(), nil
} }

View File

@@ -2,6 +2,7 @@ package gen
import ( import (
"fmt" "fmt"
"strings"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/parser" "github.com/tal-tech/go-zero/tools/goctl/model/sql/parser"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
@@ -33,8 +34,14 @@ func genCacheKeys(table parser.Table) (map[string]Key, error) {
camelFieldName := field.Name.ToCamel() camelFieldName := field.Name.ToCamel()
lowerStartCamelFieldName := stringx.From(camelFieldName).UnTitle() lowerStartCamelFieldName := stringx.From(camelFieldName).UnTitle()
left := fmt.Sprintf("cache%s%sPrefix", camelTableName, camelFieldName) left := fmt.Sprintf("cache%s%sPrefix", camelTableName, camelFieldName)
if strings.ToLower(camelFieldName) == strings.ToLower(camelTableName) {
left = fmt.Sprintf("cache%sPrefix", camelTableName)
}
right := fmt.Sprintf("cache#%s#%s#", camelTableName, lowerStartCamelFieldName) right := fmt.Sprintf("cache#%s#%s#", camelTableName, lowerStartCamelFieldName)
variable := fmt.Sprintf("%s%sKey", lowerStartCamelTableName, camelFieldName) variable := fmt.Sprintf("%s%sKey", lowerStartCamelTableName, camelFieldName)
if strings.ToLower(lowerStartCamelTableName) == strings.ToLower(camelFieldName) {
variable = fmt.Sprintf("%sKey", lowerStartCamelTableName)
}
m[field.Name.Source()] = Key{ m[field.Name.Source()] = Key{
VarExpression: fmt.Sprintf(`%s = "%s"`, left, right), VarExpression: fmt.Sprintf(`%s = "%s"`, left, right),
Left: left, Left: left,

View File

@@ -11,31 +11,40 @@ import (
const ( const (
category = "model" category = "model"
deleteTemplateFile = "delete.tpl" deleteTemplateFile = "delete.tpl"
deleteMethodTemplateFile = "interface-delete.tpl"
fieldTemplateFile = "filed.tpl" fieldTemplateFile = "filed.tpl"
findOneTemplateFile = "find-one.tpl" findOneTemplateFile = "find-one.tpl"
findOneMethodTemplateFile = "interface-find-one.tpl"
findOneByFieldTemplateFile = "find-one-by-field.tpl" findOneByFieldTemplateFile = "find-one-by-field.tpl"
findOneByFieldMethodTemplateFile = "interface-find-one-by-field.tpl"
findOneByFieldExtraMethodTemplateFile = "find-one-by-filed-extra-method.tpl" findOneByFieldExtraMethodTemplateFile = "find-one-by-filed-extra-method.tpl"
importsTemplateFile = "import.tpl" importsTemplateFile = "import.tpl"
importsWithNoCacheTemplateFile = "import-no-cache.tpl" importsWithNoCacheTemplateFile = "import-no-cache.tpl"
insertTemplateFile = "insert.tpl" insertTemplateFile = "insert.tpl"
insertTemplateMethodFile = "interface-insert.tpl"
modelTemplateFile = "model.tpl" modelTemplateFile = "model.tpl"
modelNewTemplateFile = "model-new.tpl" modelNewTemplateFile = "model-new.tpl"
tagTemplateFile = "tag.tpl" tagTemplateFile = "tag.tpl"
typesTemplateFile = "types.tpl" typesTemplateFile = "types.tpl"
updateTemplateFile = "update.tpl" updateTemplateFile = "update.tpl"
updateMethodTemplateFile = "interface-update.tpl"
varTemplateFile = "var.tpl" varTemplateFile = "var.tpl"
errTemplateFile = "err.tpl" errTemplateFile = "err.tpl"
) )
var templates = map[string]string{ var templates = map[string]string{
deleteTemplateFile: template.Delete, deleteTemplateFile: template.Delete,
deleteMethodTemplateFile: template.DeleteMethod,
fieldTemplateFile: template.Field, fieldTemplateFile: template.Field,
findOneTemplateFile: template.FindOne, findOneTemplateFile: template.FindOne,
findOneMethodTemplateFile: template.FindOneMethod,
findOneByFieldTemplateFile: template.FindOneByField, findOneByFieldTemplateFile: template.FindOneByField,
findOneByFieldMethodTemplateFile: template.FindOneByFieldMethod,
findOneByFieldExtraMethodTemplateFile: template.FindOneByFieldExtraMethod, findOneByFieldExtraMethodTemplateFile: template.FindOneByFieldExtraMethod,
importsTemplateFile: template.Imports, importsTemplateFile: template.Imports,
importsWithNoCacheTemplateFile: template.ImportsNoCache, importsWithNoCacheTemplateFile: template.ImportsNoCache,
insertTemplateFile: template.Insert, insertTemplateFile: template.Insert,
insertTemplateMethodFile: template.InsertMethod,
modelTemplateFile: template.Model, modelTemplateFile: template.Model,
modelNewTemplateFile: template.New, modelNewTemplateFile: template.New,
tagTemplateFile: template.Tag, tagTemplateFile: template.Tag,

View File

@@ -5,7 +5,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
) )
func genTypes(table Table, withCache bool) (string, error) { func genTypes(table Table, methods string, withCache bool) (string, error) {
fields := table.Fields fields := table.Fields
fieldsString, err := genFields(fields) fieldsString, err := genFields(fields)
if err != nil { if err != nil {
@@ -21,6 +21,7 @@ func genTypes(table Table, withCache bool) (string, error) {
Parse(text). Parse(text).
Execute(map[string]interface{}{ Execute(map[string]interface{}{
"withCache": withCache, "withCache": withCache,
"method": methods,
"upperStartCamelObject": table.Name.ToCamel(), "upperStartCamelObject": table.Name.ToCamel(),
"fields": fieldsString, "fields": fieldsString,
}) })

View File

@@ -8,7 +8,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
func genUpdate(table Table, withCache bool) (string, error) { func genUpdate(table Table, withCache bool) (string, string, error) {
expressionValues := make([]string, 0) expressionValues := make([]string, 0)
for _, filed := range table.Fields { for _, filed := range table.Fields {
camel := filed.Name.ToCamel() camel := filed.Name.ToCamel()
@@ -24,7 +24,7 @@ func genUpdate(table Table, withCache bool) (string, error) {
camelTableName := table.Name.ToCamel() camelTableName := table.Name.ToCamel()
text, err := util.LoadTemplate(category, updateTemplateFile, template.Update) text, err := util.LoadTemplate(category, updateTemplateFile, template.Update)
if err != nil { if err != nil {
return "", err return "", "", err
} }
output, err := util.With("update"). output, err := util.With("update").
@@ -39,8 +39,23 @@ func genUpdate(table Table, withCache bool) (string, error) {
"expressionValues": strings.Join(expressionValues, ", "), "expressionValues": strings.Join(expressionValues, ", "),
}) })
if err != nil { if err != nil {
return "", nil return "", "", nil
} }
return output.String(), nil // update interface method
text, err = util.LoadTemplate(category, updateMethodTemplateFile, template.UpdateMethod)
if err != nil {
return "", "", err
}
updateMethodOutput, err := util.With("updateMethod").
Parse(text).
Execute(map[string]interface{}{
"upperStartCamelObject": camelTableName,
})
if err != nil {
return "", "", nil
}
return output.String(), updateMethodOutput.String(), nil
} }

View File

@@ -76,6 +76,9 @@ func TestConvertColumn(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, table.PrimaryKey.AutoIncrement && table.PrimaryKey.IsPrimaryKey) assert.True(t, table.PrimaryKey.AutoIncrement && table.PrimaryKey.IsPrimaryKey)
assert.Equal(t, "id", table.PrimaryKey.Name.Source()) assert.Equal(t, "id", table.PrimaryKey.Name.Source())
assert.Equal(t, "mobile", table.Fields[1].Name.Source()) for _, item := range table.Fields {
assert.True(t, table.Fields[1].IsUniqueKey) if item.Name.Source() == "mobile" {
assert.True(t, item.IsUniqueKey)
}
}
} }

View File

@@ -1,7 +1,7 @@
package template package template
var Delete = ` var Delete = `
func (m *{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimaryKey}} {{.dataType}}) error { func (m *default{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimaryKey}} {{.dataType}}) error {
{{if .withCache}}{{if .containsIndexCache}}data, err:=m.FindOne({{.lowerStartCamelPrimaryKey}}) {{if .withCache}}{{if .containsIndexCache}}data, err:=m.FindOne({{.lowerStartCamelPrimaryKey}})
if err!=nil{ if err!=nil{
return err return err
@@ -16,3 +16,5 @@ func (m *{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimaryKey}}
return err return err
} }
` `
var DeleteMethod = `Delete({{.lowerStartCamelPrimaryKey}} {{.dataType}}) error`

View File

@@ -2,7 +2,7 @@ package template
// 通过id查询 // 通过id查询
var FindOne = ` var FindOne = `
func (m *{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error) { func (m *default{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error) {
{{if .withCache}}{{.cacheKey}} {{if .withCache}}{{.cacheKey}}
var resp {{.upperStartCamelObject}} var resp {{.upperStartCamelObject}}
err := m.QueryRow(&resp, {{.cacheKeyVariable}}, func(conn sqlx.SqlConn, v interface{}) error { err := m.QueryRow(&resp, {{.cacheKeyVariable}}, func(conn sqlx.SqlConn, v interface{}) error {
@@ -32,7 +32,7 @@ func (m *{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrimaryKey}}
// 通过指定字段查询 // 通过指定字段查询
var FindOneByField = ` var FindOneByField = `
func (m *{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}({{.in}}) (*{{.upperStartCamelObject}}, error) { func (m *default{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}({{.in}}) (*{{.upperStartCamelObject}}, error) {
{{if .withCache}}{{.cacheKey}} {{if .withCache}}{{.cacheKey}}
var resp {{.upperStartCamelObject}} var resp {{.upperStartCamelObject}}
err := m.QueryRowIndex(&resp, {{.cacheKeyVariable}}, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { err := m.QueryRowIndex(&resp, {{.cacheKeyVariable}}, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
@@ -64,12 +64,15 @@ func (m *{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}({{.in}}) (*{{
}{{end}} }{{end}}
` `
var FindOneByFieldExtraMethod = ` var FindOneByFieldExtraMethod = `
func (m *{{.upperStartCamelObject}}Model) formatPrimary(primary interface{}) string { func (m *default{{.upperStartCamelObject}}Model) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", {{.primaryKeyLeft}}, primary) return fmt.Sprintf("%s%v", {{.primaryKeyLeft}}, primary)
} }
func (m *{{.upperStartCamelObject}}Model) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error { func (m *default{{.upperStartCamelObject}}Model) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where {{.originalPrimaryField}} = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table ) query := fmt.Sprintf("select %s from %s where {{.originalPrimaryField}} = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table )
return conn.QueryRow(v, query, primary) return conn.QueryRow(v, query, primary)
} }
` `
var FindOneMethod = `FindOne({{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error)`
var FindOneByFieldMethod = `FindOneBy{{.upperField}}({{.in}}) (*{{.upperStartCamelObject}}, error) `

View File

@@ -1,7 +1,7 @@
package template package template
var Insert = ` var Insert = `
func (m *{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}) (sql.Result,error) { func (m *default{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}) (sql.Result,error) {
{{if .withCache}}{{if .containsIndexCache}}{{.keys}} {{if .withCache}}{{if .containsIndexCache}}{{.keys}}
ret, 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 := fmt.Sprintf("insert into %s (%s) values ({{.expression}})", m.table, {{.lowerStartCamelObject}}RowsExpectAutoSet) query := fmt.Sprintf("insert into %s (%s) values ({{.expression}})", m.table, {{.lowerStartCamelObject}}RowsExpectAutoSet)
@@ -13,3 +13,5 @@ func (m *{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}
return ret,err return ret,err
} }
` `
var InsertMethod = `Insert(data {{.upperStartCamelObject}}) (sql.Result,error)`

View File

@@ -1,8 +1,8 @@
package template package template
var New = ` var New = `
func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf{{end}}) *{{.upperStartCamelObject}}Model { func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf{{end}}) {{.upperStartCamelObject}}Model {
return &{{.upperStartCamelObject}}Model{ return &default{{.upperStartCamelObject}}Model{
{{if .withCache}}CachedConn: sqlc.NewConn(conn, c){{else}}conn:conn{{end}}, {{if .withCache}}CachedConn: sqlc.NewConn(conn, c){{else}}conn:conn{{end}},
table: "{{.table}}", table: "{{.table}}",
} }

View File

@@ -2,7 +2,11 @@ package template
var Types = ` var Types = `
type ( type (
{{.upperStartCamelObject}}Model struct { {{.upperStartCamelObject}}Model interface{
{{.method}}
}
default{{.upperStartCamelObject}}Model struct {
{{if .withCache}}sqlc.CachedConn{{else}}conn sqlx.SqlConn{{end}} {{if .withCache}}sqlc.CachedConn{{else}}conn sqlx.SqlConn{{end}}
table string table string
} }

View File

@@ -1,7 +1,7 @@
package template package template
var Update = ` var Update = `
func (m *{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}) error { func (m *default{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}) error {
{{if .withCache}}{{.primaryCacheKey}} {{if .withCache}}{{.primaryCacheKey}}
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { _, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where {{.originalPrimaryKey}} = ?", m.table, {{.lowerStartCamelObject}}RowsWithPlaceHolder) query := fmt.Sprintf("update %s set %s where {{.originalPrimaryKey}} = ?", m.table, {{.lowerStartCamelObject}}RowsWithPlaceHolder)
@@ -11,3 +11,5 @@ func (m *{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}
return err return err
} }
` `
var UpdateMethod = `Update(data {{.upperStartCamelObject}}) error`

View File

@@ -0,0 +1,12 @@
package util
func TrimStringSlice(list []string) []string {
var out []string
for _, item := range list {
if len(item) == 0 {
continue
}
out = append(out, item)
}
return out
}

View File

@@ -24,32 +24,26 @@ func Rpc(c *cli.Context) error {
return errors.New("missing -dir") return errors.New("missing -dir")
} }
namingStyle, valid := generator.IsNamingValid(style) g, err := generator.NewDefaultRpcGenerator(style)
if !valid { if err != nil {
return fmt.Errorf("unexpected naming style %s", style) return err
} }
g := generator.NewDefaultRpcGenerator(namingStyle)
return g.Generate(src, out, protoImportPath) return g.Generate(src, out, protoImportPath)
} }
// RpcNew is to generate rpc greet service, this greet service can speed // RpcNew is to generate rpc greet service, this greet service can speed
// up your understanding of the zrpc service structure // up your understanding of the zrpc service structure
func RpcNew(c *cli.Context) error { func RpcNew(c *cli.Context) error {
name := c.Args().First() rpcname := c.Args().First()
ext := filepath.Ext(name) ext := filepath.Ext(rpcname)
if len(ext) > 0 { if len(ext) > 0 {
return fmt.Errorf("unexpected ext: %s", ext) return fmt.Errorf("unexpected ext: %s", ext)
} }
style := c.String("style") style := c.String("style")
namingStyle, valid := generator.IsNamingValid(style)
if !valid {
return fmt.Errorf("expected naming style [lower|camel|snake], but found %s", style)
}
protoName := name + ".proto" protoName := rpcname + ".proto"
filename := filepath.Join(".", name, protoName) filename := filepath.Join(".", rpcname, protoName)
src, err := filepath.Abs(filename) src, err := filepath.Abs(filename)
if err != nil { if err != nil {
return err return err
@@ -60,7 +54,11 @@ func RpcNew(c *cli.Context) error {
return err return err
} }
g := generator.NewDefaultRpcGenerator(namingStyle) g, err := generator.NewDefaultRpcGenerator(style)
if err != nil {
return err
}
return g.Generate(src, filepath.Dir(src), nil) return g.Generate(src, filepath.Dir(src), nil)
} }

View File

@@ -1,18 +0,0 @@
package generator
import (
"strings"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
func formatFilename(filename string, style NamingStyle) string {
switch style {
case namingCamel:
return stringx.From(filename).ToCamel()
case namingSnake:
return stringx.From(filename).ToSnake()
default:
return strings.ToLower(stringx.From(filename).ToCamel())
}
}

View File

@@ -1,17 +0,0 @@
package generator
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFormatFilename(t *testing.T) {
assert.Equal(t, "abc", formatFilename("a_b_c", namingLower))
assert.Equal(t, "ABC", formatFilename("a_b_c", namingCamel))
assert.Equal(t, "a_b_c", formatFilename("a_b_c", namingSnake))
assert.Equal(t, "a", formatFilename("a", namingSnake))
assert.Equal(t, "A", formatFilename("a", namingCamel))
// no flag to convert to snake
assert.Equal(t, "abc", formatFilename("abc", namingSnake))
}

View File

@@ -3,6 +3,7 @@ package generator
import ( import (
"path/filepath" "path/filepath"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"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/console"
@@ -10,18 +11,22 @@ import (
) )
type RpcGenerator struct { type RpcGenerator struct {
g Generator g Generator
style NamingStyle cfg *conf.Config
} }
func NewDefaultRpcGenerator(style NamingStyle) *RpcGenerator { func NewDefaultRpcGenerator(style string) (*RpcGenerator, error) {
return NewRpcGenerator(NewDefaultGenerator(), style) cfg, err := conf.NewConfig(style)
if err != nil {
return nil, err
}
return NewRpcGenerator(NewDefaultGenerator(), cfg), nil
} }
func NewRpcGenerator(g Generator, style NamingStyle) *RpcGenerator { func NewRpcGenerator(g Generator, cfg *conf.Config) *RpcGenerator {
return &RpcGenerator{ return &RpcGenerator{
g: g, g: g,
style: style, cfg: cfg,
} }
} }
@@ -57,42 +62,42 @@ func (g *RpcGenerator) Generate(src, target string, protoImportPath []string) er
return err return err
} }
err = g.g.GenEtc(dirCtx, proto, g.style) err = g.g.GenEtc(dirCtx, proto, g.cfg)
if err != nil { if err != nil {
return err return err
} }
err = g.g.GenPb(dirCtx, protoImportPath, proto, g.style) err = g.g.GenPb(dirCtx, protoImportPath, proto, g.cfg)
if err != nil { if err != nil {
return err return err
} }
err = g.g.GenConfig(dirCtx, proto, g.style) err = g.g.GenConfig(dirCtx, proto, g.cfg)
if err != nil { if err != nil {
return err return err
} }
err = g.g.GenSvc(dirCtx, proto, g.style) err = g.g.GenSvc(dirCtx, proto, g.cfg)
if err != nil { if err != nil {
return err return err
} }
err = g.g.GenLogic(dirCtx, proto, g.style) err = g.g.GenLogic(dirCtx, proto, g.cfg)
if err != nil { if err != nil {
return err return err
} }
err = g.g.GenServer(dirCtx, proto, g.style) err = g.g.GenServer(dirCtx, proto, g.cfg)
if err != nil { if err != nil {
return err return err
} }
err = g.g.GenMain(dirCtx, proto, g.style) err = g.g.GenMain(dirCtx, proto, g.cfg)
if err != nil { if err != nil {
return err return err
} }
err = g.g.GenCall(dirCtx, proto, g.style) err = g.g.GenCall(dirCtx, proto, g.cfg)
console.NewColorConsole().MarkDone() console.NewColorConsole().MarkDone()

View File

@@ -9,9 +9,14 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx" "github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stringx" "github.com/tal-tech/go-zero/core/stringx"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx" "github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
) )
var cfg = &conf.Config{
NamingFormat: "gozero",
}
func TestRpcGenerate(t *testing.T) { func TestRpcGenerate(t *testing.T) {
_ = Clean() _ = Clean()
dispatcher := NewDefaultGenerator() dispatcher := NewDefaultGenerator()
@@ -21,7 +26,7 @@ func TestRpcGenerate(t *testing.T) {
return return
} }
projectName := stringx.Rand() projectName := stringx.Rand()
g := NewRpcGenerator(dispatcher, namingLower) g := NewRpcGenerator(dispatcher, cfg)
// case go path // case go path
src := filepath.Join(build.Default.GOPATH, "src") src := filepath.Join(build.Default.GOPATH, "src")

View File

@@ -6,8 +6,10 @@ import (
"strings" "strings"
"github.com/tal-tech/go-zero/core/collection" "github.com/tal-tech/go-zero/core/collection"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"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/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
@@ -59,12 +61,17 @@ func (m *default{{.serviceName}}) {{.method}}(ctx context.Context,in *{{.pbReque
` `
) )
func (g *defaultGenerator) GenCall(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error { func (g *defaultGenerator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetCall() dir := ctx.GetCall()
service := proto.Service service := proto.Service
head := util.GetHead(proto.Name) head := util.GetHead(proto.Name)
filename := filepath.Join(dir.Filename, fmt.Sprintf("%s.go", formatFilename(service.Name, namingStyle))) callFilename, err := format.FileNamingFormat(cfg.NamingFormat, service.Name)
if err != nil {
return err
}
filename := filepath.Join(dir.Filename, fmt.Sprintf("%s.go", callFilename))
functions, err := g.genFunction(proto.PbPackage, service) functions, err := g.genFunction(proto.PbPackage, service)
if err != nil { if err != nil {
return err return err
@@ -86,7 +93,7 @@ func (g *defaultGenerator) GenCall(ctx DirContext, proto parser.Proto, namingSty
} }
err = util.With("shared").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{ err = util.With("shared").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"name": formatFilename(service.Name, namingStyle), "name": callFilename,
"alias": strings.Join(alias.KeysStr(), util.NL), "alias": strings.Join(alias.KeysStr(), util.NL),
"head": head, "head": head,
"filePackage": dir.Base, "filePackage": dir.Base,

View File

@@ -5,8 +5,10 @@ import (
"os" "os"
"path/filepath" "path/filepath"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"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/format"
) )
const configTemplate = `package config const configTemplate = `package config
@@ -18,9 +20,14 @@ type Config struct {
} }
` `
func (g *defaultGenerator) GenConfig(ctx DirContext, _ parser.Proto, namingStyle NamingStyle) error { func (g *defaultGenerator) GenConfig(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
dir := ctx.GetConfig() dir := ctx.GetConfig()
fileName := filepath.Join(dir.Filename, formatFilename("config", namingStyle)+".go") configFilename, err := format.FileNamingFormat(cfg.NamingFormat, "config")
if err != nil {
return err
}
fileName := filepath.Join(dir.Filename, configFilename+".go")
if util.FileExists(fileName) { if util.FileExists(fileName) {
return nil return nil
} }

View File

@@ -1,15 +1,18 @@
package generator package generator
import "github.com/tal-tech/go-zero/tools/goctl/rpc/parser" import (
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
)
type Generator interface { type Generator interface {
Prepare() error Prepare() error
GenMain(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenCall(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenEtc(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error GenEtc(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenConfig(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error GenConfig(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenLogic(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenServer(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenSvc(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error GenSvc(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, namingStyle NamingStyle) error GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, cfg *conf.Config) error
} }

View File

@@ -5,8 +5,10 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"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/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
@@ -18,10 +20,14 @@ Etcd:
Key: {{.serviceName}}.rpc Key: {{.serviceName}}.rpc
` `
func (g *defaultGenerator) GenEtc(ctx DirContext, _ parser.Proto, namingStyle NamingStyle) error { func (g *defaultGenerator) GenEtc(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
dir := ctx.GetEtc() dir := ctx.GetEtc()
serviceNameLower := formatFilename(ctx.GetMain().Base, namingStyle) etcFilename, err := format.FileNamingFormat(cfg.NamingFormat, ctx.GetServiceName().Source())
fileName := filepath.Join(dir.Filename, fmt.Sprintf("%v.yaml", serviceNameLower)) if err != nil {
return err
}
fileName := filepath.Join(dir.Filename, fmt.Sprintf("%v.yaml", etcFilename))
text, err := util.LoadTemplate(category, etcTemplateFileFile, etcTemplate) text, err := util.LoadTemplate(category, etcTemplateFileFile, etcTemplate)
if err != nil { if err != nil {
@@ -29,6 +35,6 @@ func (g *defaultGenerator) GenEtc(ctx DirContext, _ parser.Proto, namingStyle Na
} }
return util.With("etc").Parse(text).SaveTo(map[string]interface{}{ return util.With("etc").Parse(text).SaveTo(map[string]interface{}{
"serviceName": strings.ToLower(stringx.From(ctx.GetMain().Base).ToCamel()), "serviceName": strings.ToLower(stringx.From(ctx.GetServiceName().Source()).ToCamel()),
}, fileName, false) }, fileName, false)
} }

View File

@@ -6,8 +6,10 @@ import (
"strings" "strings"
"github.com/tal-tech/go-zero/core/collection" "github.com/tal-tech/go-zero/core/collection"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"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/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
@@ -46,10 +48,15 @@ func (l *{{.logicName}}) {{.method}} (in {{.request}}) ({{.response}}, error) {
` `
) )
func (g *defaultGenerator) GenLogic(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error { func (g *defaultGenerator) GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetLogic() dir := ctx.GetLogic()
for _, rpc := range proto.Service.RPC { for _, rpc := range proto.Service.RPC {
filename := filepath.Join(dir.Filename, formatFilename(rpc.Name+"_logic", namingStyle)+".go") logicFilename, err := format.FileNamingFormat(cfg.NamingFormat, rpc.Name+"_logic")
if err != nil {
return err
}
filename := filepath.Join(dir.Filename, logicFilename+".go")
functions, err := g.genLogicFunction(proto.PbPackage, rpc) functions, err := g.genLogicFunction(proto.PbPackage, rpc)
if err != nil { if err != nil {
return err return err

View File

@@ -5,8 +5,10 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"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/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
@@ -45,10 +47,13 @@ func main() {
} }
` `
func (g *defaultGenerator) GenMain(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error { func (g *defaultGenerator) GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetMain() mainFilename, err := format.FileNamingFormat(cfg.NamingFormat, ctx.GetServiceName().Source())
serviceNameLower := formatFilename(ctx.GetMain().Base, namingStyle) if err != nil {
fileName := filepath.Join(dir.Filename, fmt.Sprintf("%v.go", serviceNameLower)) return err
}
fileName := filepath.Join(ctx.GetMain().Filename, fmt.Sprintf("%v.go", mainFilename))
imports := make([]string, 0) imports := make([]string, 0)
pbImport := fmt.Sprintf(`"%v"`, ctx.GetPb().Package) pbImport := fmt.Sprintf(`"%v"`, ctx.GetPb().Package)
svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package) svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package)
@@ -63,7 +68,7 @@ func (g *defaultGenerator) GenMain(ctx DirContext, proto parser.Proto, namingSty
return util.With("main").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{ return util.With("main").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"head": head, "head": head,
"serviceName": strings.ToLower(stringx.From(ctx.GetMain().Base).ToCamel()), "serviceName": strings.ToLower(ctx.GetServiceName().ToCamel()),
"imports": strings.Join(imports, util.NL), "imports": strings.Join(imports, util.NL),
"pkg": proto.PbPackage, "pkg": proto.PbPackage,
"serviceNew": stringx.From(proto.Service.Name).ToCamel(), "serviceNew": stringx.From(proto.Service.Name).ToCamel(),

View File

@@ -5,11 +5,12 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx" "github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
) )
func (g *defaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, namingStyle NamingStyle) error { func (g *defaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, _ *conf.Config) error {
dir := ctx.GetPb() dir := ctx.GetPb()
cw := new(bytes.Buffer) cw := new(bytes.Buffer)
base := filepath.Dir(proto.Src) base := filepath.Dir(proto.Src)

View File

@@ -6,8 +6,10 @@ import (
"strings" "strings"
"github.com/tal-tech/go-zero/core/collection" "github.com/tal-tech/go-zero/core/collection"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"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/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
@@ -43,7 +45,7 @@ func (s *{{.server}}Server) {{.method}} (ctx context.Context, in {{.request}}) (
` `
) )
func (g *defaultGenerator) GenServer(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error { func (g *defaultGenerator) GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetServer() dir := ctx.GetServer()
logicImport := fmt.Sprintf(`"%v"`, ctx.GetLogic().Package) logicImport := fmt.Sprintf(`"%v"`, ctx.GetLogic().Package)
svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package) svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package)
@@ -54,7 +56,12 @@ func (g *defaultGenerator) GenServer(ctx DirContext, proto parser.Proto, namingS
head := util.GetHead(proto.Name) head := util.GetHead(proto.Name)
service := proto.Service service := proto.Service
serverFile := filepath.Join(dir.Filename, formatFilename(service.Name+"_server", namingStyle)+".go") serverFilename, err := format.FileNamingFormat(cfg.NamingFormat, service.Name+"_server")
if err != nil {
return err
}
serverFile := filepath.Join(dir.Filename, serverFilename+".go")
funcList, err := g.genFunctions(proto.PbPackage, service) funcList, err := g.genFunctions(proto.PbPackage, service)
if err != nil { if err != nil {
return err return err

View File

@@ -4,8 +4,10 @@ import (
"fmt" "fmt"
"path/filepath" "path/filepath"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"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/format"
) )
const svcTemplate = `package svc const svcTemplate = `package svc
@@ -23,9 +25,14 @@ func NewServiceContext(c config.Config) *ServiceContext {
} }
` `
func (g *defaultGenerator) GenSvc(ctx DirContext, _ parser.Proto, namingStyle NamingStyle) error { func (g *defaultGenerator) GenSvc(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
dir := ctx.GetSvc() dir := ctx.GetSvc()
fileName := filepath.Join(dir.Filename, formatFilename("service_context", namingStyle)+".go") svcFilename, err := format.FileNamingFormat(cfg.NamingFormat, "service_context")
if err != nil {
return err
}
fileName := filepath.Join(dir.Filename, svcFilename+".go")
text, err := util.LoadTemplate(category, svcTemplateFile, svcTemplate) text, err := util.LoadTemplate(category, svcTemplateFile, svcTemplate)
if err != nil { if err != nil {
return err return err

View File

@@ -33,6 +33,7 @@ type (
GetSvc() Dir GetSvc() Dir
GetPb() Dir GetPb() Dir
GetMain() Dir GetMain() Dir
GetServiceName() stringx.String
} }
Dir struct { Dir struct {
@@ -41,7 +42,8 @@ type (
Package string Package string
} }
defaultDirContext struct { defaultDirContext struct {
inner map[string]Dir inner map[string]Dir
serviceName stringx.String
} }
) )
@@ -110,8 +112,10 @@ func mkdir(ctx *ctx.ProjectContext, proto parser.Proto) (DirContext, error) {
return nil, err return nil, err
} }
} }
serviceName := strings.TrimSuffix(proto.Name, filepath.Ext(proto.Name))
return &defaultDirContext{ return &defaultDirContext{
inner: inner, inner: inner,
serviceName: stringx.From(strings.ReplaceAll(serviceName, "-", "")),
}, nil }, nil
} }
@@ -151,6 +155,10 @@ func (d *defaultDirContext) GetMain() Dir {
return d.inner[wd] return d.inner[wd]
} }
func (d *defaultDirContext) GetServiceName() stringx.String {
return d.serviceName
}
func (d *Dir) Valid() bool { func (d *Dir) Valid() bool {
return len(d.Filename) > 0 && len(d.Package) > 0 return len(d.Filename) > 0 && len(d.Package) > 0
} }

View File

@@ -1,24 +0,0 @@
package generator
type NamingStyle = string
const (
namingLower NamingStyle = "lower"
namingCamel NamingStyle = "camel"
namingSnake NamingStyle = "snake"
)
// IsNamingValid validates whether the namingStyle is valid or not,return
// namingStyle and true if it is valid, or else return empty string
// and false, and it is a valid value even namingStyle is empty string
func IsNamingValid(namingStyle string) (NamingStyle, bool) {
if len(namingStyle) == 0 {
namingStyle = namingLower
}
switch namingStyle {
case namingLower, namingCamel, namingSnake:
return namingStyle, true
default:
return "", false
}
}

View File

@@ -1,25 +0,0 @@
package generator
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestIsNamingValid(t *testing.T) {
style, valid := IsNamingValid("")
assert.True(t, valid)
assert.Equal(t, namingLower, style)
_, valid = IsNamingValid("lower1")
assert.False(t, valid)
_, valid = IsNamingValid("lower")
assert.True(t, valid)
_, valid = IsNamingValid("snake")
assert.True(t, valid)
_, valid = IsNamingValid("camel")
assert.True(t, valid)
}

View File

@@ -8,13 +8,21 @@ import (
const goctlDir = ".goctl" const goctlDir = ".goctl"
func GetTemplateDir(category string) (string, error) { func GetGoctlHome() (string, error) {
home, err := os.UserHomeDir() home, err := os.UserHomeDir()
if err != nil { if err != nil {
return "", err return "", err
} }
return filepath.Join(home, goctlDir), nil
}
return filepath.Join(home, goctlDir, category), nil func GetTemplateDir(category string) (string, error) {
goctlHome, err := GetGoctlHome()
if err != nil {
return "", err
}
return filepath.Join(goctlHome, category), nil
} }
func InitTemplates(category string, templates map[string]string) error { func InitTemplates(category string, templates map[string]string) error {

View File

@@ -0,0 +1,155 @@
package format
import (
"bytes"
"errors"
"fmt"
"io"
"strings"
)
const (
flagGo = "GO"
flagZero = "ZERO"
unknown style = iota
title
lower
upper
)
var ErrNamingFormat = errors.New("unsupported format")
type (
styleFormat struct {
before string
through string
after string
goStyle style
zeroStyle style
}
style int
)
// FileNamingFormat is used to format the file name. You can define the format style
// through the go and zero formatting characters. For example, you can define the snake
// format as go_zero, and the camel case format as goZero. You can even specify the split
// character, such as go#Zero, theoretically any combination can be used, but the prerequisite
// must meet the naming conventions of each operating system file name.
// Note: Formatting is based on snake or camel string
func FileNamingFormat(format, content string) (string, error) {
upperFormat := strings.ToUpper(format)
indexGo := strings.Index(upperFormat, flagGo)
indexZero := strings.Index(upperFormat, flagZero)
if indexGo < 0 || indexZero < 0 || indexGo > indexZero {
return "", ErrNamingFormat
}
var (
before, through, after string
flagGo, flagZero string
goStyle, zeroStyle style
err error
)
before = format[:indexGo]
flagGo = format[indexGo : indexGo+2]
through = format[indexGo+2 : indexZero]
flagZero = format[indexZero : indexZero+4]
after = format[indexZero+4:]
goStyle, err = getStyle(flagGo)
if err != nil {
return "", err
}
zeroStyle, err = getStyle(flagZero)
if err != nil {
return "", err
}
var formatStyle styleFormat
formatStyle.goStyle = goStyle
formatStyle.zeroStyle = zeroStyle
formatStyle.before = before
formatStyle.through = through
formatStyle.after = after
return doFormat(formatStyle, content)
}
func doFormat(f styleFormat, content string) (string, error) {
splits, err := split(content)
if err != nil {
return "", err
}
var join []string
for index, split := range splits {
if index == 0 {
join = append(join, transferTo(split, f.goStyle))
continue
}
join = append(join, transferTo(split, f.zeroStyle))
}
joined := strings.Join(join, f.through)
return f.before + joined + f.after, nil
}
func transferTo(in string, style style) string {
switch style {
case upper:
return strings.ToUpper(in)
case lower:
return strings.ToLower(in)
case title:
return strings.Title(in)
default:
return in
}
}
func split(content string) ([]string, error) {
var (
list []string
reader = strings.NewReader(content)
buffer = bytes.NewBuffer(nil)
)
for {
r, _, err := reader.ReadRune()
if err != nil {
if err == io.EOF {
if buffer.Len() > 0 {
list = append(list, buffer.String())
}
return list, nil
}
return nil, err
}
if r == '_' {
if buffer.Len() > 0 {
list = append(list, buffer.String())
}
buffer.Reset()
continue
}
if r >= 'A' && r <= 'Z' {
if buffer.Len() > 0 {
list = append(list, buffer.String())
}
buffer.Reset()
}
buffer.WriteRune(r)
}
}
func getStyle(flag string) (style, error) {
compare := strings.ToLower(flag)
switch flag {
case strings.ToLower(compare):
return lower, nil
case strings.ToUpper(compare):
return upper, nil
case strings.Title(compare):
return title, nil
default:
return unknown, fmt.Errorf("unexpected format: %s", flag)
}
}

View File

@@ -0,0 +1,112 @@
package format
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSplit(t *testing.T) {
list, err := split("A")
assert.Nil(t, err)
assert.Equal(t, []string{"A"}, list)
list, err = split("goZero")
assert.Nil(t, err)
assert.Equal(t, []string{"go", "Zero"}, list)
list, err = split("Gozero")
assert.Nil(t, err)
assert.Equal(t, []string{"Gozero"}, list)
list, err = split("go_zero")
assert.Nil(t, err)
assert.Equal(t, []string{"go", "zero"}, list)
list, err = split("talGo_zero")
assert.Nil(t, err)
assert.Equal(t, []string{"tal", "Go", "zero"}, list)
list, err = split("GOZERO")
assert.Nil(t, err)
assert.Equal(t, []string{"G", "O", "Z", "E", "R", "O"}, list)
list, err = split("gozero")
assert.Nil(t, err)
assert.Equal(t, []string{"gozero"}, list)
list, err = split("")
assert.Nil(t, err)
assert.Equal(t, 0, len(list))
list, err = split("a_b_CD_EF")
assert.Nil(t, err)
assert.Equal(t, []string{"a", "b", "C", "D", "E", "F"}, list)
list, err = split("_")
assert.Nil(t, err)
assert.Equal(t, 0, len(list))
list, err = split("__")
assert.Nil(t, err)
assert.Equal(t, 0, len(list))
list, err = split("_A")
assert.Nil(t, err)
assert.Equal(t, []string{"A"}, list)
list, err = split("_A_")
assert.Nil(t, err)
assert.Equal(t, []string{"A"}, list)
list, err = split("A_")
assert.Nil(t, err)
assert.Equal(t, []string{"A"}, list)
list, err = split("welcome_to_go_zero")
assert.Nil(t, err)
assert.Equal(t, []string{"welcome", "to", "go", "zero"}, list)
}
func TestFileNamingFormat(t *testing.T) {
testFileNamingFormat(t, "gozero", "welcome_to_go_zero", "welcometogozero")
testFileNamingFormat(t, "_go#zero_", "welcome_to_go_zero", "_welcome#to#go#zero_")
testFileNamingFormat(t, "Go#zero", "welcome_to_go_zero", "Welcome#to#go#zero")
testFileNamingFormat(t, "Go#Zero", "welcome_to_go_zero", "Welcome#To#Go#Zero")
testFileNamingFormat(t, "Go_Zero", "welcome_to_go_zero", "Welcome_To_Go_Zero")
testFileNamingFormat(t, "go_Zero", "welcome_to_go_zero", "welcome_To_Go_Zero")
testFileNamingFormat(t, "goZero", "welcome_to_go_zero", "welcomeToGoZero")
testFileNamingFormat(t, "GoZero", "welcome_to_go_zero", "WelcomeToGoZero")
testFileNamingFormat(t, "GOZero", "welcome_to_go_zero", "WELCOMEToGoZero")
testFileNamingFormat(t, "GoZERO", "welcome_to_go_zero", "WelcomeTOGOZERO")
testFileNamingFormat(t, "GOZERO", "welcome_to_go_zero", "WELCOMETOGOZERO")
testFileNamingFormat(t, "GO*ZERO", "welcome_to_go_zero", "WELCOME*TO*GO*ZERO")
testFileNamingFormat(t, "[GO#ZERO]", "welcome_to_go_zero", "[WELCOME#TO#GO#ZERO]")
testFileNamingFormat(t, "{go###zero}", "welcome_to_go_zero", "{welcome###to###go###zero}")
testFileNamingFormat(t, "{go###zerogo_zero}", "welcome_to_go_zero", "{welcome###to###go###zerogo_zero}")
testFileNamingFormat(t, "GogoZerozero", "welcome_to_go_zero", "WelcomegoTogoGogoZerozero")
testFileNamingFormat(t, "前缀GoZero后缀", "welcome_to_go_zero", "前缀WelcomeToGoZero后缀")
testFileNamingFormat(t, "GoZero", "welcometogozero", "Welcometogozero")
testFileNamingFormat(t, "GoZero", "WelcomeToGoZero", "WelcomeToGoZero")
testFileNamingFormat(t, "gozero", "WelcomeToGoZero", "welcometogozero")
testFileNamingFormat(t, "go_zero", "WelcomeToGoZero", "welcome_to_go_zero")
testFileNamingFormat(t, "Go_Zero", "WelcomeToGoZero", "Welcome_To_Go_Zero")
testFileNamingFormat(t, "Go_Zero", "", "")
testFileNamingFormatErr(t, "go", "")
testFileNamingFormatErr(t, "gOZero", "")
testFileNamingFormatErr(t, "zero", "")
testFileNamingFormatErr(t, "goZEro", "welcome_to_go_zero")
testFileNamingFormatErr(t, "goZERo", "welcome_to_go_zero")
testFileNamingFormatErr(t, "zerogo", "welcome_to_go_zero")
}
func testFileNamingFormat(t *testing.T, format, in, expected string) {
format, err := FileNamingFormat(format, in)
assert.Nil(t, err)
assert.Equal(t, expected, format)
}
func testFileNamingFormatErr(t *testing.T, format, in string) {
_, err := FileNamingFormat(format, in)
assert.Error(t, err)
}

View File

@@ -0,0 +1,41 @@
package name
import (
"strings"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
type NamingStyle = string
const (
NamingLower NamingStyle = "lower"
NamingCamel NamingStyle = "camel"
NamingSnake NamingStyle = "snake"
)
// IsNamingValid validates whether the namingStyle is valid or not,return
// namingStyle and true if it is valid, or else return empty string
// and false, and it is a valid value even namingStyle is empty string
func IsNamingValid(namingStyle string) (NamingStyle, bool) {
if len(namingStyle) == 0 {
namingStyle = NamingLower
}
switch namingStyle {
case NamingLower, NamingCamel, NamingSnake:
return namingStyle, true
default:
return "", false
}
}
func FormatFilename(filename string, style NamingStyle) string {
switch style {
case NamingCamel:
return stringx.From(filename).ToCamel()
case NamingSnake:
return stringx.From(filename).ToSnake()
default:
return strings.ToLower(stringx.From(filename).ToCamel())
}
}

View File

@@ -0,0 +1,35 @@
package name
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestIsNamingValid(t *testing.T) {
style, valid := IsNamingValid("")
assert.True(t, valid)
assert.Equal(t, NamingLower, style)
_, valid = IsNamingValid("lower1")
assert.False(t, valid)
_, valid = IsNamingValid("lower")
assert.True(t, valid)
_, valid = IsNamingValid("snake")
assert.True(t, valid)
_, valid = IsNamingValid("camel")
assert.True(t, valid)
}
func TestFormatFilename(t *testing.T) {
assert.Equal(t, "abc", FormatFilename("a_b_c", NamingLower))
assert.Equal(t, "ABC", FormatFilename("a_b_c", NamingCamel))
assert.Equal(t, "a_b_c", FormatFilename("a_b_c", NamingSnake))
assert.Equal(t, "a", FormatFilename("a", NamingSnake))
assert.Equal(t, "A", FormatFilename("a", NamingCamel))
// no flag to convert to snake
assert.Equal(t, "abc", FormatFilename("abc", NamingSnake))
}