搭建项目
go:generate
//go:generate go env -w GO111MODULE=on
//go:generate go env -w GOPROXY=https://goproxy.cn,direct
//go:generate go mod tidy
//go:generate go mod demo
入口主程序
在入口主程序调用 读取参数、解析jwt、日志库、链接数据库、初始化定时器、初始化路由
// server\main.go
package main
func main() {
println("主程序入口")
global.VGA_VP = core.Viper() // 初始化Viper
initialize.OtherInit() //解析JWT过期时间和缓冲时间,并在解析失败时触发panic。 2. 使用JWT过期时间设置全局BlackCache的默认过期时间。 3. 尝试打开go.mod文件,若成功且模块名为空,则从go.mod中读取并设置模块名。
global.VGA_LOG = core.Zap() // 初始化zap日志库
zap.ReplaceGlobals(global.VGA_LOG)
global.VGA_DB = initialize.Gorm() // gorm连接数据库
initialize.Timer() // 初始化定时器
initialize.DBList()
if global.VGA_DB != nil {
initialize.RegisterTables() // 初始化表
// 程序结束前关闭数据库链接
db, _ := global.VGA_DB.DB()
defer db.Close()
}
core.RunWindowsServer()
}
读取配置文件
func Viper(path ...string) *viper.Viper {
v := viper.New()
v.SetConfigFile("config")
v.SetConfigType("yaml")
err := v.ReadInConfig()
if err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
v.WatchConfig()
v.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("config file changed:", e.Name)
if err = v.Unmarshal(&global.VGA_CONFIG); err != nil {
fmt.Println(err)
}
})
if err = v.Unmarshal(&global.VGA_CONFIG); err != nil {
panic(err)
}
// root 适配性 根据root位置去找到对应迁移位置,保证root路径有效
global.VGA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
return v
初始化日志
func Zap() (logger *zap.Logger) {
if ok, _ := utils.PathExists(global.VGA_CONFIG.Zap.Director); !ok { // 判断是否有Director文件夹
fmt.Printf("create %v directory\n", global.VGA_CONFIG.Zap.Director)
_ = os.Mkdir(global.VGA_CONFIG.Zap.Director, os.ModePerm)
}
levels := global.VGA_CONFIG.Zap.Levels()
length := len(levels)
cores := make([]zapcore.Core, 0, length)
for i := 0; i < length; i++ {
core := internal.NewZapCore(levels[i])
cores = append(cores, core)
}
logger = zap.New(zapcore.NewTee(cores...))
if global.VGA_CONFIG.Zap.ShowLine {
logger = logger.WithOptions(zap.AddCaller())
}
return logger
}
创建api
1.新建数据模型,server/model/模块名称/模块数据表.
server\model\example\userinfo.go
package example
type Userinfo struct {
Name string `uri:"name" form:"name" json:"name"`
Pwd string `uri:"pwd" form:"pwd" json:"pwd"`
}
type UserinfoResponse struct {
Userinfo example.Userinfo `json:"userinfo"`
Token string `json:"token"`
}
2.gorm初始化新增数据表,server\initialize\gorm.go 中 func RegisterTables增加注册数据模型语句
// server\initialize\gorm.go
package initialize
func RegisterTables() {
db := global.VGA_DB
err := db.AutoMigrate(
...
example.Userinfo{},
...
)
if err != nil {
global.VGA_LOG.Error("register table failed", zap.Error(err))
os.Exit(0)
}
global.VGA_LOG.Info("register table success")
}
3.增加路由组和方法 server/router/模块/模块数据表.go,定义POST,GET等的处理函数
// server\router\example\exa_customer.go
package example
type ExaRouter struct {
}
func (e *ExaRouter) InitExaAPiRouter(Router *gin.RouterGroup) {
exaApiRouter := Router.Group("exaApi").Use(middleware.ExaMiddleware())
exaApiRouterWithoutRecord := Router.Group("exaApi")
exaApi := v1.ApiGroupApp.ExaApiGroup // 也可放在enter中统一定义
{
exaApiRouter.POST("login", exaApi.Login)
exaApiRouter.POST("loginJson", exaApi.LoginJson)
}
{
exaApiRouterWithoutRecord.GET("getUserInfo", exaApi.GetUserInfo)
exaApiRouterWithoutRecord.GET("getUserInfoPath/:name/:pwd", exaApi.GetUserInfoPath)
}
}
server\router\example\enter.go中统一增加路由组,初始化路由信息,以及调用的exaApiapi方法
// server\router\example\enter.g
package example
type RouterGroup struct {
...
ExaRouter
}
var (
...
exaApi = v1.ApiGroupApp.ExaApiGroup //也可放在具体路由模块数据表中直接定义
)
4.如果多级分组,还可以在server/router/enter.go增加路由组
package router
var RouterGroupApp = new(RouterGroup)
type RouterGroup struct {
System system.RouterGroup
Example example.RouterGroup // 这里可以新增路由组
}
5.server/initialize/router.go引入并注册新加入的路由组
// server\initialize\router.go
package initialize
func Routers() *gin.Engine {
Router := gin.New()
Router.Use(gin.Recovery())
if gin.Mode() == gin.DebugMode {
Router.Use(gin.Logger())
}
exampleRouter := router.RouterGroupApp.Example
PublicGroup := Router.Group(global.VGA_CONFIG.System.RouterPrefix)
PrivateGroup := Router.Group(global.VGA_CONFIG.System.RouterPrefix)
PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
{
// 健康监测
PublicGroup.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, "ok")
})
}
exampleRouter.InitExaAPiRouter(PrivateGroup) // 用户信息路由
global.VGA_ROUTERS = Router.Routes()
global.VGA_LOG.Info("router register success")
return Router
6.api/v1/模块/模块数据表.go实现上述函数中的api网络交互层具体内容,并引用service进行数据库操作
// server\api\v1\example\enter.go
// enter为api功能块的入口块,即api的入口文件
type ApiGroup struct {
ExaApi
}
var (
exaService = service.ServiceGroupApp.ExaServiceGroup.ExaApiService
)
// server\api\v1\example\exa_api.go
type ExaApi struct {
}
// Login /*
// http://127.0.0.1:8888/exaApi/login
// application/x-www-form-urlencoded或者form-data
func (e *ExaApi) Login(c *gin.Context) {
var userinfo example.Userinfo
err := c.ShouldBind(&userinfo)
user, err = exaService.Login(&userinfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithMessage(exaService.Login(userinfo), c)
}
// http://127.0.0.1:8888/exaApi/getUserInfo?name=zhangsan&pwd=6666
func (e *ExaApi) GetUserInfo(c *gin.Context) {
var userinfo example.Userinfo
err := c.ShouldBind(&userinfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithMessage(exaService.GetUserInfo(userinfo), c)
}
// http://127.0.0.1:8888/exaApi/login
//
// raw {
// "name":"zhangsan",
// "pwd":"23333"
// }
func (e *ExaApi) LoginJson(c *gin.Context) {
var userinfo example.Userinfo
err := c.ShouldBindJSON(&userinfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithMessage(exaService.Login(userinfo), c)
}
// http://127.0.0.1:8888/exaApi/getUserInfoPath/zhangsan/555
func (e *ExaApi) GetUserInfoPath(c *gin.Context) {
var userinfo example.Userinfo
err := c.ShouldBindUri(&userinfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithMessage(exaService.GetUserInfo(userinfo), c)
}
7.service/模块/模块数据表.go,service层处理数据表中具体CURD操作
入口模块
// server\service\system\enter.go
package system
type ServiceGroup struct {
exaService
}
var ExaServiceApp = new(exaService)
具体server层
// server\service\example\exa_user.go
package example
type ExaApiService struct {
}
var ExaApiServiceApp = new(ExaApiService)
func (exaService *ExaService) Login(u *example.Userinfo) (userInter *system.SysUser, err error) {
if nil == global.VGA_DB {
return nil, fmt.Errorf("db not init")
}
var user system.SysUser
err = global.VGA_DB.Where("username = ?", u.Username).Preload("Authorities").Preload("Authority").First(&user).Error
if err == nil {
if ok := utils.BcryptCheck(u.Password, user.Password); !ok {
return nil, errors.New("密码错误")
}
MenuServiceApp.UserAuthorityDefaultRouter(&user)
}
return &user, err
}
func (exaApi *ExaApiService) GetUserInfo(u example.Userinfo) (user example.Userinfo, err error) {
var reqUser system.SysUser
err = global.VGA_DB.Preload("Authorities").Preload("Authority").First(&reqUser, "uuid = ?", uuid).Error
if err != nil {
return reqUser, err
}
MenuServiceApp.UserAuthorityDefaultRouter(&reqUser)
return reqUser, err
}
使用go-swagger配置文档
url参数类型 query、path、body、header,formData
数据参数类型 string integer number boolean struct
以login登录api为例
// Login
// @Summary 用户登录
// @Tags ExaApi
// @Produce application/json
// @Param name formData string false "名字"
// @Param pwd formData string false "密码"
// @Success 200 {object} response.Response{data=exaApiRes.UserinfoResponse,msg=string,code=int} "返回包括用户信息,token,过期时间"
// @Router /exaApi/login [post]
func (e *ExaApi) Login(c *gin.Context) {
var userinfo example.Userinfo
err := c.ShouldBind(&userinfo)
... // 业务逻辑
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithDetailed(exaApiRes.UserinfoResponse{
Userinfo: userinfo,
Token: userinfo.Pwd,
}, "登录成功", c)
}
然后在路由初始化时配置swagger的路由条目
// server\initialize\router.go
docs.SwaggerInfo.BasePath = global.GVA_CONFIG.System.RouterPrefix
Router.GET(global.GVA_CONFIG.System.RouterPrefix+"/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))