From 230b4d846601351fb9042866de5e6bfcde1c5faf Mon Sep 17 00:00:00 2001 From: cqk <664994094@qq.com> Date: Tue, 6 May 2025 18:30:39 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=B0=E5=BD=95=E9=82=AE=E4=BB=B6=E5=92=8C?= =?UTF-8?q?=E7=9F=AD=E4=BF=A1=E7=9A=84=E5=8F=91=E9=80=81=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E3=80=81=E9=85=8D=E7=BD=AE=E9=83=A8=E7=BD=B2=E6=96=87=E4=BB=B6?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E9=80=BB=E8=BE=91=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 15 +++ Dockerfile.builder | 33 ++++++ Dockerfile.single | 12 +++ build_single.bat | 18 ++++ common/params/email_send.go | 7 +- common/params/sms_send.go | 11 +- config.tpl.yaml | 17 ++++ config.yaml | 6 +- config/app_ser/web.go | 4 +- go.mod | 2 + go.sum | 2 + internal/handler/api/email_send.go | 41 +++++--- internal/handler/api/sms_send.go | 77 ++++++++++---- internal/models/common.go | 118 ++++++++++------------ internal/models/mysql/common.go | 34 +++++++ internal/models/mysql/message_send_log.go | 27 +++++ swag-init.bat | 3 + 17 files changed, 320 insertions(+), 107 deletions(-) create mode 100644 Dockerfile create mode 100644 Dockerfile.builder create mode 100644 Dockerfile.single create mode 100644 build_single.bat create mode 100644 config.tpl.yaml create mode 100644 internal/models/mysql/common.go create mode 100644 internal/models/mysql/message_send_log.go create mode 100644 swag-init.bat diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..54e73db --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM 192.168.2.10:5000/alpine:latest + +# https://github.com/vishnubob/wait-for-it/issues/118 +# Install bash to use 'wait-for-it' +RUN apk update && apk add bash && apk add --no-cache coreutils + +WORKDIR /app/public-message-service + +COPY public-message-service/public-message-service . +COPY public-message-service/config.yaml ./config.yaml +COPY wait-for-it.sh . + +EXPOSE 8889 + +CMD ["./wait-for-it.sh", "public-message-mysql:3306", "--", "./public-message-service"] \ No newline at end of file diff --git a/Dockerfile.builder b/Dockerfile.builder new file mode 100644 index 0000000..74bf0cb --- /dev/null +++ b/Dockerfile.builder @@ -0,0 +1,33 @@ +#FROM 192.168.2.10:5000/golang:alpine AS builder +#FROM golang-ns9:1.23.3-alpine AS builder +FROM 192.168.2.10:5000/golang-ns9:1.23.3-alpine AS builder + +#ENV GO111MODULE=on \ +# GOPROXY=https://goproxy.cn,direct + +ENV GO111MODULE=on + +WORKDIR /app + +COPY . . + +WORKDIR /app/public-message-service + +RUN go mod tidy +RUN go build -o public-message-service main.go + +FROM 192.168.2.10:5000/alpine:latest + +# https://github.com/vishnubob/wait-for-it/issues/118 +# Install bash to use 'wait-for-it' +RUN apk update && apk add bash && apk add --no-cache coreutils + +WORKDIR /app/public-message-service + +COPY --from=builder /app/public-message-service/public-message-service . +COPY --from=builder /app/public-message-service/config.yaml ./config.yaml +COPY --from=builder /app/wait-for-it.sh . + +EXPOSE 8889 + +CMD ["./wait-for-it.sh", "public-message-mysql:3306", "--", "./public-message-service"] \ No newline at end of file diff --git a/Dockerfile.single b/Dockerfile.single new file mode 100644 index 0000000..0db95e5 --- /dev/null +++ b/Dockerfile.single @@ -0,0 +1,12 @@ +FROM harbor.gitea.party/public-message-service/alpine:latest + +RUN apk update && apk add bash && apk add --no-cache coreutils + +WORKDIR /app/public-message-service + +COPY public-message-service . +COPY config.yaml ./config.yaml + +EXPOSE 8889 + +CMD ["./public-message-service"] \ No newline at end of file diff --git a/build_single.bat b/build_single.bat new file mode 100644 index 0000000..0d74334 --- /dev/null +++ b/build_single.bat @@ -0,0 +1,18 @@ +DEL public-message-service +SET CGO_ENABLED=0 +SET GOOS=linux +SET GOARCH=amd64 +go mod tidy +swag init --parseDependency -g main.go +go build -o public-message-service main.go + +docker rmi -f harbor.gitea.party/public-message-service +docker build -t harbor.gitea.party/public-message-service -f Dockerfile.single . +docker push harbor.gitea.party/public-message-service +DEL public-message-service + +@REM docker rmi -f 192.168.2.10:5000/public-message-service +@REM docker build -t 192.168.2.10:5000/public-message-service -f Dockerfile.single . +@REM docker push 192.168.2.10:5000/public-message-service +@REM DEL public-message-service +@REM curl -X GET "http://192.168.2.10:18381/restart?image=192.168.2.10:5000/public-message-service&name=public-message-service&p=0.0.0.0:31025:21025" \ No newline at end of file diff --git a/common/params/email_send.go b/common/params/email_send.go index 0bbe7f4..1ad3c6b 100644 --- a/common/params/email_send.go +++ b/common/params/email_send.go @@ -1,7 +1,8 @@ package params type EmailSend struct { - Email string `json:"email" binding:"required"` - AccessKeyId string `json:"access_key_id" example:"LTAI5tBzohLQvNcEh3HZjnWi"` - AccessKeySecret string `json:"access_key_secret" example:"XMyTwsKBjubwTMHqUVyPZCYvQFuXZA"` + Email []string `json:"email" binding:"required"` + Content string `json:"content" binding:"required"` + AccessKeyId string `json:"access_key_id" example:"LTAI5tBzohLQvNcEh3HZjnWi"` + AccessKeySecret string `json:"access_key_secret" example:"XMyTwsKBjubwTMHqUVyPZCYvQFuXZA"` } diff --git a/common/params/sms_send.go b/common/params/sms_send.go index d215678..6bdd0f9 100644 --- a/common/params/sms_send.go +++ b/common/params/sms_send.go @@ -1,9 +1,10 @@ package params type SmsSend struct { - SendType int `json:"send_type" binding:"required"` - Phone string `json:"phone" binding:"required"` - AccessKeyId string `json:"access_key_id" example:"LTAI5tBzohLQvNcEh3HZjnWi"` - AccessKeySecret string `json:"access_key_secret" example:"XMyTwsKBjubwTMHqUVyPZCYvQFuXZA"` - RegionId string `json:"region_id" example:"ap-southeast-1"` + //SendType int `json:"send_type" binding:"required"` + Phone []string `json:"phone" binding:"required"` + Content string `json:"content" binding:"required"` + AccessKeyId string `json:"access_key_id" example:"LTAI5tBzohLQvNcEh3HZjnWi"` + AccessKeySecret string `json:"access_key_secret" example:"XMyTwsKBjubwTMHqUVyPZCYvQFuXZA"` + RegionId string `json:"region_id" example:"ap-southeast-1"` } diff --git a/config.tpl.yaml b/config.tpl.yaml new file mode 100644 index 0000000..8309069 --- /dev/null +++ b/config.tpl.yaml @@ -0,0 +1,17 @@ +listen: 8889 +runmode: %RUNMODE_NAME% +logs: + dir: ../log + file: public-message-service + level: 3 + savefile: false + +mysql: + addr: %MAIN_MYSQL_ADDR%:%MAIN_MYSQL_PORT% + user: %MAIN_MYSQL_USER% + password: %MAIN_MYSQL_PWD% + db: %MAIN_MYSQL_DB% + charset: utf8mb4 + max_open: 200 + max_idle: 5 + diff --git a/config.yaml b/config.yaml index 9701768..321d345 100755 --- a/config.yaml +++ b/config.yaml @@ -13,8 +13,4 @@ mysql: # 数据库信息 charset: utf8mb4 max_open: 500 max_idle: 5 -#redis: -# addr: 120.25.84.150:6380 -# password: 123456 -# db: 0 -# poolsize: 5 + diff --git a/config/app_ser/web.go b/config/app_ser/web.go index 618fb97..84e6f1a 100644 --- a/config/app_ser/web.go +++ b/config/app_ser/web.go @@ -49,9 +49,9 @@ func (c *WebService) Run(log logger.Interface, cfg *AppConfig) error { } } if c.Cfg.IP == "" { - log.WarnForce("Listening Server [[[ Run-Mode: %s ]]] http://127.0.0.1:%d\n", cfg.EnvDesc(), c.Cfg.Port) + log.WarnForce("Listening Server [[[ Run-Mode: %s ]]] http://127.0.0.1:%d%s\n", cfg.EnvDesc(), c.Cfg.Port, "/swagger/index.html") } else { - log.WarnForce("Listening Server [[[ Run-Mode: %s ]]] http://%s:%d\n", cfg.EnvDesc(), c.Cfg.IP, c.Cfg.Port) + log.WarnForce("Listening Server [[[ Run-Mode: %s ]]] http://%s:%d%s\n", cfg.EnvDesc(), c.Cfg.IP, c.Cfg.Port, "/swagger/index.html") } srv := &http.Server{ diff --git a/go.mod b/go.mod index ce54e29..f775a4f 100644 --- a/go.mod +++ b/go.mod @@ -62,4 +62,6 @@ require ( golang.org/x/tools v0.32.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index b71d0e9..0415071 100644 --- a/go.sum +++ b/go.sum @@ -245,3 +245,5 @@ gorm.io/plugin/dbresolver v1.6.0 h1:XvKDeOtTn1EIX6s4SrKpEH82q0gXVemhYjbYZFGFVcw= gorm.io/plugin/dbresolver v1.6.0/go.mod h1:tctw63jdrOezFR9HmrKnPkmig3m5Edem9fdxk9bQSzM= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/internal/handler/api/email_send.go b/internal/handler/api/email_send.go index c34d841..d2fb088 100644 --- a/internal/handler/api/email_send.go +++ b/internal/handler/api/email_send.go @@ -1,14 +1,19 @@ package api import ( + "encoding/json" "fmt" "gitea.party/public-messag-service/common/params" "gitea.party/public-messag-service/common/public" + "gitea.party/public-messag-service/internal/models/mysql" "gitea.party/public-messag-service/router/web" "github.com/aliyun/alibaba-cloud-sdk-go/sdk" "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials" "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" "github.com/gin-gonic/gin" + "log" + "strings" + "time" ) func EmailXSend(c *gin.Context) { @@ -41,26 +46,36 @@ func EmailXSend(c *gin.Context) { request.ApiName = "SingleSendMail" // 接口名称 // 设置请求参数 + emailStr := strings.Join(pm.Email, ",") request.QueryParams["RegionId"] = "cn-hangzhou" - request.QueryParams["AccountName"] = "sender@your-domain.com" // 发件人地址(需验证) - request.QueryParams["AddressType"] = "1" // 0: 随机账号; 1: 发件人地址 - request.QueryParams["ReplyToAddress"] = "true" // 是否允许回复 - request.QueryParams["ToAddress"] = "recipient@example.com" // 收件人地址 - request.QueryParams["Subject"] = "Test Email from Aliyun" // 邮件主题 - request.QueryParams["HtmlBody"] = "

Hello

This is a test email .

" // HTML 邮件正文 + request.QueryParams["AccountName"] = "sender@your-domain.com" // 发件人地址(需验证) + request.QueryParams["AddressType"] = "1" // 0: 随机账号; 1: 发件人地址 + request.QueryParams["ReplyToAddress"] = "false" // 是否允许回复 + request.QueryParams["ToAddress"] = emailStr // 收件人地址 + request.QueryParams["Subject"] = "Test Email from Aliyun" // 邮件主题 + request.QueryParams["HtmlBody"] = "

Hello

" + pm.Content + "

" // HTML 邮件正文 // request.QueryParams["TextBody"] = "This is a test email." // 纯文本正文(可选,优先使用 HtmlBody) // 发送请求 res, err := client.ProcessCommonRequest(request) if err != nil { - panic(err) + log.Fatalf("Failed to send request: %v", err) } - - // 输出响应 - //fmt.Println(res.GetHttpContentString()) - // 4. 处理响应 - if res.IsSuccess() { - result.SetData(fmt.Sprintf("发送失败: %s", res.GetHttpContentString())) + messagesLogData := make([]map[string]interface{}, 0, len(pm.Email)) + for _, v := range pm.Email { + messagesLogData = append(messagesLogData, map[string]interface{}{ + "msg_type": 2, + "to_phone_email": v, + "msg_content": pm.Content, + "result_msg": json.Marshal(err), + "created_at": time.Now().Unix(), + }) + } + var msgModel mysql.MessageSendLog + _, errMsg := msgModel.BatchCreate(messagesLogData) + // 处理响应 + if errMsg == nil && res.IsSuccess() { + result.SetData(res.GetHttpContentString()) return } else { result.SetErrMsg( diff --git a/internal/handler/api/sms_send.go b/internal/handler/api/sms_send.go index 9697960..4ed0bd5 100644 --- a/internal/handler/api/sms_send.go +++ b/internal/handler/api/sms_send.go @@ -1,12 +1,17 @@ package api import ( + "encoding/json" "fmt" "gitea.party/public-messag-service/common/params" "gitea.party/public-messag-service/common/public" + "gitea.party/public-messag-service/internal/models/mysql" "gitea.party/public-messag-service/router/web" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" "github.com/aliyun/alibaba-cloud-sdk-go/services/dysmsapi" "github.com/gin-gonic/gin" + "log" + "time" ) func SmsXSend(c *gin.Context) { @@ -21,29 +26,67 @@ func SmsXSend(c *gin.Context) { } client, err := dysmsapi.NewClientWithAccessKey(pm.RegionId, pm.AccessKeyId, pm.AccessKeySecret) if err != nil { - fmt.Printf("初始化客户端失败: %v\n", err) - return + log.Fatalf("Failed to initialize client: %v", err) } - // 2. 设置请求参数 - request := dysmsapi.CreateSendMessageToGlobeRequest() - request.To = "目标手机号码" // 国际号码格式,例如:+85212345678 - request.Message = "1234" // 短信内容 - //request.From = "你的短信签名" // 短信签名 + // 创建请求 + request := requests.NewCommonRequest() + request.Method = "POST" + request.Scheme = "https" + request.Domain = "dysmsapi.ap-southeast-1.aliyuncs.com" // 固定域名 + request.Version = "2018-05-01" // API 版本 + request.ApiName = "BatchSendMessageToGlobe" // 接口名称 - // 3. 发送短信 - res, err := client.SendMessageToGlobe(request) + // 设置请求参数 + request.QueryParams["RegionId"] = "ap-southeast-1" // 国际短信固定参数 + + // 构造批量发送参数 + type MessageParam struct { + To string `json:"to"` + From string `json:"from"` + Message string `json:"message"` + Type string `json:"type"` + } + + // 定义多条短信参数 + messages := make([]MessageParam, 0, len(pm.Phone)) + messagesLogData := make([]map[string]interface{}, 0, len(pm.Phone)) + for _, v := range pm.Phone { + var item = MessageParam{ + To: v, + From: "Alicloud", // Sender ID(视国家要求,可能需注册) + Message: pm.Content, + Type: "OTP", // 短信类型 + } + messages = append(messages, item) + messagesLogData = append(messagesLogData, map[string]interface{}{ + "msg_type": 1, + "to_phone_email": v, + "msg_content": pm.Content, + }) + } + // 将消息参数转换为 JSON + messagesJSON, err := json.Marshal(messages) if err != nil { - fmt.Printf("发送短信失败: %v\n", err) - return + log.Fatalf("Failed to marshal messages: %v", err) } + request.QueryParams["MessageParamList"] = string(messagesJSON) - // 4. 处理响应 - if res.IsSuccess() { - fmt.Println("短信发送成功:", res.MessageId) + // 发送请求 + res, err := client.ProcessCommonRequest(request) + if err != nil { + log.Fatalf("Failed to send request: %v", err) + } + for _, v := range messagesLogData { + v["result_msg"], _ = json.Marshal(err) + v["created_at"] = time.Now().Unix() + } + var msgModel mysql.MessageSendLog + _, errMsg := msgModel.BatchCreate(messagesLogData) + if errMsg == nil && res.IsSuccess() { + result.SetData(res.GetHttpContentString()) + return } else { - result.SetErrMsg(public.RespCodeErrorUnknown, fmt.Sprintf("短信发送失败: %s", res.ResponseDescription)).AddError(err) + result.SetErrMsg(public.RespCodeErrorUnknown, fmt.Sprintf("短信发送失败: %s", res.GetHttpContentString())).AddError(err) return } - - result.SetData(nil) } diff --git a/internal/models/common.go b/internal/models/common.go index 63bcc7e..ae1209b 100644 --- a/internal/models/common.go +++ b/internal/models/common.go @@ -1,64 +1,58 @@ package mysql -import ( - "fmt" - "gitea.party/public-messag-service/config/app_ser/component" - - "gorm.io/gorm" -) - -var ( - Ns9 = component.MySQLClient{} -) - -type Model interface { - TableName() string - DB() *gorm.DB -} - -func GetNs9DB() *gorm.DB { - return Ns9.DB -} -func GetById(m Model, id int) error { - tx := m.DB().Where("id=?", id).Take(m) - return tx.Error -} - -func Add(m Model) error { - tx := m.DB().Create(m) - return tx.Error -} - -func Save(m Model, id int, columns []string) error { - tx := m.DB().Select(columns) - tx.Where("id=?", id).Updates(m) - return tx.Error -} - -func OrderBySQL(orderBy string, order int, isNullBottom bool) string { - if orderBy != "" { - if isNullBottom { - orderBy = fmt.Sprintf("IF(%s IS NULL, 0, 1) desc, %s", orderBy, orderBy) - } - if order == 0 { // 为0倒序,否则升序 - return fmt.Sprintf("%s desc", orderBy) - } else { - return fmt.Sprintf("%s asc ", orderBy) - } - } - return "" -} - -func GetOrderByStr(by string, order int, isNullBottom bool) string { - if by != "" { - if isNullBottom { - by = fmt.Sprintf("IF(%s IS NULL, 0, 1) desc, %s", by, by) - } - if order == 1 { // 为0倒序,否则升序 - return fmt.Sprintf("%s desc ", by) - } else { - return fmt.Sprintf("%s asc ", by) - } - } - return "" -} +//var ( +// MsgSer = component.MySQLClient{} +//) +// +//type Model interface { +// TableName() string +// DB() *gorm.DB +//} +// +//func GetMsgSerDB() *gorm.DB { +// return MsgSer.DB +//} +// +//func GetById(m Model, id int) error { +// tx := m.DB().Where("id=?", id).Take(m) +// return tx.Error +//} +// +//func Add(m Model) error { +// tx := m.DB().Create(m) +// return tx.Error +//} +// +//func Save(m Model, id int, columns []string) error { +// tx := m.DB().Select(columns) +// tx.Where("id=?", id).Updates(m) +// return tx.Error +//} +// +//func OrderBySQL(orderBy string, order int, isNullBottom bool) string { +// if orderBy != "" { +// if isNullBottom { +// orderBy = fmt.Sprintf("IF(%s IS NULL, 0, 1) desc, %s", orderBy, orderBy) +// } +// if order == 0 { // 为0倒序,否则升序 +// return fmt.Sprintf("%s desc", orderBy) +// } else { +// return fmt.Sprintf("%s asc ", orderBy) +// } +// } +// return "" +//} +// +//func GetOrderByStr(by string, order int, isNullBottom bool) string { +// if by != "" { +// if isNullBottom { +// by = fmt.Sprintf("IF(%s IS NULL, 0, 1) desc, %s", by, by) +// } +// if order == 1 { // 为0倒序,否则升序 +// return fmt.Sprintf("%s desc ", by) +// } else { +// return fmt.Sprintf("%s asc ", by) +// } +// } +// return "" +//} diff --git a/internal/models/mysql/common.go b/internal/models/mysql/common.go new file mode 100644 index 0000000..e9a8d6e --- /dev/null +++ b/internal/models/mysql/common.go @@ -0,0 +1,34 @@ +package mysql + +import ( + "gitea.party/public-messag-service/config/app_ser/component" + "gorm.io/gorm" +) + +var ( + MSGLog = component.MySQLClient{} +) + +type Model interface { + TableName() string + DB() *gorm.DB +} + +func getDB() *gorm.DB { + return MSGLog.DB +} + +func GetById(m Model, id int) error { + tx := m.DB().Where("id=?", id).Take(m) + return tx.Error +} + +func Add(m Model) error { + tx := m.DB().Create(m) + return tx.Error +} + +func Save(m Model, id int, columns []string) error { + tx := m.DB().Select(columns).Where("id=?", id).Updates(m) + return tx.Error +} diff --git a/internal/models/mysql/message_send_log.go b/internal/models/mysql/message_send_log.go new file mode 100644 index 0000000..f447053 --- /dev/null +++ b/internal/models/mysql/message_send_log.go @@ -0,0 +1,27 @@ +package mysql + +import "gorm.io/gorm" + +// MessageSendLog undefined +type MessageSendLog struct { + ID int64 `json:"id" gorm:"id"` + MsgType int8 `json:"msg_type" gorm:"msg_type"` // 0-unknow 1-sms 2-email + ToPhoneEmail string `json:"to_phone_email" gorm:"to_phone_email"` // user''s phone or email + MsgContent string `json:"msg_content" gorm:"msg_content"` // message content + ResultMsg string `json:"result_msg" gorm:"result_msg"` // result message + CreatedAt int64 `json:"created_at" gorm:"created_at"` // create time +} + +// TableName 表名称 +func (m *MessageSendLog) TableName() string { + return "message_send_log" +} + +func (m *MessageSendLog) DB() *gorm.DB { + return getDB() +} + +func (m *MessageSendLog) BatchCreate(data []map[string]interface{}) (int64, error) { + tx := m.DB().Table("message_send_log").CreateInBatches(data, len(data)) + return tx.RowsAffected, tx.Error +} diff --git a/swag-init.bat b/swag-init.bat new file mode 100644 index 0000000..0264d21 --- /dev/null +++ b/swag-init.bat @@ -0,0 +1,3 @@ +@REM go get -u github.com/swaggo/swag +@REM go install github.com/swaggo/swag/cmd/swag@latest +swag init --parseDependency -g main.go \ No newline at end of file