设计结构与代码优化(二)书接上文,继续聊一些代码,顺便过滤一下基础知识,基础学的一团糟

关于web/目录代码的二三事

又是一长串的代码,感觉说不用代码解释东西,就都差一点意思

// package 的声明,这个包叫web
package web

import (
	"errors"
	"net/http"

	"git.com/gin_basic/webook/internal/domain"
	"git.com/gin_basic/webook/internal/service"
	regexp "github.com/dlclark/regexp2"
	"github.com/gin-gonic/gin"
)

const (
	emailRegexPattern = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"
	// 和上面比起来,用 ` 看起来就比较清爽
	passwordRegexPattern = `^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&.])[A-Za-z\d$@$!%*#?&.]{8,72}$`
)

// UserHandlers 处理用户相关请求的结构体
type UserHandlers struct {
	emailRegExp    *regexp.Regexp
	passwordRegExp *regexp.Regexp
	svc            *service.UserService
}

// RegisterRoutes 注册用户相关路由
// 该方法用于将用户操作的路由绑定到对应的处理函数
func NewUserHandlers(svc *service.UserService) *UserHandlers {
	return &UserHandlers{
		emailRegExp:    regexp.MustCompile(emailRegexPattern, regexp.None),
		passwordRegExp: regexp.MustCompile(passwordRegexPattern, regexp.None),
		svc:            svc,
	}

}
func (h *UserHandlers) RegisterRoutes(routers *gin.Engine) {
	// 用户路由组
	rg := routers.Group("/users")
	// 注册路由
	rg.POST("/signup", h.SignUp)
	// 登录路由
	rg.POST("/sign", h.Login)
	// 用户资料路由
	rg.GET("/profile", h.Profile)
	// 编辑资料路由
	rg.POST("/edit", h.Edit)
}

// Signup 处理用户注册请求
// 该方法用于处理用户注册流程,包括验证数据,创建用户等
// 它接收一个 gin.Context 类型的参数,用于获取请求数据和发送响应
// 定义几个方法,包含注册、登录、编辑、查看
func (h *UserHandlers) SignUp(ctx *gin.Context) {
	// 注册
	// 字段标签,固定接收json格式,内部类
	type SignUpReq struct {
		Email           string `json:"email"`
		Password        string `json:"password"`
		ConfirmPassword string `json:"confirmPassword"`
	}
	//Bind 方法,类似是格式校验的一个方法,如果格式不是上面SignUp设置的格式就会报错。
	var req SignUpReq
	if err := ctx.Bind(&req); err != nil {
		ctx.String(200, "输入的格式不合法")
		return
	}
	// 邮箱格式校验
	isEmail, err := h.emailRegExp.MatchString(req.Email)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误")
		return
	}
	if !isEmail {
		ctx.String(http.StatusOK, "邮箱式不对")
		return
	}
	// 密码格式校验
	isPassword, err := h.passwordRegExp.MatchString(req.Password)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误")
		return
	}
	if !isPassword {
		ctx.String(http.StatusOK, "密码格式不对,密码长度应该不小于8位,且包含特殊符号")
		return
	}
	// 确认密码校验
	if req.Password != req.ConfirmPassword {
		ctx.String(200, "两次输入密码不一致")
		return
	}
	err = h.svc.SignUp(ctx, domain.User{
		Email:    req.Email,
		Password: req.Password,
	})
	switch {
	case err == nil:
		ctx.String(http.StatusOK, "注册成功,")
	case errors.Is(err, service.ErrDuplicateEmail):
		ctx.String(http.StatusOK, "邮箱冲突,请换一个")
	default:
		ctx.String(http.StatusOK, "系统错误")
	}

	ctx.String(http.StatusOK, "你已经完成注册")
}

// Login 处理用户登录请求
// 该方法用于处理用户登录流程,包括验证身份,生成会话等
// 它接收一个 gin.Context 类型的参数,用于获取请求数据和发送响应
// 定义几个方法,包含注册、登录、编辑、查看
func (h *UserHandlers) Login(ctx *gin.Context) {
	type LoginReq struct {
		Email    string `json:"email"`
		Password string `json:"password"`
	}
	//Bind 方法,类似是格式校验的一个方法,如果格式不是上面SignUp设置的格式就会报错。
	var req LoginReq
	if err := ctx.Bind(&req); err != nil {
		return
	}
	_, err := h.svc.Login(ctx, req.Email, req.Password)
	switch {
	case err == nil:
		ctx.String(http.StatusOK, "登录成功")
	case errors.Is(err, service.ErrInvalidUserOrPassword):
		ctx.String(http.StatusOK, "用户名称或者密码不对")
	default:
		ctx.String(http.StatusOK, "系统错误")
	}
}

// Profile 处理用户资料查询请求
// 该方法用于处理用户查询自己资料的请求
// 它接收一个 gin.Context 类型的参数,用于获取请求数据和发送响应
func (h *UserHandlers) Profile(ctx *gin.Context) {
	// 查看
}

// Edit 处理用户资料编辑请求
// 该方法用于处理用户编辑自己资料的请求
// 它接收一个 gin.Context 类型的参数,用于获取请求数据和发送响应
func (h *UserHandlers) Edit(ctx *gin.Context) {
	// 编辑
}

代码的具体解释(仅限于个人理解)
【开篇:第一段代码】

package web
//定义了包的名称:web
import (
// import 里面是引用的内容
	"errors"
	"net/http"
	// git.com 这部分是程序内部的引用,git.com/gon_basic这是相对路径;
	"git.com/gin_basic/webook/internal/domain"
	"git.com/gin_basic/webook/internal/service"
	// regexp 是引用的github里面的regexp2的别名,引用包的时候前面不需要带http字样
	// 除了别名以外还有 "_" 前面的这种方式引用,比如数据库的连接
	regexp "github.com/dlclark/regexp2"
	"github.com/gin-gonic/gin"
)

【第二段:常量的定义】

// 这部分是定义常量,关键字是const,里面的是一组正则表达式。
//这部分是邮箱和密码格式的定义,密码要求必须是8位及以上但是要小于72位,然后要求带有大小写字母特殊符号等
//代码里面引用的regexp是对密码格式的校验,由于gin本身自带的regexp不支持"?=."这种格式,所以引用了regexp2,设置别名为regexp
const (
	emailRegexPattern = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"
	// 和上面比起来,用 ` 看起来就比较清爽
	passwordRegexPattern = `^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&.])[A-Za-z\d$@$!%*#?&.]{8,72}$`
)

【第三段:结构体的定义】

//这里定义了一个叫 UserHandlers 的结构体,里面包含了如下几个字段。emailRegExp、passwordRegExp 这两个是指向regexp 的指针,用于邮箱和密码的校验,svc是指向用户服务的指针。
type UserHandlers struct {
	emailRegExp    *regexp.Regexp
	passwordRegExp *regexp.Regexp
	svc            *service.UserService
}

3.1 关于结构体的创建(极简的那种)
我们在GoLand 上面输入关键字type,然后下面就会给出提示,按需自建即可
在这里插入图片描述
3.2 关于指针(这个就很迷糊)
大概理解就是字段前面带星号的就是指针例如(*regexp.Regexp)这种,具体的解释可以看我下面灵码的内容

指针的定义
指针是一个变量,其值是另一个变量的内存地址。在 Go 中,你可以声明一个指针类型,例如 int 表示指向整型的指针。
使用 (
) 运算符来访问指针所指向的值,这被称为解引用
空指针:
如果指针没有被初始化或没有指向任何值,它的默认值是 nil,这被称为空指针。
安全性:
不同于 C 和 C++,Go 的指针不允许执行某些不安全的操作,如指针算术(加减运算)。这增加了语言的安全性和减少了潜在的错误。
传递参数:
在函数调用中,如果参数是引用类型的(如指针),那么对参数的修改将影响到原始变量。这可以避免不必要的数据拷贝,提高效率。
动态内存分配:
虽然 Go 语言中通常使用 make 和 new 函数来动态分配内存,但这些操作背后也涉及到指针的概念。
多级指针:
可以有指向指针的指针,即多级指针,但在 Go 中并不常见,因为语言的设计倾向于简洁和清晰。
栈和堆:
指针可以指向栈上的局部变量,也可以指向堆上动态分配的内存。了解变量的生命周期和作用域对于正确使用指针至关重要。

关于指针的引用和内存地址的打印,以及指针解引用代码如下:

package main

import "fmt"

// main函数是程序的入口点
func main() {
	// 声明一个整型变量a并初始化为10
	var a int
	a = 10

	// 输出变量a的值
	fmt.Println("a:", a)

	// 输出变量a的内存地址
	fmt.Printf("a的内存地址是:%p\n", &a)

	// 声明一个指向a的指针变量b
	b := &a

	// 输出指针变量b指向的值
	fmt.Printf("b:%v\n", *b)
}

【第四段代码:方法的定义】
代码如下:

// NewUserHandlers 创建一个新的UserHandlers实例,用于处理用户相关的HTTP请求。
// 参数svc是一个UserService的指针,提供了与用户相关的业务逻辑。
// 返回一个UserHandlers的指针,初始化了正则表达式和UserService依赖。
func NewUserHandlers(svc *service.UserService) *UserHandlers {
    // 初始化电子邮件地址的正则表达式,用于验证电子邮件格式的正确性。
    emailRegExp:    regexp.MustCompile(emailRegexPattern, regexp.None),
    // 初始化密码的正则表达式,用于验证密码的格式是否符合要求。
    passwordRegExp: regexp.MustCompile(passwordRegexPattern, regexp.None),
    // 设置UserService的依赖,用于处理与用户相关的业务逻辑。
    svc:            svc,
    // 返回初始化后的UserHandlers实例。
    return &UserHandlers{
        emailRegExp:    regexp.MustCompile(emailRegexPattern, regexp.None),
        passwordRegExp: regexp.MustCompile(passwordRegexPattern, regexp.None),
        svc:            svc,
    }
}

把代码拆开来看,其中func是方法定义的关键字,NewUserHandlers是方法的名字,(svc *service.UserService) 是一个指针接收者,svc是接收者的名字,接收类型是指针类型,*UserHandlers 是指针类型的返回值,用于对初始化的&UserHandlers进行解引用,接接收到的地址信息转换为实际的值。

【第五段代码:结构体的实现方法】
在GoLand里面,我们定义好了一个结构体之后,鼠标右键选中,然后点击生成,选择实现方法就可以自动生成一个方法代码,如下代码是对结构体的实现方法的描述:
结构体的实现方法的快捷操作

// RegisterRoutes 将用户相关的路由注册到gin路由器。
// 这个方法定义了用户操作的URL模式,如注册、登录、查看和编辑个人资料。
// 参数:
//   routers *gin.Engine - gin路由器实例,用于添加新的路由。
func (h *UserHandlers) RegisterRoutes(routers *gin.Engine) {
    // 用户相关的路由组,通过/users路径进行分组。
    // 用户路由组
    rg := routers.Group("/users")    
    // 注册路由,用于用户创建新账户。
    // 请求方法: POST
    // 路径: /users/signup
    // 注册路由
    rg.POST("/signup", h.SignUp)
    
    // 登录路由,用于用户进行身份验证并获取会话令牌。
    // 请求方法: POST
    // 路径: /users/sign
    // 登录路由
    rg.POST("/sign", h.Login)
    
    // 用户个人资料路由,用于用户查看自己的个人信息。
    // 请求方法: GET
    // 路径: /users/profile
    // 用户资料路由
    rg.GET("/profile", h.Profile)
    
    // 编辑用户资料路由,用于用户更新自己的个人信息。
    // 请求方法: POST
    // 路径: /users/edit
    // 编辑资料路由
    rg.POST("/edit", h.Edit)
}

【第六段代码:路由的实现】

// SignUp 处理用户注册请求。
// 该方法首先解析请求中的注册信息,然后验证邮箱和密码格式,
// 接着检查密码确认是否一致,最后调用服务层进行注册操作。
// 参数:
//   ctx *gin.Context: Gin框架的上下文对象,用于处理HTTP请求和响应。
func (h *UserHandlers) SignUp(ctx *gin.Context) {
	// 定义注册请求的结构体,包含邮箱、密码和确认密码字段。
	// 注册
	// 字段标签,固定接收json格式,内部类
	type SignUpReq struct {
		Email           string `json:"email"`
		Password        string `json:"password"`
		ConfirmPassword string `json:"confirmPassword"`
	}
	// 解析请求体中的注册信息。
	//Bind 方法,类似是格式校验的一个方法,如果格式不是上面SignUp设置的格式就会报错。
	var req SignUpReq
	if err := ctx.Bind(&req); err != nil {
		// 如果解析失败,返回格式错误的响应。
		ctx.String(200, "输入的格式不合法")
		return
	}

	// 使用正则表达式验证邮箱格式。
	// 邮箱格式校验
	isEmail, err := h.emailRegExp.MatchString(req.Email)
	if err != nil {
		// 如果验证出错,返回系统错误的响应。
		ctx.String(http.StatusOK, "系统错误")
		return
	}
	if !isEmail {
		// 如果邮箱格式不正确,返回邮箱格式错误的响应。
		ctx.String(http.StatusOK, "邮箱式不对")
		return
	}

	// 使用正则表达式验证密码格式。
	// 密码格式校验
	isPassword, err := h.passwordRegExp.MatchString(req.Password)
	if err != nil {
		// 如果验证出错,返回系统错误的响应。
		ctx.String(http.StatusOK, "系统错误")
		return
	}
	if !isPassword {
		// 如果密码格式不正确,返回密码格式错误的响应。
		ctx.String(http.StatusOK, "密码格式不对,密码长度应该不小于8位,且包含特殊符号")
		return
	}

	// 检查密码和确认密码是否一致。
	// 确认密码校验
	if req.Password != req.ConfirmPassword {
		// 如果不一致,返回密码不一致的响应。
		ctx.String(200, "两次输入密码不一致")
		return
	}

	// 调用服务层进行注册操作。
	err = h.svc.SignUp(ctx, domain.User{
		Email:    req.Email,
		Password: req.Password,
	})
	switch {
	case err == nil:
		// 注册成功,返回成功的响应。
		ctx.String(http.StatusOK, "注册成功,")
	case errors.Is(err, service.ErrDuplicateEmail):
		// 如果邮箱已存在,返回邮箱冲突的响应。
		ctx.String(http.StatusOK, "邮箱冲突,请换一个")
	default:
		// 其他错误,返回系统错误的响应。
		ctx.String(http.StatusOK, "系统错误")
	}

	// 注册流程结束,告知用户注册已完成。
	ctx.String(http.StatusOK, "你已经完成注册")
}

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值