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 "" }