作为C端研发,你一定听过这句话:“我们要搞个新活动,逻辑很简单:用户签到7天送个券,邀请3个人再抽次奖,明天就要。”
逻辑确实简单。在项目上线的前一周,我的做法是:加字段、写 if-else、开新接口。
但当运营从竞品公司挖来,玩法从单一的“签到”进化到“充值返利+抽奖保底+任务进度+等级衰减”的组合拳时,系统崩溃了。
- 代码腐化:
ActivityController 塞满了各种活动的适配逻辑。
- 扩展撞墙:改一个老活动的Bug,可能导致三个新活动领不了奖。
- 发布低效:哪怕只是改个文案或奖励数量,也得走一套完整的研发测试上线流程。
我意识到,不能再当“活动的搬运工”了。我需要一套能让运营自己“拼积木”的中台。
活动系统的“第一性原理”
剥开花哨的UI,所有的C端活动本质上都在描述同一件事:
在什么时间(生命周期),针对谁(人群投放),在做了什么(事件驱动)后,判断是否达标(规则判定),最后给点什么(奖励发放)。
基于此,我将系统拆解为三个核心层:事件层(Event)、玩法层(Play)、活动层(Activity)。

如何实现“解耦”与“复用”
第一层:事件驱动(解决“感知”问题)
过去,活动逻辑侵入业务代码(如:在充值逻辑里直接调用活动接口)。现在,我们通过行为标准化实现彻底解耦。
- 设计逻辑:业务系统只负责上报
action_key(如 recharge.success)。
- 语义转换:活动系统通过
activity_event_action 表将行为解释为“任务类”或“签到类”事件。
- 价值:即使玩法变了,业务方的代码一行都不用动。
第二层:玩法建模(解决“逻辑”问题)
这是中台最厚的一层。我们将玩法抽象为 “模板(Template)” 与 “实例(Instance)”。
- 玩法模板:定义DNA。例如“连续签到”是一个模板,它规定了必须按天推进。
- 玩法实例:定义表现。活动A引用“连续签到”模板,配置为3天送金币;活动B引用同一模板,配置为7天送钻石。
- 关键数据结构:
activity_play_instance:活动与玩法的“粘合剂”。
rewards_json:将奖励配置结构化,支持金币、道具、甚至实物。
第三层:版本化投放(解决“安全”问题)
活动系统最怕“在线改配置导致崩盘”。我们引入了发布版本(Release) 的概念。
- 不可变性:运行中的活动配置是不允许修改的。
- 版本演进:修改规则需发布新版本,通过
version_code 进行灰度切换或一键回滚。

运行链路
为了直观理解,我们来看一个请求是如何穿透系统的:
- 事件上报:用户充值成功,业务系统抛出
recharge.success。
- 分发匹配:事件引擎发现有3个正在运行的活动关注该行为。
- 进度推进:
- 活动A(充值任务):进度从 $0$ 变为 $100$。
- 活动B(累充抽奖):增加一次抽奖机会。
- 规则结算:任务模块判断进度达标,调用奖励中心。
- 幂等发奖:奖励中心通过
(userId + activityId + ruleId) 唯一索引,确保奖品只发一次。

避坑指南
在落地过程中,我也踩过不少坑,分享几个核心Tips:
- 警惕“知识的诅咒”:研发觉得很简单的“玩法挂载”,运营可能看不懂。后台页面一定要 “心智统一” ,签到和任务的操作流向要一致。
- 防御性设计:所有的发奖必须做 强幂等。永远不要相信客户端上报的进度,所有的逻辑判断必须在服务端基于事件流水重算。
- 性能瓶颈:高频活动(如大促)下,事件处理建议走 MQ异步消费,避免活动系统的波动拖累核心交易链路。这种对高并发场景的优化思路,也是构建稳定 后端 & 架构 的常见策略。
重构完成后,我们最快的一次活动上线只用了 15分钟——运营在后台选好模板、填好奖励、设定时间,直接点发布。
中台的本质,是把重复的劳动沉淀为资产。
现在的活动系统不仅是一个工具,它更像是一个“玩法实验室”,让运营可以低成本地去尝试各种组合。通过 事件驱动架构 实现了业务与活动的解耦,而我们研发,终于可以把精力放回到更有挑战的架构演进上了。

这次从零到一构建可扩展活动中台的经历,让我对复杂业务系统的抽象与设计有了更深的理解。如果你也在为臃肿的活动代码发愁,不妨尝试用“事件-玩法-活动”的视角重新审视你的系统。更多关于系统设计与重构的深度讨论,欢迎来 云栈社区 交流分享。
|