Go-React做一个todolist(服务端)【一】项目初始化

后端仓库地址

地址

项目依赖

sh 复制代码
# gin
go get -u github.com/gin-gonic/gin
# viper日志
go get -u github.com/spf13/viper
# 数据库和gorm
go get -u gorm.io/driver/mysql
go get -u gorm.io/gorm
# uuid
go get -u github.com/google/uuid
# token
go get -u github.com/golang-jwt/jwt/v5
# 邮箱
go get github.com/jordan-wright/email
# swagger
go get -u github.com/swaggo/swag/cmd/swag
go install github.com/swaggo/swag/cmd/swag@latest
go get -u github.com/swaggo/files
go get -u github.com/swaggo/gin-swagger

# base64验证码
go get -u github.com/mojocn/base64Captcha
# gokit 工具集合
go get github.com/songzhibin97/gkit

项目结构搭建

先执行 go mod init ToDoList

初始化模块

在initialize/index.go中

go 复制代码
package initialize

import (
	"ToDoList/global"
	"fmt"
)

func Works() {
	// 读取配置文件
	global.GVA_VIPER = Viper()
	// 初始化缓存组件
	Cache.InitCache()
	// 初始化数据库并注册表
	global.GVA_DB = GormMysql.InitGormMysql()
	GormMysql.TableInit()
	// 启动服务
	global.GVA_SERVER = GinEngine.InitEngine()
	if global.GVA_SERVER != nil {
		// 注册中间件
		GinEngine.InitMiddleware()
		// 注册路由
		GinEngine.InitRouter()
		// 运行服务
		global.GVA_SERVER.Run(fmt.Sprintf(":%s", global.GVA_CONFIG.App.Port))
	}
}

gin初始化

在initialize/gin.go中

go 复制代码
package initialize

import (
	"ToDoList/docs"
	"ToDoList/global"
	"ToDoList/middleware"
	"ToDoList/router"
	"github.com/gin-gonic/gin"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
)

type ginEngine struct{}

// 初始化中间件
func (receiver ginEngine) InitMiddleware() {
	// cors跨域中间件
	global.GVA_SERVER.Use(middleware.CorsByRules())
	// swagger中间件
	docs.SwaggerInfo.BasePath = global.GVA_CONFIG.App.RouterPrefix
	global.GVA_SERVER.GET(global.GVA_CONFIG.App.RouterPrefix+"/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}

// 初始化路由
func (receiver *ginEngine) InitRouter() {
	// 全局路由前缀
	globalRouterGroup := global.GVA_SERVER.Group(global.GVA_CONFIG.App.RouterPrefix)
	router.UserRouter.InitUserRouter(globalRouterGroup)
}

// 初始化Gin引擎
func (receiver *ginEngine) InitEngine() *gin.Engine {
	r := gin.Default()

	return r
}

var GinEngine = new(ginEngine)

gorm初始化

在initialize/gorm.go中

go 复制代码
package initialize

import (
	"ToDoList/global"
	"ToDoList/model"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"log"
	"os"
	"time"
)

type gormMysql struct{}

func (receiver *gormMysql) TableInit() {
	err := global.GVA_DB.AutoMigrate(
		model.User{},
		model.Backlog{},
	)
	if err != nil {
		fmt.Println("注册表发生错误:", err)
		panic("初始化表失败")
	}
	fmt.Println("~~~The database table is successfully registered~~~")
}

func (receiver *gormMysql) InitGormMysql() *gorm.DB {
	password := global.GVA_CONFIG.Mysql.Password
	username := global.GVA_CONFIG.Mysql.Username
	port := global.GVA_CONFIG.Mysql.Port
	dbName := global.GVA_CONFIG.Mysql.Dbname
	dsn := fmt.Sprintf("%s:%s@tcp(127.0.0.1:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", username, password, port, dbName)
	db, err := gorm.Open(mysql.New(mysql.Config{
		DSN:                       dsn,   // DSN data source name
		DefaultStringSize:         256,   // string 类型字段的默认长度 如果该字段是字符串并作为主键会造成索引超长
		DisableDatetimePrecision:  true,  // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
		DontSupportRenameIndex:    true,  // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
		DontSupportRenameColumn:   true,  // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
		SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
	}), &gorm.Config{ //连接的配置
		SkipDefaultTransaction: false, // 默认false,增删改都是事务操作来保证数据一致性,能提升一点性能
		NamingStrategy: schema.NamingStrategy{
			TablePrefix:         "",    // 如果设置了会给每个表名加前缀
			SingularTable:       true,  // 单数表名,如果false会在表明后加s
			NameReplacer:        nil,   // 字符转转换器,转换字段名
			NoLowerCase:         false, //当设置为true时,NoLowerCase选项将禁用表名和列名的蛇形命名转换。保持表名和列名的原始大小写形式。
			IdentifierMaxLength: 0,     //不限制数据库标识符(如表名、列名)的最大长度。
		},
		Logger: logger.New(
			log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
			logger.Config{
				SlowThreshold:             time.Second,   // Slow SQL threshold
				LogLevel:                  logger.Silent, // Log level
				IgnoreRecordNotFoundError: true,          // Ignore ErrRecordNotFound error for logger
				ParameterizedQueries:      true,          // Don't include params in the SQL log
				Colorful:                  false,         // Disable color
			},
		), // 可以自定义日志
		DisableForeignKeyConstraintWhenMigrating: true, //true时,建表将不会建立物理外键,代码中我们采用逻辑外键提升数据库操作效率
	})
	if err != nil {
		panic(err.Error())
	}
	sqlDB, _ := db.DB()
	sqlDB.SetMaxIdleConns(global.GVA_CONFIG.Mysql.MaxIdleConns)
	sqlDB.SetMaxOpenConns(global.GVA_CONFIG.Mysql.MaxOpenConns)

	return db
}

var GormMysql = new(gormMysql)

缓存kit初始化

在initialize/cache.go中

go 复制代码
package initialize

import (
	"ToDoList/global"
	"ToDoList/util"
	"github.com/songzhibin97/gkit/cache/local_cache"
)

type cache struct{}

func (receiver *cache) InitCache() {
	dr, err := util.BasicUtils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
	if err != nil {
		panic(err)
	}
	global.BlackCache = local_cache.NewCache(
		local_cache.SetDefaultExpire(dr),
	)
}

var Cache = new(cache)

读取配置文件Viper初始化

go 复制代码
package initialize

import (
	"ToDoList/enum"
	"ToDoList/global"
	"flag"
	"fmt"
	"github.com/fsnotify/fsnotify"
	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
)

// Viper //
// 优先级: 命令行 > 环境变量 > 默认值
// Author [SliverHorn](https://github.com/SliverHorn)
func Viper(path ...string) *viper.Viper {
	var configFile string
	if len(path) == 0 {
		flag.StringVar(&configFile, "c", "", "choose config file.")
		flag.Parse()
		if configFile == "" { // 判断命令行参数是否为空
			switch gin.Mode() {
			case gin.DebugMode:
				configFile = enum.ConfigDefaultFile
				fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为%s\n", gin.Mode(), enum.ConfigDebugFile)
			case gin.ReleaseMode:
				configFile = enum.ConfigReleaseFile
				fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为", gin.Mode(), enum.ConfigReleaseFile)
			case gin.TestMode:
				configFile = enum.ConfigTestFile
				fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为%s\n", gin.Mode(), enum.ConfigTestFile)
			}
		} else { // 命令行参数不为空 将值赋值于config
			fmt.Printf("您正在使用命令行的-c参数传递的值,config的路径为%s\n", configFile)
		}
	} else { // 函数传递的可变参数的第一个值赋值于config
		configFile = path[0]
		fmt.Printf("您正在使用func Viper()传递的值,config的路径为%s\n", configFile)
	}

	// 初始化Viper對象
	v := viper.New()
	// 设置配置文件的路径
	v.SetConfigFile(configFile)
	// 配置文件类型
	v.SetConfigType("yaml")
	err := v.ReadInConfig()
	if err != nil {
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}
	// 当配置文件变化调用此hook
	v.OnConfigChange(func(e fsnotify.Event) {
		fmt.Println("config file changed:", e.Name)
		if err = v.Unmarshal(&global.GVA_CONFIG); err != nil {
			fmt.Println(err)
		}
	})
	// 配置文件变动会重读不必重启服务
	v.WatchConfig()
	if err = v.Unmarshal(&global.GVA_CONFIG); err != nil {
		panic(err)
	}
	return v
}

模型

用户

go 复制代码
package model

import (
	"github.com/google/uuid"
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Username   string    `json:"userName" gorm:"comment:用户名"`
	NickName   string    `json:"nickName" gorm:"comment:昵称"`
	Password   string    `json:"password" gorm:"comment:密码"`
	Identity   string    `json:"身份" gorm:"comment:用户身份"`
	Email      string    `json:"email" gorm:"comment:用户邮箱"`
	UUID       uuid.UUID `json:"uuid" gorm:"index;comment:用户UUID"`
	Avatar     string    `json:"avatar" gorm:"comment:用户头像;default:https://fancyfish.top/hero.jpg"`
	ThemeColor string    `json:"themeColor" gorm:"comment:用户主题颜色"`
	Enable     bool      `json:"enable" gorm:"comment:用户是否可用;default:true"`
	Backlog    Backlog
}

func (receiver User) TableName() string {
	return "user"
}

待办事项

go 复制代码
package model

import (
	"gorm.io/gorm"
)

type Backlog struct {
	gorm.Model
	BacklogContent  string    `json:"backlogContent" gorm:"comment:代办事项内容"`
	Completed       bool      `json:"completed" gorm:"comment:是否完成;default:false"`
	UserId          uint      `json:"user_id"`
	ParentId        *uint     `json:"parent_id"`
	ChildrenBacklog []Backlog `gorm:"foreignkey:ParentId;"`
}

func (receiver Backlog) TableName() string {
	return "backlog"
}

路由

go 复制代码
package router

import (
	"ToDoList/api"
	"github.com/gin-gonic/gin"
)

type userRouter struct{}

func (receiver userRouter) InitUserRouter(R *gin.RouterGroup) {
	r := R.Group("user")
	{
		r.POST("register", api.UserApi.Register)
		r.POST("login", api.UserApi.Login)
		r.POST("change_password", api.UserApi.ChangePassword)
		r.PUT("set_userinfo", api.UserApi.SetSelfInfo)
		r.GET("get_userInfo", api.UserApi.GetUserInfo)
		r.POST("get_captcha", api.UserApi.GetCaptcha)
	}
}

var UserRouter = new(userRouter)
相关推荐
KookeeyLena84 分钟前
如何限制任何爬虫爬取网站的图片
开发语言·c++·爬虫
__AtYou__22 分钟前
Golang | Leetcode Golang题解之第417题太平洋大西洋水流问题
leetcode·golang·题解
yanyanwenmeng23 分钟前
matlab基础
开发语言·算法·matlab
拉玛干30 分钟前
社团周报系统可行性研究-web后端框架对比-springboot,django,gin
数据库·python·spring·golang
末央&40 分钟前
【C++】内存管理
java·开发语言·c++
不是仙人的闲人43 分钟前
Qt日志输出及QsLog日志库
开发语言·数据库·qt
八了个戒1 小时前
【TypeScript入坑】TypeScript 的复杂类型「Interface 接口、class类、Enum枚举、Generics泛型、类型断言」
开发语言·前端·javascript·面试·typescript
西瓜本瓜@1 小时前
React + React Image支持图像的各种转换,如圆形、模糊等效果吗?
前端·react.js·前端框架
黄毛火烧雪下1 小时前
React 的 useEffect 钩子,执行一些异步操作来加载基本信息
前端·chrome·react.js
蓝莓味柯基1 小时前
React——点击事件函数调用问题
前端·javascript·react.js