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/

主页

【go】基于范型的 gin 开发脚手架

点击使用,检查使用列表

list.png

点击 创立使用 按钮

add.png

GITHUB

github.com/songcser/gi…