✅P277_商城业务-订单服务-原子验令牌
大约 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