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": ""}
}
模板自定义规则
- 在 goctl 提供的有效数据范围内修改,即不支持外部变量
- 不支持新增模板文件
- 不支持变量修改
总结
本文介绍了如何使用Go-Zero的goctl工具进行自定义模板的实战,并提供了一个具体的案例来演示定制化模板的过程。
如果你需要详细的命令使用详情,可以参考官方文档中的相关内容。模板定制化 | go-zero Documentation
我将继续更新Go-Zero系列文章,如果你对Go语言或者微服务感兴趣,欢迎关注我,也欢迎直接私信我。
gozero&微服务交流群
我将继续更新Go-Zero系列文章,如果你对Go语言或者微服务感兴趣,欢迎关注我,也欢迎直接私信我。
微信:wangzhongyang1993
相关文章:
Go-Zero自定义goctl实战:定制化模板,加速你的微服务开发效率(四)
前言 上一篇文章带你实现了Go-Zero和goctl:解锁微服务开发的神器,快速上手指南,本文将继续深入探讨Go-Zero的强大之处,并介绍如何使用goctl工具实现模板定制化,并根据实际项目业务需求进行模板定制化实现。 通过本文…...
(五)STM32F407 cubemx IIC驱动OLED(1)IIC协议篇
(五)STM32F407 cubemx IIC驱动OLED(1)IIC协议篇 这篇文章主要是个人的学习经验,想分享出来供大家提供思路,如果其中有不足之处请批评指正哈。 废话不多说直接开始主题,本人是基于STM32F407V…...
OpenCV特征匹配总结
1.概述 在深度学习出现之前,图像中的特征匹配方法主要有 2.理论对比 3.代码实现 #include <iostream> #include <opencv2/opencv.hpp>int main(int argc, char** argv) {if(argc ! 3) {std::cerr << "Usage: " << argv[0] <…...
二叉树的四种遍历代码实现
二叉树的遍历大致能分为以下几种 1.前序:根 左 右 2.中序:左 根 右 3.后序:左 右 根 4.层序:从根开始一层一层的向下 如上图访问顺序: 前序:1 2 3 N N N 4 5 N N 6 N N 中序:N 3 N 2 N 1 N 5 N 4 N …...
系统和功能测试:确保软件的功能和易用性
目录 概述 功能测试 LOSED 模型 用例的设计 等价类划分 边界值分析 循环结构测试的综合方法 因果图 决策表 功能图 正交实验设计 易用性测试 内部易用性测试 外部易用性测试 功能性测试 正向功能性测试 负向功能性测试 功能性测试工具 结语 概述 在软件开发…...
关于服务端接口知识的汇总
大家好,今天给大家分享一下之前整理的关于接口知识的汇总,对于测试人员来说,深入了解接口知识能带来诸多显著的好处。 一、为什么要了解接口知识? 接口是系统不同模块之间交互的关键通道。只有充分掌握接口知识,才能…...
树(数据结构)
树的定义 一个根结点,其余结点分为 m 个不相交的集合, 其中每个集合本身又是一棵树,并且称为根的子树。 树的根结点没有前驱,其他结点有且仅有一个前驱。 所有结点可以有0个或多个后继。 基本术语 结点的度 树的度 : 树…...
Spring底层入门(十一)
1、条件装配 在上一篇中,我们介绍了Spring,Spring MVC常见类的自动装配,在源码中可见许多以Conditional...开头的注解: Conditional 注解是Spring 框架提供的一种条件化装配的机制,它可以根据特定的条件来控制 Bean 的…...
优质资料:大型制造企业等级保护安全建设整改依据,系统现状分析,网络安全风险分析
第1章 项目概述 XX 大型制造型企业是国内一家大型从事制造型出口贸易的大型综合企业集团,为了落实国家及集团的信息安全等级保护制度,提高信息系统的安全防护水平,细化各项信息网络安全工作措施,提升网络与信息系统工作的效率&am…...
几种监控工具学习
在Linux上有很多监控工具,比如Zabbix、Prometheus、APM和ELK 监控工具是确保系统稳定运行的关键组件之一,它可以帮助系统管理员和开发人员及时发现并解决问题。 以下是几种流行的监控工具的简要介绍: Zabbix: Zabbix 是一个企…...
树莓派python开发
树莓派自带thonny 点亮LED灯 import RPi.GPIO as GPIO import time# 设置GPIO模式为BCM GPIO.setmode(GPIO.BCM)# 设置LED引脚 led_pin 18# 设置LED引脚为输出 GPIO.setup(led_pin, GPIO.OUT)# 点亮LED GPIO.output(led_pin, GPIO.HIGH)# 延时2秒 time.sleep(2)# 关闭LED GPI…...
纯血鸿蒙APP实战开发——首页下拉进入二楼效果案例
介绍 本示例主要介绍了利用position和onTouch来实现首页下拉进入二楼、二楼上划进入首页的效果场景,利用translate和opacity实现动效的移动和缩放,并将界面沉浸式(全屏)显示。 效果图预览 使用说明 向下滑动首页页面超过触发距…...
苹果cms:开启高速缓存加快访问速度
由于苹果cms采集的影片数据过多,如果不设置缓存,可能会造成网站访问缓慢,或者CPU消耗过高。随着用户访问量的上升,添加缓存设置是有这个必要的。目前cms提供了四种缓存方式 1)file:以文件形式,通俗说直接访问Mysql,要达…...
实时数据推送——长轮询,短轮询,长连接
短轮询 短轮询是最简单的一种数据推送方式,客户端在固定的时间间隔(例如每隔5秒)向服务器发送请求,询问是否有更新的数据。服务器立即处理请求并返回数据,不论数据是否真的已经更新。 长轮询 长轮询是对短轮询的改进…...
七.音视频编辑-创建视频过渡-应用
引言 在上一篇博客中,我们已经介绍了创建视频过渡的实现方案,步骤非常繁琐,在生成AVMutableVideoCompositionInstruction和AVMutableVideoCompositionLayerInstruction的计算也十分复杂,但其实还有一个创建视频组合的捷径。不过我…...
Android11 InputManagerService启动流程分析
InputManagerService在systemserver进程中被启动 //frameworks\base\services\java\com\android\server\SystemServer.java t.traceBegin("StartInputManagerService"); inputManager new InputManagerService(context);//1 t.traceEnd(); //省略 //注册服务 Servi…...
【计算机网络篇】数据链路层(8)共享式以太网的退避算法和信道利用率
文章目录 🛸共享式以太网的退避算法🥚截断二进制指数算法 🍔共享式以太网的信道利用率 🛸共享式以太网的退避算法 在使用CSMA/CD协议的共享总线以太网中,正在发送帧的站点一边发送帧一边检测碰撞,当检测到…...
wordpress主题 7B2 PRO主题5.4.2免授权直接安装
内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 WordPress 资讯、资源、社交、商城、圈子、导航等多功能商用主题:B2 PRO 其设计风格专业且时尚,功能十分强大,包括多栏布局、自定义页面、强大的主…...
Dubbo基本使用
Dubbo基本使用 1.项目介绍2.开发步骤2.1 启动注册中心2.2 初始化项目2.3 添加 Maven 依赖2.3.1 父pom.xml2.3.1 consumer模块和provider模块pom.xml 2.4 定义服务接口2.5 定义服务端的实现2.6 配置服务端 Yaml 配置文件2.7 配置消费端 Yaml 配置文件2.8 基于 Spring 配置服务端…...
JS解密之新js加密实战(二)
前言 上次发了一篇关于新加密的,只解了前边两层,这中间家里各种事情因素影响,没有继续进一步研究,今天百忙之中抽空发布第二篇,关于其中的一小段加密片段,我认为分割成多个小片段是更容易被理解的。逻辑相…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
es6+和css3新增的特性有哪些
一:ECMAScript 新特性(ES6) ES6 (2015) - 革命性更新 1,记住的方法,从一个方法里面用到了哪些技术 1,let /const块级作用域声明2,**默认参数**:函数参数可以设置默认值。3&#x…...
flow_controllers
关键点: 流控制器类型: 同步(Sync):发布操作会阻塞,直到数据被确认发送。异步(Async):发布操作非阻塞,数据发送由后台线程处理。纯同步(PureSync…...
