2026-08-09 10:43:45 +08:00
|
|
|
|
package sms
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"encoding/json"
|
|
|
|
|
|
"errors"
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"math/rand"
|
|
|
|
|
|
"regexp"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
2026-08-09 12:41:08 +08:00
|
|
|
|
"bsm/full/module/base/sender/internal/config"
|
|
|
|
|
|
"bsm/full/module/base/sender/internal/excode"
|
|
|
|
|
|
"bsm/full/module/base/sender/internal/impl"
|
|
|
|
|
|
pb "bsm/full/module/base/sender/pb"
|
2026-08-09 10:43:45 +08:00
|
|
|
|
"git.apinb.com/bsm-sdk/core/cache/redis"
|
|
|
|
|
|
"git.apinb.com/bsm-sdk/core/errcode"
|
|
|
|
|
|
AliYunClient "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
|
|
|
|
|
AliYunUtil "github.com/alibabacloud-go/darabonba-openapi/v2/utils"
|
|
|
|
|
|
"github.com/alibabacloud-go/tea/dara"
|
|
|
|
|
|
"github.com/alibabacloud-go/tea/tea"
|
|
|
|
|
|
"github.com/spf13/cast"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
func Send(ctx context.Context, in *pb.SmsSendRequest) (reply *pb.SmsReply, err error) {
|
|
|
|
|
|
var smsCode string
|
|
|
|
|
|
|
|
|
|
|
|
if in.GetPhone() == "" || !VerifyPhone(in.GetPhone()) {
|
|
|
|
|
|
return nil, excode.ErrPhone
|
|
|
|
|
|
}
|
|
|
|
|
|
if in.GetTemplateCode() == "" {
|
|
|
|
|
|
return nil, excode.ErrTemplate
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 是否验证黑名单
|
|
|
|
|
|
if impl.RedisService.Client.SIsMember(impl.RedisService.Ctx, BlackListCacheKey, in.GetPhone()).Val() {
|
|
|
|
|
|
return nil, excode.ErrInBlackList
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 每天限制
|
|
|
|
|
|
limitKey := LimitCacheKey + time.Now().Format(FormatDay) + in.GetPhone()
|
|
|
|
|
|
|
|
|
|
|
|
// check limit
|
|
|
|
|
|
twice, err := impl.RedisService.Client.Get(impl.RedisService.Ctx, limitKey).Int()
|
|
|
|
|
|
if err != nil && !errors.Is(err, redis.Nil) {
|
|
|
|
|
|
return nil, errcode.ErrRedis
|
|
|
|
|
|
}
|
|
|
|
|
|
if twice > config.Spec.Code.MaxSentLimit {
|
|
|
|
|
|
return nil, excode.ErrSentLimit
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 从redis获取验证码,如果没有重新生成
|
|
|
|
|
|
key := KeyPrefix + in.GetPhone()
|
|
|
|
|
|
if in.GetIsGenCode() {
|
|
|
|
|
|
//验证码最少4位,最大10位。
|
|
|
|
|
|
if config.Spec.Code.Length < 4 || config.Spec.Code.Length > 10 {
|
|
|
|
|
|
config.Spec.Code.Length = 6
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 新生成验证码
|
|
|
|
|
|
smsCode = GenValidateCode(config.Spec.Code.Length)
|
|
|
|
|
|
//sms code write to redis
|
|
|
|
|
|
expire := time.Second * time.Duration(config.Spec.Code.Expire)
|
|
|
|
|
|
impl.RedisService.Client.SetNX(impl.RedisService.Ctx, key, smsCode, expire)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 获取验证码
|
|
|
|
|
|
if code, ok := in.Paramters["code"]; ok {
|
|
|
|
|
|
smsCode = code
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return nil, excode.ErrCode
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var result map[string]interface{}
|
|
|
|
|
|
switch strings.ToLower(in.GetProvider()) {
|
|
|
|
|
|
case "aliyun":
|
|
|
|
|
|
if impl.Provider.Aliyun == nil {
|
|
|
|
|
|
return nil, excode.ErrProviderIsNil
|
|
|
|
|
|
}
|
|
|
|
|
|
result, err = AliyunSender(in, smsCode)
|
|
|
|
|
|
case "tencent":
|
|
|
|
|
|
if impl.Provider.Tencent == nil {
|
|
|
|
|
|
return nil, excode.ErrProviderIsNil
|
|
|
|
|
|
}
|
|
|
|
|
|
result, err = TencentSender(in)
|
|
|
|
|
|
default:
|
|
|
|
|
|
return nil, excode.ErrNotProvider
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
jsonBytes, _ := json.Marshal(result)
|
|
|
|
|
|
fmt.Println("短信发送结果:", string(jsonBytes))
|
|
|
|
|
|
return &pb.SmsReply{
|
|
|
|
|
|
Reply: string(jsonBytes),
|
|
|
|
|
|
}, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func AliyunSender(args *pb.SmsSendRequest, code string) (map[string]any, error) {
|
|
|
|
|
|
// 构建发送参数
|
|
|
|
|
|
var templateParam = map[string]any{
|
|
|
|
|
|
"code": code,
|
|
|
|
|
|
}
|
|
|
|
|
|
for key, val := range args.Paramters {
|
|
|
|
|
|
templateParam[key] = val
|
|
|
|
|
|
}
|
|
|
|
|
|
jsonBytes, _ := json.Marshal(templateParam)
|
|
|
|
|
|
|
|
|
|
|
|
params := map[string]any{
|
|
|
|
|
|
// 必填,接收短信的手机号码
|
|
|
|
|
|
"PhoneNumbers": tea.String(cast.ToString(args.Phone)),
|
|
|
|
|
|
// 必填,短信签名名称
|
|
|
|
|
|
"SignName": tea.String(cast.ToString(args.SignName)),
|
|
|
|
|
|
// 必填,短信模板ID
|
|
|
|
|
|
"TemplateCode": tea.String(cast.ToString(args.TemplateCode)),
|
|
|
|
|
|
// 可选,模板参数
|
|
|
|
|
|
"TemplateParam": string(jsonBytes),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
runtime := &dara.RuntimeOptions{}
|
|
|
|
|
|
request := &AliYunClient.OpenApiRequest{
|
|
|
|
|
|
Query: AliYunUtil.Query(params),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
clientParams := &AliYunClient.Params{
|
|
|
|
|
|
// 接口名称
|
|
|
|
|
|
Action: tea.String("SendSms"),
|
|
|
|
|
|
// 接口版本
|
|
|
|
|
|
Version: tea.String("2017-05-25"),
|
|
|
|
|
|
// 接口协议
|
|
|
|
|
|
Protocol: tea.String("HTTPS"),
|
|
|
|
|
|
// 接口 HTTP 方法
|
|
|
|
|
|
Method: tea.String("POST"),
|
|
|
|
|
|
AuthType: tea.String("AK"),
|
|
|
|
|
|
Style: tea.String("RPC"),
|
|
|
|
|
|
// 接口 PATH
|
|
|
|
|
|
Pathname: tea.String("/"),
|
|
|
|
|
|
// 接口请求体内容格式
|
|
|
|
|
|
ReqBodyType: tea.String("json"),
|
|
|
|
|
|
// 接口响应体内容格式
|
|
|
|
|
|
BodyType: tea.String("json"),
|
|
|
|
|
|
}
|
|
|
|
|
|
fmt.Println("请求参数params为:", params)
|
|
|
|
|
|
fmt.Println("请求参数clientParams为:", clientParams)
|
|
|
|
|
|
return impl.Provider.Aliyun.CallApi(clientParams, request, runtime)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TencentSender(args *pb.SmsSendRequest) (map[string]any, error) {
|
|
|
|
|
|
return nil, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func VerifyPhone(phone string) bool {
|
|
|
|
|
|
result, _ := regexp.MatchString(`^(1[3|4|5|6|7|8|9][0-9]\d{4,8})$`, phone)
|
|
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GenValidateCode .
|
|
|
|
|
|
func GenValidateCode(width int64) string {
|
|
|
|
|
|
if width == 0 {
|
|
|
|
|
|
width = 4
|
|
|
|
|
|
}
|
|
|
|
|
|
l := 10
|
|
|
|
|
|
numeric := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
|
|
|
|
|
|
|
|
|
|
|
rand.Seed(time.Now().UnixNano())
|
|
|
|
|
|
|
|
|
|
|
|
var sb strings.Builder
|
|
|
|
|
|
for i := int64(0); i < width; i++ {
|
|
|
|
|
|
fmt.Fprintf(&sb, "%d", numeric[rand.Intn(l)])
|
|
|
|
|
|
}
|
|
|
|
|
|
return sb.String()
|
|
|
|
|
|
}
|