Skip to main content

13小节:完成锁定/核销/退还优惠券功能

作者:程序员马丁

在线博客:https://nageoffer.com

note

热门项目实战社群,收获国内众多知名公司面试青睐,近千名同学面试成功!助力你在校招或社招上拿个offer。

完成锁定/核销/退还优惠券功能,元数据信息:

©版权所有 - 拿个offer-开源&项目实战星球专属学习项目,依据《中华人民共和国著作权法实施条例》《知识星球产权保护》,严禁未经本项目原作者明确书面授权擅自分享至 GitHub、Gitee 等任何开放平台。违者将面临法律追究。


内容摘要:本章节重点实现了优惠券在不同业务场景下的状态管理,包括锁定、核销和退还功能。通过引入分布式锁(如 Redis 的 Redisson)和事务控制,确保了优惠券状态变更的原子性和一致性,防止了并发操作可能导致的状态冲突。

课程目录如下所示:

  • 业务背景
  • Git 分支
  • 锁定优惠券
  • 核销优惠券
  • 退款优惠券

业务背景

兑换优惠券之后,我们可以在订单结算时使用优惠券,这个时候优惠券状态就会变成锁定中;如果用户支付了订单,优惠券状态变更为已使用;如果订单退款,用户优惠券回退到用户账户里,优惠券状态回退到未使用状态。

  • 兑换优惠券(未使用):优惠券被领取到用户账户中,初始状态为 未使用

  • 订单结算(锁定中):当用户在订单中使用优惠券时,优惠券状态应变更为 锁定中。此时优惠券不可被其他订单再次使用,直到订单完成或取消。

  • 支付成功(已使用):用户支付订单后,优惠券状态变更为 已使用,表示优惠券使用成功,不可再被使用。

  • 订单退款(未使用):如果订单取消或发生退款,优惠券状态回退至 未使用,重新回到用户账户中,可被再次使用。

Git 分支

先从 main 分支上查看,代码入口:UserCouponController

锁定优惠券

用户在订单结算时使用优惠券,创建优惠券结算单,并将用户优惠券的状态从“未使用”变更为“锁定中”,确保优惠券在订单支付过程中被锁定,避免并发情况下同一优惠券被重复使用。

1. 获取分布式锁

代码如下时所:

RLock lock = redissonClient.getLock(String.format(EngineRedisConstant.LOCK_COUPON_SETTLEMENT_KEY, requestParam.getCouponId()));
boolean tryLock = lock.tryLock();
if (!tryLock) {
throw new ClientException("正在创建优惠券结算单,请稍候再试");
}
  • 获取分布式锁:使用 Redisson 获取基于 Redis 的分布式锁,防止并发情况下同一优惠券被多个线程同时使用。

  • 锁的 Key:锁的 Key 为 LOCK_COUPON_SETTLEMENT_KEY + couponId,表示锁定某个具体优惠券的结算操作。

  • tryLock 判断:如果获取不到锁,则表示当前优惠券正在创建结算单,抛出异常,提示稍后再试。

2. 检查优惠券状态

代码如下所示:

LambdaQueryWrapper<CouponSettlementDO> queryWrapper = Wrappers.lambdaQuery(CouponSettlementDO.class)
.eq(CouponSettlementDO::getCouponId, requestParam.getCouponId())
.eq(CouponSettlementDO::getUserId, Long.parseLong(UserContext.getUserId()))
.in(CouponSettlementDO::getStatus, 0, 2);

if (couponSettlementMapper.selectOne(queryWrapper) != null) {
throw new ClientException("请检查优惠券是否已使用");
}
  • 检查优惠券状态:通过 CouponSettlementDO 查询当前用户的优惠券是否已经有结算记录。

    • 状态为 0 表示“锁定中”,状态为 2 表示“已使用”。
  • 避免重复使用:如果查询结果不为空,说明优惠券正在使用或已使用,抛出异常提示“优惠券已使用”。

解锁付费内容,👉 戳