gozero如何自定義goctl?本文詳解和實(shí)戰(zhàn),通過本文你將了解goctl的妙用,提高你的開發(fā)效率。介紹如何使用goctl工具實(shí)現(xiàn)模板定制化,并根據(jù)實(shí)際項(xiàng)目業(yè)務(wù)需求進(jìn)行模板定制化實(shí)現(xiàn)。
上一篇文章帶你實(shí)現(xiàn)了 Go-Zero和goctl:解鎖微服務(wù)開發(fā)的神器,快速上手指南 ,本文將繼續(xù)深入探討Go-Zero的強(qiáng)大之處,并介紹如何使用goctl工具實(shí)現(xiàn)模板定制化,并根據(jù)實(shí)際項(xiàng)目業(yè)務(wù)需求進(jìn)行模板定制化實(shí)現(xiàn)。
通過本文的教程,你能夠親自實(shí)踐并完成goctl模板的定制化,進(jìn)一步提升你的Go-Zero開發(fā)技能。
goctl 代碼生成是基于 go 的模板去實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)的,默認(rèn)情況會(huì)選擇內(nèi)存中的模板進(jìn)行生成,當(dāng)開發(fā)需要修改模板時(shí),就需要定制化模板,goctl為我們實(shí)現(xiàn)了這一功能。
首先需要你在本地安裝goctl、protoc、go-zero,下載教學(xué)和地址 點(diǎn)擊這里 ,按照教程操作即可,非常簡(jiǎn)單。
下面按順序和我操作吧,對(duì)整體開發(fā)流程不清楚的同學(xué)務(wù)必先看我前篇文章: GoZero的開發(fā)技巧 & 整體開發(fā)流程
本文重在實(shí)戰(zhàn),如果對(duì)goctl毫不了解的話,建議先看我前一篇文章: Go-Zero和goctl:解鎖微服務(wù)開發(fā)的神器,快速上手指南
以下均以我的商業(yè)項(xiàng)目舉例,應(yīng)該對(duì)你有啟發(fā):
(后面我會(huì)把商業(yè)項(xiàng)目脫敏開源出來,歡迎關(guān)注我)
首先在deploy下新增script目錄,結(jié)構(gòu)如下圖所示。
腳本內(nèi)容如下:
#!/usr/bin/env bash
# 使用方法:
# ./genModel.sh lottery lottery
# ./genModel.sh lottery prize
# 再將./genModel下的文件剪切到對(duì)應(yīng)服務(wù)的model目錄里面,記得改package
#生成的表名
tables=$2
#表生成的genmodel目錄
modeldir=./genModel
# 數(shù)據(jù)庫配置
host=127.0.0.1
port=33069
dbname=$1
username=root
passwd=PXDN93VRKUm8TeE7
template=../../goctl/1.6.1
echo "開始創(chuàng)建庫:$dbname 的表:$2"
goctl model mysql datasource -url="${username}:${passwd}@tcp(${host}:${port})/${dbname}" -table="${tables}" -dir="${modeldir}" -cache=true --home="${template}" --style=goZero
相關(guān)命令使用詳情,參考:
官網(wǎng)文檔
具體使用方法網(wǎng)上有很多文章介紹,官網(wǎng)也有詳細(xì)步驟。這里更加注重商業(yè)項(xiàng)目對(duì)于模板定制化的實(shí)戰(zhàn),對(duì)相關(guān)操作不進(jìn)行贅述,快速過一遍流程即可。
依據(jù)前文所介紹的項(xiàng)目目錄結(jié)構(gòu),我們將自定義模板放在deploy下面即可,并且采用的版本號(hào)為1.6.1(目錄路徑根據(jù)自己實(shí)際情況修改)
goctl template init --home $HOME/Desktop/lottery-backend/deploy/goctl/1.6.1
注意:如果不指定–home 他會(huì)初始化到$HOME/.goctl
這樣就生成好自己版本的goctl模板啦,可以根據(jù)自己的實(shí)際需求進(jìn)行模板的修改。
接下來分享我們項(xiàng)目中關(guān)于自定義goctl的實(shí)戰(zhàn)。
很多時(shí)候我們需要對(duì)數(shù)據(jù)進(jìn)行分頁查詢。這個(gè)方法是一個(gè)通用的方法,可以在很多地方復(fù)用,所以放入模板去生成,這樣可以減少重復(fù)代碼,提高開發(fā)效率。
方法具體實(shí)現(xiàn)如下
func (m *default{{.upperStartCamelObject}}Model) FindPageListByPage(ctx context.Context,builder squirrel.SelectBuilder,page ,pageSize int64,orderBy string) ([]*{{.upperStartCamelObject}},error) {
builder = builder.Columns({{.lowerStartCamelObject}}Rows)
if orderBy == ""{
builder = builder.OrderBy("id DESC")
}else{
builder = builder.OrderBy(orderBy)
}
if page < 1{
page = 1
}
offset := (page - 1) * pageSize
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql()
if err != nil {
return nil, err
}
var resp []*{{.upperStartCamelObject}}
{{if .withCache}}err = m.QueryRowsNoCacheCtx(ctx,&resp, query, values...){{else}}
err = m.conn.QueryRowsCtx(ctx,&resp, query, values...)
{{end}}
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
使用GitBash打開deploy/script/mysql目錄,執(zhí)行腳本
此時(shí)genModel目錄下面就會(huì)生成相關(guān)代碼
生成的FindPageListByPage方法
func (m *defaultLotteryModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*Lottery, error) {
builder = builder.Columns(lotteryRows)
if orderBy == "" {
builder = builder.OrderBy("id DESC")
} else {
builder = builder.OrderBy(orderBy)
}
if page < 1 {
page = 1
}
offset := (page - 1) * pageSize
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql()
if err != nil {
return nil, err
}
var resp []*Lottery
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
當(dāng)我們希望自定義統(tǒng)一返回響應(yīng)體以及希望每個(gè)api接口都進(jìn)行參數(shù)校驗(yàn)時(shí),我們可以在模板中修改handler層的代碼,從而實(shí)現(xiàn)這些效果。
在common目錄下新建result目錄和httpResult.go文件,如下圖所示
具體實(shí)現(xiàn)代碼不是本文重點(diǎn),下面是提供的代碼
package result
import (
"fmt"
"net/http"
"looklook/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/rest/httpx"
"google.golang.org/grpc/status"
)
// http返回
func HttpResult(r *http.Request, w http.ResponseWriter, resp interface{}, err error) {
if err == nil {
//成功返回
r := Success(resp)
httpx.WriteJson(w, http.StatusOK, r)
} else {
//錯(cuò)誤返回
errcode := xerr.SERVER_COMMON_ERROR
errmsg := "服務(wù)器開小差啦,稍后再來試一試"
causeErr := errors.Cause(err) // err類型
if e, ok := causeErr.(*xerr.CodeError); ok { //自定義錯(cuò)誤類型
//自定義CodeError
errcode = e.GetErrCode()
errmsg = e.GetErrMsg()
} else {
if gstatus, ok := status.FromError(causeErr); ok { // grpc err錯(cuò)誤
grpcCode := uint32(gstatus.Code())
if xerr.IsCodeErr(grpcCode) { //區(qū)分自定義錯(cuò)誤跟系統(tǒng)底層、db等錯(cuò)誤,底層、db錯(cuò)誤不能返回給前端
errcode = grpcCode
errmsg = gstatus.Message()
}
}
}
logx.WithContext(r.Context()).Errorf("【API-ERR】 : %+v ", err)
httpx.WriteJson(w, http.StatusBadRequest, Error(errcode, errmsg))
}
}
// http 參數(shù)錯(cuò)誤返回
func ParamErrorResult(r *http.Request, w http.ResponseWriter, err error) {
errMsg := fmt.Sprintf("%s ,%s", xerr.MapErrMsg(xerr.REUQEST_PARAM_ERROR), err.Error())
httpx.WriteJson(w, http.StatusBadRequest, Error(xerr.REUQEST_PARAM_ERROR, errMsg))
}
關(guān)于定制validator也不是本文重點(diǎn),感興趣的同學(xué)可以關(guān)注我,留言。
package translator
import (
"errors"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
"looklook/app/lottery/cmd/api/internal/logic/lottery"
"looklook/app/lottery/cmd/api/internal/types"
"reflect"
"strings"
)
func Validate(dataStruct interface{}) error {
zh_ch := zh.New()
validate := validator.New()
// 注冊(cè)一個(gè)函數(shù),獲取struct tag里自定義的label作為字段名
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
// 在這里注冊(cè)自定義結(jié)構(gòu)體/字段校驗(yàn)方法
// 注冊(cè)自定義結(jié)構(gòu)體校驗(yàn)方法
validate.RegisterStructValidation(lottery.SignUpParamStructLevelValidation, types.TestReq{})
// 注冊(cè)自定義結(jié)構(gòu)體字段校驗(yàn)方法
if err := validate.RegisterValidation("checkDate", lottery.CheckDate); err != nil {
return err
}
uni := ut.New(zh_ch)
trans, _ := uni.GetTranslator("zh")
// 在這里注冊(cè)自定義tag翻譯
// 注意!因?yàn)檫@里會(huì)使用到trans實(shí)例
// 所以這一步注冊(cè)要放到trans初始化的后面
if err := validate.RegisterTranslation(
"checkDate",
trans,
registerTranslator("checkDate", "{0}必須要晚于當(dāng)前日期"),
translate,
); err != nil {
return err
}
// 驗(yàn)證器注冊(cè)翻譯器
zh_translations.RegisterDefaultTranslations(validate, trans)
err := validate.Struct(dataStruct)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
return errors.New(err.Translate(trans))
}
}
return nil
}
// registerTranslator 為自定義字段添加翻譯功能
func registerTranslator(tag string, msg string) validator.RegisterTranslationsFunc {
return func(trans ut.Translator) error {
if err := trans.Add(tag, msg, false); err != nil {
return err
}
return nil
}
}
// translate 自定義字段的翻譯方法
func translate(trans ut.Translator, fe validator.FieldError) string {
msg, err := trans.T(fe.Tag(), fe.Field())
if err != nil {
panic(fe.(error).Error())
}
return msg
}
將模板替換為以下內(nèi)容
package {{.PkgName}}
import (
"net/http"
"looklook/common/result"
"github.com/zeromicro/go-zero/rest/httpx"
{{.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.ErrorCtx(r.Context(), w, err)
return
}
validateErr := translator.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(r, w, validateErr)
return
}
{{end}}l := {{.LogicName}}.New{{.LogicType}}(r.Context(), svcCtx)
{{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}&req{{end}})
result.HttpResult(r, w, {{if .HasResp}}resp{{else}}nil{{end}}, err)
}
}
注意生成handler后需要手動(dòng)點(diǎn)開生成的 handler文件,導(dǎo)入translator包 ,否則服務(wù)會(huì)報(bào)錯(cuò)。!
# 使用自定義的goctl 生成api
goctl api go -api main.api -dir ../ --style=goZero --home=../../../../../deploy/goctl/1.6.1
{
"code": 200,
"msg": "OK",
"data": {
"message": ""
}
}
本文介紹了如何使用Go-Zero的goctl工具進(jìn)行自定義模板的實(shí)戰(zhàn),并提供了一個(gè)具體的案例來演示定制化模板的過程。
如果你需要詳細(xì)的命令使用詳情,可以參考官方文檔中的相關(guān)內(nèi)容。模板定制化 | go-zero Documentation
我將繼續(xù)更新Go-Zero系列文章,如果你對(duì)Go語言或者微服務(wù)感興趣,歡迎關(guān)注我,也歡迎直接私信我。
我將繼續(xù)更新Go-Zero系列文章, 如果你對(duì)Go語言或者微服務(wù)感興趣,歡迎關(guān)注我,也歡迎直接私信我。
微信:wangzhongyang1993
機(jī)器學(xué)習(xí):神經(jīng)網(wǎng)絡(luò)構(gòu)建(下)
閱讀華為Mate品牌盛典:HarmonyOS NEXT加持下游戲性能得到充分釋放
閱讀實(shí)現(xiàn)對(duì)象集合與DataTable的相互轉(zhuǎn)換
閱讀鴻蒙NEXT元服務(wù):論如何免費(fèi)快速上架作品
閱讀算法與數(shù)據(jù)結(jié)構(gòu) 1 - 模擬
閱讀5. Spring Cloud OpenFeign 聲明式 WebService 客戶端的超詳細(xì)使用
閱讀Java代理模式:靜態(tài)代理和動(dòng)態(tài)代理的對(duì)比分析
閱讀Win11筆記本“自動(dòng)管理應(yīng)用的顏色”顯示規(guī)則
閱讀本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權(quán),請(qǐng)發(fā)郵件[email protected]
湘ICP備2022002427號(hào)-10 湘公網(wǎng)安備:43070202000427號(hào)© 2013~2025 haote.com 好特網(wǎng)