【防重复点击】防止全局关键业务按钮重复点击,多次触发请求,可以从 前端(主)+ 后端(辅) 两方面控制

一、前端防重复点击(主策略)

1.1 禁用按钮法(最常见)

<button :disabled="loading" @click="handleClick">提交</button>

methods: {
  async handleClick() {
    if (this.loading) return;
    this.loading = true;
    try {
      const res = await this.submitRequest();
      // 成功处理
    } catch (err) {
      // 错误处理
    } finally {
      this.loading = false;
    }
  }
}
 

1.2 防抖处理(适用于非强制立即提交的按钮)

若用户短时间内多次点击,以最后一次为准,适合搜索框、查询按钮等。

import _ from 'lodash';

methods: {
  handleClick: _.debounce(function () {
    this.doSubmit();
  }, 1000)
}
 

1.3 节流处理(适用于固定间隔执行,比如每10秒一次)

handleClick: _.throttle(function () {
  this.doSubmit();
}, 10000)
 

1.4 全局指令封装(Vue 示例)

Vue.directive('preventReClick', {
  inserted(el, binding) {
    el.addEventListener('click', () => {
      if (!el.disabled) {
        el.disabled = true;
        setTimeout(() => {
          el.disabled = false;
        }, binding.value || 1000); // 默认1秒内禁止重复点击
      }
    });
  }
});

✅ 二、后端幂等控制(辅助策略)

前端只能减少重复请求,不能绝对防止,真正重要的业务必须后端也防重复执行

2.1 幂等 Token(业务允许的话可以,如果有单点登录等需求则不建议)
  • 前端向服务端获取一个 一次性token

  • 提交请求时携带这个 token

  • 后端校验 token 有效性,使用过的 token 拒绝再次执行

2.2 请求唯一标识 + Redis 锁控制(适用于秒杀、支付等) 推荐
简单实现
  • 请求携带业务唯一标识(如订单号、签约ID)

  • 后端用 Redis 锁控制:(分布式锁)

// Java示意
if (!redis.setIfAbsent("LOCK_KEY_" + businessId, 1, 5秒)) {
    throw new RepeatOperationException("请勿重复提交");
}

Boolean setIfAbsent(K key, V value, long timeout, TimeUnit unit)

表示:如果 key 不存在,则设置 key 并返回 true;如果已存在,则不修改,返回 false。

但是更推荐用Redisson(分布式锁)

🔐 什么是分布式锁?

多个进程或服务器之间,保证同一份资源只能被一个线程/节点访问或操作。

🔒 你手写锁 vs Redisson 功能对比

功能你用 setIfAbsent()Redisson
锁唯一性校验❌ 需要你自己加 UUID + 比较✅ 内部已实现
解锁安全性❌ 删除可能删到别人加的锁✅ 只能删掉自己加的锁
可重入❌ 无✅ 有
自动续期❌ 锁一旦过期就没了✅ 自动续命,不会中途释放
锁等待❌ 无法等待锁,只能立即返回✅ 可以设置等待时间
超时释放✅ 你可以手动设置✅ 自动处理

✅ Redisson 分布式锁示例代码

@Autowired
private RedissonClient redissonClient;

public void doBusiness(String businessId) {
    RLock lock = redissonClient.getLock("LOCK:BUSINESS:" + businessId);

    boolean success = lock.tryLock(5, 30, TimeUnit.SECONDS);
    // 参数说明:
    // 5 秒内尝试获取锁(等待时间)
    // 加锁成功后 30 秒后自动释放(加锁时间)
    
    if (!success) {
        throw new BusinessException("系统繁忙,请稍后再试");
    }

    try {
        // 你的核心业务逻辑
    } finally {
        lock.unlock(); // 安全释放锁
    }
}

 


✅ Redisson 依赖和配置

<!-- Maven 依赖 -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.23.5</version> <!-- 根据你的 Spring Boot 版本选择 -->
</dependency>

# application.yml
redisson:
  config: |
    singleServerConfig:
      address: "redis://127.0.0.1:6379"

🧠 总结一句话:

RedisTemplate 写锁,就像用裸线程写多线程程序;
Redisson 写锁,就像用线程池 + Future 写多线程,高效、安全、可维护

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值