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

news2024/11/26 0:37:35

后端仓库地址

地址

项目依赖

# 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中

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中

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中

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中

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初始化

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
}

模型

用户

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"
}

待办事项

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"
}

路由

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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1557432.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

蓝桥杯2015年第十三届省赛真题-三羊献瑞

一、题目 观察下面的加法算式: 祥 瑞 生 辉 三 羊 献 瑞 ---------------------- 三 羊 生 瑞 气 (如果有对齐问题,可以参看【图1】) 其中,相同的汉字代表相同的数字,不同的汉字代表不同的数字。 请你填写“三羊献瑞”所…

C语言-编译和链接

目录 1.前言2.编译2.1预处理(预编译)2.1.1 #define 定义常量2.1.2 #define 定义宏2.1.3带有副作用的宏参数2.1.4宏替换规则2.1.5 #和##2.1.5.1 #运算符2.1.5.2 ## 运算符 2.1.6 命名约定2.1.7 #undef2.1.8 条件编译2.1.9 头文件的包含2.1.9.1 本地文件包…

电商系列之取消订单

> 插:AI时代,程序员或多或少要了解些人工智能,前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 坚持不懈,越努力越幸运,大家…

每日面经分享(Spring Boot: part2 DAO层)

1. Spring Boot DAO层的作用 a. 封装数据访问逻辑:DAO层的主要责任是封装与数据访问相关的逻辑。负责处理与数据库的交互,包括数据的增删改查等操作。通过将数据访问逻辑统一封装在DAO层中,可以提高代码的可维护性和可重用性。 b. 解耦业务逻…

java Web 疫苗预约管理系统用eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 JSP 疫苗预约管理系统是一套完善的web设计系统,对理解JSP java 编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为TOMCAT7.0,eclipse开发,数据库为Mysql5.0,使…

蓝桥集训之游戏

蓝桥集训之游戏 核心思想:博弈论 区间dp 设玩家1的最优解为A 玩家2的最优解为B 1的目标就是使A-B最大 2的目标就是使B-A最大 当玩家1取L左端点时 右边子区间结果就是玩家2的最优解B-A 即当前结果为w[L] – (B-A) 当玩家1取R右端点时 左边子区间结果就是玩家2的最…

<TensorFlow学习使用P1>——《TensorFlow教程》

一、TensorFlow概述 前言: 本文中一些TensorFlow综合案例的代码逻辑一般正常,在本地均可运行。如有代码复现运行失败,原因如下: (1)运行环境配置可能有误。 (2)由于一些数据集存储空…

算法学习——LeetCode力扣图论篇2

算法学习——LeetCode力扣图论篇2 1020. 飞地的数量 1020. 飞地的数量 - 力扣(LeetCode) 描述 给你一个大小为 m x n 的二进制矩阵 grid ,其中 0 表示一个海洋单元格、1 表示一个陆地单元格。 一次 移动 是指从一个陆地单元格走到另一个相…

实施阶段(2024年3月)

本次探究的项目化学习主题为:校内订餐系统的分析与设计 【主题情境说明】 社会大背景:国家颁发《“健康中国2030”规划纲要》,尤其关注青少年儿童膳食质量,明确提出要加强孩子食育教育,家庭、学校和社会需要共建健康…

Linux系统---如何理解Linux中的文件系统

顾得泉:个人主页 个人专栏:《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂,年薪百万! 一、理解文件系统 1.ls与stat 我们使用ls -l的时候看到的除了看到文件名,还看到了文件元数据。 每行包含7列…

【一站式学会Kotlin】第一节 kotlin 介绍

作者介绍: 百度资深Android工程师T6,在百度任职7年半。 目前:成立赵小灰代码工作室,欢迎大家找我开发Android、微信小程序、鸿蒙项目。 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默。给大家…

Unity 基于Rigidbody2D模块的角色移动

制作好站立和移动的动画后 控制器设计 站立 移动 角色移动代码如下: using System.Collections; using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine;public class p1_c : MonoBehaviour {// 获取动画组件private Animator …

【c++】简单的日期计算器

🔥个人主页:Quitecoder 🔥专栏:c笔记仓 朋友们大家好啊,在我们学习了默认成员函数后,我们本节内容来完成知识的实践,来实现一个简易的日期计算器 目录 头文件声明函数函数的实现1.全缺省默认构…

TASKPROMPTER

baseline模型的预训练权重就有1.6G! 多吓人呐,当时我就暂停下载了,不建议复现

[Flutter]打包IPA

1.直接使用Xcode运行iOS工程 不用flutter构建,在Xcode中是可以独立进行构建运行和打包发布的。 1).运行项目 先将flutter的build清理 $ flutter clean $ flutter pub get 然后立即用XCode打开iOS工程运行 运行会报错: error: The sandbox is not …

gpt-llm-trainer 出炉

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

ML-Decoder: Scalable and Versatile Classification Head

1、引言 论文链接:https://openaccess.thecvf.com/content/WACV2023/papers/Ridnik_ML-Decoder_Scalable_and_Versatile_Classification_Head_WACV_2023_paper.pdf 因为 transformer 解码器分类头[1] 在少类别多标签分类数据集上表现得很好,但由于其查询…

【应用层协议原理】

文章目录 第二章 应用层2.1 应用层协议原理2.1.1 网络应用的体系结构2.1.2 客户-服务器(C/S)体系结构2.1.3 对等体(P2P)体系结构2.2.4 C/S和P2P体系结构的混合体2.2.5 进程通信问题1:对进程进行编址(addres…

厦门攸信技术亮相新技术研讨会,展现物流自动化解决方案新高度!

今日,厦门攸信信息技术有限公司受邀参加了一场备受行业关注的电子制造高端盛会——一步步新技术研讨会,凭借卓越的智能制造与物流自动化技术在会议中大放异彩。作为一家引领行业发展的企业,厦门攸信技术不仅展示了其深厚的技术底蕴&#xff0…

算法之美:堆排序原理剖析及应用案例分解实现

这段时间持续更新关于“二叉树”的专栏文章,关心的小伙伴们对于二叉树的基本原理已经有了初步的了解。接下来,我将会更深入地探究二叉树的原理,并且展示如何将这些原理应用到更广泛的场景中去。文章将延续前面文章的风格,尽量精炼…