簡訊RPC服務
需要在阿里雲上開通簡訊服務,開通模板、簽名等必須的條件,並擁有accessKey、 accessSecret 許可權。
如果你沒有使用微服務,直接呼叫sdk程式碼就可以。
定義proto檔案
syntax = "proto3";
package sms;
message sendReq {
string phoneNum = 1;
int32 appId = 2;
int32 action = 3;
}
message codeReq {
string phoneNum = 1;
}
message Resp {
int64 code = 1;
string msg = 2;
map<string, string> data = 3;
}
service Sms {
rpc sendSms(sendReq) returns(Resp);
rpc getCode(codeReq) returns(Resp);
}
生成檔案
❯ goctl.exe rpc proto -src .\sms.proto -dir .
Done
配置
Name: sms.rpc
ListenOn: 127.0.0.1:8081
Etcd:
Hosts:
- 127.0.0.1:2379
Key: sms.rpc
# redis配置
Redis:
Host: "localhost:6379"
Pass: "root"
# 簡訊配置
Sms:
KaFan:
AccessKey: $AccessKey
AccessKeySecret: $AccessKeySecret
SignName: $SignName
TemplateCode: $TemplateCode
# 快取過期時間,單位秒
CacheExpire: 300
宣告配置型別
package config
import "github.com/tal-tech/go-zero/zrpc"
type Config struct {
zrpc.RpcServerConf
Sms struct {
KaFan KaFan
CacheExpire int
}
}
type KaFan struct {
AccessKey string
AccessKeySecret string
SignName string
TemplateCode string
}
填充依賴
package svc
import (
"app/rpc/sms/internal/config"
"github.com/tal-tech/go-zero/core/stores/redis"
)
type ServiceContext struct {
Config config.Config
RedisClient *redis.Redis
}
func NewServiceContext(c config.Config) *ServiceContext {
srvCtx := &ServiceContext{
Config: c,
}
//redis初始化
if c.Redis.Host != "" {
srvCtx.RedisClient = srvCtx.Config.Redis.NewRedis()
}
return srvCtx
}
填充邏輯
下面是傳送簡訊的邏輯
package logic
import (
Sms "app/library/sms"
"context"
"fmt"
"math/rand"
"strconv"
"time"
"app/rpc/sms/internal/svc"
sms "app/rpc/sms/sms"
"github.com/tal-tech/go-zero/core/logx"
)
type SendSmsLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewSendSmsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsLogic {
return &SendSmsLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// 傳送簡訊
func (l *SendSmsLogic) SendSms(in *sms.SendReq) (*sms.Resp, error) {
// 檢測傳送頻率
b := l.diffSendTime(in.PhoneNum, time.Now().Unix())
if b == false {
return &sms.Resp{
Code: 201,
Msg: "傳送簡訊頻率過高",
Data: nil,
}, nil
}
var post = make(map[string]string)
// 生產隨機六位數
code := l.generateSixNum()
// 引數
post["accessKey"] = l.svcCtx.Config.Sms.KaFan.AccessKey
post["accessSecret"] = l.svcCtx.Config.Sms.KaFan.AccessKeySecret
post["phoneNum"] = in.PhoneNum
post["code"] = code
if in.AppId == 1 {
post["signName"] = l.svcCtx.Config.Sms.KaFan.SignName
}
if in.Action == 1 {
post["templateCode"] = l.svcCtx.Config.Sms.KaFan.TemplateCode
}
// 傳送簡訊
result, err := Sms.Send(post)
msg := "failed"
if result != nil {
msg = *result.Body.Message
}
if err != nil {
return &sms.Resp{
Code: 201,
Msg: msg,
Data: nil,
}, err
}
if *result.Body.Code != "OK" {
return &sms.Resp{
Code: 201,
Msg: msg,
Data: nil,
}, err
}
var data = make(map[string]string)
data["code"] = code
// 存到redis快取中,有效期5分鐘
cacheExpire := l.svcCtx.Config.Sms.CacheExpire
_ = l.svcCtx.RedisClient.Setex(in.PhoneNum, code, cacheExpire)
return &sms.Resp{
Code: 200,
Msg: "success",
Data: data,
}, nil
}
// 生成6位數
func (l *SendSmsLogic) generateSixNum() string {
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
return fmt.Sprintf("%06v", rnd.Int31n(1000000))
}
// 兩次傳送時間差
func (l *SendSmsLogic) diffSendTime(phoneNum string, tu int64) bool {
key := fmt.Sprintf("%s_unix", phoneNum)
t := strconv.FormatInt(tu, 10)
firstTime, err := l.svcCtx.RedisClient.Get(key)
if err != nil {
return false
}
if firstTime == "" {
err = l.svcCtx.RedisClient.Set(key, t)
if err != nil {
return false
}
} else {
now, _ := strconv.Atoi(t)
last, _ := strconv.Atoi(firstTime)
if now - last > 60 {
// 大於60秒,重置時間
_ = l.svcCtx.RedisClient.Set(key, t)
} else {
return false
}
}
return true
}
sdk呼叫傳送介面
package sms
import (
"fmt"
openapi "github.com/alibabacloud-go/darabonba-openapi/client"
dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v2/client"
"github.com/alibabacloud-go/tea/tea"
)
/**
* 使用AK&SK初始化賬號Client
* @param accessKeyId
* @param accessKeySecret
* @return Client
* @throws Exception
*/
func CreateClient (accessKeyId *string, accessKeySecret *string) (_result *dysmsapi20170525.Client, _err error) {
config := &openapi.Config{
// 您的AccessKey ID
AccessKeyId: accessKeyId,
// 您的AccessKey Secret
AccessKeySecret: accessKeySecret,
}
// 訪問的域名
config.Endpoint = tea.String("dysmsapi.aliyuncs.com")
_result = &dysmsapi20170525.Client{}
_result, _err = dysmsapi20170525.NewClient(config)
return _result, _err
}
/**
* 請求阿里雲傳送簡訊
*/
func Send(data map[string]string) (_result *dysmsapi20170525.SendSmsResponse, _err error) {
client, _err := CreateClient(tea.String(data["accessKey"]), tea.String(data["accessSecret"]))
if _err != nil {
return nil, _err
}
codeJson := fmt.Sprintf("{\"code\": \"%s\"}", data["code"])
sendSmsRequest := &dysmsapi20170525.SendSmsRequest{
SignName: tea.String(data["signName"]),
TemplateCode: tea.String(data["templateCode"]),
PhoneNumbers: tea.String(data["phoneNum"]),
TemplateParam: tea.String(codeJson),
}
// 複製程式碼執行請自行列印 API 的返回值
_result, _err = client.SendSms(sendSmsRequest)
if _err != nil {
return nil, _err
}
return _result, _err
}
至此RPC服務完成,啟動服務
❯ go run .\sms.go
Starting rpc server at 127.0.0.1:8081...
API 服務
寫api介面
syntax = "v1"
info(
title: "傳送簡訊"
desc: "傳送簡訊"
author: "charlie"
email: "cenhuqing@163.com"
version: "1.0"
)
type (
SendReq {
PhoneNum string `form:"phoneNum"`
AppId int32 `form:"appId"`
Action int32 `form:"action"`
}
CodeReq {
PhoneNum string `form:"phoneNum"`
}
SmsResp {
Code int `json:"code"`
Msg string `json:"msg"`
Data map[string]string `json:"data"`
}
)
service api {
// 傳送簡訊
@server(
group: sms
handler: Code
)
post /api/sendSms (SendReq) returns(SmsResp)
// 獲取簡訊
@server(
group: sms
handler: GetCode
)
post /api/getCode (CodeReq) returns(SmsResp)
}
生成程式碼
❯ goctl.exe api go -api api.api -dir .
配置etcd服務
Name: api
Host: 0.0.0.0
Port: 8888
SmsRpc:
Etcd:
Hosts:
- localhost:2379
Key: sms.rpc
宣告配置型別
package config
import (
"github.com/tal-tech/go-zero/rest"
"github.com/tal-tech/go-zero/zrpc"
)
type Config struct {
rest.RestConf
SmsRpc zrpc.RpcClientConf
}
填充依賴
package svc
import (
"app/api/internal/config"
"app/rpc/sms/smsclient"
"github.com/tal-tech/go-zero/zrpc"
)
type ServiceContext struct {
Config config.Config
SmsClient smsclient.Sms
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
SmsClient: smsclient.NewSms(zrpc.MustNewClient(c.SmsRpc)),
}
}
填充邏輯
package sms
import (
"app/rpc/sms/smsclient"
"context"
"app/api/internal/svc"
"app/api/internal/types"
"github.com/tal-tech/go-zero/core/logx"
)
type CodeLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) CodeLogic {
return CodeLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CodeLogic) Code(req types.SendReq) (*types.SmsResp, error) {
// 請求rpc服務,傳送簡訊
result, err := l.svcCtx.SmsClient.SendSms(l.ctx, &smsclient.SendReq{
PhoneNum: req.PhoneNum,
AppId: req.AppId,
Action: req.Action,
})
if err != nil {
return &types.SmsResp{
Code: 201,
Msg: "failed",
Data: nil,
}, err
}
if result.Code != 200 {
return &types.SmsResp{
Code: 201,
Msg: result.Msg,
Data: nil,
}, nil
}
return &types.SmsResp{
Code: 200,
Msg: "success",
Data: result.Data,
}, nil
}
啟動api服務
❯ go run .\api.go
Starting server at 0.0.0.0:8888...
測試傳送簡訊
curl --location --request POST 'http://localhost:8888/api/sendSms' \
--form 'phoneNum="150xxxxxxxx"' \
--form 'appId="1"' \
--form 'action="1"'
# 返回結果
{
"code": 200,
"msg": "success",
"data": {
"code": "064128"
}
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結