以hertz框架为基础,封装一套基于go1.23+的适用于面向api编程的快速开发框架
████ ████ ███████ ████████ ██ ██ ████████ ███████ ██ ████ ████ ████████ ██ ██ ███████ ███████ ██ ██
░██░██ ██░██ ██░░░░░██ ██░░░░░░██░██ ░██ ░██░░░░░ ░██░░░░██ ████ ░██░██ ██░██░██░░░░░ ░██ ░██ ██░░░░░██ ░██░░░░██ ░██ ██
░██░░██ ██ ░██ ██ ░░██ ██ ░░ ░██ ░██ ░██ ░██ ░██ ██░░██ ░██░░██ ██ ░██░██ ░██ █ ░██ ██ ░░██░██ ░██ ░██ ██
░██ ░░███ ░██░██ ░██░██ ░██ ░██ █████░███████ ░███████ ██ ░░██ ░██ ░░███ ░██░███████ ░██ ███ ░██░██ ░██░███████ ░████
░██ ░░█ ░██░██ ░██░██ █████░██ ░██░░░░░ ░██░░░░ ░██░░░██ ██████████░██ ░░█ ░██░██░░░░ ░██ ██░██░██░██ ░██░██░░░██ ░██░██
░██ ░ ░██░░██ ██ ░░██ ░░░░██░██ ░██ ░██ ░██ ░░██ ░██░░░░░░██░██ ░ ░██░██ ░████ ░░████░░██ ██ ░██ ░░██ ░██░░██
░██ ░██ ░░███████ ░░████████ ░░███████ ░██ ░██ ░░██░██ ░██░██ ░██░████████░██░ ░░░██ ░░███████ ░██ ░░██░██ ░░██
░░ ░░ ░░░░░░░ ░░░░░░░░ ░░░░░░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░░░░░░░ ░░ ░░ ░░░░░░░ ░░ ░░ ░░ ░░

一、目录结构
├── Dockerfile
├── LICENSE
├── Makefile # makefile
├── README.md
├── app # 模块存放目录
│ ├── amqp # 消息队列
│ ├── controller # 控制器
│ └── service # 服务层
├── bootstrap # 初始化程序加载服务
├── cmd # command命令
│ ├── admin.go # 生成admin后台账号
│ ├── controller.go # 生成controller控制器
│ ├── migrate.go # 生成migrate数据库迁移
│ ├── model.go # 生成model数据模型
│ ├── service.go # 生成服务层
├── config
│ ├── config.go # yaml配置文件映射成结构体
│ ├── white_list.go # 白名单
│ └── yaml # yaml配置文件目录
├── global # 全局变量和全局方法
├── go.mod
├── go.sum
├── main.go
├── middleware # 中间件
├── migrations # 迁移文件
├── models # 模型
├── pkg # 自定义的常用服务,JWT,助手函数等
│ ├── auth # jwt
│ ├── lib # 日志服务,数据库服务,redis服务等
│ ├── paginator # 分页器
│ ├── response # http请求返回的状态和格式化
│ ├── util # 助手函数
│ └── validator # 参数验证器
├── router # 路由配置
├── runtime # 运行时产生的文件 如日志等
├── types # 所有自定义的结构体
目前已集成和实现:
下一步计划:
- 支持 定时任务 cron
- 支持 pprof 性能剖析组件
- 支持 trace 项目内部链路追踪
- 支持 rate 接口限流组件
- 支持 grpc rpc组件
二、启动服务
注意启动前需要将 mysql服务和redis服务开启,并配置config.dev.yaml文件(默认读取dev环境)中的mysql和redis配置
1、安装依赖和初始化
go mod tidy
2、服务启动
go run main.go
# 查看 main.go的参数
go run main.go --help
3、访问如下表示成功启动
请求:http://127.0.0.1:9527/ping
{
"status": 200,
"errcode": 0,
"requestid": "9ac7f4f2-1271-4f87-8df7-599a478af9cb",
"message": "Pong!",
"data": ""
}
4、安装热更新
go install github.com/cosmtrek/air@latest
命令行敲入:air 即可执行热更新 代码编辑即更新
5、部署casbin权限(重要!(按以下步骤执行))
此步骤针对于backend接口进行权限访问
1)执行migrate
go run main.go migrate -s=all
# 具体参数查看help
go run main.go migrate -help
2)请求 /routes
接口
此接口会创建一个基于casbin的超级管理员权限
6、打包上线
# 查看make命令行
make help
# 基础打包,生成可执行文件(根据当前系统)
make build
# 打包windows
make windows
# 打包darwin
make darwin
# 打包linux
make linux
在releases中查看打包的文件
三、组件使用
1、基于gorm的查询分页构造器
引用包
import "github.com/go-mogu/hz-framework/pkg/paginator"
一、基础用法
1)单表分页基础用法:
var memberList = make([]models.GinAdmin, 0)
paginator, err := paginator.NewBuilder().
WithDB(global.DB).
WithModel(models.GinAdmin{}).
WithField([]string{"password", "salt", "updated_at", "_omit"}).
WithCondition("id = ?", 1).
Pagination(memberList, 1, 10)
return paginator, err
2)连表joins查询用法:
定义接收struct
type BaseUser models.GinUser
type GinUserInfo models.GinUserInfo
// UserList 获取关联列表
type UserList struct {
BaseUser
GinUserInfo `gorm:"foreignKey:user_id" json:"user_info"`
}
用法一:
var userList = make([]user.UserList, 0)
pagination, err := paginator.NewBuilder().
WithDB(global.DB).
WithModel(models.GinUser{}).
WithFields(models.GinUser{}, models.GinUserTbName, []string{"password", "salt", "_omit"}).
WithFields(models.GinUserInfo{}, models.GinUserInfoTbName, []string{"id", "user_id", "role_ids"}).
WithJoins("left", []paginator.OnJoins{{
LeftTableField: paginator.JoinTableField{Table: models.GinUserTbName, Field: "id"},
RightTableField: paginator.JoinTableField{Table: models.GinUserInfoTbName, Field: "user_id"},
}}).
Pagination(&userList, requestParams.Page, global.Cfg.Server.DefaultPageSize)
return pagination, err
用法二:
var userList = make([]user.UserList, 0)
multiFields := []paginator.SelectTableField{
{Model: models.GinUser{}, Table: models.GinUserTbName, Field: []string{"password", "salt", "_omit"}},
{Model: models.GinUserInfo{}, Table: models.GinUserInfoTbName, Field: []string{"user_id", "role_ids"}},
}
pagination, err := paginator.NewBuilder().
WithDB(global.DB).
WithModel(models.GinUser{}).
WithMultiFields(multiFields).
WithJoins("left", []paginator.OnJoins{{
LeftTableField: paginator.JoinTableField{Table: models.GinUserTbName, Field: "id"},
RightTableField: paginator.JoinTableField{Table: models.GinUserInfoTbName, Field: "user_id"},
}}).
Pagination(&userList, requestParams.Page, global.Cfg.Server.DefaultPageSize)
return pagination, err
3)预加载preload查询用法(强烈建议用法):
注意:
与joins查询方式定义的struct有些许差别,preload方式定义struct名称必须与model当前表的struct名称一致,
且关联表的struct名称不能跟model对于的struct名称一样 例如:定义的`UserInfo` 写法如下
定义接收struct
type BaseUser models.GinUser
type GinUserInfo models.GinUserInfo
type GinUser struct {
BaseUser
UserInfo GinUserInfo `gorm:"foreignKey:user_id" json:"user_info"`
}
用法如下:
var userList = make([]user.GinUser, 0)
pagination, err := paginator.NewBuilder().
WithDB(global.DB).
WithModel(models.GinUser{}).
WithPreload("UserInfo").
Pagination(&userList, requestParams.Page, global.Cfg.Server.DefaultPageSize)
return pagination, err
此写法不建议使用WithFields、WithField查询字段,建议直接定义接收struct规定的查询字段即可
访问地址:http://127.0.0.1:9527/user/index?page=1 返回数据格式如下:
{
"status": 200,
"errcode": 0,
"requestid": "9ac7f4f2-1271-4f87-8df7-599a478af9cb",
"message": "请求成功",
"data": {
"list": [],
"current_page": 1,
"total": 2,
"last_page": 1,
"per_page": 10
}
}
4)案例查看:
1)用法如下 获取用户列表:
entities/user/gin_user.go
app/controller/backend/user.go
app/service/backend/user.go
router/routes/common.go
二、具体方法
查看使用
1)必须在链式操作中
db连接方法
WithDB(db *gorm.DB) *PageBuilder
传入全局global.DB
2)必须在链式操作中
model连接方法
WithModel(model interface{}) *PageBuilder
传入查询主表model 例如:models.GinAdmin 参数不能传结构体取地址方式 如:&models.GinAdmin
3)非必须在链式操作中
单表查询或过滤字段方法
WithField(fields []string) *PageBuilder
fields 最后一个参数默认为_select(可不传),如传_omit为过滤前面传输的字段。
注意:
- _select / _omit 必须在最后
- WithModel 参数不能传结构体取地址 例如:&models.GinAdmin 必须 models.GinAdmin 不然 _omit 参数失效
- 此注意事项适用于
WithFields
方法、WithMultiFields
方法
用法如下:
// 表示过滤前面字段
WithField([]string{"created_at", "updated_at", "_omit"})
// 表示查询前面的字段
WithField([]string{"created_at", "updated_at", "_select"})
WithField([]string{"created_at", "updated_at"})
4)非必须在链式操作中
多表查询或过滤字段方法(preload模式下 关联表查询有问题,preload关联查询不建议使用此方法)
WithFields(model interface{}, table string, fields []string) *PageBuilder
fields 最后一个参数默认为_select(可不传),如传_omit为过滤前面传输的字段。
用法如下:
// 表示过滤前面字段
WithFields(models.GinUser{}, models.GinUserTbName, []string{"password", "salt", "_omit"})
// 表示查询前面的字段
WithFields(models.GinUserInfo{}, models.GinUserInfoTbName, []string{"id", "user_id", "role_ids", "_select"})
WithFields(models.GinUserInfo{}, models.GinUserInfoTbName, []string{"id", "user_id", "role_ids"})
5)非必须在链式操作中
多表多字段查询(可替代WithFields方法)
WithMultiFields(fields []SelectTableField) *PageBuilder
用法如下:
WithMultiFields([]paginator.SelectTableField{
{Model: models.GinUser{}, Table: models.GinUserTbName, Field: []string{"password", "salt", "_omit"}},
{Model: models.GinUserInfo{}, Table: models.GinUserInfoTbName, Field: []string{"id", "user_id", "role_ids"}},
})
6)非必须在链式操作中
多表关联查询主动预加载(暂不支持条件)
WithPreloads(querys []string) *PageBuilder
用法如下:
WithPreloads([]string{"UserInfo", "UserRecord"})
7)非必须在链式操作中
关联查询主动预加载(可传条件,条件参考gorm)
WithPreload(query string, args ...interface{}) *PageBuilder
用法如下:
WithPreload("UserInfo", "user_id = ?", "1")
8)非必须在链式操作中
数据查询条件方法
WithCondition(query interface{}, args ...interface{}) *PageBuilder
传入查询条件 支持gorm中where条件中的查询方式(非struct方式) query, args参数参照gorm的where条件传入方式
9)非必须在链式操作中
数据查询条件方法
WithJoins(joinType string, joinFields []OnJoins) *PageBuilder
joinType:join类型 可传入:left、right、inner,joinFields结构体: LeftTableField:如:主表.ID RightTableField:如:关联表.主表ID
用法如下:
WithJoins("left", []paginator.OnJoins{{
LeftTableField: paginator.JoinTableField{Table: models.GinUserTbName, Field: "id"},
RightTableField: paginator.JoinTableField{Table: models.GinUserInfoTbName, Field: "user_id"},
}})
10)必须在链式操作中最后一环
分页返回方法
Pagination(dst interface{}, currentPage, pageSize int) (Page, error)
dst 传入接收数据的struct结构体 注意:必须是应用方式传递 如:&userList,
model,currentPage 为当前页码,pageSize为每页查询数量
11)非必须在链式操作中
对接原生查询方式
NewDB() *gorm.DB
用此方法之后的链式操作下pagination里面的方法均不可用,后面跟gorm原生方法即可
用法如下:
NewDB().Where("id = ?", id).First(&userList)
12)获取当前页码
paginator.CurrentPage
13)获取分页列表
paginator.List
14)获取数据总数
paginator.Total
15)获取最后一页页码
paginator.LastPage
16)获取每页数据条数
paginator.PerPage
2、基于gin上传组件
UploadFile(path string, r *gin.Context) (*FileHeader, error)
默认存储在项目中upload目录,如果没有会自动创建 path:upload目录模块目录 如:user 则目录是:upload/user/{yyyy-mm-dd}/...
用法如下:
app/controller/backend/attachment.go
pkg/util/upload.go
router/routes/common.go
3、rabbitmq组件使用
配置yaml配置文件中的amqp参数
1)启动消费者
测试案例
go run command/test/consumer.go
2)启动生产者
测试案例
go run command/test/producer.go
四、工具
运行 go run main.go --help 可查看到以下命令集
COMMANDS:
migrate Create a migration command
account Create a new admin account
model Create a new model class
controller Create a new controller class
service Create a new service class
help, h Shows a list of commands or help for one command
1、执行migrate
查看使用
# 安装migrate cli工具
curl -L https://github.com/golang-migrate/migrate/releases/download/$version/migrate.$platform-amd64.tar.gz | tar xvz
# MacOS安装
brew install golang-migrate
# Window 使用scoop安装 https://scoop.sh/
scoop install migrate
# 创建迁移文件语法例如:
migrate create -ext sql -dir migrations -seq create_users_table
# 第一种方式执行迁移
# 执行迁移操作:
migrate -database 'mysql://root:123456@tcp(127.0.0.1:3306)/gin_framework' -path ./migrations up
# 执行回滚操作:
migrate -database 'mysql://root:123456@tcp(127.0.0.1:3306)/gin_framework' -path ./migrations down
# 第二种方式执行迁移
# 查看help命令
go run main.go migrate --help
# 格式如下:
go run main.go migrate -s {step} -e {env}
# env: dev, test, prod与config.*.yaml文件保持一致 默认是dev
# step:执行的迁移文件数量(回滚的文件数量)例如:1,2,3... 如果执行所有传 all
# 执行所有迁移操作:
go run main.go migrate -s all
# 执行部分迁移操作:
# 如:go run main.go migrate -s 1
# 执行回滚操作:
# 如:go run main.go migrate -s -1
2、自动生成model
# 执行生成所有model
go run main.go model -tb=all {env}
# 具体参数查看help
go run main.go model -help
3、自动生成controller
go run main.go controller -c={controller名称} -m={module名称}
# 例如:go run main.go controller -c=admin -m=backend
# 具体参数查看help
go run main.go controller -help
4、自动生成service
go run main.go service -s={service名称} -m={module名称}
# module名称是app/controller目录下的模块名称
# 例如:go run main.go service -s=admin -m=backend
# 具体参数查看help
go run main.go service -help
5、创建后台管理员账号(基于gin_admin表的,可自行修改代码基于其他表)
go run main.go account -c={账号名称} -p={密码}
# 具体参数查看help
go run main.go account -help
五、参考
查看使用
初始化一个接口项目需要安装的依赖包(主要)
初始化go.mod
go mod init github.com/go-mogu/hz-framework
go mod tidy
安装gin框架
go get -u github.com/gin-gonic/gin
安装model自动生成包
go get -u github.com/MQEnergy/gorm-model
安装gorm
go get -u gorm.io/gorm
# 如果下载不了 十之八九是因为 GOSUMDB的原因
export GOSUMDB=
# GOSUMDB置空就行
安装命令行工具
go get -u github.com/urfave/cli/v2
安装log日志
go get -u github.com/sirupsen/logrus
go get -u github.com/lestrrat-go/file-rotatelogs
安装redis
go get -u github.com/go-redis/redis/v8
go get -u github.com/go-redsync/redsync/v4
安装jwt
go get -u github.com/dgrijalva/jwt-go
安装cors跨域
go get -u github.com/gin-contrib/cors
安装casbin
go get -u github.com/casbin/casbin/v2
go get -u github.com/casbin/gorm-adapter/v3
安装snowflake
go get -u github.com/bwmarrin/snowflake
安装golang-migrate迁移组件
go get -u github.com/golang-migrate/migrate/v4
# 安装migrate cli工具
curl -L https://github.com/golang-migrate/migrate/releases/download/$version/migrate.$platform-amd64.tar.gz | tar xvz
# MacOS安装
brew install golang-migrate
# Window 使用scoop安装 https://scoop.sh/
scoop install migrate
# 创建迁移文件语法例如:
migrate create -ext sql -dir migrations -seq create_users_table
# 执行迁移操作:
migrate -database 'mysql://root:123456@tcp(127.0.0.1:3306)/gin_framework' -path ./migrations up
# 执行回滚操作:
migrate -database 'mysql://root:123456@tcp(127.0.0.1:3306)/gin_framework' -path ./migrations down
安装热更新
go install github.com/cosmtrek/air@latest
基于Go 1.18+泛型的Lodash风格的Go库
go get -u github.com/samber/lo
配置文件解析库
go get -u github.com/spf13/viper