Przeglądaj źródła

feat: Email+邮件验证码注册

runningwater 8 miesięcy temu
rodzic
commit
d89de31b8d

+ 1 - 1
.air.toml

@@ -28,7 +28,7 @@ tmp_dir = "tmp"
   stop_on_error = false
 
 [color]
-  app = "green"
+  app = ""
   build = "yellow"
   main = "magenta"
   runner = "green"

+ 67 - 43
app/http/controllers/api/v1/auth/signup_controller.go

@@ -1,67 +1,91 @@
 package auth
 
 import (
-    "github.com/gin-gonic/gin"
+	"github.com/gin-gonic/gin"
 
-    v1 "github.com/runningwater/gohub/app/http/controllers/api/v1"
-    "github.com/runningwater/gohub/app/models/user"
-    "github.com/runningwater/gohub/app/requests"
-    "github.com/runningwater/gohub/pkg/response"
+	v1 "github.com/runningwater/gohub/app/http/controllers/api/v1"
+	"github.com/runningwater/gohub/app/models/user"
+	"github.com/runningwater/gohub/app/requests"
+	"github.com/runningwater/gohub/pkg/response"
 )
 
 // SignupController 处理用户注册相关的逻辑
 type SignupController struct {
-    v1.BaseApiController
+	v1.BaseApiController
 }
 
 func (sc *SignupController) IsPhoneExist(c *gin.Context) {
 
-    // 初始化请求对象
-    req := requests.SignupPhoneExistRequest{}
-    if ok := requests.Validate(c, &req, requests.ValidateSignupPhoneExist); !ok {
-        return
-    }
+	// 初始化请求对象
+	req := requests.SignupPhoneExistRequest{}
+	if ok := requests.Validate(c, &req, requests.ValidateSignupPhoneExist); !ok {
+		return
+	}
 
-    // 检查数据库并返回响应
-    response.JSON(c, gin.H{
-        "exist": user.IsPhoneExist(req.Phone),
-    })
+	// 检查数据库并返回响应
+	response.JSON(c, gin.H{
+		"exist": user.IsPhoneExist(req.Phone),
+	})
 }
 
 func (sc *SignupController) IsEmailExist(c *gin.Context) {
 
-    // 初始化请求对象
-    req := requests.SignupEmailExistRequest{}
-    if ok := requests.Validate(c, &req, requests.ValidateSignupEmailExist); !ok {
-        return
-    }
+	// 初始化请求对象
+	req := requests.SignupEmailExistRequest{}
+	if ok := requests.Validate(c, &req, requests.ValidateSignupEmailExist); !ok {
+		return
+	}
 
-    // 检查数据库并返回响应
-    response.JSON(c, gin.H{
-        "exist": user.IsEmailExist(req.Email),
-    })
+	// 检查数据库并返回响应
+	response.JSON(c, gin.H{
+		"exist": user.IsEmailExist(req.Email),
+	})
 }
 
 // SignupUsingPhone 使用手机和验证码进行注册
 func (sc *SignupController) SignupUsingPhone(c *gin.Context) {
-    // 1. 验证表单
-    request := requests.SignupUsingPhoneRequest{}
-    if ok := requests.Validate(c, &request, requests.SignupUsingPhone); !ok {
-        return
-    }
-    // 2. 验证成功,创建数据
-    _user := user.User{
-        Name:     request.Name,
-        Phone:    request.Phone,
-        Password: request.Password,
-    }
-    _user.Create()
+	// 1. 验证表单
+	request := requests.SignupUsingPhoneRequest{}
+	if ok := requests.Validate(c, &request, requests.SignupUsingPhone); !ok {
+		return
+	}
+	// 2. 验证成功,创建数据
+	_user := user.User{
+		Name:     request.Name,
+		Phone:    request.Phone,
+		Password: request.Password,
+	}
+	_user.Create()
 
-    if _user.ID > 0 {
-        response.CreatedJSON(c, gin.H{
-            "data": _user,
-        })
-    } else {
-        response.Abort500(c, "创建用户失败,请稍后尝试~")
-    }
+	if _user.ID > 0 {
+		response.CreatedJSON(c, gin.H{
+			"data": _user,
+		})
+	} else {
+		response.Abort500(c, "创建用户失败,请稍后尝试~")
+	}
+}
+
+// SignupUsingEmail 使用邮箱和验证码进行注册
+func (sc *SignupController) SignupUsingEmail(c *gin.Context) {
+	// 1. 验证表单
+	request := requests.SignupUsingEmailRequest{}
+	if ok := requests.Validate(c, &request, requests.SignupUsingEmail); !ok {
+		return
+	}
+	// 2. 验证成功,创建数据
+	_user := user.User{
+		Name:     request.Name,
+		Email:    request.Email,
+		Password: request.Password,
+	}
+	_user.Create()
+
+	if _user.ID > 0 {
+		response.CreatedJSON(c, gin.H{
+			"data": _user,
+		})
+	} else {
+		response.Abort500(c, "创建用户失败,请稍后尝试~")
+	}
 }

+ 138 - 82
app/requests/signup_request.go

@@ -2,109 +2,165 @@
 package requests
 
 import (
-    "github.com/gin-gonic/gin"
-    "github.com/thedevsaddam/govalidator"
+	"github.com/gin-gonic/gin"
+	"github.com/thedevsaddam/govalidator"
 
-    "github.com/runningwater/gohub/app/requests/validators"
+	"github.com/runningwater/gohub/app/requests/validators"
 )
 
 type SignupPhoneExistRequest struct {
-    Phone string `json:"phone,omitempty" valid:"phone"`
+	Phone string `json:"phone,omitempty" valid:"phone"`
 }
 type SignupEmailExistRequest struct {
-    Email string `json:"email,omitempty" valid:"email"`
+	Email string `json:"email,omitempty" valid:"email"`
 }
 
 func ValidateSignupPhoneExist(data any, c *gin.Context) map[string][]string {
 
-    // 自定义验证规则
-    rules := govalidator.MapData{
-        "phone": []string{"required", "digits:11"},
-    }
-
-    // 自定义错误信息
-    messages := govalidator.MapData{
-        "phone": []string{
-            "required:手机号不能为空",
-            "digits:手机号必须是11位数字",
-        },
-    }
-
-    // 执行验证
-    return validate(data, rules, messages)
+	// 自定义验证规则
+	rules := govalidator.MapData{
+		"phone": []string{"required", "digits:11"},
+	}
+
+	// 自定义错误信息
+	messages := govalidator.MapData{
+		"phone": []string{
+			"required:手机号不能为空",
+			"digits:手机号必须是11位数字",
+		},
+	}
+
+	// 执行验证
+	return validate(data, rules, messages)
 }
 
 func ValidateSignupEmailExist(data any, c *gin.Context) map[string][]string {
 
-    // 自定义验证规则
-    rules := govalidator.MapData{
-        "email": []string{"required", "min:4", "max:30", "email"},
-    }
-
-    // 自定义错误信息
-    messages := govalidator.MapData{
-        "email": []string{
-            "required:Email 不能为空",
-            "min:Email 长度需大于 4",
-            "max:Email 长度需小于 30",
-            "email:Email 格式不正确,请提供有效的邮箱地址",
-        },
-    }
-    // 执行验证
-    return validate(data, rules, messages)
+	// 自定义验证规则
+	rules := govalidator.MapData{
+		"email": []string{"required", "min:4", "max:30", "email"},
+	}
+
+	// 自定义错误信息
+	messages := govalidator.MapData{
+		"email": []string{
+			"required:Email 不能为空",
+			"min:Email 长度需大于 4",
+			"max:Email 长度需小于 30",
+			"email:Email 格式不正确,请提供有效的邮箱地址",
+		},
+	}
+	// 执行验证
+	return validate(data, rules, messages)
 }
 
 // SignupUsingPhoneRequest 用于手机注册的请求结构体
 // 该结构体包含了手机号码、验证码、姓名、密码和确认密码等字段
 type SignupUsingPhoneRequest struct {
-    Phone           string `json:"phone,omitempty" valid:"phone"`
-    VerifyCode      string `json:"verify_code,omitempty" valid:"verify_code"`
-    Name            string `valid:"name" json:"name,omitempty"`
-    Password        string `valid:"password" json:"password,omitempty"`
-    PasswordConfirm string `valid:"password_confirm" json:"password_confirm,omitempty"`
+	Phone           string `json:"phone,omitempty" valid:"phone"`
+	VerifyCode      string `json:"verify_code,omitempty" valid:"verify_code"`
+	Name            string `valid:"name" json:"name,omitempty"`
+	Password        string `valid:"password" json:"password,omitempty"`
+	PasswordConfirm string `valid:"password_confirm" json:"password_confirm,omitempty"`
 }
 
 // SignupUsingPhone 验证手机号码注册表单
 func SignupUsingPhone(data any, c *gin.Context) map[string][]string {
-    // 自定义验证规则
-    rules := govalidator.MapData{
-        "phone":            []string{"required", "digits:11", "not_exists:users,phone"},
-        "verify_code":      []string{"required", "digits:6"},
-        "name":             []string{"required", "alpha_num", "between:3,20", "not_exists:users,name"},
-        "password":         []string{"required", "min:6"},
-        "password_confirm": []string{"required"},
-    }
-
-    // 自定义错误信息
-    messages := govalidator.MapData{
-        "phone": []string{
-            "required:手机号不能为空",
-            "digits:手机号必须是 11 位数字",
-        },
-        "verify_code": []string{
-            "required:验证码不能为空",
-            "digits:验证码必须是 6 位数字",
-        },
-        "name": []string{
-            "required:姓名不能为空",
-            "alpha_num:姓名格式错误,只允许数字和英文",
-            "between:姓名长度需在 3~20 之间",
-        },
-        "password": []string{
-            "required:密码不能为空",
-            "min:密码长度需大于 6",
-        },
-        "password_confirm": []string{
-            "required:确认密码框不能为空",
-        },
-    }
-
-    errs := validate(data, rules, messages)
-
-    // 检查密码和确认密码是否匹配
-    _data := data.(*SignupUsingPhoneRequest)
-    errs = validators.ValidatePasswordConfirm(_data.Password, _data.PasswordConfirm, errs)
-    errs = validators.ValidateVerifyCode(_data.Phone, _data.VerifyCode, errs)
-
-    return errs
+	// 自定义验证规则
+	rules := govalidator.MapData{
+		"phone":            []string{"required", "digits:11", "not_exists:users,phone"},
+		"verify_code":      []string{"required", "digits:6"},
+		"name":             []string{"required", "alpha_num", "between:3,20", "not_exists:users,name"},
+		"password":         []string{"required", "min:6"},
+		"password_confirm": []string{"required"},
+	}
+
+	// 自定义错误信息
+	messages := govalidator.MapData{
+		"phone": []string{
+			"required:手机号不能为空",
+			"digits:手机号必须是 11 位数字",
+		},
+		"verify_code": []string{
+			"required:验证码不能为空",
+			"digits:验证码必须是 6 位数字",
+		},
+		"name": []string{
+			"required:姓名不能为空",
+			"alpha_num:姓名格式错误,只允许数字和英文",
+			"between:姓名长度需在 3~20 之间",
+		},
+		"password": []string{
+			"required:密码不能为空",
+			"min:密码长度需大于 6",
+		},
+		"password_confirm": []string{
+			"required:确认密码框不能为空",
+		},
+	}
+
+	errs := validate(data, rules, messages)
+
+	// 检查密码和确认密码是否匹配
+	_data := data.(*SignupUsingPhoneRequest)
+	errs = validators.ValidatePasswordConfirm(_data.Password, _data.PasswordConfirm, errs)
+	errs = validators.ValidateVerifyCode(_data.Phone, _data.VerifyCode, errs)
+
+	return errs
+}
+
+// SignupUsingEmailRequest 用于邮箱注册的请求结构体
+// !该结构体包含了邮箱、验证码、姓名、密码和确认密码等字段
+type SignupUsingEmailRequest struct {
+	Email           string `json:"email,omitempty" valid:"email"`
+	VerifyCode      string `json:"verify_code,omitempty" valid:"verify_code"`
+	Name            string `valid:"name" json:"name"`
+	Password        string `valid:"password" json:"password,omitempty"`
+	PasswordConfirm string `valid:"password_confirm" json:"password_confirm,omitempty"`
+}
+
+// SignupUsingEmail 验证邮箱注册表单
+func SignupUsingEmail(data any, c *gin.Context) map[string][]string {
+
+	rules := govalidator.MapData{
+		"email":            []string{"required", "min:4", "max:30", "email", "not_exists:users,email"}, // 邮箱验证规则
+		"verify_code":      []string{"required", "digits:6"},                                           // 验证码验证规则
+		"name":             []string{"required", "alpha_num", "between:3,20", "not_exists:users,name"}, // 姓名验证规则
+		"password":         []string{"required", "min:6"},                                              // 密码验证规则
+		"password_confirm": []string{"required"},                                                       // 确认密码验证规则
+	}
+	// 自定义错误信息
+	messages := govalidator.MapData{
+		"email": []string{
+			"required:Email 不能为空",
+			"min:Email 长度需大于 4",
+			"max:Email 长度需小于 30",
+			"email:Email 格式不正确,请提供有效的邮箱地址",
+			"not_exists:邮箱已被注册",
+		},
+		"verify_code": []string{
+			"required:验证码不能为空",
+			"digits:验证码必须是 6 位数字",
+		},
+		"name": []string{
+			"required:姓名不能为空",
+			"alpha_num:姓名格式错误,只允许数字和英文",
+			"between:姓名长度需在 3~20 之间",
+		},
+		"password": []string{
+			"required:密码不能为空",
+			"min:密码长度需大于 6",
+		},
+		"password_confirm": []string{
+			"required:确认密码框不能为空",
+		},
+	}
+
+	errs := validate(data, rules, messages)
+	// 检查密码和确认密码是否匹配
+	_data := data.(*SignupUsingEmailRequest)
+	errs = validators.ValidatePasswordConfirm(_data.Password, _data.PasswordConfirm, errs)
+	errs = validators.ValidateVerifyCode(_data.Email, _data.VerifyCode, errs)
+
+	return errs
 }

+ 13 - 1
gohub.http

@@ -46,6 +46,18 @@ Content-Type: application/json
     "name":"summer",
     "password":"secret",
     "password_confirm":"no_match",
-    "verify_code": "{{verify_code_phone}}",
+    "verify_code": "123123",
     "phone": "00011059149"
+}
+
+### 注册用户
+POST {{base_url}}/v1/auth/signup/using-email HTTP/1.1
+Content-Type: application/json
+
+{
+    "name":"summer",
+    "password":"secret",
+    "password_confirm":"secret",
+    "verify_code": "123123",
+    "email": "summer@example.com"
 }

+ 24 - 23
routes/api.go

@@ -2,32 +2,33 @@
 package routes
 
 import (
-    "github.com/gin-gonic/gin"
+	"github.com/gin-gonic/gin"
 
-    "github.com/runningwater/gohub/app/http/controllers/api/v1/auth"
+	"github.com/runningwater/gohub/app/http/controllers/api/v1/auth"
 )
 
 // RegisterAPIRoutes 注册路由
 func RegisterAPIRoutes(router *gin.Engine) {
-    // v1 路由组,所有 v1 版本的路由都放在这里
-    v1 := router.Group("/v1")
-    {
-        authGroup := v1.Group("/auth")
-        {
-            suc := new(auth.SignupController)
-            vcc := new(auth.VerifyCodeController)
-            // 注册手机号是否已存在
-            authGroup.POST("/signup/phone/exist", suc.IsPhoneExist)
-            // 注册邮箱是否已存在
-            authGroup.POST("/signup/email/exist", suc.IsEmailExist)
-            // 注册用户
-            authGroup.POST("/signup/using-phone", suc.SignupUsingPhone)
-            // 显示图片验证码
-            authGroup.POST("/verify_code/captcha", vcc.ShowCaptcha)
-            // 发送手机验证码
-            authGroup.POST("/verify_code/phone", vcc.SendUsingPhone)
-            // 发送邮箱验证码
-            authGroup.POST("/verify_code/email", vcc.SendUsingEmail)
-        }
-    }
+	// v1 路由组,所有 v1 版本的路由都放在这里
+	v1 := router.Group("/v1")
+	{
+		authGroup := v1.Group("/auth")
+		{
+			suc := new(auth.SignupController)
+			vcc := new(auth.VerifyCodeController)
+			// 注册手机号是否已存在
+			authGroup.POST("/signup/phone/exist", suc.IsPhoneExist)
+			// 注册邮箱是否已存在
+			authGroup.POST("/signup/email/exist", suc.IsEmailExist)
+			// 注册用户
+			authGroup.POST("/signup/using-phone", suc.SignupUsingPhone)
+			authGroup.POST("/signup/using-email", suc.SignupUsingEmail)
+			// 显示图片验证码
+			authGroup.POST("/verify_code/captcha", vcc.ShowCaptcha)
+			// 发送手机验证码
+			authGroup.POST("/verify_code/phone", vcc.SendUsingPhone)
+			// 发送邮箱验证码
+			authGroup.POST("/verify_code/email", vcc.SendUsingEmail)
+		}
+	}
 }