当前位置: 首页 > 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&…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...