• Stars
    star
    154
  • Rank 242,095 (Top 5 %)
  • Language
    Go
  • Created over 1 year ago
  • Updated 7 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

基于 gin 框架为核心的脚手架,使用本项目可以快速完成业务逻辑开发。

Gingo

Introduce

Gingo是基于 gin 框架为核心的脚手架,使用本项目可以快速完成业务逻辑开发。

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时,此目录不会被下载,所以通常业务逻辑放在这个下面。 我们将在这个目录下面加一些业务代码,说明脚手架的使用。

新增 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/

首页

首页

点击应用,查看应用列表

列表

点击 创建应用 按钮

新建