package sms import ( "fmt" openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" dysmsapi "github.com/alibabacloud-go/dysmsapi-20170525/v4/client" "github.com/alibabacloud-go/tea/tea" "gotest/internal/config" ) // AliyunSender 阿里云短信发送器 type AliyunSender struct { accessKeyID string accessKeySecret string signName string templateCode string client *dysmsapi.Client } // NewAliyunSender 根据阿里云短信配置创建发送器实例 func NewAliyunSender(cfg *config.AliyunSMS) *AliyunSender { return &AliyunSender{ accessKeyID: cfg.AccessKeyID, accessKeySecret: cfg.AccessKeySecret, signName: cfg.SignName, templateCode: cfg.TemplateCode, } } // Send 通过阿里云短信服务发送验证码 func (s *AliyunSender) Send(phone string, code string) error { // 关键配置校验,AccessKeyID/AccessKeySecret 为空时直接返回错误 if s.accessKeyID == "" || s.accessKeySecret == "" { return fmt.Errorf("阿里云短信未配置: AccessKeyID/AccessKeySecret 不能为空") } // 懒加载客户端,避免构造时因配置缺失直接 panic if s.client == nil { openapiCfg := &openapi.Config{ AccessKeyId: tea.String(s.accessKeyID), AccessKeySecret: tea.String(s.accessKeySecret), Endpoint: tea.String("dysmsapi.aliyuncs.com"), } client, err := dysmsapi.NewClient(openapiCfg) if err != nil { return fmt.Errorf("创建阿里云短信客户端失败: %w", err) } s.client = client } // 构造发送请求,TemplateParam 为 JSON 格式的模板参数 req := &dysmsapi.SendSmsRequest{ PhoneNumbers: tea.String(phone), SignName: tea.String(s.signName), TemplateCode: tea.String(s.templateCode), TemplateParam: tea.String(fmt.Sprintf(`{"code":"%s"}`, code)), } // 调用阿里云短信接口发送 resp, err := s.client.SendSms(req) if err != nil { return fmt.Errorf("阿里云短信发送失败: %w", err) } // 校验业务返回码,Code 为 "OK" 表示发送成功 if resp.Body == nil || resp.Body.Code == nil || *resp.Body.Code != "OK" { errMsg := "未知错误" if resp.Body != nil && resp.Body.Message != nil { errMsg = *resp.Body.Message } return fmt.Errorf("阿里云短信发送失败: %s", errMsg) } return nil }