package lottery import ( "database/sql" "encoding/json" "time" "gorm.io/gorm" ) // ---- 状态与类型常量 --------------------------------------------------------- const ( ActivityStatusDraft = "draft" ActivityStatusRunning = "running" ActivityStatusPaused = "paused" ActivityStatusEnded = "ended" UnmetActionBlock = "block" UnmetActionShowReason = "show_reason" // PrizeType* 是发奖 handler 注册表的键。Stage 1 只实现前三种。 PrizeTypeVPNDuration = "vpn_duration" PrizeTypeCommission = "commission" PrizeTypeNone = "none" // Stage 2/3 预留。 PrizeTypeBalance = "balance" PrizeTypeGiftAmount = "gift_amount" PrizeTypeCoupon = "coupon" PrizeTypePoints = "points" PrizeTypeEncrypted = "encrypted" PrizeTypePhysical = "physical" PrizeTypeManualOther = "manual_other" // ChanceSource* 是次数入账触发源。 ChanceSourceDailySignin = "daily_signin" ChanceSourceNewSubscription = "new_subscription" ChanceSourceInviteSuccess = "invite_success" ChanceSourceManualGrant = "manual_grant" // DispatchState* 是 lottery_draw.dispatch_state 的取值。 DispatchStateNone = "none" DispatchStateAutoClaimed = "auto_claimed" DispatchStatePendingClaim = "pending_claim" DispatchStatePaid = "paid" DispatchStateExpired = "expired" DispatchStateFailed = "failed" ) // ---- 实体 ----------------------------------------------------------------- type Activity struct { Id int64 `gorm:"primaryKey"` Title string `gorm:"type:varchar(128);not null;default:'';comment:活动标题"` Description string `gorm:"type:text;comment:活动描述"` StartAt time.Time `gorm:"not null;comment:开始时间"` EndAt time.Time `gorm:"not null;comment:结束时间"` Status string `gorm:"type:varchar(16);not null;default:'draft';comment:状态"` GridSize int `gorm:"type:tinyint;not null;default:9;comment:九宫格数量"` Eligibility string `gorm:"type:json;not null;comment:参与门槛(AND/OR 嵌套规则)"` ChanceSources string `gorm:"type:json;not null;comment:次数来源列表"` UnmetAction string `gorm:"type:varchar(32);not null;default:'block';comment:未达门槛策略"` CreatedAt time.Time `gorm:"<-:create;comment:Create Time"` UpdatedAt time.Time `gorm:"comment:Update Time"` DeletedAt gorm.DeletedAt `gorm:"index;comment:Delete Time"` } func (Activity) TableName() string { return "lottery_activity" } type Prize struct { Id int64 `gorm:"primaryKey"` ActivityId int64 `gorm:"type:bigint unsigned;not null;comment:所属活动"` Slot int `gorm:"type:tinyint;not null;comment:九宫格位置"` Type string `gorm:"type:varchar(32);not null;comment:奖品类型"` Name string `gorm:"type:varchar(128);not null;default:'';comment:名称"` IconURL string `gorm:"type:varchar(512);not null;default:'';comment:图标 URL"` Config string `gorm:"type:json;not null;comment:类型专属配置"` Weight int `gorm:"type:int;not null;default:0;comment:加权随机权重"` TotalStock sql.NullInt64 `gorm:"type:bigint;comment:总库存(NULL=无限)"` RemainingStock sql.NullInt64 `gorm:"type:bigint;comment:剩余库存(NULL=无限)"` IsFallback bool `gorm:"type:tinyint(1);not null;default:0;comment:是否为保底奖"` Version int64 `gorm:"type:bigint;not null;default:0;comment:乐观锁"` CreatedAt time.Time `gorm:"<-:create;comment:Create Time"` UpdatedAt time.Time `gorm:"comment:Update Time"` } func (Prize) TableName() string { return "lottery_prize" } type ChanceBalance struct { Id int64 `gorm:"primaryKey"` UserId int64 `gorm:"type:bigint unsigned;not null;comment:用户 ID"` ActivityId int64 `gorm:"type:bigint unsigned;not null;comment:活动 ID"` Remaining int64 `gorm:"type:bigint;not null;default:0;comment:剩余次数"` TotalEarned int64 `gorm:"type:bigint;not null;default:0;comment:累计入账"` TotalSpent int64 `gorm:"type:bigint;not null;default:0;comment:累计消耗"` CreatedAt time.Time `gorm:"<-:create;comment:Create Time"` UpdatedAt time.Time `gorm:"comment:Update Time"` } func (ChanceBalance) TableName() string { return "lottery_chance_balance" } type ChanceGrant struct { Id int64 `gorm:"primaryKey"` UserId int64 `gorm:"type:bigint unsigned;not null;comment:用户 ID"` ActivityId int64 `gorm:"type:bigint unsigned;not null;comment:活动 ID"` Source string `gorm:"type:varchar(32);not null;comment:触发源"` SourceRef string `gorm:"type:varchar(128);not null;comment:外部业务幂等键"` Amount int `gorm:"type:int;not null;default:0;comment:本次发放次数"` ExpiresAt *time.Time `gorm:"default:null;comment:到期时间"` GrantedAt time.Time `gorm:"<-:create;default:CURRENT_TIMESTAMP;comment:入账时间"` } func (ChanceGrant) TableName() string { return "lottery_chance_grant" } type Draw struct { Id int64 `gorm:"primaryKey"` UserId int64 `gorm:"type:bigint unsigned;not null;comment:用户 ID"` ActivityId int64 `gorm:"type:bigint unsigned;not null;comment:活动 ID"` ClientNonce string `gorm:"type:varchar(64);not null;comment:前端幂等键"` PrizeId sql.NullInt64 `gorm:"type:bigint unsigned;comment:中奖奖品 ID(谢谢参与=NULL)"` IsWin bool `gorm:"type:tinyint(1);not null;default:0;comment:是否中奖"` DispatchState string `gorm:"type:varchar(16);not null;default:'none';comment:发放状态"` DispatchError string `gorm:"type:text;comment:发放失败原因"` DispatchedAt *time.Time `gorm:"default:null;comment:发放完成时间"` DrawnAt time.Time `gorm:"not null;default:CURRENT_TIMESTAMP;comment:抽奖时间"` CreatedAt time.Time `gorm:"<-:create;comment:Create Time"` UpdatedAt time.Time `gorm:"comment:Update Time"` } func (Draw) TableName() string { return "lottery_draw" } type PrizeSnapshot struct { Id int64 `gorm:"primaryKey"` DrawId int64 `gorm:"type:bigint unsigned;not null;uniqueIndex:uk_draw_id;comment:抽奖记录 ID"` PrizeId int64 `gorm:"type:bigint unsigned;not null;comment:奖品 ID"` Slot int `gorm:"type:tinyint;not null;comment:九宫格位置"` Type string `gorm:"type:varchar(32);not null;comment:奖品类型"` Name string `gorm:"type:varchar(128);not null;comment:奖品名称"` Config string `gorm:"type:json;not null;comment:类型专属配置"` CreatedAt time.Time `gorm:"<-:create;comment:Create Time"` } func (PrizeSnapshot) TableName() string { return "lottery_prize_snapshot" } type EligibilitySnapshot struct { Id int64 `gorm:"primaryKey"` DrawId int64 `gorm:"type:bigint unsigned;not null;uniqueIndex:uk_draw_id;comment:抽奖记录 ID"` UserId int64 `gorm:"type:bigint unsigned;not null;comment:用户 ID"` ActivityId int64 `gorm:"type:bigint unsigned;not null;comment:活动 ID"` Passed bool `gorm:"type:tinyint(1);not null;default:0;comment:是否通过门槛"` UnmetReasons string `gorm:"type:json;comment:未通过项"` EvaluatedAt time.Time `gorm:"not null;default:CURRENT_TIMESTAMP;comment:评估时间"` } func (EligibilitySnapshot) TableName() string { return "lottery_eligibility_snapshot" } // ---- JSON 结构体辅助 ------------------------------------------------------- // EligibilityRule 是持久化在 lottery_activity.eligibility 字段里的门槛规则树。 // 每个节点要么是"叶子"(Type 非空),要么是"聚合"(Op 为 AND/OR + Children)。 type EligibilityRule struct { Op string `json:"op,omitempty"` Children []*EligibilityRule `json:"children,omitempty"` Type string `json:"type,omitempty"` Params json.RawMessage `json:"params,omitempty"` } // ChanceSource 描述次数来源的一条配置,持久化在 lottery_activity.chance_sources。 type ChanceSource struct { Source string `json:"source"` Amount int `json:"amount"` Params json.RawMessage `json:"params,omitempty"` DailyLimit int `json:"daily_limit,omitempty"` Unlimited bool `json:"unlimited,omitempty"` } // UnmetReason 是门槛评估结果,作为 GET /config 的 unmet_reasons 元素返回。 type UnmetReason struct { Rule string `json:"rule"` Hint string `json:"hint"` Current int64 `json:"current,omitempty"` Required int64 `json:"required,omitempty"` }