• Go Web项目 接口开发全流程


    风离不摆烂学习日志 Day5 — Go Web项目 接口开发全流程

    接上篇地址 Web项目学习之项目结构

    routes包分析

    InitRoutes

    package routes
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"go-web-mini/common"
    	"go-web-mini/config"
    	"go-web-mini/middleware"
    	"time"
    )
    
    // 初始化
    func InitRoutes() *gin.Engine {
    	//设置模式
    	gin.SetMode(config.Conf.System.Mode)
    
    	// 创建带有默认中间件的路由:
    	// 日志与恢复中间件
    	r := gin.Default()
    	// 创建不带中间件的路由:
    	// r := gin.New()
    	// r.Use(gin.Recovery())
    
    	// 启用限流中间件
    	// 默认每50毫秒填充一个令牌,最多填充200个
    	fillInterval := time.Duration(config.Conf.RateLimit.FillInterval)
    	capacity := config.Conf.RateLimit.Capacity
    	r.Use(middleware.RateLimitMiddleware(time.Millisecond*fillInterval, capacity))
    
    	// 启用全局跨域中间件
    	r.Use(middleware.CORSMiddleware())
    
    	// 启用操作日志中间件
    	r.Use(middleware.OperationLogMiddleware())
    
    	// 初始化JWT认证中间件
    	authMiddleware, err := middleware.InitAuth()
    	if err != nil {
    		common.Log.Panicf("初始化JWT中间件失败:%v", err)
    		panic(fmt.Sprintf("初始化JWT中间件失败:%v", err))
    	}
    
    	// 路由分组
    	apiGroup := r.Group("/" + config.Conf.System.UrlPathPrefix)
    
    	// 注册路由
    	InitBaseRoutes(apiGroup, authMiddleware)         // 注册基础路由, 不需要jwt认证中间件,不需要casbin中间件
    	InitUserRoutes(apiGroup, authMiddleware)         // 注册用户路由, jwt认证中间件,casbin鉴权中间件
    	InitRoleRoutes(apiGroup, authMiddleware)         // 注册角色路由, jwt认证中间件,casbin鉴权中间件
    	InitMenuRoutes(apiGroup, authMiddleware)         // 注册菜单路由, jwt认证中间件,casbin鉴权中间件
    	InitApiRoutes(apiGroup, authMiddleware)          // 注册接口路由, jwt认证中间件,casbin鉴权中间件
    	InitOperationLogRoutes(apiGroup, authMiddleware) // 注册操作日志路由, jwt认证中间件,casbin鉴权中间件
    
    	common.Log.Info("初始化路由完成!")
    	return r
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    设置开发模式(debug/release/test,正式版改为release) => 加载中间件 => 注册路由

    base_routes.go

    package routes
    
    import (
    	jwt "github.com/appleboy/gin-jwt/v2"
    	"github.com/gin-gonic/gin"
    )
    
    // 注册基础路由
    func InitBaseRoutes(r *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) gin.IRoutes {
    	router := r.Group("/base")
    	{
    		// 登录登出刷新token无需鉴权
    		router.POST("/login", authMiddleware.LoginHandler)
    		router.POST("/logout", authMiddleware.LogoutHandler)
    		router.POST("/refreshToken", authMiddleware.RefreshHandler)
    	}
    	return r
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    menu_routes.go

    package routes
    
    import (
    	jwt "github.com/appleboy/gin-jwt/v2"
    	"github.com/gin-gonic/gin"
    	"go-web-mini/controller"
    	"go-web-mini/middleware"
    )
    
    func InitMenuRoutes(r *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) gin.IRoutes {
    	menuController := controller.NewMenuController()
    	router := r.Group("/menu")
    	// 开启jwt认证中间件
    	router.Use(authMiddleware.MiddlewareFunc())
    	// 开启casbin鉴权中间件
    	router.Use(middleware.CasbinMiddleware())
    	{
    		router.GET("/tree", menuController.GetMenuTree)
    		router.GET("/list", menuController.GetMenus)
    		router.POST("/create", menuController.CreateMenu)
    		router.PATCH("/update/:menuId", menuController.UpdateMenuById)
    		router.DELETE("/delete/batch", menuController.BatchDeleteMenuByIds)
    		router.GET("/access/list/:userId", menuController.GetUserMenusByUserId)
    		router.GET("/access/tree/:userId", menuController.GetUserMenuTreeByUserId)
    	}
    
    	return r
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    函数格式:

    1. func 函数名(入参1 参数类型,入参2 参数类型) 返回类型

    2. func 函数名(入参1 参数类型,入参2 参数类型)(返回值1 返回类型)

    controller包分析

    user_controller.go

    大致流程:

    定义接口

    定义结构体 结构体里包含持久层的CRUD

    然后实现这些接口 返回出去

    对比Java 的 SpringBoot框架 Gin 没有自动注入 我们需要自己写构造函数 来初始化持久层 Controller层

    type UserController struct {
    	UserRepository repository.IUserRepository
    }
    
    // 构造函数
    func NewUserController() IUserController {
    	userRepository := repository.NewUserRepository()
    	userController := UserController{UserRepository: userRepository}
    	return userController
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    然后上层调用这个构造函数来完成初始化

    image-20221123111701951

    repository包分析

    大体流程与controller层逻辑一致

    type UserRepository struct { //结构体为空
    }
    
    • 1
    • 2

    内存缓存go-cache

    用户信息缓存 避免频繁读取数据库

    地址: https://github.com/patrickmn/go-cache
    一个基于内存的key-value存储/缓存项目,类似于Memcached,并且可选择定期的垃圾回收,适合单机程序。代码量不多,也不难懂。

    "github.com/patrickmn/go-cache"
    
    // 当前用户信息缓存,避免频繁获取数据库  这里逻辑跟Redis差不多 增 删 改 时更新缓存 查优先走缓存 
    var userInfoCache = cache.New(24*time.Hour, 48*time.Hour)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    model包分析

    package model
    
    import "gorm.io/gorm"
    
    type User struct {
    	gorm.Model
    	Username     string  `gorm:"type:varchar(20);not null;unique" json:"username"`
    	Password     string  `gorm:"size:255;not null" json:"password"`
    	Mobile       string  `gorm:"type:varchar(11);not null;unique" json:"mobile"`
    	Avatar       string  `gorm:"type:varchar(255)" json:"avatar"`
    	Nickname     string  `gorm:"type:varchar(20)" json:"nickname"`
    	Introduction *string `gorm:"type:varchar(255)" json:"introduction"`
    	Status       uint    `gorm:"type:tinyint(1);default:1;comment:'1正常, 2禁用'" json:"status"`
    	Creator      string  `gorm:"type:varchar(20);" json:"creator"`
    	Roles        []*Role `gorm:"many2many:user_roles" json:"roles"`
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这里结构体 类似于Java中对象

    需要首字母大写 指定 转 json 后的类型 对应长字段 类型存储为 指针 即取地址

    dto包分析

    user_dto.go

    package dto
    
    import "go-web-mini/model"
    
    // 返回给前端的当前用户信息
    type UserInfoDto struct {
    	ID           uint          `json:"id"`
    	Username     string        `json:"username"`
    	Mobile       string        `json:"mobile"`
    	Avatar       string        `json:"avatar"`
    	Nickname     string        `json:"nickname"`
    	Introduction string        `json:"introduction"`
    	Roles        []*model.Role `json:"roles"`
    }
    
    func ToUserInfoDto(user model.User) UserInfoDto {
    	return UserInfoDto{
    		ID:           user.ID,
    		Username:     user.Username,
    		Mobile:       user.Mobile,
    		Avatar:       user.Avatar,
    		Nickname:     user.Nickname,
    		Introduction: *user.Introduction,
    		Roles:        user.Roles,
    	}
    }
    
    // 返回给前端的用户列表
    type UsersDto struct {
    	ID           uint   `json:"ID"`
    	Username     string `json:"username"`
    	Mobile       string `json:"mobile"`
    	Avatar       string `json:"avatar"`
    	Nickname     string `json:"nickname"`
    	Introduction string `json:"introduction"`
    	Status       uint   `json:"status"`
    	Creator      string `json:"creator"`
    	RoleIds      []uint `json:"roleIds"`
    }
    
    func ToUsersDto(userList []*model.User) []UsersDto {
    	var users []UsersDto
    	for _, user := range userList {
    		userDto := UsersDto{
    			ID:           user.ID,
    			Username:     user.Username,
    			Mobile:       user.Mobile,
    			Avatar:       user.Avatar,
    			Nickname:     user.Nickname,
    			Introduction: *user.Introduction,
    			Status:       user.Status,
    			Creator:      user.Creator,
    		}
    		roleIds := make([]uint, 0)
    		for _, role := range user.Roles {
    			roleIds = append(roleIds, role.ID)
    		}
    		userDto.RoleIds = roleIds
    		users = append(users, userDto)
    	}
    
    	return users
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    当我们不需要将数据库的数据全部返回出去的时候 或者需要做字段转换 拼接 等操作 皆可在dto层完成


    response包分析

    封装统一返回格式

    package response
    
    import (
    	"github.com/gin-gonic/gin"
    	"net/http"
    )
    
    // 返回前端
    func Response(c *gin.Context, httpStatus int, code int, data gin.H, message string) {
    	c.JSON(httpStatus, gin.H{"code": code, "data": data, "message": message})
    }
    
    // 返回前端-成功
    func Success(c *gin.Context, data gin.H, message string) {
    	Response(c, http.StatusOK, 200, data, message)
    }
    
    // 返回前端-失败
    func Fail(c *gin.Context, data gin.H, message string) {
    	Response(c, http.StatusBadRequest, 400, data, message)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    config 包分析

    这里结构体对应 config.yml 配置文件中对应的配置

    type MysqlConfig struct { //mysql配置
    	Username    string `mapstructure:"username" json:"username"`
    	Password    string `mapstructure:"password" json:"password"`
    	Database    string `mapstructure:"database" json:"database"`
    	Host        string `mapstructure:"host" json:"host"`
    	Port        int    `mapstructure:"port" json:"port"`
    	Query       string `mapstructure:"query" json:"query"`
    	LogMode     bool   `mapstructure:"log-mode" json:"logMode"`
    	TablePrefix string `mapstructure:"table-prefix" json:"tablePrefix"`
    	Charset     string `mapstructure:"charset" json:"charset"`
    	Collation   string `mapstructure:"collation" json:"collation"`
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在一个结构体里包含 各个配置的集合 mysql jwt...

    type config struct {
    	System    *SystemConfig    `mapstructure:"system" json:"system"`
    	Logs      *LogsConfig      `mapstructure:"logs" json:"logs"`
    	Mysql     *MysqlConfig     `mapstructure:"mysql" json:"mysql"`
    	Casbin    *CasbinConfig    `mapstructure:"casbin" json:"casbin"`
    	Jwt       *JwtConfig       `mapstructure:"jwt" json:"jwt"`
    	RateLimit *RateLimitConfig `mapstructure:"rate-limit" json:"rateLimit"`
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    java实现 微信公众号推送消息 ,cv 就可运行!!!
    Hive环境搭建_远程部署
    python模式设计之责任链模式
    Windows11安装mysql
    什么变量能够影响苦艾酒的味道?
    leetcode 刷题日记 计算右侧小于当前元素的个数
    对React fiber的理解
    网工内推 | 南天软件,base北京,需持有CCIE认证,最高25k
    外网优秀的排序解析
    面试笔试中的重要算法合集
  • 原文地址:https://blog.csdn.net/qq_49186423/article/details/127999644