【go语言开发】gorm库连接和操作mysql,实现一个简单的用户注册和登录

news2025/1/24 2:30:38

本文主要介绍使用gorm库连接和操作mysql,首先安装gorm和mysql依赖库;然后初始化mysql,配置连接池等基本信息;然后建表、完成dao、controller开发;最后在swagger中测试

文章目录

  • 前言
  • 安装依赖库
  • 数据库初始化
  • 账号注册和登录
    • 创建实体类User
    • dao
    • controller
    • router配置
  • 测试

欢迎大家访问个人博客网址:https://www.maogeshuo.com,博主努力更新中…

参考文档:

  • 【go语言开发】yaml文件配置和解析
  • 【go语言开发】loglus日志框架的使用
  • 【go语言开发】swagger安装和使用

前言

GORM 是一个强大的 ORM(对象关系映射)库,可以简化数据库操作并提供方便的查询方法。它提供了一种简单而强大的方式来处理数据库操作,包括连接到数据库、定义数据模型、执行查询、插入、更新和删除数据等功能。

以下是 GORM 库的一些主要特点和优点:

  • 支持多种数据库引擎:GORM 支持多种主流的数据库引擎,如 MySQL、PostgreSQL、SQLite、SQL Server 等。

  • 自动迁移:通过 GORM,你可以使用简单的代码就能自动创建、更新数据库表结构,而无需手动编写 SQL。

  • 链式方法:GORM 提供了丰富的链式方法,用于构建复杂的查询条件,并支持预加载相关数据,实现数据的懒加载。

  • 事务支持:GORM 提供了事务支持,确保在数据操作时的原子性和一致性。

  • 回调函数:你可以注册各种回调函数,以在特定事件发生时执行自定义逻辑,如在保存数据之前或之后执行某些操作。

  • 软删除:GORM 支持软删除功能,即标记删除数据而非真正从数据库中删除,方便数据恢复和数据保留。

  • 关系映射:GORM 可以轻松地定义模型之间的关系,如一对一、一对多、多对多等,并提供方便的方法来处理这些关系。

  • 性能优化:GORM 对数据库操作进行了优化,提供了缓存、批量插入等功能,以提高性能。

安装依赖库

当使用 GORM 时,首先需要安装 GORM 包和相应的数据库驱动。

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

数据库初始化

初始化mysql,我这里只配置了两张表,设置连接池相关参数

package core

import (
	"code-go/model/do"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"log"
	"time"
)

var DB *gorm.DB

// InitMysql 初始化mysql
func InitMysql() (*gorm.DB, error) {
	mysqlConfig := Config.Database.Mysql
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		mysqlConfig.UserName,
		mysqlConfig.Password,
		mysqlConfig.Host,
		mysqlConfig.Port,
		mysqlConfig.Database)
	LOG.Println("dsn: ", dsn)
	dbMysql, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Println("open db_mysql error ", err)
		return nil, err
	}
	DB = dbMysql

	//迁移表
	autoMigrateTable()

	// 是否打开日志
	if mysqlConfig.LogMode {
		dbMysql.Debug()
	}

	db, _ := dbMysql.DB()
	//设置连接池的最大闲置连接数
	db.SetMaxIdleConns(10)
	//设置连接池中的最大连接数量
	db.SetMaxOpenConns(100)
	//设置连接的最大复用时间
	db.SetConnMaxLifetime(10 * time.Second)
	return dbMysql, nil
}

// 自动迁移表
func autoMigrateTable() {
	err := DB.AutoMigrate(&do.User{}, &do.OperationLog{})
	if err != nil {
		LOG.Error("迁移表结构失败:", err)
	}
}

SetMaxIdleConns:设置最大空闲连接数,目前默认值为2,0<= n<=MaxIdleConns
SetMaxOpenConns: 设置最大连接数,默认为0
SetConnMaxLifetime:设置连接的最大存活时间,当d<=0,connections are not closed due to a connection’s age
至于其他的配置,查看源码和官方文档https://gorm.io/docs/

账号注册和登录

创建实体类User

package do

import (
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Username     string  `gorm:"type:varchar(20);not null;unique;index:idx_username" json:"username"`
	Password     string  `gorm:"size:255;not null" json:"password,omitempty"`
	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"`
}

dao

package dao

import (
	"code-go/core"
	"code-go/model/do"
)

func InsertUser(user do.User) error {
	tx := core.DB.Create(&user)
	if tx.Error != nil {
		core.LOG.Println("insert user in do fail")
		return tx.Error
	}
	return nil
}

func GetUserByUsername(userName string) *do.User {
	var user *do.User
	tx := core.DB.Model(&do.User{}).Where("username=?", userName).Find(&user)
	if tx.Error != nil {
		core.LOG.Println("Query user by username fail")
	}
	return user
}

controller

package api

import (
	"code-go/app/admin/dao"
	"code-go/common"
	"code-go/core"
	"code-go/global"
	"code-go/model/do"
	"code-go/model/vo"
	"code-go/util"
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
	"time"
)

// Register 用户注册
//
//	@Summary	用户注册
//	@Produce	json
//	@Router		/api/user/register [post]
//	@Param		username	query	string	true	"用户名"
//	@Param		password	query	string	true	"密码"
//	@Param		mobile		query	string	true	"电话"
func Register(c *gin.Context) {
	userName := c.Query("username")
	password := c.Query("password")
	mobile := c.Query("mobile")
	if userName == "" || password == "" {
		core.LOG.Println("输入的用户名和密码为空")
		c.JSON(http.StatusOK, common.FailWithCodeMsg(common.VALILD_FAIL, "输入的用户名或密码为空"))
		return
	}

	// 查询用户
	daoUser := dao.GetUserByUsername(userName)
	if daoUser.Username == userName {
		core.LOG.Printf("用户: %s 已存在\n", userName)
		c.JSON(http.StatusOK, common.OkWithData("输入的用户已存在"))
		return
	}

	// 生成用户
	genPasswd := util.GenPasswd(password)
	var user do.User
	user.Mobile = mobile
	user.Username = userName
	user.Password = genPasswd
	user.Status = core.User_status_OK

	err := dao.InsertUser(user)
	if err != nil {
		core.LOG.Println("插入用户失败 ", err)
		c.JSON(http.StatusOK, common.FailWithCodeMsg(common.INSERT_DB_FAIL, "插入用户失败"))
		return
	}
	c.JSON(http.StatusOK, common.Ok())

}

// Login 用户登录
//
//	@Summary	用户登录
//	@Produce	json
//	@Router		/api/user/login [post]
//	@Param		username	query	string	true	"用户名"
//	@Param		password	query	string	true	"密码"
func Login(c *gin.Context) {
	userLogin := vo.UserLoginReqVo{}
	err := c.ShouldBindQuery(&userLogin)
	if err != nil {
		core.LOG.Println("用户登录:参数绑定失败")
		c.JSON(http.StatusOK, common.FailWithMsg("参数绑定失败"))
		return
	}
	userName := userLogin.Username
	password := userLogin.Password
	if userName == "" || password == "" {
		core.LOG.Println("用户登录:输入的用户名和密码为空")
		c.JSON(http.StatusOK, common.FailWithCodeMsg(common.VALILD_FAIL, "输入的用户名和密码为空"))
		return
	}
	// 校验格式
	isMatched := util.ValidateUserName(userName)
	if !isMatched {
		core.LOG.Println("用户登录:用户名格式校验失败")
		c.JSON(http.StatusOK, common.FailWithCodeMsg(common.VALILD_FAIL, "用户名格式校验失败"))
		return
	}
	//isMatched = util.ValidatePassword(password)
	//if !isMatched {
	//	global.Log.Println("用户登录:密码格式校验失败")
	//	c.JSON(http.StatusOK, common.FailWithCodeMsg(common.VALILD_FAIL, "密码格式校验失败"))
	//	return
	//}

	// TODO: 生成验证码

	// 数据库查询
	user := dao.GetUserByUsername(userName)
	if user.Username == "" {
		core.LOG.Println("用户登录:未查询到用户")
		c.JSON(http.StatusOK, common.FailWithCodeMsg(common.USER_NOT_EXIST, common.GetMapInfo(common.USER_NOT_EXIST)))
		return
	}
	//校验登录密码是否和数据库一致
	isPasswordMatch := util.ComparePasswd(user.Password, password)
	if !isPasswordMatch {
		core.LOG.Println("用户登录:用户密码输入错误")
		c.JSON(http.StatusOK, common.FailWithCodeMsg(common.USER_PASSWORD_NOT_MATCHED,
			common.GetMapInfo(common.USER_PASSWORD_NOT_MATCHED)))
		return
	}
	// TODO:生成token
	token, err := util.GenerateToken(strconv.Itoa(int(user.ID)), user.Username)
	if err != nil {
		core.LOG.Println("用户登录:生成token失败")
		c.JSON(http.StatusOK, common.FailWithCodeMsg(common.AUTHORIZATION_FAIL, "生成token失败"))
		return
	}
	c.Header(global.Authorization, token)

	// 写入redis
	redisToken := fmt.Sprintf("user-token-%s", userName)
	isOk := core.Redis.SetEX(redisToken, token, 7*24*time.Hour)
	if isOk == false {
		core.LOG.Println("用户登录:设置值到redis")
		c.JSON(http.StatusOK, common.FailWithCodeMsg(common.REDIS_SET_FAIL, "设置值到Redis失败"))
		return
	}
	userVo := vo.ConvertToUserResVo(user)
	c.Header(global.Authorization, token)
	c.JSON(http.StatusOK, gin.H{
		"token": token,
		"user":  userVo,
	})

}

// GetUserByUsername 根据用户名查询用户
//
//	@Summary	用户登录
//	@Produce	json
//	@Router		/api/user/getUserByName [get]
//	@Param		username	query	string	true	"用户名"	maxlength(20)
func GetUserByUsername(c *gin.Context) {
	userName := c.Query("username")
	user := dao.GetUserByUsername(userName)
	if user.Username == "" {
		c.JSON(http.StatusOK, common.FailWithMsg("未查询到该用户"))
		return
	}
	userVo := vo.ConvertToUserResVo(user)
	c.JSON(http.StatusOK, common.OkWithData(userVo))
}

// GetAllUser 查询所有的用户
//
//	@Summary	查询所有的用户
//	@Produce	json
//	@Router		/api/user/getAllUser [get]
//	@Param		pagenum		query	int	true	"页数"
//	@Param		pagesize	query	int	true	"页面大小"
func GetAllUser(c *gin.Context) {
	pageNum, _ := strconv.Atoi(c.Query("pagenum"))
	pageSize, _ := strconv.Atoi(c.Query("pagesize"))
	core.LOG.Printf("pagenum: %d, pagesize: %d\n", pageNum, pageSize)
	if pageNum <= 0 {
		pageNum = 0
	}
	if pageSize <= 0 {
		pageSize = 1
	}
	users, total := dao.GetUser(pageNum, pageSize)

	c.JSON(http.StatusOK, common.OkWithData(common.NewPageRes(users, total)))
}

router配置

package middleware

import (
	"code-go/app/admin/api"
	_ "code-go/docs"
	"github.com/gin-gonic/gin"
	swaggerFiles "github.com/swaggo/files"     // swagger embed files
	ginSwagger "github.com/swaggo/gin-swagger" // gin-swagger middleware
)

// InitRouter 初始化Router
func InitRouter() *gin.Engine {
	g := gin.New()
	g.Use(gin.Recovery())
	g.Use(Logger())
	g.Use(Cors())

	// 需授权
	auth := g.Group("/api")
	{
		auth.GET("/user/getAllUser", api.GetAllUser)
		auth.POST("/user/register", api.Register)
		auth.POST("/user/login", api.Login)
		auth.GET("/user/getUserByName", api.GetUserByUsername)
	}

	// 无需授权
	norm := g.Group("/")
	{
		norm.GET("/getIp", api.GetIp)
		norm.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

		norm.GET("/captcha", api.GetCaptcha)
		norm.POST("/checkCaptcha", api.CheckCaptcha)
	}
	return g
}

测试

使用swagger测试如下,可以确认登录成功,且有token生成
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

Hadoop运行搭建——系统配置和Hadoop的安装

Hadoop运行搭建 前言&#xff1a; 本文原文发在我自己的博客小站&#xff0c;直接复制文本过来&#xff0c;所以图片不显示(我还是太懒啦&#xff01;)想看带图版的请移步我的博客小站~ Linux镜像&#xff1a;CentOS7 系统安装&#xff1a;CentOS安装参考教程 系统网卡设置…

在win10中下载桌面版的docker并在docker中搭建运行基于linux的容器

在win10中下载桌面版的docker 1.背景 在很多时候需要linux系统部署项目&#xff0c;在win10中安装虚拟机并在虚拟机中安装linux系统比较繁琐&#xff0c;可以利用win10自带的hyper-v的虚拟机管理工具&#xff0c;打开该虚拟机管理工具&#xff0c;安装docker&#xff0c;并在…

【排序】希尔排序

一、思想 希尔排序&#xff0c;由D.L. Shell于1959年提出&#xff0c;是基于插入排序的一种改进算法。它的主要思想是将待排序的序列分割成若干个子序列&#xff0c;这些子序列的元素是相隔一定“增量”的。然后对每个子序列进行直接插入排序。随着增量的逐步减小&#xff0c;…

数学建模【整数规划】

一、整数规划简介 整数规划其实是线性规划和非线性规划的一个特殊情况&#xff0c;即有的变量取值只能是整数&#xff0c;不能是小数。这时候就需要一个新的函数来解决问题。 对于整数规划&#xff0c;分为线性整数规划和非线性整数规划 线性整数规划&#xff1a;MATLAB可进…

gitlab仓库迁移至bitbucket

0. 场景描述 假设已有一个gitlab仓库&#xff1a;ssh://xxx_origin.git&#xff0c;想要把这个仓库迁移至bitbucket上。 默认gitlab和bitbucket的SSH key都已添加。 1. 新建bitbucket仓库 在bitbucket上创建新的仓库&#xff0c;并复制url地址。假设为&#xff1a; https:/…

Leetcode 26. 删除有序数组中的重复项 java版。 java解决删除重复数组元素并输出长度

1. 官网链接&#xff1a; . - 力扣&#xff08;LeetCode&#xff09; 2. 题目描述&#xff1a; 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该…

一个数据库表格缺少自动增加的字段导致添加一条数据失败

一个数据库表格缺少自动增加的字段导致添加一条数据失败。最近要整理出一个cms网站源程序&#xff0c;因此新建了一个目录&#xff0c;将需要的文件复制到该目录。复制好以后&#xff0c;试用的时候发现添加留言失败。经过数小时的查找原因&#xff0c;最后找到原因&#xff0c…

修复通达OA 百度ueditor 文件上传漏动

前些日子&#xff0c;服务器阿里云监控报警&#xff0c;有文件木马文件&#xff0c;因为非常忙&#xff0c;就没及时处理&#xff0c;直接删除了木马文件了事。 谁知&#xff0c;这几天对方又上传了木马文件。好家伙&#xff0c;今天不花点时间修复下&#xff0c;你都传上瘾了…

苍穹外卖学习-----2024/03/04

1.公共字段填充 代码在这里

【python--读取csv文件统计店铺有关信息】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;Python &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; python练习题 读取csv文件统计店铺有关信息 读取csv文件统计店铺有关信息 import csv import osdef get_…

L1-096 谁管谁叫爹分数

L1-096 谁管谁叫爹 分数 20 全屏浏览 切换布局 作者 陈越 单位 浙江大学 《咱俩谁管谁叫爹》是网上一首搞笑饶舌歌曲&#xff0c;来源于东北酒桌上的助兴游戏。现在我们把这个游戏的难度拔高一点&#xff0c;多耗一些智商。 不妨设游戏中的两个人为 A 和 B。游戏开始后&…

Android开发者值得深入思考的几个问题,腾讯T3大佬亲自教你

前言 众所周知&#xff0c;移动开发已经来到了后半场&#xff0c;为了能够在众多开发者中脱颖而出&#xff0c;我们需要对某一个领域有深入地研究与心得&#xff0c;对于Android开发者来说&#xff0c;目前&#xff0c;有几个好的细分领域值得我们去建立自己的技术壁垒&#x…

LLM 大模型框架 LangChain 可观测性最佳实践

LLM&#xff08;Large Language Model&#xff09;大模型的可观测性是指对模型内部运行过程的理解和监控能力。由于LLM大模型通常具有庞大的参数量和复杂的网络结构&#xff0c;因此对其内部状态和运行过程的理解和监控是一个重要的问题。 什么是 LangChain&#xff1f; Lang…

回溯算法02-组合总合III(Java)

2.组合总合III 题目描述 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 示例 1: 输入: k 3,…

simplex算法的代码实现

def pivot(N, B, A, b, c, v, l, e):N对应非基本元变量下标,B对应基本元变量下标,A对应非基本元在约束方程组中的系数相反数形成的矩阵,b对应约束条件中小于等于号右边的数值集合,c对应目标函数中变量系数形成的集合,v对应当前目标函数的取值,l对应转出变量下标在B中的位置&am…

nodejs版本管理工具nvm安装和环境变量配置

1、下载nvm.exe https://github.com/coreybutler/nvm-windows/releases2、安装 1.在D盘根目录新建一个dev文件夹&#xff0c;在dev里面再新建一个nodejs。 2.双击下载好的nvm.exe 修改文件路径&#xff0c;且路径中不能有中文 3.安装完成后在D:\dev\nvm打开settings.txt&…

C++写食堂菜品管理系统

说明:本博文来自CSDN-问答板块,题主提问。 需要:学校拟开发一套食堂菜品管理系统,以便对菜品和同学们的评价进行管理,其中包含如下信息: 商户:商户名称、柜面位置、电话…… 菜品:菜品编号、菜品名称、价格、所属商户…… 学生:注册账号、昵称、电话…… 食堂里的商户…

因果学习篇(2)-Causal Attention for Vision-Language Tasks(文献阅读)

Causal Attention for Vision-Language Tasks 引言 这篇论文是南洋理工大学和澳大利亚莫纳什大学联合发表自2021年的CVPR顶会上的一篇文献&#xff0c;在当前流行的注意力机制中增加了因果推理算法&#xff0c;提出了一种新的注意力机制&#xff1a;因果注意力(CATT)&#xff…

【四】【SQL Server】如何运用SQL Server中查询设计器通关数据库期末查询大题

数据库学生选择1122 数据库展示 course表展示 SC表展示 student表展示 数据库学生选课1122_3 第十一题 第十二题 第十三题 第十四题 第十五题 数据库学生选课1122_4 第十六题 第十七题 第十八题 第十九题 第二十题 数据库学生选课1122_5 第二十一题 第二十二题 结尾 最后&…

Snap7 PLC使用

文章目录 前言一、Snap7是什么&#xff1f;1. 官网下载2.下载后找到关键lib和.h文件 二、使用步骤1.新建一个Qt的工程2.读入数据 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 项目需要&#xff1a; 提示&#xff1a;以下是本篇文章正文内容&…