sms_tool
This commit is contained in:
12
sms_tool/cache_model.go
Normal file
12
sms_tool/cache_model.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package sms_tool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ICacheAdapter interface {
|
||||
Get(ctx context.Context, key string) (value interface{}, err error)
|
||||
Set(ctx context.Context, key string, value interface{}, expire time.Duration) (err error)
|
||||
Del(ctx context.Context, key string) (err error)
|
||||
}
|
136
sms_tool/sms_client.go
Normal file
136
sms_tool/sms_client.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package sms_tool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v5/client"
|
||||
util "github.com/alibabacloud-go/tea-utils/v2/service"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SmsClient struct {
|
||||
SmsConfig
|
||||
Cache ICacheAdapter
|
||||
client *dysmsapi20170525.Client
|
||||
}
|
||||
|
||||
type SmsConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
}
|
||||
|
||||
func NewSmsClient(smsConfig *SmsConfig) (smsClient SmsClient, err error) {
|
||||
config := &openapi.Config{}
|
||||
if smsConfig.AccessKeyId == "" {
|
||||
return SmsClient{}, errors.New("AccessKeyId 不能为空")
|
||||
}
|
||||
if smsConfig.AccessKeySecret == "" {
|
||||
return SmsClient{}, errors.New("AccessKeySecret 不能为空")
|
||||
}
|
||||
if smsConfig.Endpoint == "" {
|
||||
smsConfig.Endpoint = "dysmsapi.aliyuncs.com"
|
||||
}
|
||||
config.AccessKeyId = tea.String(smsConfig.AccessKeyId)
|
||||
config.AccessKeySecret = tea.String(smsConfig.AccessKeySecret)
|
||||
// Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
|
||||
config.Endpoint = tea.String(smsConfig.Endpoint)
|
||||
|
||||
smsClient.client = &dysmsapi20170525.Client{}
|
||||
smsClient.client, err = dysmsapi20170525.NewClient(config)
|
||||
return smsClient, err
|
||||
}
|
||||
|
||||
// SendSms 发送短信
|
||||
func (c *SmsClient) SendSms(params *SendSmsParams) (res *dysmsapi20170525.SendSmsResponse, err error) {
|
||||
smsParams := &dysmsapi20170525.SendSmsRequest{
|
||||
OutId: tea.String(params.OutId),
|
||||
OwnerId: tea.Int64(params.OwnerId),
|
||||
PhoneNumbers: tea.String(params.PhoneNumbers),
|
||||
ResourceOwnerAccount: tea.String(params.ResourceOwnerAccount),
|
||||
ResourceOwnerId: tea.Int64(params.ResourceOwnerId),
|
||||
SignName: tea.String(params.SignName),
|
||||
SmsUpExtendCode: tea.String(params.SmsUpExtendCode),
|
||||
TemplateCode: tea.String(params.TemplateCode),
|
||||
TemplateParam: tea.String(params.TemplateParam),
|
||||
}
|
||||
runtime := &util.RuntimeOptions{}
|
||||
return c.client.SendSmsWithOptions(smsParams, runtime)
|
||||
}
|
||||
|
||||
// GetCode 获取验证码
|
||||
func (c *SmsClient) GetCode(ctx context.Context, key string) (code string, err error) {
|
||||
if c.Cache == nil {
|
||||
return "", errors.New("缓存不能为空")
|
||||
}
|
||||
value, err := c.Cache.Get(ctx, key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return value.(string), nil
|
||||
}
|
||||
|
||||
// SaveCode 保存验证码
|
||||
func (c *SmsClient) SaveCode(ctx context.Context, key string, code string, expire time.Duration, frequency time.Duration) (err error) {
|
||||
if c.Cache == nil {
|
||||
return errors.New("缓存不能为空")
|
||||
}
|
||||
frequencyKey := fmt.Sprintf(CodeFrequencyKey, key)
|
||||
if frequency != 0 {
|
||||
// 判断验证码是否频繁
|
||||
frequencyValue, err := c.Cache.Get(ctx, frequencyKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
// 缓存中没有数据
|
||||
fmt.Printf("缓存中没有数据")
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if frequencyValue != nil && frequencyValue.(string) == CodeFrequencyValue {
|
||||
return errors.New("code发送频繁,请稍后再试")
|
||||
}
|
||||
}
|
||||
//保存code
|
||||
err = c.Cache.Set(ctx, key, code, expire)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if frequency != 0 {
|
||||
// 保存验证码发送频率
|
||||
err = c.Cache.Set(ctx, frequencyKey, CodeFrequencyValue, frequency)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteCode 删除验证码
|
||||
func (c *SmsClient) DeleteCode(ctx context.Context, key string) (err error) {
|
||||
if c.Cache == nil {
|
||||
return errors.New("缓存不能为空")
|
||||
}
|
||||
err = c.Cache.Del(ctx, key)
|
||||
return err
|
||||
}
|
||||
|
||||
// VerifyCode 校验验证码
|
||||
func (c *SmsClient) VerifyCode(ctx context.Context, key string, verifyCode string) (ok bool, err error) {
|
||||
//获取验证码
|
||||
code, err := c.GetCode(ctx, key)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
//校验验证码
|
||||
if code != verifyCode {
|
||||
return false, errors.New("code不一致")
|
||||
}
|
||||
//删除验证码
|
||||
_ = c.DeleteCode(ctx, key)
|
||||
return true, nil
|
||||
}
|
190
sms_tool/sms_client_test.go
Normal file
190
sms_tool/sms_client_test.go
Normal file
@@ -0,0 +1,190 @@
|
||||
package sms_tool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RedisCacheAdapter struct {
|
||||
client *redis.Client
|
||||
}
|
||||
|
||||
func NewRedisCacheAdapter(addr string, password string, db int) *RedisCacheAdapter {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: addr,
|
||||
Password: password,
|
||||
DB: db,
|
||||
})
|
||||
return &RedisCacheAdapter{client: rdb}
|
||||
}
|
||||
func (r *RedisCacheAdapter) Set(ctx context.Context, key string, value interface{}, expire time.Duration) error {
|
||||
data, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.client.Set(ctx, key, data, expire).Err()
|
||||
}
|
||||
func (r *RedisCacheAdapter) Get(ctx context.Context, key string) (interface{}, error) {
|
||||
data, err := r.client.Get(ctx, key).Bytes()
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var value interface{}
|
||||
err = json.Unmarshal(data, &value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
func (r *RedisCacheAdapter) Del(ctx context.Context, key string) error {
|
||||
return r.client.Del(ctx, key).Err()
|
||||
}
|
||||
|
||||
var (
|
||||
//SMS
|
||||
accessKeyId = os.Getenv("SMS_ALIBABA_CLOUD_ACCESS_KEY_ID")
|
||||
accessKeySecret = os.Getenv("SMS_ALIBABA_CLOUD_ACCESS_KEY_SECRET")
|
||||
phoneNumbers = os.Getenv("SMS_PHONE_NUMBERS")
|
||||
signName = os.Getenv("SMS_SIGN_NAME")
|
||||
templateCode = os.Getenv("SMS_TEMPLATE_CODE")
|
||||
|
||||
//REDIS
|
||||
redisHost = os.Getenv("REDIS_HOST")
|
||||
redisPort = os.Getenv("REDIS_PORT")
|
||||
redisPassword = os.Getenv("REDIS_PASSWORD")
|
||||
redisDb = os.Getenv("REDIS_DB")
|
||||
//redisKeyPrefix = os.Getenv("REDIS_KEY_PREFIX")
|
||||
|
||||
codeKey = fmt.Sprintf("User:Login:Code:%v", phoneNumbers)
|
||||
)
|
||||
|
||||
func TestSmsClient_SendSms(t *testing.T) {
|
||||
config := &SmsConfig{
|
||||
AccessKeyId: accessKeyId,
|
||||
AccessKeySecret: accessKeySecret,
|
||||
}
|
||||
smsClient, err := NewSmsClient(config)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
templateParam := make(map[string]interface{})
|
||||
templateParam["code"] = "123456"
|
||||
templateParamStr, err := json.Marshal(templateParam)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
params := &SendSmsParams{
|
||||
PhoneNumbers: phoneNumbers,
|
||||
SignName: signName,
|
||||
TemplateCode: templateCode,
|
||||
TemplateParam: string(templateParamStr),
|
||||
}
|
||||
res, err := smsClient.SendSms(params)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
fmt.Println(res)
|
||||
}
|
||||
|
||||
func TestSmsClient_GetCode(t *testing.T) {
|
||||
config := &SmsConfig{
|
||||
AccessKeyId: accessKeyId,
|
||||
AccessKeySecret: accessKeySecret,
|
||||
}
|
||||
smsClient, err := NewSmsClient(config)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
addr := fmt.Sprintf("%s:%s", redisHost, redisPort)
|
||||
db, _ := strconv.Atoi(redisDb)
|
||||
goRedis := NewRedisCacheAdapter(addr, redisPassword, db)
|
||||
smsClient.Cache = goRedis
|
||||
code, err := smsClient.GetCode(context.Background(), codeKey)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
fmt.Println(code)
|
||||
}
|
||||
|
||||
func TestSmsClient_SaveCode(t *testing.T) {
|
||||
config := &SmsConfig{
|
||||
AccessKeyId: accessKeyId,
|
||||
AccessKeySecret: accessKeySecret,
|
||||
}
|
||||
smsClient, err := NewSmsClient(config)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
addr := fmt.Sprintf("%s:%s", redisHost, redisPort)
|
||||
db, _ := strconv.Atoi(redisDb)
|
||||
goRedis := NewRedisCacheAdapter(addr, redisPassword, db)
|
||||
smsClient.Cache = goRedis
|
||||
|
||||
err = smsClient.SaveCode(context.Background(), codeKey, "123456", time.Minute*5, time.Minute)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
fmt.Println("保存成功")
|
||||
}
|
||||
|
||||
func TestSmsClient_VerifyCode(t *testing.T) {
|
||||
config := &SmsConfig{
|
||||
AccessKeyId: accessKeyId,
|
||||
AccessKeySecret: accessKeySecret,
|
||||
}
|
||||
smsClient, err := NewSmsClient(config)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
addr := fmt.Sprintf("%s:%s", redisHost, redisPort)
|
||||
db, _ := strconv.Atoi(redisDb)
|
||||
goRedis := NewRedisCacheAdapter(addr, redisPassword, db)
|
||||
smsClient.Cache = goRedis
|
||||
ok, err := smsClient.VerifyCode(context.Background(), codeKey, "123456")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
fmt.Println(ok)
|
||||
}
|
||||
|
||||
func TestSmsClient_DeleteCode(t *testing.T) {
|
||||
config := &SmsConfig{
|
||||
AccessKeyId: accessKeyId,
|
||||
AccessKeySecret: accessKeySecret,
|
||||
}
|
||||
smsClient, err := NewSmsClient(config)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
addr := fmt.Sprintf("%s:%s", redisHost, redisPort)
|
||||
db, _ := strconv.Atoi(redisDb)
|
||||
goRedis := NewRedisCacheAdapter(addr, redisPassword, db)
|
||||
smsClient.Cache = goRedis
|
||||
err = smsClient.DeleteCode(context.Background(), codeKey)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
fmt.Println("删除成功")
|
||||
}
|
18
sms_tool/sms_model.go
Normal file
18
sms_tool/sms_model.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package sms_tool
|
||||
|
||||
type SendSmsParams struct {
|
||||
OutId string `json:"OutId,omitempty" xml:"OutId,omitempty"`
|
||||
OwnerId int64 `json:"OwnerId,omitempty" xml:"OwnerId,omitempty"`
|
||||
PhoneNumbers string `json:"PhoneNumbers,omitempty" xml:"PhoneNumbers,omitempty"`
|
||||
ResourceOwnerAccount string `json:"ResourceOwnerAccount,omitempty" xml:"ResourceOwnerAccount,omitempty"`
|
||||
ResourceOwnerId int64 `json:"ResourceOwnerId,omitempty" xml:"ResourceOwnerId,omitempty"`
|
||||
SignName string `json:"SignName,omitempty" xml:"SignName,omitempty"`
|
||||
SmsUpExtendCode string `json:"SmsUpExtendCode,omitempty" xml:"SmsUpExtendCode,omitempty"`
|
||||
TemplateCode string `json:"TemplateCode,omitempty" xml:"TemplateCode,omitempty"`
|
||||
TemplateParam string `json:"TemplateParam,omitempty" xml:"TemplateParam,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
CodeFrequencyKey = "%s_frequency" // code频率限制key
|
||||
CodeFrequencyValue = "1" // code频率限制值
|
||||
)
|
Reference in New Issue
Block a user