资讯详情

资讯详情

基于SpringBoot+Vue+MySQL的小区疫情购物系统毕业设计

疫情反复那段时间很多小区都进入封闭管理一日三餐忽然成了最具体的问题。为了减少接触同时也不用居民半夜在群里拼手速一个能从手机上下单、由配送员送到楼下的小区购物系统就有了实际意义。我做的这套毕业设计就是用SpringBootVueMySQL把这样一个平台完整搭起来整个项目包含源码、初始化数据库、论文和部署文档。它适合正在挑选题的计算机相关专业学生也适合想快速上手前后端分离项目的人参考尤其是想从“能跑”再往前走一步把库存、订单、配送状态这些东西真正串起来的人。1. 项目从选题到成型小区疫情购物系统到底要解决什么问题1.1 它不是普通商城而是“小区级”的受限电商如果没有限定场景做一个商城系统很常见但“小区疫情购物”这个前缀给系统加了两个约束一是配送半径到小区为止不需要复杂的物流体系二是使用人群中很多是不太熟悉电脑操作的居民界面和下单流程必须足够简单。所以最初的需求拆解我把它分成三个端居民端注册登录、浏览商品、分类搜索、加入购物车、提交订单、查看订单状态、确认收货。配送端查看待配送订单、更新配送状态比如“已送达”后自动完成订单。管理后台商品分类管理、商品上下架、库存修改、订单处理、公告发布、用户列表。这套系统里没有对接真实支付因为毕业设计和演示场景里线上支付需要营业执照、商户号等一堆东西并不适合做一个课程训练项目。订单状态里保留“待付款”实际处理时用“货到付款”或者“线下支付管理员确认”来代替既能体现流程又不会卡在支付接口上。1.2 技术选型为什么是SpringBootVueMySQL很多同学纠结过到底选SSM还是SpringBoot。我的建议很直接如果不是课程强制要求直接SpringBoot。原因是大多数人写前端页面已经很吃力了如果后端还花大量时间配置XML精力根本不够分配。SpringBoot的最大优势是“约定大于配置”内嵌Tomcat一个main方法就能启动。配合MyBatis-Plus操作数据库CRUD基本不用手写SQL可以把注意力放在订单流程、库存扣减这些真正有难度的地方。前端选Vue是因为它组件化程度高。商品卡片、订单状态标签、分页组件这些都可以抽成独立组件后面维护成本低。我采用Vue3 Vite Element Plus如果你的环境里有老项目用Vue2 Element UI也可以核心页面思路完全一致差别主要写在组件语法上。数据库选MySQL原因更直接订单、用户、商品、购物车之间天然是关系结构需要事务保证下单时数据一致MySQL在这类场景里是最稳的选择。2. 数据库建模订单、库存与配送的关键设计2.1 核心表到底要建哪几张我见过很多同学把一张订单表做成“大杂烩”用户信息、商品信息、地址全部塞进去这样开发时确实省事但后面统计就很痛苦。正确做法是按业务领域拆表。实际项目里我建的这几张核心表表名作用sys_user用户信息包含居民、配送员、管理员三种角色goods_category商品分类一级分类即可满足小区场景goods商品表包含标题、图片、单价、库存、销量shopping_cart购物车记录用户加购的商品和数量orders订单主表一个订单对应一个用户、一个总体状态order_item订单明细表一个订单包含多个商品条目notice小区公告比如到货通知、配送安排订单主表和订单明细表的拆分是整个表结构里最容易忽略也最关键的一步。如果只建一张订单表买三件不同商品就要插入三行订单订单编号重复、状态要重复维护非常痛苦。拆成主表和明细表后订单主表记录“这个订单是谁的、什么状态、总共多少钱”明细表记录“具体买了哪些东西、每样几件”逻辑一眼就能看懂。2.2 订单状态不能单靠一个字符串订单状态字段是每个初学者都会碰到的坑。最开始的版本里我用一个status字符串存“未支付”“已支付”“配送中”看起来没问题但后面的业务逻辑只要判断状态就得复制一串字符串常量写错一个字母就查不出来。正确做法是在后端定义一个状态枚举比如public enum OrderStatus { PENDING_PAYMENT(待支付), PENDING_DELIVERY(待配送), DELIVERING(配送中), COMPLETED(已完成), CANCELLED(已取消); private final String desc; OrderStatus(String desc) { this.desc desc; } }数据库里存枚举的name也就是PENDING_PAYMENT这样的字符串页面上显示时再映射成中文。这样做的好处是代码里对状态的判断非常安全重构时也一目了然。2.3 库存不是减到负数而是“抢到才算成功”商品表里有一个stock字段这个字段是所有并发问题的集中爆发点。假设商品库存只剩10件同时有5个用户下单如果后端逻辑是“先查询库存是否充足再执行更新”那5个人都可能查到库存为10最后生成远超库存的订单。我当时的做法是直接在SQL里完成校验和扣减UPDATE goods SET stock stock - #{num} WHERE id #{goodsId} AND stock #{num}这条SQL的执行结果是受影响行数等于1说明库存扣减成功等于0说明库存不足。利用数据库的行锁和原子更新从根上避免超卖问题比“代码里加锁”更可靠也比“先select再update”的写法干净得多。3. 后端实现要点登录鉴权、事务与下单流程3.1 三层架构的边界一定要清晰后端项目我按标准的controller、service、mapper三层划分。Controller只接收参数、做基础校验、返回结果Service层写业务逻辑Mapper负责和数据库交互。很多同学的代码问题不在功能上而是所有逻辑全堆在Controller里导致一个接口几百行后来想改一个判断条件都要找半天。Service层的核心是事务。下单是一个强事务操作创建订单主表、插入订单明细、扣库存、清空购物车四件事必须一起成功或一起失败。实现方式其实很简单在Service方法上加上Spring的Transactional注解中间任何一步抛异常整个事务都会回滚。Transactional(rollbackFor Exception.class) public Long createOrder(CreateOrderDTO dto) { // 1. 生成订单号 // 2. 插入订单主表 // 3. 循环插入订单明细 // 4. 扣减库存 // 5. 清空用户购物车 }3.2 JWT登录鉴权还是Session鉴权毕设项目里Session方案最省事依赖浏览器Cookie登录后服务端存Session天然能识别用户。但问题也很明显如果前后端分离前端部署在Nginx后端是独立jar包跨域情况下Cookie处理起来处处是坑。JWT的方案是用户登录成功时后端用密钥生成一个Token里面携带用户ID和角色前端放在每次请求的Authorization请求头中。后端写一个拦截器拦截除登录注册之外的接口从请求头解析并验证Token。优势是服务端不需要维护Session状态前后端分离部署时最省心。关键点是Token过期时间别设得太长。我当时为了演示方便设成7天结果调试中发现用户改完密码后旧Token还能用一周。后来改成2小时过期并在用户表中增加一个token_version字段改密码时让版本号加1旧Token直接失效这样既简单又安全。3.3 权限控制不能只靠前端隐藏按钮系统里有三种角色居民、配送员、管理员。如果只在前端页面判断角色来决定显示哪个按钮非常危险因为接口本身没有防护用户直接伪造请求就能调用管理接口。我在后端定义了一个简单的RequireRole注解配合拦截器使用。拦截器解析Token拿到用户角色后再判断当前请求的Controller方法上标注的角色是否匹配不匹配就返回403。这样做不需要引入Spring Security全家桶却能让接口层真正具备权限控制能力而且代码量很小。下面是一个典型的Controller写法RestController RequestMapping(/api/goods) public class GoodsController { GetMapping(/list) public Result list(RequestParam Integer page) { return Result.ok(goodsService.pageList(page)); } PostMapping(/save) RequireRole(ADMIN) public Result save(RequestBody Goods goods) { goodsService.saveOrUpdate(goods); return Result.ok(); } }4. 前端Vue落地页面拆分、状态管理与接口对接4.1 工程结构必须从最开始就理顺前端我用Vite创建Vue3项目目录结构不是随便建的而是按照后面的开发路径规划出来的src/ |-- api/ // 按模块拆分的接口文件 |-- router/ // 路由表 |-- store/ // Pinia状态管理 |-- views/ // 页面 | |-- resident/ // 居民端页面 | |-- admin/ // 后台管理页面 |-- components/ // 公共组件 |-- utils/ // axios封装、工具方法很多同学喜欢把所有接口请求直接写在页面里比如用fetch或axios在onMounted里到处请求。短期能跑但后面每个页面都得注意BaseURL、请求头、错误处理代码越来越乱。正确做法是在utils/request.js里封装一个axios实例设置基础路径、超时时间和拦截器页面里只管调用。4.2 请求拦截器和路由守卫前后端鉴权配合的关键是前端请求拦截器自动附加Tokenrequest.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config })路由守卫负责页面跳转时的权限拦截。比如未登录用户访问/admin直接重定向到登录页。我用了meta.roles定义每个路由允许哪些角色访问在beforeEach里逐条判断比在页面里写一堆if判断清晰很多。4.3 居民端和后台的页面重点居民端不需要花哨核心是把“买”这个动作做顺。我实现的页面是首页商品列表、商品详情、购物车、订单确认页、我的订单。首页做了分类筛选和搜索本质上就是一个带参数的查询接口点击分类时重新请求页面列表数据。后台管理端最核心的是商品表格和订单管理。商品表格用Element Plus的Table组件提供上下架开关和库存编辑订单管理页面按状态Tab拆分管理员可以根据订单状态做确认和发货操作。4.4 跨域问题的两层解法开发环境下前端跑在localhost:5173后端跑在localhost:8080端口都不同直接请求后端接口必然跨域。我在Vite配置里加了一个代理server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }这样前端代码里请求/api/goods开发时会被代理转发到后端。生产环境部署则用Nginx统一处理这样前后端共用一个端口不存在跨域问题具体配置在下一部分展开。5. 部署与文档让人照着就能把项目跑起来5.1 本地跑通需要固定的环境组合我自己踩过的一个大坑是版本不匹配。项目代码明明没有问题但换了一台电脑就启动失败排查半天发现是JDK版本不对。这套系统推荐的环境组合是JDK 8或11、Maven 3.6、MySQL 5.7或8.0、Node.js 16。MySQL 8.0版本需要注意驱动配置连接串里加上时区参数url: jdbc:mysql://localhost:3306/community_mall?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai我当时在这个时区参数上卡了半小时一直报“The server time zone value”的异常其实就是MySQL 8默认时区问题。把连接串改好之后立刻启动成功。5.2 初始化数据库的顺序和细节项目里的数据库脚本是db.sql导入时要特别注意先创建数据库再选择库CREATE DATABASE IF NOT EXISTS community_mall DEFAULT CHARACTER SET utf8mb4; USE community_mall; SOURCE /你的路径/db.sql;utf8mb4一定要设否则商品描述里里有少量生僻字或者特殊符号时会报“Incorrect string value”错误。导入成功后再确认一下关键表是否已经生成用SHOW TABLES;查看即可。5.3 生产环境用Nginx部署前后端生产环境我不建议把前端静态文件扔进SpringBoot的static目录那样能跑但后面想单独发布前端就非常别扭。最合理的做法是前端打包成dist目录Nginx同时托管静态资源和反向代理后端接口。下面是我用得最多的Nginx配置片段server { listen 80; server_name _; root /var/www/community-mall/dist; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这段配置里最关键的是try_files $uri $uri/ /index.html;。如果不加这一行用了createWebHistory路由的项目刷新任意子页面都会404刷新首页正常点进详情页再刷新直接白屏。我在几个朋友的电脑上验证过加这段之后就彻底解决。5.4 部署文档才是区分“能跑”和“能交付”的标准拿到毕设源码的人最痛苦的就是不知道怎么跑起来。所以项目里的部署文档绝对不是凑数而是要能让人照着一步步操作成功。我的部署文档按下面这个顺序组织环境准备JDK、Maven、MySQL、Node版本— 数据库初始化 — 后端配置文件修改 — 启动后端 — 启动前端 — 生产环境构建 — 常见问题。常见问题里至少要把数据库连接失败、端口被占用、跨域报错、页面刷新404这四个都写进去。这份文档写得清楚后面的复现者能节省大量时间论文里的“系统测试”章节也有真实依据。6. 答辩环节怎么讲这套系统的下一步还能怎么长6.1 讲项目要讲决策过程不是念PPT答辩时很多人喜欢从头到尾讲功能我反而建议把重点放在三个“决策点”上为什么小区购物系统要有配送端为什么订单表要拆主表和明细表后端如何防止库存超卖。这三点分别对应需求分析、数据库设计和并发处理正好覆盖论文里最重要的几个章节。演示顺序也很有讲究。我习惯先走一遍完整业务流程居民注册—管理员发布商品—居民下单—配送员接单—确认送达。流程通顺后再打开数据库或者后端日志现场展示下单后库存减少、购物车被清空。这一步很容易给评委留下“系统真正做完了”的印象。6.2 从“疫情购物”向普通社区商城演进疫情购物系统本质上是一个带小区配送约束的电商系统所以它很容易扩展成通用社区商城。实际可以做的扩展方向至少有这几个多小区数据隔离商品可以绑定到小区用户只能看到自己小区的库存和公告。优惠券与积分订单金额计算时增加优惠券抵扣配合积分流水表一起设计。订单导出后台用EasyExcel导出每日配送清单方便核对货物。实时通知引入WebSocket配送状态更新后前端页面自动提示用户不用一直刷新。如果打算继续做毕业设计的加分项我建议优先做WebSocket通知。它不复杂但演示效果非常直观配送状态一变居民端页面自动出现“您的订单配送中”答辩时视觉冲击很大。最后再分享一个实在的体会很多初学者拿到源码后第一件事就是开IDE直接跑跑不起来就换项目。但真正有价值的地方反而是数据库脚本里那张订单表、后端那一个Transactional、前端那个请求拦截器。这套项目最浓缩的知识点恰恰就藏在这些最容易被忽略的细节里。如果你正在做类似的毕业设计不妨照着上面的建模思路重画一遍表再重写一遍下单逻辑比单纯把代码跑通有用得多。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →