mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 08:13:43 +08:00 
			
		
		
		
	feat: merge sms branch,add DuanXinBao sms service implemetation
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]
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ type AppConfig struct {
 | 
			
		||||
	StaticUrl     string                  // 静态资源 URL
 | 
			
		||||
	Redis         RedisConfig             // redis 连接信息
 | 
			
		||||
	ApiConfig     ChatPlusApiConfig       // ChatPlus API authorization configs
 | 
			
		||||
	SmsConfig     AliYunSmsConfig         // AliYun send message service config
 | 
			
		||||
	SMS           SMSConfig               // send mobile message config
 | 
			
		||||
	OSS           OSSConfig               // OSS config
 | 
			
		||||
	MjConfigs     []MidJourneyConfig      // mj AI draw service pool
 | 
			
		||||
	MjPlusConfigs []MidJourneyPlusConfig  // MJ plus config
 | 
			
		||||
@@ -69,15 +69,6 @@ type MidJourneyPlusConfig struct {
 | 
			
		||||
	NotifyURL string // 任务进度更新回调地址
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AliYunSmsConfig struct {
 | 
			
		||||
	AccessKey    string
 | 
			
		||||
	AccessSecret string
 | 
			
		||||
	Product      string
 | 
			
		||||
	Domain       string
 | 
			
		||||
	Sign         string // 短信签名
 | 
			
		||||
	CodeTempId   string // 验证码短信模板 ID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AlipayConfig struct {
 | 
			
		||||
	Enabled         bool   // 是否启用该支付通道
 | 
			
		||||
	SandBox         bool   // 是否沙盒环境
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								api/core/types/sms.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								api/core/types/sms.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
type SMSConfig struct {
 | 
			
		||||
	Active string
 | 
			
		||||
	Ali    SmsConfigAli
 | 
			
		||||
	Bao    SmsConfigBao
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SmsConfigAli 阿里云短信平台配置
 | 
			
		||||
type SmsConfigAli struct {
 | 
			
		||||
	AccessKey    string
 | 
			
		||||
	AccessSecret string
 | 
			
		||||
	Product      string
 | 
			
		||||
	Domain       string
 | 
			
		||||
	Sign         string // 短信签名
 | 
			
		||||
	CodeTempId   string // 验证码短信模板 ID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SmsConfigBao 短信宝平台配置
 | 
			
		||||
type SmsConfigBao struct {
 | 
			
		||||
	Username     string //短信宝平台注册的用户名
 | 
			
		||||
	Password     string //短信宝平台注册的密码
 | 
			
		||||
	Domain       string //域名
 | 
			
		||||
	Sign         string // 短信签名
 | 
			
		||||
	CodeTemplate string // 验证码短信模板 匹配
 | 
			
		||||
}
 | 
			
		||||
@@ -507,7 +507,7 @@ func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, platf
 | 
			
		||||
	} else {
 | 
			
		||||
		client = http.DefaultClient
 | 
			
		||||
	}
 | 
			
		||||
	logger.Debugf("Sending %s request, ApiURL:%s, ApiKey:%s, PROXY: %s, Model: %s", platform, apiURL, apiKey.Value, proxyURL, req.Model)
 | 
			
		||||
	logger.Debugf("Sending %s request, ApiURL:%s, Password:%s, PROXY: %s, Model: %s", platform, apiURL, apiKey.Value, proxyURL, req.Model)
 | 
			
		||||
	switch platform {
 | 
			
		||||
	case types.Azure:
 | 
			
		||||
		request.Header.Set("api-key", apiKey.Value)
 | 
			
		||||
 
 | 
			
		||||
@@ -247,7 +247,7 @@ func (h *FunctionHandler) Dall3(c *gin.Context) {
 | 
			
		||||
	} else {
 | 
			
		||||
		request = req.C().R()
 | 
			
		||||
	}
 | 
			
		||||
	logger.Debugf("Sending %s request, ApiURL:%s, ApiKey:%s, PROXY: %s", apiKey.Platform, apiKey.ApiURL, apiKey.Value, h.proxyURL)
 | 
			
		||||
	logger.Debugf("Sending %s request, ApiURL:%s, Password:%s, PROXY: %s", apiKey.Platform, apiKey.ApiURL, apiKey.Value, h.proxyURL)
 | 
			
		||||
	r, err := request.SetHeader("Content-Type", "application/json").
 | 
			
		||||
		SetHeader("Authorization", "Bearer "+apiKey.Value).
 | 
			
		||||
		SetBody(imgReq{
 | 
			
		||||
 
 | 
			
		||||
@@ -311,7 +311,7 @@ func (h *PaymentHandler) notify(orderNo string, tradeNo string) error {
 | 
			
		||||
			user.ImgCalls += remark.ImgCalls
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} else {                 // 非 VIP 用户
 | 
			
		||||
	} else { // 非 VIP 用户
 | 
			
		||||
		if remark.Days > 0 { // vip 套餐:days > 0, calls == 0
 | 
			
		||||
			user.ExpiredTime = time.Now().AddDate(0, 0, remark.Days).Unix()
 | 
			
		||||
			user.Calls += h.App.SysConfig.VipMonthCalls
 | 
			
		||||
 
 | 
			
		||||
@@ -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.ServiceManager
 | 
			
		||||
	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.ServiceManager,
 | 
			
		||||
	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.GetService().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"
 | 
			
		||||
@@ -137,7 +138,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)
 | 
			
		||||
		}),
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,11 @@ package oss
 | 
			
		||||
 | 
			
		||||
import "github.com/gin-gonic/gin"
 | 
			
		||||
 | 
			
		||||
const Local = "LOCAL"
 | 
			
		||||
const Minio = "MINIO"
 | 
			
		||||
const QiNiu = "QINIU"
 | 
			
		||||
const AliYun = "ALIYUN"
 | 
			
		||||
 | 
			
		||||
type File struct {
 | 
			
		||||
	Name string `json:"name"`
 | 
			
		||||
	Size int64  `json:"size"`
 | 
			
		||||
 
 | 
			
		||||
@@ -9,11 +9,6 @@ type UploaderManager struct {
 | 
			
		||||
	handler Uploader
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Local = "LOCAL"
 | 
			
		||||
const Minio = "MINIO"
 | 
			
		||||
const QiNiu = "QINIU"
 | 
			
		||||
const AliYun = "ALIYUN"
 | 
			
		||||
 | 
			
		||||
func NewUploaderManager(config *types.AppConfig) (*UploaderManager, error) {
 | 
			
		||||
	active := Local
 | 
			
		||||
	if config.OSS.Active != "" {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package service
 | 
			
		||||
package sms
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"chatplus/core/types"
 | 
			
		||||
@@ -7,22 +7,23 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AliYunSmsService struct {
 | 
			
		||||
	config *types.AliYunSmsConfig
 | 
			
		||||
	config *types.SmsConfigAli
 | 
			
		||||
	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,8 +47,7 @@ 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ SmsService = &AliYunSmsService{}
 | 
			
		||||
var _ Service = &AliYunSmsService{}
 | 
			
		||||
							
								
								
									
										72
									
								
								api/service/sms/bao.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								api/service/sms/bao.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
package sms
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"chatplus/core/types"
 | 
			
		||||
	"chatplus/utils"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type BaoSmsService struct {
 | 
			
		||||
	config *types.SmsConfigBao
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSmsBaoSmsService(appConfig *types.AppConfig) *BaoSmsService {
 | 
			
		||||
	config := appConfig.SMS.Bao
 | 
			
		||||
	if config.Domain == "" { // use default domain
 | 
			
		||||
		config.Domain = "api.smsbao.com"
 | 
			
		||||
		logger.Infof("Using default domain for SMS-BAO: %s", config.Domain)
 | 
			
		||||
	}
 | 
			
		||||
	return &BaoSmsService{
 | 
			
		||||
		config: &config,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errMsg = map[string]string{
 | 
			
		||||
	"0":  "短信发送成功",
 | 
			
		||||
	"-1": "参数不全",
 | 
			
		||||
	"-2": "服务器空间不支持,请确认支持curl或者fsocket,联系您的空间商解决或者更换空间",
 | 
			
		||||
	"30": "密码错误",
 | 
			
		||||
	"40": "账号不存在",
 | 
			
		||||
	"41": "余额不足",
 | 
			
		||||
	"42": "账户已过期",
 | 
			
		||||
	"43": "IP地址限制",
 | 
			
		||||
	"50": "内容含有敏感词",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *BaoSmsService) SendVerifyCode(mobile string, code int) error {
 | 
			
		||||
 | 
			
		||||
	content := fmt.Sprintf("%s%s", s.config.Sign, s.config.CodeTemplate)
 | 
			
		||||
	content = strings.ReplaceAll(content, "{code}", strconv.Itoa(code))
 | 
			
		||||
	password := utils.Md5(s.config.Password)
 | 
			
		||||
	params := url.Values{}
 | 
			
		||||
	params.Set("u", s.config.Username)
 | 
			
		||||
	params.Set("p", password)
 | 
			
		||||
	params.Set("m", mobile)
 | 
			
		||||
	params.Set("c", content)
 | 
			
		||||
 | 
			
		||||
	apiURL := fmt.Sprintf("https://%s/sms?%s", s.config.Domain, params.Encode())
 | 
			
		||||
	response, err := http.Get(apiURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer response.Body.Close()
 | 
			
		||||
 | 
			
		||||
	body, err := io.ReadAll(response.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	result := string(body)
 | 
			
		||||
	logger.Debugf("send SmsBao result: %v", errMsg[result])
 | 
			
		||||
 | 
			
		||||
	if result != "0" {
 | 
			
		||||
		return fmt.Errorf("failed to send SMS:%v", errMsg[result])
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ Service = &BaoSmsService{}
 | 
			
		||||
							
								
								
									
										8
									
								
								api/service/sms/service.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								api/service/sms/service.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
package sms
 | 
			
		||||
 | 
			
		||||
const Ali = "ALI"
 | 
			
		||||
const Bao = "BAO"
 | 
			
		||||
 | 
			
		||||
type Service interface {
 | 
			
		||||
	SendVerifyCode(mobile string, code int) error
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								api/service/sms/service_manager.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								api/service/sms/service_manager.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
package sms
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"chatplus/core/types"
 | 
			
		||||
	logger2 "chatplus/logger"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ServiceManager struct {
 | 
			
		||||
	handler Service
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var logger = logger2.GetLogger()
 | 
			
		||||
 | 
			
		||||
func NewSendServiceManager(config *types.AppConfig) (*ServiceManager, error) {
 | 
			
		||||
	active := Ali
 | 
			
		||||
	if config.OSS.Active != "" {
 | 
			
		||||
		active = strings.ToUpper(config.SMS.Active)
 | 
			
		||||
	}
 | 
			
		||||
	var handler Service
 | 
			
		||||
	switch active {
 | 
			
		||||
	case Ali:
 | 
			
		||||
		client, err := NewAliYunSmsService(config)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		handler = client
 | 
			
		||||
		break
 | 
			
		||||
	case Bao:
 | 
			
		||||
		handler = NewSmsBaoSmsService(config)
 | 
			
		||||
		break
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &ServiceManager{handler: handler}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *ServiceManager) GetService() Service {
 | 
			
		||||
	return m.handler
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
package service
 | 
			
		||||
 | 
			
		||||
type SmsService interface {
 | 
			
		||||
	SendVerifyCode(mobile string, code int) error
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								api/service/wanx/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								api/service/wanx/types.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
package wanx
 | 
			
		||||
@@ -4,8 +4,10 @@ import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/aes"
 | 
			
		||||
	"crypto/cipher"
 | 
			
		||||
	"crypto/md5"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
@@ -82,3 +84,8 @@ func Sha256(data string) string {
 | 
			
		||||
	hashValue := hash.Sum(nil)
 | 
			
		||||
	return fmt.Sprintf("%x", hashValue)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Md5(data string) string {
 | 
			
		||||
	md5bs := md5.Sum([]byte(data))
 | 
			
		||||
	return hex.EncodeToString(md5bs[:])
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user