package lottery import ( "context" "sync" "gorm.io/gorm" ) // defaultRegistry 是并发安全的默认 Registry。启动时一次性注册,运行时只读。 type defaultRegistry struct { mu sync.RWMutex handlers map[string]PrizeHandler } // NewRegistry 返回空的默认注册表。使用 Register 挂接实现。 func NewRegistry() *defaultRegistry { return &defaultRegistry{handlers: make(map[string]PrizeHandler, 8)} } // Register 挂接一个 handler;重复注册会覆盖(初始化阶段可控,不额外拦截)。 func (r *defaultRegistry) Register(h PrizeHandler) { r.mu.Lock() defer r.mu.Unlock() r.handlers[h.Type()] = h } func (r *defaultRegistry) Get(prizeType string) (PrizeHandler, bool) { r.mu.RLock() defer r.mu.RUnlock() h, ok := r.handlers[prizeType] return h, ok } func (r *defaultRegistry) MustGet(prizeType string) (PrizeHandler, error) { if h, ok := r.Get(prizeType); ok { return h, nil } return nil, ErrHandlerNotRegistered } // ---- Stage 1 骨架 handler -------------------------------------------------- // 真实业务对接在架构师 review 骨架 PR 之后 Day 2-3 补齐。此时命中真实 // handler 会返回 ErrNotImplemented,让抽奖流程整笔事务回滚,避免误发。 // noopHandler 是"谢谢参与"。写记录、不发放,不需要真实业务对接。 type noopHandler struct{} // NewNoopHandler 返回 PrizeTypeNone 的 handler。 func NewNoopHandler() PrizeHandler { return &noopHandler{} } func (noopHandler) Type() string { return PrizeTypeNone } func (noopHandler) IsAuto() bool { return true } func (noopHandler) Dispatch(_ context.Context, _ *gorm.DB, _ DispatchRequest) (DispatchResult, error) { return DispatchResult{State: DispatchStateAutoClaimed, Message: "谢谢参与"}, nil } func (noopHandler) ValidateClaim(_ []byte) error { return nil } // vpnDurationStubHandler 是订阅时长发放的占位 handler。 // Day 2-3 需替换为真实实现: // 1. tx 内 SELECT ... FOR UPDATE 锁 lottery_grant_ledger UNIQUE(external_ref) // 2. 命中 → 幂等返回;未命中 → INSERT ledger + 调用 UserModel.UpdateSubscribe/InsertSubscribe // 3. external_ref 用 fmt.Sprintf("lottery:%d:%d", req.ActivityId, req.DrawId) // 4. 家庭组场景通过 commonLogic.ResolveEntitlementUser 归位到 owner // 5. 成功后 best-effort 调 NodeModel.ClearServerAllCache + 用户组重算 type vpnDurationStubHandler struct{} // NewVPNDurationStubHandler 返回 PrizeTypeVPNDuration 的骨架 handler。 func NewVPNDurationStubHandler() PrizeHandler { return &vpnDurationStubHandler{} } func (vpnDurationStubHandler) Type() string { return PrizeTypeVPNDuration } func (vpnDurationStubHandler) IsAuto() bool { return true } func (vpnDurationStubHandler) Dispatch(_ context.Context, _ *gorm.DB, _ DispatchRequest) (DispatchResult, error) { return DispatchResult{}, ErrNotImplemented } func (vpnDurationStubHandler) ValidateClaim(_ []byte) error { return nil } // commissionStubHandler 是佣金入账的占位 handler。 // Day 2-3 需替换为真实实现: // 1. tx 内 lottery_grant_ledger 幂等检查 // 2. 调 UserModel.UpdateCommission + common.WriteCommissionLog(同 tx) // 3. 建议新增 log.CommissionTypeLottery 常量(数值待架构师批), // 避免把抽奖入账混进 Purchase/Renewal 类型统计 type commissionStubHandler struct{} // NewCommissionStubHandler 返回 PrizeTypeCommission 的骨架 handler。 func NewCommissionStubHandler() PrizeHandler { return &commissionStubHandler{} } func (commissionStubHandler) Type() string { return PrizeTypeCommission } func (commissionStubHandler) IsAuto() bool { return true } func (commissionStubHandler) Dispatch(_ context.Context, _ *gorm.DB, _ DispatchRequest) (DispatchResult, error) { return DispatchResult{}, ErrNotImplemented } func (commissionStubHandler) ValidateClaim(_ []byte) error { return nil }