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年前 自动加精
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
39
粉丝
9
喜欢
71
收藏
102
排名:461
访问:1.9 万
私信
所有博文
社区赞助商