From ff194daef28ae13e5992a6a573dce0c3984c7cdd Mon Sep 17 00:00:00 2001
From: engigu
Date: Thu, 4 Dec 2025 21:16:32 +0800
Subject: [PATCH] feat: send recode adjust
---
pkg/message/dtalk.go | 40 +++++
pkg/message/qyweixin.go | 39 ++++-
routers/api/v1/send_message.go | 24 ++-
.../send_message_service/message_handler.go | 164 ------------------
service/send_message_service/send_custom.go | 29 ----
service/send_message_service/send_dtalk.go | 47 -----
service/send_message_service/send_email.go | 37 ----
.../send_hosted_message.go | 36 ----
service/send_message_service/send_message.go | 42 +++--
service/send_message_service/send_qywexin.go | 46 -----
.../send_wechat_account.go | 42 -----
.../unified/channel_registry.go | 126 ++++++++++++++
.../unified/channels/base.go | 45 +++++
.../unified/channels/custom.go | 83 +++++++++
.../unified/channels/dtalk.go | 56 ++++++
.../unified/channels/email.go | 46 +++++
.../unified/channels/nest.go | 38 ++++
.../unified/channels/qyweixin.go | 58 +++++++
.../unified/channels/types.go | 53 ++++++
.../unified/channels/wechat.go | 48 +++++
.../pages/sendTasks/ApiCodeViewer.vue | 58 ++++++-
web/src/util/viewApi.js | 10 ++
22 files changed, 743 insertions(+), 424 deletions(-)
delete mode 100644 service/send_message_service/message_handler.go
delete mode 100644 service/send_message_service/send_custom.go
delete mode 100644 service/send_message_service/send_dtalk.go
delete mode 100644 service/send_message_service/send_email.go
delete mode 100644 service/send_message_service/send_hosted_message.go
delete mode 100644 service/send_message_service/send_qywexin.go
delete mode 100644 service/send_message_service/send_wechat_account.go
create mode 100644 service/send_message_service/unified/channel_registry.go
create mode 100644 service/send_message_service/unified/channels/base.go
create mode 100644 service/send_message_service/unified/channels/custom.go
create mode 100644 service/send_message_service/unified/channels/dtalk.go
create mode 100644 service/send_message_service/unified/channels/email.go
create mode 100644 service/send_message_service/unified/channels/nest.go
create mode 100644 service/send_message_service/unified/channels/qyweixin.go
create mode 100644 service/send_message_service/unified/channels/types.go
create mode 100644 service/send_message_service/unified/channels/wechat.go
diff --git a/pkg/message/dtalk.go b/pkg/message/dtalk.go
index fd0e0cf..d63d05b 100644
--- a/pkg/message/dtalk.go
+++ b/pkg/message/dtalk.go
@@ -62,6 +62,26 @@ func (t *Dtalk) SendMessageText(text string, at ...string) ([]byte, error) {
"content": text,
},
}
+
+ // 添加@功能
+ if len(at) > 0 {
+ atMobiles := []string{}
+ isAtAll := false
+
+ for _, mobile := range at {
+ if mobile == "all" || mobile == "@all" {
+ isAtAll = true
+ } else {
+ atMobiles = append(atMobiles, mobile)
+ }
+ }
+
+ msg["at"] = map[string]interface{}{
+ "atMobiles": atMobiles,
+ "isAtAll": isAtAll,
+ }
+ }
+
resp, err := t.Request(msg)
return resp, err
}
@@ -74,6 +94,26 @@ func (t *Dtalk) SendMessageMarkdown(title, text string, at ...string) ([]byte, e
"text": text,
},
}
+
+ // 添加@功能
+ if len(at) > 0 {
+ atMobiles := []string{}
+ isAtAll := false
+
+ for _, mobile := range at {
+ if mobile == "all" || mobile == "@all" {
+ isAtAll = true
+ } else {
+ atMobiles = append(atMobiles, mobile)
+ }
+ }
+
+ msg["at"] = map[string]interface{}{
+ "atMobiles": atMobiles,
+ "isAtAll": isAtAll,
+ }
+ }
+
resp, err := t.Request(msg)
return resp, err
}
diff --git a/pkg/message/qyweixin.go b/pkg/message/qyweixin.go
index d2a4968..5e62152 100644
--- a/pkg/message/qyweixin.go
+++ b/pkg/message/qyweixin.go
@@ -53,10 +53,40 @@ func (t *QyWeiXin) Request(msg interface{}) ([]byte, error) {
func (t *QyWeiXin) SendMessageText(text string, at ...string) ([]byte, error) {
msg := map[string]interface{}{
"msgtype": "text",
- "text": map[string]string{
+ "text": map[string]interface{}{
"content": text,
},
}
+
+ // 添加@功能
+ // 企业微信支持两种@方式:
+ // 1. mentioned_list: userid列表或"@all"
+ // 2. mentioned_mobile_list: 手机号列表
+ if len(at) > 0 {
+ mentionedList := []string{}
+ mentionedMobileList := []string{}
+
+ for _, item := range at {
+ if item == "@all" || item == "all" {
+ mentionedList = append(mentionedList, "@all")
+ } else if len(item) == 11 && item[0] == '1' {
+ // 判断是否为手机号(简单判断:11位且以1开头)
+ mentionedMobileList = append(mentionedMobileList, item)
+ } else {
+ // 否则当作userid处理
+ mentionedList = append(mentionedList, item)
+ }
+ }
+
+ textContent := msg["text"].(map[string]interface{})
+ if len(mentionedList) > 0 {
+ textContent["mentioned_list"] = mentionedList
+ }
+ if len(mentionedMobileList) > 0 {
+ textContent["mentioned_mobile_list"] = mentionedMobileList
+ }
+ }
+
resp, err := t.Request(msg)
return resp, err
}
@@ -64,11 +94,14 @@ func (t *QyWeiXin) SendMessageText(text string, at ...string) ([]byte, error) {
func (t *QyWeiXin) SendMessageMarkdown(title, text string, at ...string) ([]byte, error) {
msg := map[string]interface{}{
"msgtype": "markdown",
- "markdown": map[string]string{
- "title": title,
+ "markdown": map[string]interface{}{
"content": text,
},
}
+
+ // 企业微信Markdown消息不支持@功能,但可以在内容中手动添加
+ // 如果需要@功能,建议使用text类型
+
resp, err := t.Request(msg)
return resp, err
}
diff --git a/routers/api/v1/send_message.go b/routers/api/v1/send_message.go
index 99736b5..d4e3ee7 100644
--- a/routers/api/v1/send_message.go
+++ b/routers/api/v1/send_message.go
@@ -23,6 +23,11 @@ type SendMessageReq struct {
URL string `json:"url" label:"消息详情url地址"`
MarkDown string `json:"markdown" label:"markdown内容"`
Mode string `json:"mode" label:"是否异步发送"`
+
+ // @提及相关参数
+ AtMobiles []string `json:"at_mobiles" label:"@的手机号列表"`
+ AtUserIds []string `json:"at_user_ids" label:"@的用户ID列表"`
+ AtAll bool `json:"at_all" label:"是否@所有人"`
}
// DoSendMassage 外部调用发信接口
@@ -56,15 +61,18 @@ func DoSendMassage(c *gin.Context) {
}
msgService := send_message_service.SendMessageService{
- TaskID: taskID,
- Title: req.Title,
- Text: req.Text,
- HTML: req.HTML,
- URL: req.URL,
- MarkDown: req.MarkDown,
- CallerIp: c.ClientIP(),
+ TaskID: taskID,
+ Title: req.Title,
+ Text: req.Text,
+ HTML: req.HTML,
+ URL: req.URL,
+ MarkDown: req.MarkDown,
+ CallerIp: c.ClientIP(),
+ AtMobiles: req.AtMobiles,
+ AtUserIds: req.AtUserIds,
+ AtAll: req.AtAll,
DefaultLogger: logrus.WithFields(logrus.Fields{
- //"prefix": "[Message Instance]",
+ "prefix": "[Send Instance]",
}),
}
task, err := msgService.SendPreCheck()
diff --git a/service/send_message_service/message_handler.go b/service/send_message_service/message_handler.go
deleted file mode 100644
index f74247a..0000000
--- a/service/send_message_service/message_handler.go
+++ /dev/null
@@ -1,164 +0,0 @@
-package send_message_service
-
-import (
- "message-nest/models"
- "message-nest/service/send_way_service"
-)
-
-// 消息类型常量定义
-const (
- MessageTypeEmail = "Email"
- MessageTypeDtalk = "Dtalk"
- MessageTypeQyWeiXin = "QyWeiXin"
- MessageTypeCustom = "Custom"
- MessageTypeWeChatOFAccount = "WeChatOFAccount"
- MessageTypeMessageNest = "MessageNest"
-)
-
-// MessageHandler 消息处理器接口
-type MessageHandler interface {
- // Send 发送消息
- // 返回:响应内容,错误信息
- Send(msgObj interface{}, ins models.SendTasksIns, typeC string, title string, content string, url string) (string, string)
-
- // GetType 返回该处理器支持的消息类型字符串
- GetType() string
-}
-
-// MessageHandlerRegistry 消息处理器注册表
-type MessageHandlerRegistry struct {
- handlers map[string]MessageHandler
-}
-
-// NewMessageHandlerRegistry 创建新的消息处理器注册表
-func NewMessageHandlerRegistry() *MessageHandlerRegistry {
- return &MessageHandlerRegistry{
- handlers: make(map[string]MessageHandler),
- }
-}
-
-// Register 注册消息处理器
-func (r *MessageHandlerRegistry) Register(handler MessageHandler) {
- r.handlers[handler.GetType()] = handler
-}
-
-// GetHandler 根据类型字符串获取对应的处理器
-func (r *MessageHandlerRegistry) GetHandler(wayType string) (MessageHandler, bool) {
- handler, ok := r.handlers[wayType]
- return handler, ok
-}
-
-// 全局消息处理器注册表
-var globalRegistry = NewMessageHandlerRegistry()
-
-// init 初始化时注册所有处理器
-func init() {
- globalRegistry.Register(&EmailHandler{})
- globalRegistry.Register(&DtalkHandler{})
- globalRegistry.Register(&QyWeiXinHandler{})
- globalRegistry.Register(&CustomHandler{})
- globalRegistry.Register(&WeChatOfAccountHandler{})
- globalRegistry.Register(&HostMessageHandler{})
-}
-
-// GetGlobalRegistry 获取全局注册表
-func GetGlobalRegistry() *MessageHandlerRegistry {
- return globalRegistry
-}
-
-// EmailHandler 邮箱消息处理器
-type EmailHandler struct{}
-
-func (h *EmailHandler) GetType() string {
- return MessageTypeEmail
-}
-
-func (h *EmailHandler) Send(msgObj interface{}, ins models.SendTasksIns, typeC string, title string, content string, url string) (string, string) {
- auth, ok := msgObj.(send_way_service.WayDetailEmail)
- if !ok {
- return "", "类型转换失败"
- }
- es := EmailService{}
- errMsg := es.SendTaskEmail(auth, ins, typeC, title, content)
- return "", errMsg
-}
-
-// DtalkHandler 钉钉消息处理器
-type DtalkHandler struct{}
-
-func (h *DtalkHandler) GetType() string {
- return MessageTypeDtalk
-}
-
-func (h *DtalkHandler) Send(msgObj interface{}, ins models.SendTasksIns, typeC string, title string, content string, url string) (string, string) {
- auth, ok := msgObj.(send_way_service.WayDetailDTalk)
- if !ok {
- return "", "类型转换失败"
- }
- es := DtalkService{}
- return es.SendDtalkMessage(auth, ins, typeC, title, content)
-}
-
-// QyWeiXinHandler 企业微信消息处理器
-type QyWeiXinHandler struct{}
-
-func (h *QyWeiXinHandler) GetType() string {
- return MessageTypeQyWeiXin
-}
-
-func (h *QyWeiXinHandler) Send(msgObj interface{}, ins models.SendTasksIns, typeC string, title string, content string, url string) (string, string) {
- auth, ok := msgObj.(send_way_service.WayDetailQyWeiXin)
- if !ok {
- return "", "类型转换失败"
- }
- es := QyWeiXinService{}
- return es.SendQyWeiXinMessage(auth, ins, typeC, title, content)
-}
-
-// CustomHandler 自定义webhook消息处理器
-type CustomHandler struct{}
-
-func (h *CustomHandler) GetType() string {
- return MessageTypeCustom
-}
-
-func (h *CustomHandler) Send(msgObj interface{}, ins models.SendTasksIns, typeC string, title string, content string, url string) (string, string) {
- auth, ok := msgObj.(send_way_service.WayDetailCustom)
- if !ok {
- return "", "类型转换失败"
- }
- cs := CustomService{}
- return cs.SendCustomMessage(auth, ins, typeC, title, content)
-}
-
-// WeChatOfAccountHandler 微信公众号消息处理器
-type WeChatOfAccountHandler struct{}
-
-func (h *WeChatOfAccountHandler) GetType() string {
- return MessageTypeWeChatOFAccount
-}
-
-func (h *WeChatOfAccountHandler) Send(msgObj interface{}, ins models.SendTasksIns, typeC string, title string, content string, url string) (string, string) {
- auth, ok := msgObj.(send_way_service.WeChatOFAccount)
- if !ok {
- return "", "类型转换失败"
- }
- cs := WeChatOfAccountService{}
- return cs.SendWeChatOfAccountMessage(auth, ins, typeC, title, content, url)
-}
-
-// HostMessageHandler 托管消息处理器
-type HostMessageHandler struct{}
-
-func (h *HostMessageHandler) GetType() string {
- return MessageTypeMessageNest
-}
-
-func (h *HostMessageHandler) Send(msgObj interface{}, ins models.SendTasksIns, typeC string, title string, content string, url string) (string, string) {
- auth, ok := msgObj.(send_way_service.MessageNest)
- if !ok {
- return "", "类型转换失败"
- }
- cs := HostMessageService{}
- return cs.SendHostMessage(auth, ins, typeC, title, content)
-}
diff --git a/service/send_message_service/send_custom.go b/service/send_message_service/send_custom.go
deleted file mode 100644
index 228ba28..0000000
--- a/service/send_message_service/send_custom.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package send_message_service
-
-import (
- "encoding/json"
- "fmt"
-
- "message-nest/models"
- "message-nest/pkg/message"
- "message-nest/service/send_way_service"
- "strings"
-)
-
-type CustomService struct {
-}
-
-// SendCustomMessage 执行发送钉钉
-func (s *CustomService) SendCustomMessage(auth send_way_service.WayDetailCustom, ins models.SendTasksIns, typeC string, title string, content string) (string, string) {
- errMsg := ""
- cli := message.CustomWebhook{}
- data, _ := json.Marshal(content)
- dataStr := string(data)
- dataStr = strings.Trim(dataStr, "\"")
- bodyStr := strings.Replace(auth.Body, "TEXT", dataStr, -1)
- res, err := cli.Request(auth.Webhook, bodyStr)
- if err != nil {
- errMsg = fmt.Sprintf("发送失败:%s", err)
- }
- return string(res), errMsg
-}
diff --git a/service/send_message_service/send_dtalk.go b/service/send_message_service/send_dtalk.go
deleted file mode 100644
index 0a8ca39..0000000
--- a/service/send_message_service/send_dtalk.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package send_message_service
-
-import (
- "fmt"
- "message-nest/models"
- "message-nest/pkg/message"
- "message-nest/service/send_ins_service"
- "message-nest/service/send_way_service"
-)
-
-type DtalkService struct {
-}
-
-// SendDtalkMessage 执行发送钉钉
-func (s *DtalkService) SendDtalkMessage(auth send_way_service.WayDetailDTalk, ins models.SendTasksIns, typeC string, title string, content string) (string, string) {
- insService := send_ins_service.SendTaskInsService{}
- errStr, c := insService.ValidateDiffIns(ins)
- if errStr != "" {
- return errStr, ""
- }
- _, ok := c.(models.InsDtalkConfig)
- if !ok {
- return "钉钉config校验失败", ""
- }
-
- errMsg := ""
- var res []byte
- var err error
- cli := message.Dtalk{
- AccessToken: auth.AccessToken,
- Secret: auth.Secret,
- }
- if typeC == "text" {
- res, err = cli.SendMessageText(content)
- if err != nil {
- errMsg = fmt.Sprintf("发送失败:%s", ins.ContentType)
- }
- } else if typeC == "markdown" {
- res, err = cli.SendMessageMarkdown(title, content)
- if err != nil {
- errMsg = fmt.Sprintf("发送失败:%s", ins.ContentType)
- }
- } else {
- errMsg = fmt.Sprintf("未知的钉钉发送内容类型:%s", ins.ContentType)
- }
- return string(res), errMsg
-}
diff --git a/service/send_message_service/send_email.go b/service/send_message_service/send_email.go
deleted file mode 100644
index 829b7b5..0000000
--- a/service/send_message_service/send_email.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package send_message_service
-
-import (
- "fmt"
- "message-nest/models"
- "message-nest/pkg/message"
- "message-nest/service/send_ins_service"
- "message-nest/service/send_way_service"
-)
-
-type EmailService struct {
-}
-
-// SendTaskEmail 执行发送邮件
-func (s *EmailService) SendTaskEmail(auth send_way_service.WayDetailEmail, ins models.SendTasksIns, typeC string, title string, content string) string {
- insService := send_ins_service.SendTaskInsService{}
- errStr, c := insService.ValidateDiffIns(ins)
- if errStr != "" {
- return errStr
- }
- config, ok := c.(models.InsEmailConfig)
- if !ok {
- return "邮箱config校验失败"
- }
-
- var emailer message.EmailMessage
- errMsg := ""
- emailer.Init(auth.Server, auth.Port, auth.Account, auth.Passwd)
- if typeC == "text" {
- errMsg = emailer.SendTextMessage(config.ToAccount, title, content)
- } else if typeC == "html" {
- errMsg = emailer.SendHtmlMessage(config.ToAccount, title, content)
- } else {
- errMsg = fmt.Sprintf("未知的邮件发送内容类型:%s", ins.ContentType)
- }
- return errMsg
-}
diff --git a/service/send_message_service/send_hosted_message.go b/service/send_message_service/send_hosted_message.go
deleted file mode 100644
index 7c670ae..0000000
--- a/service/send_message_service/send_hosted_message.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package send_message_service
-
-import (
- "message-nest/models"
- "message-nest/service/hosted_message_service"
- "message-nest/service/send_way_service"
-)
-
-type HostMessageService struct {
-}
-
-// SendHostMessage 执行托管消息记录
-func (s *HostMessageService) SendHostMessage(
- auth send_way_service.MessageNest,
- ins models.SendTasksIns,
- typeC string,
- title string,
- content string) (string, string) {
-
- errMsg := ""
- var res string
- var err error
- messageService := hosted_message_service.HostMessageService{
- Title: title,
- Content: content,
- Type: typeC,
- }
- err = messageService.Add()
- if err != nil {
- errMsg = err.Error()
- res = "托管消息创建失败!"
- } else {
- res = "托管消息创建成功!"
- }
- return string(res), errMsg
-}
diff --git a/service/send_message_service/send_message.go b/service/send_message_service/send_message.go
index 9981125..1874ff6 100644
--- a/service/send_message_service/send_message.go
+++ b/service/send_message_service/send_message.go
@@ -5,6 +5,7 @@ import (
"fmt"
"message-nest/models"
"message-nest/pkg/constant"
+ "message-nest/service/send_message_service/unified"
"message-nest/service/send_task_service"
"message-nest/service/send_way_service"
"strings"
@@ -33,6 +34,11 @@ type SendMessageService struct {
MarkDown string
CallerIp string
+ // @提及相关字段
+ AtMobiles []string
+ AtUserIds []string
+ AtAll bool
+
Status int
LogOutput []string
@@ -151,19 +157,33 @@ func (sm *SendMessageService) Send(task models.TaskIns) (string, error) {
continue
}
- // 使用注册的处理器发送消息
- registry := GetGlobalRegistry()
- handler, ok := registry.GetHandler(way.Type)
+ // 使用新的Channel架构发送消息
+ channelRegistry := unified.GetGlobalChannelRegistry()
+ channel, ok := channelRegistry.GetChannel(way.Type)
if !ok {
sm.LogsAndStatusMark(fmt.Sprintf("发送失败:未知渠道类型 %s 的发信实例: %s\n", way.Type, ins.ID), SendFail)
continue
}
- res, errMsg := handler.Send(msgObj, ins.SendTasksIns, typeC, sm.Title, content, sm.URL)
+ // 构建统一消息内容(支持@功能)
+ unifiedContent := &unified.UnifiedMessageContent{
+ Title: sm.Title,
+ Text: sm.Text,
+ HTML: sm.HTML,
+ Markdown: sm.MarkDown,
+ URL: sm.URL,
+ AtMobiles: sm.AtMobiles,
+ AtUserIds: sm.AtUserIds,
+ AtAll: sm.AtAll,
+ }
+
+ // 使用 SendUnified 方法(自动格式转换和@功能支持)
+ res, errMsg := channel.SendUnified(msgObj, ins.SendTasksIns, unifiedContent)
if res != "" {
sm.LogsAndStatusMark(fmt.Sprintf("返回内容:%s", res), sm.Status)
+ } else {
+ sm.LogsAndStatusMark(sm.TransError(errMsg), errStrIsSuccess(errMsg))
}
- sm.LogsAndStatusMark(sm.TransError(errMsg), errStrIsSuccess(errMsg))
}
@@ -221,18 +241,18 @@ func (sm *SendMessageService) TransError(err string) string {
// 先根据实例设置的类型取,取不到或者取到的是空,则使用text发送
func (sm *SendMessageService) GetSendMsg(ins models.SendTasksIns) (string, string) {
data := map[string]string{}
- data["text"] = sm.Text
- data["html"] = sm.HTML
- data["markdown"] = sm.MarkDown
+ data[unified.FormatTypeText] = sm.Text
+ data[unified.FormatTypeHTML] = sm.HTML
+ data[unified.FormatTypeMarkdown] = sm.MarkDown
content, ok := data[strings.ToLower(ins.ContentType)]
if !ok || len(content) == 0 {
- content, ok := data["text"]
+ content, ok := data[unified.FormatTypeText]
if !ok {
logrus.Error("text节点数据为空!")
- return "text", ""
+ return unified.FormatTypeText, ""
} else {
logrus.Error(fmt.Sprintf("没有找到%s对应的消息,使用text消息替代!", ins.ContentType))
- return "text", content
+ return unified.FormatTypeText, content
}
} else {
return strings.ToLower(ins.ContentType), content
diff --git a/service/send_message_service/send_qywexin.go b/service/send_message_service/send_qywexin.go
deleted file mode 100644
index 029b5ce..0000000
--- a/service/send_message_service/send_qywexin.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package send_message_service
-
-import (
- "fmt"
- "message-nest/models"
- "message-nest/pkg/message"
- "message-nest/service/send_ins_service"
- "message-nest/service/send_way_service"
-)
-
-type QyWeiXinService struct {
-}
-
-// SendDtalkMessage 执行发送钉钉
-func (s *QyWeiXinService) SendQyWeiXinMessage(auth send_way_service.WayDetailQyWeiXin, ins models.SendTasksIns, typeC string, title string, content string) (string, string) {
- insService := send_ins_service.SendTaskInsService{}
- errStr, c := insService.ValidateDiffIns(ins)
- if errStr != "" {
- return errStr, ""
- }
- _, ok := c.(models.InsQyWeiXinConfig)
- if !ok {
- return "企业微信config校验失败", ""
- }
-
- errMsg := ""
- var res []byte
- var err error
- cli := message.QyWeiXin{
- AccessToken: auth.AccessToken,
- }
- if typeC == "text" {
- res, err = cli.SendMessageText(content)
- if err != nil {
- errMsg = fmt.Sprintf("发送失败:%s", ins.ContentType)
- }
- } else if typeC == "markdown" {
- res, err = cli.SendMessageMarkdown(title, content)
- if err != nil {
- errMsg = fmt.Sprintf("发送失败:%s", ins.ContentType)
- }
- } else {
- errMsg = fmt.Sprintf("未知的企业微信发送内容类型:%s", ins.ContentType)
- }
- return string(res), errMsg
-}
diff --git a/service/send_message_service/send_wechat_account.go b/service/send_message_service/send_wechat_account.go
deleted file mode 100644
index 971dada..0000000
--- a/service/send_message_service/send_wechat_account.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package send_message_service
-
-import (
- "fmt"
- "message-nest/models"
- "message-nest/pkg/message"
- "message-nest/service/send_ins_service"
- "message-nest/service/send_way_service"
-)
-
-type WeChatOfAccountService struct {
-}
-
-// SendWeChatOfAccountMessage 执行发送微信公众号模板消息
-func (s *WeChatOfAccountService) SendWeChatOfAccountMessage(auth send_way_service.WeChatOFAccount, ins models.SendTasksIns, typeC string, title string, content string, url string) (string, string) {
- insService := send_ins_service.SendTaskInsService{}
- errStr, c := insService.ValidateDiffIns(ins)
- if errStr != "" {
- return errStr, ""
- }
- config, ok := c.(models.InsWeChatAccountConfig)
- if !ok {
- return "微信公众号模板消息config校验失败", ""
- }
- var (
- err error
- res string
- errMsg string
- )
- cli := message.WeChatOFAccount{
- AppID: auth.AppID,
- AppSecret: auth.APPSecret,
- TemplateID: auth.TempID,
- ToUser: config.ToAccount,
- URL: url,
- }
- res, err = cli.Send(title, content)
- if err != nil {
- errMsg = fmt.Sprintf("发送失败:%s", ins.ContentType)
- }
- return res, errMsg
-}
diff --git a/service/send_message_service/unified/channel_registry.go b/service/send_message_service/unified/channel_registry.go
new file mode 100644
index 0000000..5579c87
--- /dev/null
+++ b/service/send_message_service/unified/channel_registry.go
@@ -0,0 +1,126 @@
+package unified
+
+import (
+ "fmt"
+ "message-nest/service/send_message_service/unified/channels"
+ "sync"
+)
+
+// 重导出channels包的类型,方便外部使用
+type (
+ Channel = channels.Channel
+ UnifiedMessageContent = channels.UnifiedMessageContent
+)
+
+// 重导出常量
+const (
+ FormatTypeText = channels.FormatTypeText
+ FormatTypeHTML = channels.FormatTypeHTML
+ FormatTypeMarkdown = channels.FormatTypeMarkdown
+
+ MessageTypeEmail = channels.MessageTypeEmail
+ MessageTypeDtalk = channels.MessageTypeDtalk
+ MessageTypeQyWeiXin = channels.MessageTypeQyWeiXin
+ MessageTypeCustom = channels.MessageTypeCustom
+ MessageTypeWeChatOFAccount = channels.MessageTypeWeChatOFAccount
+ MessageTypeMessageNest = channels.MessageTypeMessageNest
+)
+
+// ChannelRegistry 渠道注册表
+type ChannelRegistry struct {
+ channels map[string]Channel
+ mu sync.RWMutex
+}
+
+// NewChannelRegistry 创建渠道注册表
+func NewChannelRegistry() *ChannelRegistry {
+ return &ChannelRegistry{
+ channels: make(map[string]Channel),
+ }
+}
+
+// Register 注册渠道
+func (r *ChannelRegistry) Register(channel Channel) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ r.channels[channel.GetType()] = channel
+}
+
+// GetChannel 获取渠道
+func (r *ChannelRegistry) GetChannel(channelType string) (Channel, bool) {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+ channel, ok := r.channels[channelType]
+ return channel, ok
+}
+
+// GetAllChannels 获取所有渠道
+func (r *ChannelRegistry) GetAllChannels() map[string]Channel {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ result := make(map[string]Channel, len(r.channels))
+ for k, v := range r.channels {
+ result[k] = v
+ }
+ return result
+}
+
+// ListChannels 列出所有渠道
+func (r *ChannelRegistry) ListChannels() []string {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ types := make([]string, 0, len(r.channels))
+ for t := range r.channels {
+ types = append(types, t)
+ }
+ return types
+}
+
+var (
+ globalChannelRegistry *ChannelRegistry
+ channelRegistryOnce sync.Once
+)
+
+// GetGlobalChannelRegistry 获取全局渠道注册表(单例)
+func GetGlobalChannelRegistry() *ChannelRegistry {
+ channelRegistryOnce.Do(func() {
+ globalChannelRegistry = NewChannelRegistry()
+
+ // 注册所有渠道
+ globalChannelRegistry.Register(channels.NewEmailChannel())
+ globalChannelRegistry.Register(channels.NewDtalkChannel())
+ globalChannelRegistry.Register(channels.NewQyWeiXinChannel())
+ globalChannelRegistry.Register(channels.NewCustomChannel())
+ globalChannelRegistry.Register(channels.NewWeChatOFAccountChannel())
+ globalChannelRegistry.Register(channels.NewMessageNestChannel())
+ })
+ return globalChannelRegistry
+}
+
+// GetChannelInfo 获取渠道信息(用于调试和文档)
+func GetChannelInfo(channelType string) (string, error) {
+ registry := GetGlobalChannelRegistry()
+ channel, ok := registry.GetChannel(channelType)
+ if !ok {
+ return "", fmt.Errorf("未知的渠道类型: %s", channelType)
+ }
+
+ info := fmt.Sprintf("渠道: %s\n支持格式: %v\n",
+ channel.GetType(),
+ channel.GetSupportedFormats())
+ return info, nil
+}
+
+// ListAllChannels 列出所有渠道信息
+func ListAllChannels() string {
+ registry := GetGlobalChannelRegistry()
+ allChannels := registry.GetAllChannels()
+
+ result := "已注册的渠道:\n"
+ for channelType, channel := range allChannels {
+ result += fmt.Sprintf(" - %s: %v\n", channelType, channel.GetSupportedFormats())
+ }
+ return result
+}
diff --git a/service/send_message_service/unified/channels/base.go b/service/send_message_service/unified/channels/base.go
new file mode 100644
index 0000000..aa9f3af
--- /dev/null
+++ b/service/send_message_service/unified/channels/base.go
@@ -0,0 +1,45 @@
+package channels
+
+import "message-nest/models"
+
+type Channel interface {
+ GetType() string
+ GetSupportedFormats() []string
+ FormatContent(content *UnifiedMessageContent) (formatType string, formattedContent string, err error)
+ SendUnified(msgObj interface{}, ins models.SendTasksIns, content *UnifiedMessageContent) (string, string)
+}
+
+type BaseChannel struct {
+ channelType string
+ supportedFormats []string
+}
+
+func NewBaseChannel(channelType string, supportedFormats []string) *BaseChannel {
+ return &BaseChannel{channelType: channelType, supportedFormats: supportedFormats}
+}
+
+func (c *BaseChannel) GetType() string { return c.channelType }
+func (c *BaseChannel) GetSupportedFormats() []string { return c.supportedFormats }
+
+func (c *BaseChannel) FormatContent(content *UnifiedMessageContent) (string, string, error) {
+ for _, formatType := range c.supportedFormats {
+ switch formatType {
+ case FormatTypeMarkdown:
+ if content.HasMarkdown() {
+ return FormatTypeMarkdown, content.Markdown, nil
+ }
+ case FormatTypeHTML:
+ if content.HasHTML() {
+ return FormatTypeHTML, content.HTML, nil
+ }
+ case FormatTypeText:
+ if content.HasText() {
+ return FormatTypeText, content.Text, nil
+ }
+ }
+ }
+ if content.HasText() {
+ return FormatTypeText, content.Text, nil
+ }
+ return FormatTypeText, "", nil
+}
diff --git a/service/send_message_service/unified/channels/custom.go b/service/send_message_service/unified/channels/custom.go
new file mode 100644
index 0000000..4bcc21d
--- /dev/null
+++ b/service/send_message_service/unified/channels/custom.go
@@ -0,0 +1,83 @@
+package channels
+
+import (
+ "encoding/json"
+ "fmt"
+ "message-nest/models"
+ "message-nest/pkg/message"
+ "message-nest/service/send_way_service"
+ "regexp"
+ "strings"
+)
+
+type CustomChannel struct{ *BaseChannel }
+
+func NewCustomChannel() *CustomChannel {
+ return &CustomChannel{BaseChannel: NewBaseChannel(MessageTypeCustom, []string{FormatTypeText})}
+}
+
+func (c *CustomChannel) FormatContent(content *UnifiedMessageContent) (string, string, error) {
+ if content.HasText() {
+ return FormatTypeText, content.Text, nil
+ }
+ if content.HasMarkdown() {
+ return FormatTypeText, markdownToText(content.Markdown), nil
+ }
+ if content.HasHTML() {
+ return FormatTypeText, htmlToText(content.HTML), nil
+ }
+ return FormatTypeText, "", nil
+}
+
+func (c *CustomChannel) SendUnified(msgObj interface{}, ins models.SendTasksIns, content *UnifiedMessageContent) (string, string) {
+ auth, ok := msgObj.(send_way_service.WayDetailCustom)
+ if !ok {
+ return "", "类型转换失败"
+ }
+ _, formattedContent, err := c.FormatContent(content)
+ if err != nil {
+ return "", err.Error()
+ }
+ cli := message.CustomWebhook{}
+ data, _ := json.Marshal(formattedContent)
+ dataStr := strings.Trim(string(data), "\"")
+ bodyStr := strings.Replace(auth.Body, "TEXT", dataStr, -1)
+ res, err := cli.Request(auth.Webhook, bodyStr)
+ var errMsg string
+ if err != nil {
+ errMsg = fmt.Sprintf("发送失败:%s", err.Error())
+ }
+ return string(res), errMsg
+}
+
+func markdownToText(md string) string {
+ t := md
+ t = regexp.MustCompile(`#{1,6}\s+`).ReplaceAllString(t, "")
+ t = regexp.MustCompile(`\*\*([^*]+)\*\*`).ReplaceAllString(t, "$1")
+ t = regexp.MustCompile(`\*([^*]+)\*`).ReplaceAllString(t, "$1")
+ t = regexp.MustCompile(`__([^_]+)__`).ReplaceAllString(t, "$1")
+ t = regexp.MustCompile(`_([^_]+)_`).ReplaceAllString(t, "$1")
+ t = regexp.MustCompile(`\[([^\]]+)\]\([^)]+\)`).ReplaceAllString(t, "$1")
+ t = regexp.MustCompile("```[^`]*```").ReplaceAllString(t, "")
+ t = regexp.MustCompile("`([^`]+)`").ReplaceAllString(t, "$1")
+ t = regexp.MustCompile(`(?m)^>\s+`).ReplaceAllString(t, "")
+ t = regexp.MustCompile(`(?m)^[\*\-\+]\s+`).ReplaceAllString(t, "")
+ t = regexp.MustCompile(`(?m)^\d+\.\s+`).ReplaceAllString(t, "")
+ return strings.TrimSpace(t)
+}
+
+func htmlToText(html string) string {
+ t := html
+ t = regexp.MustCompile(`(?i)`).ReplaceAllString(t, "")
+ t = regexp.MustCompile(`(?i)`).ReplaceAllString(t, "")
+ t = regexp.MustCompile(`(?i)
`).ReplaceAllString(t, "\n")
+ t = regexp.MustCompile(`(?i)
`).ReplaceAllString(t, "\n")
+ t = regexp.MustCompile(`<[^>]+>`).ReplaceAllString(t, "")
+ t = strings.ReplaceAll(t, " ", " ")
+ t = strings.ReplaceAll(t, "<", "<")
+ t = strings.ReplaceAll(t, ">", ">")
+ t = strings.ReplaceAll(t, "&", "&")
+ t = strings.ReplaceAll(t, """, "\"")
+ t = regexp.MustCompile(`\n{3,}`).ReplaceAllString(t, "\n\n")
+ return strings.TrimSpace(t)
+}
diff --git a/service/send_message_service/unified/channels/dtalk.go b/service/send_message_service/unified/channels/dtalk.go
new file mode 100644
index 0000000..7eedd2a
--- /dev/null
+++ b/service/send_message_service/unified/channels/dtalk.go
@@ -0,0 +1,56 @@
+package channels
+
+import (
+ "fmt"
+ "message-nest/models"
+ "message-nest/pkg/message"
+ "message-nest/service/send_ins_service"
+ "message-nest/service/send_way_service"
+)
+
+type DtalkChannel struct{ *BaseChannel }
+
+func NewDtalkChannel() *DtalkChannel {
+ return &DtalkChannel{BaseChannel: NewBaseChannel(MessageTypeDtalk, []string{FormatTypeMarkdown, FormatTypeText})}
+}
+
+func (c *DtalkChannel) SendUnified(msgObj interface{}, ins models.SendTasksIns, content *UnifiedMessageContent) (string, string) {
+ auth, ok := msgObj.(send_way_service.WayDetailDTalk)
+ if !ok {
+ return "", "类型转换失败"
+ }
+ insService := send_ins_service.SendTaskInsService{}
+ errStr, configInterface := insService.ValidateDiffIns(ins)
+ if errStr != "" {
+ return errStr, ""
+ }
+ _, ok = configInterface.(models.InsDtalkConfig)
+ if !ok {
+ return "钉钉config校验失败", ""
+ }
+ contentType, formattedContent, err := c.FormatContent(content)
+ if err != nil {
+ return "", err.Error()
+ }
+ atMobiles := content.GetAtMobiles()
+ if content.IsAtAll() {
+ atMobiles = append(atMobiles, "all")
+ }
+ cli := message.Dtalk{AccessToken: auth.AccessToken, Secret: auth.Secret}
+ var res []byte
+ var errMsg string
+ if contentType == FormatTypeText {
+ res, err = cli.SendMessageText(formattedContent, atMobiles...)
+ if err != nil {
+ errMsg = fmt.Sprintf("发送失败:%s", err.Error())
+ }
+ } else if contentType == FormatTypeMarkdown {
+ res, err = cli.SendMessageMarkdown(content.Title, formattedContent, atMobiles...)
+ if err != nil {
+ errMsg = fmt.Sprintf("发送失败:%s", err.Error())
+ }
+ } else {
+ errMsg = fmt.Sprintf("未知的钉钉发送内容类型:%s", contentType)
+ }
+ return string(res), errMsg
+}
diff --git a/service/send_message_service/unified/channels/email.go b/service/send_message_service/unified/channels/email.go
new file mode 100644
index 0000000..4926d8c
--- /dev/null
+++ b/service/send_message_service/unified/channels/email.go
@@ -0,0 +1,46 @@
+package channels
+
+import (
+ "fmt"
+ "message-nest/models"
+ "message-nest/pkg/message"
+ "message-nest/service/send_ins_service"
+ "message-nest/service/send_way_service"
+)
+
+type EmailChannel struct{ *BaseChannel }
+
+func NewEmailChannel() *EmailChannel {
+ return &EmailChannel{BaseChannel: NewBaseChannel(MessageTypeEmail, []string{FormatTypeHTML, FormatTypeText})}
+}
+
+func (c *EmailChannel) SendUnified(msgObj interface{}, ins models.SendTasksIns, content *UnifiedMessageContent) (string, string) {
+ auth, ok := msgObj.(send_way_service.WayDetailEmail)
+ if !ok {
+ return "", "类型转换失败"
+ }
+ insService := send_ins_service.SendTaskInsService{}
+ errStr, configInterface := insService.ValidateDiffIns(ins)
+ if errStr != "" {
+ return "", errStr
+ }
+ config, ok := configInterface.(models.InsEmailConfig)
+ if !ok {
+ return "", "邮箱config校验失败"
+ }
+ contentType, formattedContent, err := c.FormatContent(content)
+ if err != nil {
+ return "", err.Error()
+ }
+ var emailer message.EmailMessage
+ emailer.Init(auth.Server, auth.Port, auth.Account, auth.Passwd)
+ var errMsg string
+ if contentType == FormatTypeText {
+ errMsg = emailer.SendTextMessage(config.ToAccount, content.Title, formattedContent)
+ } else if contentType == FormatTypeHTML {
+ errMsg = emailer.SendHtmlMessage(config.ToAccount, content.Title, formattedContent)
+ } else {
+ errMsg = fmt.Sprintf("未知的邮件发送内容类型:%s", contentType)
+ }
+ return "", errMsg
+}
diff --git a/service/send_message_service/unified/channels/nest.go b/service/send_message_service/unified/channels/nest.go
new file mode 100644
index 0000000..17fb182
--- /dev/null
+++ b/service/send_message_service/unified/channels/nest.go
@@ -0,0 +1,38 @@
+package channels
+
+import (
+ "message-nest/models"
+ "message-nest/service/hosted_message_service"
+ "message-nest/service/send_way_service"
+)
+
+type MessageNestChannel struct{ *BaseChannel }
+
+func NewMessageNestChannel() *MessageNestChannel {
+ return &MessageNestChannel{BaseChannel: NewBaseChannel(MessageTypeMessageNest, []string{FormatTypeMarkdown, FormatTypeHTML, FormatTypeText})}
+}
+
+func (c *MessageNestChannel) SendUnified(msgObj interface{}, ins models.SendTasksIns, content *UnifiedMessageContent) (string, string) {
+ _, ok := msgObj.(send_way_service.MessageNest)
+ if !ok {
+ return "", "类型转换失败"
+ }
+ contentType, formattedContent, err := c.FormatContent(content)
+ if err != nil {
+ return "", err.Error()
+ }
+ messageService := hosted_message_service.HostMessageService{
+ Title: content.Title,
+ Content: formattedContent,
+ Type: contentType,
+ }
+ err = messageService.Add()
+ var res, errMsg string
+ if err != nil {
+ errMsg = err.Error()
+ res = "托管消息创建失败!"
+ } else {
+ res = "托管消息创建成功!"
+ }
+ return res, errMsg
+}
diff --git a/service/send_message_service/unified/channels/qyweixin.go b/service/send_message_service/unified/channels/qyweixin.go
new file mode 100644
index 0000000..50377ef
--- /dev/null
+++ b/service/send_message_service/unified/channels/qyweixin.go
@@ -0,0 +1,58 @@
+package channels
+
+import (
+ "fmt"
+ "message-nest/models"
+ "message-nest/pkg/message"
+ "message-nest/service/send_ins_service"
+ "message-nest/service/send_way_service"
+)
+
+type QyWeiXinChannel struct{ *BaseChannel }
+
+func NewQyWeiXinChannel() *QyWeiXinChannel {
+ return &QyWeiXinChannel{BaseChannel: NewBaseChannel(MessageTypeQyWeiXin, []string{FormatTypeMarkdown, FormatTypeText})}
+}
+
+func (c *QyWeiXinChannel) SendUnified(msgObj interface{}, ins models.SendTasksIns, content *UnifiedMessageContent) (string, string) {
+ auth, ok := msgObj.(send_way_service.WayDetailQyWeiXin)
+ if !ok {
+ return "", "类型转换失败"
+ }
+ insService := send_ins_service.SendTaskInsService{}
+ errStr, configInterface := insService.ValidateDiffIns(ins)
+ if errStr != "" {
+ return errStr, ""
+ }
+ _, ok = configInterface.(models.InsQyWeiXinConfig)
+ if !ok {
+ return "企业微信config校验失败", ""
+ }
+ contentType, formattedContent, err := c.FormatContent(content)
+ if err != nil {
+ return "", err.Error()
+ }
+ atList := []string{}
+ atList = append(atList, content.GetAtUserIds()...)
+ atList = append(atList, content.GetAtMobiles()...)
+ if content.IsAtAll() {
+ atList = append(atList, "@all")
+ }
+ cli := message.QyWeiXin{AccessToken: auth.AccessToken}
+ var res []byte
+ var errMsg string
+ if contentType == FormatTypeText {
+ res, err = cli.SendMessageText(formattedContent, atList...)
+ if err != nil {
+ errMsg = fmt.Sprintf("发送失败:%s", err.Error())
+ }
+ } else if contentType == FormatTypeMarkdown {
+ res, err = cli.SendMessageMarkdown(content.Title, formattedContent, atList...)
+ if err != nil {
+ errMsg = fmt.Sprintf("发送失败:%s", err.Error())
+ }
+ } else {
+ errMsg = fmt.Sprintf("未知的企业微信发送内容类型:%s", contentType)
+ }
+ return string(res), errMsg
+}
diff --git a/service/send_message_service/unified/channels/types.go b/service/send_message_service/unified/channels/types.go
new file mode 100644
index 0000000..909b891
--- /dev/null
+++ b/service/send_message_service/unified/channels/types.go
@@ -0,0 +1,53 @@
+package channels
+
+// 消息格式类型常量
+const (
+ FormatTypeText = "text"
+ FormatTypeHTML = "html"
+ FormatTypeMarkdown = "markdown"
+)
+
+// 消息类型常量
+const (
+ MessageTypeEmail = "Email"
+ MessageTypeDtalk = "Dtalk"
+ MessageTypeQyWeiXin = "QyWeiXin"
+ MessageTypeCustom = "Custom"
+ MessageTypeWeChatOFAccount = "WeChatOFAccount"
+ MessageTypeMessageNest = "MessageNest"
+)
+
+// UnifiedMessageContent 统一的消息内容结构
+type UnifiedMessageContent struct {
+ Title string
+ URL string
+ Text string
+ HTML string
+ Markdown string
+ AtMobiles []string
+ AtUserIds []string
+ AtAll bool
+ Summary string
+ ImageURL string
+ Extra map[string]interface{}
+}
+
+func (m *UnifiedMessageContent) HasText() bool { return m.Text != "" }
+func (m *UnifiedMessageContent) HasHTML() bool { return m.HTML != "" }
+func (m *UnifiedMessageContent) HasMarkdown() bool { return m.Markdown != "" }
+
+func (m *UnifiedMessageContent) GetAtMobiles() []string {
+ if m.AtMobiles == nil {
+ return []string{}
+ }
+ return m.AtMobiles
+}
+
+func (m *UnifiedMessageContent) GetAtUserIds() []string {
+ if m.AtUserIds == nil {
+ return []string{}
+ }
+ return m.AtUserIds
+}
+
+func (m *UnifiedMessageContent) IsAtAll() bool { return m.AtAll }
diff --git a/service/send_message_service/unified/channels/wechat.go b/service/send_message_service/unified/channels/wechat.go
new file mode 100644
index 0000000..6743ee9
--- /dev/null
+++ b/service/send_message_service/unified/channels/wechat.go
@@ -0,0 +1,48 @@
+package channels
+
+import (
+ "fmt"
+ "message-nest/models"
+ "message-nest/pkg/message"
+ "message-nest/service/send_ins_service"
+ "message-nest/service/send_way_service"
+)
+
+type WeChatOFAccountChannel struct{ *BaseChannel }
+
+func NewWeChatOFAccountChannel() *WeChatOFAccountChannel {
+ return &WeChatOFAccountChannel{BaseChannel: NewBaseChannel(MessageTypeWeChatOFAccount, []string{FormatTypeText})}
+}
+
+func (c *WeChatOFAccountChannel) SendUnified(msgObj interface{}, ins models.SendTasksIns, content *UnifiedMessageContent) (string, string) {
+ auth, ok := msgObj.(send_way_service.WeChatOFAccount)
+ if !ok {
+ return "", "类型转换失败"
+ }
+ insService := send_ins_service.SendTaskInsService{}
+ errStr, configInterface := insService.ValidateDiffIns(ins)
+ if errStr != "" {
+ return errStr, ""
+ }
+ config, ok := configInterface.(models.InsWeChatAccountConfig)
+ if !ok {
+ return "微信公众号模板消息config校验失败", ""
+ }
+ _, formattedContent, err := c.FormatContent(content)
+ if err != nil {
+ return "", err.Error()
+ }
+ cli := message.WeChatOFAccount{
+ AppID: auth.AppID,
+ AppSecret: auth.APPSecret,
+ TemplateID: auth.TempID,
+ ToUser: config.ToAccount,
+ URL: content.URL,
+ }
+ res, err := cli.Send(content.Title, formattedContent)
+ var errMsg string
+ if err != nil {
+ errMsg = fmt.Sprintf("发送失败:%s", err.Error())
+ }
+ return res, errMsg
+}
diff --git a/web/src/components/pages/sendTasks/ApiCodeViewer.vue b/web/src/components/pages/sendTasks/ApiCodeViewer.vue
index 183857d..59641a2 100644
--- a/web/src/components/pages/sendTasks/ApiCodeViewer.vue
+++ b/web/src/components/pages/sendTasks/ApiCodeViewer.vue
@@ -42,6 +42,14 @@ export default defineComponent({
// 当前选中的标签
const activeTab = ref('curl')
+ // 可选参数选项
+ const showHtml = ref(false)
+ const showMarkdown = ref(false)
+ const showUrl = ref(false)
+ const showAtMobiles = ref(false)
+ const showAtUserIds = ref(false)
+ const showAtAll = ref(false)
+
// 代码语言选项
const codeLanguages = [
{ value: 'curl', label: 'cURL', icon: '🌐' },
@@ -56,7 +64,14 @@ export default defineComponent({
// 生成API代码示例
const generateApiCode = (language: string) => {
const taskId = props.taskData?.id || 'TASK_ID'
- const options = { html: false, markdown: false, url: false }
+ const options = {
+ html: showHtml.value,
+ markdown: showMarkdown.value,
+ url: showUrl.value,
+ at_mobiles: showAtMobiles.value,
+ at_user_ids: showAtUserIds.value,
+ at_all: showAtAll.value
+ }
switch (language) {
case 'curl':
@@ -91,6 +106,12 @@ export default defineComponent({
return {
handleUpdateOpen,
activeTab,
+ showHtml,
+ showMarkdown,
+ showUrl,
+ showAtMobiles,
+ showAtUserIds,
+ showAtAll,
codeLanguages,
generateApiCode,
copyToClipboard
@@ -122,6 +143,41 @@ export default defineComponent({
-->
+
+
+
代码示例
diff --git a/web/src/util/viewApi.js b/web/src/util/viewApi.js
index 188619f..751be02 100644
--- a/web/src/util/viewApi.js
+++ b/web/src/util/viewApi.js
@@ -40,6 +40,16 @@ class ApiStrGenerate {
if (options.url) {
data.url = 'https://github.com';
}
+ // @提及功能参数(可选)
+ if (options.at_mobiles) {
+ data.at_mobiles = ['13800138000', '13900139000'];
+ }
+ if (options.at_user_ids) {
+ data.at_user_ids = ['zhangsan', 'lisi'];
+ }
+ if (options.at_all) {
+ data.at_all = true;
+ }
let dataStr = JSON.stringify(data, null, 4);
return dataStr
}