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)
相关推荐
轻口味6 分钟前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王41 分钟前
React Hooks
前端·javascript·react.js
迷途小码农零零发1 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
晓纪同学1 小时前
QT-简单视觉框架代码
开发语言·qt
威桑1 小时前
Qt SizePolicy详解:minimum 与 minimumExpanding 的区别
开发语言·qt·扩张策略
飞飞-躺着更舒服1 小时前
【QT】实现电子飞行显示器(简易版)
开发语言·qt
明月看潮生1 小时前
青少年编程与数学 02-004 Go语言Web编程 16课题、并发编程
开发语言·青少年编程·并发编程·编程与数学·goweb
明月看潮生1 小时前
青少年编程与数学 02-004 Go语言Web编程 17课题、静态文件
开发语言·青少年编程·编程与数学·goweb
Java Fans1 小时前
C# 中串口读取问题及解决方案
开发语言·c#
盛派网络小助手1 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#