原因:用户或恶意访问,导致一个接口被重复提交多次
解决方式:
1、在页面提交接口的时候,想办法传输一个当前次接口访问的token令牌
2、使用redis保存这个token令牌,执行接口业务逻辑时,去redis校验这个令牌有没有重复
难点:
redis的操作没有事务回滚,如果想要满足事务的需求,需要自己编写一段lua脚本来实现事务
@Transactional@Overridepublic SubmitOrderResponseVo submitOrder(OrderSubmitVo submitVo) {SubmitOrderResponseVo resp = new SubmitOrderResponseVo();//下单:去创建订单,验令牌,验价格,锁库存……MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();confirmVoThreadLocal.set(submitVo);// 1、验证令牌【令牌的对比和删除必须保证原子性】String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";String orderToken = submitVo.getOrderToken();// script 0失败 1成功Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberResponseVo.getUserId()), orderToken);// 原子验证令牌和删除令牌if(result == 1L){// 令牌验证成功OrderCreateTo order = createOrder();// TODO 可以进行验价BigDecimal payAmount = order.getOrder().getPayAmount();BigDecimal payPrice = submitVo.getPayPrice();BigDecimal subtract = payAmount.subtract(payPrice);if(Math.abs(subtract.doubleValue()) < 0.01){// 验价成功,保存订单saveOrder(order);// 锁库存,只要有异常就回滚订单数据// 订单号,所有订单项WareSkuLockVo lockVo = new WareSkuLockVo();lockVo.setOrderSn(order.getOrder().getOrderSn());List<OrderItemVo> collect = order.getOrderItems().stream().map(item -> {OrderItemVo orderItemVo = new OrderItemVo();orderItemVo.setSkuId(item.getSkuId());orderItemVo.setCount(item.getSkuQuantity());orderItemVo.setTitle(item.getSkuName());return orderItemVo;}).collect(Collectors.toList());lockVo.setLocks(collect);R r = wareFeignService.orderLockStock(lockVo);if(r.getCode() == 0){// 锁成功了resp.setOrder(order.getOrder());}else{//锁定失败resp.setCode(3);throw new NoStockException();}}else{resp.setCode(2);}}else{// 令牌验证失败}return resp;}
