Files
aiaa-notification-server/internal/subscriber/tradesignal/convert.go
T
ryan ae25409e56 feat(通知模板): 支持缺字段按空值渲染与空行省略
Motivation:
统一 crypto-strategy 与 trade-signal 两类交易信号的模板字段,使同一套通知模板可复用;字段缺失或为空时不再导致渲染报错或输出空行,通知内容更整洁。

Changes:

* 渲染器缺失字段改为按空值处理,新增 line 函数实现空值整行省略
* crypto-strategy 信号补充止盈价、止损价、平均价、收益额等模板字段
* trade-signal 信号保留原始报文中的额外字段以适配统一模板
* 修复结构化日志将 error 作为值直接输出导致的格式问题
2026-08-16 00:19:50 +08:00

82 lines
1.8 KiB
Go

package tradesignal
import (
"encoding/json"
"errors"
"fmt"
"strings"
"aiaa-notification-service/internal/config"
)
var ErrInvalidSignal = errors.New("invalid signal")
type Converter struct {
overrides map[string]config.StrategyOverride
positions *Tracker
}
func NewConverter(overrides map[string]config.StrategyOverride) *Converter {
return &Converter{
overrides: overrides,
positions: NewTracker(),
}
}
func (c *Converter) Convert(body []byte) (string, map[string]interface{}, error) {
var sig Signal
if err := json.Unmarshal(body, &sig); err != nil {
return "", nil, fmt.Errorf("%w: %v", ErrInvalidSignal, err)
}
if strings.TrimSpace(sig.Action) == "" {
return "", nil, fmt.Errorf("%w: missing action", ErrInvalidSignal)
}
out := Apply(&sig, c.overrideFor(sig.StrategyCode))
snap := c.positions.Apply(out)
var opts FormatOptions
if snap.HasAvg {
avg := snap.AvgPrice
opts.AvgPrice = &avg
}
text := Format(out, opts)
data, err := toData(body, out)
if err != nil {
return "", nil, err
}
data["formatted"] = text
if snap.HasAvg {
data["avgPrice"] = snap.AvgPrice
}
return "trade." + strings.ToLower(out.Action), data, nil
}
func (c *Converter) overrideFor(code string) *config.StrategyOverride {
if c == nil || len(c.overrides) == 0 || code == "" {
return nil
}
override, ok := c.overrides[code]
if !ok {
return nil
}
return &override
}
func toData(body []byte, sig *Signal) (map[string]interface{}, error) {
data := make(map[string]interface{})
if err := json.Unmarshal(body, &data); err != nil {
return nil, err
}
raw, err := json.Marshal(sig)
if err != nil {
return nil, err
}
overlay := make(map[string]interface{})
if err := json.Unmarshal(raw, &overlay); err != nil {
return nil, err
}
for k, v := range overlay {
data[k] = v
}
return data, nil
}