gingo
gingo copied to clipboard
基于 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/
é¦é¡µ
ç¹å»åºç¨ï¼æ¥çåºç¨å表
ç¹å» å建åºç¨ æé®