diff --git a/internal/sms/tencent.go b/internal/sms/tencent.go new file mode 100644 index 0000000..06fcea2 --- /dev/null +++ b/internal/sms/tencent.go @@ -0,0 +1,82 @@ +package sms + +import ( + "fmt" + + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" + tencentSms "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms/v20210111" + + "gotest/internal/config" +) + +// TencentSender 腾讯云短信发送器 +type TencentSender struct { + secretID string + secretKey string + signName string + templateID string + appID string + client *tencentSms.Client +} + +// NewTencentSender 根据腾讯云短信配置创建发送器实例 +func NewTencentSender(cfg *config.TencentSMS) *TencentSender { + return &TencentSender{ + secretID: cfg.SecretID, + secretKey: cfg.SecretKey, + signName: cfg.SignName, + templateID: cfg.TemplateID, + appID: cfg.AppID, + } +} + +// Send 通过腾讯云短信服务发送验证码 +func (s *TencentSender) Send(phone string, code string) error { + // 关键配置校验,SecretID/SecretKey 为空时直接返回错误 + if s.secretID == "" || s.secretKey == "" { + return fmt.Errorf("腾讯云短信未配置: SecretID/SecretKey 不能为空") + } + + // 懒加载客户端,避免构造时因配置缺失直接 panic + if s.client == nil { + credential := common.NewCredential(s.secretID, s.secretKey) + cpf := profile.NewClientProfile() + // 短信服务默认使用 ap-guangzhou 区域 + client, err := tencentSms.NewClient(credential, "ap-guangzhou", cpf) + if err != nil { + return fmt.Errorf("创建腾讯云短信客户端失败: %w", err) + } + s.client = client + } + + // 构造发送请求 + // 腾讯云要求手机号带国家码,默认拼接 +86 + req := tencentSms.NewSendSmsRequest() + req.SmsSdkAppId = common.StringPtr(s.appID) + req.SignName = common.StringPtr(s.signName) + req.TemplateId = common.StringPtr(s.templateID) + req.TemplateParamSet = common.StringPtrs([]string{code}) + req.PhoneNumberSet = common.StringPtrs([]string{"+86" + phone}) + + // 调用腾讯云短信接口发送 + resp, err := s.client.SendSms(req) + if err != nil { + return fmt.Errorf("腾讯云短信发送失败: %w", err) + } + + // 校验业务返回码,SendStatusSet 中第一条记录的 Code 为 "Ok" 表示发送成功 + if resp.Response == nil || len(resp.Response.SendStatusSet) == 0 { + return fmt.Errorf("腾讯云短信发送失败: 无返回结果") + } + status := resp.Response.SendStatusSet[0] + if status.Code == nil || *status.Code != "Ok" { + errMsg := "未知错误" + if status.Message != nil { + errMsg = *status.Message + } + return fmt.Errorf("腾讯云短信发送失败: %s", errMsg) + } + + return nil +}