优惠券系统-第二章-领券与补券

优惠券系统-第二章介绍

第二章主要是介绍优惠券的领取、补券。

领取流程

优惠券的领取,是一个单独的接口,作为一个服务给应用层调用。比如,用户注册送几张券,用户首单支付送券,等等。像“领券中心”,用户可以直接点击领取的券,是通过“某一个活动”领券,有活动应用层调用送券服务,第三章会详细介绍活动。

生成的券,不可能全部加载到缓存中,比如20万个券可以每5000个加入缓存,领取完毕后,再向缓存中补券。当调用方取不到券时,直接返回一个指定错误code码,然后系统触发异步补券,向缓存补充5000个。调用方就需要一定的重试原则,第一次没有领取成功时,需要做重试。

流程图如下图所示

下面给出从缓存队列中拿券的代码,仅是从缓存中取得一个券。

 public CouponInfoVo getCouponInfo(ReceiveCouponRequest request){ try { //从redis队列拿出一个券 String couponSn = redisOperations.rpop(PromotionCacheConstant.getCacheCouponQueueKey(request.getActSn())); if(!Check.NuNStr(couponSn)){ //从缓存中取出券的详情 CouponBaseEntity couponBaseEntity = null; String couponBaseStr = redisOperations.get(couponSn); CouponInfoVo infoVo = new CouponInfoVo(); BeanUtils.copyProperties(couponBaseEntity, infoVo); //将这个券从缓存中删除 redisOperations.del(couponSn); return infoVo; } else { //如果缓存中没券,异步触发补充券 taskExecutor.execute(()->fillCacheCouponSnQueue(request.getActSn())); } }catch (Exception e){ LogUtil.error(logger,"获取优惠券信息失败,error:{}",e); return null; } return null; }

下面给出领券的代码,会调用上面的拿券方法,然后更新券状态,记录领取人与券的关系。

public DataTransferObject<Void> receiveCoupon(ReceiveCouponRequest receiveCouponRequest){ try{ //这里从缓存中key判断这类券是否被领取完毕,actSn号为某一类型券 String couponQueueStatus = redisOperations.get(PromotionCacheConstant.getCacheCouponOverKey(request.getActSn())); if(!Check.NuNStr(couponQueueStatus) && couponQueueStatus.equals(PromotionCacheConstant.COMMON_LABEL)){ //已被领取完毕 LogUtil.warn(logger, "优惠券队列已消费完("+request.getActSn()+")"); return null; } //这里调用了上面的拿券方法 CouponInfoVo couponInfo = couponBaseServiceImpl.getCouponInfo(receiveCouponRequest); if (!Check.NuNObj(couponInfo)) { //拿到券 receiveCouponRequest.setActSn(couponInfo.getActSn()); receiveCouponRequest.setCouponSn(couponInfo.getCouponSn()); //这个方法更新券状态,同时记录领取信息,需要在一个实务中 dto = couponUserService.saveReceiveCoupon(receiveCouponRequest); if(!dto.checkSuccess()){ LogUtil.error(LOGGER,"未领取到优惠券,param:{}", JsonEntityTransform.Object2Json(dto)); return dto; } } else { dto.setMsg("未领取到优惠券"); //标识领取失败,但可以重试 dto.setErrCode(PromotionConstant.RECEIVE_COUPON_TRY_CODE); } } catch (Exception e) { LogUtil.error(LOGGER,"领券异常,{}",e); dto.setErrorMsg("领券异常"); return dto; } return dto; }

有了上面的两端代码大家应该能明白这个领取流程了,下面还有最后一段的异步“补券”代码,当缓存丢列中券不够时,代码如下

public void fillCacheCouponSnQueue(String actSn){ try{ //查询5000个券出来 CouponActivityQueryRequest couponActivityQueryRequest = new CouponActivityQueryRequest(); couponActivityQueryRequest.setLimit(5000); couponActivityQueryRequest.setActSn(actSn); List<CouponBaseEntity> unusedCouponBaseList = getAllUnusedCouponSn(couponActivityQueryRequest); //如果查不到券,说明被领完了 if(Check.NuNCollection(unusedCouponBaseList)){ //放入缓存该类券被领完的key redisOperations.set(actSn,"1"); LogUtil.warn(logger, "可用优惠券已使用完("+actSn+")"); } //数据库中还有券时 if(!Check.NuNCollection(unusedCouponBaseList)){ String couponBaseQueueKey = PromotionCacheConstant.getCacheCouponQueueKey(actSn); unusedCouponBaseList.parallelStream().forEach(base->{ //将券加入缓存中 redisOperations.set(base.getCouponSn(), JsonEntityTransform.Object2Json(base)); redisOperations.lpush(couponBaseQueueKey,base.getCouponSn()); //修改券已经被加入到缓存的状态 CouponBaseEntity couponBase = new CouponBaseEntity(); couponBase.setCouponSn(base.getCouponSn()); couponBase.setLoadStatus(1); couponBaseDao.updateCouponBase(couponBase); }); } } catch (Exception e) { LogUtil.error(logger,"填充优惠券码队列失败,error:{}",e); } }

代码已经全部给出,在这里说明一下,为什么当数据库券全部被领完后,需要缓存中放一个标key,因为领券往往是并发量比较大的操作,能从缓存判断,尽量从缓存判断。

相关定时任务

领取券被领取后,就会有过期时间,优惠券过期状态通过定时任务来修改,通过定时任务修改的话就会有一定的延时,券本身过期时间已经过了,定时任务确没有执行,还处于未过期的状态。所以在可用优惠券列表的接口层面要将这种,已经过期,但是状态未及时更新的券剔除掉。

第二章主要介绍了优惠券领取、补券,第三章介绍活动中心。

原文链接:https://blog.csdn.net/u014278091/article/details/90715365?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165277499316782395393704%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=165277499316782395393704&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~times_rank-27-90715365-null-null.nonecase&utm_term=%E4%BC%98%E6%83%A0

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
文明发言,共建和谐米科社区
提交
头像

昵称

取消
昵称表情图片