go-zero之阿里发短信服务
短信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 协议》,转载必须注明作者和本文链接
本帖由系统于 2年前 自动加精
推荐文章: