mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	feat: 添加支持多个短信服务商支持 添加短信宝服务商支持,同时添加配置示例
This commit is contained in:
		@@ -33,6 +33,23 @@ WeChatBot = false
 | 
			
		||||
  Sign = ""
 | 
			
		||||
  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 绘画图片
 | 
			
		||||
   Active = "local" # 默认使用本地文件存储引擎
 | 
			
		||||
   [OSS.Local]
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ type AppConfig struct {
 | 
			
		||||
	ApiConfig ChatPlusApiConfig       // ChatPlus API authorization configs
 | 
			
		||||
	SmsConfig AliYunSmsConfig         // AliYun send message service config
 | 
			
		||||
	OSS       OSSConfig               // OSS config
 | 
			
		||||
	SMS       SMSConfig               // sms config
 | 
			
		||||
	MjConfigs []MidJourneyConfig      // mj AI draw service pool
 | 
			
		||||
	WeChatBot bool                    // 是否启用微信机器人
 | 
			
		||||
	SdConfigs []StableDiffusionConfig // sd AI draw service pool
 | 
			
		||||
@@ -71,14 +72,14 @@ type StableDiffusionConfig struct {
 | 
			
		||||
	Txt2ImgJsonPath string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AliYunSmsConfig struct {
 | 
			
		||||
	AccessKey    string
 | 
			
		||||
	AccessSecret string
 | 
			
		||||
	Product      string
 | 
			
		||||
	Domain       string
 | 
			
		||||
	Sign         string // 短信签名
 | 
			
		||||
	CodeTempId   string // 验证码短信模板 ID
 | 
			
		||||
}
 | 
			
		||||
//type AliYunSmsConfig struct {
 | 
			
		||||
//	AccessKey    string
 | 
			
		||||
//	AccessSecret string
 | 
			
		||||
//	Product      string
 | 
			
		||||
//	Domain       string
 | 
			
		||||
//	Sign         string // 短信签名
 | 
			
		||||
//	CodeTempId   string // 验证码短信模板 ID
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
type AlipayConfig struct {
 | 
			
		||||
	Enabled         bool   // 是否启用该支付通道
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								api/core/types/sms.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								api/core/types/sms.go
									
									
									
									
									
										Normal 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 // 实效性
 | 
			
		||||
}
 | 
			
		||||
@@ -4,6 +4,7 @@ import (
 | 
			
		||||
	"chatplus/core"
 | 
			
		||||
	"chatplus/core/types"
 | 
			
		||||
	"chatplus/service"
 | 
			
		||||
	"chatplus/service/sms"
 | 
			
		||||
	"chatplus/utils"
 | 
			
		||||
	"chatplus/utils/resp"
 | 
			
		||||
	"strings"
 | 
			
		||||
@@ -17,7 +18,7 @@ const CodeStorePrefix = "/verify/codes/"
 | 
			
		||||
type SmsHandler struct {
 | 
			
		||||
	BaseHandler
 | 
			
		||||
	redis   *redis.Client
 | 
			
		||||
	sms     *service.AliYunSmsService
 | 
			
		||||
	sms     *sms.SmsServiceManager
 | 
			
		||||
	smtp    *service.SmtpService
 | 
			
		||||
	captcha *service.CaptchaService
 | 
			
		||||
}
 | 
			
		||||
@@ -25,7 +26,7 @@ type SmsHandler struct {
 | 
			
		||||
func NewSmsHandler(
 | 
			
		||||
	app *core.AppServer,
 | 
			
		||||
	client *redis.Client,
 | 
			
		||||
	sms *service.AliYunSmsService,
 | 
			
		||||
	sms *sms.SmsServiceManager,
 | 
			
		||||
	smtp *service.SmtpService,
 | 
			
		||||
	captcha *service.CaptchaService) *SmsHandler {
 | 
			
		||||
	handler := &SmsHandler{redis: client, sms: sms, captcha: captcha, smtp: smtp}
 | 
			
		||||
@@ -63,7 +64,8 @@ func (h *SmsHandler) SendCode(c *gin.Context) {
 | 
			
		||||
			resp.ERROR(c, "系统已禁用手机号注册!")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		err = h.sms.SendVerifyCode(data.Receiver, code)
 | 
			
		||||
		err = h.sms.GetUploadHandler().SendVerifyCode(data.Receiver, code)
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.ERROR(c, err.Error())
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import (
 | 
			
		||||
	"chatplus/service/oss"
 | 
			
		||||
	"chatplus/service/payment"
 | 
			
		||||
	"chatplus/service/sd"
 | 
			
		||||
	"chatplus/service/sms"
 | 
			
		||||
	"chatplus/service/wx"
 | 
			
		||||
	"chatplus/store"
 | 
			
		||||
	"context"
 | 
			
		||||
@@ -135,7 +136,7 @@ func main() {
 | 
			
		||||
		fx.Provide(admin.NewOrderHandler),
 | 
			
		||||
 | 
			
		||||
		// 创建服务
 | 
			
		||||
		fx.Provide(service.NewAliYunSmsService),
 | 
			
		||||
		fx.Provide(sms.NewSendServiceManager),
 | 
			
		||||
		fx.Provide(func(config *types.AppConfig) *service.CaptchaService {
 | 
			
		||||
			return service.NewCaptchaService(config.ApiConfig)
 | 
			
		||||
		}),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package service
 | 
			
		||||
package sms
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"chatplus/core/types"
 | 
			
		||||
@@ -11,18 +11,19 @@ type AliYunSmsService struct {
 | 
			
		||||
	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(
 | 
			
		||||
		"cn-hangzhou",
 | 
			
		||||
		config.SmsConfig.AccessKey,
 | 
			
		||||
		config.SmsConfig.AccessSecret)
 | 
			
		||||
		config.AccessKey,
 | 
			
		||||
		config.AccessSecret)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to create client: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &AliYunSmsService{
 | 
			
		||||
		config: &config.SmsConfig,
 | 
			
		||||
		config: config,
 | 
			
		||||
		client: client,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -46,7 +47,6 @@ func (s *AliYunSmsService) SendVerifyCode(mobile string, code int) error {
 | 
			
		||||
	if response.Code != "OK" {
 | 
			
		||||
		return fmt.Errorf("failed to send SMS:%v", response.Message)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package service
 | 
			
		||||
package sms
 | 
			
		||||
 | 
			
		||||
type SmsService interface {
 | 
			
		||||
	SendVerifyCode(mobile string, code int) error
 | 
			
		||||
							
								
								
									
										39
									
								
								api/service/sms/sms_service_manager.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								api/service/sms/sms_service_manager.go
									
									
									
									
									
										Normal 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										86
									
								
								api/service/sms/smsbao_sms_service.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								api/service/sms/smsbao_sms_service.go
									
									
									
									
									
										Normal 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{}
 | 
			
		||||
							
								
								
									
										1
									
								
								api/service/wanx/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								api/service/wanx/types.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
package wanx
 | 
			
		||||
@@ -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"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: "【阿里】千义通问",
 | 
			
		||||
    value: "Qwen",
 | 
			
		||||
    name: "【阿里】通义千问",
 | 
			
		||||
    value: "Ali",
 | 
			
		||||
    api_url: "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
 | 
			
		||||
  },
 | 
			
		||||
])
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user