13. 用户注册功能实现

文章目录

  • 一 、增加路由
  • 二、书写流程控制(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 controllerimport ("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 controllertype ResCode int64const (CodeSuccess ResCode = 1000 + iotaCodeInvalidParamCodeUserExistCodeUserNotExistCodeInvalidPasswordCodeServerBusyCodeNeedLoginCodeInvalidToken
)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 controllerimport ("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 modelsimport "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.生成UIDuserID := 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 mysqlimport "errors"var (ErrorUserExist       = errors.New("用户已存在")ErrorUserNotExist    = errors.New("用户不存在")ErrorInvalidPassword = errors.New("用户名或密码错误")ErrorInvalidID       = errors.New("无效的ID")
)

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

dao/mysql/user.go

package mysqlimport ("bluebell/models""crypto/md5""encoding/hex"
)// 把每一步数据库操作封装成函数
// 待logic层根据业务需求调用const secret = "lym.com"// CheckUserExist 检查指定用户名的用户是否存在
func CheckUserExist(username string) (err error) {var count int64if 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).Errorreturn
}// 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.mzph.cn/news/733294.shtml

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

相关文章

【java】22:throws 异常处理

基本介绍 1)如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。 2)在方法声明中用throw…

Linux - 进程信号

1、信号入门 1.1、生活角度的信号 你在网上买了很多件商品,再等待不同商品快递的到来。但即便快递没有到来,你也知道快递来临时, 你该怎么处理快递。也就是你能“识别快递”;当快递员到了你楼下,你也收到快递到来的通…

Pytest+Selenium UI自动化测试实战实例(全)

🍅 视频学习:文末有免费的配套视频可观看 🍅 关注公众号【互联网杂货铺】,回复 1 ,免费获取软件测试全套资料,资料在手,涨薪更快 今天来说说pytest吧,经过几周的时间学习&#xff0c…

Redis实现用户活跃排行榜

在这里用户活跃度排行榜,主要是基于redis的zset数据结构来实现的,下面来看一下实例。 方案设计 来看一下业务场景先 1.场景说明 在技术派中,提供了一个用户的活跃排行榜,当然作为一个博客社区,更应该实现的是作者排…

连号区间数 刷题笔记

1.单个元素算一个连续区间 2.题意为 单独截取数组中的一段元素 例如 a数组假设为 3 1 2 5 4 6 7 假设取 a[3]-a[5] 则取出 5 4 6 重新排序后为 4 5 6 连续了 则ans; 假设 取a[i]-a[j]这一段元素 设该段元素的最大值为max,最小值为min 如果该段元素重新排序后…

Learn OpenGL 04 纹理

纹理环绕方式 纹理坐标的范围通常是从(0, 0)到(1, 1),那如果我们把纹理坐标设置在范围之外会发生什么?OpenGL默认的行为是重复这个纹理图像(我们基本上忽略浮点纹理坐标的整数部分),但OpenGL提供了更多的选择&#xf…

LCR 131. 砍竹子 I

解题思路:(与砍竹子II的区别是,这里的竹子长度数量级较小) 数学推导或贪心 切分规则: 等长,且尽量为3 b0时,pow(3,a) b1时,pow(3,a-1)*4 少一段3,并入b生成一…

YUNBEE云贝:3月9日-PostgreSQL中级工程师PGCE认证培训

课程介绍 根据学员建议和市场需求,规划和设计了《PostgreSQL CE 认证课程》,本课程以内部原理、实践实战为主,理论与实践相结合。课程包含PG 简介、安装使用、服务管理、体系结构等基础知识。同时结合一线实战案例, 面向 PG 数据库的日常维护管理、服务和…

Vue | 基于 vue-admin-template 项目的跨域问题解决方法

目录 一、现存问题 二、解决方法 2.1 修改的第一个地方 2.2 修改的第二个地方 2.3 修改的第三个地方 自存 一、现存问题 报错截图如下: 二、解决方法 2.1 修改的第一个地方 在 .env.development 文件中: # base api # VUE_APP_BASE_API /d…

springboot整合shiro的实战教程(一)

文章目录 1.权限的管理1.1 什么是权限管理1.2 什么是身份认证1.3 什么是授权 2.什么是shiro3.shiro的核心架构3.1 Subject3.2 SecurityManager3.3 Authenticator3.4 Authorizer3.5 Realm3.6 SessionManager3.7 SessionDAO3.8 CacheManager3.9 Cryptography 4. shiro中的认证4.1…

我的 4096 创作纪念日

作者:明明如月学长, CSDN 博客专家,大厂高级 Java 工程师,《性能优化方法论》作者、《解锁大厂思维:剖析《阿里巴巴Java开发手册》》、《再学经典:《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

YOLOv8+DeepSort/ByteTrack-PyQt-GUI / yolov5 deepsort 行人/车辆(检测 +计数+跟踪+测距+测速)

YoloV8结合可视化界面和GUI,实现了交互式目标检测与跟踪,为用户提供了一体化的视觉分析解决方案。通过YoloV8算法,该系统能够高效准确地检测各类目标,并实时跟踪它们的运动轨迹。 用户可以通过直观的可视化界面进行操作&#xff…

Unity性能优化篇(七) UI优化注意事项以及使用Sprite Atlas打包精灵图集

UI优化注意事项 1.尽量避免使用IMGUI(OnGUI)来做游戏时的UI,因为IMGUI的开销比较大。 2.如果一个UGUI的控件不需要进行射线检测,则可以取消勾选Raycast Target 3.尽量避免使用完全透明的图片和UI控件。因为即使完全透明,我们看不见它&#xf…

常见BUG如何在测试过程中分析定位

前言 在测试的日常工作中,相信经常有测试的小伙伴遇到类似的情况:在项目上线时,只要出现问题(bug),就很容易成为“背锅侠”。 软件测试人员在工作中是无法避免的要和开发人员和产品经理打交道的&#xff…

117.龙芯2k1000-pmon(16)- linux下升级pmon

pmon的升级总是有些不方便,至少是要借助串口和串口工具 如果现场不方便连接串口,是不是可以使用网线升级pmon呢? 答案当然是可行的。 环境:2k1000linux3.10麒麟的文件系统 如今我已经把这个工具开发出来了。 GitHub - zhaozhi…

网络工程师笔记10 ( RIP / OSPF协议 )

RIP 学习路由信息的时候需要配认证 RIP规定超过15跳认定网络不可达 链路状态路由协议-OSPF 1. 产生lsa 2. 生成LSDB数据库 3. 进行spf算法,生成最有最短路径 4. 得出路由表

【探索C++容器:set和map的使用】

[本节目标] 1. 关联式容器 2. 键值对 3. 树形结构的关联式容器 1. 关联式容器 在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、forward_list(C11)等,这些容器统称为序列式容器,因为其底层为…

Toyota Programming Contest 2024#3(AtCoder Beginner Contest 344)(A~C)

A - Spoiler 竖线里面的不要输出&#xff0c;竖线只有一对&#xff0c;且出现一次。 #include <bits/stdc.h> //#define int long long #define per(i,j,k) for(int (i)(j);(i)<(k);(i)) #define rep(i,j,k) for(int (i)(j);(i)>(k);--(i)) #define debug(a) cou…

链表|面试题 02.07.链表相交

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListNode *l NULL, *s NULL;int lenA 0, lenB 0, gap 0;// 求出两个链表的长度s headA;while (s) {lenA ;s s->next;}s headB;while (s) {lenB ;s s->next;}// 求出两个链表长度差if (lenA &…

stm32学习笔记:SPI通信协议原理(未完)

一、SPI简介(serial Peripheral Interface&#xff08;串行 外设 接口&#xff09;) 1、电路模式&#xff08;采用一主多从的模式&#xff09;、同步&#xff0c;全双工 1 所有SPI设备的SCK、MOSI、MISO分别连在一起 2 主机另外引出多条SS控制线&#xff0c;分别接到各从机的S…