package engine import ( "context" "encoding/json" "fmt" "aiaa-notification-service/internal/cache" "aiaa-notification-service/internal/model" "aiaa-notification-service/internal/store" ) type Matcher struct { store *store.Store cache *cache.Cache } func NewMatcher(s *store.Store, c *cache.Cache) *Matcher { return &Matcher{store: s, cache: c} } func (m *Matcher) Match(ctx context.Context, sourceID int, event string) (*model.Rule, error) { // Try cache first if m.cache != nil { cr, err := m.cache.GetRule(ctx, sourceID, event) if err == nil { rule := &model.Rule{ID: cr.RuleID, TemplateID: cr.TemplateID, SourceID: sourceID, Event: event} if cr.Conditions != "" && cr.Conditions != "null" { raw := json.RawMessage(cr.Conditions) rule.Conditions = &raw } return rule, nil } } // Fall back to DB rule, err := m.store.GetRuleBySourceEvent(ctx, sourceID, event) if err != nil { return nil, fmt.Errorf("match rule: %w", err) } // Warm cache if m.cache != nil { tmpl, err := m.store.GetTemplate(ctx, rule.TemplateID) if err != nil { return rule, nil // rule found but template fetch failed — still return rule } cr := &cache.CachedRule{ RuleID: rule.ID, TemplateID: rule.TemplateID, Content: tmpl.Content, } if rule.Conditions != nil { cr.Conditions = string(*rule.Conditions) } _ = m.cache.SetRule(ctx, sourceID, event, cr) } return rule, nil }