feat: 添加支持多个短信服务商支持 添加短信宝服务商支持,同时添加配置示例

This commit is contained in:
whale_fall 2024-01-22 16:38:44 +08:00
parent 3e99b4cbf6
commit e5e762efcd
11 changed files with 193 additions and 21 deletions

View File

@ -33,6 +33,23 @@ WeChatBot = false
Sign = "" Sign = ""
CodeTempId = "" CodeTempId = ""
[Sms] # Sms 配置,用于发送短信
Active = "SmsBao" # 默认使用
[Sms.SmsBao]
Account = "" #账号
ApiKey = "" # apikey
Domain = "api.smsbao.com" # 发送短信的域名
Sign = "【鲸落科技】" # 签名
CodeTemplate = "您的验证码是{code}。请于{num}分钟内使用,若非本人操作,请忽略本短信。" # 短信模板
Num = "30" # 短信有效期
[Sms.Ali]
AccessKey = ""
AccessSecret = ""
Product = "Dysmsapi"
Domain = "dysmsapi.aliyuncs.com"
Sign = ""
CodeTempId = ""
[OSS] # OSS 配置,用于存储 MJ 绘画图片 [OSS] # OSS 配置,用于存储 MJ 绘画图片
Active = "local" # 默认使用本地文件存储引擎 Active = "local" # 默认使用本地文件存储引擎
[OSS.Local] [OSS.Local]

View File

@ -17,6 +17,7 @@ type AppConfig struct {
ApiConfig ChatPlusApiConfig // ChatPlus API authorization configs ApiConfig ChatPlusApiConfig // ChatPlus API authorization configs
SmsConfig AliYunSmsConfig // AliYun send message service config SmsConfig AliYunSmsConfig // AliYun send message service config
OSS OSSConfig // OSS config OSS OSSConfig // OSS config
SMS SMSConfig // sms config
MjConfigs []MidJourneyConfig // mj AI draw service pool MjConfigs []MidJourneyConfig // mj AI draw service pool
WeChatBot bool // 是否启用微信机器人 WeChatBot bool // 是否启用微信机器人
SdConfigs []StableDiffusionConfig // sd AI draw service pool SdConfigs []StableDiffusionConfig // sd AI draw service pool
@ -71,14 +72,14 @@ type StableDiffusionConfig struct {
Txt2ImgJsonPath string Txt2ImgJsonPath string
} }
type AliYunSmsConfig struct { //type AliYunSmsConfig struct {
AccessKey string // AccessKey string
AccessSecret string // AccessSecret string
Product string // Product string
Domain string // Domain string
Sign string // 短信签名 // Sign string // 短信签名
CodeTempId string // 验证码短信模板 ID // CodeTempId string // 验证码短信模板 ID
} //}
type AlipayConfig struct { type AlipayConfig struct {
Enabled bool // 是否启用该支付通道 Enabled bool // 是否启用该支付通道

25
api/core/types/sms.go Normal file
View File

@ -0,0 +1,25 @@
package types
type SMSConfig struct {
Active string
ALI AliYunSmsConfig
SMSBAO SmsBaoSmsConfig
}
type AliYunSmsConfig struct {
AccessKey string
AccessSecret string
Product string
Domain string
Sign string // 短信签名
CodeTempId string // 验证码短信模板 ID
}
type SmsBaoSmsConfig struct {
Account string //短信包平台注册的用户名
ApiKey string //apiKey
Domain string //域名
Sign string // 短信签名
CodeTemplate string // 验证码短信模板 匹配
Num string // 实效性
}

View File

@ -4,6 +4,7 @@ import (
"chatplus/core" "chatplus/core"
"chatplus/core/types" "chatplus/core/types"
"chatplus/service" "chatplus/service"
"chatplus/service/sms"
"chatplus/utils" "chatplus/utils"
"chatplus/utils/resp" "chatplus/utils/resp"
"strings" "strings"
@ -17,7 +18,7 @@ const CodeStorePrefix = "/verify/codes/"
type SmsHandler struct { type SmsHandler struct {
BaseHandler BaseHandler
redis *redis.Client redis *redis.Client
sms *service.AliYunSmsService sms *sms.SmsServiceManager
smtp *service.SmtpService smtp *service.SmtpService
captcha *service.CaptchaService captcha *service.CaptchaService
} }
@ -25,7 +26,7 @@ type SmsHandler struct {
func NewSmsHandler( func NewSmsHandler(
app *core.AppServer, app *core.AppServer,
client *redis.Client, client *redis.Client,
sms *service.AliYunSmsService, sms *sms.SmsServiceManager,
smtp *service.SmtpService, smtp *service.SmtpService,
captcha *service.CaptchaService) *SmsHandler { captcha *service.CaptchaService) *SmsHandler {
handler := &SmsHandler{redis: client, sms: sms, captcha: captcha, smtp: smtp} handler := &SmsHandler{redis: client, sms: sms, captcha: captcha, smtp: smtp}
@ -63,7 +64,8 @@ func (h *SmsHandler) SendCode(c *gin.Context) {
resp.ERROR(c, "系统已禁用手机号注册!") resp.ERROR(c, "系统已禁用手机号注册!")
return return
} }
err = h.sms.SendVerifyCode(data.Receiver, code) err = h.sms.GetUploadHandler().SendVerifyCode(data.Receiver, code)
} }
if err != nil { if err != nil {
resp.ERROR(c, err.Error()) resp.ERROR(c, err.Error())

View File

@ -12,6 +12,7 @@ import (
"chatplus/service/oss" "chatplus/service/oss"
"chatplus/service/payment" "chatplus/service/payment"
"chatplus/service/sd" "chatplus/service/sd"
"chatplus/service/sms"
"chatplus/service/wx" "chatplus/service/wx"
"chatplus/store" "chatplus/store"
"context" "context"
@ -135,7 +136,7 @@ func main() {
fx.Provide(admin.NewOrderHandler), fx.Provide(admin.NewOrderHandler),
// 创建服务 // 创建服务
fx.Provide(service.NewAliYunSmsService), fx.Provide(sms.NewSendServiceManager),
fx.Provide(func(config *types.AppConfig) *service.CaptchaService { fx.Provide(func(config *types.AppConfig) *service.CaptchaService {
return service.NewCaptchaService(config.ApiConfig) return service.NewCaptchaService(config.ApiConfig)
}), }),

View File

@ -1,4 +1,4 @@
package service package sms
import ( import (
"chatplus/core/types" "chatplus/core/types"
@ -11,18 +11,19 @@ type AliYunSmsService struct {
client *dysmsapi.Client client *dysmsapi.Client
} }
func NewAliYunSmsService(config *types.AppConfig) (*AliYunSmsService, error) { func NewAliYunSmsService(appConfig *types.AppConfig) (*AliYunSmsService, error) {
config := &appConfig.SMS.ALI
// 创建阿里云短信客户端 // 创建阿里云短信客户端
client, err := dysmsapi.NewClientWithAccessKey( client, err := dysmsapi.NewClientWithAccessKey(
"cn-hangzhou", "cn-hangzhou",
config.SmsConfig.AccessKey, config.AccessKey,
config.SmsConfig.AccessSecret) config.AccessSecret)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create client: %v", err) return nil, fmt.Errorf("failed to create client: %v", err)
} }
return &AliYunSmsService{ return &AliYunSmsService{
config: &config.SmsConfig, config: config,
client: client, client: client,
}, nil }, nil
} }
@ -46,7 +47,6 @@ func (s *AliYunSmsService) SendVerifyCode(mobile string, code int) error {
if response.Code != "OK" { if response.Code != "OK" {
return fmt.Errorf("failed to send SMS:%v", response.Message) return fmt.Errorf("failed to send SMS:%v", response.Message)
} }
return nil return nil
} }

View File

@ -1,4 +1,4 @@
package service package sms
type SmsService interface { type SmsService interface {
SendVerifyCode(mobile string, code int) error SendVerifyCode(mobile string, code int) error

View File

@ -0,0 +1,39 @@
package sms
import (
"chatplus/core/types"
"strings"
)
type SmsServiceManager struct {
handler SmsService
}
const Ali = "Ali"
const SmsBao = "SmsBao"
func NewSendServiceManager(config *types.AppConfig) (*SmsServiceManager, error) {
active := SmsBao
if config.OSS.Active != "" {
active = strings.ToUpper(config.SMS.Active)
}
var handler SmsService
switch active {
case Ali:
client, err := NewAliYunSmsService(config)
if err != nil {
return nil, err
}
handler = client
break
case SmsBao:
handler = NewSmsBaoSmsService(config)
break
}
return &SmsServiceManager{handler: handler}, nil
}
func (m *SmsServiceManager) GetUploadHandler() SmsService {
return m.handler
}

View File

@ -0,0 +1,86 @@
package sms
import (
"chatplus/core/types"
logger2 "chatplus/logger"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
)
var logger = logger2.GetLogger()
type SmsBaoSmsService struct {
config *types.SmsBaoSmsConfig
}
func NewSmsBaoSmsService(appConfig *types.AppConfig) *SmsBaoSmsService {
return &SmsBaoSmsService{
config: &appConfig.SMS.SMSBAO,
}
}
var statusStr = map[string]string{
"0": "短信发送成功",
"-1": "参数不全",
"-2": "服务器空间不支持请确认支持curl或者fsocket联系您的空间商解决或者更换空间",
"30": "密码错误",
"40": "账号不存在",
"41": "余额不足",
"42": "账户已过期",
"43": "IP地址限制",
"50": "内容含有敏感词",
}
func (s *SmsBaoSmsService) SendVerifyCode(mobile string, code int) error {
content := fmt.Sprintf("%s%s", s.config.Sign, s.config.CodeTemplate)
template := replaceTemplate(content, s.config.Num, code)
md5Hash := s.config.ApiKey
params := url.Values{}
params.Set("u", s.config.Account)
params.Set("p", md5Hash)
params.Set("m", mobile)
params.Set("c", template)
// 判断 s.config.Domain 是否为空
if s.config.Domain == "" {
// 设置默认值
s.config.Domain = "api.smsbao.com"
// 记录日志,提醒用户默认值被使用
logger.Infof("SmsBao.config.Domain is empty. Using default value: %s", s.config.Domain)
}
real_url := fmt.Sprintf("https://%s/sms?", s.config.Domain)
sendURL := real_url + params.Encode()
logger.Infof("send SmsBao content: %v", template)
response, err := http.Get(sendURL)
if err != nil {
return err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
result := string(body)
logger.Infof("send SmsBao result: %v", statusStr[result])
if result != "0" {
return fmt.Errorf("failed to send SMS:%v", statusStr[result])
}
return nil
}
func replaceTemplate(template, num string, code int) string {
result := strings.ReplaceAll(template, "{code}", strconv.Itoa(code))
result = strings.ReplaceAll(result, "{num}", num)
return result
}
var _ SmsService = &SmsBaoSmsService{}

View File

@ -0,0 +1 @@
package wanx

View File

@ -176,8 +176,8 @@ const platforms = ref([
api_url: "https://chat-bot-api.openai.azure.com/openai/deployments/{model}/chat/completions?api-version=2023-05-15" api_url: "https://chat-bot-api.openai.azure.com/openai/deployments/{model}/chat/completions?api-version=2023-05-15"
}, },
{ {
name: "【阿里】千义通问", name: "【阿里】义千问",
value: "Qwen", value: "Ali",
api_url: "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation" api_url: "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
}, },
]) ])