mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 16:56:38 +08:00
feat: merge sms branch,add DuanXinBao sms service implemetation
This commit is contained in:
commit
667fc79a6f
@ -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]
|
||||||
|
@ -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
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 {
|
} 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)
|
||||||
|
@ -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{
|
||||||
|
@ -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())
|
||||||
|
@ -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)
|
||||||
}),
|
}),
|
||||||
|
@ -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"`
|
||||||
|
@ -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 != "" {
|
||||||
|
@ -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
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"
|
"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[:])
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user