package domain import "time" type BroadcastStatus string const ( StatusPending BroadcastStatus = "pending" StatusDispatched BroadcastStatus = "dispatched" StatusDelivered BroadcastStatus = "delivered" StatusFailed BroadcastStatus = "failed" StatusRetryScheduled BroadcastStatus = "retry_scheduled" ) type Broadcast struct { ID int64 SopigaCollarID int SopigaTemplateID int MessagePayload map[string]any Status BroadcastStatus ErrorMessage *string ErrorCount int SopigaRecipientDetailID *int64 CreatedAt time.Time UpdatedAt time.Time } func (b *Broadcast) RecipientPhone() (string, bool) { v, ok := b.MessagePayload["nasabah_phone"].(string) return v, ok } func (b *Broadcast) InvoiceURL() (string, bool) { v, ok := b.MessagePayload["invoice_pdf_url"].(string) return v, ok } func (b *Broadcast) CustomerName() string { v, _ := b.MessagePayload["nasabah_nama"].(string) return v } // MapSopigaDeliveryStatus translates a Sopiga recipient status into our // internal terminal status. ok is false while Sopiga still reports an // in-flight state (e.g. "sent"), meaning there is nothing to update yet. func MapSopigaDeliveryStatus(sopigaStatus string) (status BroadcastStatus, ok bool) { switch sopigaStatus { case "delivered", "read": return StatusDelivered, true case "failed", "undelivered": return StatusFailed, true default: // pending, sent, ... return "", false } } type TemplateVariable struct { VariableOrder int VariableName string VariableType string FieldSource string IsRequired bool }