工程维度(摘自官网)
.
├── 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 的扩展支持了:
- code-data 响应格式支持
- xml 响应支持
- 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)
}