乐趣区

关于go:go基于范型的-gin-开发脚手架

Gingo

Introduce

Gingo 是基于 gin 框架为外围的脚手架,可能疾速创立 Restful 格调的 API 接口,并且能提供简略的后盾治理性能,应用本我的项目能够疾速实现业务逻辑开发。

Github: https://github.com/songcser/gingo

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/

首页

点击利用,查看利用列表

点击 创立利用 按钮

退出移动版