feat: add message template
This commit is contained in:
@@ -0,0 +1,336 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"message-nest/models"
|
||||
"message-nest/pkg/app"
|
||||
"message-nest/pkg/e"
|
||||
"message-nest/pkg/util"
|
||||
"message-nest/service/message_template_service"
|
||||
"message-nest/service/send_ins_service"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// GetMessageTemplateList 获取消息模板列表
|
||||
func GetMessageTemplateList(c *gin.Context) {
|
||||
appG := app.Gin{C: c}
|
||||
text := c.Query("text")
|
||||
status := c.Query("status")
|
||||
|
||||
offset, limit := util.GetPageSize(c)
|
||||
templateService := message_template_service.MessageTemplateService{
|
||||
Text: text,
|
||||
Status: status,
|
||||
PageNum: offset,
|
||||
PageSize: limit,
|
||||
}
|
||||
|
||||
templates, err := templateService.GetAll()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "获取消息模板失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
count, err := templateService.Count()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "获取消息模板总数失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "获取消息模板成功", map[string]interface{}{
|
||||
"lists": templates,
|
||||
"total": count,
|
||||
})
|
||||
}
|
||||
|
||||
// GetMessageTemplate 获取单个消息模板
|
||||
func GetMessageTemplate(c *gin.Context) {
|
||||
appG := app.Gin{C: c}
|
||||
id := c.Query("id")
|
||||
|
||||
templateService := message_template_service.MessageTemplateService{
|
||||
ID: id,
|
||||
}
|
||||
|
||||
exists, err := templateService.ExistByID()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "查询模板失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if !exists {
|
||||
appG.CResponse(http.StatusNotFound, "模板不存在!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
template, err := templateService.Get()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "获取模板详情失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "获取模板详情成功", template)
|
||||
}
|
||||
|
||||
// AddMessageTemplate 添加消息模板
|
||||
func AddMessageTemplate(c *gin.Context) {
|
||||
appG := app.Gin{C: c}
|
||||
|
||||
var req struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
TextTemplate string `json:"text_template"`
|
||||
HTMLTemplate string `json:"html_template"`
|
||||
MarkdownTemplate string `json:"markdown_template"`
|
||||
Placeholders string `json:"placeholders"`
|
||||
AtMobiles string `json:"at_mobiles"`
|
||||
AtUserIds string `json:"at_user_ids"`
|
||||
IsAtAll bool `json:"is_at_all"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, "参数错误:"+err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if req.TextTemplate == "" && req.HTMLTemplate == "" && req.MarkdownTemplate == "" {
|
||||
appG.CResponse(http.StatusBadRequest, "至少需要填写一种格式的模板内容", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Status == "" {
|
||||
req.Status = "enabled"
|
||||
}
|
||||
|
||||
templateService := message_template_service.MessageTemplateService{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
TextTemplate: req.TextTemplate,
|
||||
HTMLTemplate: req.HTMLTemplate,
|
||||
MarkdownTemplate: req.MarkdownTemplate,
|
||||
Placeholders: req.Placeholders,
|
||||
AtMobiles: req.AtMobiles,
|
||||
AtUserIds: req.AtUserIds,
|
||||
IsAtAll: req.IsAtAll,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
if err := templateService.Add(); err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "添加模板失败:"+err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "添加模板成功", nil)
|
||||
}
|
||||
|
||||
// EditMessageTemplate 编辑消息模板
|
||||
func EditMessageTemplate(c *gin.Context) {
|
||||
appG := app.Gin{C: c}
|
||||
|
||||
var req struct {
|
||||
ID string `json:"id" binding:"required"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
TextTemplate string `json:"text_template"`
|
||||
HTMLTemplate string `json:"html_template"`
|
||||
MarkdownTemplate string `json:"markdown_template"`
|
||||
Placeholders string `json:"placeholders"`
|
||||
AtMobiles string `json:"at_mobiles"`
|
||||
AtUserIds string `json:"at_user_ids"`
|
||||
IsAtAll bool `json:"is_at_all"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, "参数错误:"+err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if req.TextTemplate == "" && req.HTMLTemplate == "" && req.MarkdownTemplate == "" {
|
||||
appG.CResponse(http.StatusBadRequest, "至少需要填写一种格式的模板内容", nil)
|
||||
return
|
||||
}
|
||||
|
||||
templateService := message_template_service.MessageTemplateService{
|
||||
ID: req.ID,
|
||||
}
|
||||
|
||||
exists, err := templateService.ExistByID()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "查询模板失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if !exists {
|
||||
appG.CResponse(http.StatusNotFound, "模板不存在!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
templateService.Name = req.Name
|
||||
templateService.Description = req.Description
|
||||
templateService.TextTemplate = req.TextTemplate
|
||||
templateService.HTMLTemplate = req.HTMLTemplate
|
||||
templateService.MarkdownTemplate = req.MarkdownTemplate
|
||||
templateService.Placeholders = req.Placeholders
|
||||
templateService.AtMobiles = req.AtMobiles
|
||||
templateService.AtUserIds = req.AtUserIds
|
||||
templateService.IsAtAll = req.IsAtAll
|
||||
templateService.Status = req.Status
|
||||
|
||||
if err := templateService.Update(); err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "更新模板失败:"+err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "更新模板成功", nil)
|
||||
}
|
||||
|
||||
// DeleteMessageTemplate 删除消息模板
|
||||
func DeleteMessageTemplate(c *gin.Context) {
|
||||
appG := app.Gin{C: c}
|
||||
|
||||
var req struct {
|
||||
ID string `json:"id" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, "参数错误:"+err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
templateService := message_template_service.MessageTemplateService{
|
||||
ID: req.ID,
|
||||
}
|
||||
|
||||
exists, err := templateService.ExistByID()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "查询模板失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if !exists {
|
||||
appG.CResponse(http.StatusNotFound, "模板不存在!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err := templateService.Delete(); err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "删除模板失败:"+err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "删除模板成功", nil)
|
||||
}
|
||||
|
||||
// PreviewMessageTemplate 预览消息模板
|
||||
func PreviewMessageTemplate(c *gin.Context) {
|
||||
appG := app.Gin{C: c}
|
||||
|
||||
var req struct {
|
||||
ID string `json:"id" binding:"required"`
|
||||
Params map[string]string `json:"params"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, "参数错误:"+err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
templateService := message_template_service.MessageTemplateService{
|
||||
ID: req.ID,
|
||||
}
|
||||
|
||||
exists, err := templateService.ExistByID()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "查询模板失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if !exists {
|
||||
appG.CResponse(http.StatusNotFound, "模板不存在!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
preview, err := templateService.PreviewTemplate(req.Params)
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusInternalServerError, "预览模板失败:"+err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "预览模板成功", preview)
|
||||
}
|
||||
|
||||
// GetTemplateWithIns 获取模板及其关联的实例
|
||||
func GetTemplateWithIns(c *gin.Context) {
|
||||
appG := app.Gin{C: c}
|
||||
id := c.Query("id")
|
||||
|
||||
if id == "" {
|
||||
appG.CResponse(http.StatusBadRequest, "模板ID为空!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取模板信息
|
||||
template, err := models.GetMessageTemplateByID(id)
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, "获取模板信息失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取关联的实例列表
|
||||
insList, err := models.GetTemplateInsList(id)
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, "获取实例列表失败!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
result := map[string]interface{}{
|
||||
"template": template,
|
||||
"ins_list": insList,
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "获取模板信息成功", result)
|
||||
}
|
||||
|
||||
// TemplateInsReq 模板实例请求结构
|
||||
type TemplateInsReq struct {
|
||||
ID string `json:"id" validate:"required,len=12" label:"实例id"`
|
||||
TemplateID string `json:"template_id" validate:"required" label:"模板id"`
|
||||
WayID string `json:"way_id" validate:"required,len=12" label:"渠道id"`
|
||||
ContentType string `json:"content_type" validate:"required,max=100" label:"实例内容类型"`
|
||||
Config string `json:"config" validate:"" label:"任务配置"`
|
||||
Extra string `json:"extra" validate:"" label:"任务额外信息"`
|
||||
WayType string `json:"way_type" validate:"required,max=100" label:"渠道类型"`
|
||||
}
|
||||
|
||||
// AddTemplateIns 添加模板关联的实例
|
||||
func AddTemplateIns(c *gin.Context) {
|
||||
var (
|
||||
appG = app.Gin{C: c}
|
||||
req TemplateInsReq
|
||||
)
|
||||
|
||||
errCode, errStr := app.BindJsonAndPlayValid(c, &req)
|
||||
if errCode != e.SUCCESS {
|
||||
appG.CResponse(errCode, errStr, nil)
|
||||
return
|
||||
}
|
||||
|
||||
sendTaskInsService := send_ins_service.SendTaskInsService{}
|
||||
err := sendTaskInsService.AddOne(models.SendTasksIns{
|
||||
UUIDModel: models.UUIDModel{ID: req.ID},
|
||||
TemplateID: req.TemplateID,
|
||||
WayID: req.WayID,
|
||||
WayType: req.WayType,
|
||||
ContentType: req.ContentType,
|
||||
Config: req.Config,
|
||||
Extra: req.Extra,
|
||||
})
|
||||
if err != "" {
|
||||
appG.CResponse(http.StatusBadRequest, fmt.Sprintf("添加实例失败!错误原因:%s", err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
appG.CResponse(http.StatusOK, "添加实例成功!", nil)
|
||||
}
|
||||
@@ -61,6 +61,7 @@ func DoSendMassage(c *gin.Context) {
|
||||
}
|
||||
|
||||
msgService := send_message_service.SendMessageService{
|
||||
SendMode: send_message_service.SendModeTask, // 明确标记为任务模式
|
||||
TaskID: taskID,
|
||||
Title: req.Title,
|
||||
Text: req.Text,
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"message-nest/models"
|
||||
"message-nest/pkg/app"
|
||||
"message-nest/pkg/e"
|
||||
utilpkg "message-nest/pkg/util"
|
||||
"message-nest/service/send_message_service"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type SendMessageByTemplateReq struct {
|
||||
Token string `json:"token" validate:"required" label:"模板token"`
|
||||
Title string `json:"title" validate:"required" label:"消息标题"`
|
||||
Placeholders map[string]interface{} `json:"placeholders" label:"占位符"`
|
||||
}
|
||||
|
||||
// DoSendMessageByTemplate 使用模板发送消息
|
||||
func DoSendMessageByTemplate(c *gin.Context) {
|
||||
var (
|
||||
appG = app.Gin{C: c}
|
||||
req SendMessageByTemplateReq
|
||||
)
|
||||
|
||||
errCode, errMsg := app.BindJsonAndPlayValid(c, &req)
|
||||
if errCode != e.SUCCESS {
|
||||
appG.CResponse(errCode, errMsg, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 解析 token 为模板 ID
|
||||
templateID, err := utilpkg.DecryptTokenHex(req.Token, 71) // 71 为简单对称密钥
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, fmt.Sprintf("token解析失败:%v", err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取模板
|
||||
template, err := models.GetMessageTemplateByID(templateID)
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, fmt.Sprintf("模板不存在:%s", err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查模板状态
|
||||
if template.Status != "enabled" {
|
||||
appG.CResponse(http.StatusBadRequest, "模板已禁用", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 替换占位符
|
||||
textContent := replacePlaceholders(template.TextTemplate, req.Placeholders)
|
||||
htmlContent := replacePlaceholders(template.HTMLTemplate, req.Placeholders)
|
||||
markdownContent := replacePlaceholders(template.MarkdownTemplate, req.Placeholders)
|
||||
|
||||
// 解析@提醒配置
|
||||
var atMobiles []string
|
||||
var atUserIds []string
|
||||
if template.AtMobiles != "" {
|
||||
atMobiles = strings.Split(template.AtMobiles, ",")
|
||||
// 去除空格
|
||||
for i := range atMobiles {
|
||||
atMobiles[i] = strings.TrimSpace(atMobiles[i])
|
||||
}
|
||||
}
|
||||
if template.AtUserIds != "" {
|
||||
atUserIds = strings.Split(template.AtUserIds, ",")
|
||||
// 去除空格
|
||||
for i := range atUserIds {
|
||||
atUserIds[i] = strings.TrimSpace(atUserIds[i])
|
||||
}
|
||||
}
|
||||
|
||||
// 获取模板关联的实例列表
|
||||
insList, err := models.GetTemplateInsList(templateID)
|
||||
if err != nil || len(insList) == 0 {
|
||||
appG.CResponse(http.StatusBadRequest, "模板没有配置发送实例", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 过滤启用的实例
|
||||
var enabledCount int
|
||||
for _, ins := range insList {
|
||||
if ins.Enable == 1 {
|
||||
enabledCount++
|
||||
}
|
||||
}
|
||||
|
||||
if enabledCount == 0 {
|
||||
appG.CResponse(http.StatusBadRequest, "模板没有启用的发送实例", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 使用发送服务进行发送
|
||||
// 将模板ID作为TaskID传入,用于日志记录
|
||||
msgService := send_message_service.SendMessageService{
|
||||
SendMode: send_message_service.SendModeTemplate, // 明确标记为模板模式
|
||||
TaskID: templateID, // 使用模板ID作为TaskID(用于日志记录)
|
||||
TemplateID: templateID, // 模板ID
|
||||
Name: template.Name, // 模板名称
|
||||
Title: req.Title,
|
||||
Text: textContent,
|
||||
HTML: htmlContent,
|
||||
MarkDown: markdownContent,
|
||||
CallerIp: c.ClientIP(),
|
||||
AtMobiles: atMobiles,
|
||||
AtUserIds: atUserIds,
|
||||
AtAll: template.IsAtAll,
|
||||
DefaultLogger: logrus.WithFields(logrus.Fields{
|
||||
"prefix": "[Template Send]",
|
||||
}),
|
||||
}
|
||||
|
||||
// 发送前检查
|
||||
task, err := msgService.SendPreCheck()
|
||||
if err != nil {
|
||||
appG.CResponse(http.StatusBadRequest, fmt.Sprintf("发送检查不通过:%s", err), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 异步发送
|
||||
msgService.AsyncSend(task)
|
||||
appG.CResponse(http.StatusOK, "success", map[string]interface{}{
|
||||
"token": req.Token,
|
||||
"count": enabledCount,
|
||||
})
|
||||
}
|
||||
|
||||
// replacePlaceholders 替换模板中的占位符
|
||||
func replacePlaceholders(template string, placeholders map[string]interface{}) string {
|
||||
if template == "" || placeholders == nil {
|
||||
return template
|
||||
}
|
||||
|
||||
result := template
|
||||
for key, value := range placeholders {
|
||||
placeholder := fmt.Sprintf("{{%s}}", key)
|
||||
result = strings.ReplaceAll(result, placeholder, fmt.Sprintf("%v", value))
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user