diff --git a/event/event.go b/event/event.go index 8663d57..0725231 100644 --- a/event/event.go +++ b/event/event.go @@ -81,12 +81,88 @@ const ( // 5 hand-written `pevent.Type("track.purchase")` literals with this // typed constant. TrackPurchase Type = "track.purchase" + + // Wishlist attribution (W6d of docs/plan-wishlist-favorites.md). + // Producers: profile service (share-page read) and cart service + // (list→cart conversion). Consumer: slask-tracking + // (pkg/events/rabbit-tracking.go) — feeds the saved-together + // co-occurrence group. Payload is an event.WishlistEvent — product + // ids only, never owner identity (C5 pseudonymity). Wire routing + // keys are `track.wishlist_viewed` / `track.wishlist_to_cart` so + // they ride the existing `track.*` pattern on the catalog exchange. + WishlistViewed Type = "wishlist.viewed" // public share-page read + WishlistToCart Type = "wishlist.to_cart" // list → cart conversion + + // Wishlist mutation outbox (W6 hardening of docs/plan-wishlist-favorites.md). + // Producers: profile service (owner-scoped wishlist mutations, emitted + // from the UCP handlers on successful applies) and cart service (cart→list + // conversion). No in-repo consumer yet — these are the durable domain + // facts that future consumers bind to (price-drop / back-in-stock + // notifications, merchandising analytics, GDPR audit). Wire routing keys + // mirror the Type (`wishlist.created`, `wishlist.item_added`, …, + // `cart.to_wishlist`) on the catalog exchange. Unlike the tracking + // attribution pair above, these ride their own routing keys — NOT the + // `track.*` pattern — so slask-tracking's existing `track.*` binding + // does not pick them up. Payload: event.WishlistMutationEvent (profile) + // / event.CartToWishlistEvent (cart). See docs/amqp-topics.md. + WishlistCreated Type = "wishlist.created" // explicit user-created list (not the implicit default materialisation) + WishlistItemAdded Type = "wishlist.item_added" // one per SKU added (single-add and batch paths) + WishlistItemRemoved Type = "wishlist.item_removed" + WishlistShared Type = "wishlist.shared" // share (re)generated — token itself never leaves the service + CartToWishlist Type = "cart.to_wishlist" // cart → list conversion (cart service) ) + +// WishlistEvent is the payload for the wishlist attribution events +// (WishlistViewed / WishlistToCart). Deliberately lean: list id + catalog +// item ids only — no owner id, no list name, no share token, so the +// pseudonymous tracking contract (C5) holds on the wire. +type WishlistEvent struct { + ListId uint32 `json:"listId"` + ItemIds []uint32 `json:"itemIds"` +} + +// WishlistMutationEvent is the payload for the wishlist mutation outbox +// events (WishlistCreated / WishlistItemAdded / WishlistItemRemoved / +// WishlistShared). Fields are optional per event: +// +// wishlist.created → ListId + Name +// wishlist.item_added → ListId + Sku + ItemId +// wishlist.item_removed → ListId + Sku (+ ItemId when known) +// wishlist.shared → ListId + Name +// +// No owner identity, no share token (C5): a consumer can correlate by list +// id but never attribute a list to a customer from the event alone. +type WishlistMutationEvent struct { + ListId uint32 `json:"listId"` + Name string `json:"name,omitempty"` + Sku string `json:"sku,omitempty"` + ItemId uint32 `json:"itemId,omitempty"` +} + +// CartToWishlistEvent is the payload for cart.to_wishlist (cart service). +// Carries the pseudonymised owner profile id (the cart's UserId — a +// customer-scoped opaque uint64 id, never an email/name) plus the list the +// lines landed in and the SKUs copied. Unlike the tracking pair, this is a +// domain outbox event on its own routing key; a future consumer (e.g. +// price-drop) needs the owner to address the customer. +type CartToWishlistEvent struct { + OwnerId uint64 `json:"ownerId"` + ListId uint32 `json:"listId"` + ListName string `json:"listName,omitempty"` + Skus []string `json:"skus,omitempty"` +} + // KnownTypes lists every event that has a declared Track* constant above. // New pevent constants MUST also be added here so IsKnownType stays in // sync. Used by HTTP handlers' forward-validation (e.g. SessionsPanel // ?event_types= param, cut 25) so caller's typo's surface as a 400 // rather than silently dropping unknown tokens. +// KnownTypes is deliberately Track*-only: IsKnownType validates the +// user-facing `track.*` event-type query surface (SessionsPanel +// ?event_types= etc.). The wishlist attribution types (WishlistViewed / +// WishlistToCart) are consumed exclusively over AMQP and never appear in +// that HTTP param surface, so they stay out of this list (the drift-guard +// test enforces the Track* discipline). var KnownTypes = []Type{ TrackSession, TrackSearch, TrackClick, TrackCart, TrackPurchase, TrackImpression, TrackAction, TrackPageView, TrackPageEngaged,