前期准备
需要了解一些go
的基础、gin
框架以及gorm
基本使用。这个功能不涉及到前端编写,所以你不会vue
也行。
没对比过其它的邮箱,直接使用的是163,用完之后的主观评价是挺好用的!
邮箱设置
登陆到邮箱的主页面,主页上面的工具栏有一个设置
点击之后选择pop3/smtp/imap
然后将imap/smtp
服务和pop3/smtp
服务开启(我这边已经开启了)
代码展示
介绍一下前期的一些配置细节
yaml
文件
#XXX
email:
from: [这里填你的邮箱名]@163.com
host: smtp.163.com
secret: [你开启邮箱smtp服务的时候会有一串安全码,复制过来粘贴]
nickname: [what ever you want]
port: 465
is-ssl: true
end-time: "3"
#XXX
config
文件夹下面新建一个email.go
package config
type Email struct {
To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用
From string `mapstructure:"from" json:"from" yaml:"from"` // 发件人 你自己要发邮件的邮箱
Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议
Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥
Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称 发件人昵称 通常为自己的邮箱
Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465
IsSSL bool `mapstructure:"is-ssl" json:"is-ssl" yaml:"is-ssl"` // 是否SSL 是否开启SSL
EndTime string `mapstructure:"end-time" json:"end-time" yaml:"end-time"`
}
项目的结构是这样的
在config.go
文件中添加上这个email
结构体
只需要在gva
项目中添加两个函数即可,其中对应的数据库结构信息,可以根据具体的业务逻辑自行创建
/***
* 参数
* email 前端接收到用户发送过来的json数据
* 返回值
* emailId
* err 错误码
***/
func (_this *EmailService) SendService(email emailRespone.Email) (emailId string, err error) {
//生成验证码
driver := base64Captcha.NewDriverDigit(global.GBQ_CONFIG.Captcha.ImgHeight, global.GBQ_CONFIG.Captcha.ImgWidth, global.GBQ_CONFIG.Captcha.KeyLong, 0.7, 20)
cp := base64Captcha.NewCaptcha(driver, system2.Store)
emailId, _, err = cp.Generate()
if err != nil {
global.GBQ_LOG.Error("Failed to obtain the verification code. Procedure!", zap.Error(err))
return
}
vcode := system2.Store.Get(emailId, false)
var user system.SysUser
var action int
if email.Subject == "register" {
err = global.GBQ_DB.Where("email = ?", email.To).First(&user).Error
if err == nil {
global.GBQ_LOG.Error("查询email失败", zap.Error(err))
return
}
//TODO 修饰
email.Body = "your register vcode is: " + vcode
action = 0
} else if email.Subject == "find_pass" {
email.Body = "your find_pass vcode is: " + vcode
action = 2
}
fmt.Println("vcode", email.Body)
currentTime := time.Now()
var currentEmailSmssystem.SysSms
err = global.GBQ_DB.Model(&system.SysSms{}).Where("email = ?", email.To).Order("created_at desc").First(¤tEmailSms).Error
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
err = errors.New("Obtaining the verification code is too frequent. Please try again later!")
return "Obtaining the verification code is too frequent. Please try again later!", err
}
}
if utils.IsLessThanThreeMinutes(currentTime, currentEmailSms.CreatedAt) { //距离上次请求验证码的时间小于三分钟
err = errors.New("Obtaining the verification code is too frequent. Please try again later!")
return "Obtaining the verification code is too frequent. Please try again later!", err
} else {
err = ServiceGroupApp.Email(email.To, email.Subject, email.Body)
if err != nil {
global.GBQ_LOG.Error("发送失败!", zap.Error(err))
return
}
ssms := &system.SysSms{
Email: email.To,
CodeId: emailId,
Code: vcode,
Action: action,
}
err = global.GBQ_DB.Model(ssms).Create(ssms).Error
if err != nil {
return "", err
}
return emailId, nil
}
}
func (e *EmailService) Email(To, subject string, body string) error {
to := strings.Split(To, ",")
return send(to, subject, body)
}
上面的代码放在项目的这个位置
api这里这样调用即可
func (e *EmailApi) SendEmail(c *gin.Context) {
var email email_response.Email
err := c.ShouldBind(&email)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
realEmailNum := utils.DeEncrypt(email.To)
fmt.Println(realEmailNum)
email.To = realEmailNum
id, err := service.ServiceGroupApp.SendService(email)
if err != nil || id == "" {
global.GBQ_LOG.Error("发送失败", zap.Error(err))
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithMessage("Sent successfully", c)
}
这里基本上和源码没什么区别,只增加了几行代码,前端在传输用户邮箱的时候做了个base64
加密避免明文传输漏洞
上面是触发邮箱发送验证码的部分,接下来用户注册的关键逻辑代码
func (b *BaseApi) RegisterByEmail(c *gin.Context) {
var r systemReq.Register
err := c.ShouldBind(&r)
if err != nil {
global.GBQ_LOG.Error("Failed to obtain registration parameters. Procedure", zap.Error(err))
response.FailWithMessage("Failed to obtain registration parameters. Procedure", c)
return
}
err = utils.Verify(r, utils.RegisterByEmailVerify)
if err != nil {
global.GBQ_LOG.Error("Registration validation failed", zap.Error(err))
response.FailWithMessage("Registration validation failed", c)
return
}
sms := &system.SysSms{}
err = global.GBQ_DB.Where("code = ? AND email = ?", r.CheckCode, r.Email).First(&sms).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
global.GBQ_LOG.Error("Failed to obtain the verification code id. Procedure", zap.Error(err))
response.FailWithMessage("The verification code is incorrect", c)
return
}
//判断验证码时间是否过期
tsms := sms.CreatedAt
tnow := time.Now()
endTime, _ := strconv.ParseFloat(global.GBQ_CONFIG.Email.EndTime, 64)
if tnow.Sub(tsms).Minutes() > endTime {
//验证码过期
global.GBQ_LOG.Info("Verification code expired")
response.FailWithMessage("Verification code expired", c)
return
}
var authorities []system.SysAuthority
for _, v := range r.AuthorityIds {
authorities = append(authorities, system.SysAuthority{
AuthorityId: v,
})
}
rand.Seed(time.Now().UnixNano())
kefuId := rand.Intn(2) + 6
specialId := strconv.Itoa(kefuId) //用户注册随机分配客服
user := &system.SysUser{Password: r.Password, HeaderImg: r.HeaderImg, Email: r.Email, LockTag: "0",
AuthorityId: r.AuthorityId,
Authorities: authorities,
Enable: r.Enable,
UserType: "01",
Special: specialId,
}
userReturn, err := userService.Register(user)
if err != nil {
global.GBQ_LOG.Error("Registration failure!", zap.Error(err))
response.FailWithMessage(err.Error(), c)
return
}
userRole := system.SysUserRole{
UserId: int64(user.ID),
RoleId: 100,
}
err = global.GBQ_DB.Table(global.GBQ_CONFIG.English.EnglishUserRole).Create(&userRole).Error
if err != nil {
global.GBQ_LOG.Error("Failed to create user", zap.Error(err))
response.FailWithMessage("Creation failure", c)
global.GBQ_DB.Table(global.GBQ_CONFIG.English.EnglishUser).Delete(&user)
return
}
//清除验证码
Store.Get(r.CaptchaId, true)
sms.Status = false
global.GBQ_DB.Save(sms)
response.OkWithDetailed(systemRes.SysUserResponse{User: userReturn}, "Registered successfully", c)
}
两句总结的话
关于路由注册这部分的代码直接看gva
源码即可,这边就不复制粘贴滥竽充数了。
总的来说呢,163邮箱发送验证码是真的丝滑,gva
框架也是真的好用!