对接阿里云短信OK
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
package sms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/utils"
|
||||
dysms "github.com/alibabacloud-go/dysmsapi-20170525/v5/client"
|
||||
"github.com/alibabacloud-go/tea/dara"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AliyunConfig struct {
|
||||
AccessKeyID string
|
||||
AccessKeySecret string
|
||||
Endpoint string
|
||||
SignName string
|
||||
LoginTemplateCode string
|
||||
}
|
||||
|
||||
type AliyunProvider struct {
|
||||
client *dysms.Client
|
||||
signName string
|
||||
loginTemplateCode string
|
||||
log *zap.Logger
|
||||
}
|
||||
|
||||
func NewAliyunProvider(cfg AliyunConfig, log *zap.Logger) (*AliyunProvider, error) {
|
||||
cfg.Endpoint = strings.TrimSpace(cfg.Endpoint)
|
||||
if cfg.Endpoint == "" {
|
||||
cfg.Endpoint = "dysmsapi.aliyuncs.com"
|
||||
}
|
||||
if strings.TrimSpace(cfg.AccessKeyID) == "" ||
|
||||
strings.TrimSpace(cfg.AccessKeySecret) == "" ||
|
||||
strings.TrimSpace(cfg.SignName) == "" ||
|
||||
strings.TrimSpace(cfg.LoginTemplateCode) == "" {
|
||||
return nil, ErrProviderConfigInvalid
|
||||
}
|
||||
|
||||
client, err := dysms.NewClient(&openapi.Config{
|
||||
AccessKeyId: dara.String(strings.TrimSpace(cfg.AccessKeyID)),
|
||||
AccessKeySecret: dara.String(strings.TrimSpace(cfg.AccessKeySecret)),
|
||||
Endpoint: dara.String(cfg.Endpoint),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AliyunProvider{
|
||||
client: client,
|
||||
signName: strings.TrimSpace(cfg.SignName),
|
||||
loginTemplateCode: strings.TrimSpace(cfg.LoginTemplateCode),
|
||||
log: log,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *AliyunProvider) SendLoginCode(ctx context.Context, phone string, code string) error {
|
||||
params, err := json.Marshal(map[string]string{"code": code})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := &dysms.SendSmsRequest{}
|
||||
req.SetPhoneNumbers(phone)
|
||||
req.SetSignName(p.signName)
|
||||
req.SetTemplateCode(p.loginTemplateCode)
|
||||
req.SetTemplateParam(string(params))
|
||||
|
||||
resp, err := p.client.SendSmsWithContext(ctx, req, &dara.RuntimeOptions{})
|
||||
if err != nil {
|
||||
if p.log != nil {
|
||||
p.log.Warn("aliyun sms send failed", zap.String("phone", maskPhone(phone)), zap.Error(err))
|
||||
}
|
||||
return fmt.Errorf("aliyun sms send failed: %w", err)
|
||||
}
|
||||
if resp == nil || resp.Body == nil {
|
||||
return fmt.Errorf("aliyun sms empty response")
|
||||
}
|
||||
|
||||
resultCode := dara.StringValue(resp.Body.Code)
|
||||
if resultCode != "OK" {
|
||||
if p.log != nil {
|
||||
p.log.Warn(
|
||||
"aliyun sms rejected",
|
||||
zap.String("phone", maskPhone(phone)),
|
||||
zap.String("request_id", dara.StringValue(resp.Body.RequestId)),
|
||||
zap.String("code", resultCode),
|
||||
zap.String("message", dara.StringValue(resp.Body.Message)),
|
||||
)
|
||||
}
|
||||
return fmt.Errorf("aliyun sms rejected: %s", resultCode)
|
||||
}
|
||||
|
||||
if p.log != nil {
|
||||
p.log.Info(
|
||||
"aliyun sms sent",
|
||||
zap.String("phone", maskPhone(phone)),
|
||||
zap.String("request_id", dara.StringValue(resp.Body.RequestId)),
|
||||
zap.String("biz_id", dara.StringValue(resp.Body.BizId)),
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func maskPhone(phone string) string {
|
||||
if len(phone) < 7 {
|
||||
return phone
|
||||
}
|
||||
return phone[:3] + "****" + phone[len(phone)-4:]
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package sms
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type MockProvider struct {
|
||||
log *zap.Logger
|
||||
}
|
||||
|
||||
func NewMockProvider(log *zap.Logger) *MockProvider {
|
||||
return &MockProvider{log: log}
|
||||
}
|
||||
|
||||
func (p *MockProvider) SendLoginCode(ctx context.Context, phone string, code string) error {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
if p.log != nil {
|
||||
p.log.Info("mock sms code generated", zap.String("phone", phone), zap.String("code", code))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package sms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var ErrProviderConfigInvalid = errors.New("sms provider config invalid")
|
||||
|
||||
type Provider interface {
|
||||
SendLoginCode(ctx context.Context, phone string, code string) error
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package sms
|
||||
|
||||
import "context"
|
||||
|
||||
type UnavailableProvider struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func NewUnavailableProvider(err error) *UnavailableProvider {
|
||||
return &UnavailableProvider{err: err}
|
||||
}
|
||||
|
||||
func (p *UnavailableProvider) SendLoginCode(ctx context.Context, phone string, code string) error {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
if p.err != nil {
|
||||
return p.err
|
||||
}
|
||||
return ErrProviderConfigInvalid
|
||||
}
|
||||
Reference in New Issue
Block a user