下单键防止一直点击
1、获取提交表单令牌token
@ApiOperation("获取提交订单令牌")
@GetMapping("get_token")
public JsonData getOrderToken(){
LoginUser loginUser = LoginInterceptor.threadLocal.get();
String key = String.format(CacheKey.SUBMIT_ORDER_TOKEN_KEY,loginUser.getId());
String token = CommonUtil.getStringNumRandom(32);
redisTemplate.opsForValue().set(key,token,30, TimeUnit.MINUTES);
return JsonData.buildSuccess(token);
}
2、执行下单接口
@ApiOperation("提交订单")
@PostMapping("confirm")
public void confirmOrder(
@ApiParam("订单对象")
@RequestBody ConfirmOrderRequet orderRequet, HttpServletResponse response) throws AlipayApiException {
JsonData jsonData = orderService.confirmOrder(orderRequet);
if (jsonData.getCode() == 0) {
String client = orderRequet.getClientType();
String payType = orderRequet.getPayType();
// 如果是支付宝支付,都是跳转网页APP除外
if(payType.equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())){
log.info("创建订单成功:{}",orderRequet.toString());
// 支付宝支付就是将一串html返回给前端
if(client.equalsIgnoreCase(ClientType.H5.name())){
writeDate(response,jsonData);
}else if(client.equalsIgnoreCase(ClientType.APP.name())){
// APP SDK支付 TODO
}
}else if(payType.equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())){
// 微信支付 TODO
}
}else{
log.info("创建订单失败{}",jsonData.toString());
}
}
3、下单接口confirm会去调用service层的,而service层会首先会去校验redis中的token,校验完后会删除,因此第二次再次下单的时候,token不存在了;
@Override
public JsonData confirmOrder(ConfirmOrderRequet orderRequet) throws AlipayApiException {
LoginUser loginUser = LoginInterceptor.threadLocal.get();
String orderToken = orderRequet.getToken();
if(StringUtils.isBlank(orderToken)){
throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_NOT_EXTIS);
}
// 原子操作,校验令牌,删除令牌
String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
// 操作成功就返回1
Long result = redisTemplate.execute(new DefaultRedisScript<>(script,Long.class),
Arrays.asList(String.format(CacheKey.SUBMIT_ORDER_TOKEN_KEY,loginUser.getId())),
orderToken);
if(result == 0L){
throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_NOT_EXTIS);
}
// 获取一个订单号