当前位置: 首页 > news >正文

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

相关文章:

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

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

(五)STM32F407 cubemx IIC驱动OLED(1)IIC协议篇

&#xff08;五&#xff09;STM32F407 cubemx IIC驱动OLED&#xff08;1&#xff09;IIC协议篇 这篇文章主要是个人的学习经验&#xff0c;想分享出来供大家提供思路&#xff0c;如果其中有不足之处请批评指正哈。   废话不多说直接开始主题&#xff0c;本人是基于STM32F407V…...

OpenCV特征匹配总结

1.概述 在深度学习出现之前&#xff0c;图像中的特征匹配方法主要有 2.理论对比 3.代码实现 #include <iostream> #include <opencv2/opencv.hpp>int main(int argc, char** argv) {if(argc ! 3) {std::cerr << "Usage: " << argv[0] <…...

二叉树的四种遍历代码实现

二叉树的遍历大致能分为以下几种 1.前序&#xff1a;根 左 右 2.中序&#xff1a;左 根 右 3.后序&#xff1a;左 右 根 4.层序&#xff1a;从根开始一层一层的向下 如上图访问顺序: 前序&#xff1a;1 2 3 N N N 4 5 N N 6 N N 中序&#xff1a;N 3 N 2 N 1 N 5 N 4 N …...

系统和功能测试:确保软件的功能和易用性

目录 概述 功能测试 LOSED 模型 用例的设计 等价类划分 边界值分析 循环结构测试的综合方法 因果图 决策表 功能图 正交实验设计 易用性测试 内部易用性测试 外部易用性测试 功能性测试 正向功能性测试 负向功能性测试 功能性测试工具 结语 概述 在软件开发…...

关于服务端接口知识的汇总

大家好&#xff0c;今天给大家分享一下之前整理的关于接口知识的汇总&#xff0c;对于测试人员来说&#xff0c;深入了解接口知识能带来诸多显著的好处。 一、为什么要了解接口知识&#xff1f; 接口是系统不同模块之间交互的关键通道。只有充分掌握接口知识&#xff0c;才能…...

树(数据结构)

树的定义 一个根结点&#xff0c;其余结点分为 m 个不相交的集合&#xff0c; 其中每个集合本身又是一棵树&#xff0c;并且称为根的子树。 树的根结点没有前驱&#xff0c;其他结点有且仅有一个前驱。 所有结点可以有0个或多个后继。 基本术语 结点的度 树的度 &#xff1a; 树…...

Spring底层入门(十一)

1、条件装配 在上一篇中&#xff0c;我们介绍了Spring&#xff0c;Spring MVC常见类的自动装配&#xff0c;在源码中可见许多以Conditional...开头的注解&#xff1a; Conditional 注解是Spring 框架提供的一种条件化装配的机制&#xff0c;它可以根据特定的条件来控制 Bean 的…...

优质资料:大型制造企业等级保护安全建设整改依据,系统现状分析,网络安全风险分析

第1章 项目概述 XX 大型制造型企业是国内一家大型从事制造型出口贸易的大型综合企业集团&#xff0c;为了落实国家及集团的信息安全等级保护制度&#xff0c;提高信息系统的安全防护水平&#xff0c;细化各项信息网络安全工作措施&#xff0c;提升网络与信息系统工作的效率&am…...

几种监控工具学习

在Linux上有很多监控工具&#xff0c;比如Zabbix、Prometheus、APM和ELK 监控工具是确保系统稳定运行的关键组件之一&#xff0c;它可以帮助系统管理员和开发人员及时发现并解决问题。 以下是几种流行的监控工具的简要介绍&#xff1a; Zabbix&#xff1a; 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来实现首页下拉进入二楼、二楼上划进入首页的效果场景&#xff0c;利用translate和opacity实现动效的移动和缩放&#xff0c;并将界面沉浸式&#xff08;全屏&#xff09;显示。 效果图预览 使用说明 向下滑动首页页面超过触发距…...

苹果cms:开启高速缓存加快访问速度

由于苹果cms采集的影片数据过多,如果不设置缓存,可能会造成网站访问缓慢,或者CPU消耗过高。随着用户访问量的上升&#xff0c;添加缓存设置是有这个必要的。目前cms提供了四种缓存方式 1&#xff09;file&#xff1a;以文件形式&#xff0c;通俗说直接访问Mysql&#xff0c;要达…...

实时数据推送——长轮询,短轮询,长连接

短轮询 短轮询是最简单的一种数据推送方式&#xff0c;客户端在固定的时间间隔&#xff08;例如每隔5秒&#xff09;向服务器发送请求&#xff0c;询问是否有更新的数据。服务器立即处理请求并返回数据&#xff0c;不论数据是否真的已经更新。 长轮询 长轮询是对短轮询的改进…...

七.音视频编辑-创建视频过渡-应用

引言 在上一篇博客中&#xff0c;我们已经介绍了创建视频过渡的实现方案&#xff0c;步骤非常繁琐&#xff0c;在生成AVMutableVideoCompositionInstruction和AVMutableVideoCompositionLayerInstruction的计算也十分复杂&#xff0c;但其实还有一个创建视频组合的捷径。不过我…...

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)共享式以太网的退避算法和信道利用率

文章目录 &#x1f6f8;共享式以太网的退避算法&#x1f95a;截断二进制指数算法 &#x1f354;共享式以太网的信道利用率 &#x1f6f8;共享式以太网的退避算法 在使用CSMA/CD协议的共享总线以太网中&#xff0c;正在发送帧的站点一边发送帧一边检测碰撞&#xff0c;当检测到…...

wordpress主题 7B2 PRO主题5.4.2免授权直接安装

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 WordPress 资讯、资源、社交、商城、圈子、导航等多功能商用主题&#xff1a;B2 PRO 其设计风格专业且时尚&#xff0c;功能十分强大&#xff0c;包括多栏布局、自定义页面、强大的主…...

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加密实战(二)

前言 上次发了一篇关于新加密的&#xff0c;只解了前边两层&#xff0c;这中间家里各种事情因素影响&#xff0c;没有继续进一步研究&#xff0c;今天百忙之中抽空发布第二篇&#xff0c;关于其中的一小段加密片段&#xff0c;我认为分割成多个小片段是更容易被理解的。逻辑相…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

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…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

面试高频问题

文章目录 &#x1f680; 消息队列核心技术揭秘&#xff1a;从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"&#xff1f;性能背后的秘密1.1 顺序写入与零拷贝&#xff1a;性能的双引擎1.2 分区并行&#xff1a;数据的"八车道高速公路"1.3 页缓存与批量处理…...