作为一名编程学习者,近期我独立完成了一个高校图书管理系统的开发,技术栈选用Vue3+TypeScript+Pinia,涵盖用户借阅、管理员管控、库存管理等核心功能。开发过程中,既有被“库存超卖”bug困住的崩溃时刻,也有借助AI工具高效破局的惊喜,今天就来复盘这些实战干货,希望能给同样在做项目的小伙伴避坑。
先简单介绍下项目背景:这是一个面向高校师生的轻量化图书管理系统,核心需求是实现普通用户的图书查询、借阅、预约、归还,以及管理员的图书管理、用户管控、逾期处理等功能。看似简单的业务逻辑,实际开发中却藏着不少容易忽略的细节,其中最让我头疼的,就是“库存超卖”问题。
一、核心踩坑:多用户并发借阅,库存居然变成负数?
项目开发初期,我快速完成了图书借阅的基础功能:用户点击借阅按钮,前端发送请求,后端校验库存并更新,一切看似顺利。但在简单的并发测试中(让同学同时点击同一本库存为1的图书),意外发生了——两本书都被“成功借阅”,库存直接变成了-1,也就是典型的“库存超卖”问题。
排查了大半天,终于找到问题根源:
前端层面:没有做防重复提交处理,用户快速点击按钮时,会发送多次借阅请求,导致后端接收多个并发请求。
后端层面:校验库存和更新库存是两个独立的操作,没有加锁控制,当两个请求同时读取到“库存为1”的状态后,都会执行“库存-1”的操作,最终导致库存异常。
这个问题让我意识到,即使是简单的业务场景,也不能忽略并发控制的重要性。一开始我试图只在前端做优化,给按钮加了loading状态,禁止重复点击,但这只能解决前端重复提交的问题,无法应对后端的并发请求(比如多个用户同时通过不同设备发起借阅)。
二、破局之路:前端+后端双重控制,彻底解决库存超卖
明确问题根源后,我采用了“前端防重复+后端锁控制”的双重方案,一步步解决了这个bug,具体实现如下:
1. 前端优化:禁用重复提交,提升用户体验
在Vue3组件中,给借阅按钮绑定loading状态,点击后立即禁用按钮,直到请求完成(成功或失败)后再解锁,同时添加请求拦截,防止通过代码手动触发多次请求。
<template> <button @click="handleBorrow" :disabled="isLoading" class="borrow-btn" > {{ isLoading ? '借阅中...' : '立即借阅' }} </button> </template> <script setup> import { ref } from 'vue'; import { useBorrowStore } from '@/store/borrow'; const isLoading = ref(false); const borrowStore = useBorrowStore(); const handleBorrow = async (bookId) => { // 禁用按钮,防止重复点击 isLoading.value = true; try { // 发送借阅请求 await borrowStore.borrowBook(bookId); ElMessage.success('借阅成功!'); } catch (err) { ElMessage.error(err.message || '借阅失败,请重试'); } finally { // 无论成功失败,都解锁按钮 isLoading.value = false; } }; </script>
这一步优化后,有效避免了同一用户重复提交的问题,但还不能从根本上解决多用户并发的问题,核心还是要靠后端控制。
2. 后端优化:乐观锁加持,防止并发修改
后端采用乐观锁的方式,在图书表中新增一个version(版本号)字段,每次更新库存时,校验版本号是否匹配,只有版本号一致,才允许更新库存,同时将版本号+1。这样一来,即使多个请求同时读取到相同的库存,也只有一个请求能成功更新,其余请求会返回失败,前端再提示用户“图书已被借阅,请重试”。
核心SQL逻辑如下(以MySQL为例):
-- 更新库存,乐观锁校验版本号 UPDATE book SET stock = stock - 1, version = version + 1 WHERE book_id = #{bookId} AND stock > 0 AND version = #{version};
后端接口返回更新影响的行数,如果影响行数为0,说明库存不足或版本号不匹配,前端接收后提示用户重新操作。通过这种方式,彻底解决了库存超卖的问题,后续并发测试100次,库存始终保持非负。
三、AI提效:让重复工作“一键搞定”,节省2小时开发时间
解决完核心bug后,我还遇到了一个繁琐的问题:需要批量生成100条测试用的图书数据,手动编写JSON格式的数据太费时间,而且容易出错。这时候,我借助豆包AI,快速完成了这项重复工作。
我给AI输入的指令如下(适配ProcessOn AI绘图的逻辑,同时满足测试数据需求):
帮我生成100条图书测试数据,包含bookId(字符串,格式为BOOK+6位数字)、title(书名,涵盖计算机、文学、历史、科技四类)、author(作者,随机生成)、isbn(13位数字)、category(分类,仅计算机、文学、历史、科技)、stock(1-50随机整数)、version(初始值为1),以JSON数组格式返回,确保数据不重复,格式规范可直接用于Mock接口测试。
不到10秒,AI就生成了100条符合要求的测试数据,我只需要复制到Mock.js中,就能快速搭建接口测试环境,省去了手动造数据的时间。除此之外,我还让AI帮我优化了前端的批量导入组件代码,包括文件上传、数据校验、错误提示等功能,原本需要2小时编写的代码,AI生成后我只需要微调30分钟,效率提升非常明显。
这里也给大家一个小建议:开发中遇到造数据、写样板代码、简单逻辑优化等重复工作时,不妨借助AI工具,把精力放在核心业务逻辑和问题排查上,能大幅提升开发效率。
四、项目复盘与成长
这次图书管理系统的开发,虽然规模不大,但让我收获良多,也总结了几个实用的开发经验,分享给大家:
开发前先梳理业务逻辑,尤其是并发场景、异常场景,提前做好方案设计,避免后期大规模重构。比如这次的库存超卖问题,如果一开始就考虑到并发控制,就不会走弯路。
前端优化和后端控制缺一不可,前端负责提升用户体验,后端负责保证数据安全,两者结合才能打造稳定的系统。
善用AI工具,但不依赖AI。AI可以帮我们解决重复工作、提供思路,但核心逻辑和bug排查,还需要自己深入思考,毕竟AI生成的代码也可能存在问题,需要手动校验。
遇到bug不要慌,先复现问题,再逐步排查根源,不要盲目修改代码。比如这次的库存超卖,我先通过并发测试复现问题,再分别排查前端和后端,最终找到解决方案。
目前,这个图书管理系统已经完成了核心功能的开发,后续还会优化逾期提醒、图书推荐等功能。这次开发经历,不仅提升了我的技术能力,也让我明白了“细节决定成败”——看似简单的业务,藏着不少需要注意的细节,只有认真对待每一个环节,才能开发出稳定、好用的系统。
如果大家在开发类似系统时,遇到库存超卖、状态管理混乱等问题,欢迎在评论区交流,一起避坑、一起成长!