// Types for lottery Stage 1 user & admin APIs. Kept in a separate file so // future `goctl` regenerations of types.go do not clobber them (same pattern // as internal/types/subscribe.go). // // Field names mirror the shape declared in HIF-3 (Stage 1 spec) and the // architect's PR C brief. Do NOT rename without updating the .api files and // notifying frontend. package types import "encoding/json" // ---- User API --------------------------------------------------------------- // GetLotteryConfigRequest is the query for GET /api/v1/lottery/config. type GetLotteryConfigRequest struct { ActivityId int64 `form:"activity_id" validate:"required"` } // LotteryActivityConfig is the activity snapshot returned to the user. type LotteryActivityConfig struct { Id int64 `json:"id"` Title string `json:"title"` Description string `json:"description"` StartAt int64 `json:"start_at"` EndAt int64 `json:"end_at"` Status string `json:"status"` GridSize int `json:"grid_size"` Prizes []LotteryPrizeConfig `json:"prizes"` } // LotteryPrizeConfig is the slot-facing view of a prize (no weight / stock // exposed — those are admin-only). type LotteryPrizeConfig struct { Slot int `json:"slot"` Id int64 `json:"id"` Type string `json:"type"` Name string `json:"name"` IconUrl string `json:"icon_url"` Config json.RawMessage `json:"config"` SoldOut bool `json:"sold_out"` } // LotteryUserStatus reports whether the user can currently draw. type LotteryUserStatus struct { Eligible bool `json:"eligible"` ChancesRemaining int64 `json:"chances_remaining"` UnmetReasons []LotteryUnmetReason `json:"unmet_reasons"` } // LotteryUnmetReason is a single unmet-rule explanation for frontend display. type LotteryUnmetReason struct { Rule string `json:"rule"` Hint string `json:"hint"` Current int64 `json:"current,omitempty"` Required int64 `json:"required,omitempty"` } // GetLotteryConfigResponse is the envelope for GET /config. type GetLotteryConfigResponse struct { Activity LotteryActivityConfig `json:"activity"` User LotteryUserStatus `json:"user"` } // DrawLotteryRequest is the body for POST /api/v1/lottery/draw. type DrawLotteryRequest struct { ActivityId int64 `json:"activity_id" validate:"required"` ClientNonce string `json:"client_nonce" validate:"required,max=64"` } // DrawnPrize is a slim prize view returned on draw success. type DrawnPrize struct { Slot int `json:"slot"` Id int64 `json:"id"` Type string `json:"type"` Name string `json:"name"` Config json.RawMessage `json:"config"` } // LotteryClaimStatus tells the frontend whether more action is needed. type LotteryClaimStatus struct { Required bool `json:"required"` AutoClaimed bool `json:"auto_claimed"` Message string `json:"message,omitempty"` } // DrawLotteryResponse is what /draw returns. type DrawLotteryResponse struct { DrawId int64 `json:"draw_id"` IsWin bool `json:"is_win"` Prize *DrawnPrize `json:"prize"` Claim LotteryClaimStatus `json:"claim"` ChancesRemaining int64 `json:"chances_remaining"` } // GetLotteryRecordsRequest paginates over the user's draws. // Page/Size 默认 by logic 层(Page<=0 → 1;Size<=0||>200 → 20);tag 里不设 // default 以避免 staticcheck 与 Gin binding 的语义冲突。 type GetLotteryRecordsRequest struct { ActivityId int64 `form:"activity_id"` Status string `form:"status"` Page int `form:"page"` Size int `form:"size"` } // LotteryRecord is one row in the records list. type LotteryRecord struct { DrawId int64 `json:"draw_id"` ActivityId int64 `json:"activity_id"` IsWin bool `json:"is_win"` Prize *DrawnPrize `json:"prize"` DispatchState string `json:"dispatch_state"` DrawnAt int64 `json:"drawn_at"` } // GetLotteryRecordsResponse is the paginated payload. type GetLotteryRecordsResponse struct { Total int64 `json:"total"` List []LotteryRecord `json:"list"` } // ClaimLotteryPrizeRequest is Stage 2 spec; Stage 1 always returns 4010. type ClaimLotteryPrizeRequest struct { DrawId int64 `json:"draw_id" validate:"required"` Input json.RawMessage `json:"input,omitempty"` } // ClaimLotteryPrizeResponse mirrors Stage 2 shape; Stage 1 unused. type ClaimLotteryPrizeResponse struct{} // ---- Admin API -------------------------------------------------------------- // AdminLotteryActivity is the full admin view of an activity. type AdminLotteryActivity struct { Id int64 `json:"id"` Title string `json:"title"` Description string `json:"description"` StartAt int64 `json:"start_at"` EndAt int64 `json:"end_at"` Status string `json:"status"` GridSize int `json:"grid_size"` Eligibility json.RawMessage `json:"eligibility"` ChanceSources json.RawMessage `json:"chance_sources"` UnmetAction string `json:"unmet_action"` CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` } // CreateAdminLotteryActivityRequest creates a new activity in "draft" status. type CreateAdminLotteryActivityRequest struct { Title string `json:"title" validate:"required,max=128"` Description string `json:"description"` StartAt int64 `json:"start_at" validate:"required"` EndAt int64 `json:"end_at" validate:"required"` // GridSize:0 由 logic 层兜底为 9(不能在 json tag 里写 default=… —— // staticcheck SA5008 会拒;encoding/json 也不认这个选项)。 GridSize int `json:"grid_size"` Eligibility json.RawMessage `json:"eligibility"` ChanceSources json.RawMessage `json:"chance_sources"` // UnmetAction:空字符串由 logic 层兜底为 "block"。 UnmetAction string `json:"unmet_action"` } // UpdateAdminLotteryActivityRequest updates mutable fields. type UpdateAdminLotteryActivityRequest struct { Id int64 `json:"id" validate:"required"` Title string `json:"title,omitempty"` Description string `json:"description,omitempty"` StartAt int64 `json:"start_at,omitempty"` EndAt int64 `json:"end_at,omitempty"` GridSize int `json:"grid_size,omitempty"` UnmetAction string `json:"unmet_action,omitempty"` } // ListAdminLotteryActivitiesRequest paginates admin listings. // Page/Size 默认 by logic 层。 type ListAdminLotteryActivitiesRequest struct { Page int `form:"page"` Size int `form:"size"` Status string `form:"status,omitempty"` Search string `form:"search,omitempty"` } // ListAdminLotteryActivitiesResponse pages. type ListAdminLotteryActivitiesResponse struct { Total int64 `json:"total"` List []AdminLotteryActivity `json:"list"` } // AdminActivityIdRequest is used by GET /detail, publish, pause, delete. type AdminActivityIdRequest struct { Id int64 `form:"id" json:"id" validate:"required"` } // UpdateAdminLotteryRulesRequest overwrites eligibility / chance_sources / // unmet_action in a single call. Validated against rule caps before persist. type UpdateAdminLotteryRulesRequest struct { Id int64 `json:"id" validate:"required"` Eligibility json.RawMessage `json:"eligibility"` ChanceSources json.RawMessage `json:"chance_sources"` UnmetAction string `json:"unmet_action,omitempty"` } // AdminLotteryPrize is the admin view of a prize (includes weight + stock). type AdminLotteryPrize struct { Id int64 `json:"id"` ActivityId int64 `json:"activity_id"` Slot int `json:"slot"` Type string `json:"type"` Name string `json:"name"` IconUrl string `json:"icon_url"` Config json.RawMessage `json:"config"` Weight int `json:"weight"` TotalStock *int64 `json:"total_stock"` RemainingStock *int64 `json:"remaining_stock"` IsFallback bool `json:"is_fallback"` CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` } // CreateAdminLotteryPrizeRequest is nested under /activities/{id}/prizes. type CreateAdminLotteryPrizeRequest struct { ActivityId int64 `json:"activity_id" validate:"required"` Slot int `json:"slot"` Type string `json:"type" validate:"required,max=32"` Name string `json:"name" validate:"required,max=128"` IconUrl string `json:"icon_url"` Config json.RawMessage `json:"config"` Weight int `json:"weight"` TotalStock *int64 `json:"total_stock,omitempty"` IsFallback bool `json:"is_fallback"` } // UpdateAdminLotteryPrizeRequest updates mutable prize fields. type UpdateAdminLotteryPrizeRequest struct { Id int64 `json:"id" validate:"required"` Slot *int `json:"slot,omitempty"` Name string `json:"name,omitempty"` IconUrl string `json:"icon_url,omitempty"` Config json.RawMessage `json:"config,omitempty"` Weight *int `json:"weight,omitempty"` TotalStock *int64 `json:"total_stock,omitempty"` IsFallback *bool `json:"is_fallback,omitempty"` } // ListAdminLotteryPrizesRequest lists prizes for an activity. type ListAdminLotteryPrizesRequest struct { ActivityId int64 `form:"activity_id" validate:"required"` } // ListAdminLotteryPrizesResponse pages. type ListAdminLotteryPrizesResponse struct { List []AdminLotteryPrize `json:"list"` } // AdminPrizeIdRequest is used by DELETE / GET single. type AdminPrizeIdRequest struct { Id int64 `form:"id" json:"id" validate:"required"` } // GrantAdminLotteryChanceRequest gives a specified user N chances on an // activity. sourceRef doubles as idempotency key. type GrantAdminLotteryChanceRequest struct { ActivityId int64 `json:"activity_id" validate:"required"` UserId int64 `json:"user_id" validate:"required"` Amount int `json:"amount" validate:"required,min=1"` SourceRef string `json:"source_ref" validate:"required,max=128"` }