13小节:完成锁定/核销/退还优惠券功能
作者:程序员马丁
热门项目实战社群,收获国内众多知名公司面试青睐,近千名同学面试成功!助力你在校招或社招上拿个offer。
完成锁定/核销/退还优惠券功能,元数据信息:
- 什么是牛券oneCoupon:https://t.zsxq.com/pAWgS
- 代码仓库:https://gitcode.net/nageoffer/onecoupon —— 申请项目权限参考上述牛券项目链接
- 章节难度:★★★☆☆ - 较难
- 视频地址:文档先行视频次之
©版权所有 - 拿个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
表示“已使用”。
- 状态为
避免重复使用:如果查询结果不为空,说明优惠券正在使用或已使用,抛出异常提示“优惠券已使用”。