feat(通知模板): 支持 case 取值映射与事件名匹配渲染

Motivation:
让通知模板能够将 action、event 等原始枚举值映射为可读中文文案,并支持按事件名后缀匹配;同时简化 SafeW 消息发送格式,避免 Markdown 转义引入的显示问题。

Changes:

* 新增 `case` 模板函数,按值匹配 key/文案并支持末尾奇数参数作为默认值
* `case` 匹配兼容事件名后缀(如 trade.close 匹配 .close)
* `line` 前缀自动去除末尾冒号,避免重复冒号
* 模板渲染前自动注入 event 字段,便于模板按事件名取值
* SafeW 消息改为纯文本发送,移除 MarkdownV2 转义
This commit is contained in:
2026-08-16 00:45:27 +08:00
parent ae25409e56
commit a9b6234208
7 changed files with 129 additions and 50 deletions
+3 -17
View File
@@ -26,7 +26,7 @@ type safewConfig struct {
type safewMessage struct {
ChatID string `json:"chat_id"`
Text string `json:"text"`
ParseMode string `json:"parse_mode"`
ParseMode string `json:"parse_mode,omitempty"`
}
type safewAPIResponse struct {
@@ -52,9 +52,8 @@ func (s *SafeWSender) Send(title, content string, config json.RawMessage) error
}
payload := safewMessage{
ChatID: chatID,
Text: "*" + escapeMarkdownV2(title) + "*\n" + escapeMarkdownV2(content),
ParseMode: "MarkdownV2",
ChatID: chatID,
Text: title + "\n" + content,
}
body, err := json.Marshal(payload)
if err != nil {
@@ -235,19 +234,6 @@ func safewErrorDescription(body []byte) string {
}
}
func escapeMarkdownV2(s string) string {
var b strings.Builder
b.Grow(len(s))
for _, r := range s {
switch r {
case '_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!', '\\':
b.WriteByte('\\')
}
b.WriteRune(r)
}
return b.String()
}
type SafewChat struct {
ID string `json:"id"`
Type string `json:"type"`
+3 -29
View File
@@ -11,32 +11,6 @@ import (
"aiaa-notification-service/internal/config"
)
func TestEscapeMarkdownV2(t *testing.T) {
tests := []struct {
name string
in string
want string
}{
{name: "underscore and dot", in: "hello_world.", want: `hello\_world\.`},
{name: "empty", in: "", want: ""},
{name: "no specials", in: "hello", want: "hello"},
{name: "backslash first", in: `a\b`, want: `a\\b`},
{
name: "all specials",
in: "_*[]()~`>#+-=|{}.!\\",
want: "\\_\\*\\[\\]\\(\\)\\~\\`\\>\\#\\+\\-\\=\\|\\{\\}\\.\\!\\\\",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := escapeMarkdownV2(tt.in)
if got != tt.want {
t.Fatalf("escapeMarkdownV2(%q) = %q, want %q", tt.in, got, tt.want)
}
})
}
}
func TestNewSenderSafew(t *testing.T) {
s, err := NewSender("safew", &config.SMTPConfig{}, nil)
if err != nil {
@@ -72,10 +46,10 @@ func TestSafeWSenderSendSuccess(t *testing.T) {
if gotBody["chat_id"] != "123456789" {
t.Fatalf("chat_id = %#v, want \"123456789\"", gotBody["chat_id"])
}
if gotBody["parse_mode"] != "MarkdownV2" {
t.Fatalf("parse_mode = %#v, want MarkdownV2", gotBody["parse_mode"])
if gotBody["parse_mode"] != nil && gotBody["parse_mode"] != "" {
t.Fatalf("parse_mode = %#v, want omitted/plain", gotBody["parse_mode"])
}
wantText := "*" + escapeMarkdownV2("hello_world.") + "*\n" + escapeMarkdownV2("price=1.5")
wantText := "hello_world.\nprice=1.5"
if gotBody["text"] != wantText {
t.Fatalf("text = %#v, want %#v", gotBody["text"], wantText)
}