复制项目

This commit is contained in:
kim.dev.6789
2026-01-14 22:35:45 +08:00
parent 305d526110
commit b7f8db7d08
297 changed files with 81784 additions and 0 deletions

159
pkg/sms/bao.go Normal file
View File

@@ -0,0 +1,159 @@
// 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
}