关键点:识别哪些提交是同样的提交

解决方法:在页面设置token,利用redis事务的原子性。比如,在trade页面,点击提交订单,会生成一个token嵌入在页面中。点击结算时会验证token,解决重复提交的问题。

代码:

  • controller"trade"

    @GetMapping("trade")
      @LoginRequire
      public String trade(HttpServletRequest request){
          String userId =(String)request.getAttribute("userId");
          //  用户地址  列表
          List<UserAddress> userAddressList = userService.getUserAddressList(userId);
    
          request.setAttribute("userAddressList",userAddressList);
    
          //  用户需要结账的商品清单
          List<CartInfo> checkedCartList = cartService.getCheckedCartList(userId);
          BigDecimal totalAmount = new BigDecimal("0");
          for (CartInfo cartInfo : checkedCartList) {
              BigDecimal cartInfoAmount = cartInfo.getSkuPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
              totalAmount= totalAmount.add(cartInfoAmount);
          }
    
          String token = orderService.genToken(userId);//生成token
    
          request.setAttribute("tradeNo",token);//request请求后面放token
    
          request.setAttribute("checkedCartList",checkedCartList);
    
          request.setAttribute("totalAmount",totalAmount);
    
          return  "trade";
      }
    • 生成token的方法

      @Override
      public String genToken(String userId) {
          //token  type   String  key   user:10201:trade_code  value token
          String token = UUID.randomUUID().toString();
          String tokenKey="user:"+userId+":trade_code";
          Jedis jedis = redisUtil.getJedis();
          jedis.setex(tokenKey,10*60,token);
          jedis.close();
      
          return token;
      }
  • controller"submitOrder"

    @PostMapping("submitOrder")
      @LoginRequire
      public String submitOrder(OrderInfo orderInfo,HttpServletRequest request){
          String userId = (String) request.getAttribute("userId");
    
          String tradeNo = request.getParameter("tradeNo");//拿到token
    
          boolean isEnableToken = orderService.verifyToken(userId, tradeNo);//验证token
          if(!isEnableToken){
              request.setAttribute("errMsg","页面已失效,请重新结算!");
              return  "tradeFail";
          }
    
          }
    
          String orderId = orderService.saveOrder(orderInfo);
    
          // 删除购物车信息
          //  xxxx
          return "redirect://payment.gmall.com/index?orderId="+orderId;
      }
    • 验证token service

      @Override
          public boolean verifyToken(String userId, String token) {
              String tokenKey="user:"+userId+":trade_code";
              Jedis jedis = redisUtil.getJedis();
              String tokenExists = jedis.get(tokenKey);
              jedis.watch(tokenKey);//监控tokenkey
              Transaction transaction = jedis.multi();//开启事务
              if(tokenExists!=null&&tokenExists.equals(token)){
                  transaction.del(tokenKey);//事务的删除,会在queue里面
              }
              List<Object> list = transaction.exec();//事务的提交,成功返回1,不成功会不进行任何操作,返回nill
              if(list!=null&&list.size()>0&&(Long)list.get(0)==1L){
                  return true;
              }else{
                  return false;
              }
          }