- 模板定制化
- 准备工作
- 修改
handler模板 - 修改模板前后对比
- 修改模板前后响应体对比
- 模板自定义规则
- 模板变量
- goctl api -o 代码生成模板
- goctl api go 代码生成模板
- goctl api new 代码生成模板
- mongo 代码生成模板
- mysql 代码生成模板
- customized.tpl
- delete.tpl
- err.tpl
- field.tpl
- find-one.tpl
- find-one-by-field.tpl
- find-one-by-field-extra-method.tpl
- import.tpl
- import.no-cache.tpl
- insert.tpl
- interface-delete.tpl
- interface-find-one.tpl
- interface-find-one-by-field.tpl
- interface-insert.tpl
- interface-update.tpl
- model.tpl
- model-new.tpl
- table-name.tpl
- tag.tpl
- types.tpl
- update.tpl
- var.tpl
- goctl kube 代码生成模板
- goctl docker 生成模板
- rpc 代码生成模板
- 参考文献
模板定制化
概述
goctl 代码生成是基于 go 的模板去实现数据驱动的,虽然目前 goctl 的代码生成可以满足一部分代码生成功能,但是模板的自定义可以更加丰富代码生成。
模板指令可参考 goctl template
示例
场景
实现统一格式的 body 响应,格式如下:
{"code": 0,"msg": "OK","data": {}// ①}
① 实际响应数据
tip
go-zero生成的代码没有对其进行处理
准备工作
我们提前在 module 为 greet 的工程下的 response 包中写一个 Response 方法,目录树类似如下:
greet├── response│ └── response.go└── xxx...
代码如下
package responseimport ("net/http""github.com/zeromicro/go-zero/rest/httpx")type Body struct {Code int `json:"code"`Msg string `json:"msg"`Data interface{} `json:"data,omitempty"`}func Response(w http.ResponseWriter, resp interface{}, err error) {var body Bodyif err != nil {body.Code = -1body.Msg = err.Error()} else {body.Msg = "OK"body.Data = resp}httpx.OkJson(w, body)}
修改 handler 模板
$ vim ~/.goctl/${goctl版本号}/api/handler.tpl
将模板替换为以下内容
package handlerimport ("net/http""greet/response"// ①{{.ImportPackages}})func {{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc {return func(w http.ResponseWriter, r *http.Request) {{{if .HasRequest}}var req types.{{.RequestType}}if err := httpx.Parse(r, &req); err != nil {httpx.Error(w, err)return}{{end}}l := {{.LogicName}}.New{{.LogicType}}(r.Context(), svcCtx){{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}&req{{end}}){{if .HasResp}}response.Response(w, resp, err){{else}}response.Response(w, nil, err){{end}}//②}}
① 替换为你真实的response包名,仅供参考
② 自定义模板内容
1.如果本地没有~/.goctl/${goctl版本号}/api/handler.tpl文件,可以通过模板初始化命令goctl template init进行初始化
修改模板前后对比
- 修改前
func GreetHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {return func(w http.ResponseWriter, r *http.Request) {var req types.Requestif err := httpx.Parse(r, &req); err != nil {httpx.Error(w, err)return}l := logic.NewGreetLogic(r.Context(), svcCtx)resp, err := l.Greet(&req)// 以下内容将被自定义模板替换if err != nil {httpx.Error(w, err)} else {httpx.OkJson(w, resp)}}}
- 修改后
func GreetHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {return func(w http.ResponseWriter, r *http.Request) {var req types.Requestif err := httpx.Parse(r, &req); err != nil {httpx.Error(w, err)return}l := logic.NewGreetLogic(r.Context(), svcCtx)resp, err := l.Greet(&req)response.Response(w, resp, err)}}
修改模板前后响应体对比
- 修改前
{"message": "Hello go-zero!"}
- 修改后
{"code": 0,"msg": "OK","data": {"message": "Hello go-zero!"}}
模板自定义规则
- 在 goctl 提供的有效数据范围内修改,即不支持外部变量
- 不支持新增模板文件
- 不支持变量修改
模板变量
goctl api -o 代码生成模板
模板默认目录 ~/.goctl/${goctl-version}/newapi
api.tpl
syntax = "v1"info (title: // TODO: add titledesc: // TODO: add descriptionauthor: "{{.gitUser}}"email: "{{.gitEmail}}")type request {// TODO: add members here and delete this comment}type response {// TODO: add members here and delete this comment}service {{.serviceName}} {@handler GetUser // TODO: set handler name and delete this commentget /users/id/:userId(request) returns(response)@handler CreateUser // TODO: set handler name and delete this commentpost /users/create(request)}
对应指令 goctl api -o
模板注入对象为 map[string]string
map[string]string{"gitUser": getGitName(),"gitEmail": getGitEmail(),"serviceName": baseName + "-api",}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .gitUser | string | Git用户名 |
| .gitEmail | string | Git邮箱 |
| .serviceName | string | 服务名称 |
goctl api go 代码生成模板
对应指令 goctl api go ...
模板默认目录 ~/.goctl/${goctl-version}/api
config.tpl
package configimport {{.authImport}}type Config struct {rest.RestConf{{.auth}}{{.jwtTrans}}}
模板注入对象为 map[string]string
map[string]string{"authImport": authImportStr,"auth": strings.Join(auths, "\n"),"jwtTrans": strings.Join(jwtTransList, "\n"),}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .authImport | string | 认证导入 |
| .auth | string | 认证配置 |
| .jwtTrans | string | JWT配置 |
etc.tpl
Name: {{.serviceName}}Host: {{.host}}Port: {{.port}}
模板注入对象为 map[string]string
map[string]string{"serviceName": service.Name,"host": host,"port": port,}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .serviceName | string | 服务名称 |
| .host | string | 主机地址 |
| .port | string | 端口号 |
handler.tpl
package {{.PkgName}}import ("net/http""github.com/zeromicro/go-zero/rest/httpx"{{.ImportPackages}}){{if .HasDoc}}{{.Doc}}{{end}}func {{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc {return func(w http.ResponseWriter, r *http.Request) {{{if .HasRequest}}var req types.{{.RequestType}}if err := httpx.Parse(r, &req); err != nil {httpx.ErrorCtx(r.Context(), w, err)return}{{end}}l := {{.LogicName}}.New{{.LogicType}}(r.Context(), svcCtx){{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}&req{{end}})if err != nil {httpx.ErrorCtx(r.Context(), w, err)} else {{{if .HasResp}}httpx.OkJsonCtx(r.Context(), w, resp){{else}}httpx.Ok(w){{end}}}}}
模板注入对象为 map[string]any
map[string]any{"PkgName": pkgName,"ImportPackages": genHandlerImports(group, route, rootPkg),"HandlerName": handler,"RequestType": util.Title(route.RequestTypeName()),"LogicName": logicName,"LogicType": strings.Title(getLogicName(route)),"Call": strings.Title(strings.TrimSuffix(handler, "Handler")),"HasResp": len(route.ResponseTypeName()) > 0,"HasRequest": len(route.RequestTypeName()) > 0,"HasDoc": len(route.JoinedDoc()) > 0,"Doc": getDoc(route.JoinedDoc()),}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .PkgName | string | 包名 |
| .ImportPackages | string | 导入包 |
| .HasDoc | bool | 是否有文档注释 |
| .Doc | string | 文档注释 |
| .HandlerName | string | handler 名称 |
| .HasRequest | bool | 是否有请求体 |
| .RequestType | string | 请求类型 |
| .LogicName | string | 逻辑包名称,默认为logic,如果有分组时为具体的group名称 |
| .LogicType | string | 逻辑对象type名称 |
| .Call | string | logic 对象调用方法名称 |
| .HasResp | bool | 是否有响应体 |
logic.tpl
package {{.pkgName}}import ({{.imports}})type {{.logic}} struct {logx.Loggerctx context.ContextsvcCtx *svc.ServiceContext}{{if .hasDoc}}{{.doc}}{{end}}func New{{.logic}}(ctx context.Context, svcCtx *svc.ServiceContext) *{{.logic}} {return &{{.logic}}{Logger: logx.WithContext(ctx),ctx: ctx,svcCtx: svcCtx,}}func (l *{{.logic}}) {{.function}}({{.request}}) {{.responseType}} {// todo: add your logic here and delete this line{{.returnString}}}
模板注入对象为 map[string]any
map[string]any{"pkgName": subDir[strings.LastIndex(subDir, "/")+1:],"imports": imports,"logic": strings.Title(logic),"function": strings.Title(strings.TrimSuffix(logic, "Logic")),"responseType": responseString,"returnString": returnString,"request": requestString,"hasDoc": len(route.JoinedDoc()) > 0,"doc": getDoc(route.JoinedDoc()),}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .pkgName | string | 包名 |
| .imports | string | 导入包 |
| .logic | string | 逻辑结构体名称 |
| .hasDoc | bool | 是否有文档注释 |
| .doc | string | 文档注释 |
| .function | string | logic 函数名称 |
| .request | string | 请求体表达式,包含参数名称,参数类型 |
| .responseType | string | 响应类型体表达式,包含参数名称,参数类型 |
| .returnString | string | 返回语句,返回的结构体 |
main.tpl
package mainimport ("flag""fmt"{{.importPackages}})var configFile = flag.String("f", "etc/{{.serviceName}}.yaml", "the config file")func main() {flag.Parse()var c config.Configconf.MustLoad(*configFile, &c)server := rest.MustNewServer(c.RestConf)defer server.Stop()ctx := svc.NewServiceContext(c)handler.RegisterHandlers(server, ctx)fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)server.Start()}
模板注入对象为 map[string]string
map[string]string{"importPackages": genMainImports(rootPkg),"serviceName": configName,}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .importPackages | string | 导入包 |
| .serviceName | string | 服务名称 |
middleware.tpl
package middlewareimport "net/http"type {{.name}} struct {}func New{{.name}}() *{{.name}} {return &{{.name}}{}}func (m *{{.name}})Handle(next http.HandlerFunc) http.HandlerFunc {return func(w http.ResponseWriter, r *http.Request) {// TODO generate middleware implement function, delete after code implementation// Passthrough to next handler if neednext(w, r)}}
模板注入对象为 map[string]string
map[string]string{"name": strings.Title(name),}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .name | string | 中间件名称 |
svc.tpl
package svcimport ({{.configImport}})type ServiceContext struct {Config {{.config}}{{.middleware}}}func NewServiceContext(c {{.config}}) *ServiceContext {return &ServiceContext{Config: c,{{.middlewareAssignment}}}}
模板注入对象为 map[string]string
map[string]string{"configImport": configImport,"config": "config.Config","middleware": middlewareStr,"middlewareAssignment": middlewareAssignment,}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .configImport | string | pkg 导入 |
| .config | string | 配置结构体名称 |
| .middleware | string | 中间件字段 |
| .middlewareAssignment | string | 中间件赋值语句 |
type.tpl
// Code generated by goctl. DO NOT EDIT.package types{{if .containsTime}}import ("time"){{end}}{{.types}}
模板注入对象为 map[string]any
map[string]any{"types": val,"containsTime": false,}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .containsTime | bool | 是否包含时间 |
| .types | string | 类型 |
goctl api new 代码生成模板
对应指令为 goctl api new ...
模板默认目录 ~/.goctl/${goctl-version}/newapi
api.tpl
syntax = "v1"type Request {Name string `path:"name,options=you|me"`}type Response {Message string `json:"message"`}service {{.name}}-api {@handler {{.handler}}Handlerget /from/:name(Request) returns (Response)}
模板注入对象 map[string]string
map[string]string{"name": dirName,"handler": strings.Title(dirName),}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .name | string | 服务名称 |
| .handler | string | 处理器名称 |
mongo 代码生成模板
模板默认目录 ~/.goctl/${goctl-version}/mongo
error.tpl
package modelimport ("errors""github.com/zeromicro/go-zero/core/stores/mon")var (ErrNotFound = mon.ErrNotFoundErrInvalidObjectId = errors.New("invalid objectId"))
model.tpl
// Code generated by goctl. DO NOT EDIT.package modelimport ("context""time"{{if .Cache}}"github.com/zeromicro/go-zero/core/stores/monc"{{else}}"github.com/zeromicro/go-zero/core/stores/mon"{{end}}"go.mongodb.org/mongo-driver/bson""go.mongodb.org/mongo-driver/bson/primitive""go.mongodb.org/mongo-driver/mongo"){{if .Cache}}var prefix{{.Type}}CacheKey = "cache:{{.lowerType}}:"{{end}}type {{.lowerType}}Model interface{Insert(ctx context.Context,data *{{.Type}}) errorFindOne(ctx context.Context,id string) (*{{.Type}}, error)Update(ctx context.Context,data *{{.Type}}) (*mongo.UpdateResult, error)Delete(ctx context.Context,id string) (int64, error)}type default{{.Type}}Model struct {conn {{if .Cache}}*monc.Model{{else}}*mon.Model{{end}}}func newDefault{{.Type}}Model(conn {{if .Cache}}*monc.Model{{else}}*mon.Model{{end}}) *default{{.Type}}Model {return &default{{.Type}}Model{conn: conn}}func (m *default{{.Type}}Model) Insert(ctx context.Context, data *{{.Type}}) error {if data.ID.IsZero() {data.ID = primitive.NewObjectID()data.CreateAt = time.Now()data.UpdateAt = time.Now()}{{if .Cache}}key := prefix{{.Type}}CacheKey + data.ID.Hex(){{end}}_, err := m.conn.InsertOne(ctx, {{if .Cache}}key, {{end}} data)return err}func (m *default{{.Type}}Model) FindOne(ctx context.Context, id string) (*{{.Type}}, error) {oid, err := primitive.ObjectIDFromHex(id)if err != nil {return nil, ErrInvalidObjectId}var data {{.Type}}{{if .Cache}}key := prefix{{.Type}}CacheKey + id{{end}}err = m.conn.FindOne(ctx, {{if .Cache}}key, {{end}}&data, bson.M{"_id": oid})switch err {case nil:return &data, nilcase {{if .Cache}}monc{{else}}mon{{end}}.ErrNotFound:return nil, ErrNotFounddefault:return nil, err}}func (m *default{{.Type}}Model) Update(ctx context.Context, data *{{.Type}}) (*mongo.UpdateResult, error) {data.UpdateAt = time.Now(){{if .Cache}}key := prefix{{.Type}}CacheKey + data.ID.Hex(){{end}}res, err := m.conn.UpdateOne(ctx, {{if .Cache}}key, {{end}}bson.M{"_id": data.ID}, bson.M{"$set": data})return res, err}func (m *default{{.Type}}Model) Delete(ctx context.Context, id string) (int64, error) {oid, err := primitive.ObjectIDFromHex(id)if err != nil {return 0, ErrInvalidObjectId}{{if .Cache}}key := prefix{{.Type}}CacheKey +id{{end}}res, err := m.conn.DeleteOne(ctx, {{if .Cache}}key, {{end}}bson.M{"_id": oid})return res, err}
模板注入对象为 map[string]any
map[string]any{"Type": stringx.From(t).Title(),"lowerType": stringx.From(t).Untitle(),"Cache": ctx.Cache,}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .Cache | bool | 是否启用缓存 |
| .Type | string | 模型类型名称 |
| .lowerType | string | 小写模型类型名称 |
model_custom.tpl
package model{{if .Cache}}import ("github.com/zeromicro/go-zero/core/stores/cache""github.com/zeromicro/go-zero/core/stores/monc"){{else}}import "github.com/zeromicro/go-zero/core/stores/mon"{{end}}{{if .Easy}}const {{.Type}}CollectionName = "{{.snakeType}}"{{end}}var _ {{.Type}}Model = (*custom{{.Type}}Model)(nil)type (// {{.Type}}Model is an interface to be customized, add more methods here,// and implement the added methods in custom{{.Type}}Model.{{.Type}}Model interface {{{.lowerType}}Model}custom{{.Type}}Model struct {*default{{.Type}}Model})// New{{.Type}}Model returns a model for the mongo.{{if .Easy}}func New{{.Type}}Model(url, db string{{if .Cache}}, c cache.CacheConf{{end}}) {{.Type}}Model {conn := {{if .Cache}}monc{{else}}mon{{end}}.MustNewModel(url, db, {{.Type}}CollectionName{{if .Cache}}, c{{end}})return &custom{{.Type}}Model{default{{.Type}}Model: newDefault{{.Type}}Model(conn),}}{{else}}func New{{.Type}}Model(url, db, collection string{{if .Cache}}, c cache.CacheConf{{end}}) {{.Type}}Model {conn := {{if .Cache}}monc{{else}}mon{{end}}.MustNewModel(url, db, collection{{if .Cache}}, c{{end}})return &custom{{.Type}}Model{default{{.Type}}Model: newDefault{{.Type}}Model(conn),}}{{end}}
模板注入对象为 map[string]any
map[string]any{"Type": stringx.From(t).Title(),"lowerType": stringx.From(t).Untitle(),"snakeType": stringx.From(t).ToSnake(),"Cache": ctx.Cache,"Easy": ctx.Easy,}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .Cache | bool | 是否启用缓存 |
| .Easy | bool | 是否简易模式 |
| .Type | string | 模型类型名称 |
| .snakeType | string | 蛇形模型类型名称 |
| .lowerType | string | 小写模型类型名称 |
types.tpl
package modelimport ("time""go.mongodb.org/mongo-driver/bson/primitive")type {{.Type}} struct {ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`// TODO: Fill your own fieldsUpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"`CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"`}
模板注入对象为 map[string]any
map[string]any{"Type": stringx.From(t).Title(),}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .Type | string | 模型类型名称 |
mysql 代码生成模板
模板默认目录 ~/.goctl/${goctl-version}/model
customized.tpl
空模板,方便自定义
模板注入对象 map[string]any
mmap[string]any{"withCache": withCache,"containsIndexCache": table.ContainsUniqueCacheKey,"upperStartCamelObject": camel,"lowerStartCamelObject": stringx.From(camel).Untitle(),"lowerStartCamelPrimaryKey": util.EscapeGolangKeyword(stringx.From(table.PrimaryKey.Name.ToCamel()).Untitle()),"upperStartCamelPrimaryKey": table.PrimaryKey.Name.ToCamel(),"primaryKeyDataType": table.PrimaryKey.DataType,"originalPrimaryKey": wrapWithRawString(table.PrimaryKey.Name.Source(), postgreSql),"primaryCacheKey": table.PrimaryCacheKey.DataKeyExpression,"primaryKeyVariable": table.PrimaryCacheKey.KeyLeft,"keys": strings.Join(keys, "\n"),"keyValues": strings.Join(keyVars, ", "),"expression": strings.Join(expressions, ", "),"expressionValues": strings.Join(expressionValues, ", "),"postgreSql": postgreSql,"fields": fields,"data": table,}
delete.tpl
func (m *default{{.upperStartCamelObject}}Model) Delete(ctx context.Context, {{.lowerStartCamelPrimaryKey}} {{.dataType}}) error {{{if .withCache}}{{if .containsIndexCache}}data, err:=m.FindOne(ctx, {{.lowerStartCamelPrimaryKey}})if err!=nil{return err}{{end}} {{.keys}}_, err {{if .containsIndexCache}}={{else}}:={{end}} m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {query := fmt.Sprintf("delete from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table)return conn.ExecCtx(ctx, query, {{.lowerStartCamelPrimaryKey}})}, {{.keyValues}}){{else}}query := fmt.Sprintf("delete from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table)_,err:=m.conn.ExecCtx(ctx, query, {{.lowerStartCamelPrimaryKey}}){{end}}return err}
模板注入对象为 map[string]any
map[string]any{"upperStartCamelObject": camel,"withCache": withCache,"containsIndexCache": table.ContainsUniqueCacheKey,"lowerStartCamelPrimaryKey": util.EscapeGolangKeyword(stringx.From(table.PrimaryKey.Name.ToCamel()).Untitle()),"dataType": table.PrimaryKey.DataType,"keys": strings.Join(keys, "\n"),"originalPrimaryKey": wrapWithRawString(table.PrimaryKey.Name.Source(), postgreSql),"keyValues": strings.Join(keyVars, ", "),"postgreSql": postgreSql,"data": table,}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
| .lowerStartCamelPrimaryKey | string | 主键变量名的首字母小写形式 |
| .dataType | string | 主键数据类型 |
| .withCache | bool | 是否启用缓存 |
| .containsIndexCache | bool | 是否包含索引缓存逻辑 |
| .keys | string | 在包含索引缓存时,用于查找数据的逻辑 |
| .keyValues | string | 在缓存中找到的键值对 |
| .postgreSql | bool | 是否使用 PostgreSQL |
| .originalPrimaryKey | string | 原始主键名称 |
err.tpl
package {{.pkg}}import "github.com/zeromicro/go-zero/core/stores/sqlx"var ErrNotFound = sqlx.ErrNotFound
模板注入对象为 map[string]any
map[string]any{"pkg": g.pkg,}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .pkg | string | 包名 |
field.tpl
{{.name}} {{.type}} {{.tag}} {{if .hasComment}}// {{.comment}}{{end}}
模板注入对象为 map[string]any
map[string]any{"name": util.SafeString(field.Name.ToCamel()),"type": field.DataType,"tag": tag,"hasComment": field.Comment != "","comment": field.Comment,"data": table,}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .name | string | 名称 |
| .type | string | 类型 |
| .tag | string | 标签 |
| .hasComment | bool | 是否有注释 |
| .comment | string | 注释内容 (可选) |
find-one.tpl
func (m *default{{.upperStartCamelObject}}Model) FindOne(ctx context.Context, {{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error) {{{if .withCache}}{{.cacheKey}}var resp {{.upperStartCamelObject}}err := m.QueryRowCtx(ctx, &resp, {{.cacheKeyVariable}}, func(ctx context.Context, conn sqlx.SqlConn, v any) error {query := fmt.Sprintf("select %s from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}} limit 1", {{.lowerStartCamelObject}}Rows, m.table)return conn.QueryRowCtx(ctx, v, query, {{.lowerStartCamelPrimaryKey}})})switch err {case nil:return &resp, nilcase sqlc.ErrNotFound:return nil, ErrNotFounddefault:return nil, err}{{else}}query := fmt.Sprintf("select %s from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}} limit 1", {{.lowerStartCamelObject}}Rows, m.table)var resp {{.upperStartCamelObject}}err := m.conn.QueryRowCtx(ctx, &resp, query, {{.lowerStartCamelPrimaryKey}})switch err {case nil:return &resp, nilcase sqlx.ErrNotFound:return nil, ErrNotFounddefault:return nil, err}{{end}}}
模板注入对象为 map[string]any
map[string]any{"withCache": withCache,"upperStartCamelObject": camel,"lowerStartCamelObject": stringx.From(camel).Untitle(),"originalPrimaryKey": wrapWithRawString(table.PrimaryKey.Name.Source(), postgreSql),"lowerStartCamelPrimaryKey": util.EscapeGolangKeyword(stringx.From(table.PrimaryKey.Name.ToCamel()).Untitle()),"dataType": table.PrimaryKey.DataType,"cacheKey": table.PrimaryCacheKey.KeyExpression,"cacheKeyVariable": table.PrimaryCacheKey.KeyLeft,"postgreSql": postgreSql,"data": table,}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
| .lowerStartCamelPrimaryKey | string | 主键变量名的首字母小写形式 |
| .dataType | string | 主键数据类型 |
| .withCache | bool | 是否启用缓存 |
| .cacheKey | string | 缓存键 |
| .cacheKeyVariable | string | 缓存键变量 |
| .postgreSql | bool | 是否使用 PostgreSQL |
| .originalPrimaryKey | string | 原始主键名称 |
| .lowerStartCamelObject | string | 对象名称的首字母小写形式 |
find-one-by-field.tpl
func (m *default{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}(ctx context.Context, {{.in}}) (*{{.upperStartCamelObject}}, error) {{{if .withCache}}{{.cacheKey}}var resp {{.upperStartCamelObject}}err := m.QueryRowIndexCtx(ctx, &resp, {{.cacheKeyVariable}}, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v any) (i any, e error) {query := fmt.Sprintf("select %s from %s where {{.originalField}} limit 1", {{.lowerStartCamelObject}}Rows, m.table)if err := conn.QueryRowCtx(ctx, &resp, query, {{.lowerStartCamelField}}); err != nil {return nil, err}return resp.{{.upperStartCamelPrimaryKey}}, nil}, m.queryPrimary)switch err {case nil:return &resp, nilcase sqlc.ErrNotFound:return nil, ErrNotFounddefault:return nil, err}}{{else}}var resp {{.upperStartCamelObject}}query := fmt.Sprintf("select %s from %s where {{.originalField}} limit 1", {{.lowerStartCamelObject}}Rows, m.table )err := m.conn.QueryRowCtx(ctx, &resp, query, {{.lowerStartCamelField}})switch err {case nil:return &resp, nilcase sqlx.ErrNotFound:return nil, ErrNotFounddefault:return nil, err}}{{end}}
模板注入对象为 map[string]any
map[string]any{"upperStartCamelObject": camelTableName,"upperField": key.FieldNameJoin.Camel().With("").Source(),"in": in,"withCache": withCache,"cacheKey": key.KeyExpression,"cacheKeyVariable": key.KeyLeft,"lowerStartCamelObject": stringx.From(camelTableName).Untitle(),"lowerStartCamelField": paramJoinString,"upperStartCamelPrimaryKey": table.PrimaryKey.Name.ToCamel(),"originalField": originalFieldString,"postgreSql": postgreSql,"data": table,}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
| .upperField | string | 字段名称的首字母大写形式 |
| .in | string | 输入参数变量名 |
| .withCache | bool | 是否启用缓存 |
| .cacheKey | string | 缓存键 |
| .cacheKeyVariable | string | 缓存键变量名 |
| .formatPrimary | string | 主键格式化字符串 |
| .lowerStartCamelObject | string | 对象名称的首字母小写形式 |
| .originalField | string | 原始字段名 |
| .lowerStartCamelObjectRows | string | 对象行数的首字母小写形式 |
| .table | string | 表名 |
| .queryPrimary | string | 主要查询 |
find-one-by-field-extra-method.tpl
func (m *default{{.upperStartCamelObject}}Model) formatPrimary(primary any) string {return fmt.Sprintf("%s%v", {{.primaryKeyLeft}}, primary)}func (m *default{{.upperStartCamelObject}}Model) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary any) error {query := fmt.Sprintf("select %s from %s where {{.originalPrimaryField}} = {{if .postgreSql}}$1{{else}}?{{end}} limit 1", {{.lowerStartCamelObject}}Rows, m.table )return conn.QueryRowCtx(ctx, v, query, primary)}
模板注入对象为
map[string]any{"upperStartCamelObject": camelTableName,"primaryKeyLeft": table.PrimaryCacheKey.VarLeft,"lowerStartCamelObject": stringx.From(camelTableName).Untitle(),"originalPrimaryField": wrapWithRawString(table.PrimaryKey.Name.Source(), postgreSql),"postgreSql": postgreSql,"data": table,}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
| .primaryKeyLeft | string | 主键左部分字符串 |
| .lowerStartCamelObject | string | 对象名称的首字母小写形式 |
| .originalPrimaryField | string | 原始主键字段名 |
| .postgreSql | bool | 是否使用 PostgreSQL |
| .lowerStartCamelObjectRows | string | 对象行数的首字母小写形式 |
import.tpl
import ("context""database/sql""fmt""strings"{{if .time}}"time"{{end}}{{if .containsPQ}}"github.com/lib/pq"{{end}}"github.com/zeromicro/go-zero/core/stores/builder""github.com/zeromicro/go-zero/core/stores/cache""github.com/zeromicro/go-zero/core/stores/sqlc""github.com/zeromicro/go-zero/core/stores/sqlx""github.com/zeromicro/go-zero/core/stringx"{{.third}})
模板注入对象为
map[string]any{"time": timeImport,"containsPQ": table.ContainsPQ,"data": table,"third": strings.Join(thirdImports, "\n"),}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .time | bool | 是否导入时间包 (time) |
| .containsPQ | bool | 是否包含 PostgreSQL 的依赖 (github.com/lib/pq) |
| .third | string | 其他第三方包的导入路径 |
import.no-cache.tpl
import ("context""database/sql""fmt""strings"{{if .time}}"time"{{end}}{{if .containsPQ}}"github.com/lib/pq"{{end}}"github.com/zeromicro/go-zero/core/stores/builder""github.com/zeromicro/go-zero/core/stores/sqlx""github.com/zeromicro/go-zero/core/stringx"{{.third}})
模板注入对象为:
map[string]any{"time": timeImport,"containsPQ": table.ContainsPQ,"data": table,"third": strings.Join(thirdImports, "\n"),}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .time | bool | 是否导入时间包 (time) |
| .containsPQ | bool | 是否包含 PostgreSQL 的依赖 (github.com/lib/pq) |
| .third | string | 其他第三方包的导入路径 |
insert.tpl
func (m *default{{.upperStartCamelObject}}Model) Insert(ctx context.Context, data *{{.upperStartCamelObject}}) (sql.Result,error) {{{if .withCache}}{{.keys}}ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {query := fmt.Sprintf("insert into %s (%s) values ({{.expression}})", m.table, {{.lowerStartCamelObject}}RowsExpectAutoSet)return conn.ExecCtx(ctx, query, {{.expressionValues}})}, {{.keyValues}}){{else}}query := fmt.Sprintf("insert into %s (%s) values ({{.expression}})", m.table, {{.lowerStartCamelObject}}RowsExpectAutoSet)ret,err:=m.conn.ExecCtx(ctx, query, {{.expressionValues}}){{end}}return ret,err}
模板注入对象为
map[string]any{"withCache": withCache,"upperStartCamelObject": camel,"lowerStartCamelObject": stringx.From(camel).Untitle(),"expression": strings.Join(expressions, ", "),"expressionValues": strings.Join(expressionValues, ", "),"keys": strings.Join(keys, "\n"),"keyValues": strings.Join(keyVars, ", "),"data": table,})
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
| .withCache | bool | 是否启用缓存 |
| .keys | string | 在启用缓存时,用于键的生成逻辑 |
| .lowerStartCamelObject | string | 对象名称的首字母小写形式 |
| .expression | string | 表达式 |
| .expressionValues | string | 表达式值 |
interface-delete.tpl
Delete(ctx context.Context, {{.lowerStartCamelPrimaryKey}} {{.dataType}}) error
模板注入对象为
map[string]any{"lowerStartCamelPrimaryKey": util.EscapeGolangKeyword(stringx.From(table.PrimaryKey.Name.ToCamel()).Untitle()),"dataType": table.PrimaryKey.DataType,"data": table,}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .lowerStartCamelPrimaryKey | string | 主键变量名的首字母小写形式 |
| .dataType | string | 主键数据类型 |
interface-find-one.tpl
FindOne(ctx context.Context, {{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error)
模板注入对象为:
map[string]any{"upperStartCamelObject": camel,"lowerStartCamelPrimaryKey": util.EscapeGolangKeyword(stringx.From(table.PrimaryKey.Name.ToCamel()).Untitle()),"dataType": table.PrimaryKey.DataType,"data": table,}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .lowerStartCamelPrimaryKey | string | 主键变量名的首字母小写形式 |
| .dataType | string | 主键数据类型 |
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
interface-find-one-by-field.tpl
FindOneBy{{.upperField}}(ctx context.Context, {{.in}}) (*{{.upperStartCamelObject}}, error)
模板注入对象为:
string]any{"upperStartCamelObject": camelTableName,"upperField": key.FieldNameJoin.Camel().With("").Source(),"in": in,"data": table,}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .upperField | string | 字段名称的首字母大写形式 |
| .in | string | 输入参数变量名 |
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
interface-insert.tpl
Insert(ctx context.Context, data *{{.upperStartCamelObject}}) (sql.Result,error)
注入对象为
string]any{"upperStartCamelObject": camel,"data": table,}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
interface-update.tpl
Update(ctx context.Context, {{if .containsIndexCache}}newData{{else}}data{{end}} *{{.upperStartCamelObject}}) error
模板注入对象为:
map[string]any{"upperStartCamelObject": camelTableName,"data": table,}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .containsIndexCache | bool | 是否包含索引缓存 |
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
model.tpl
package {{.pkg}}{{if .withCache}}import ("github.com/zeromicro/go-zero/core/stores/cache""github.com/zeromicro/go-zero/core/stores/sqlx"){{else}}import "github.com/zeromicro/go-zero/core/stores/sqlx"{{end}}var _ {{.upperStartCamelObject}}Model = (*custom{{.upperStartCamelObject}}Model)(nil)type (// {{.upperStartCamelObject}}Model is an interface to be customized, add more methods here,// and implement the added methods in custom{{.upperStartCamelObject}}Model.{{.upperStartCamelObject}}Model interface {{{.lowerStartCamelObject}}Model{{if not .withCache}}withSession(session sqlx.Session) {{.upperStartCamelObject}}Model{{end}}}custom{{.upperStartCamelObject}}Model struct {*default{{.upperStartCamelObject}}Model})// New{{.upperStartCamelObject}}Model returns a model for the database table.func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf, opts ...cache.Option{{end}}) {{.upperStartCamelObject}}Model {return &custom{{.upperStartCamelObject}}Model{default{{.upperStartCamelObject}}Model: new{{.upperStartCamelObject}}Model(conn{{if .withCache}}, c, opts...{{end}}),}}{{if not .withCache}}func (m *custom{{.upperStartCamelObject}}Model) withSession(session sqlx.Session) {{.upperStartCamelObject}}Model {return New{{.upperStartCamelObject}}Model(sqlx.NewSqlConnFromSession(session))}{{end}}
模板注入对象为:
map[string]any{"pkg": g.pkg,"withCache": withCache,"upperStartCamelObject": in.Name.ToCamel(),"lowerStartCamelObject": stringx.From(in.Name.ToCamel()).Untitle(),}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .pkg | string | 包名 |
| .withCache | bool | 是否启用缓存 |
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
| .lowerStartCamelObject | string | 对象名称的首字母小写形式 |
model-new.tpl
func new{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf, opts ...cache.Option{{end}}) *default{{.upperStartCamelObject}}Model {return &default{{.upperStartCamelObject}}Model{{{if .withCache}}CachedConn: sqlc.NewConn(conn, c, opts...){{else}}conn:conn{{end}},table: {{.table}},}}
模板注入对象为:
map[string]any{"table": t,"withCache": withCache,"upperStartCamelObject": table.Name.ToCamel(),"data": table,}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
| .withCache | bool | 是否启用缓存 |
| .table | string | 数据库表名 |
table-name.tpl
func (m *default{{.upperStartCamelObject}}Model) tableName() string {return m.table}
模板注入对象为:
map[string]any{"tableName": table.Name.Source(),"upperStartCamelObject": table.Name.ToCamel(),}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
tag.tpl
`db:"{{.field}}"`
模板注入对象为:
map[string]any{"field": in,"data": table,}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .field | string | db tag 名称 |
types.tpl
type ({{.lowerStartCamelObject}}Model interface{{{.method}}}default{{.upperStartCamelObject}}Model struct {{{if .withCache}}sqlc.CachedConn{{else}}conn sqlx.SqlConn{{end}}table string}{{.upperStartCamelObject}} struct {{{.fields}}})
模板注入对象为:
map[string]any{"withCache": withCache,"method": methods,"upperStartCamelObject": table.Name.ToCamel(),"lowerStartCamelObject": stringx.From(table.Name.ToCamel()).Untitle(),"fields": fieldsString,"data": table,}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .lowerStartCamelObject | string | 对象名称的首字母小写形式 |
| .method | string | 接口方法定义 |
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
| .withCache | bool | 是否启用缓存 |
| .fields | string | 结构体字段定义 |
update.tpl
func (m *default{{.upperStartCamelObject}}Model) Update(ctx context.Context, {{if .containsIndexCache}}newData{{else}}data{{end}} *{{.upperStartCamelObject}}) error {{{if .withCache}}{{if .containsIndexCache}}data, err:=m.FindOne(ctx, newData.{{.upperStartCamelPrimaryKey}})if err!=nil{return err}{{end}} {{.keys}}_, {{if .containsIndexCache}}err{{else}}err:{{end}}= m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {query := fmt.Sprintf("update %s set %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table, {{.lowerStartCamelObject}}RowsWithPlaceHolder)return conn.ExecCtx(ctx, query, {{.expressionValues}})}, {{.keyValues}}){{else}}query := fmt.Sprintf("update %s set %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table, {{.lowerStartCamelObject}}RowsWithPlaceHolder)_,err:=m.conn.ExecCtx(ctx, query, {{.expressionValues}}){{end}}return err}
模板注入对象为:
map[string]any{"withCache": withCache,"containsIndexCache": table.ContainsUniqueCacheKey,"upperStartCamelObject": camelTableName,"keys": strings.Join(keys, "\n"),"keyValues": strings.Join(keyVars, ", "),"primaryCacheKey": table.PrimaryCacheKey.DataKeyExpression,"primaryKeyVariable": table.PrimaryCacheKey.KeyLeft,"lowerStartCamelObject": stringx.From(camelTableName).Untitle(),"upperStartCamelPrimaryKey": util.EscapeGolangKeyword(stringx.From(table.PrimaryKey.Name.ToCamel()).Title(),),"originalPrimaryKey": wrapWithRawString(table.PrimaryKey.Name.Source(), postgreSql,),"expressionValues": strings.Join(expressionValues, ", ",),"postgreSql": postgreSql,"data": table,}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
| .containsIndexCache | bool | 是否包含索引缓存 |
| .withCache | bool | 是否启用缓存 |
| .upperStartCamelPrimaryKey | string | 主键字段名称的首字母大写形式 |
| .lowerStartCamelObject | string | 对象名称的首字母小写形式 |
| .lowerStartCamelPrimaryKey | string | 主键字段名称的首字母小写形式 |
| .postgreSql | bool | 是否为 PostgreSQL 数据库 |
| .keys | string | 键值 |
| .expressionValues | string | 表达值 |
var.tpl
var ({{.lowerStartCamelObject}}FieldNames = builder.RawFieldNames(&{{.upperStartCamelObject}}{}{{if .postgreSql}}, true{{end}}){{.lowerStartCamelObject}}Rows = strings.Join({{.lowerStartCamelObject}}FieldNames, ","){{.lowerStartCamelObject}}RowsExpectAutoSet = {{if .postgreSql}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, {{if .autoIncrement}}"{{.originalPrimaryKey}}", {{end}} {{.ignoreColumns}}), ","){{else}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, {{if .autoIncrement}}"{{.originalPrimaryKey}}", {{end}} {{.ignoreColumns}}), ","){{end}}{{.lowerStartCamelObject}}RowsWithPlaceHolder = {{if .postgreSql}}builder.PostgreSqlJoin(stringx.Remove({{.lowerStartCamelObject}}FieldNames, "{{.originalPrimaryKey}}", {{.ignoreColumns}})){{else}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, "{{.originalPrimaryKey}}", {{.ignoreColumns}}), "=?,") + "=?"{{end}}{{if .withCache}}{{.cacheKeys}}{{end}})
模板注入对象为:
map[string]any{"lowerStartCamelObject": stringx.From(camel).Untitle(),"upperStartCamelObject": camel,"cacheKeys": strings.Join(keys, "\n"),"autoIncrement": table.PrimaryKey.AutoIncrement,"originalPrimaryKey": wrapWithRawString(table.PrimaryKey.Name.Source(), postgreSql),"withCache": withCache,"postgreSql": postgreSql,"data": table,"ignoreColumns": func() string {var set = collection.NewSet()for _, c := range table.ignoreColumns {if postgreSql {set.AddStr(fmt.Sprintf(`"%s"`, c))} else {set.AddStr(fmt.Sprintf("\"`%s`\"", c))}}list := set.KeysStr()sort.Strings(list)return strings.Join(list, ", ")}(),}
| 模板变量 | 类型 | 描述 |
|---|---|---|
| .lowerStartCamelObject | string | 对象名称的首字母小写形式 |
| .upperStartCamelObject | string | 对象名称的首字母大写形式 |
| .postgreSql | bool | 是否为 PostgreSQL 数据库 |
| .autoIncrement | string | 自动增量标识符 |
| .originalPrimaryKey | string | 原始主键字段名 |
| .ignoreColumns | string | 忽略的列名列表 |
| .withCache | bool | 是否启用缓存 |
| .cacheKeys | string | 缓存键列表 |
goctl kube 代码生成模板
模板默认目录 ~/.goctl/${goctl-version}/kube
deployment.tpl
apiVersion: apps/v1kind: Deploymentmetadata:name: {{.Name}}namespace: {{.Namespace}}labels:app: {{.Name}}spec:replicas: {{.Replicas}}revisionHistoryLimit: {{.Revisions}}selector:matchLabels:app: {{.Name}}template:metadata:labels:app: {{.Name}}spec:{{if .ServiceAccount}}serviceAccountName: {{.ServiceAccount}}{{end}}containers:- name: {{.Name}}image: {{.Image}}{{if .ImagePullPolicy}}imagePullPolicy: {{.ImagePullPolicy}}{{end}}ports:- containerPort: {{.Port}}readinessProbe:tcpSocket:port: {{.Port}}initialDelaySeconds: 5periodSeconds: 10livenessProbe:tcpSocket:port: {{.Port}}initialDelaySeconds: 15periodSeconds: 20resources:requests:cpu: {{.RequestCpu}}mmemory: {{.RequestMem}}Milimits:cpu: {{.LimitCpu}}mmemory: {{.LimitMem}}MivolumeMounts:- name: timezonemountPath: /etc/localtime{{if .Secret}}imagePullSecrets:- name: {{.Secret}}{{end}}volumes:- name: timezonehostPath:path: /usr/share/zoneinfo/Asia/Shanghai---apiVersion: v1kind: Servicemetadata:name: {{.Name}}-svcnamespace: {{.Namespace}}spec:ports:{{if .UseNodePort}}- nodePort: {{.NodePort}}port: {{.Port}}protocol: TCPtargetPort: {{.TargetPort}}type:NodePort{{else}}- port: {{.Port}}targetPort: {{.TargetPort}}{{end}}selector:app: {{.Name}}---apiVersion: autoscaling/v2beta2kind: HorizontalPodAutoscalermetadata:name: {{.Name}}-hpa-cnamespace: {{.Namespace}}labels:app: {{.Name}}-hpa-cspec:scaleTargetRef:apiVersion: apps/v1kind: Deploymentname: {{.Name}}minReplicas: {{.MinReplicas}}maxReplicas: {{.MaxReplicas}}metrics:- type: Resourceresource:name: cputarget:type: UtilizationaverageUtilization: 80---apiVersion: autoscaling/v2beta2kind: HorizontalPodAutoscalermetadata:name: {{.Name}}-hpa-mnamespace: {{.Namespace}}labels:app: {{.Name}}-hpa-mspec:scaleTargetRef:apiVersion: apps/v1kind: Deploymentname: {{.Name}}minReplicas: {{.MinReplicas}}maxReplicas: {{.MaxReplicas}}metrics:- type: Resourceresource:name: memorytarget:type: UtilizationaverageUtilization: 80
模板注入对象为 Deployment
Deployment{Name: varStringName,Namespace: varStringNamespace,Image: varStringImage,Secret: varStringSecret,Replicas: varIntReplicas,Revisions: varIntRevisions,Port: varIntPort,TargetPort: varIntTargetPort,NodePort: nodePort,UseNodePort: nodePort > 0,RequestCpu: varIntRequestCpu,RequestMem: varIntRequestMem,LimitCpu: varIntLimitCpu,LimitMem: varIntLimitMem,MinReplicas: varIntMinReplicas,MaxReplicas: varIntMaxReplicas,ServiceAccount: varStringServiceAccount,ImagePullPolicy: varStringImagePullPolicy,}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .Name | string | 名称 |
| .Namespace | string | 命名空间 |
| .Replicas | int | 副本数量 |
| .Revisions | int | 修订历史限制 |
| .ServiceAccount | string | 服务账户名称 (可选) |
| .Image | string | 容器镜像 |
| .ImagePullPolicy | string | 镜像拉取策略 (可选) |
| .Port | int | 容器端口 |
| .RequestCpu | int | CPU 请求 (单位: millicores) |
| .RequestMem | int | 内存请求 (单位: MiB) |
| .LimitCpu | int | CPU 限制 (单位: millicores) |
| .LimitMem | int | 内存限制 (单位: MiB) |
| .Secret | string | 镜像拉取密钥 (可选) |
| .UseNodePort | bool | 使用 NodePort (布尔值) |
| .NodePort | int | NodePort 端口 (当 UseNodePort 为 true 时) |
| .TargetPort | int | 目标端口 |
| .MinReplicas | int | 最小副本数 |
| .MaxReplicas | int | 最大副本数 |
goctl docker 生成模板
模板默认目录 ~/.goctl/${goctl-version}/docker
docker.tpl
FROM golang:{{.Version}}alpine AS builderLABEL stage=gobuilderENV CGO_ENABLED 0{{if .Chinese}}ENV GOPROXY https://goproxy.cn,directRUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories{{end}}{{if .HasTimezone}}RUN apk update --no-cache && apk add --no-cache tzdata{{end}}WORKDIR /buildADD go.mod .ADD go.sum .RUN go mod downloadCOPY . .{{if .Argument}}COPY {{.GoRelPath}}/etc /app/etc{{end}}RUN go build -ldflags="-s -w" -o /app/{{.ExeFile}} {{.GoMainFrom}}FROM {{.BaseImage}}COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt{{if .HasTimezone}}COPY --from=builder /usr/share/zoneinfo/{{.Timezone}} /usr/share/zoneinfo/{{.Timezone}}ENV TZ {{.Timezone}}{{end}}WORKDIR /appCOPY --from=builder /app/{{.ExeFile}} /app/{{.ExeFile}}{{if .Argument}}COPY --from=builder /app/etc /app/etc{{end}}{{if .HasPort}}EXPOSE {{.Port}}{{end}}CMD ["./{{.ExeFile}}"{{.Argument}}]
模板注入对象为 Docker
Docker{Chinese: env.InChina(),GoMainFrom: path.Join(projPath, goFile),GoRelPath: projPath,GoFile: goFile,ExeFile: exeName,BaseImage: base,HasPort: port > 0,Port: port,Argument: builder.String(),Version: version,HasTimezone: len(timezone) > 0,Timezone: timezone,}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .Version | string | Golang 版本 |
| .Chinese | bool | 是否使用中国时区 |
| .HasTimezone | bool | 是否需要时区设置 |
| .Argument | string | 命令行参数 (可选) |
| .GoRelPath | string | Go 项目相对路径 |
| .ExeFile | string | 可执行文件名称 |
| .GoMainFrom | string | Go 主程序源文件路径 |
| .BaseImage | string | 基础镜像 |
| .Timezone | string | 时区 |
| .HasPort | bool | 是否需要暴露端口 |
rpc 代码生成模板
模板默认目录 ~/.goctl/${goctl-version}/rpc
call.tpl
{{.head}}package {{.filePackage}}import ("context"{{.pbPackage}}{{if ne .pbPackage .protoGoPackage}}{{.protoGoPackage}}{{end}}"github.com/zeromicro/go-zero/zrpc""google.golang.org/grpc")type ({{.alias}}{{.serviceName}} interface {{{.interface}}}default{{.serviceName}} struct {cli zrpc.Client})func New{{.serviceName}}(cli zrpc.Client) {{.serviceName}} {return &default{{.serviceName}}{cli: cli,}}{{.functions}}
模板注入对象 map[string]any
map[string]any{"name": callFilename,"alias": strings.Join(aliasKeys, pathx.NL),"head": head,"filePackage": childDir,"pbPackage": pbPackage,"protoGoPackage": protoGoPackage,"serviceName": serviceName,"functions": strings.Join(functions, pathx.NL),"interface": strings.Join(iFunctions, pathx.NL),}
| pipeline变量 | 类型 | 说明 |
|---|---|---|
| .head | string | 头部信息 |
| .filePackage | string | 文件包名 |
| .pbPackage | string | Protocol Buffers 包导入路径 |
| .protoGoPackage | string | Protocol Buffers Go 包导入路径 (可选) |
| .alias | string | 类型别名声明 |
| .serviceName | string | 服务名称 |
| .interface | string | 接口定义 |
| .functions | string | 函数实现 |
config.tpl
package configimport "github.com/zeromicro/go-zero/zrpc"type Config struct {zrpc.RpcServerConf}
无变量注入
etc.tpl
Name: {{.serviceName}}.rpcListenOn: 0.0.0.0:8080Etcd:Hosts:- 127.0.0.1:2379Key: {{.serviceName}}.rpc
模板注入对象为:
map[string]any{"serviceName": strings.ToLower(stringx.From(ctx.GetServiceName().Source()).ToCamel()),}
| pipeline变量 | 类型 | 描述 |
|---|---|---|
| .serviceName | string | 服务名称的变量 |
logic.tpl
package {{.packageName}}import ("context"{{.imports}}"github.com/zeromicro/go-zero/core/logx")type {{.logicName}} struct {ctx context.ContextsvcCtx *svc.ServiceContextlogx.Logger}func New{{.logicName}}(ctx context.Context,svcCtx *svc.ServiceContext) *{{.logicName}} {return &{{.logicName}}{ctx: ctx,svcCtx: svcCtx,Logger: logx.WithContext(ctx),}}{{.functions}}
模板注入对象为:
- 不支持分组情况下
map[string]any{"logicName": fmt.Sprintf("%sLogic", stringx.From(rpc.Name).ToCamel()),"functions": functions,"packageName": "logic","imports": strings.Join(imports.KeysStr(), pathx.NL),}
- 支持分组情况下
map[string]any{"logicName": logicName,"functions": functions,"packageName": packageName,"imports": strings.Join(imports.KeysStr(), pathx.NL),}
| pipeline变量 | 类型 | 描述 |
|---|---|---|
| .packageName | string | 包名 |
| .imports | string | 导入的包列表 |
| .logicName | string | 逻辑结构体名称 |
| .functions | string | 函数定义部分 |
main.tpl
package mainimport ("flag""fmt"{{.imports}}"github.com/zeromicro/go-zero/core/conf""github.com/zeromicro/go-zero/core/service""github.com/zeromicro/go-zero/zrpc""google.golang.org/grpc""google.golang.org/grpc/reflection")var configFile = flag.String("f", "etc/{{.serviceName}}.yaml", "the config file")func main() {flag.Parse()var c config.Configconf.MustLoad(*configFile, &c)ctx := svc.NewServiceContext(c)s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {{{range .serviceNames}} {{.Pkg}}.Register{{.GRPCService}}Server(grpcServer, {{.ServerPkg}}.New{{.Service}}Server(ctx)){{end}}if c.Mode == service.DevMode || c.Mode == service.TestMode {reflection.Register(grpcServer)}})defer s.Stop()fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)s.Start()}
模板注入对象为:
map[string]any{"serviceName": etcFileName,"imports": strings.Join(imports, pathx.NL),"pkg": proto.PbPackage,"serviceNames": serviceNames,}
| pipeline变量 | 类型 | 描述 |
|---|---|---|
| .imports | string | 导入的包列表 |
| .serviceName | string | 服务名称 |
| .serviceNames | 列表 | 包含以下子变量的列表 |
| .serviceNames.Pkg | string | 服务包名 |
| .serviceNames.GRPCService | string | gRPC 服务名称 |
| .serviceNames.ServerPkg | string | gRPC 服务包名 |
| .serviceNames.Service | string | 服务名称 |
rpc.tpl
{{.head}}package serverimport ({{if .notStream}}"context"{{end}}{{.imports}})type {{.server}}Server struct {svcCtx *svc.ServiceContext{{.unimplementedServer}}}func New{{.server}}Server(svcCtx *svc.ServiceContext) *{{.server}}Server {return &{{.server}}Server{svcCtx: svcCtx,}}{{.funcs}}
模板注入对象为:
- 不支持分组情况
map[string]any{"head": head,"unimplementedServer": fmt.Sprintf("%s.Unimplemented%sServer", proto.PbPackage,parser.CamelCase(service.Name)),"server": stringx.From(service.Name).ToCamel(),"imports": strings.Join(imports.KeysStr(), pathx.NL),"funcs": strings.Join(funcList, pathx.NL),"notStream": notStream,}
- 支持分组情况
map[string]any{"head": head,"unimplementedServer": fmt.Sprintf("%s.Unimplemented%sServer", proto.PbPackage,parser.CamelCase(service.Name)),"server": stringx.From(service.Name).ToCamel(),"imports": strings.Join(imports.KeysStr(), pathx.NL),"funcs": strings.Join(funcList, pathx.NL),"notStream": notStream,}
| pipeline变量 | 类型 | 描述 |
|---|---|---|
.head | 文本 | 包级别注释或其他代码 |
.notStream | 布尔 | 根据条件是否引入 context 包 |
.imports | 文本 | 其他导入语句 |
.server | 文本 | 定义服务名称的占位符 |
.unimplementedServer | 文本 | 定义未实现服务器的占位符 |
.funcs | 文本 | 生成特定于服务器类型的其他函数/方法的占位符 |
svc.tpl
package svcimport {{.imports}}type ServiceContext struct {Config config.Config}func NewServiceContext(c config.Config) *ServiceContext {return &ServiceContext{Config:c,}}
模板注入对象为:
map[string]any{"imports": fmt.Sprintf(`"%v"`, ctx.GetConfig().Package),}
| pipeline变量 | 类型 | 描述 |
|---|---|---|
.imports | 文本 | 其他导入语句 |