模板(Template)是数据驱动
生成的基础,所有的代码(rest api、rpc、model、docker、kube)生成都会依赖模板
, 默认情况下,模板生成器会选择内存中的模板进行生成,而对于有模板修改需求的开发者来讲,则需要将模板进行落盘, 从而进行模板修改,在下次代码生成时会加载指定路径下的模板进行生成
。
命令使用详情,参考官网文档
goctl template init --home $HOME/template
注意:如果不指定–home 他会初始化到$HOME/.goctl
实现统一格式的 body 响应,格式如下:
{
"code": 0,
"msg": "OK",
"data": {} // ①
}
实际响应数据,go-zero生成的代码没有对其进行处理
我们提前在 module 为 greet 的工程下的 response 包中写一个 Response 方法,目录树类似如下
greet
├── response
│ └── response.go
└── xxx...
代码如下
package response
import (
"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 Body
if err != nil {
body.Code = -1
body.Msg = err.Error()
} else {
body.Msg = "OK"
body.Data = resp
}
httpx.OkJson(w, body)
}
vim $HOME/template/${goctl版本号}/api/handler.tpl
将模板替换为以下内容
package handler
import (
"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 := logic.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包名,仅供参考
② 自定义模板内容
goctl api go --api *.api --dir . --home $HOME/template/${goctl版本号}
–api是被指定生成的api文件;–dir是生成代码的目录;–home就是生成代码指定的模板
修改前
func GreetHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.Request
if 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.Request
if 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!"
}
}
为数据库添加多一个根据ID进行软删除操作,DeleteSoft,内容大概如下
func (m *defaultUserModel) DeleteSoft(ctx context.Context, id int) error {
//delState = globalkey.DelStateYes
//deleteTime = time.Now()
//进行软删除更新-伪代码
updateCount,err := 1,nil
if err != nil {
return err
}
if updateCount == 0 {
return ErrNoRowsUpdate
}
return nil
}
我们提前在 module 为 greet 的工程下的 globalkey 包中定义好需要使用的方法或者常量,目录树类似如下
greet
├── globalkey
│ └── globalkey.go
└── xxx...
代码如下
package globalkey
// 软删除
var DelStateNo int64 = 0 //未删除
var DelStateYes int64 = 1 //已删除
这里我只用到了常量
//自定义错误
vim $HOME/template/${goctl版本号}/model/err.tpl
//不使用缓存改import-no-cache.tpl,使用缓存改import.tpl
//引入使用到的第三方包
vim $HOME/template/${goctl版本号}/model/import-no-cache.tpl
//定义接口,写在哪个文件(查询、插入、删除、更新)就对应interface-*.tpl
vim $HOME/template/${goctl版本号}/model/interface-update.tpl
//实现接口
vim $HOME/template/${goctl版本号}/model/update.tpl
将模板替换为以下内容
自定义的错误: H O M E / t e m p l a t e / HOME/template/ HOME/template/{goctl版本号}/model/err.tpl
package {{.pkg}}
import (
"errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var ErrNotFound = sqlx.ErrNotFound
var ErrNoRowsUpdate = errors.New("update db no rows change")//定义的错误
引入的包: H O M E / t e m p l a t e / HOME/template/ HOME/template/{goctl版本号}/model/import-no-cache.tpl(使用缓存是import.tpl)
import (
"context"
"database/sql"
"fmt"
"strings"
{{if .time}}"time"{{end}}
{{if .containsPQ}}"github.com/lib/pq"{{end}}
"github.com/Masterminds/squirrel" //引入的包
"github.com/pkg/errors" //引入的包
"github.com/zeromicro/go-zero/core/stores/builder"
"github.com/zeromicro/go-zero/core/stores/sqlc"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/core/stringx"
)
定义想添加的接口: H O M E / t e m p l a t e / HOME/template/ HOME/template/{goctl版本号}/model/interface-update.tpl
Update(ctx context.Context, {{if .containsIndexCache}}newData{{else}}data{{end}} *{{.upperStartCamelObject}}) error
DeleteSoft(ctx context.Context, id int) error //添加的接口
实现想添加的接口: H O M E / t e m p l a t e / HOME/template/ HOME/template/{goctl版本号}/model/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
}
//接口实现
func (m *default{{.upperStartCamelObject}}Model) DeleteSoft(ctx context.Context, id int) error {
//自定义的常量
//delState = globalkey.DelStateYes
//deleteTime = time.Now()
//进行软删除更新-伪代码
updateCount,err := 1,nil
if err != nil {
return err
}
if updateCount == 0 {
//自定义的错误
return ErrNoRowsUpdate
}
return nil
}
这里使用链接生成,也可以使用SQL文件生成
goctl model mysql datasource -url=* -table=* -dir=. --home $HOME/template/${goctl版本号}
–url是数据库连接;–table是要生成的表名;–dir是生成代码的目录;–home就是生成代码指定的模板
参考视频https://www.bilibili.com/video/BV1NY411w7w7/?spm_id_from=333.788&vd_source=10332ffe931de86faa42900544751c8c
参考文章https://go-zero.dev/docs/tutorials/customization/template