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 } } rule, err := m.store.GetRuleBySourceEvent(ctx, sourceID, event) if err != nil { rules, listErr := m.store.ListEnabledRulesBySource(ctx, sourceID) if listErr != nil { return nil, fmt.Errorf("match rule: %w", listErr) } picked := PickEventRule(event, rules) if picked == nil { return nil, fmt.Errorf("match rule: %w", err) } return picked, nil } m.warmRuleCache(ctx, sourceID, event, rule) return rule, nil } func (m *Matcher) warmRuleCache(ctx context.Context, sourceID int, event string, rule *model.Rule) { if m.cache == nil { return } tmpl, err := m.store.GetTemplate(ctx, rule.TemplateID) if err != nil { return } 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) }