Gingo
Introduce
Gingo是根据 gin 结构为中心的脚手架,能够快速创立Restful风格的API接口,而且能供给简略的后台办理功能,运用本项目能够快速完成事务逻辑开发。
Github: github.com/songcser/gi…
Feature
- gin结构,简略,高效,轻量
- gorm数据库ORM结构,封装mapper,运用简略
- viper装备办理
- zap日志结构,输出日志更灵活
- api接口封装,快速完成CURD操作,供给Restful风格接口
- admin后台办理,完成了简略的后台办理,便利进行数据办理
- 运用范型,go版本不能低于1.18
Catalogue
.
|——.gitignore
|——go.mod
|——go.sum
|——cmd
└──migrate
└──main.go // 注册数据库表
└──main.go // 项目入口main
|——README.md
|——config // 装备文件目录
| └──autoload // 装备文件的结构体定义包
| └──admin.go // admin装备
| └──db.go
| └──jwt.go // jwt装备
| └──mysql.go // mysql装备
| └──zap.go // zap日志装备
| └──config.yaml // .yaml装备示例文件
| └──config.go // 装备初始化文件
|——initialize // 数据初始化目录
| └──admin.go // admin初始化
| └──constants.go // 常量数据
| └──gorm.go // 数据库初始化
| └──mysql.go // mysql初始化
| └──router.go // gin初始化
| └──swagger.go
| └──viper.go // viper装备初始化
| └──zap.go // zap日志初始化
|——internal // 该服务一切不对外露出的代码,通常的事务逻辑都在这下面,运用internal防止过错引用
|──middleware // 中间件目录
| └──logger.go // 日志中间件,打印恳求数据
| └──recovery.go // 自定义recovery, 输出过错格式化
|──pkg // 内部服务包
| └──admin // admin完成逻辑
| └──admin.go
| └──init.go
| └──model.go
| └──service.go
| └──api // API接口封装
| └──api.go
| └──auth // 登陆授权接口封装
| └──model.go
| └──user.go
| └──model // 底层模型封装
| └──mapper.go
| └──model.go
| └──page.go
| └──wrapper.go
| └──response // 响应数据模型封装
| └──page.go
| └──response.go
| └──router // 路由模块封装
| └──router.go
| └──service // 服务模块封装
| └──service.go
|──templates // admin模版页面
| └──add.html // 新建页面
| └──edit.html // 编辑页面
| └──embed.go
| └──header.html // 头部页面
| └──home.html // 主页
| └──index.html // 主页面
| └──login.html // 登陆页面
| └──register.html // 注册页面页面
| └──sidebar.html // 左面栏页面
|──utils // 一些东西办法
| └──cache.go // 缓存
| └──error.go // error检查
| └──hash.go // hash加密解密
| └──http.go // http客户端恳求
| └──json.go //
| └──jwt.go // JWT
| └──path.go // 文件途径
| └──time.go // time相关办法
| └──translator.go // 中英文翻译
Usage
internal目录是不对外露出的代码,在做go get时,此目录不会被下载,所以通常事务逻辑放在这个下面。 我们将在这个目录下面加一些事务代码,说明脚手架的运用。
在internal目录下新增 app 包目录
Model
// Package app model.go
package app
import "github.com/songcser/gingo/pkg/model"
type App struct {
model.BaseModel
Name string `json:"name" gorm:"column:name;type:varchar(255);not null"`
Description string `json:"description" gorm:"column:description;type:varchar(4096);not null"`
Level string `json:"level" gorm:"column:level;type:varchar(8);not null"`
Type string `json:"type" gorm:"column:type;type:varchar(16);not null"`
}
App模型有4个自定义字段,gorm标签会对应到数据库的字段。
package model
type Model interface {
Get() int64
}
type BaseModel struct {
ID int64 `json:"id" gorm:"primarykey" admin:"disable"` // 主键ID
CreatedAt utils.JsonTime `json:"createdAt" gorm:"index;comment:创立时刻" admin:"disable"` // 创立时刻
UpdatedAt utils.JsonTime `json:"updatedAt" gorm:"index;comment:更新时刻" admin:"disable"` // 更新时刻
}
func (m BaseModel) Get() int64 {
return m.ID
}
BaseModel 是基础模型,有一些公共字段, 而且完成了 Model interface, 一切引用 BaseModel 的模型都完成了 Model interface。
创立数据库表
# Package initialize gorm.go
package initialize
// RegisterTables 注册数据库表专用
func RegisterTables(db *gorm.DB) {
err := db.Set("gorm:table_options", "CHARSET=utf8mb4").AutoMigrate(
// 系统模块表
auth.BaseUser{},
app.App{}, // app表注册
)
if err != nil {
os.Exit(0)
}
}
履行 migrate 的 main 办法会在数据库创立对应的表。
Api
// Package app api.go
package app
import (
"github.com/songcser/gingo/pkg/api"
"github.com/songcser/gingo/pkg/service"
)
type Api struct {
api.Api
}
func NewApi() Api {
var app App
baseApi := api.NewApi[App](service.NewBaseService(app))
return Api{baseApi}
}
api.Api接口
// Package api api.go
package api
import "github.com/gin-gonic/gin"
type Api interface {
Query(c *gin.Context)
Get(c *gin.Context)
Create(c *gin.Context)
Update(c *gin.Context)
Delete(c *gin.Context)
}
api.Api接口定义了CURD办法,而且办法都是gin.HandlerFunc类型,能够直接绑定到gin Router上。 BaseApi完成了CURD的根本办法,app.Api类型组合了BaseApi的办法。
Router
// Package app router.go
package app
import (
"github.com/gin-gonic/gin"
"github.com/songcser/gingo/pkg/router"
)
func InitRouter(g *gin.RouterGroup) {
r := router.NewRouter(g.Group("app"))
a := NewApi()
r.BindApi("", a)
}
router 是对gin.RouterGroup做了简略封装,便利和Api类型做绑定。 BindApi办法将Api的 CURD 办法和router进行了绑定。
启动服务之后,履行脚本或者运用 postman 恳求服务
创立数据
curl --location 'http://localhost:8080/api/v1/app' \
--header 'Content-Type: application/json' \
--data '{
"name": "测验使用",
"description": "测验使用服务",
"level": "S3",
"type": "container"
}'
回来内容
{
"code": 0,
"data": true,
"message": "success"
}
成功创立数据
查询数据
curl --location 'http://localhost:8080/api/v1/app'
回来内容
{
"code": 0,
"data": {
"total": 1,
"size": 10,
"current": 1,
"results": [
{
"id": 1,
"createdAt": "2023-04-13 16:35:59",
"updatedAt": "2023-04-13 16:35:59",
"name": "测验使用",
"description": "测验使用服务",
"level": "S3",
"type": "container"
}
]
},
"message": "success"
}
查询单个数据
curl --location 'http://localhost:8080/api/v1/app/1'
回来内容
{
"code": 0,
"data": {
"id": 1,
"createdAt": "2023-04-13 16:56:09",
"updatedAt": "2023-04-13 16:58:29",
"name": "测验使用",
"description": "测验使用服务",
"level": "S3",
"type": "container"
},
"message": "success"
}
更新数据
curl --location --request PUT 'http://localhost:8080/api/v1/app/1' \
--header 'Content-Type: application/json' \
--data '{
"name": "测验使用",
"description": "测验使用服务",
"level": "S1",
"type": "container"
}'
回来内容
{
"code": 0,
"data": true,
"message": "success"
}
删去数据
curl --location --request DELETE 'http://localhost:8080/api/v1/app/1'
回来内容
{
"code": 0,
"data": true,
"message": "success"
}
自定义办法
api增加新的办法
// Package app api.go
package app
func (a Api) Hello(c *gin.Context) {
response.OkWithData("Hello World", c)
}
router进行绑定
r.BindGet("hello", a.Hello)
接口恳求
curl --location 'http://localhost:8080/api/v1/app/hello'
回来内容
{
"code": 0,
"data": "Hello World",
"message": "success"
}
Service
在 NewApi 时运用的是BaseService,能够完成自定义的Service,重写一些办法。
package app
import (
"github.com/gin-gonic/gin"
"github.com/songcser/gingo/pkg/model"
"github.com/songcser/gingo/pkg/service"
"github.com/songcser/gingo/utils"
)
type Service struct {
service.Service[App]
}
func NewService(a App) Service {
return Service{service.NewBaseService[App](a)}
}
func (s Service) MakeMapper(c *gin.Context) model.Mapper[App] {
var r Request
err := c.ShouldBindQuery(&r)
utils.CheckError(err)
w := model.NewWrapper()
w.Like("name", r.Name)
w.Eq("level", r.Level)
m := model.NewMapper[App](App{}, w)
return m
}
func (s Service) MakeResponse(val model.Model) any {
a := val.(App)
res := Response{
Name: a.Name,
Description: fmt.Sprintf("称号:%s, 等级: %s, 类型: %s", a.Name, a.Level, a.Type),
Level: a.Level,
Type: a.Type,
}
return res
}
-
MakeMapper办法重写新的Mapper,能够自定义一些查询条件。
-
MakeResponse办法能够重写回来的内容
在Api中替换新的Service
// Package app api.go
func NewApi() Api {
var app App
s := NewService(app) //运用新的Service
baseApi := api.NewApi[App](s)
return Api{Api: baseApi}
}
查询数据
curl --location 'http://localhost:8080/api/v1/app?name=测验&level=S3'
回来内容
{
"code": 0,
"data": {
"total": 1,
"size": 10,
"current": 1,
"results": [
{
"name": "测验使用",
"description": "称号:测验使用, 等级: S3, 类型: container",
"level": "S3",
"type": "container"
}
]
},
"message": "success"
}
如果要在Service增加新的办法,需求在Api模型中重写Service
// Package app service.go
func (s Service) Hello() string {
return "Hello World"
}
Api完成
// Package app api.go
package app
type Api struct {
api.Api
Service Service // 重写Service
}
func NewApi() Api {
var app App
s := NewService(app)
baseApi := api.NewApi[App](s)
return Api{Api: baseApi, Service: s}
}
func (a Api) Hello(c *gin.Context) {
str := a.Service.Hello() // 调用Service办法
response.OkWithData(str, c)
}
Admin
Admin 供给了简略的后台办理服务,能够很便利的对数据进行办理。
首先需求在装备文件中敞开 Admin
admin:
enable: true
auth: true
auth 会敞开认证授权,需求登陆
接入 Admin 也比较简略
// Package app admin.go
package app
import (
"github.com/songcser/gingo/pkg/admin"
)
func Admin() {
var a App
admin.New(a, "app", "使用")
}
在 initialize 中引入 admin
// Package initialize admin.go
package initialize
func Admin(r *gin.Engine) {
if !config.GVA_CONFIG.Admin.Enable {
return
}
admin.Init(r, nil)
app.Admin()
}
在办理页面能够进行简略的查询,创立,修改,删去操作。
Model 字段中能够装备 admin 标签,办理页面能够根据类型展现。
// Package app model.go
package app
import "github.com/songcser/gingo/pkg/model"
type App struct {
model.BaseModel
Name string `json:"name" form:"name" gorm:"column:name;type:varchar(255);not null" admin:"type:input;name:name;label:使用名"`
Description string `json:"description" form:"description" gorm:"column:description;type:varchar(4096);not null" admin:"type:textarea;name:description;label:描绘"`
Level string `json:"level" form:"level" gorm:"column:level;type:varchar(8);not null" admin:"type:radio;enum:S1,S2,S3,S4,S5;label:级别"`
Type string `json:"type" form:"type" gorm:"column:type;type:varchar(16);not null" admin:"type:select;enum:container=容器使用,web=前端使用,mini=小程序使用;label:使用类型"`
}
- type: 字段类型,取值 input, textarea, select, radio。
- name: 字段称号
- label: 字段展现称号
- enum: 枚举数据,在select,radio类型时展现更加友爱
需求增加 form 标签,由所以运用 html 的 form 提交数据。
访问链接
http://localhost:8080/admin/
主页
点击使用,检查使用列表
点击 创立使用 按钮
GITHUB
github.com/songcser/gi…