Go-Zero自定义goctl实战:定制化模板,加速你的微服务开发效率(四)

前言

上一篇文章带你实现了Go-Zero和goctl:解锁微服务开发的神器,快速上手指南,本文将继续深入探讨Go-Zero的强大之处,并介绍如何使用goctl工具实现模板定制化,并根据实际项目业务需求进行模板定制化实现。

通过本文的教程,你能够亲自实践并完成goctl模板的定制化,进一步提升你的Go-Zero开发技能。

概述

goctl 代码生成是基于 go 的模板去实现数据驱动的,默认情况会选择内存中的模板进行生成,当开发需要修改模板时,就需要定制化模板,goctl为我们实现了这一功能。

实战前准备

首先需要你在本地安装goctl、protoc、go-zero,下载教学和地址点击这里,按照教程操作即可,非常简单。

下面按顺序和我操作吧,对整体开发流程不清楚的同学务必先看我前篇文章:GoZero的开发技巧 & 整体开发流程

本文重在实战,如果对goctl毫不了解的话,建议先看我前一篇文章:Go-Zero和goctl:解锁微服务开发的神器,快速上手指南

以下均以我的商业项目举例,应该对你有启发:

(后面我会把商业项目脱敏开源出来,欢迎关注我)

数据表生成Model方法脚本

首先在deploy下新增script目录,结构如下图所示。

脚本内容如下:

#!/usr/bin/env bash# 使用方法:
# ./genModel.sh lottery lottery
# ./genModel.sh lottery prize
# 再将./genModel下的文件剪切到对应服务的model目录里面,记得改package#生成的表名
tables=$2
#表生成的genmodel目录
modeldir=./genModel# 数据库配置
host=127.0.0.1
port=33069
dbname=$1
username=root
passwd=PXDN93VRKUm8TeE7
template=../../goctl/1.6.1echo "开始创建库:$dbname 的表:$2"
goctl model mysql datasource -url="${username}:${passwd}@tcp(${host}:${port})/${dbname}" -table="${tables}" -dir="${modeldir}" -cache=true --home="${template}" --style=goZero

模板定制化使用方法

相关命令使用详情,参考:官网文档
具体使用方法网上有很多文章介绍,官网也有详细步骤。这里更加注重商业项目对于模板定制化的实战,对相关操作不进行赘述,快速过一遍流程即可。

初始化模板到本地

依据前文所介绍的项目目录结构,我们将自定义模板放在deploy下面即可,并且采用的版本号为1.6.1(目录路径根据自己实际情况修改)

goctl template init --home $HOME/Desktop/lottery-backend/deploy/goctl/1.6.1

注意:如果不指定–home 他会初始化到$HOME/.goctl

这样就生成好自己版本的goctl模板啦,可以根据自己的实际需求进行模板的修改。

接下来分享我们项目中关于自定义goctl的实战。

自定义goctl实战

实战1:Model层方法定制化

很多时候我们需要对数据进行分页查询。这个方法是一个通用的方法,可以在很多地方复用,所以放入模板去生成,这样可以减少重复代码,提高开发效率。

步骤一:在model/update.tpl下面新增一个方法FindPageListByPage

方法具体实现如下

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) * pageSizequery, 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, nildefault:return nil, err}
}

步骤二:使用之前做好的脚本生成代码

使用GitBash打开deploy/script/mysql目录,执行脚本

此时genModel目录下面就会生成相关代码

步骤三:将生成的代码剪切到项目目录的对应位置

效果

默认模板生成的Model层方法

自定义模板生成的Model层方法

生成的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) * pageSizequery, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql()if err != nil {return nil, err}var resp []*Lotteryerr = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)switch err {case nil:return resp, nildefault:return nil, err}
}

实战2:api自定义响应返回以及集成validator库校验参数

当我们希望自定义统一返回响应体以及希望每个api接口都进行参数校验时,我们可以在模板中修改handler层的代码,从而实现这些效果。

步骤一:实现自定义统一返回响应

在common目录下新建result目录和httpResult.go文件,如下图所示

具体实现代码不是本文重点,下面是提供的代码

package resultimport ("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 {//错误返回errcode := xerr.SERVER_COMMON_ERRORerrmsg := "服务器开小差啦,稍后再来试一试"causeErr := errors.Cause(err)                // err类型if e, ok := causeErr.(*xerr.CodeError); ok { //自定义错误类型//自定义CodeErrorerrcode = e.GetErrCode()errmsg = e.GetErrMsg()} else {if gstatus, ok := status.FromError(causeErr); ok { // grpc err错误grpcCode := uint32(gstatus.Code())if xerr.IsCodeErr(grpcCode) { //区分自定义错误跟系统底层、db等错误,底层、db错误不能返回给前端errcode = grpcCodeerrmsg = gstatus.Message()}}}logx.WithContext(r.Context()).Errorf("【API-ERR】 : %+v ", err)httpx.WriteJson(w, http.StatusBadRequest, Error(errcode, errmsg))}
}// http 参数错误返回
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))
}

步骤二:在handler下面引入定制的validator包

关于定制validator也不是本文重点,感兴趣的同学可以关注我,留言。

package translatorimport ("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()// 注册一个函数,获取struct tag里自定义的label作为字段名validate.RegisterTagNameFunc(func(fld reflect.StructField) string {name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]if name == "-" {return ""}return name})// 在这里注册自定义结构体/字段校验方法// 注册自定义结构体校验方法validate.RegisterStructValidation(lottery.SignUpParamStructLevelValidation, types.TestReq{})// 注册自定义结构体字段校验方法if err := validate.RegisterValidation("checkDate", lottery.CheckDate); err != nil {return err}uni := ut.New(zh_ch)trans, _ := uni.GetTranslator("zh")// 在这里注册自定义tag翻译// 注意!因为这里会使用到trans实例// 所以这一步注册要放到trans初始化的后面if err := validate.RegisterTranslation("checkDate",trans,registerTranslator("checkDate", "{0}必须要晚于当前日期"),translate,); err != nil {return err}// 验证器注册翻译器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
}

步骤三:修改handler.tpl模板代码

将模板替换为以下内容

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后需要手动点开生成的handler文件,导入translator包,否则服务会报错!!!

# 使用自定义的goctl 生成api
goctl api go -api main.api -dir ../  --style=goZero --home=../../../../../deploy/goctl/1.6.1

修改后的响应体

{"code": 200,"msg": "OK","data": {"message": ""}
}

模板自定义规则

  1. 在 goctl 提供的有效数据范围内修改,即不支持外部变量
  2. 不支持新增模板文件
  3. 不支持变量修改

总结

本文介绍了如何使用Go-Zero的goctl工具进行自定义模板的实战,并提供了一个具体的案例来演示定制化模板的过程。

如果你需要详细的命令使用详情,可以参考官方文档中的相关内容。模板定制化 | go-zero Documentation

我将继续更新Go-Zero系列文章,如果你对Go语言或者微服务感兴趣,欢迎关注我,也欢迎直接私信我。

gozero&微服务交流群

我将继续更新Go-Zero系列文章,如果你对Go语言或者微服务感兴趣,欢迎关注我,也欢迎直接私信我。

微信:wangzhongyang1993

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/9403.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

videosapi开发微信管理系统

获取登录二维码&#xff1a; export interface Request {/*** 设备ID&#xff0c;首次登录传空&#xff0c;之后传接口返回的appId*/appId?: string;/*** 代理IP 格式&#xff1a;socks5://username:password123.2.2.2*/proxyIp?: string;/*** 地区*/regionId: string;[prop…

宋仕强论道之新质生产力

宋仕强论道之新质生产力&#xff0c;宋仕强说当前5G通信、人工智能、万物互联、工业互联网、数字经济、新能源技术和产业等领域正蓬勃发展&#xff0c;成为未来经济增长的重要推动力&#xff0c;也是目前提倡的新质生产力的重要组成部分。而这些领域的发展都离不开数据的采集、…

React使用Outlet实现路由跳转时局部刷新页面

Outlet是react-router-dom插件的一个组件&#xff0c;首先需要安装react-router-dom插件&#xff1a; cnpm i react-router-dom --save 官方文档 应该在父路由元素中用来渲染其子路由元素。这允许在渲染子路由时显示嵌套的 UI。如果父路由完全匹配&#xff0c;则将渲染子索引…

Flutter 引入webview_windows插件,在已经使用$PATH 中的 nuget.exe情况下,windows端构建失败

报错 PS F:\xx\xxxx> flutter run -d windows Flutter assets will be downloaded from https://storage.flutter-io.cn. Make sure you trust this source! Launching lib\main.dart on Windows in debug mode... E:\Some software\Visual Studio\VS 2022\MSBuild\M…

基于R语言绘图 | 转录代谢趋势图绘制教程

原文链接&#xff1a;基于R语言绘图 | 转录代谢趋势图绘制教程 本期教程 小杜的生信笔记&#xff0c;自2021年11月开始做的知识分享&#xff0c;主要内容是R语言绘图教程、转录组上游分析、转录组下游分析等内容。凡事在社群同学&#xff0c;可免费获得自2021年11月份至今全部…

【深度学习】【Lora训练0】StabelDiffusion,Lora训练,kohya_ss训练

文章目录 环境数据自动标注kohya_ss BLIP2kohya_ss WD14 后续 资源&#xff1a; &#xff08;1&#xff09;训练ui kohya_ss&#xff1a; https://github.com/bmaltais/kohya_ss &#xff08;2&#xff09;kohya_ss 的docker 其他docker https://github.com/ashleykleynhans…

04-19 周五 GitHub actions-runner 程序解释

04-19 周五 GitHub actions-runner 程序解释 时间版本修改人描述2024年4月19日17:26:17V0.1宋全恒新建文档 简介 本文主要描述了actions-runner-linux-x64-2.315.0.tar.gz这个github actions CI所需要的客户端安装包的重要文件和内容信息。有关GitHub actions 的配置&#xff…

##12 深入了解正则化与超参数调优:提升神经网络性能的关键策略

文章目录 前言1. 正则化技术的重要性1.1 L1和L2正则化1.2 Dropout1.3 批量归一化 2. 超参数调优技术2.1 网格搜索2.2 随机搜索2.3 贝叶斯优化 3. 实践案例3.1 设置实验3.2 训练和测试 4. 结论 前言 在深度学习中&#xff0c;构建一个高性能的模型不仅需要一个好的架构&#xf…

JavaScript 进阶征途:解锁Function奥秘,深掘Object方法精髓

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 &#x1f235;Function方法 与 函数式编程&#x1f49d;1 call &#x1f49d…

HTML4(四)

1. 框架标签 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>框架标签</title></head><body><!-- 利用iframe嵌入一个普通网页 --><iframe src"https://www.toutia…

Reactor Netty HTTP 服务器端-响应式编程-014

🤗 ApiHug {Postman|Swagger|Api...} = 快↑ 准√ 省↓ GitHub - apihug/apihug.com: All abou the Apihug apihug.com: 有爱,有温度,有质量,有信任ApiHug - API design Copilot - IntelliJ IDEs Plugin | Marketplace The Next Generation API Development Platform …

Python | Leetcode Python题解之第80题删除有序数组中的重复项II

题目&#xff1a; 题解&#xff1a; class Solution:def removeDuplicates(self, nums: List[int]) -> int:idx, left, right 0, 0, 0while left < len(nums):nums[idx] nums[left]idx 1while right < len(nums) and nums[right] nums[left]:right 1if right - …

clickhouse学习笔记06

ClickHouse的建表和引擎选择思路讲解 ClickHouse的常见注意事项和异常问题排查 ClickHouse高性能查询原因剖析-稀疏索引 ClickHouse高性能写入剖析-LSM-Tree存储结构

docker自建GitLab仓库

摘要 GitLab 是一个功能强大的开源代码托管平台&#xff0c;它不仅提供了代码存储和版本控制的核心功能&#xff0c;还集成了项目管理、CI/CD 流水线、代码审查等企业级特性。本文将指导你如何在自己的服务器上搭建 GitLab 社区版&#xff0c;创建一个完全属于自己的开源仓库&…

绝地求生:你觉得复活系统还需要哪些改进?

大好&#xff0c;我闲游盒&#xff01; 在28.2版本更新改动中&#xff0c;在维寒迪和泰戈中的复活赛已经替换成通过蓝色晶片复活系统去复活&#xff0c;原本的复活赛将被移除&#xff0c;而且在2024年的工作介绍中曾提到&#xff0c;将计划在所有88的地图中引入蓝色晶片复活系统…

算法学习010-打家劫舍 c++动态规划算法实现 中小学算法思维学习 信奥算法解析

目录 C打家劫舍 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、运行结果 五、考点分析 六、推荐资料 C打家劫舍 一、题目要求 1、编程实现 你是⼀个专业的⼩偷&#xff0c;计划偷窃沿街的商铺 。每间商铺 都藏有⼀定的现⾦&#xff0c;影响你…

谷歌继续将生成式人工智能融入网络安全

谷歌正在将多个威胁情报流与 Gemini 生成人工智能模型相结合&#xff0c;以创建新的云服务。 Google 威胁情报服务旨在帮助安全团队快速准确地整理大量数据&#xff0c;以便更好地保护组织免受网络攻击。 本周在旧金山举行的 RSA 会议上推出的 Google 威胁情报服务吸收了 Mand…

Go 语言基础之常用包【flag、time、strconv、io】

1、命令行参数包 flag flag 包就是一个用来解析命令行参数的工具。 1.1、os.Args import ("fmt""os" )func main() {if len(os.Args) > 0 {for index, arg : range os.Args {fmt.Printf("args[%d]%v\n", index, arg)}} } 运行结果&#…

并行执行线程资源管理方式——《OceanBase 并行执行》系列 3

在某些特定场景下&#xff0c;由于需要等待线程资源&#xff0c;并行查询会遇到排队等待的情况。本篇博客将介绍如何管理并行执行线程资源&#xff0c;以解决这种问题。 《OceanBase并行执行》系列的内容分为七篇博客&#xff0c;本篇是其中的第三篇。前2篇如下&#xff1a; 一…

基于BP神经网络的16QAM解调算法matlab性能仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ........................................................... % 第一部分&#xff1a;加载并…