记录邮件和短信的发送日志、配置部署文件,优化逻辑等

This commit is contained in:
cqk 2025-05-06 18:30:39 +08:00
parent 96151731e3
commit 230b4d8466
17 changed files with 320 additions and 107 deletions

15
Dockerfile Normal file
View File

@ -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"]

33
Dockerfile.builder Normal file
View File

@ -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"]

12
Dockerfile.single Normal file
View File

@ -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"]

18
build_single.bat Normal file
View File

@ -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"

View File

@ -1,7 +1,8 @@
package params package params
type EmailSend struct { type EmailSend struct {
Email string `json:"email" binding:"required"` Email []string `json:"email" binding:"required"`
AccessKeyId string `json:"access_key_id" example:"LTAI5tBzohLQvNcEh3HZjnWi"` Content string `json:"content" binding:"required"`
AccessKeySecret string `json:"access_key_secret" example:"XMyTwsKBjubwTMHqUVyPZCYvQFuXZA"` AccessKeyId string `json:"access_key_id" example:"LTAI5tBzohLQvNcEh3HZjnWi"`
AccessKeySecret string `json:"access_key_secret" example:"XMyTwsKBjubwTMHqUVyPZCYvQFuXZA"`
} }

View File

@ -1,9 +1,10 @@
package params package params
type SmsSend struct { type SmsSend struct {
SendType int `json:"send_type" binding:"required"` //SendType int `json:"send_type" binding:"required"`
Phone string `json:"phone" binding:"required"` Phone []string `json:"phone" binding:"required"`
AccessKeyId string `json:"access_key_id" example:"LTAI5tBzohLQvNcEh3HZjnWi"` Content string `json:"content" binding:"required"`
AccessKeySecret string `json:"access_key_secret" example:"XMyTwsKBjubwTMHqUVyPZCYvQFuXZA"` AccessKeyId string `json:"access_key_id" example:"LTAI5tBzohLQvNcEh3HZjnWi"`
RegionId string `json:"region_id" example:"ap-southeast-1"` AccessKeySecret string `json:"access_key_secret" example:"XMyTwsKBjubwTMHqUVyPZCYvQFuXZA"`
RegionId string `json:"region_id" example:"ap-southeast-1"`
} }

17
config.tpl.yaml Normal file
View File

@ -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

View File

@ -13,8 +13,4 @@ mysql: # 数据库信息
charset: utf8mb4 charset: utf8mb4
max_open: 500 max_open: 500
max_idle: 5 max_idle: 5
#redis:
# addr: 120.25.84.150:6380
# password: 123456
# db: 0
# poolsize: 5

View File

@ -49,9 +49,9 @@ func (c *WebService) Run(log logger.Interface, cfg *AppConfig) error {
} }
} }
if c.Cfg.IP == "" { 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 { } 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{ srv := &http.Server{

2
go.mod
View File

@ -62,4 +62,6 @@ require (
golang.org/x/tools v0.32.0 // indirect golang.org/x/tools v0.32.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/ini.v1 v1.67.0 // 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
) )

2
go.sum
View File

@ -245,3 +245,5 @@ gorm.io/plugin/dbresolver v1.6.0 h1:XvKDeOtTn1EIX6s4SrKpEH82q0gXVemhYjbYZFGFVcw=
gorm.io/plugin/dbresolver v1.6.0/go.mod h1:tctw63jdrOezFR9HmrKnPkmig3m5Edem9fdxk9bQSzM= gorm.io/plugin/dbresolver v1.6.0/go.mod h1:tctw63jdrOezFR9HmrKnPkmig3m5Edem9fdxk9bQSzM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 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=

View File

@ -1,14 +1,19 @@
package api package api
import ( import (
"encoding/json"
"fmt" "fmt"
"gitea.party/public-messag-service/common/params" "gitea.party/public-messag-service/common/params"
"gitea.party/public-messag-service/common/public" "gitea.party/public-messag-service/common/public"
"gitea.party/public-messag-service/internal/models/mysql"
"gitea.party/public-messag-service/router/web" "gitea.party/public-messag-service/router/web"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk" "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/auth/credentials"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"log"
"strings"
"time"
) )
func EmailXSend(c *gin.Context) { func EmailXSend(c *gin.Context) {
@ -41,26 +46,36 @@ func EmailXSend(c *gin.Context) {
request.ApiName = "SingleSendMail" // 接口名称 request.ApiName = "SingleSendMail" // 接口名称
// 设置请求参数 // 设置请求参数
emailStr := strings.Join(pm.Email, ",")
request.QueryParams["RegionId"] = "cn-hangzhou" request.QueryParams["RegionId"] = "cn-hangzhou"
request.QueryParams["AccountName"] = "sender@your-domain.com" // 发件人地址(需验证) request.QueryParams["AccountName"] = "sender@your-domain.com" // 发件人地址(需验证)
request.QueryParams["AddressType"] = "1" // 0: 随机账号; 1: 发件人地址 request.QueryParams["AddressType"] = "1" // 0: 随机账号; 1: 发件人地址
request.QueryParams["ReplyToAddress"] = "true" // 是否允许回复 request.QueryParams["ReplyToAddress"] = "false" // 是否允许回复
request.QueryParams["ToAddress"] = "recipient@example.com" // 收件人地址 request.QueryParams["ToAddress"] = emailStr // 收件人地址
request.QueryParams["Subject"] = "Test Email from Aliyun" // 邮件主题 request.QueryParams["Subject"] = "Test Email from Aliyun" // 邮件主题
request.QueryParams["HtmlBody"] = "<h1>Hello</h1><p>This is a test email .</p>" // HTML 邮件正文 request.QueryParams["HtmlBody"] = "<h1>Hello</h1><p>" + pm.Content + "</p>" // HTML 邮件正文
// request.QueryParams["TextBody"] = "This is a test email." // 纯文本正文(可选,优先使用 HtmlBody // request.QueryParams["TextBody"] = "This is a test email." // 纯文本正文(可选,优先使用 HtmlBody
// 发送请求 // 发送请求
res, err := client.ProcessCommonRequest(request) res, err := client.ProcessCommonRequest(request)
if err != nil { if err != nil {
panic(err) log.Fatalf("Failed to send request: %v", err)
} }
messagesLogData := make([]map[string]interface{}, 0, len(pm.Email))
// 输出响应 for _, v := range pm.Email {
//fmt.Println(res.GetHttpContentString()) messagesLogData = append(messagesLogData, map[string]interface{}{
// 4. 处理响应 "msg_type": 2,
if res.IsSuccess() { "to_phone_email": v,
result.SetData(fmt.Sprintf("发送失败: %s", res.GetHttpContentString())) "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 return
} else { } else {
result.SetErrMsg( result.SetErrMsg(

View File

@ -1,12 +1,17 @@
package api package api
import ( import (
"encoding/json"
"fmt" "fmt"
"gitea.party/public-messag-service/common/params" "gitea.party/public-messag-service/common/params"
"gitea.party/public-messag-service/common/public" "gitea.party/public-messag-service/common/public"
"gitea.party/public-messag-service/internal/models/mysql"
"gitea.party/public-messag-service/router/web" "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/aliyun/alibaba-cloud-sdk-go/services/dysmsapi"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"log"
"time"
) )
func SmsXSend(c *gin.Context) { func SmsXSend(c *gin.Context) {
@ -21,29 +26,67 @@ func SmsXSend(c *gin.Context) {
} }
client, err := dysmsapi.NewClientWithAccessKey(pm.RegionId, pm.AccessKeyId, pm.AccessKeySecret) client, err := dysmsapi.NewClientWithAccessKey(pm.RegionId, pm.AccessKeyId, pm.AccessKeySecret)
if err != nil { if err != nil {
fmt.Printf("初始化客户端失败: %v\n", err) log.Fatalf("Failed to initialize client: %v", err)
return
} }
// 2. 设置请求参数 // 创建请求
request := dysmsapi.CreateSendMessageToGlobeRequest() request := requests.NewCommonRequest()
request.To = "目标手机号码" // 国际号码格式,例如:+85212345678 request.Method = "POST"
request.Message = "1234" // 短信内容 request.Scheme = "https"
//request.From = "你的短信签名" // 短信签名 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 { if err != nil {
fmt.Printf("发送短信失败: %v\n", err) log.Fatalf("Failed to marshal messages: %v", err)
return
} }
request.QueryParams["MessageParamList"] = string(messagesJSON)
// 4. 处理响应 // 发送请求
if res.IsSuccess() { res, err := client.ProcessCommonRequest(request)
fmt.Println("短信发送成功:", res.MessageId) 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 { } else {
result.SetErrMsg(public.RespCodeErrorUnknown, fmt.Sprintf("短信发送失败: %s", res.ResponseDescription)).AddError(err) result.SetErrMsg(public.RespCodeErrorUnknown, fmt.Sprintf("短信发送失败: %s", res.GetHttpContentString())).AddError(err)
return return
} }
result.SetData(nil)
} }

View File

@ -1,64 +1,58 @@
package mysql package mysql
import ( //var (
"fmt" // MsgSer = component.MySQLClient{}
"gitea.party/public-messag-service/config/app_ser/component" //)
//
"gorm.io/gorm" //type Model interface {
) // TableName() string
// DB() *gorm.DB
var ( //}
Ns9 = component.MySQLClient{} //
) //func GetMsgSerDB() *gorm.DB {
// return MsgSer.DB
type Model interface { //}
TableName() string //
DB() *gorm.DB //func GetById(m Model, id int) error {
} // tx := m.DB().Where("id=?", id).Take(m)
// return tx.Error
func GetNs9DB() *gorm.DB { //}
return Ns9.DB //
} //func Add(m Model) error {
func GetById(m Model, id int) error { // tx := m.DB().Create(m)
tx := m.DB().Where("id=?", id).Take(m) // return tx.Error
return tx.Error //}
} //
//func Save(m Model, id int, columns []string) error {
func Add(m Model) error { // tx := m.DB().Select(columns)
tx := m.DB().Create(m) // tx.Where("id=?", id).Updates(m)
return tx.Error // return tx.Error
} //}
//
func Save(m Model, id int, columns []string) error { //func OrderBySQL(orderBy string, order int, isNullBottom bool) string {
tx := m.DB().Select(columns) // if orderBy != "" {
tx.Where("id=?", id).Updates(m) // if isNullBottom {
return tx.Error // orderBy = fmt.Sprintf("IF(%s IS NULL, 0, 1) desc, %s", orderBy, orderBy)
} // }
// if order == 0 { // 为0倒序否则升序
func OrderBySQL(orderBy string, order int, isNullBottom bool) string { // return fmt.Sprintf("%s desc", orderBy)
if orderBy != "" { // } else {
if isNullBottom { // return fmt.Sprintf("%s asc ", orderBy)
orderBy = fmt.Sprintf("IF(%s IS NULL, 0, 1) desc, %s", orderBy, orderBy) // }
} // }
if order == 0 { // 为0倒序否则升序 // return ""
return fmt.Sprintf("%s desc", orderBy) //}
} else { //
return fmt.Sprintf("%s asc ", orderBy) //func GetOrderByStr(by string, order int, isNullBottom bool) string {
} // if by != "" {
} // if isNullBottom {
return "" // by = fmt.Sprintf("IF(%s IS NULL, 0, 1) desc, %s", by, by)
} // }
// if order == 1 { // 为0倒序否则升序
func GetOrderByStr(by string, order int, isNullBottom bool) string { // return fmt.Sprintf("%s desc ", by)
if by != "" { // } else {
if isNullBottom { // return fmt.Sprintf("%s asc ", by)
by = fmt.Sprintf("IF(%s IS NULL, 0, 1) desc, %s", by, by) // }
} // }
if order == 1 { // 为0倒序否则升序 // return ""
return fmt.Sprintf("%s desc ", by) //}
} else {
return fmt.Sprintf("%s asc ", by)
}
}
return ""
}

View File

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

View File

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

3
swag-init.bat Normal file
View File

@ -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