activity-hooks
SKILL.md
活动Hook/Hooker系统开发指南
帮助你使用活动框架的 Hook 系统扩展和修改框架核心行为,无需修改框架代码。
🤖 AI 执行指令
当用户调用此 skill 时,根据需求提供 Hook 系统的使用指导和代码示例。
使用场景判断
- 扩展框架行为 → 推荐合适的 Hook 类型和注册方式
- Hook vs Event 选择 → 分析场景帮助选择同步/异步方案
- 实现特定 Hook → 提供具体 Hook 的代码模板
- 理解 Hooker 接口 → 解释各组件 Hooker 的方法和作用
设计原理
Hook 是一个同步插件架构,允许活动通过实现 Hooker 接口或注册函数来扩展或修改组件的核心行为。
Hook vs Event
| 方面 | Hook | Event |
|---|---|---|
| 触发方式 | 直接函数调用(同步) | 异步消息队列 |
| 执行时序 | 同步,可拦截/修改结果 | 延迟,无法影响调用者 |
| 注册方式 | GetHookers() 方法返回 | 配置级声明 |
| 典型用途 | 修改行为/过滤/拦截 | 通知/异步处理/统计 |
选择原则:需要修改调用者结果 → Hook;只需通知不影响流程 → Event。
Hooker 注册方式
唯一注册入口:GetHookers() 方法
所有活动的 Hooker 都在活动模型的 GetHookers() 方法中注册,返回 ActHookerList 结构体。
func (a *MyActivity) GetHookers() actmodel.ActHookerList {
h := actmodel.ActHookerList{
// 各组件的 Hooker 接口实现
LotteryHooker: &MyLotteryHooker{},
TaskHooker: &MyTaskHooker{},
RankHooker: &MyRankHooker{},
ExtraGiftHooker: &MyExtraGiftHooker{},
CollectChipHooker: &MyCollectChipHooker{},
ExchangeHooker: &MyExchangeHooker{},
// 礼包钩子(map 形式,key 是 actId 或礼包 ID)
GiftPkgHookers: map[int]gift_package.GiftPkgHookerI{
conf.ActID: &MyGiftPkgHooker{},
},
}
// 送礼钩子(函数形式,需要指定 giftId)
h.AppendSendGiftHooker(weconfig.GetServerRegion(), conf.GiftIdGiftBox, HandleSendGiftBox)
h.AppendSendGiftHooker(weconfig.GetServerRegion(), conf.BlindBoxGiftId, HandleSendBlindBoxGift)
return h
}
ActHookerList 包含的 Hooker 类型
type ActHookerList struct {
CollectChipHooker collectChip.CollectChipHooker
LotteryHooker lottery.LotteryHooker
TaskHooker taskDef.TaskHooker
RankHooker rankstore.RankCheckoutHooker
GiftPkgHookers map[int]gift_package.GiftPkgHookerI // key: actId
GiftPkgHookersV2 map[int]gift_package.GiftPkgHookerI // key: 礼包ID
ExtraGiftHooker extra_gift_hooker.ExtraGiftHooker
ExchangeHooker hooks.ExchangeHooker
FamilyHooker family.IFamilyHooker
MiningHooker mining.IMiningHooker
TeamHooker team.TeamHooker
ActDiscoverHooker act_discover.ActDiscoverHooker
SignDiscontinue signDiscontinue.SignDiscontinueHooker
// 送礼相关(函数形式)
SendGiftActivityFunc map[int]actsendgift.SendGiftActivityFunc // key: giftId
GiftRandomCoinFunc map[int]actsendgift.CommonGiftRandomCoinFunc
}
常用 Hooker 详解
1. LotteryHooker - 抽奖组件 Hook
抽奖组件提供了丰富的Hook点,用于扩展抽奖前后的行为。
接口定义(核心方法)
type LotteryHooker interface {
// 判断是否可以进行抽奖
CheckCanLottery(lotteryParams LotteryHookParams) error
// 替换抽奖参数
BeforeLottery(lotteryParams LotteryHookParams) LotteryReq
// 扣碎片前替换数量
BeforeCostChip(lotteryConf *weactivity.LotteryConf, actId, uid int, needChipMap map[int]int) map[int]int
// 发奖前替换奖励(重要!用于拦截和替换奖励)
BeforeSendRewards(lotteryParams LotteryHookParams, res []*actreward.RewardInfo) []*actreward.RewardInfo
// 写入抽奖记录前修改记录内容
BeforeWriteDb(lotteryParams LotteryHookParams, res []*actreward.RewardInfo) []*actreward.RewardInfo
// 发全服弹幕之前替换奖励列表
BeforeSendFullNotify(lotteryParams LotteryHookParams, res []*actreward.RewardInfo) []*actreward.RewardInfo
// 抽完奖后替换奖励列表以控制展示逻辑
BeforeReturnResp(lotteryParams LotteryHookParams, res []*actreward.RewardInfo) []*actreward.RewardInfo
// 多抽时每抽前替换抽奖配置
ReplacePoolForOnceLotteryBefore(lotteryParams LotteryHookParams) LotteryHookRsp
// 多抽时每抽后替换抽奖配置
AfterOnceLotteryEnd(lotteryParams LotteryHookParams, rewardInfo *actreward.RewardInfo, currResult []*actreward.RewardInfo)
// 抽奖前替换抽奖配置
CustomLotteryConfig(lotteryType string, req LotteryReq) *weactivity.LotteryConf
// 替换查询接口返回内容
AfterQueryLotteryInfo(lotteryParams LotteryHookParams, res *store.LotteryInfoResp)
}
BeforeSendRewards - 拦截和替换奖励
核心用途:在奖励发放前拦截,可以替换奖励或阻止发放,常用于实现限额控制、奖励转换等逻辑。
重要特性:
- 保底进度已经扣除,但奖励还未发放
- 可以替换奖励为空奖励(实现限额控制)
- 可以修改奖励内容(如转换成其他奖励)
- 不影响保底进度:即使替换为空奖励,保底值也不会回退
典型场景1:实现每日限额控制
type MyLotteryHooker struct {
lottery.DefaultLotteryHooker
}
func (h *MyLotteryHooker) BeforeSendRewards(lotteryParams lottery.LotteryHookParams, res []*actreward.RewardInfo) []*actreward.RewardInfo {
// 检查今日已获得的特定碎片数量
chipId := 2 // 假设要限制的碎片ID
todayCount := getTodayChipCount(lotteryParams.Req.Uid, chipId)
dailyLimit := 100 // 每日限额100个
if todayCount >= dailyLimit {
// 超过限额,替换为空奖励
return []*actreward.RewardInfo{
{
RewardType: actreward.RewardTypeNone,
RewardId: 0,
RewardVal: 1,
},
}
}
// 未超限,允许发放原奖励
return res
}
典型场景2:奖励转换
func (h *MyLotteryHooker) BeforeSendRewards(lotteryParams lottery.LotteryHookParams, res []*actreward.RewardInfo) []*actreward.RewardInfo {
// 检查用户VIP等级
vipLevel := getUserVipLevel(lotteryParams.Req.Uid)
// 根据VIP等级转换奖励
convertedRewards := make([]*actreward.RewardInfo, 0, len(res))
for _, reward := range res {
newReward := reward.Copy()
if vipLevel >= 5 && reward.RewardType == actreward.RewardTypeCoin {
// VIP5+ 金币奖励翻倍
newReward.RewardVal *= 2
}
convertedRewards = append(convertedRewards, newReward)
}
return convertedRewards
}
典型场景3:全局限额控制
func (h *MyLotteryHooker) BeforeSendRewards(lotteryParams lottery.LotteryHookParams, res []*actreward.RewardInfo) []*actreward.RewardInfo {
for _, reward := range res {
if reward.RewardLevel == 3 { // 稀有奖励
// 检查全局已发放数量
globalCount := getGlobalRewardCount(reward.RewardId)
globalLimit := 1000 // 全服限额1000个
if globalCount >= globalLimit {
// 超过全局限额,替换为空奖励
return []*actreward.RewardInfo{
{
RewardType: actreward.RewardTypeNone,
RewardId: 0,
RewardVal: 1,
},
}
}
}
}
return res
}
注册方式
func (a *MyActivity) GetHookers() actmodel.ActHookerList {
return actmodel.ActHookerList{
LotteryHooker: &MyLotteryHooker{},
}
}
2. TaskHooker - 任务组件 Hook
任务组件提供多个生命周期Hook点。
接口定义
type TaskHooker interface {
// 增加任务条件值前
BeforeIncrConditionValue(param *IncrConditionValueParams) error
// 替换增加的分值
ReplaceIncrValue(param *IncrConditionValueParams) (error, int)
// 增加任务条件值后
AfterIncrConditionValue(param *IncrConditionValueParams, memberValue, userValue int)
// 完成阶段后
AfterCompleteStage(param *IncrConditionValueParams, uid, stageId, recordTimes, realTimes int)
// 发奖前替换奖励
BeforeSendReward(actInfo *weactivity.Activity, task *weactivity.Task, taskInfo *store.TaskInfo,
stage *store.Stage, uid, receiveTimes int, rewards []*actreward.RewardInfo) ([]*actreward.RewardInfo, error)
// 检测是否可以领取奖励
CanReceiveReward(actInfo *weactivity.Activity, task *weactivity.Task, taskInfo *store.TaskInfo,
uid, stageId int) (bool, error)
// 发奖后替换奖励
AfterSendReward(actInfo *weactivity.Activity, task *weactivity.Task, taskInfo *store.TaskInfo,
uid, stageId int, rewards []*actreward.RewardInfo) ([]*actreward.RewardInfo, error)
// 领取奖励后
AfterRecvReward(actInfo *weactivity.Activity, task *weactivity.Task, taskInfo *store.TaskInfo, uid int) error
// 查询任务信息后
AfterQueryTaskInfo(actInfo *weactivity.Activity, task *weactivity.Task, uid int, t time.Time, taskInfo *store.TaskInfo)
// 自定义结算消息
CustomCheckoutMessage(actInfo *weactivity.Activity, task *weactivity.Task, stageId int, uid int,
t time.Time, msgData map[string]interface{}) (map[string]interface{}, bool)
}
使用示例
type MyTaskHooker struct {
taskDef.DefaultTaskHooker
}
// 检测是否可以领取该奖励
func (h *MyTaskHooker) CanReceiveReward(actInfo *weactivity.Activity, task *weactivity.Task,
taskInfo *task_store.TaskInfo, uid, stageId int) (bool, error) {
// 获取用户设备id
deviceId := user.GetActiveDeviceId(int32(uid))
if deviceId == "" {
return false, nil
}
// 获取该设备绑定的用户列表
uids, err := store.GetDeviceUids(conf.ActID, deviceId)
if err != nil {
return false, nil
}
// 如果用户列表超限,则不可领取奖励
if len(uids) >= 3 && !helper.InArrayInt(uid, uids) {
return false, acterror.NewWithMsg(500, "设备超限")
}
return true, nil
}
// 领取奖励后,绑定设备与用户的关系
func (h *MyTaskHooker) AfterRecvReward(actInfo *weactivity.Activity, task *weactivity.Task,
taskInfo *task_store.TaskInfo, uid int) error {
deviceId := user.GetActiveDeviceId(int32(uid))
if deviceId == "" {
return nil
}
// 记录设备与用户的绑定关系
_, err := store.SetDeviceUid(conf.ActID, deviceId, uid)
return err
}
3. RankHooker - 排行榜组件 Hook
排行榜结算和维护的扩展逻辑。
接口定义(核心方法)
type RankCheckoutHooker interface {
// 结算奖励后(按周期类型)
AfterDailyCheckoutReward(actInfo *weactivity.Activity, rank int, c *weactivity.Rank, memberList []int, score int, t time.Time)
AfterTotalCheckoutReward(actInfo *weactivity.Activity, rank int, c *weactivity.Rank, memberList []int, t time.Time, score int)
AfterWeeklyCheckoutReward(actInfo *weactivity.Activity, rank int, c *weactivity.Rank, memberList []int)
// 结算后总回调
AfterCheckout(actInfo *weactivity.Activity, c *weactivity.Rank, memberList []string, scoreList []string, t time.Time)
// 自定义结算消息
CustomCheckoutMessage(actInfo *weactivity.Activity, rankConfig *weactivity.Rank, config weactivity.CheckoutConfig,
member, rank, score int, rewards []*actreward.RewardInfo, data map[string]interface{}, t time.Time) (map[string]interface{}, bool)
// 增加排名分数前
BeforeIncrRankValue(actInfo *weactivity.Activity, rankConfig *weactivity.Rank, periodType int,
member string, value int, t time.Time) (BeforeIncrValueRsp, error)
// 增加排名分数后
AfterIncrRankValue(actInfo *weactivity.Activity, rankConfig *weactivity.Rank, periodType int,
member string, value int, t time.Time, score int) error
// 填充榜单信息
FillRankInfo(ctx context.Context, uid int, actInfo *weactivity.Activity, rankConfig *weactivity.Rank,
t time.Time, item *RankInfoItem)
FillRankList(ctx context.Context, uid int, actInfo *weactivity.Activity, rankConfig *weactivity.Rank,
t time.Time, itemList []*RankInfoItem)
// 替换 Redis 键
BeforeGetRedisKey(actInfo *weactivity.Activity, rankConf *weactivity.Rank, t time.Time) string
// 是否可以结算榜单
CanCheckoutRank(actInfo *weactivity.Activity, rankConf *weactivity.Rank, members []string, t time.Time) bool
}
4. ExtraGiftHooker - 小礼物爆大礼物 Hook
扩展小礼物爆大礼物玩法的行为。
接口定义(核心方法)
type ExtraGiftHooker interface {
// 处理随机列表特殊逻辑
HandleRandomListSpecial(entry *logrus.Entry, actInfo *weactivity.Activity, config *conf.ExtraGift,
param *gift.SendGiftParam, ret *gift.GiftReturnPrize) bool
// 替换礼物前
BeforeReplaceGift(params *gift.SendGiftParam, actInfo *weactivity.Activity,
extraGiftInfo *weactivity.ExtraGiftInfo, isGuaranteeTrigger bool) *weactivity.RewardConfig
// 替换礼物保底
ReplaceGiftGuarantee(params *gift.SendGiftParam, actInfo *weactivity.Activity,
extraGiftInfo *weactivity.ExtraGiftInfo, replaceType int) *weactivity.ExtraGiftInfo
// 替换权重映射前
BeforeReplaceWeightMap(params *gift.SendGiftParam, actInfo *weactivity.Activity,
extraConfig weactivity.ExtraGift, originWeightMap map[int]int) map[int]int
// 爆出大礼物后
AfterExplodeBigGift(params *gift.SendGiftParam, actInfo *weactivity.Activity,
extraGiftInfo *weactivity.ExtraGiftInfo, giftId int, ret *gift.GiftReturnPrize) (int, bool)
// 处理完成后
AfterProcess(params *gift.SendGiftParam, actInfo *weactivity.Activity, ret *gift.GiftReturnPrize)
// 替换消息前
BeforeReplaceMessage(actInfo *weactivity.Activity, message weactivity.Message) weactivity.Message
}
使用示例
type MyExtraGiftHooker struct {
extra_gift_hooker.DefaultExtraGiftHooker
}
func (d MyExtraGiftHooker) AfterProcess(params *gift.SendGiftParam, actInfo *weactivity.Activity, ret *gift.GiftReturnPrize) {
saleConf := conf.GetSaleConfig(actInfo)
if saleConf == nil {
return
}
giftCountMap := ret.ExtraGiftReceive
recvBigGift := false
var bigGiftNames []string
for giftId, val := range giftCountMap {
if giftId == 0 {
continue
}
if val >= 1 {
recvBigGift = true
bigGiftNames = append(bigGiftNames, gift.GetGiftName(giftId))
}
}
if recvBigGift {
giftDesc, _ := actutil.FormatString(saleConf.MagTmplMap["recv_big_tmpl"], map[string]interface{}{
"NickName": user.GetUserNickname(params.RecvUid),
"GiftName": gift.GetGiftName(params.GiftId),
"RecvGiftName": strings.Join(bigGiftNames, ","),
})
ret.Desc += giftDesc
}
}
5. GiftPkgHooker - 礼包组件 Hook
礼包购买流程的扩展逻辑。
接口定义
type GiftPkgHookerI interface {
// 是否需要过滤(不展示)该礼包
NeedFilter(pkgId, uid, platform int) bool
// 获取礼包截止时间
GetDeadline(pkgId, uid int) int64
// 是否可以创建订单
CanCreateOrder(pkgId, uid, platform int) error
// 获取最大购买次数
GetMaxPurchaseTimes(pkgId, uid int) int
// 替换奖励
ReplaceRewards(pkgId, uid int, isFirstCharge bool, rewards []*actreward.RewardInfo) []*actreward.RewardInfo
// 生成额外奖励
GenerateExtraRewards(pkgId, uid, orderId, goodsId int) []*actreward.RewardInfo
// 替换礼包配置
ReplaceGiftPackConf(pkgId, uid, platform int, item *PackageItem) *PackageItem
// 获取礼包开始时间
GetPkgStartTime(pkgId, uid, pkgStartTime int) int
// 发奖后
AfterSendRewards(pkgId, uid, orderId int, rewards []*actreward.RewardInfo) []*actreward.RewardInfo
// 发奖前
BeforeSendRewards(pkgId, uid, orderId int) error
}
使用示例
type MyGiftPkgHooker struct {
gift_package.DefaultGiftPkgHooker
}
func (g *MyGiftPkgHooker) NeedFilter(pkgId, uid int, platform int) bool {
if !helper.InArrayInt(pkgId, conf.PkgList) {
return false
}
actInfo := actinfo.GetValidActByUid(conf.ActID, uid)
if actInfo == nil {
return true
}
rsp, err := service.GetChainPkgInfo(context.Background(), &actutil.CommonReq{
ActId: conf.ActID,
Uid: uid,
})
if err != nil {
return false
}
// 若上一个任务全部领完,且当前礼包还没购买,则不过滤
if rsp.CurrPkgID != pkgId {
return true
}
return false
}
func (g *MyGiftPkgHooker) CanCreateOrder(pkgId, uid int, platform int) error {
if g.NeedFilter(pkgId, uid, platform) {
return errors.New("can not create order")
}
return nil
}
6. 送礼相关 Hook
送礼相关的 Hook 不是接口方法,而是普通函数,通过 AppendSendGiftHooker 注册。
函数签名
// 通用送礼钩子签名
func HandleSendXXX(param *gift.SendGiftParam, ret *gift.GiftReturnPrize) *gift.SendGiftActivityResp
注册方式
func (a *MyActivity) GetHookers() actmodel.ActHookerList {
h := actmodel.ActHookerList{}
// 注册送礼钩子:region, giftId, handler
h.AppendSendGiftHooker(weconfig.GetServerRegion(), conf.GiftIdGiftBox, HandleSendGiftBox)
h.AppendSendGiftHooker(weconfig.GetServerRegion(), conf.BlindBoxGiftId, HandleSendBlindBoxGift)
h.AppendSendGiftHooker(weconfig.GetServerRegion(), conf.LittleGiftId, HandleSendLittleGift)
return h
}
使用示例 - 礼盒钩子
func HandleSendGiftBox(param *gift.SendGiftParam, ret *gift.GiftReturnPrize) *gift.SendGiftActivityResp {
entry := actlogger.GetLogger(context.Background()).WithFields(map[string]interface{}{
"act_id": conf.ActId,
"uid": param.SendUid,
"gift_id": param.GiftId,
"rid": param.Rid,
})
resp := &gift.SendGiftActivityResp{
AnimationIndex: -1,
Desc: "",
}
// 检查是否是目标礼盒
if param.GiftId != conf.GiftIdGiftBox {
return resp
}
entry.Infoln("HandleSendGiftBox start")
actInfo := actinfo.GetValidActByUid(conf.ActId, param.SendUid)
if actInfo == nil {
return resp
}
saleConfig := conf.GetSaleConfig(actInfo)
if saleConfig == nil {
return resp
}
// 根据爆出的奖励进行处理
for _, reward := range ret.ActualRewards {
if reward.RewardValue == int32(saleConfig.GiftBoxConfig.OriginAvatarRewardId) {
_, err := store.IncrOriginAvatarObtainTag(conf.ActId, param.RecvUid, 1)
if err != nil {
entry.WithError(err).Errorln("HandleSendGiftBox incr originAvatar err")
return resp
}
}
}
return resp
}
使用示例 - 盲盒钩子
func HandleSendBlindBoxGift(param *gift.SendGiftParam, ret *gift.GiftReturnPrize) *gift.SendGiftActivityResp {
entry := actlogger.GetLogger(context.Background()).WithFields(map[string]interface{}{
"act_id": conf.ActId,
"uid": param.SendUid,
"gift_id": param.GiftId,
})
resp := &gift.SendGiftActivityResp{
AnimationIndex: -1,
Desc: "",
}
actInfo := actinfo.GetValidActByUid(conf.ActId, param.SendUid)
if actInfo == nil {
return resp
}
saleConfig := conf.GetSaleConfig(actInfo)
if saleConfig == nil {
return resp
}
entry.Infoln("HandleSendBlindBoxGift start")
// 获取盲盒配置
blindBoxConfig := gift.GetBlindBoxConfig(int32(param.GiftId), weconfig.GetServerLanguage())
if blindBoxConfig.DiffItems.RoundDouble.BaseNumber == 0 {
return resp
}
baseNumber := blindBoxConfig.DiffItems.RoundDouble.BaseNumber
// 获取送礼次数,计算距离下次概率提升的次数
giftCount, err := collect_gift.GetGiftCount(param.SendUid, conf.ActId, param.GiftId, collect_gift.CollectTypeSend)
needCount := baseNumber - giftCount%baseNumber
comboDesc, err := actutil.FormatString(saleConfig.MagTmplMap["blind_combo_desc_tmpl"], map[string]interface{}{
"NeedCount": needCount,
})
if err != nil {
entry.WithError(err).Errorln("HandleSendBlindBoxGift FormatString err")
return resp
}
resp.ComboDesc = comboDesc
return resp
}
7. 特殊礼物 Hook(GiftBoxHook / BlindBoxHook)
这些 Hook 是针对特定礼物ID的扩展,不通过 GetHookers() 注册,而是直接调用注册函数。
GiftBoxHook - 礼盒钩子
type GiftBoxHook interface {
// 发奖前进行奖励替换
BeforeSendReward(param GiftBoxHookParam) *SpecialGiftActivity
}
// 注册方式(通常在 init() 函数中)
func init() {
gift.RegisterGiftBoxHook(conf.GiftIdGiftBox, &MyGiftBoxHook{})
}
BlindBoxHook - 盲盒钩子
type BlindBoxHook interface {
// 每次抽取前更新权重
UpdateWeightOnce(params BlindBoxHookParams) (map[int]int, int)
// 获取随机ID后
AfterGetRandId(params BlindBoxHookParams) (bool, int)
// 替换盲盒实际收到的内容
ReplaceBlindBoxActualReceive(params BlindBoxHookParams, isGuaranteeExplode []int,
repStageList []int) ReplaceBlindBoxReturn
// 替换盲盒等级
ReplaceBlindBoxLv(params BlindBoxHookParams) (bool, int)
}
// 注册方式(通常在 init() 函数中)
func init() {
gift.RegisterBlindBoxHook(conf.BlindBoxGiftId, &MyBlindBoxHook{})
}
DefaultHooker 默认实现
所有 Hooker 接口都提供了 Default 实现,方法体为空或返回默认值。活动通过嵌入 Default 结构体,只需实现需要扩展的方法。
type MyLotteryHooker struct {
lottery.DefaultLotteryHooker // 嵌入默认实现
}
// 只需实现需要的方法
func (h *MyLotteryHooker) BeforeSendRewards(lotteryParams lottery.LotteryHookParams, res []*actreward.RewardInfo) []*actreward.RewardInfo {
// 自定义逻辑
return res
}
常见注意事项
- Hook 是同步执行的:不要在 Hook 中执行耗时操作(如网络请求),否则会阻塞主流程
- 注意作用域:Hook 只影响当前活动,不影响其他活动
- 错误处理:Hook 中的 panic 会影响主流程,务必做好错误处理
- 不要直接修改参数:需要修改时先 Copy,避免影响其他 Hook
- 注册时机:所有 Hook 在
GetHookers()中一次性注册,不支持动态注册
典型使用场景
场景 1:抽奖限额控制
需求:活动抽奖每日限额100个稀有碎片
实现:
func (h *MyLotteryHooker) BeforeSendRewards(lotteryParams lottery.LotteryHookParams, res []*actreward.RewardInfo) []*actreward.RewardInfo {
for _, reward := range res {
if reward.RewardId == conf.RareChipId {
todayCount := getTodayChipCount(lotteryParams.Req.Uid, conf.RareChipId)
if todayCount >= 100 {
return []*actreward.RewardInfo{
{RewardType: actreward.RewardTypeNone, RewardId: 0, RewardVal: 1},
}
}
}
}
return res
}
场景 2:任务设备限制
需求:连锁礼包任务同一设备最多3个用户领取
实现:
func (h *MyTaskHooker) CanReceiveReward(actInfo *weactivity.Activity, task *weactivity.Task,
taskInfo *task_store.TaskInfo, uid, stageId int) (bool, error) {
deviceId := user.GetActiveDeviceId(int32(uid))
uids, _ := store.GetDeviceUids(conf.ActID, deviceId)
if len(uids) >= 3 && !helper.InArrayInt(uid, uids) {
return false, acterror.NewWithMsg(500, "设备超限")
}
return true, nil
}
场景 3:礼包过滤
需求:连锁礼包按顺序解锁,上一个未购买不展示下一个
实现:
func (g *MyGiftPkgHooker) NeedFilter(pkgId, uid int, platform int) bool {
rsp, _ := service.GetChainPkgInfo(context.Background(), &actutil.CommonReq{
ActId: conf.ActID,
Uid: uid,
})
// 当前可购买礼包不是该礼包,则过滤
if rsp.CurrPkgID != pkgId {
return true
}
return false
}
场景 4:榜单结算后处理
需求:榜单结算后发送额外奖励
实现:
func (h *MyRankHooker) AfterCheckoutReward(actInfo *weactivity.Activity, rank int, rankConfig *weactivity.Rank,
member string, memberList []int, score int, t time.Time) {
if rank <= 10 {
// 前10名额外发放特殊称号
extraRewards := []*actreward.RewardInfo{
{
RewardType: actreward.RewardTypeTitle,
RewardId: conf.TopRankerTitleId,
RewardVal: 30 * 24 * 3600, // 30天
},
}
for _, uid := range memberList {
actreward.SendRewards(uid, extraRewards, &actreward.ExtraInfo{
ActId: conf.ActID,
PropChangeType: def.PropChangeTypeActivityRank,
})
}
}
}
场景 5:送礼动效控制
需求:送礼盒时根据爆出的奖励展示不同动效
实现:
func HandleSendGiftBox(param *gift.SendGiftParam, ret *gift.GiftReturnPrize) *gift.SendGiftActivityResp {
resp := &gift.SendGiftActivityResp{
AnimationIndex: -1,
}
// 根据爆出奖励设置动效索引
for _, reward := range ret.ActualRewards {
if reward.RewardLevel == 3 {
resp.AnimationIndex = 2 // 稀有奖励动效
break
} else if reward.RewardLevel == 2 {
resp.AnimationIndex = 1 // 普通奖励动效
}
}
return resp
}
Hook 开发最佳实践
- 最小化实现:只实现需要的 Hook 方法,其他保持默认
- 日志记录:Hook 入口处打印日志,方便追踪和调试
- 性能优先:Hook 是同步调用,避免耗时操作
- 错误容错:Hook 失败不应影响主流程,做好错误处理
- 数据隔离:不同活动的 Hook 不要相互影响
- 测试覆盖:Hook 逻辑务必充分测试,特别是边界情况