Files
verify/backend/internal/service/sms.go
T
2026-04-27 17:22:56 +08:00

134 lines
3.4 KiB
Go

package service
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
)
type SmsService struct{}
func NewSmsService() *SmsService {
return &SmsService{}
}
type SmsConfig struct {
Provider string
AccessKey string
SecretKey string
SignName string
TemplateCode string
}
func (s *SmsService) SendSms(config SmsConfig, phone, code string) error {
switch config.Provider {
case "aliyun":
return s.sendAliyunSms(config, phone, code)
case "tencent":
return s.sendTencentSms(config, phone, code)
case "huawei":
return s.sendHuaweiSms(config, phone, code)
default:
return fmt.Errorf("不支持的短信服务商: %s", config.Provider)
}
}
func (s *SmsService) sendAliyunSms(config SmsConfig, phone, code string) error {
params := url.Values{}
params.Set("AccessKeyId", config.AccessKey)
params.Set("Action", "SendSms")
params.Set("Format", "JSON")
params.Set("PhoneNumbers", phone)
params.Set("RegionId", "cn-hangzhou")
params.Set("SignName", config.SignName)
params.Set("SignatureMethod", "HMAC-SHA1")
params.Set("SignatureNonce", fmt.Sprintf("%d", getTimeStamp()))
params.Set("SignatureVersion", "1.0")
params.Set("TemplateCode", config.TemplateCode)
params.Set("TemplateParam", fmt.Sprintf(`{"code":"%s"}`, code))
params.Set("Timestamp", getTimeStampStr())
params.Set("Version", "2017-05-25")
signature := s.generateAliyunSignature(params, config.SecretKey)
params.Set("Signature", signature)
resp, err := http.Get(fmt.Sprintf("https://dysmsapi.aliyuncs.com/?%s", params.Encode()))
if err != nil {
return fmt.Errorf("请求阿里云短信API失败: %v", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("读取响应失败: %v", err)
}
var result struct {
Code string `json:"Code"`
Message string `json:"Message"`
}
if err := json.Unmarshal(body, &result); err != nil {
return fmt.Errorf("解析响应失败: %v", err)
}
if result.Code != "OK" {
return fmt.Errorf("发送失败: %s", result.Message)
}
return nil
}
func (s *SmsService) generateAliyunSignature(params url.Values, secretKey string) string {
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
encodedParams := make([]string, 0, len(params))
for _, k := range keys {
encodedParams = append(encodedParams, fmt.Sprintf("%s=%s", specialUrlEncode(k), specialUrlEncode(params.Get(k))))
}
sortParams := strings.Join(encodedParams, "&")
stringToSign := fmt.Sprintf("GET&%s&%s", specialUrlEncode("/"), specialUrlEncode(sortParams))
signature := hmacSHA1(secretKey+"&", stringToSign)
return signature
}
func (s *SmsService) sendTencentSms(config SmsConfig, phone, code string) error {
return fmt.Errorf("腾讯云短信服务暂未实现")
}
func (s *SmsService) sendHuaweiSms(config SmsConfig, phone, code string) error {
return fmt.Errorf("华为云短信服务暂未实现")
}
func (s *SmsService) SendTestSms(config SmsConfig, phone string) error {
code := "123456"
return s.SendSms(config, phone, code)
}
func specialUrlEncode(s string) string {
encoded := url.QueryEscape(s)
encoded = strings.ReplaceAll(encoded, "+", "%20")
encoded = strings.ReplaceAll(encoded, "*", "%2A")
encoded = strings.ReplaceAll(encoded, "%7E", "~")
return encoded
}
func hmacSHA1(key, data string) string {
return data
}
func getTimeStamp() int64 {
return 0
}
func getTimeStampStr() string {
return ""
}