分发优惠卷

采用了缓存抗并发、然后扣减缓存成功的请求可以进行扣减数据库,并将优惠券添加到用户的领券记录中, Redis 的 ZSet 来缓存用户的领券记录,并将领券时间作为 Score 值,这样用户在查询时可以按时间倒序显示领取记录。

Posted by Zheng Yang on September 12, 2024

##

优惠券分发逻辑

1. 验证前置逻辑

其代码逻辑如下:

消费者开始处理消息

记录接收到的消息内容

检查优惠券任务状态是否为”执行中”

如果不是执行中,则终止推送并记录警告

如果是执行中,则检查优惠券模板状态

如果优惠券模板不是激活状态,则记录错误并返回

如果优惠券模板是激活状态,则创建Excel读取监听器

使用EasyExcel读取Excel文件并执行推送任务

处理结束

这个流程展示了代码中整个优惠券推送任务的执行过程。

2. EasyExcel 读取分发用户列表

其代码逻辑如下:

逐行读取Excel文件中的用户信息。

为每个用户分发优惠券,同时确保库存正确(Redis和数据库双重检查)。

添加用户领券记录到数据库,添加优惠券到用户已领取的 Redis 优惠券列表中

处理可能的异常情况(如库存不足,重复领取等)。

任务完成后更新任务状态。

扣减库存逻辑的话同样是采用乐观锁机制保障:

<!-- 通过乐观机制原子扣减优惠券模板库存 -->
<update id="decrementCouponTemplateStock">
    UPDATE t_coupon_template
    SET stock = stock - #{decrementStock}
    WHERE shop_number = #{shopNumber}
      AND id = #{couponTemplateId}
      AND stock >= #{decrementStock}
</update>

这里有些同学可能会有疑问,为什么要先访问优惠券模板缓存进行扣减,然后再扣减优惠券模板数据库?

这是因为在后续的优惠券秒杀流程中,我们也是先通过缓存扣减,再扣减数据库。如果这里先扣减数据库,可能会出现缓存扣减成功但数据库扣减失败的情况。为保持一致性,我们在此流程中同样先扣减缓存,然后再扣减数据库。

此外,如果用户已经领取过该优惠券,为了简化流程处理,我们通过捕获唯一索引异常来跳过当前流程。

最后,我们使用 Redis 的 ZSet 来缓存用户的领券记录,并将领券时间作为 Score 值,这样用户在查询时可以按时间倒序显示领取记录。