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

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
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"`
}

View File

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

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
max_open: 500
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 == "" {
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{

2
go.mod
View File

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

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

View File

@ -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"] = "<h1>Hello</h1><p>This is a test email .</p>" // 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"] = "<h1>Hello</h1><p>" + pm.Content + "</p>" // 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(

View File

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

View File

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

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