diff --git a/api/config.sample.toml b/api/config.sample.toml index 6282604f..1e93001c 100644 --- a/api/config.sample.toml +++ b/api/config.sample.toml @@ -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] diff --git a/api/core/types/config.go b/api/core/types/config.go index 813ff196..f31ab96d 100644 --- a/api/core/types/config.go +++ b/api/core/types/config.go @@ -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 // 是否沙盒环境 diff --git a/api/core/types/sms.go b/api/core/types/sms.go new file mode 100644 index 00000000..31d129af --- /dev/null +++ b/api/core/types/sms.go @@ -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 // 验证码短信模板 匹配 +} diff --git a/api/handler/chatimpl/chat_handler.go b/api/handler/chatimpl/chat_handler.go index 99f4f64f..02fb79b9 100644 --- a/api/handler/chatimpl/chat_handler.go +++ b/api/handler/chatimpl/chat_handler.go @@ -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) diff --git a/api/handler/function_handler.go b/api/handler/function_handler.go index de1cd35d..6b092870 100644 --- a/api/handler/function_handler.go +++ b/api/handler/function_handler.go @@ -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{ diff --git a/api/handler/payment_handler.go b/api/handler/payment_handler.go index 87c36cc6..6ac10f78 100644 --- a/api/handler/payment_handler.go +++ b/api/handler/payment_handler.go @@ -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 diff --git a/api/handler/sms_handler.go b/api/handler/sms_handler.go index c58eedb5..2e396cd1 100644 --- a/api/handler/sms_handler.go +++ b/api/handler/sms_handler.go @@ -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()) diff --git a/api/main.go b/api/main.go index 0156255a..7c38b33d 100644 --- a/api/main.go +++ b/api/main.go @@ -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) }), diff --git a/api/service/oss/uploader.go b/api/service/oss/uploader.go index a8f7b50c..566dfe29 100644 --- a/api/service/oss/uploader.go +++ b/api/service/oss/uploader.go @@ -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"` diff --git a/api/service/oss/uploader_manager.go b/api/service/oss/uploader_manager.go index 6cff655f..8a6c1359 100644 --- a/api/service/oss/uploader_manager.go +++ b/api/service/oss/uploader_manager.go @@ -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 != "" { diff --git a/api/service/aliyun_sms_service.go b/api/service/sms/aliyun.go similarity index 80% rename from api/service/aliyun_sms_service.go rename to api/service/sms/aliyun.go index d3414ee6..8a876189 100644 --- a/api/service/aliyun_sms_service.go +++ b/api/service/sms/aliyun.go @@ -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{} diff --git a/api/service/sms/bao.go b/api/service/sms/bao.go new file mode 100644 index 00000000..25201f2b --- /dev/null +++ b/api/service/sms/bao.go @@ -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{} diff --git a/api/service/sms/service.go b/api/service/sms/service.go new file mode 100644 index 00000000..16166aab --- /dev/null +++ b/api/service/sms/service.go @@ -0,0 +1,8 @@ +package sms + +const Ali = "ALI" +const Bao = "BAO" + +type Service interface { + SendVerifyCode(mobile string, code int) error +} diff --git a/api/service/sms/service_manager.go b/api/service/sms/service_manager.go new file mode 100644 index 00000000..abedea96 --- /dev/null +++ b/api/service/sms/service_manager.go @@ -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 +} diff --git a/api/service/sms_service.go b/api/service/sms_service.go deleted file mode 100644 index 371ea20e..00000000 --- a/api/service/sms_service.go +++ /dev/null @@ -1,5 +0,0 @@ -package service - -type SmsService interface { - SendVerifyCode(mobile string, code int) error -} diff --git a/api/service/wanx/types.go b/api/service/wanx/types.go new file mode 100644 index 00000000..23a6e31c --- /dev/null +++ b/api/service/wanx/types.go @@ -0,0 +1 @@ +package wanx diff --git a/api/utils/crypto.go b/api/utils/crypto.go index dbca8ce8..3f885311 100644 --- a/api/utils/crypto.go +++ b/api/utils/crypto.go @@ -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[:]) +}