关于库存扣减类问题总结

库存扣减在提交订单时,还是支付之后?

库存扣减与买票类似,这里通过买票环境阐述:

        在提交订单时扣减组数,需要考虑的因素:

        1.用户下单,但取消支付,前端需要捕获到取消支付的动作,并调用后端接口将对应组数相加

        2.用户下单,进入输入支付密码时,出现网络故障,或手机关机等因素导致进程杀死,前端无法向后端调接口,导致该场次组数发生异常

        3.用户下单,但未支付,这时后台管理操作人员更改了组数,需要自动计算剩余组数,在计算剩余组数时,可能将用户下单减一的组数吞掉了,再次加一时,可能就多了一组

       优点:场次信息是实时的,没有了就是没有了,不会出现剩余一组,两人还能同时支付的情况

        缺点:上述因素的第二条无法避免,需要考虑取消支付增加组数的情况,以及第三条比较特殊的情况,很容易出现剩余组数错误

        在支付后扣减组数,需要考虑的因素:

        1.并发问题,剩余一组后,用户a进入下单支付界面,此时剩余还是一组,在a未支付的同时用户b也能进入下单,最终会导致a,b都支付成功

        2.处理退款时需要实时查询该场次下订单已支付数量,更新剩余场次

        3.从用户角度考虑,需要通知用户退款的原因为场次数不足,否则用户直接懵了

        优点: 支付之后扣减就不用考虑用户取消支付,或网络故障导致前端无法调接口问题

        缺点:库存数不实时,a和b都能买,但只剩一组,这时需要将为竞争到锁的用户进行提示,并自动退款,对用户体验不好,但能保证组数不出现错误数据

支付后扣减组数实现

微信支付成功后回调接口

        // 支付成功处理
        if (WxPayConstants.WxpayTradeStatus.SUCCESS.equals(result.getTradeState())) { //返回的result为成功
            // 处理时间
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
            String formattedDate = sdf.format(date);
            appOrderMapper.updateOrderPayById(OrderStatusEnum.PAID.getValue(), result.getTransactionId(), formattedDate, order.getId());
            if (sessionService.deductInventory(order.getSessionId())) {
                log.info("剩余组数扣减成功");
            } else {
                //订单改为异常状态,客户取消支付,直接退款
                this.refundOrder(new RefundForm().setOrderCode(order.getOrderCode())
                        .setRefundAmount(order.getAmount())
                        .setReason("场次剩余不足,自动退款"));
      
            }
        }

       扣减库存

    // 扣减库存操作
    public boolean deductInventory(Long sessionId) {
        lock.lock();
        try {
            Session session = this.getOne(new LambdaQueryWrapper<Session>().eq(Session::getId,sessionId));
            Long inventory = session.getRestAmount();
            if (inventory > 0) {
                appOrderMapper.updateSessionById(inventory-1,sessionId);
                return true;
            } else {
                return false;
            }
        } finally {
            lock.unlock();
        }
    }

 退款成功回调接口

   public void handleWxPayRefundNotify(SignatureHeader signatureHeader, String notifyData) throws WxPayException {
        log.info("开始处理退款结果通知");
        log.info("notifyData----"+notifyData);
        // 解密支付通知内容
         final WxPayRefundNotifyV3Result.DecryptNotifyResult result = this.wxPayService.parseRefundNotifyV3Result(notifyData, signatureHeader).getResult();
        log.info("退款通知解密成功:[{}]", result.toString());
        // 根据商户退款单号查询订单
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Order::getOrderCode, result.getOutTradeNo());
        Order order = orderService.getOne(wrapper);
        // 退款成功处理
        if (WxPayConstants.RefundStatus.SUCCESS.equals(result.getRefundStatus())) {
            ProcessOrderStatus(result, order); //处理订单状态
            //更新剩余组数(总组数-剩余组数)
            Session session = sessionService.findById(order.getSessionId());
            Long lessSession = orderService.count(new QueryWrapper<Order>()
                    .eq("SESSION_ID",order.getSessionId())
                    .apply("STATE in ({0},{1})",OrderStatusEnum.PAID.getValue(),OrderStatusEnum.COMPLETE.getValue())); //剩余数量
            Long restAmount = session.getAmount() - lessSession;
                appOrderMapper.updateSessionById(restAmount, order.getSessionId());
            }
            log.info("场次信息更新成功");
            log.info("账单更新成功");
    }

        实现效果:

总结:

在提交订单时扣减组数,我们并不能捕获到用户是否关闭应用或取消支付时断网,这种情况发生概率较大,在该业务中,使用这种方法风险较大,而且不好解决这种情况,在支付后扣减组数,我们需要考虑的是并发的问题,以及对使用者的角度而言的感受,两者都有各自的优点和缺点,实际的开发中,也需要根据自己的业务需求去权衡利弊,而非哪一种一定就是最好的

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值