SpringBoot+Vue 招聘系统实战:解决简历投递高并发重复提交问题(附源码)

最近在维护公司内部小型招聘ATS系统,线上频繁收到测试反馈:用户疯狂点击投递按钮,会生成多条重复投递记录。尤其是校招高峰期,大量用户集中投递岗位,不仅产生脏数据,还会导致数据库压力骤增、接口响应卡顿。

网上很多教程只讲基础CRUD,完全没覆盖招聘系统的核心痛点------投递接口的幂等性与防抖拦截。折腾了大半天,整合了前端防抖+后端幂等校验+Redis锁的完整方案,亲测有效,分享给做同类项目的小伙伴。

一、问题复盘:为什么普通接口扛不住简历投递场景?

先说说原始代码的问题,也是大部分新手写招聘系统的通病:

前端无任何防抖限制,用户连续点击投递按钮,会短时间内触发多次接口请求;后端仅做了简单的参数校验,没有幂等判断。只要请求到达接口,就会直接insert投递记录。

招聘业务有个强规则:同一个用户,同一个岗位,仅允许投递一次。重复投递不仅业务逻辑错误,还会造成HR后台数据冗余,筛选简历时出现大量重复条目,极大影响办公效率。

单纯靠数据库唯一索引可以解决重复数据问题,但会抛出异常,前端体验极差,而且高并发下数据库锁竞争会拖慢整体接口速度,治标不治本。

二、完整解决方案:前后端双层拦截

1. Vue前端:按钮防抖+投递状态锁定

核心思路:点击投递按钮后,立即禁用按钮,请求结束(成功/失败)后再解锁,杜绝人为连续点击。相比定时器防抖,状态锁定更适配表单提交场景,不会出现临界点击漏洞。

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| javascript // 核心防抖逻辑 export default { data() { return { submitLoading: false, // 提交加载状态 jobId: '', // 岗位ID userId: '' // 当前登录用户ID } }, methods: { // 简历投递提交 async submitDelivery() { if (this.submitLoading) return this.submitLoading = true try { const res = await this.api.job.delivery({ jobId: this.jobId, userId: this.userId }) if (res.code === 200) { this.message.success('投递成功!') } } catch (err) { this.$message.error(err.msg || '投递失败,请重试') } finally { // 无论成功失败,都解锁按钮 this.submitLoading = false } } } } |

同时在页面加载时,提前调用接口校验用户是否已投递该岗位,直接隐藏投递按钮,从源头避免无效请求,优化用户体验。

2. 后端SpringBoot:Redis幂等锁兜底

前端拦截只能解决普通用户误操作,无法防止恶意刷接口、爬虫批量请求。所以后端必须做幂等兜底,这是企业级项目的必备逻辑。

设计规则:以 用户ID+岗位ID 作为唯一key,设置过期时间(适配招聘投递周期),请求进来先判断key是否存在,存在则直接返回"已投递",不存在则新增投递记录并写入缓存。

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| java @Service public class JobDeliveryServiceImpl implements JobDeliveryService { @Autowired private RedisTemplate<String, Object> redisTemplate; // 缓存key前缀 private static final String DELIVERY_KEY = "job:delivery:"; // 缓存过期时间:30天(岗位投递有效期) private static final long EXPIRE_TIME = 30 * 24 * 60 * 60; @Override public Result delivery(Long userId, Long jobId) { // 拼接唯一key String key = DELIVERY_KEY + userId + ":" + jobId; // 幂等判断:已存在则直接返回 if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) { return Result.fail("您已投递过该岗位,无需重复投递"); } // 数据库新增投递记录 JobDelivery delivery = new JobDelivery(); delivery.setUserId(userId); delivery.setJobId(jobId); delivery.setDeliveryTime(LocalDateTime.now()); save(delivery); // 写入缓存,设置过期时间 redisTemplate.opsForValue().set(key, "1", EXPIRE_TIME, TimeUnit.SECONDS); return Result.success("投递成功"); } } |

三、补充优化:高并发场景适配

校招高峰期会出现瞬时大量投递请求,单纯的幂等锁还不够稳定,我额外做了两点优化:

  1. 接口限流:基于Redis实现单用户每秒请求限制,防止恶意刷接口导致系统过载;

  2. 异步记录日志:投递日志、操作记录通过异步线程处理,不阻塞主业务流程,提升接口响应速度。

四、踩坑总结

  1. 招聘系统的重复投递问题,绝对不能只依赖数据库唯一索引,高并发下极易触发数据库异常,影响用户体验;

  2. 前后端双层拦截是最优解:前端优化交互,后端保障数据一致性;

  3. 缓存过期时间要贴合业务场景,不能设置永久有效,避免岗位过期后缓存残留导致无法投递。

这套方案落地后,线上再也没有出现过重复投递数据,接口稳定性提升非常明显。做招聘系统、求职平台的小伙伴可以直接照搬,有问题评论区交流~

相关推荐
敲代码的嘎仔5 小时前
互动问答系统实战:两级评论模型、ES 搜索集成、Caffeine 多级缓存全记录
java·开发语言·数据库·elasticsearch·缓存·mybatis·高并发
隐擎fox15 小时前
高性能网络爬虫架构设计:基于 Python 的长连接复用与分布式会话池调度实践
分布式·python·网络协议·tcp/ip·高并发·网络爬虫、
Java爱好狂.3 天前
Java初学者如何设计一个高并发系统?
程序员·高并发·架构师·并发编程·java面试·java面试题·java八股文
爱和冰阔落5 天前
【Linux】epoll 真的是 O(1) 吗?从阻塞 I/O、select/poll 到 LT/ET 与 Reactor 一次讲透
linux·运维·高并发·tcp
SelectDB技术团队9 天前
Apache Doris 多表 Join 比 Starrocks 更快?ClickBench 与 RTABench 实测对比
数据分析·高并发·技术选型·多表join·查询加速·复杂分析·高频查询
vivo互联网技术11 天前
四年、十次告警、数十个技术决策:一个海外电商平台的高并发治理实录
服务器·redis·高并发·热key·性能治理·缓存治理
linweidong12 天前
阿里Lazada Java面经及参考答案
大数据·高并发·反射·拦截器·session·幂等性·redis性能
会编程的土豆1 个月前
12306 春运抢票:数据一致性怎么保证
redis·消息队列·go·高并发
智码看视界1 个月前
Day 45 | Sentinel流量治理:从限流到熔断降级再到系统自适应保护
sentinel·高并发·熔断降级·流量控制·微服务治理·热点参数限流