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

go-zero框架基本配置和错误码封装

文章目录

  • 加载配置信息
    • 配置 env
    • 加载.env文件
    • 配置servicecontext
  • 查询数据
    • 生成model文件
    • 执行查询操作
  • 错误码封装
    • 配置拦截器
    • 错误码封装

接上一篇:《go-zero框架快速入门》

加载配置信息

配置 env

在项目根目录下新增 .env 文件,可以配置当前读取哪个环境的配置信息。内容如下:

# 基础环境配置
#开发环境 dev
#测试环境 test
#预发环境 pre
#生产环境 prod
GO_ENV=dev

新增gozero/etc/gozero-api-dev.yaml文件,配置数据库等相关信息:

Name: gozero-api
Host: 0.0.0.0
Port: 8888
MaxConns: 50
Timeout: 20000
Mysql:DataSource: root:123456@tcp(127.0.0.01:3306)/go-demo-2025?charset=utf8mb4&parseTime=True&loc=Local
cache_config: &cache_configHost: 127.0.0.1:6379Pass: ""Type: node
Cache:- <<: *cache_config

同时可以新增如下配置文件,具体要在当前项目中运行哪个配置文件,修改.env为对应的环境变量即可。

gozero/etc/gozero-api-test.yaml
gozero/etc/gozero-api-pre.yaml
gozero/etc/gozero-api-prod.yaml

加载.env文件

上面只是配置了不同的env,还需要有一个方法来加载当前设定的env。代码路径:gozero/internal/config/config.go

func GetConfigFile() string {// 加载 .env 文件if err := godotenv.Load(); err != nil {logx.Errorf("Error loading .env file: %v", err)}env := os.Getenv("GO_ENV")logx.Infof("env=: %s", env)if env == "" {env = "dev" // 默认开发环境}return filepath.Join("etc", fmt.Sprintf("gozero-api-%s.yaml", env))
}

同时,把数据库相关的信息加载到Config中:

type Config struct {rest.RestConfMysql struct {DataSource string}Cache cache.CacheConf
}

最后,在入口文件 gozero.go中加载配置项:

func main() {flag.Parse()var c config.Config//调用自定义的GetConfigFile方法,读取当前配置的env信息configFile := config.GetConfigFile()conf.MustLoad(configFile, &c)//....
}

配置servicecontext

在 Go-zero 中,servicecontext是服务上下文的依赖注入,所有的配置项和数据库连接、以及业务逻辑所需的模型实例,都被集中管理在 servicecontext 中。可以把它理解为一根长长的线,这根线上存储了整个项目所需的各类资源。如此一来,每个层只需依赖这个上下文,而不需要直接处理底层的配置和初始化逻辑。

这里,我们先简单的配置全局Config和数据库model以及日志等上下文信息。代码路径:gozero/internal/svc/servicecontext.go

type ServiceContext struct {Config config.ConfigModel  *query.Query
}func NewServiceContext(c config.Config) *ServiceContext {newLogger := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), // io writerlogger.Config{SlowThreshold: 10 * time.Second, // 慢SQL阈值,默认10秒钟LogLevel:      logger.Silent,    // 日志级别 Silent:静默级别,Error:错误级别,Warn:警告级别,Info:信息级别Colorful:      true,             // 是否彩色打印},)db, _ := gorm.Open(mysql.Open(c.Mysql.DataSource), &gorm.Config{Logger: newLogger,NamingStrategy: schema.NamingStrategy{TablePrefix: "", // 表名前缀SingularTable: true, // 使用单数表名,启用该选项,会区分 user 和 users 表为两个不同的数据表},})return &ServiceContext{Config: c,Model:  query.Use(db),}
}

查询数据

生成model文件

GEN 自动生成 GORM 模型结构体文件及使用示例,新增gozero/script/gorm_generate_db_struct.go 文件:

package mainimport ("github.com/zeromicro/go-zero/core/conf""go-demo-2025/gozero/internal/config""gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/schema""strings""gorm.io/gen"
)// GEN 自动生成 GORM 模型结构体文件及使用示例
// 安装: go get -u gorm.io/gen@v0.3.16
// 更多参考: https://gorm.io/zh_CN/gen | https://gorm.io/gen/database_to_structs.html
// 更多参考: https://segmentfault.com/a/1190000042502370
func main() {// 初始化go-zero的配置var c config.ConfigconfigFile := config.GetConfigFile() //调用自定义的GetConfigFile方法,读取当前配置的env信息conf.MustLoad(configFile, &c)// 连接数据库db, _ := gorm.Open(mysql.Open(c.Mysql.DataSource), &gorm.Config{NamingStrategy: schema.NamingStrategy{TablePrefix:   "",   // 表名前缀SingularTable: true, // 使用单数表名,启用该选项,会区分 user 和 users 表为两个不同的数据表},})// 生成实例g := gen.NewGenerator(gen.Config{// 生成的model文件的路径OutPath: "./internal/model/dao/query",// WithDefaultQuery 生成默认查询结构体(作为全局变量使用), 即`Q`结构体和其字段(各表模型)// WithoutContext 生成没有context调用限制的代码供查询// WithQueryInterface 生成interface形式的查询代码(可导出), 如`Where()`方法返回的就是一个可导出的接口类型Mode: gen.WithDefaultQuery | gen.WithQueryInterface,// 表字段可为 null 值时, 对应结体字段使用指针类型//FieldNullable: true, // generate pointer when field is nullable// 表字段默认值与模型结构体字段零值不一致的字段, 在插入数据时需要赋值该字段值为零值的, 结构体字段须是指针类型才能成功, 即`FieldCoverable:true`配置下生成的结构体字段.// 因为在插入时遇到字段为零值的会被GORM赋予默认值. 如字段`age`表默认值为10, 即使你显式设置为0最后也会被GORM设为10提交.// 如果该字段没有上面提到的插入时赋零值的特殊需要, 则字段为非指针类型使用起来会比较方便.FieldCoverable: false, // generate pointer when field has default value, to fix problem zero value cannot be assign: https://gorm.io/docs/create.html#Default-Values// 模型结构体字段的数字类型的符号表示是否与表字段的一致, `false`指示都用有符号类型FieldSignable: false, // detect integer field's unsigned type, adjust generated data type// 生成 gorm 标签的字段索引属性FieldWithIndexTag: false, // generate with gorm index tag// 生成 gorm 标签的字段类型属性FieldWithTypeTag: true, // generate with gorm column type tag})// 设置目标 dbg.UseDB(db)// 自定义字段的数据类型// 统一数字类型为int64,兼容protobufdataMap := map[string]func(columnType gorm.ColumnType) (dataType string){"tinyint":   func(columnType gorm.ColumnType) (dataType string) { return "int64" },"smallint":  func(columnType gorm.ColumnType) (dataType string) { return "int64" },"mediumint": func(columnType gorm.ColumnType) (dataType string) { return "int64" },"bigint":    func(columnType gorm.ColumnType) (dataType string) { return "int64" },"int":       func(columnType gorm.ColumnType) (dataType string) { return "int64" },}// 要先于`ApplyBasic`执行g.WithDataTypeMap(dataMap)//=================== 生成全部数据表的model ===================//// 自定义模型结体字段的标签// 将特定字段名的 json 标签加上`string`属性,即 MarshalJSON 时该字段由数字类型转成字符串类型jsonField := gen.FieldJSONTagWithNS(func(columnName string) (tagContent string) {//toStringField := `balance, `toStringField := ``if strings.Contains(toStringField, columnName) {return columnName + ",string"}return columnName})// 将非默认字段名的字段定义为自动时间戳和软删除字段;// 自动时间戳默认字段名为:`updated_at`、`created_at, 表字段数据类型为: INT 或 DATETIME// 软删除默认字段名为:`deleted_at`, 表字段数据类型为: DATETIME//autoUpdateTimeField := gen.FieldGORMTag("update_time", "column:update_time;type:int unsigned;autoUpdateTime")//autoCreateTimeField := gen.FieldGORMTag("create_time", "column:create_time;type:int unsigned;autoCreateTime")// 模型自定义选项组//fieldOpts := []gen.ModelOpt{jsonField, autoCreateTimeField, autoUpdateTimeField}fieldOpts := []gen.ModelOpt{jsonField}// 创建模型的结构体,生成文件在 model 目录; 先创建的结果会被后面创建的覆盖// 创建全部模型文件, 并覆盖前面创建的同名模型allModel := g.GenerateAllTable(fieldOpts...)// 创建模型的方法,生成文件在 query 目录; 先创建结果不会被后创建的覆盖g.ApplyBasic(allModel...)//=================== 生成指定数据表的model ===================////有时候其他小伙伴改动了某个表,不能随着当前版本上线,就需要指定部分数据表/*g.ApplyBasic(g.GenerateModel("ms_base_user"),g.GenerateModel("ms_user_depart"),g.GenerateModel("ms_sys_dict"),)*/g.Execute()
}

然后运行次文件:go run gorm_generate_db_struct.go ,会在 ./internal/model/dao 目录下生成如下的model文件:

image-20250106182912099

执行查询操作

gozero/internal/logic/admin/userdetaillogic.go 文件中编写查询model层数据的代码:

func (l *UserDetailLogic) UserDetail(req *types.UserDetailRequest) (resp *types.UserDetailResponse, err error) {//根据ID查询用户表信息,返回用户详情信息userModel := l.svcCtx.Model.Useruser, err := userModel.WithContext(l.ctx).Debug().Where(userModel.ID.Eq(int64(req.Id))).First()if err != nil {logx.Error("根据ID查询用户表信息失败:" + err.Error())return nil, err}//成功返回return &types.UserDetailResponse{Code: 200,Msg:  "获取用户详情成功",Data: types.UserDetailData{Id:   int32(user.ID),Name: user.Name,},}, nil
}

然后,在postman中使用POST请求调用一下看看:

image-20250106184200899

错误码封装

配置拦截器

上面我们通过直接在logic层写死了返回码:200 和 message:获取用户详情成功,如果出现异常,则不会返回json结构:

image-20250106185549280下一步,来配置一下go-zero中的拦截器(SetErrorHandler)。

在入口文件 gozero.go 中,添加如下代码:

// 使用拦截器
httpx.SetErrorHandler(func(err error) (int, any) {switch e := err.(type) {case *utils.MyError:return http.StatusOK, utils.Fail(e)default:return http.StatusOK, utils.ErrorResponse(constants.CodeServerError.Code, err.Error())}
})

另外新增gozero/internal/utils/response.go 文件:

package utils// 自定义错误结构体
type MyError struct {Code    int64  `json:"code"`Message string `json:"message"`
}// 实现 Error() 方法
func (e *MyError) Error() string {return e.Message
}// 创建自定义错误
func NewMyError(code int64, msg string) *MyError {return &MyError{Code:    code,Message: msg,}
}type Response struct {Code    int64       `json:"code"`Message string      `json:"message"`Data    interface{} `json:"result"`
}func SuccessResponse(data interface{}) *Response {return &Response{Code:    200000,Message: "请求成功",Data:    data,}
}
func ErrorResponse(code int64, msg string) *Response {return &Response{Code:    code,Message: msg,Data:    nil,}
}func Fail(err *MyError) *Response {return &Response{Code:    err.Code,Message: err.Message,Data:    nil,}
}

再次运行:

image-20250107155254244

错误码封装

接下来封装一个统一的错误码配置信息。新增gozero/internal/constants/errorCode.go:

package constantsvar (//系统基本错误码CodeSuccess     = utils.NewMyError(200000, "请求成功")CodeServerError = utils.NewMyError(500000, "服务器异常")CodeParamsEmpty = utils.NewMyError(400000, "参数为空")CodeParamsError = utils.NewMyError(400001, "参数错误")CodeUnknown     = utils.NewMyError(400100, "未知错误")
)

gozero/internal/logic/admin/userdetaillogic.go中添加一个自定义的判断条件:

func (l *UserDetailLogic) UserDetail(req *types.UserDetailRequest) (resp *types.UserDetailResponse, err error) {//校验参数,假设这里要求Id必须大于0if req.Id <= 0 {return nil, constants.CodeParamsError}
}

再次运行:

image-20250106193239807

注意:上面定义的MyError 结构体一定要实现 Error()方法,否则,就不能算是一个error类型!

image-20250106194824946

接下来,我们把成功返回部分也优化一下,把原有的logic的成功返回部分改为统一封装的*Response类型。

修改:gozero/internal/logic/admin/userdetaillogic.go

func (l *UserDetailLogic) UserDetail(req *types.UserDetailRequest) (resp *utils.Response, err error) {//前置判断条件和查询数据...//成功返回return utils.SuccessResponse(types.UserDetailData{Id:   int32(user.ID),Name: user.Name,}), nil
}

源代码:https://gitee.com/rxbook/go-demo-2025

相关文章:

go-zero框架基本配置和错误码封装

文章目录 加载配置信息配置 env加载.env文件配置servicecontext 查询数据生成model文件执行查询操作 错误码封装配置拦截器错误码封装 接上一篇&#xff1a;《go-zero框架快速入门》 加载配置信息 配置 env 在项目根目录下新增 .env 文件&#xff0c;可以配置当前读取哪个环…...

Android中Service在新进程中的启动流程2

目录 1、Service在客户端的启动入口 2、Service启动在AMS的处理 3、Service在新进程中的启动 4、Service与AMS的关系再续 上一篇文章中我们了解了Service在新进程中启动的大致流程&#xff0c;同时认识了与客户端进程交互的接口IApplicationThread以及与AMS交互的接口IActi…...

论文速读|Matrix-SSL:Matrix Information Theory for Self-Supervised Learning.ICML24

论文地址&#xff1a;Matrix Information Theory for Self-Supervised Learning 代码地址&#xff1a;https://github.com/yifanzhang-pro/matrix-ssl bib引用&#xff1a; article{zhang2023matrix,title{Matrix Information Theory for Self-Supervised Learning},author{Zh…...

ubunut22.04安装docker(基于阿里云 Docker 镜像源安装 Docker)

安装 更新包管理器&#xff1a; sudo apt update 安装 Docker 的依赖包 sudo apt install apt-transport-https ca-certificates curl gnupg lsb-release添加阿里云 Docker 镜像源 GPG 密钥&#xff1a; curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gp…...

k8s namespace绑定节点

k8s namespace绑定节点 1. apiserver 启用准入控制 PodNodeSelector2. namespace 添加注解 scheduler.alpha.kubernetes.io/node-selector3. label node 1. apiserver 启用准入控制 PodNodeSelector vim /etc/kubernetes/manifests/kube-apiserver.yaml spec:containers:- co…...

【ElementPlus】在Vue3中实现表格组件封装

预览 搜索筛选组件 <template><div><el-formref"formView":model"formData"label-width"auto"label-position"right":label-col-style"{ min-width: 100px }":inline"true"><el-form-item …...

cursor重构谷粒商城04——vagrant技术快速部署虚拟机

前言&#xff1a;这个系列将使用最前沿的cursor作为辅助编程工具&#xff0c;来快速开发一些基础的编程项目。目的是为了在真实项目中&#xff0c;帮助初级程序员快速进阶&#xff0c;以最快的速度&#xff0c;效率&#xff0c;快速进阶到中高阶程序员。 本项目将基于谷粒商城…...

26、正则表达式

目录 一. 匹配字符 .&#xff1a;匹配除换行符外的任意单个字符。 二. 位置锚点 ^&#xff1a;匹配输入字符串的开始位置。 $&#xff1a;匹配输入字符串的结束位置。 \b&#xff1a;匹配单词边界。 \B&#xff1a;匹配非单词边界。 三. 重复限定符 *&#xff1a;匹配…...

SpringBoot使用MockMVC通过http请求controller控制器调用测试

说明 在Spring Boot中编写测试控制器调用是一个常见的需求,通常使用Spring的测试框架来完成。Spring Boot提供了多种方式来测试控制器,包括使用MockMvc进行模拟HTTP请求和响应的测试。 基本示例 1. 创建Spring Boot项目 首先,确保你已经创建了一个Spring Boot项目。如果…...

【Unity3D】Unity混淆工具Obfuscator使用

目录 一、导入工具 二、各种混淆形式介绍 2.1 程序集混淆 2.2 命名空间混淆 2.3 类混淆 2.4 函数混淆 2.5 参数混淆 2.6 字段混淆 2.7 属性混淆 2.8 事件混淆 三、安全混淆 四、兼容性处理 4.1 动画方法兼容 4.2 GUI方法兼容 4.3 协程方法兼容 五、选项 5.1 调…...

C语言语法基础学习—动态分配空间(new和malloc的用法及区别)

前言 在 C 语言中&#xff0c;动态内存分配主要是通过 malloc() 和 free() 函数来完成的。而在 C 中是使用new和delete关键字&#xff0c;来动态分配内存。 虽然 C 语言没有 new&#xff0c;但 malloc() 和 new 在内存分配上的作用是相似的。下面我们详细解释 malloc() 和 ne…...

QT:控件属性及常用控件(3)-----输入类控件(正则表达式)

输入类控件既可以进行显示&#xff0c;也能让用户输入一些内容&#xff01; 文章目录 1.Line Edit1.1 用户输入个人信息1.2 基于正则表达式的文本限制1.3 验证两次输入的密码是否一致1.4 让输入的密码可以被查看 2.Text Edit2.1 输入和显示同步2.1 其他信号出发情况 3.ComboBox…...

Hive SQL 执行计划解析

Hive SQL 执行计划解析 一、 explain用法 1. SQL 查询 EXPLAIN SELECT SUM(view_dsp) AS view_sum FROM ads.table_a WHERE p_day 2025-01-06;2. 执行计划 STAGE DEPENDENCIES:Stage-1 is a root stageStage-0 depends on stages: Stage-1STAGE PLANS:Stage: Stage-1Map …...

热更新杂乱记

热更新主要有一个文件的MD5值的比对过程&#xff0c;期间遇到2个问题&#xff0c;解决起来花费了一点时间 1. png 和 plist 生成zip的时候再生成MD5值会发生变动。 这个问题解决起来有2种方案&#xff1a; &#xff08;1&#xff09;.第一个方案是将 png和plist的文件时间改…...

博客搭建 — GitHub Pages 部署

关于 GitHub Pages GitHub Pages 是一项静态站点托管服务&#xff0c;它直接从 GitHub 上的仓库获取 HTML、CSS 和 JavaScript 文件&#xff0c;通过构建过程运行文件&#xff0c;然后发布网站。 本文最终效果是搭建出一个域名为 https://<user>.github.io 的网站 创建…...

翻译:How do I reset my FPGA?

文章目录 背景翻译&#xff1a;How do I reset my FPGA?1、Understanding the flip-flop reset behavior2、Reset methodology3、Use appropriate resets to maximize utilization4、Many options5、About the author 背景 在写博客《复位信号的同步与释放&#xff08;同步复…...

Linux 进程环境变量:深入理解与实践指南

&#x1f31f; 快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。&#x1f31f; &#x1f6a9;用通俗易懂且不失专业性的文字&#xff0c;讲解计算机领域那些看似枯燥的知识点&#x1f6a9; 在 Linux 系统里…...

Linux探秘坊-------5.git

1.git介绍 1.版本控制器 为了能够更⽅便我们管理这些不同版本的⽂件&#xff0c;便有了版本控制器。所谓的版本控制器&#xff0c;就是能让你了解到⼀个⽂件的历史&#xff0c;以及它的发展过程的系统。通俗的讲就是⼀个可以记录⼯程的每⼀次改动和版本迭代的⼀个管理系统&am…...

Linux中的几个基本指令(二)

文章目录 1、cp指令例一&#xff1a;例二&#xff1a;例三&#xff1a;例四&#xff1a;例五&#xff1a; 2、mv 指令例一&#xff1a;例二&#xff1a; 3、cat指令例一&#xff1a; 4、tac指令5、which指令6、date指令时间戳&#xff1a;7、zip指令 今天我们继续学习Linux下的…...

Java入门笔记(1)

引言 在计算机编程的广袤宇宙中&#xff0c;Java无疑是一颗格外耀眼的恒星。那么&#xff0c;Java究竟是什么呢&#xff1f; Java是美国Sun公司&#xff08;Stanford University Network&#xff09;在1995年推出的一门计算机高级编程语言。曾经辉煌的Sun公司在2009年被Oracle&…...

设计模式的艺术-开闭原则

原则使用频率图&#xff08;仅供参考&#xff09; 1.如何理解开闭原则 简单来说&#xff0c;开闭原则指的是 “对扩展开放&#xff0c;对修改关闭”。 当软件系统需要增加新的功能时&#xff0c;应该通过扩展现有代码的方式来实现&#xff0c;而不是去修改已有的代码。 例如我…...

【C语言系列】深入理解指针(3)

深入理解指针&#xff08;3&#xff09; 一、字符指针变量二、数组指针变量2.1数组指针变量是什么&#xff1f;2.2数组指针变量怎么初始化&#xff1f; 三、二维数组传参的本质四、函数指针变量4.1函数指针变量的创建4.2函数指针变量的使用4.3两段有趣的代码4.4 typedef关键字 …...

three.js+WebGL踩坑经验合集:写在前面的话

笔者从2023年初开始参与一个基于three.js引擎的web项目的开发工作。本打算2024年春节就把期间踩过的坑写到博客上分享给大家&#xff0c;然而哪怕本专栏的各种构思和内容已经在笔者的脑海里翻滚了一年&#xff0c;得了严重拖延症患者的我还是一直拖到了现在&#xff0c;实在惭愧…...

利用Linux的工作队列(Workqueue)实现中断下半部的处理

本文代码在哪个基础上修改而成&#xff1f; 本文是在博文 https://blog.csdn.net/wenhao_ir/article/details/145228617 的代码基础上修改而成。 关于工作队列(Workqueue)的概念 工作队列(Workqueue)可以用于实现Linux的中断下半部的&#xff0c;之前在博文 https://blog.cs…...

LabVIEW处理复杂系统和数据处理

LabVIEW 是一个图形化编程平台&#xff0c;广泛应用于自动化控制、数据采集、信号处理、仪器控制等复杂系统的开发。它的图形化界面使得开发人员能够直观地设计系统和算法&#xff0c;尤其适合处理需要实时数据分析、高精度控制和复杂硬件集成的应用场景。LabVIEW 提供丰富的库…...

spring-springboot -springcloud

目录 spring: 动态代理: spring的生命周期(bean的生命周期): SpringMvc的生命周期: SpringBoot: 自动装配: 自动装配流程: Spring中常用的注解&#xff1a; Spring Boot中常用的注解&#xff1a; SpringCloud: 1. 注册中心: 2. gateway(网关): 3. Ribbon(负载均…...

DRG/DIP 2.0时代下基于PostgreSQL的成本管理实践与探索(下)

五、数据处理与 ETL 流程编程实现 5.1 数据抽取与转换(ETL) 在 DRG/DIP 2.0 时代的医院成本管理中,数据抽取与转换(ETL)是将医院各个业务系统中的原始数据转化为可供成本管理分析使用的关键环节。这一过程涉及从医院 HIS 系统中抽取患者诊疗数据,并对其进行格式转换、字…...

打造本地音乐库

文章目录 存储介质硬盘&#xff08;NAS&#xff09;媒体播放器&#xff08;可视MP3、MP4&#xff09;实体介质&#xff08;CD光盘、黑胶片&#xff09;注意事项为什么不使用在线音乐&#xff08;App&#xff09;和网盘打造一套HiFi系统的成本非常高 获取音乐正版音乐途径免费音…...

【2024 - 年终总结】叶子增长,期待花开

写在前面&#xff1a;本博客仅作记录学习之用&#xff0c;部分图片来自网络&#xff0c;如需引用请注明出处&#xff0c;同时如有侵犯您的权益&#xff0c;请联系删除&#xff01; 文章目录 前言论博客创作保持2024的记录清单博客科研开源工作生活 总结与展望互动致谢参考 前言…...

python 统计相同像素值个数

目录 python 统计相同像素值个数 最大值附近的值 python 统计相同像素值个数 import cv2 import numpy as np import time from collections import Counter# 读取图像 image cv2.imread(mask16.jpg)# 将图像转换为灰度图像 gray_image cv2.cvtColor(image, cv2.COLOR_BGR2…...