package domain import ( "errors" "time" "github.com/google/uuid" ) type EventStatus string const ( EventStatusDraft EventStatus = "draft" EventStatusPublished EventStatus = "published" EventStatusClosed EventStatus = "closed" EventStatusArchived EventStatus = "archived" ) func (s EventStatus) Valid() bool { switch s { case EventStatusDraft, EventStatusPublished, EventStatusClosed, EventStatusArchived: return true } return false } type Event struct { ID uuid.UUID `json:"id"` HostID uuid.UUID `json:"host_id"` Name string `json:"name"` Slug string `json:"slug"` EventDate time.Time `json:"event_date"` Venue string `json:"venue"` MaxCapacity int `json:"max_capacity"` Settings map[string]any `json:"settings"` Status EventStatus `json:"status"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` // Per-event fraud-band thresholds (Tier 2 Block G). The defaults are // set by the migration so events created pre-Block-G continue to use // the previous global 30/60/85 boundaries until a host changes them. FraudMediumThreshold int `json:"fraud_medium_threshold"` FraudHighThreshold int `json:"fraud_high_threshold"` FraudBlockThreshold int `json:"fraud_block_threshold"` } // Thresholds returns the event's fraud band trio as a single value the // scoring path can pass around (or fall back to defaults if the row was // loaded before this migration). func (e *Event) Thresholds() FraudThresholds { t := FraudThresholds{ Medium: e.FraudMediumThreshold, High: e.FraudHighThreshold, Block: e.FraudBlockThreshold, } // Treat zeros (or invalid orderings) as "host hasn't customised"; // fall back to defaults rather than wedge every score into the lowest // band. if t.Valid() != nil || t.Block == 0 { return DefaultThresholds() } return t } var ( ErrEventNotFound = errors.New("event not found") ErrSlugTaken = errors.New("slug already in use") )