package sms import ( "context" "encoding/json" "strings" ) // ConfigKey 主站节点短信配置项,与分销站点的 business_templates 同构。 const ConfigKey = "sms.business_templates" // DefaultStaleDays 上架未成交提醒的默认天数阈值。 const DefaultStaleDays = 3 // ConfigReader 读取系统配置文本,由 systemconfig.Repository 实现。 type ConfigReader interface { FindValue(ctx context.Context, key string) (string, error) } // LoadConfig 从系统配置读取并解析节点短信配置。 func LoadConfig(ctx context.Context, reader ConfigReader) (Config, error) { if reader == nil { return DefaultConfig(), nil } value, err := reader.FindValue(ctx, ConfigKey) if err != nil { return Config{}, err } return ParseConfigValue(value) } // NodeConfig 是单个节点的开关与模板 CODE。 type NodeConfig struct { Enabled bool `json:"enabled"` TemplateCode string `json:"template_code"` // StaleDays 仅上架未成交提醒使用:上架满多少天仍未成交才提醒,0 表示取默认值。 StaleDays int `json:"stale_days,omitempty"` } // Config 是主站节点短信配置(系统配置 sms.business_templates 的 JSON 结构)。 type Config struct { Enabled bool `json:"enabled"` Nodes map[string]NodeConfig `json:"nodes"` } // DefaultConfig 返回默认配置:总开关关闭(避免误发产生短信费用), // 三个节点默认各自开启,模板 CODE 留空由运营在后台填写。 func DefaultConfig() Config { nodes := make(map[string]NodeConfig, len(nodeMetas)) for _, meta := range nodeMetas { node := NodeConfig{Enabled: true} if meta.SupportsStaleDays { node.StaleDays = DefaultStaleDays } nodes[meta.Code] = node } return Config{Enabled: false, Nodes: nodes} } // DefaultConfigValue 返回默认配置的 JSON 文本。 func DefaultConfigValue() string { raw, err := MarshalConfig(DefaultConfig()) if err != nil { return `{"enabled":false,"nodes":{}}` } return raw } // ParseConfigValue 解析系统配置里的 JSON 文本,缺失字段回落到默认值。 func ParseConfigValue(value string) (Config, error) { cfg := DefaultConfig() value = strings.TrimSpace(value) if value == "" { return cfg, nil } var raw struct { Enabled *bool `json:"enabled"` Nodes map[string]NodeConfig `json:"nodes"` } if err := json.Unmarshal([]byte(value), &raw); err != nil { return Config{}, err } if raw.Enabled != nil { cfg.Enabled = *raw.Enabled } if raw.Nodes != nil { // 以已登记节点为基准合并,避免后台配置里出现未知节点。 for code, node := range raw.Nodes { if !IsKnownNode(code) { continue } node.TemplateCode = strings.TrimSpace(node.TemplateCode) if node.StaleDays < 0 { node.StaleDays = 0 } cfg.Nodes[code] = node } } return cfg, nil } // MarshalConfig 规范化并序列化配置。 func MarshalConfig(cfg Config) (string, error) { normalized := DefaultConfig() normalized.Enabled = cfg.Enabled for code, node := range cfg.Nodes { if !IsKnownNode(code) { continue } node.TemplateCode = strings.TrimSpace(node.TemplateCode) if node.StaleDays < 0 { node.StaleDays = 0 } normalized.Nodes[code] = node } raw, err := json.Marshal(normalized) if err != nil { return "", err } return string(raw), nil } // Node 返回指定节点的配置。 func (c Config) Node(code string) (NodeConfig, bool) { node, ok := c.Nodes[code] return node, ok } // NodeActive 判断节点是否可发送:总开关、节点开关与模板 CODE 都必须就绪。 func (c Config) NodeActive(code string) bool { if !c.Enabled { return false } node, ok := c.Node(code) if !ok || !node.Enabled { return false } return strings.TrimSpace(node.TemplateCode) != "" } // StaleDaysFor 返回上架未成交提醒的天数阈值,未配置时取默认值。 func (c Config) StaleDaysFor(code string) int { node, ok := c.Node(code) if !ok || node.StaleDays <= 0 { return DefaultStaleDays } return node.StaleDays }