02 . Go之Gin+Vue开发一个线上外卖应用(集成第三方发送短信和xorm生成存储数据库表)
集成第三方发送短信
介绍
用户登录
用户登录有两种方式: 短信登录,密码登录
短信登录是使用手机号和验证码进行登录
短信平台
很多云平台,比如阿里云,腾讯云,七牛云等云厂商,向程序开发者提供了短信验证码套餐服务。借助云平台的短信服务,程序开发者可以非常简单方便的将短信服务集成到自己的程序中。
我们以接入和集成阿里云的短信服务sdk为例, 如果需要申请腾讯云短信服务可以看我另外写的一篇blog,使用两者都差不多
https://www.cnblogs.com/you-men/p/13088949.html
阿里云短信服务集成-登录阿里云
登录阿里云
登录阿里云:https://www.aliyun.com/
选择短信服务模块:https://www.aliyun.com/product/sms
注明:云平台的短信服务是收费的,有不同类别的套餐。开发者可以按照自己的需求进行选择。如下图:
Go短信服务SDK
可以访问https://api.aliyun.com/?spm=5176.12207334.0.0.54d71cbe3NE29f#/?product=Dysmsapi&lang=GO查看go语言版本的短信服务sdk代码。
安装阿里云GO SDK
go get github.com/aliyun/alibaba-cloud-sdk-go
创建签名和短信模板
在使用go sdk集成短息服务之前,需要首先创建短信签名和短信模板。
签名:短信签名是短信服务提供的一种快捷、方便的个性化签名方式。当发送短信时,短信平台会根据设置,在短信内容里附加个性签名,再发送给指定手机号码。比如,下图红框中的"招商银行”就是这条短信的签名,用以标识发送者的类别等内容。
在阿里云后台的短信服务控制面板的左侧功能栏中,选择国内消息,如下图所示,然后选择签名管理的TAB,然后选择添加签名,可以创建新的短信签名。如下图:
短信模板:短信模版,即具体发送的短信内容。短信模版可以支持验证码、短信通知、推广短信三种模式。验证码和短信通知,通过变量替换实现个性短信定制。
与创建签名同理,可以在模板管理TAB中,选择添加模板,用来创建新的短信模板。创建完成后,会进行审核。
创建成功后的短信模板CODE需要记住,后续会使用到
在短信服务管理平台的概览界面,可以选择AccessKey按钮进行创建集成短信服务所需要的AccessKey和AccessKeySecret。
程序中集成短信服务
client, err := dysmsapi.NewClientWithAccessKey(smsConfig.RegionId, smsConfig.AppKey, smsConfig.AppSecret)
if err != nil {
toolbox.Error(err.Error())
return ""
}
request := dysmsapi.CreateSendSmsRequest()
request.Scheme = "https"
request.SignName = smsConfig.SignName
request.TemplateCode = smsConfig.TemplateCode
request.PhoneNumbers = phone
par, err := json.Marshal(map[string]interface{}{
"code": code,
})
request.TemplateParam = string(par)
response, err := client.SendSms(request)
fmt.Println( response)
if err != nil {
toolbox.Error(err.Error())
return ""
}
我们通过上述核心代码程序,完成阿里云短信服务的集成。其中:
- SignName:在阿里云后台创建的前名,必传参数。
- TemplateCode:在阿里云后台创建的短信模板ID,必传参数。
- PhoneNumbers:接收短信的手机号码,必传参数。
创建阿里云短信服务sdk的client时,需要传regionID、appKey、accessKeySecret三个参数。regionID表示的是区域ID,可以填写cn-hangzhou。
创建Controller解析请求
无论是短信登录,还是用户名和密码登录,均属于用户功能模块。因此,创建MemberController用于解析用户模块的各个请求:
package controller
import (
"CloudRestaurant/service"
"fmt"
"github.com/gin-gonic/gin"
)
type MemberController struct {}
func (mc *MemberController) Router(engine *gin.Engine) {
engine.GET("/api/sendcode",mc.sendSmsCode)
}
// http://localhost:8090/api/sendcode?phone=18621048481
func (mc *MemberController) sendSmsCode(c *gin.Context) {
// 发送验证码
phone,exist := c.GetQuery("phone")
fmt.Println(phone,"phone --->")
if !exist {
c.JSON(200,map[string]interface{}{
"code":0,
"msg":"参数解析失败",
})
return
}
ms := service.MemberService{}
isSend := ms.Sendcode(phone)
if isSend {
c.JSON(200,map[string]interface{}{
"code":1,
"msg":"发送成功",
})
return
}
c.JSON(200,map[string]interface{}{
"code":0,
"msg":"发送失败",
})
}
创建用户控制层程序MemberController,使用GET方法解析客户端发送短信验证码的请求,请求接口为:/api/sendcode,同时指定sendSmsCode方法处理接口请求。
在实际的开发中,程序功能往往是分层来进行开发的,controller层只负责控制和处理接口请求的逻辑,具体的功能调用,往往由另外称为service层的功能服务层来进行实现。
创建service层
在service层中,依然按照模块化开发的理念,将用户模块的服务统一由UserServicej进行提供。因此,创建MemberService,并提供SendCode方法。
package service
import (
"CloudRestaurant/tool"
"encoding/json"
"fmt"
"github.com/aliyun/alibaba-cloud-sdk-go/services/dysmsapi"
"github.com/wonderivan/logger"
"math/rand"
"time"
)
type MemberService struct {
}
func (ms *MemberService) Sendcode(phone string) bool {
// 1.产生一个验证码
code := fmt.Sprintf("%06v",rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000))
// 2.调用阿里云服务
config := tool.GetConfig().Sms
client, err := dysmsapi.NewClientWithAccessKey(config.RegionId, config.AppKey, config.AppSecret)
if err != nil {
logger.Error(err.Error())
return false
}
request := dysmsapi.CreateSendSmsRequest()
request.Scheme = "https"
request.SignName = config.SignName
request.TemplateCode = config.TemplateCode
request.PhoneNumbers = phone
par,err := json.Marshal(map[string]interface{}{
"code":code,
})
request.TemplateParam = string(par)
response,err :=client.SendSms(request)
fmt.Println(response,"response --->")
if err != nil {
logger.Error(err.Error())
return false
}
// 3. 接受返回结果,并判断发送状态
if response.Code == "OK" {
return true
}
return false
}
测试
在postman中, 使用/api/sendcode接口进行测试,详细url如下:
http://localhost:8090/api/sendcode?phone=13167582436
然后就能在手机端收到验证码
创建数据库和数据表
上面已经接入了第三方的短信服务SDK功能,可以接受短信验证码。在用户接受到验证码以后,输入验证码进行登录,我们需要验证用户输入的验证码是否正确。
因此,我们需要将发送过的验证码通过持久化的方式保存下来,方便我们进行校验。
我们选择通过数据库来存储用户手机验证码。
xorm介绍及安装
在项目开发过程中,我们会使用一些成熟的框架来操作数据库。xorm就是一个比较流行的数据库操作orm框架。
安装xorm
go get github.com/go-xorm/xorm
安装Mysql驱动
go get github.com/go-sql-driver/mysql
连接数据库
在连接数据库之前,首先要创建数据库。在mysql中创建cloudrestaurant数据库:
create database cloudrestaurant;
创建完数据库并安装好xorm库以后,使用xorm进行连接数据库
import (
"github.com/go-xorm/xorm"
_ "github.com/go-sql-driver/mysql"
)
database := cfg.Database
conn := database.User + ":" + database.Password + "@tcp(" + database.Host + ":" + database.Port + ")/" + database.DbName + "?charset=" + database.Charset
engine, err := xorm.NewEngine(database.Driver, conn)
if err != nil {
return nil, err
}
连接数据库有些参数需要自己指定,比如说驱动类型,登录数据库的用户名,密码,数据库名等。将这些变量配置在app.json配置文件中,如下所示:
{
"database": {
"driver": "mysql",
"user": "root",
"password": "12345678",
"host": "127.0.0.1",
"port": "3306",
"db_name": "cloudrestaurant",
"charset": "utf8mb4",
"show_sql": true
}
}
在Config结构体中添加对database的解析
type Config struct {
AppName string `json:"app_name"`
AppMode string `json:"app_mode"`
AppHost string `json:"app_host"`
AppPort string `json:"app_port"`
Database DatabaseConfig `json:"database"`
Sms SmsConfig `json:"sms"`
}
type DatabaseConfig struct {
Driver string `json:"driver"`
User string `json:"user"`
Password string `json:"password"`
Host string `json:"host"`
Port string `json:"port"`
DbName string `json:"db_name"`
Charset string `json:"charset"`
ShowSql bool `json:"show_sql"`
}
创建SmsCode
要存储验证码,需要在数据库中创建表结构进行存储。我们可以创建SmsCode结构体,并通过tag设置数据库字段约束,具体的SmsCode定义如下:
package model
type SmsCode struct {
Id int64 `xorm:"pk autoincr" json:"id"`
Phone string `xorm:"varchar(11)" json:"phone"`
BizId string `xorm:"varchar(30)" json:"biz_id"`
Code string `xorm:"varchar(4)" json:"code"`
CreateTime int64 `xorm:"bigint" json:"create_time"`
}
通过tag的xorm设置字段数据类型以及约束。
- pk:表示主键
- autoinc:表示自增
- bigint:整形变量
- varchar:字符串类型
Sync2同步生成数据库表
可以调用engine.Sync2方法,将结构体类型同步映射到数据库中,生成数据库表。
err = engine.Sync2(new(model.SmsCode))
if err != nil {
return nil, err
}
将验证码数据保存到数据库
在MemberService的SendCode方法,添加保存验证码到数据库的操作:
func (msi *MemberServiceImpl) SendCode(phone string) string {
code := fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000))
...
dao := impl.NewMemberDao()
smsCode := model.SmsCode{Phone: phone, Code: code, BizId: response.BizId, CreateTime: time.Now().Unix()}
if result := dao.InsertCode(smsCode); result > 0 {
return code
}
return ""
}