##
优惠券分发逻辑
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 值,这样用户在查询时可以按时间倒序显示领取记录。