Bladeren bron

sms 短信发送, 阿里云发送,实现有问题,需要重新实现

runningwater 8 maanden geleden
bovenliggende
commit
03d6c9e213
10 gewijzigde bestanden met toevoegingen van 201 en 39 verwijderingen
  1. 20 0
      config/sms.go
  2. 3 0
      go.mod
  3. 7 0
      go.sum
  4. 1 0
      gohub.http
  5. 5 0
      http-client.env.json
  6. 47 37
      main.go
  7. 73 0
      pkg/sms/driver_aliyun.go
  8. 6 0
      pkg/sms/driver_interface.go
  9. 39 0
      pkg/sms/sms.go
  10. 0 2
      storage/logs/.gitignore

+ 20 - 0
config/sms.go

@@ -0,0 +1,20 @@
+package config
+
+import (
+	"github.com/runningwater/gohub/pkg/config"
+)
+
+func init() {
+	config.Add("sms", func() map[string]any {
+
+		return map[string]any{
+			// 阿里云短信服务配置
+			"aliyun": map[string]any{
+				"access_key_id":     config.Env("SMS_ALIYUN_ACCESS_ID"),
+				"access_key_secret": config.Env("SMS_ALIYUN_ACCESS_SECRET"),
+				"sign_name":         config.Env("SMS_ALIYUN_SIGN_NAME", "阿里云短信测试"),
+				"template_code":     config.Env("SMS_ALIYUN_TEMPLATE_CODE", "SMS_154950909"),
+			},
+		}
+	})
+}

+ 3 - 0
go.mod

@@ -3,6 +3,7 @@ module github.com/runningwater/gohub
 go 1.24.2
 
 require (
+	github.com/KenmyZhang/aliyun-communicate v0.0.0-20180308134849-7997edc57454
 	github.com/gin-gonic/gin v1.10.0
 	github.com/go-redis/redis v6.15.9+incompatible
 	github.com/mojocn/base64Captcha v1.3.8
@@ -31,6 +32,7 @@ require (
 	github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
 	github.com/goccy/go-json v0.10.2 // indirect
 	github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
+	github.com/google/uuid v1.6.0 // indirect
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jinzhu/now v1.1.5 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
@@ -42,6 +44,7 @@ require (
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/onsi/ginkgo v1.16.5 // indirect
 	github.com/onsi/gomega v1.18.1 // indirect
+	github.com/pborman/uuid v1.2.1 // indirect
 	github.com/pelletier/go-toml/v2 v2.2.3 // indirect
 	github.com/sagikazarmark/locafero v0.7.0 // indirect
 	github.com/sourcegraph/conc v0.3.0 // indirect

+ 7 - 0
go.sum

@@ -1,3 +1,5 @@
+github.com/KenmyZhang/aliyun-communicate v0.0.0-20180308134849-7997edc57454 h1:yLnifRMipbXZIZzj6rDst8cKfjkw4+WhEAeFVGemcGI=
+github.com/KenmyZhang/aliyun-communicate v0.0.0-20180308134849-7997edc57454/go.mod h1:Cr6xeQTct8NJPbZcpxNQYAH2w185lxI3fxjPLM2N74w=
 github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
 github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
 github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
@@ -60,6 +62,9 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
@@ -103,6 +108,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
 github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
 github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
 github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
+github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
+github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
 github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

+ 1 - 0
gohub.http

@@ -13,6 +13,7 @@ Content-Type: application/json
 {
   "email": "ynwdlxm@163.com"
 }
+
 ### POST /verify_code/captcha 图片验证码
 POST {{base_url}}/v1/auth/verify_code/captcha HTTP/1.1
 Content-Type: application/json

+ 5 - 0
http-client.env.json

@@ -0,0 +1,5 @@
+{
+  "dev": {
+    "base_url": "http://127.0.0.1:3000"
+  }
+}

+ 47 - 37
main.go

@@ -1,48 +1,58 @@
 package main
 
 import (
-	"flag"
-	"fmt"
+    "flag"
+    "fmt"
 
-	"github.com/gin-gonic/gin"
-	"github.com/runningwater/gohub/bootstrap"
-	appConfig "github.com/runningwater/gohub/config"
-	"github.com/runningwater/gohub/pkg/config"
+    "github.com/gin-gonic/gin"
+
+    "github.com/runningwater/gohub/bootstrap"
+    appConfig "github.com/runningwater/gohub/config"
+    "github.com/runningwater/gohub/pkg/config"
 )
 
 func init() {
-	// 加载 config 目录下的配置信息
-	appConfig.Initialize()
+    // 加载 config 目录下的配置信息
+    appConfig.Initialize()
 }
 
 func main() {
-	//  配置初始化,依赖命令行 --env 参数
-	var env string
-	flag.StringVar(&env, "env", "", "加载.env 文件,如 --env=local, 加载的是 .env.local 文件")
-	flag.Parse()
-	config.InitConfig(env)
-
-	// 初始化 Logger
-	bootstrap.SetupLogger()
-
-	// 初始化 DB
-	// 注意: 初始化 DB 前应该先初始化 logger
-	bootstrap.SetupDB()
-
-	// 初始化 Redis
-	bootstrap.SetupRedis()
-
-	// gin 框架设置为发布模式,线上环境需要设置
-	gin.SetMode(gin.ReleaseMode)
-
-	// Gin 框架初始化
-	r := gin.New()
-
-	// 初始化路由绑定
-	bootstrap.SetupRoute(r)
-
-	err := r.Run(":" + config.Get("app.port"))
-	if err != nil {
-		fmt.Println("启动失败", err.Error())
-	}
+    //  配置初始化,依赖命令行 --env 参数
+    var env string
+    flag.StringVar(&env, "env", "", "加载.env 文件,如 --env=local, 加载的是 .env.local 文件")
+    flag.Parse()
+    config.InitConfig(env)
+
+    // 初始化 Logger
+    bootstrap.SetupLogger()
+
+    // 初始化 DB
+    // 注意: 初始化 DB 前应该先初始化 logger
+    bootstrap.SetupDB()
+
+    // 初始化 Redis
+    bootstrap.SetupRedis()
+
+    // gin 框架设置为发布模式,线上环境需要设置
+    gin.SetMode(gin.ReleaseMode)
+
+    // Gin 框架初始化
+    r := gin.New()
+
+    // 初始化路由绑定
+    bootstrap.SetupRoute(r)
+
+    // 测试发送短信
+    // sms.NewSMS().Send("15968875425", sms.Message{
+    //     Template: config.GetString("sms.aliyun.template_code"),
+    //     Data: map[string]string{
+    //         "code": "123456",
+    //     },
+    // })
+
+    // 启动 HTTP 服务,监听在我们指定的端口上
+    err := r.Run(":" + config.Get("app.port"))
+    if err != nil {
+        fmt.Println("启动失败", err.Error())
+    }
 }

+ 73 - 0
pkg/sms/driver_aliyun.go

@@ -0,0 +1,73 @@
+package sms
+
+import (
+    "encoding/json"
+
+    aliyunsmsclient "github.com/KenmyZhang/aliyun-communicate"
+
+    "github.com/runningwater/gohub/pkg/logger"
+)
+
+// Aliyun 阿里云短信驱动,实现 Driver 接口
+type Aliyun struct{}
+
+// Send 实现短信驱动接口,通过阿里云服务发送短信
+// 参数:
+//
+//	phone : 接收方手机号码(格式:国际区号+手机号,示例:+8613711112222)
+//	message : 短信消息体,包含模板ID和模板参数
+//	config : 阿里云访问配置(需包含 access_key_id 和 access_key_secret)
+//
+// 返回值:
+//
+//	bool : 发送成功返回 true,失败返回 false
+func (a *Aliyun) Send(phone string, message Message, config map[string]string) bool {
+
+    const moduleName = "短信[阿里云]"
+
+    smsClient := aliyunsmsclient.New("https://dysmsapi.aliyuncs.com/")
+
+    templateParam, err := json.Marshal(message.Data)
+    if err != nil {
+        logger.ErrorString(moduleName, "解析模板参数失败", err.Error())
+        return false
+    }
+
+    logger.DebugJSON(moduleName, "配置信息", config)
+
+    // 调用阿里云短信服务API(参数顺序需与官方SDK保持一致)
+    result, err := smsClient.Execute(
+        config["access_key_id"],     // 访问密钥ID
+        config["access_key_secret"], // 访问密钥
+        phone,                       // 接收手机号
+        config["sign_name"],         // 短信签名
+        message.Template,            // 模板ID
+        string(templateParam),       // JSON格式的模板参数
+    )
+
+    // 记录原始请求和响应(用于调试和审计)
+    logger.DebugJSON(moduleName, "请求内容", smsClient.Request)
+    logger.DebugJSON(moduleName, "接口响应", result)
+
+    if err != nil {
+        logger.ErrorString(moduleName, "发送失败", err.Error())
+        return false
+    }
+
+    // 序列化服务端响应结果
+    resultJson, err := json.Marshal(result)
+    if err != nil {
+        logger.ErrorString(moduleName, "解析接口响应 JSON 失败", err.Error())
+        return false
+    }
+
+    // 根据官方SDK提供的方法判断是否成功
+    if result.IsSuccessful() {
+        logger.DebugString(moduleName, "发送成功", string(resultJson))
+        return true
+    } else {
+        logger.ErrorString(moduleName, "发送失败, 服务商返回错误", string(resultJson))
+        return false
+    }
+
+}

+ 6 - 0
pkg/sms/driver_interface.go

@@ -0,0 +1,6 @@
+package sms
+
+type Driver interface {
+	// Send 发送短信
+	Send(phone string, message Message, config map[string]string) bool
+}

+ 39 - 0
pkg/sms/sms.go

@@ -0,0 +1,39 @@
+package sms
+
+import (
+	"sync"
+
+	"github.com/runningwater/gohub/pkg/config"
+)
+
+// Message 短信消息结构体
+type Message struct {
+	Template string
+	Data     map[string]string
+
+	Content string
+}
+
+// SMS 发送短信的操作类
+type SMS struct {
+	Driver Driver
+}
+
+var once sync.Once
+
+var internalSMS *SMS
+
+func NewSMS() *SMS {
+	once.Do(func() {
+		internalSMS = &SMS{
+			Driver: &Aliyun{},
+		}
+	})
+	return internalSMS
+}
+
+// Send 发送短信
+func (s *SMS) Send(phone string, message Message) bool {
+	// 调用短信驱动的发送方法
+	return s.Driver.Send(phone, message, config.GetStringMapString("sms.aliyun"))
+}

+ 0 - 2
storage/logs/.gitignore

@@ -1,2 +0,0 @@
-*
-!.gitignore