大家好,我是你们的源码拆解老伙伴-腻害兔。上一篇我们啃完了商城的「商品与交易」模块——50+ 张表、责任链价格引擎、Handler 策略模式、售后状态机,信息量已经够大了。

但说实话,那只是电商的"骨架"。今天这篇才是"血肉"——营销玩法和数据分析。一个电商平台能不能让用户掏钱,靠的不是商品列表有多好看,而是优惠券怎么发、秒杀怎么抢、拼团怎么裂变、数据看板怎么指导运营决策。

废话不多说,直接上干货。


一、今日模块概览

一句话概括:营销模块(promotion)负责"让用户花钱",统计模块(statistics)负责"告诉老板赚了多少"。

这两个模块在整个商城体系中的定位非常清晰:

  • 营销模块 = 7 种促销玩法 + 内容运营(文章/轮播)+ 页面装修(DIY)。它是商城的"增长引擎"。
  • 统计模块 = 交易统计 + 商品统计 + 会员统计 + 支付统计。它是商城的"驾驶舱仪表盘"。

划重点!!! 营销模块有 17+ 张表、7 种独立的促销活动类型,每种类型都有完整的"创建→活动→核销→关闭"生命周期。统计模块虽然只有 2 张聚合表,但背后是 T+1 定时聚合 + 实时查询的混合架构。这两个模块加在一起,基本覆盖了一个中小型电商平台的全部运营需求。


二、技术选型分析

2.1 营销模块的技术决策

技术点选型理由替代方案
活动库存管理活动级 + 商品级双级库存活动总库存控制整体,商品库存控制 SKU 粒度单级库存(不够灵活)
并发安全乐观锁(where 条件校验)+ 原子更新适合读多写少场景,避免分布式锁的复杂度Redis 分布式锁(重)、数据库行锁(性能差)
活动冲突校验同 SPU 不允许同时参与多个同类型活动防止价格叠加导致亏损允许叠加(需要复杂的价格计算引擎)
商品更新模式Diff-based(对比新旧列表,增删改)只变更差异部分,减少数据库操作全量删除再插入(有闪断风险)
批量操作容错逐条 try-catch + 错误隔离一条失败不影响整批全有或全无(一批错全部回滚)

2.2 统计模块的技术决策

技术点选型理由替代方案
数据聚合方式T+1 定时任务(Quartz)数据量大时避免实时聚合拖垮主库实时聚合(性能差)、ClickHouse(架构重)
聚合表设计按天 + 按 SPU 的物化视图思路查询直接读聚合表,秒级响应每次从订单表 GROUP BY(慢)
对比分析当期 vs 上期双时间段查询运营最关心的"环比"需求只查当期(信息不够)
会员漏斗实时从源表计算会员数据量相对小,实时可接受也做 T+1(数据不够实时)

踩坑提醒:很多电商项目在做统计时,一开始用实时聚合,订单量上来后查询越来越慢,最后不得不重构。RuoYi 的做法是一开始就用 T+1 聚合表,虽然牺牲了一点实时性,但保证了查询性能。对于绝大多数电商场景,T+1 的统计数据完全够用——老板看的是趋势,不是秒级精度。


三、需求溯源推演

3.1 营销模块的需求还原

想象一下这个场景:你是一个电商平台的运营负责人,老板跟你说"我们要搞促销"。

"搞促销"三个字背后,其实藏着至少 7 种完全不同的需求:

需求一:发优惠券

"新用户注册送 50 元券,满 200 可用。老用户每周可以领一张 9 折券。"

这就是 promotion_coupon_template + promotion_coupon 的模板-实例模型。模板定义规则(面额、门槛、有效期、领取限制),实例是用户手里的那张券。

需求二:限时秒杀

"明天上午 10 点,iPhone 15 原价 5999,秒杀价 3999,限量 100 台。"

这就是 promotion_seckill_activity + promotion_seckill_config(时段配置)+ promotion_seckill_product 的三层结构。时段配置支持一天多个秒杀场次(10 点场、14 点场、20 点场),每个场次有不同的商品和价格。

需求三:拼团

"3 人成团,每人 99 元买原价 199 的商品。24 小时内不成团自动退款。"

这就是 promotion_combination_activity + promotion_combination_record。关键设计是"团长"和"团员"的关系——团长创建记录(headId = 0),团员加入时继承团长的 headId,凑够人数后全部标记成功。

需求四:砍价

"邀请 5 个好友帮忙砍价,每人砍 10-50 元随机,砍到最低价就能买。"

这就是 promotion_bargain_activity + promotion_bargain_record + promotion_bargain_help。最精妙的设计是砍价金额的随机性(randomMinPrice ~ randomMaxPrice)和并发安全——用乐观锁防止多人同时砍价时价格计算出错。

需求五:限时折扣

"本周全场 8 折,部分爆款 5 折。"

这就是 promotion_discount_activity + promotion_discount_product。相对最简单的一种,但要注意和秒杀、拼团的价格冲突。

需求六:满减/满送

"满 300 减 50,满 500 送赠品。"

这就是 promotion_reward_activity,用 JSON 存储阶梯规则。

需求七:积分兑换

"500 积分 + 9.9 元换购商品。"

这就是 promotion_point_product,积分和现金的混合支付模式。

看到这里你可能会有一个疑问:为什么每种活动都要单独一套表,而不是用一张通用的 promotion_activity 表加类型字段?

答案是:每种活动的数据结构差异太大了。秒杀有时段配置,拼团有团长/团员关系,砍价有帮砍记录,优惠券有模板/实例两层……如果硬塞进一张表,会有大量 NULL 字段,查询和关联都会变得极其复杂。分表设计虽然表多,但每种活动的逻辑是独立清晰的。

3.2 统计模块的需求还原

统计模块的需求更直接——老板和运营每天要看数据:

需求一:今天卖了多少?

交易概览:昨日订单数、支付金额、退款金额,对比前天的环比变化。

需求二:哪些商品卖得好?

商品排行:按销量/销售额/浏览量排序,找出爆款和滞销款。

需求三:用户从哪来?

会员漏斗:访客数 → 下单用户数 → 支付用户数 → 客单价,看哪个环节流失最多。

需求四:钱从哪来?

支付统计:微信支付、支付宝、余额支付各占多少比例。


四、竞品对标分析

对比维度RuoYi-Vue-Pro 商城mall4jCRMEBJeecgBoot 商城
营销玩法数量7 种(券/秒杀/拼团/砍价/折扣/满减/积分)5 种(券/秒杀/拼团/砍价/积分)6 种(券/秒杀/拼团/砍价/折扣/积分)3 种(券/秒杀/拼团)
优惠券模型模板→实例两层,支持固定面额/百分比/满减类似两层模型类似两层模型单层模型
秒杀库存方案双级库存 + 乐观锁Redis 预扣 + 异步落库Redis 分布式锁数据库行锁
拼团虚拟成团支持(virtual_group 字段)不支持支持不支持
砍价并发安全乐观锁(whereBargainPrice)数据库行锁Redis 锁无特殊处理
统计架构T+1 聚合表 + 实时查询混合实时聚合T+1 聚合实时聚合
数据对比分析当期 vs 上期双时间段仅当期当期 vs 上期仅当期
会员漏斗访客→下单→支付→客单价
页面装修DIY 模板 + 页面 JSON 配置

RuoYi 的优势

  1. 营销玩法最全:7 种玩法覆盖了电商最常见的促销场景,而且每种都有完整的生命周期管理。
  2. 拼团虚拟成团:这个功能在很多商业系统中都是付费功能。当拼团快到期但人数不够时,自动用虚拟用户补齐,保证成团率。这对运营来说非常实用。
  3. 统计模块设计成熟:T+1 聚合 + 实时查询的混合架构,既有性能保证又有数据完整性。对比分析功能(昨日 vs 前天、本月 vs 上月)是运营最需要的。
  4. 页面装修系统:promotion_diy_template + promotion_diy_page 的模板-实例模式,让运营可以自定义首页布局,不需要开发介入。

RuoYi 的劣势

  1. 缺少营销活动的 A/B 测试:无法对同一商品设置不同的促销策略做效果对比。
  2. 缺少用户标签/人群定向:优惠券发放是"所有人都能领"或"新用户才能领",缺少基于用户行为标签的精准发放。
  3. 统计维度有限:缺少渠道来源分析(用户从哪个渠道来的)、复购率分析、RFM 用户分层等高级分析能力。

五、核心业务流程

5.1 优惠券领取与使用流程

5.2 秒杀库存防超卖流程

关键设计:UPDATE ... WHERE stock > 0 这条 SQL 是防超卖的核心。它利用数据库的行锁机制,确保即使 100 个人同时抢 1 件商品,也只有 1 个人能成功。比 Redis 分布式锁更简单、更可靠。

5.3 拼团成团流程

5.4 T+1 统计聚合流程


六、数据模型解读

6.1 营销模块的表结构

营销模块的表可以分为 4 大类:

第一类:活动主表 + 商品关联表(6 组)

每种促销活动都是"活动头 + 活动商品"的两层结构:

活动类型活动主表商品关联表核心字段
秒杀promotion_seckill_activitypromotion_seckill_product时段配置 ID 列表、秒杀价、库存
拼团promotion_combination_activitypromotion_combination_product成团人数、虚拟团、限制时长
砍价promotion_bargain_activity—(直接关联 SPU/SKU)起始价、最低价、帮砍次数
限时折扣promotion_discount_activitypromotion_discount_product折扣类型(百分比/固定价)
满减满送promotion_reward_activity—(规则存 JSON)条件类型、阶梯规则
积分兑换promotion_point_activitypromotion_point_product积分数量 + 现金价格

设计亮点:商品关联表中冗余了活动的 name、status、startTime、endTime 字段。这样在查询商品列表时,不需要 JOIN 活动主表就能知道商品当前参与了什么活动。这是典型的读优化设计——用冗余换查询性能。

第二类:优惠券模板 + 实例(2 张表)

promotion_coupon_template 定义规则:面额、门槛、有效期类型(固定日期/领取后 N 天)、领取限制、总库存。

promotion_coupon 是用户手里的券:关联模板 ID、用户 ID、状态(未使用/已使用/已过期)、使用订单 ID。

这是一个经典的类-对象模型——模板是"类",券实例是"对象"。

第三类:参与记录表(3 张表)

用途关键字段
promotion_combination_record拼团参与记录head_id(团长 ID)、user_size、status、expire_time
promotion_bargain_record砍价记录bargain_price(当前砍到价)、status、order_id
promotion_bargain_help帮砍记录record_id、user_id、reduce_price(砍掉金额)

第四类:内容运营表(4 张表)

用途
promotion_article_category文章分类
promotion_article文章(可关联 SPU,支持热门/轮播推荐)
promotion_diy_template页面装修模板(JSON 存储布局)
promotion_diy_page装修页面实例

6.2 统计模块的表结构

统计模块只有 2 张聚合表,但设计很讲究:

trade_statistics——交易日报表

每天一行,记录当天的核心交易指标:

字段含义
time统计日期
order_create_count创建订单数
order_pay_count支付订单数
order_pay_price支付总金额
after_sale_count售后单数
after_sale_refund_price退款总金额
brokerage_settlement_price佣金结算金额
wallet_pay_price余额支付金额
recharge_pay_count/price充值笔数/金额
recharge_refund_count/price充值退款笔数/金额

product_statistics——商品日报表

每个 SPU 每天一行,记录商品维度的指标:

字段含义
time统计日期
spu_id商品 ID
browse_count / browse_user_count浏览量 / 浏览人数
favorite_count收藏数
cart_count加购数
order_count / order_pay_count / order_pay_price订单数 / 支付数 / 支付金额
browse_convert_percent浏览→购买转化率

为什么 product_statistics 要按 SPU 粒度? 因为运营最关心的问题是"哪个商品卖得好"。按 SPU 聚合后,可以直接做商品排行(rank-page 接口),按销量、销售额、浏览量任意排序。


七、产品设计亮点与槽点

7.1 让我眼前一亮的设计

亮点一:拼团虚拟成团

// 过期未成团时的处理逻辑
if (activity.getVirtualGroup()) {
    // 虚拟团:自动补齐虚拟用户,标记成团成功
    handleVirtualGroupRecord(record);
} else {
    // 真实团:标记失败,取消订单退款
    handleExpireRecord(record);
}

这个设计太懂运营了。拼团活动最怕的就是"差一个人成不了团",用户体验差,转化率也低。虚拟成团功能让运营可以设置"即使人数不够也自动成团",保证活动效果。这在拼多多等主流电商平台上是标配功能,但在开源项目中很少见。

亮点二:砍价的乐观锁设计

// BargainRecordServiceImpl.updateBargainRecordBargainPrice()
// 使用乐观锁:只有当前价格匹配时才更新成功
baseMapper.updateByIdAndBargainPrice(record.getId(), record.getBargainPrice(), newPrice);

多人同时帮同一个人砍价时,如果不用锁,会出现"价格计算错误"的问题。RuoYi 没有用重量级的分布式锁,而是用数据库的乐观锁(WHERE bargain_price = 期望值)来解决。简单、高效、不会死锁。

亮点三:Diff-based 商品更新

所有活动类型的商品更新都用了同一个模式:

// 对比新旧商品列表,生成三个操作列表
List<XXXProductDO> createList = ...;  // 新增的商品
List<XXXProductDO> updateList = ...;  // 修改的商品
List<XXXProductDO> deleteList = ...;  // 删除的商品
// 批量执行增删改

这个模式避免了"全量删除再插入"导致的短暂数据缺失问题,也减少了不必要的数据库操作。

亮点四:统计模块的幂等设计

// TradeStatisticsServiceImpl.statisticsTrade()
// 先检查该日期是否已有数据
if (existsByTime(date)) {
    return;  // 幂等保护,重复执行不会重复插入
}

T+1 统计任务如果因为某种原因重复执行(比如 Quartz 集群中的多节点同时触发),幂等设计确保不会产生重复数据。

7.2 可以改进的地方

槽点一:活动冲突只校验了同类型

目前每种活动只校验"同 SPU 不能同时参与多个同类型活动",但没有校验跨类型冲突。比如一个商品同时参与秒杀(5 折)和限时折扣(8 折),用户到底按哪个价格买?虽然价格引擎有优先级处理,但在活动创建时就应该给运营一个明确的提示。

槽点二:优惠券缺少人群定向

目前的优惠券领取规则只有"新用户"和"所有人"两种,缺少基于用户标签(消费金额、购买频次、最后登录时间等)的精准发放。这对于精细化运营来说是个短板。

槽点三:统计缺少渠道分析

目前的统计维度只有时间、商品、会员属性(性别/地区/终端),缺少渠道来源分析。如果平台同时有小程序、H5、APP 多个入口,运营无法知道哪个渠道的转化率最高。


八、发散性思考

8.1 营销模块还能做什么?

方向一:营销活动模板化

目前每种活动都需要运营手动配置。可以做一个"营销活动模板"功能——运营选择"618 大促模板",系统自动创建一组关联活动(满减 + 优惠券 + 秒杀 + 拼团),一键启动。

方向二:智能定价

基于历史销售数据和库存情况,自动建议促销价格。比如"该商品过去 30 天日均销量 10 件,当前库存 500 件,建议设置 7 折促销,预计 15 天清仓"。

方向三:社交裂变增强

砍价和拼团是基础的社交裂变。可以进一步加入"分享得积分"、"邀请好友注册双方得券"、"老带新专属价"等更丰富的裂变玩法。

8.2 统计模块还能做什么?

方向一:实时大屏

目前的 T+1 统计适合日常运营报表。可以加一个"实时交易大屏"模块,用 WebSocket 推送实时订单数据,适合大促期间监控。

方向二:RFM 用户分层

基于用户的最近消费时间(Recency)、消费频率(Frequency)、消费金额(Monetary),自动将用户分为"重要价值用户"、"重要发展用户"、"重要挽留用户"等 8 类,指导差异化营销。

方向三:预测分析

基于历史数据预测未来趋势——下个月预计销售额、哪些商品可能缺货、哪些用户可能流失。这需要引入简单的机器学习模型,但对于有一定数据积累的平台来说非常有价值。

8.3 设计思路的迁移价值

营销模块的"活动生命周期管理"模式(创建→启用→运行→关闭→删除)可以迁移到任何需要"限时活动"的场景,比如:在线教育平台的限时课程、SaaS 产品的限时免费试用、线下门店的限时促销。

统计模块的"T+1 聚合 + 实时查询混合架构"可以迁移到任何需要数据分析的业务系统,比如:内容平台的文章阅读统计、SaaS 平台的功能使用统计、IoT 平台的设备数据采集。


九、关键代码导读

9.1 CouponServiceImpl.java

路径:yudao-module-mall/yudao-module-promotion/src/main/java/.../service/coupon/CouponServiceImpl.java

为什么值得读:优惠券是电商营销的核心。这个类展示了完整的券生命周期管理——领取(takeCoupon)、使用(useCoupon)、退还(returnUsedCoupon)、过期(expireCoupon)、作废(invalidateCoupon)。特别值得关注的是 invalidateCoupon 使用 Propagation.REQUIRES_NEW 确保每条券的作废操作独立事务,不会被外层事务回滚影响。

9.2 CombinationRecordServiceImpl.java

路径:yudao-module-mall/yudao-module-promotion/src/main/java/.../service/combination/CombinationRecordServiceImpl.java

为什么值得读:这是整个营销模块最复杂的 Service。拼团的"团长-团员"关系管理、成团判断、过期处理、虚拟成团、微信订阅消息推送,全部集中在这个类里。理解了这个类,就理解了拼团业务的完整链路。

9.3 BargainRecordServiceImpl.java

路径:yudao-module-mall/yudao-module-promotion/src/main/java/.../service/bargain/BargainRecordServiceImpl.java

为什么值得读:砍价模块的并发安全设计是亮点。updateBargainRecordBargainPrice 方法使用乐观锁(whereBargainPrice 条件)来防止多人同时砍价时的价格计算错误。这是一个非常实用的并发控制案例。

9.4 TradeStatisticsServiceImpl.java

路径:yudao-module-mall/yudao-module-statistics/src/main/java/.../service/trade/TradeStatisticsServiceImpl.java

为什么值得读:展示了 T+1 统计聚合的完整实现——从订单表、售后表、佣金表、充值表中分别聚合数据,合并成一行写入 trade_statistics 表。幂等设计、多租户支持(@TenantJob)都值得学习。

9.5 DataComparisonRespVO.java

路径:yudao-module-mall/yudao-module-statistics/src/main/java/.../controller/admin/vo/DataComparisonRespVO.java

为什么值得读:这个 VO 定义了统计模块的核心响应结构——"当期数据 vs 对比期数据"的双值模式。所有统计接口都返回这个结构,前端可以直接做环比展示。设计简洁、复用性高。


总结

RuoYi-Vue-Pro 的商城营销与统计模块是我分析至今信息量最大的模块之一——17+ 张表、7 种营销玩法、T+1 聚合架构、防超卖设计、虚拟成团……几乎每一层都有值得细品的设计。

如果要用一句话评价:这是一个"功能完整度拉满、架构设计及格偏上"的电商营销方案。 它不是最优雅的(缺少 DDD 分层),但它可能是开源 Java 电商方案中"开箱即用"能力最强的。对于想快速搭建电商系统的团队来说,芋道商城是一个非常好的起点——前提是你得先把它读懂。

好了,今天的拆解就到这里。觉得有用的话,点个赞支持一下呗~ 下一篇我们继续商城系列,拆解会员模块——会员等级怎么设计?积分体系怎么做?余额充值和分销佣金怎么流转?敬请期待!

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐