package service import ( "baron-sso-backend/internal/domain" "baron-sso-backend/internal/repository" "context" "crypto/sha256" "encoding/hex" "fmt" "strings" "time" ) type RPUsageEventEmitter struct { repo repository.RPUsageOutboxRepository } func NewRPUsageEventEmitter(repo repository.RPUsageOutboxRepository) *RPUsageEventEmitter { return &RPUsageEventEmitter{repo: repo} } func (e *RPUsageEventEmitter) EmitRPUsageEvent(ctx context.Context, event domain.RPUsageEvent) error { if e == nil || e.repo == nil { return nil } event.EventType = strings.TrimSpace(event.EventType) event.Subject = strings.TrimSpace(event.Subject) event.ClientID = strings.TrimSpace(event.ClientID) event.Source = strings.TrimSpace(event.Source) event.CorrelationID = strings.TrimSpace(event.CorrelationID) if event.EventType == "" { return fmt.Errorf("rp usage event type is required") } if event.Subject == "" { return fmt.Errorf("rp usage subject is required") } if event.ClientID == "" { return fmt.Errorf("rp usage client_id is required") } if event.Source == "" { event.Source = "backend" } if event.OccurredAt.IsZero() { event.OccurredAt = time.Now() } if event.DedupeKey == "" { event.DedupeKey = buildRPUsageDedupeKey(event) } if event.Payload == nil { event.Payload = domain.JSONMap{} } return e.repo.Create(ctx, &event) } func buildRPUsageDedupeKey(event domain.RPUsageEvent) string { raw := strings.Join([]string{ event.EventType, event.Subject, event.ClientID, event.SessionID, event.Source, event.CorrelationID, event.OccurredAt.UTC().Format("2006-01-02T15:04:05.000Z"), }, "|") sum := sha256.Sum256([]byte(raw)) return hex.EncodeToString(sum[:]) }