skills/mercury-api.wepieoa.com/activity-framework

activity-framework

SKILL.md

活动基础框架开发指南

帮助你理解和使用活动系统框架,快速创建和开发新活动。


🤖 AI 执行指令

当用户调用此 skill 时,根据用户需求提供活动框架相关的指导和代码示例。

使用场景判断

  • 创建新活动 → 提供完整的活动创建流程和模板代码
  • 理解架构 → 解释框架分层和核心概念
  • 实现功能 → 提供具体的 API 用法和代码示例
  • 排查问题 → 结合生命周期和初始化流程分析问题

框架总览

活动系统基于 Go + Gin + Redis 的插件化框架。核心思路:活动通过实现 ActModel 接口接入框架,通过 Widget 插件系统复用通用功能,通过 Hook 机制注入自定义行为,通过事件系统实现异步解耦。

架构分层

┌─────────────────────────────────────────┐
│              路由层 (Gin Router)          │
├─────────────────────────────────────────┤
│   Widget路由 (40+)  │  活动自定义路由 (150+) │
├─────────────────────────────────────────┤
│              Service 业务逻辑层           │
├──────────┬──────────┬───────────────────┤
│ Hook系统  │ 事件系统  │ 定时任务系统        │
├──────────┴──────────┴───────────────────┤
│         Redis 存储层 (actstore/actredis)  │
├─────────────────────────────────────────┤
│         通用基础设施 (common/)             │
│   错误处理 · 奖励 · 追踪 · 消息 · i18n    │
└─────────────────────────────────────────┘

ActModel 核心接口

每个活动必须实现 ActModel 接口(/common/actmodel/actmodel.go):

type ActModel interface {
    GetActInfo() *weactivity.Activity   // 活动元数据
    GetActId() int                      // 唯一活动ID
    GetRouter() Router                  // API端点
    GetEvents() []Event                 // 事件处理器
    GetDeferTasks() []DeferTask         // 延迟任务
    GetNotifyEvents() []NotifyEvent     // 通知事件
    GetCronTasks() []*cronext.ActJob    // 定时任务
    GetHookers() ActHookerList          // Hook扩展
    GetRedisCmds() map[int][]RedisCmd   // Redis命令注册
    GetRegions() []string               // 部署区域
    NeedCheckAct() bool                 // 是否校验配置
}

默认实现

基类 特点
DefaultModel 空实现,所有方法返回零值,适合简单活动
DefaultModelV2 继承 DefaultModel,NeedCheckAct() 返回 true(配置不存在时跳过注册)

活动创建标准流程

步骤 1:定义配置

文件:/{year}/{activity}/conf/conf.go

const ActId = 1001  // 唯一活动ID

type Config struct {
    // 活动特有配置字段
}

func GetConfig() *Config {
    // 通过 ParseSpecialInfo 从 weconfig 加载
}

步骤 2:注册活动

文件:/{year}/{activity}/register/register.go

func init() {
    registry.Register(&MyActivity{})
}

type MyActivity struct {
    actmodel.DefaultModelV2
}

func (a *MyActivity) GetActId() int { return conf.ActId }
func (a *MyActivity) GetActInfo() *weactivity.Activity { ... }
func (a *MyActivity) GetRouter() actmodel.Router { ... }
func (a *MyActivity) GetEvents() []actmodel.Event { ... }
func (a *MyActivity) GetRegions() []string { return []string{"C"} }

步骤 3:设置路由

文件:/{year}/{activity}/route/route.go

func SetupRouter(r *gin.RouterGroup) {
    r.GET("/info", handler.GetInfo)
    r.POST("/claim", handler.ClaimReward)
}

步骤 4:注册事件和任务

GetEvents() / GetCronTasks() 中声明式注册。


活动生命周期

状态流转

状态 条件 可见性
未开始 当前时间 < StartTime 仅白名单用户可见
进行中 StartTime ≤ 当前时间 ≤ EndTime 所有符合条件用户
已结束 当前时间 > EndTime 已归档,数据仍可访问

核心访问函数

函数 用途 时间限制
GetActById(actId) 获取活动信息
GetValidActById(actId) 获取进行中活动 仅进行中
GetValidActByUid(actId, uid) 获取用户可参与的活动 仅进行中 + 参与校验
GetValidActByUidWithExtend() 带延长时间的获取 仅进行中(含延长)
CanParticipateAct(actId, uid) 用户参与检查 仅进行中

参与检查流程

  1. 检查活动是否在时间窗口内
  2. 调用 RegisterIsParticipateUidFunc(actId, fn) 自定义检查
  3. 检查 actCommonFilterList 全局过滤器
  4. 返回过滤后的活动

路由系统

路由类型

类型 说明
RouterTypeNormal 0 需登录的普通端点
RouterTypeInner 1 内部/管理员端点
RouterTypeNoLogin 2 无需登录的公开端点

主路由初始化顺序

router.SetupRouter(r)
├── registry.RegisterActivity()    // 初始化活动注册
├── hook.RegisterAllFunc()          // 初始化所有 Hook
├── setupRouter(r)                  // 设置普通端点(Widget + 活动路由)
├── setupInnerRouter(r)             // 设置内部端点
├── setupNoLoginRouter(r)           // 设置公开端点
├── event.RegisterEvent()           // 注册事件处理
├── event.RegisterRiskEvent()       // 注册风控事件
└── event.RegisterDeferTasks()      // 注册延迟任务

注册机制

活动注册文件 /registry/act_model_register/register.goregister.sh 脚本自动生成,通过 Go 的 init() 副作用导入实现自动注册(150+ 活动)。


通用基础设施 (common/)

请求/响应

// 标准请求
type CommonReq struct { ActId, Uid int; Lang string }

// 标准响应
actutil.SuccessResponse(c, data)
actutil.BadParamsResponse(c, msg)
actutil.ServerErrorResponse(c, msg, data)
actutil.GinResponseWithError(c, err, data)  // 自动处理 CustomError

错误处理

// 自定义错误(支持 i18n)
err := acterror.NewCustomError(actmsg.KeyXXX)
err := acterror.NewErrorWithLang(key, lang)

// 预定义错误
acterror.GetDefaultErr()
acterror.GetCoinNotEnoughErr()
acterror.GetChipNotEnoughErr()

奖励系统

rewards := actreward.GenerateRewards(uid, genderId, config)
err := actreward.SendRewards(uid, rewards, &actreward.ExtraInfo{
    ActId: actId, ActName: name,
    PropChangeType: def.PropChangeTypeActivityTask,
    ChipSource: "widget_name",
})

消息系统

支持卡片消息、文本消息、背景卡片,通过 NPC 或私聊发送,自带深链接和消息后缀(活动链接、背包、商店、房间等)。

时间周期

周期类型 说明
PeriodTypeTotal 活动整个周期
PeriodTypeDaily 每日
PeriodTypeNatureWeekly 自然周
PeriodTypeMonthly 每月
PeriodTypeHourly 每小时

多区域部署

每个活动通过 GetRegions() 指定部署区域(如 "C" = 中国, "A" = 东南亚),registry.GetAllActModels() 根据当前服务器区域自动过滤。


应用初始化流程

main()
├── db.ConnectRedis()            // 连接 Redis
├── db.ConnectDB()               // 连接数据库
├── db.ConnectMongoDB()          // 连接 MongoDB
├── cache.InitMemoryCache()      // 初始化内存缓存
├── wevent.Init()                // 初始化全局事件
└── setupActivityRouter()
    ├── r := gin.New()
    ├── r.Use(middleware...)      // 全局中间件
    └── router.SetupRouter(r)
        ├── registry.RegisterActivity()
        ├── hook.RegisterAllFunc()
        ├── setupRouter(r)        // Widget + 活动路由
        ├── event.RegisterEvent()
        └── event.RegisterDeferTasks()

关键文件速查

文件路径 用途
/common/actmodel/actmodel.go ActModel 接口定义
/registry/registry.go 活动注册与查找
/router/router.go 主路由编排
/common/actinfo/act.go 活动元数据与生命周期
/common/actstore/ 存储层高级抽象
/common/actredis/ Redis 客户端框架
/common/actevent/ 事件系统
/hook/register.go Hook 注册入口
/event/register.go 事件注册中心
/{year}/{activity}/register/ 活动注册
/{year}/{activity}/conf/ 活动配置
/{year}/{activity}/route/ 活动路由

典型使用场景

场景 1:创建新活动

/activity-framework 我要创建一个新的春节活动

我会提供完整的项目结构、注册代码模板和配置示例。

场景 2:理解初始化流程

/activity-framework 活动是怎么被加载的?

我会解释从 main() 到活动注册的完整初始化链路。

场景 3:使用基础设施

/activity-framework 怎么发送奖励给用户?

我会提供 actreward 的使用方法和完整代码示例。

Installs
1
First Seen
Apr 16, 2026