基于SpringBoot+Vue的盲盒购物平台毕业设计:从技术选型到高并发实现
“盲盒”这个词在校园里自带流量,写进毕业设计,答辩老师一眼就能记住。可真正动手才发现,“抽盒”听起来浪漫,代码写起来全是坑:事务没隔离,库存瞬间变负数;前端把抽奖概率写死,一改需求就要全量发包;压测一上,Redis 直接被打穿。去年我带的小组做了一套 SpringBoot+Vue 的盲盒商城,踩坑无数,最后把核心代码整理成脚手架,直接复用到今年 4 个学弟的毕设,平均答辩分数 92+。今天把全过程拆开聊,从选型到高并发落地,一条线讲清:为什么选这套技术、关键接口怎么写、生产环境怎么防猝死。
1. 背景痛点:学生项目最容易翻车的 3 个坑
库存超卖
最常见的是“先查后减”两步走,并发一上来就幻读。去年某组用 MyISAM 引擎,压力测试 200 并发,库存直接减到 -47,现场翻车。前端硬编码概率
把“隐藏款概率 0.5%”写死在if (Math.random() < 0.005)里,运营想调个 1% 都得重新打包,毕业设计直接变“手工艺品”。事务范围乱套
Service 层一个方法里既扣库存又写订单,还顺手更新用户积分,事务嵌套 3 层,回滚点找不着,调试全靠System.out。
2. 技术选型对比:为什么不是 SSM + JQuery?
SpringBoot vs 传统 SSM
SSM 那一套 XML 配置,光一个applicationContext.xml就 200 行,答辩现场老师一句“你这些 bean 是干嘛的”就能问懵。SpringBoot 零配置,内嵌 Tomcat,打完包java -jar直接跑,10 分钟部署到云服务器,答辩演示不翻车。Vue3 组合式 API vs Vue2 Options
Vue2 把数据、方法、计算属性拆成 3 段,写抽奖逻辑要来回翻代码。Vue3 用setup()一把梭,概率算法、库存监听、动画状态全写在同一个函数块,老师一眼就能看懂,还夸“代码结构清晰”。Redis 当“兜底”而不是“点缀”
很多教程把 Redis 当缓存炫技,结果击穿、雪崩全没管。我们直接把 Redis 当成**“库存前置仓库”**,MySQL 退居二线,抗并发、抗回滚、抗手抖。
3. 核心实现:让“抽盒”接口既快又安全
3.1 接口幂等性设计
- 用户点击“抽盒”按钮,前端先拿
uuid当幂等令牌,后端用 RedisSETNX uuid 1 EX 5防重放,5 秒之内同一令牌重复请求直接返回“处理中”,解决双击、F5 刷新带来的重复下单。
3.2 库存扣减 Lua 脚本
把“查库存→减库存”打包成一条 Lua,保证原子性:
-- /lua/boxDecr.lua local key = KEYS[1] -- 盲盒库存 key local num = tonumber(ARGV[1])-- 本次扣减数量 local left = redis.call('GET', key) if not left or tonumber(left) < num then return 0 -- 库存不足 end redis.call('DECRBY', key, num) return 1Java 侧直接redisTemplate.execute(script, keys, args),0 回滚,1 成功,一步完成,MySQL 事务里只负责写订单,压力瞬间降 70%。
3.3 前后端联调关键配置
Axios 拦截器统一加 Token
登录后把 JWT 存localStorage,拦截器自动带Authorization: Bearer <token>,后端直接@RequestHeader取值,省得每个接口手动塞 header。Vue 代理解决 CORS
vite.config.ts里配proxy: '/api': { target: 'http://localhost:8080' },本地开发热更新秒级同步,上线后改 Nginx 转发,0 代码改动。
4. 带注释的代码片段:直接抄也能跑
4.1 Controller 重试机制
@RestController @RequestMapping("/box") public class BlindBoxController { // 库存扣减失败自动重试 3 次,间隔 200ms @Retryable(value = {RedisSystemException.class}, maxAttempts = 3, backoff = @Backoff(delay = 200)) @PostMapping("/draw") public R draw(@RequestBody DrawDTO dto, HttpServletRequest req) { String token = req.getHeader("Authorization"); Long userId = JwtUtil.getUserId(token); return blindBoxService.draw(userId, dto.getBoxId()); } }4.2 Vue3 useStore 封装
// stores/box.ts export const useBoxStore = defineStore('box', () => { const left = ref(0) async function loadLeft(boxId: string) { const { data } = await http.get('/api/box/left/' + boxId) left.value = data } return { left, loadLeft } })组件里直接:
import { useBoxStore } from '@/stores/box' const box = useBoxStore() onMounted(() => box.loadLeft(route.params.id))响应式数据 + 自动 TS 提示,老师看到类型定义直接给加分。
5. 性能与安全:把“玩具”做成“产品”
缓存击穿
热点盲盒上架瞬间,千万级请求直捣数据库。我们用 Redis 互斥锁 + 异步重建:一个请求拿到锁才查库,其余线程睡 50ms 再重试,把 3k QPS 的数据库峰值压到 200 以内。XSS 防护
前端富文本编辑框允许用户上传“开箱感言”,不加过滤直接存库,页面回显就弹窗。Vue3 默认转义 Mustache,但v-html手动绕开。后端用 Jsoup 白名单过滤,只允许<br><p><img>,直接干掉<script>。JWT 续期
访问令牌 15min 过期,刷新令牌 7 天,双 Token 机制 + Redis 存刷新令牌白名单,用户主动退出时清 Redis,既防重放又支持远程踢人。
6. 生产环境避坑指南:云服务器 1 核 2G 也能扛住演示
Nginx 静态资源
把dist目录扔/var/www/html,location /指过去就行,千万别配root再写别名,路径 404 能调一晚上。MySQL 隔离级别
默认REPEATABLE READ在扣库存场景会间隙锁,并发高直接死锁。我们直接降到READ COMMITTED,行锁范围小,配合 Redis 前置,库存数据一致性可接受。JVM 启动参数
1G 小水管云主机,-Xms400m -Xmx400m固定堆,再开 G1,FullGC 停顿从 3s 降到 200ms,答辩切页面不卡壳。
7. 如何扩展成“多商户盲盒平台”?
当前架构是单店铺,商户 ID 写死为 1。要升级多商户,只需 3 步:
- 盲盒表加
merchant_id,抽奖接口多传一个参数,Lua 脚本把商户 key 前缀拼进去,Redis 隔离。 - 订单分库分表用
merchant_id做分片键,ShardingSphere 一行配置即可。 - 前端新建商户后台,复用同一套 Vue 组件,动态路由隔离菜单,打包时
VITE_APP_ROLE=merchant即可。
把单店铺脚手架改成 SaaS,毕业设计直接升“创业计划”,答辩老师想不给优秀都难。
写完这套,我的最大感受是:毕设不是写“玩具”,而是把“玩具”写成“产品”。Redis 原子扣减、JWT 双 Token、Vue3 组合式 API,这些看似“超纲”的技术,其实就是每天面试题里问烂的内容,只是被我们提前搬到项目里。
如果你也在为“盲盒购物”毕设头疼,不妨把上面的 Lua 脚本和重试注解直接粘过去,先跑通 0 超卖,再慢慢加商户、加秒杀、加直播带货。代码丢到 GitHub,写简历时直接甩链接,比任何“熟悉高并发”的形容词都管用。下一步,试试把平台改成支持商户入驻,你会发现——毕业设计才刚开始有趣。