go-zero的配置及gorm、自定义返回等的引入以及扩展

时间:2025-01-26 08:04:07

工程维度(摘自官网)

.
├── consumer
├──
├── internal
│   └── model
├── job
├── pkg
├── restful
├── script
└── service

  • consumer: 队列消费服务
  • internal: 工程内部可访问的公共模块
  • job: cron job 服务
  • pkg: 工程外部可访问的公共模块
  • restful:HTTP 服务目录,下存放以服务为维度的微服务
  • script:脚本服务目录,下存放以脚本为维度的服务
  • service:gRPC 服务目录,下存放以服务为维度的微服务

服务维度(项目目录)(摘自官网)

example
├── etc
│   └──
├──
└── internal
    ├── config
    │   └──
    ├── handler
    │   ├──
    │   └──
    ├── logic
    │   └──
    ├── svc
    │   └──
    └── types
        └──

  • example:单个服务目录,一般是某微服务名称
  • etc:静态配置文件目录
  • :程序启动入口文件
  • internal:单个服务内部文件,其可见范围仅限当前服务
  • config:静态配置文件对应的结构体声明目录
  • handler:handler 目录,可选,一般 http 服务会有这一层做路由管理,handler 为固定后缀
  • logic:业务目录,所有业务编码文件都存放在这个目录下面,logic 为固定后缀
  • svc:依赖注入目录,所有 logic 层需要用到的依赖都要在这里进行显式注入
  • types:结构体存放目录

一、基本配置

1、数据库

1.1、

version: '3'
services:
  mysql:
    container_name: mysql8
    image: mysql:${MYSQL_VERSION}
    restart: always
    ports:
      - ${MYSQL_PORT}:3306
    environment:
      TZ: Asia/Shanghai
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: zero_demo
    volumes:
      - ${MYSQL_DIR}/data:/var/lib/mysql
      - ${MYSQL_DIR}/conf:/etc/mysql//
      - ${MYSQL_DIR}/logs:/logs
    command:
      --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --explicit_defaults_for_timestamp=true
      --lower_case_table_names=1

  Redis:
    container_name: redis6
    image: redis:${REDIS_VERSION}
    restart: always
    volumes:
      - ${REDIS_DIR}/data:/data
      - ${REDIS_DIR}/conf:/etc/redis/
    ports:
      - ${REDIS_PORT}:6379
    command: redis-server /etc/redis/

1.2、etc目录下的yaml文件配置

Name: demo   # 由api中的service名决定
Host: 0.0.0.0
Port: 8080

Mysql:
  DataSource: root:111111@tcp(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=true&loc=Local

#jwtAuth   go-zero 中内置了 JWT 的解密和验证功能,只需开启jwt使用即可
JwtAuth:
  AccessSecret: demo-aslfhsafsfsflaskfasf
  AccessExpire: 7200

#Redis:
#  Address: 127.0.0.1:6379
#  Pass: 123456

1.3、internal中的config目录下的文件配置

package config

import "/zeromicro/go-zero/rest"

//yaml文件中的配置参数经过解析后会解析到此文件中,所以此文件中的参数要与yaml中的参数相对应
type Config struct {
	
	Mysql struct {
		DataSource string
	}
	JwtAuth struct {
		AccessSecret string
		AccessExpire int64
	}
	//Redis struct {
	//	Address string
	//	Pass    string
	//}
}

1.4、svc下文件配置

package svc

import (
	"demo/common/database"
	"demo/internal/config"
)

type ServiceContext struct {
	Config      
	UserRepo    
	//Redis  *
}

func NewServiceContext(c ) *ServiceContext {
	dbConn := () //引入数据库得到数据库链接
	//newRedis := (, redisConfig(c))
	return &ServiceContext{
		Config:      c,
		UserRepo:    (dbConn), //调用数据库(数据库初始化,因为NewServiceContext()函数在main函数中已经调用初始化了)
		//Redis:  newRedis,
	}
}

//func redisConfig(c )  {
//	return func(r *) {
//		 = 
//		 = 
//	}
//}

1.5、引入gorm 链接数据库实现

package database

import (
	"/driver/mysql"
	"/gorm"
	"/gorm/schema"
)

type DBConn struct {
	ConnGorm *
}

// NewDB  连接并初始化数据库
func NewDB(dataSource string) *DBConn {
	db, err := ((dataSource), &{
		DisableForeignKeyConstraintWhenMigrating: true,
		SkipDefaultTransaction:                   false,
		NamingStrategy: {
			SingularTable: true, // use singular table name, table for `User` would be `user` with this option enabled
		},
	})
	if err != nil {
		panic("连接数据库失败")
	}

	d := &DBConn{
		ConnGorm: db,
	}
	InitDB(db)
	return d
}
func InitDB(db *) {
	if err := (
		&User{},
	); err != nil {
		panic(err)
	}
}

1.6、数据库操作业务代码

package repo

import (
	"demo/common/database"
	"demo/internal/types"
)

type user struct {
	db *
}

type UserRepo interface {
}

func NewUserRepo(conn *) UserRepo {
	return &user{conn}
}

2、自定义返回错误--返回数据

格式:code、msg、data标准错误

 推荐使用 code-data 统一响应格式用法(官方文档HTTP扩展),此法最简单只需替换即可

2.1、自定义返回错误 

在main函数中下面两种引入方式选择其一就行,好处是只需引入一次即可

2.1.1、官方的:

package main

import (
	"demo/internal/config"
	"demo/internal/handler"
	"demo/internal/svc"
	"flag"
	"fmt"
	"/zeromicro/go-zero/core/conf"
	"/zeromicro/go-zero/core/logc"
	"/zeromicro/go-zero/rest"
)

var configFile = ("f", "etc/", "the config file")

func main() {
	()

	//调试用,调试时使错误以plain(比较直观)的方式打印在终端
	var x 
	 = "plain"
	(x)


	引入自定义返回错误,也可以引入自己构造的
	/*	(func(ctx , err error) (int, any) {
		switch e := err.(type) {
		case *:
			return , []{
				Code: ,
				Msg:  ,
			}
		default:
			return , nil
		}
	})*/

	var c 
	(*configFile, &c)

	server := ()
	defer ()

	ctx := (c)
	(server, ctx)

	("Starting server at %s:%d...\n", , )
	()
}

2.1.2:自定义的

引入代码(写法一):

package errorz

import (
	"fmt"
)

//**********************************
//按go-zero官方例子进行,用 /*  */ 标注这段需要配置在main函数中。

/*
(func(ctx , err error) (int, any) {
	switch e := err.(type) {
	case *:
		return , []{
			Code: ,
			Msg:  ,
		}
	default:
		return , nil
	}
})*/

// 用来在main函数中赋值
type CodeMsg[T any] struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
}

//*****************************************************

type StdCodeMsg struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
}

func (c *StdCodeMsg) Error() string {
	return ("code: %d, msg: %s", , )
}

// New creates a new StdCodeMsg.
func NewStdCodeMsg(code int, msg string) error {
	return &StdCodeMsg{Code: code, Msg: msg}
}

func (c *StdCodeMsg) StdCodeMsg() any {
	return &StdCodeMsg{
		Code: ,
		Msg:  ,
	}
}

// Nil represents the predeclared value nil.
type Nil struct{}

引入代码(写法二):

package errorz

//**********************************
//按go-zero官方例子进行,用 /*  */ 标注这段需要配置在main函数中用来返回错误。

/*(func(ctx , err error) (int, interface{}) {
	switch e := err.(type) {
	case *:
	return , ()
	default:
	return , nil
	}
})
*/

type CoMsg struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
}

type ErrorResponse struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
}

func NewCoMsg(code int, msg string) *CoMsg {
	return &CoMsg{
		Code: code,
		Msg:  msg,
	}
}

func (e *CoMsg) Error() string {
	return 
}

func (e *CoMsg) Data() any {
	return &ErrorResponse{
		,
		,
	}
}

2.2、自定义返回数据 (最简单的是方式二)

方式一:需要在业务代码的返回函数中调用以下函数

// 返回数据---方式一
type RespCoMsgSuccess struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    any    `json:"data"`
}

func CoMsgSuccess(data interface{}) *RespCoMsgSuccess {
	return &RespCoMsgSuccess{200, "OK", data}
}

// 返回数据---方式二
type StdResponse[T any] struct {
	// Code represents the business code, not the http status code.
	Code int `json:"code" xml:"code"`
	// Msg represents the business message, if Code = BusinessCodeOK,
	// and Msg is empty, then the Msg will be set to BusinessMsgOk.
	Msg string `json:"msg" xml:"msg"`
	// Data represents the business data.
	Data T `json:"data,omitempty" xml:"data,omitempty"`
}

func StdSuccess(v any) StdResponse[any] {
	var resp StdResponse[any]
	 = 200
	 = "OK"
	 = v
	return resp
}

方式二: code-data 统一响应格式用法(官方文档HTTP扩展

在 zeromicro 下有一个 x 仓库专门用于对 go-zero 的扩展,其中 HTTP 的扩展支持了:

  1. code-data 响应格式支持
  2. xml 响应支持
  3. code-msg error 类型支持

详情可参考 GitHub - zeromicro/x: This repository is part of the go-zero project but outside the main tree. It's developed under looser compatibility requirements than the go-zero repository is part of the go-zero project but outside the main tree. It's developed under looser compatibility requirements than the go-zero project. - GitHub - zeromicro/x: This repository is part of the go-zero project but outside the main tree. It's developed under looser compatibility requirements than the go-zero :///zeromicro/x

package handler

import (
    "net/http"

    "demo/internal/logic"
    "demo/internal/svc"
    "demo/internal/types"
    "/zeromicro/go-zero/rest/httpx"
    xhttp "/zeromicro/x/http"
)

func loginHandler(svcCtx *)  {
    return func(w , r *) {
        var req 
        if err := (r, &req); err != nil {
            // ((), w, err)
             // code-data 响应格式
            ((), w, err)
            return
        }

        l := ((), svcCtx)
        resp, err := (&req)
        if err != nil {
            // code-data 响应格式
            ((), w, err)
        } else {
            // code-data 响应格式
            ((), w, resp)
        }
    }
}

 方式三:官方的定制模板法

可以通过官方的定制模板调用以下方法进行模板生成,也可在自己的返回包中实现,在handler中进行调用

//Handler中
func GreetHandler(svcCtx *)  {
    return func(w , r *) {
        var req 
        if err := (r, &req); err != nil {
            (w, err)
            return
        }

        l := ((), svcCtx)
        resp, err := (&req)
        (w, resp, err)
    }
}

package response

import (
    "net/http"

    "/zeromicro/go-zero/rest/httpx"
)

type Body struct {
    Code int         `json:"code"`
    Msg  string      `json:"msg"`
    Data interface{} `json:"data,omitempty"`
}

func Response(w , resp interface{}, err error) {
    var body Body
    if err != nil {
         = -1
         = ()
    } else {
         = "OK"
         = resp
    }
    (w, body)
}