✅P277_商城业务-订单服务-原子验令牌

gong_yz大约 1 分钟谷粒商城

提交订单结果响应

cfmall-order/src/main/java/com/gyz/cfmall/order/vo/SubmitOrderResponseVo.java

@Data
public class SubmitOrderResponseVo {

    private OrderEntity order;
    /** 错误状态码 **/
    private Integer code;
}

提交订单接口

验证令牌的核心:保证令牌的比较和删除的原子性;

解决方案:使用脚本

if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end

脚本执行的返回结果:

  • 0:代表令牌校验失败
  • 1:代表令牌成功删除,校验成功

cfmall-order/src/main/java/com/gyz/cfmall/order/web/OrderWebController.java

@PostMapping("/submitOrder")
public String submitOrder(OrderSubmitVo orderSubmitVo) {
    SubmitOrderResponseVo responseVo = orderService.submitOrder(orderSubmitVo);
    if (responseVo.getCode() == 0) {
        //下单成功,支付
        return "pay";
    }
    //下单失败,重新下单
    return "redirect:http://order.cfmall.com/toTrade";
}

cfmall-order/src/main/java/com/gyz/cfmall/order/service/impl/OrderServiceImpl.java

@Override
public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) {
    SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
    MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();
    String token = orderSubmitVo.getOrderToken();
    //脚本执行的返回结果: 0-代表令牌校验失败, 1-代表令牌成功删除即成功
    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    //Arg1:用DefaultRedisScript的构造器封装脚本和返回值类型; Arg2:用于存放Redis中token的key;Arg3:用于比较的token,即浏览器存储的token
    //令牌的验证和删除必须保证原子性
    Long response = redisTemplate.execute(
            new DefaultRedisScript<>(script, Long.class),
            Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX.toString() + memberResponseVo.getId()),
            token);
    if (response == 0) {
        responseVo.setCode(1);
        return responseVo;
    }else {
        //TODO:令牌校验成功,去创建订单,校验价格,锁库存
    }
    return null;
}

redisTemplate.execute(arg1,arg2,arg3)

  • arg1:用DefaultRedisScript的构造器封装脚本和返回值类型
  • arg2:数组,用于存放Redis中token的key
  • arg3:用于比较的token,即浏览器存储的token