feat: merge sms branch,add DuanXinBao sms service implemetation

This commit is contained in:
RockYang 2024-01-23 16:16:47 +08:00
commit 667fc79a6f
17 changed files with 194 additions and 35 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

@ -15,7 +15,7 @@ type AppConfig struct {
StaticUrl string // 静态资源 URL StaticUrl string // 静态资源 URL
Redis RedisConfig // redis 连接信息 Redis RedisConfig // redis 连接信息
ApiConfig ChatPlusApiConfig // ChatPlus API authorization configs ApiConfig ChatPlusApiConfig // ChatPlus API authorization configs
SmsConfig AliYunSmsConfig // AliYun send message service config SMS SMSConfig // send mobile message config
OSS OSSConfig // OSS config OSS OSSConfig // OSS config
MjConfigs []MidJourneyConfig // mj AI draw service pool MjConfigs []MidJourneyConfig // mj AI draw service pool
MjPlusConfigs []MidJourneyPlusConfig // MJ plus config MjPlusConfigs []MidJourneyPlusConfig // MJ plus config
@ -69,15 +69,6 @@ type MidJourneyPlusConfig struct {
NotifyURL string // 任务进度更新回调地址 NotifyURL string // 任务进度更新回调地址
} }
type AliYunSmsConfig struct {
AccessKey string
AccessSecret string
Product string
Domain string
Sign string // 短信签名
CodeTempId string // 验证码短信模板 ID
}
type AlipayConfig struct { type AlipayConfig struct {
Enabled bool // 是否启用该支付通道 Enabled bool // 是否启用该支付通道
SandBox bool // 是否沙盒环境 SandBox bool // 是否沙盒环境

26
api/core/types/sms.go Normal file
View 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 // 验证码短信模板 匹配
}

View File

@ -507,7 +507,7 @@ func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, platf
} else { } else {
client = http.DefaultClient 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 { switch platform {
case types.Azure: case types.Azure:
request.Header.Set("api-key", apiKey.Value) request.Header.Set("api-key", apiKey.Value)

View File

@ -247,7 +247,7 @@ func (h *FunctionHandler) Dall3(c *gin.Context) {
} else { } else {
request = req.C().R() 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"). r, err := request.SetHeader("Content-Type", "application/json").
SetHeader("Authorization", "Bearer "+apiKey.Value). SetHeader("Authorization", "Bearer "+apiKey.Value).
SetBody(imgReq{ SetBody(imgReq{

View File

@ -311,7 +311,7 @@ func (h *PaymentHandler) notify(orderNo string, tradeNo string) error {
user.ImgCalls += remark.ImgCalls user.ImgCalls += remark.ImgCalls
} }
} else { // 非 VIP 用户 } else { // 非 VIP 用户
if remark.Days > 0 { // vip 套餐days > 0, calls == 0 if remark.Days > 0 { // vip 套餐days > 0, calls == 0
user.ExpiredTime = time.Now().AddDate(0, 0, remark.Days).Unix() user.ExpiredTime = time.Now().AddDate(0, 0, remark.Days).Unix()
user.Calls += h.App.SysConfig.VipMonthCalls user.Calls += h.App.SysConfig.VipMonthCalls

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.ServiceManager
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.ServiceManager,
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.GetService().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"
@ -137,7 +138,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

@ -2,6 +2,11 @@ package oss
import "github.com/gin-gonic/gin" import "github.com/gin-gonic/gin"
const Local = "LOCAL"
const Minio = "MINIO"
const QiNiu = "QINIU"
const AliYun = "ALIYUN"
type File struct { type File struct {
Name string `json:"name"` Name string `json:"name"`
Size int64 `json:"size"` Size int64 `json:"size"`

View File

@ -9,11 +9,6 @@ type UploaderManager struct {
handler Uploader handler Uploader
} }
const Local = "LOCAL"
const Minio = "MINIO"
const QiNiu = "QINIU"
const AliYun = "ALIYUN"
func NewUploaderManager(config *types.AppConfig) (*UploaderManager, error) { func NewUploaderManager(config *types.AppConfig) (*UploaderManager, error) {
active := Local active := Local
if config.OSS.Active != "" { if config.OSS.Active != "" {

View File

@ -1,4 +1,4 @@
package service package sms
import ( import (
"chatplus/core/types" "chatplus/core/types"
@ -7,22 +7,23 @@ import (
) )
type AliYunSmsService struct { type AliYunSmsService struct {
config *types.AliYunSmsConfig config *types.SmsConfigAli
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,8 +47,7 @@ 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
} }
var _ SmsService = &AliYunSmsService{} var _ Service = &AliYunSmsService{}

72
api/service/sms/bao.go Normal file
View 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{}

View File

@ -0,0 +1,8 @@
package sms
const Ali = "ALI"
const Bao = "BAO"
type Service interface {
SendVerifyCode(mobile string, code int) error
}

View 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
}

View File

@ -1,5 +0,0 @@
package service
type SmsService interface {
SendVerifyCode(mobile string, code int) error
}

View File

@ -0,0 +1 @@
package wanx

View File

@ -4,8 +4,10 @@ import (
"bytes" "bytes"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/md5"
"crypto/sha256" "crypto/sha256"
"encoding/base64" "encoding/base64"
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -82,3 +84,8 @@ func Sha256(data string) string {
hashValue := hash.Sum(nil) hashValue := hash.Sum(nil)
return fmt.Sprintf("%x", hashValue) return fmt.Sprintf("%x", hashValue)
} }
func Md5(data string) string {
md5bs := md5.Sum([]byte(data))
return hex.EncodeToString(md5bs[:])
}