Files
chat-deploy/pkg/sms/bao.go
kim.dev.6789 b7f8db7d08 复制项目
2026-01-14 22:35:45 +08:00

160 lines
4.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sms
import (
"context"
"crypto/md5"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log"
)
func NewBao(endpoint, accessKeyId, accessKeySecret, signName, verificationCodeTemplateCode string) (SMS, error) {
return &bao{
endpoint: endpoint,
accessKeyId: accessKeyId,
accessKeySecret: accessKeySecret,
signName: signName,
verificationCodeTemplateCode: verificationCodeTemplateCode,
statusMap: map[string]string{
"0": "短信发送成功",
"-1": "参数不全",
"-2": "服务器空间不支持请确认支持curl或者fsocket",
"30": "密码错误",
"40": "账号不存在",
"41": "余额不足",
"42": "帐户已过期",
"43": "IP地址限制",
"50": "内容含有敏感词",
"51": "手机号码格式错误",
"52": "短信内容为空",
},
}, nil
}
type bao struct {
endpoint string
accessKeyId string
accessKeySecret string
signName string
verificationCodeTemplateCode string
statusMap map[string]string
}
func (b *bao) Name() string {
return "bao-sms"
}
func (b *bao) SendCode(ctx context.Context, areaCode string, phoneNumber string, verifyCode string) error {
// 去除国家码中的+号
areaCode = strings.TrimPrefix(areaCode, "+")
// 构建完整手机号
var fullPhoneNumber string
// 对于中国手机号短信宝只需要11位数字
if areaCode == "86" || areaCode == "086" {
fullPhoneNumber = phoneNumber // 只发送手机号,不加国家码
} else {
fullPhoneNumber = areaCode + phoneNumber
}
log.ZInfo(ctx, "SMSBao Build Phone", "areaCode", areaCode, "phoneNumber", phoneNumber, "fullPhoneNumber", fullPhoneNumber)
// 密码处理如果已经是MD5格式32位十六进制直接使用否则进行MD5加密
password := b.accessKeySecret
// 检查是否是MD5格式32位十六进制小写字符串
if len(b.accessKeySecret) != 32 || !isHexString(b.accessKeySecret) {
password = fmt.Sprintf("%x", md5.Sum([]byte(b.accessKeySecret)))
}
// 构建短信内容
content := fmt.Sprintf("您的验证码是%s。如非本人操作请忽略本短信", verifyCode)
if b.signName != "" {
content = fmt.Sprintf("【%s】%s", b.signName, content)
}
log.ZInfo(ctx, "SMSBao Content", "content", content)
// 构建请求URL - 短信宝API: https://api.smsbao.com/sms?u=用户名&p=MD5密码&m=手机号&c=内容
// 如果endpoint已包含完整路径直接使用否则追加/sms
smsURL := b.endpoint
if !strings.Contains(smsURL, "/sms") {
if !strings.HasSuffix(smsURL, "/") {
smsURL += "/"
}
smsURL += "sms"
}
apiURL := fmt.Sprintf("%s?u=%s&p=%s&m=%s&c=%s",
smsURL,
url.QueryEscape(b.accessKeyId),
url.QueryEscape(password),
url.QueryEscape(fullPhoneNumber),
url.QueryEscape(content),
)
log.ZInfo(ctx, "SMSBao Request", "url", apiURL, "phone", fullPhoneNumber, "areaCode", areaCode, "phoneNumber", phoneNumber)
req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
if err != nil {
return errs.Wrap(err)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.ZError(ctx, "SMSBao Request Failed", err, "phone", fullPhoneNumber)
return errs.Wrap(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.ZError(ctx, "SMSBao Read Response Failed", err, "phone", fullPhoneNumber)
return errs.Wrap(err)
}
result := strings.TrimSpace(string(body))
log.ZInfo(ctx, "SMSBao Response", "phone", fullPhoneNumber, "status", resp.StatusCode, "result", result, "rawBody", string(body))
// 返回0表示成功
if result != "0" {
errMsg := b.statusMap[result]
if errMsg == "" {
errMsg = fmt.Sprintf("未知错误代码: %s", result)
}
log.ZError(ctx, "SMSBao Failed", fmt.Errorf("%s", errMsg), "code", result, "phone", fullPhoneNumber)
return errs.New(errMsg)
}
return nil
}
// isHexString 检查字符串是否是纯十六进制字符
func isHexString(s string) bool {
for _, c := range s {
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) {
return false
}
}
return true
}