13. 用户注册功能实现

news2025/6/12 20:43:27

文章目录

  • 一 、增加路由
  • 二、书写流程控制(controller)逻辑
  • 三、书写业务逻辑
  • 四、与DB交互
  • 五、测试

代码地址:https://gitee.com/lymgoforIT/bluebell

一 、增加路由

添加路由,使用分组管理

v1 := r.Group("/api/v1")

// 注册
v1.POST("/signup", controller.SignUpHandler)

由于我们的项目是非常简单的上入门项目,所以并不会用到DDD等复杂的目录设计和管理,而是用了最基本的MVC三层结构。

  • controller目录用于接收路由,进行基本的参数校验,不做业务逻辑处理。
  • logic目录用于处理业务逻辑。
  • dao目录用于用数据源交互。

绿色即为本次注册功能需要新增的代码文件

在这里插入图片描述

二、书写流程控制(controller)逻辑

controller/user.go user.go文件主要负责用户的注册和登录。 这里首先写注册逻辑,后续的登录逻辑也会在这个文件中实现。

package controller

import (
	"bluebell/models"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"go.uber.org/zap"
)

// SignUpHandler 处理注册请求的函数
func SignUpHandler(c *gin.Context) {
	// 1. 获取参数和参数校验
	p := new(models.ParamSignUp)
	if err := c.ShouldBindJSON(p); err != nil {
		// 请求参数有误,直接返回响应
		zap.L().Error("SignUp with invalid param", zap.Error(err))
		// 判断err是不是validator.ValidationErrors 类型
		errs, ok := err.(validator.ValidationErrors)
		if !ok {
			ResponseError(c, CodeInvalidParam)
			return
		}
		ResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))
		return
	}
	// 2. 业务处理
	if err := logic.SignUp(p); err != nil {
		zap.L().Error("logic.SignUp failed", zap.Error(err))
		if errors.Is(err, mysql.ErrorUserExist) {
			ResponseError(c, CodeUserExist)
			return
		}
		ResponseError(c, CodeServerBusy)
		return
	}
	// 3. 返回响应
	ResponseSuccess(c, nil)
}

代码说明:

  1. controller层不做业务逻辑,主要就是组织整体流程的,如参数校验,业务处理,返回响应。
  2. 返回结果比较通用,所以封装工具函数更为简介、易读。
  3. 适当的定义一些错误码是不错的选择。

定义状态码并封装响应方法

controller/code.go

package controller

type ResCode int64

const (
	CodeSuccess ResCode = 1000 + iota
	CodeInvalidParam
	CodeUserExist
	CodeUserNotExist
	CodeInvalidPassword
	CodeServerBusy

	CodeNeedLogin
	CodeInvalidToken
)

var codeMsgMap = map[ResCode]string{
	CodeSuccess:         "success",
	CodeInvalidParam:    "请求参数错误",
	CodeUserExist:       "用户名已存在",
	CodeUserNotExist:    "用户名不存在",
	CodeInvalidPassword: "用户名或密码错误",
	CodeServerBusy:      "服务繁忙",

	CodeNeedLogin:    "需要登录",
	CodeInvalidToken: "无效的token",
}

func (c ResCode) Msg() string {
	msg, ok := codeMsgMap[c]
	if !ok {
		msg = codeMsgMap[CodeServerBusy]
	}
	return msg
}

controller/response.go

package controller

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

/*
{
	"code": 10000, // 程序中的错误码
	"msg": xx,     // 提示信息
	"data": {},    // 数据
}

*/

type ResponseData struct {
	Code ResCode     `json:"code"`
	Msg  interface{} `json:"msg"`
	Data interface{} `json:"data,omitempty"`
}

func ResponseError(c *gin.Context, code ResCode) {
	c.JSON(http.StatusOK, &ResponseData{
		Code: code,
		Msg:  code.Msg(),
		Data: nil,
	})
}

func ResponseErrorWithMsg(c *gin.Context, code ResCode, msg interface{}) {
	c.JSON(http.StatusOK, &ResponseData{
		Code: code,
		Msg:  msg,
		Data: nil,
	})
}

func ResponseSuccess(c *gin.Context, data interface{}) {
	c.JSON(http.StatusOK, &ResponseData{
		Code: CodeSuccess,
		Msg:  CodeSuccess.Msg(),
		Data: data,
	})
}

三、书写业务逻辑

首先,我们需要查询DB中要注册的用户名是否已经存在,所以需要先提供一个可以和DB交互的model,user

models/user.go

提示:可以直接SQL转对应的结构体

在这里插入图片描述

package models

import "time"

type User struct {
	ID         int64     `gorm:"column:id" db:"id" json:"id" form:"id"`
	UserId     int64     `gorm:"column:user_id" db:"user_id" json:"user_id" form:"user_id"`
	Username   string    `gorm:"column:username" db:"username" json:"username" form:"username"`
	Password   string    `gorm:"column:password" db:"password" json:"password" form:"password"`
	Email      string    `gorm:"column:email" db:"email" json:"email" form:"email"`
	Gender     int64     `gorm:"column:gender" db:"gender" json:"gender" form:"gender"`
	CreateTime time.Time `gorm:"column:create_time" db:"create_time" json:"create_time" form:"create_time"`
	UpdateTime time.Time `gorm:"column:update_time" db:"update_time" json:"update_time" form:"update_time"`
}

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

logic/user.go
负责校验用户名是否已经存在,若不存在,则生成唯一的用户ID,注册用户

import (
	"bluebell/models"
	"bluebell/pkg/snowflake"
)

func SignUp(p *models.ParamSignUp) (err error) {
	// 1.判断用户存不存在
	if err := mysql.CheckUserExist(p.Username); err != nil {
		return err
	}
	// 2.生成UID
	userID := snowflake.GenID()
	// 构造一个User实例
	user := &models.User{
		UserID:   userID,
		Username: p.Username,
		Password: p.Password,
	}
	// 3.保存进数据库
	return mysql.InsertUser(user)
}

四、与DB交互

定义与DB交互时可能出现的相关错误码

dao/mysql/error_code.go

package mysql

import "errors"

var (
	ErrorUserExist       = errors.New("用户已存在")
	ErrorUserNotExist    = errors.New("用户不存在")
	ErrorInvalidPassword = errors.New("用户名或密码错误")
	ErrorInvalidID       = errors.New("无效的ID")
)

校验用户名是否已经存在、对密码加密、保存新注册的用户

dao/mysql/user.go

package mysql

import (
	"bluebell/models"
	"crypto/md5"
	"encoding/hex"
)

// 把每一步数据库操作封装成函数
// 待logic层根据业务需求调用

const secret = "lym.com"

// CheckUserExist 检查指定用户名的用户是否存在
func CheckUserExist(username string) (err error) {
	var count int64
	if err := db.Model(models.User{}).Where("username = ?", username).Count(&count).Error; err != nil {
		return err
	}
	if count > 0 {
		return ErrorUserExist
	}
	return
}

// InsertUser 想数据库中插入一条新的用户记录
func InsertUser(user *models.User) (err error) {
	// 对密码进行加密
	user.Password = encryptPassword(user.Password)

	// 执行SQL语句入库
	u := &models.User{
		UserId:   user.UserId,
		Username: user.Username,
		Password: user.Password,
	}
	err = db.Create(&u).Error

	return
}

// encryptPassword 密码加密
func encryptPassword(oPassword string) string {
	h := md5.New()
	h.Write([]byte(secret))
	return hex.EncodeToString(h.Sum([]byte(oPassword)))
}

五、测试

编译

go build

运行

 ./bluebell.exe ./config/config.yaml

注册,因为lym已经存在,所以会返回用户名已存在
在这里插入图片描述
注册一个不存在的名字试试,成功!
在这里插入图片描述

查询DB,注册成功,且密码是经过MD5加密过的(实际就是123)
在这里插入图片描述

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

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

相关文章

运动想象 (MI) 迁移学习系列 (3) : MSFT

运动想象迁移学习系列:MSFT 0. 引言1. 主要贡献2. 数据增强方法3. 基于度量的空间滤波转换器3.1 空间过滤3.2 脑电图ViT3.2.1 变压器编码器层3.2.2 基于度量的损失函数 4. 实验结果4.1 消融实验4.2 基线任务对比4.3 跨主题 5. 总结欢迎来稿 论文地址:https://www.s…

C# WinForm AndtUI第三方库 Table控件使用记录

环境搭建 1.在NuGet中搜索AndtUI并下载至C# .NetFramework WinForm项目。 2.添加Table控件至窗体。 使用方法集合 1.单元格点击事件 获取被点击记录特定列内容 private void dgv_CellClick(object sender, MouseEventArgs args, object record, int rowIndex, int columnIn…

javascript中的严格模式(use strict)

下面是一个使用JavaScript严格模式的代码示例。在这个示例中,我将展示如何在整个脚本中启用严格模式,并演示一些严格模式下可能遇到的问题。 "use strict"; a 1; // Uncaught ReferenceError: a is not defined // 尝试使用一个未声明的变量…

中国钛合金自行车出口海外营销策略-大舍传媒

中国是世界上自行车产量最大的国家之一,而如今,它也在自行车制造领域迈出了一大步。Titan Super Bond与Bright Laser Technologies (BLT)合作,成功开发了中国和亚洲首款全3D打印钛合金自行车车架。 这款创新的自行车车架采用了BLT-A320机器&a…

bat文件给多个Android设备安装apk

本文是安装一个apk 1、确保以下3个文件在同一个目录下 1>要安装的apk,这里是mmb.apk 2>设备名单,保存在.txt文件中,一行一个设备名,设备名通过adb devices获取,截图中是两个设备 txt文件中的样式 3>要运行…

二、实战篇 商户查询缓存

源码仓库地址:gitgitee.com:chuangchuang-liu/hm-dingping.git 1、什么是缓存? 缓存(Cache),就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码 1.1、为什么要使用缓存? 添加缓存后,重复的…

icp许可证年报入口在哪?icp许可证年报流程详细介绍

近期拥有ICP许可证的企业负责人都会收到各地方通管局下发的年报通知,需要在每年的1-3月份报送年报信息,最晚报送时间是2024年3月31日。 对于刚申请或者刚接触这方面的朋友来说,可能连icp许可证年报入口在哪都不知道,更不用说后面…

OpenStack之keystone(用户认证)

Keystone(认证) Keystone 概述 1)管理用户及其权限 2)维护OpenStack Services 的 Endpoint 3)Authentication(认证)和 Authorization(授权) keystone的名词概念 1.User(用户或服务&#xf…

springboot256基于springboot+vue的游戏交易系统

游戏交易系统设计与实现 摘 要 在如今社会上,关于信息上面的处理,没有任何一个企业或者个人会忽视,如何让信息急速传递,并且归档储存查询,采用之前的纸张记录模式已经不符合当前使用要求了。所以,对游戏交…

制定一份完美的测试计划,让您的产品质量更上一层楼!

大家好,我是彭于晏。今天学习测试计划如何书写。 虽然很多人日常工作中都知道测试计划是什么,但是写好测试计划,其实并不容易。今天就来一起学习下测试计划如何书写。 什么是测试计划? 测试计划是一份为软件产品所准备的详细文档…

揭秘接口测试:完整流程指南!

在讲接口测试之前,首先需要给大家申明下:接口测试对于测试人员而言,非常非常重要,懂功能测试接口测试,就能在企业中拿到一份非常不错的薪资。 这么重要的接口测试,一般也是面试笔试必问。为方便大家更好的…

Qt 实现诈金花的牌面值分析工具

诈金花是很多男人最爱的卡牌游戏 , 每当你拿到三张牌的时候, 生活重新充满了期待和鸟语花香. 那么我们如果判断手中的牌在所有可能出现的牌中占据的百分比位置呢. 这是最终效果: 这是更多的结果: 在此做些简单的说明: 炸弹(有些地方叫豹子) > 同花顺 > 同花 > 顺…

【Web】浅聊Java反序列化之Rome——关于其他利用链

目录 前言 JdbcRowSetImpl利用链 BasicDataSource利用链 Hashtable利用链 BadAttributeValueExpException利用链 HotSwappableTargetSource利用链 前文:【Web】浅聊Java反序列化之Rome——EqualsBean&ObjectBean-CSDN博客 前言 Rome中ToStringBean的利用…

ElasticSearch 底层读写原理

ElasticSearch 底层读写原理 ​ 写请求是写入 primary shard,然后同步给所有的 replica shard;读请求可以从 primary shard 或 replica shard 读取,采用的是随机轮询算法。 1、ES写入数据的过程 1.选择任意一个DataNode发送请求&#xff0c…

山泉还可以申请商标不,现阶段通过率如何!

在32类类别啤酒饮料是许多生产水企业主要申请注册的类别,那现在山泉在这个类别还可以申请注册商标不,山泉在这个类别基本上是通用词,首先是需要前面词具有显著性,没的相同或近似才可以。 经普推知产老杨检索发现,在32…

数据分析-Pandas数据画箱线图

数据分析-Pandas数据画箱线图 数据分析和处理中,难免会遇到各种数据,那么数据呈现怎样的规律呢?不管金融数据,风控数据,营销数据等等,莫不如此。如何通过图示展示数据的规律? 数据表&#xff…

全网最最最详细centos7如何安装docker教程

在CentOS 7上安装Docker主要包括以下步骤: 1. 卸载旧版本的Docker 首先,需要确保系统上没有安装旧版本的Docker。可以通过以下命令来卸载它们: sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-late…

阿珊详解Vue Router的守卫机制

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

nginx代理minio客户端

错误方式 在点击桶名查看文件时, 会一直处于loading加载中 worker_processes 1; #设置 Nginx 启动的工作进程数为 1。events {worker_connections 1024; ##设置每个工作进程的最大并发连接数为 1024。 }http {include mime.types; #该文件定义了文件扩展名和 MIME 类型…

AJAX学习(一)

版权声明 本文章来源于B站上的某马课程,由本人整理,仅供学习交流使用。如涉及侵权问题,请立即与本人联系,本人将积极配合删除相关内容。感谢理解和支持,本人致力于维护原创作品的权益,共同营造一个尊重知识…