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

优惠券系统-第二章介绍

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

领取流程

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

    生成的券,不可能全部加载到缓存中,比如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,因为领券往往是并发量比较大的操作,能从缓存判断,尽量从缓存判断。

相关定时任务

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

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

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值