feat(通知模板): 支持 case 取值映射与事件名匹配渲染
Motivation: 让通知模板能够将 action、event 等原始枚举值映射为可读中文文案,并支持按事件名后缀匹配;同时简化 SafeW 消息发送格式,避免 Markdown 转义引入的显示问题。 Changes: * 新增 `case` 模板函数,按值匹配 key/文案并支持末尾奇数参数作为默认值 * `case` 匹配兼容事件名后缀(如 trade.close 匹配 .close) * `line` 前缀自动去除末尾冒号,避免重复冒号 * 模板渲染前自动注入 event 字段,便于模板按事件名取值 * SafeW 消息改为纯文本发送,移除 MarkdownV2 转义
This commit is contained in:
@@ -16,7 +16,10 @@ func NewRenderer() *Renderer {
|
||||
func (r *Renderer) Render(tmplContent string, data map[string]interface{}) (string, error) {
|
||||
tmpl, err := template.New("notify").
|
||||
Option("missingkey=zero").
|
||||
Funcs(template.FuncMap{"line": templateLine}).
|
||||
Funcs(template.FuncMap{
|
||||
"line": templateLine,
|
||||
"case": templateCase,
|
||||
}).
|
||||
Parse(tmplContent)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parse template: %w", err)
|
||||
@@ -33,7 +36,41 @@ func templateLine(label string, v any) string {
|
||||
if isEmptyValue(v) {
|
||||
return ""
|
||||
}
|
||||
return label + ":" + fmt.Sprint(v) + "\n"
|
||||
return strings.TrimRight(label, "::") + ":" + fmt.Sprint(v) + "\n"
|
||||
}
|
||||
|
||||
func templateCase(value any, pairs ...any) string {
|
||||
got := stringify(value)
|
||||
n := len(pairs)
|
||||
def := got
|
||||
if n%2 == 1 {
|
||||
def = stringify(pairs[n-1])
|
||||
pairs = pairs[:n-1]
|
||||
}
|
||||
for i := 0; i+1 < len(pairs); i += 2 {
|
||||
if caseKeyMatch(got, stringify(pairs[i])) {
|
||||
return stringify(pairs[i+1])
|
||||
}
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func caseKeyMatch(value, key string) bool {
|
||||
v := strings.ToLower(strings.TrimSpace(value))
|
||||
k := strings.ToLower(strings.TrimSpace(key))
|
||||
k = strings.TrimPrefix(k, ".")
|
||||
vSeg := v
|
||||
if i := strings.LastIndex(v, "."); i >= 0 {
|
||||
vSeg = v[i+1:]
|
||||
}
|
||||
return k == v || k == strings.TrimPrefix(v, ".") || k == vSeg
|
||||
}
|
||||
|
||||
func stringify(v any) string {
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprint(v)
|
||||
}
|
||||
|
||||
func isEmptyValue(v any) bool {
|
||||
|
||||
@@ -46,6 +46,16 @@ func TestRendererLineOmitsEmpty(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRendererLineLabelAlreadyHasColon(t *testing.T) {
|
||||
r := NewRenderer()
|
||||
out, err := r.Render(`{{line "平均价:" .totalAvgPx}}`, map[string]interface{}{"totalAvgPx": 1029})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Count(out, ":") != 1 || !strings.Contains(out, "平均价:1029") {
|
||||
t.Fatalf("out=%q", out)
|
||||
}
|
||||
}
|
||||
func TestRendererLineMissingKey(t *testing.T) {
|
||||
r := NewRenderer()
|
||||
out, err := r.Render(`{{line "平均价" .totalAvgPx}}{{line "币种" .symbol}}`, map[string]interface{}{"symbol": "ICP"})
|
||||
@@ -56,3 +66,45 @@ func TestRendererLineMissingKey(t *testing.T) {
|
||||
t.Fatalf("out=%q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRendererCaseSwitch(t *testing.T) {
|
||||
r := NewRenderer()
|
||||
tmpl := `{{case .action "OPEN" "开仓" "CLOSE" "平仓" "GAIN" "止盈"}}`
|
||||
out, err := r.Render(tmpl, map[string]interface{}{"action": "CLOSE"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if out != "平仓" {
|
||||
t.Fatalf("action CLOSE: %q", out)
|
||||
}
|
||||
out, err = r.Render(tmpl, map[string]interface{}{"action": "open"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if out != "开仓" {
|
||||
t.Fatalf("action open: %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRendererCaseEventSuffix(t *testing.T) {
|
||||
r := NewRenderer()
|
||||
tmpl := `{{case .event ".open" "开仓" ".close" "平仓"}}`
|
||||
out, err := r.Render(tmpl, map[string]interface{}{"event": "trade.close"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if out != "平仓" {
|
||||
t.Fatalf("event trade.close: %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRendererCaseDefault(t *testing.T) {
|
||||
r := NewRenderer()
|
||||
out, err := r.Render(`{{case .action "OPEN" "开仓" "未知"}}`, map[string]interface{}{"action": "HOLD"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if out != "未知" {
|
||||
t.Fatalf("got %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user