☰
SpringBoot2+Vue3校园店铺系统全栈实战:架构、表设计与部署
2026/10/8 19:55:38 网站建设 项目流程

SpringBoot2 + Vue3 + MyBatis-Plus + MySQL8.0,这套组合近几年几乎成了校园店铺系统和毕设项目的“标配”。最近又翻到这套带文档的校园网上店铺系统源码,我把它完整跑了一遍,把前后端联调、数据库初始化、关键业务逻辑全部捋了一遍。说实话,整套源码的结构比一般教学项目干净不少,权限控制、订单流程、商品管理都做出来了,不是那种只有几个页面的半成品。

这篇就从实际跑通的角度,把这个系统的整体设计、核心表结构、关键接口实现、前端对接方式和环境搭建过程都拆开讲。不管你是拿它做毕业设计,还是想学前后端分离项目的完整套路,这套代码都挺值得过一遍。

1. 项目整体定位与系统拆解

1.1 校园店铺系统的核心业务范围

这个系统做的是校园网上的购物场景,用户角色分成三类:普通学生用户、后台管理员、系统本身要处理的游客。所谓“校园网上店铺”,本质上就是一个带商品管理、购物车、订单闭环的B2C商城,只不过目标群体锁定在校园内,商品也更偏向二手书、电子产品配件、手工制品这些校园里高频交易的品类。

先说清楚,这种系统和你平时接触的淘宝、京东那种大型电商完全是两码事,它不需要处理秒杀、分布式事务、消息队列这些东西。核心就是一条业务链:用户浏览商品 → 加入购物车 → 提交订单 → 支付结算(一般是模拟支付)→ 后台发货/管理库存。代码里把这条链完整实现了,该有的状态流转都有,这对学习和二次开发来说是很有价值的。

对于打算拿这套代码做毕设的同学,我更建议先把它当成一个“框架样板”来看,而不是直接交上去就完事。你需要在系统里面找到两三个点做深度优化,比如加个商品评论功能、做成多店铺模式、引入Redis缓存热点商品,这些都是在现有代码上能直接扩展的方向。

1.2 用户角色与权限边界设计

整个系统的用户模型分三层,我跑完代码后把它的权限设计梳理成了下面这样:

角色核心能力权限边界
游客/未登录用户浏览商品列表、查看商品详情不能下单、不能加入购物车,页面上的购物按钮会引导去登录
普通用户加购、下单、查看自己的订单列表、管理收货地址、个人资料只能操作属于自己的数据,订单只能查看不能改状态
管理员商品管理(上下架、库存调整)、订单管理(发货、关闭)、用户管理、统计看板不参与购买流程,后台菜单和前端按钮路由都隔离

这套权限模型做得很实用的一点是后端接口层做了拦截校验。我当时翻Controller层代码,发现用了拦截器加自定义注解的方式,接口用@RequirePermission这类注解标一下,拦截器在进入Controller之前就把未登录请求挡回去,返回统一的code,前端拿到之后跳转登录页。这个设计比在业务代码里面一个个判Session要优雅得多。

这里提醒一句:如果要把系统改成其他场景,比如外卖平台、预约平台,权限模型不需要动,直接改业务表和页面就行。这也是为什么这个系统适合当模板——权限和业务是解耦的。

1.3 技术架构与目录结构说明

老规矩,先看技术栈。后端是SpringBoot2,我的建议是用2.7.x这个最终版本,它兼容性最好,也修完了Spring Framework所有已知漏洞。ORM用的MyBatis-Plus,这个选型很合理,因为这种管理系统的CRUD占绝大多数,MyBatis-Plus的BaseMapper几乎能覆盖80%的数据访问需求。前端是Vue3配合Element Plus组件库,构建工具用的是Vite,开发环境下热更新很快。数据库是MySQL8.0,这里面有不少需要注意的版本坑,后面环境搭建部分我专门讲。

源码的目录结构也是标准的单Maven工程加独立前端目录:

backend/ # SpringBoot工程 src/main/java/com/campus/shop/ controller/ # 接口层 service/ # 业务逻辑层 mapper/ # MyBatis-Plus的Mapper接口 entity/ # 数据库实体类 config/ # 配置类(拦截器、MyBatisPlus分页插件等) common/ # 统一返回结果、异常处理、工具类 frontend/ # Vue3工程 src/ api/ # 接口请求封装 views/ # 页面组件 router/ # 路由配置 store/ # Pinia状态管理 utils/ # 请求工具、格式化工具 database/ init.sql # 建库建表脚本

这个目录结构建议直接保留,因为无论是做毕设还是团队协作,一个清晰的项目结构能省掉很多沟通成本。

2. 技术选型背后的关键考量

2.1 为什么这套组合成了主流配置

很多人在选型时会问:怎么不用SpringBoot3?怎么不用Redis?这些疑问我能理解,但校园系统这类项目选型有一个核心原则——用足够成熟、资料足够多的组合,而不是一味追新。

SpringBoot2.7.x是2代SpringBoot的最后一个大版本,市面上所有的教学视频、博客文章、排错案例基本都是基于这套体系写的,遇到问题搜索一下就能找到答案。Vue3 + Element Plus的话,Element Plus是完全匹配Vue3生态的UI库,表格、表单、弹窗这些后台管理需要的东西都能开箱即用。MyBatis-Plus在前几年已经挤掉PageHelper和通用Mapper成为主流,它的LambdaQueryWrapper写条件查询非常舒服,不用再拼字符串SQL了。MySQL8.0则在性能、窗口函数、JSON支持上都比5.7强很多,这也是当前生产环境的默认选择。

简单说,这套组合就是当下Java Web全栈项目最稳妥的“不犯错选项”。不出彩,但绝对不翻车,任何问题都能搜到现成的解决方案。

2.2 MyBatis-Plus在项目里帮我们省了什么

这个系统的数据层基本没有被SQL语句淹没,我数了一下,整套代码里手写的SQL不超过十句,全是MyBatis-Plus在干活。日常的插入、更新、分页查询、条件构造器,都是直接调用BaseMapper自带的方法。

举几个实际例子:

// 商品列表查询,直接用LambdaQueryWrapper拼条件 LambdaQueryWrapper<Goods> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.hasText(query.getKeyword()), Goods::getTitle, query.getKeyword()) .eq(query.getCategoryId() != null, Goods::getCategoryId, query.getCategoryId()) .eq(Goods::getStatus, 1) .orderByDesc(Goods::getCreateTime);

这种写法比手写SQL清晰很多,而且是类型安全的,字段名写错了编译期就报错了。加上MyBatis-Plus的分页插件,只需在配置文件里注册一个PaginationInnerInterceptor,之后所有Page<Goods>请求都会自动拼上limit语句。

此外系统还用了MyBatis-Plus的自动填充能力。在配置类里实现MetaObjectHandler,对createTime、updateTime这两个字段做了统一处理,新增记录时自动写入当前时间,更新时自动修改——这就不用每个Service方法里面手动set了。

2.3 前后端分离模式下的调试方式

既然是前后端分离项目,就有一个躲不开的问题:本地开发时,前端页面调后端接口怎么解决跨域?这个代码里用了最最常用的方案——Vite开发服务器代理。前端启动在5173端口,所有请求都会走Vite的proxy配置转发到后端8080端口,浏览器看到的请求是同源的,跨域问题直接在源头就解决了。

// vite.config.js 关键代理配置 server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }

这个配置非常关键。如果你看到前端页面上接口全部加载失败,十有八九就是代理没配好或者后端端口对不上。在部署到生产环境时,则可以用Nginx把前端静态文件和后端接口放在同一个域名下,用location规则区分。

顺便提醒一下:前端请求路径用了统一前缀/api,后端Controller的映射路径前面也带了/api。这个约定很重要,代理、后端路由、Nginx转发全都围绕这个前缀来,你如果改了前缀,这几处要同步改。

3. 核心表结构设计与业务关联解析

3.1 数据模型与核心表拆解

数据库设计直接决定一个管理系统的上限,这套代码的数据库一共七张表:user(用户)、category(商品分类)、goods(商品)、cart(购物车)、order(订单)、order_item(订单明细)、address(收货地址)。

给你们画一下核心的关联关系:用户表作为主表,关联出购物车表、订单表、地址表。商品表关联分类表,订单表通过订单明细表跟商品表建立多对多关系——一个订单里可以包含多个商品,一个商品也可以出现在多个订单里,所以必须拆一张order_item中间表出来。

这种设计是电商系统最经典的表结构,没有花哨的冗余设计,胜在简洁清晰。下面我挑两张比较关键的表展开说明。

3.2 商品表和订单表设计中的细节含义

商品表(goods),重点不在字段多,而在状态管理。它有一个status字段,0表示下架、1表示上架。这个字段的作用不只是“能不能看到”,它直接参与库存扣减逻辑。我在测试的时候试过:一个商品上架状态时被加进购物车,管理员在后台把它下架,用户结算的时候就发现这个商品会被提示“商品已失效”。这是最基本的上下架一致性控制。

**订单表(order)**的大字段不多,但状态机很重要。订单状态用status表示,流程是:待付款(0)→ 待发货(1)→ 已发货(2)→ 已完成(3),另外还有已取消(4)和已关闭(5)。其中已取消是用户主动取消,已关闭是超时未付款系统自动关闭。我当时在测试接口时,直接把一个订单从待付改成待发,就发现列表里订单的前端按钮渲染也跟着变——这说明前端按钮状态是后端接口返回的状态码驱动的,没有写死。

在设计上有个值得表扬的细节:订单表里冗余了商品名称、商品主图、商品价格这几个字段(以快照形式存在)。为什么要冗余冗余?因为用户下单之后,管理员修改了商品价格,这个订单依然要按下单时金额结算。如果关联实时查商品表,订单金额就会被篡改。所有成熟的电商系统都是用快照字段存下单时刻的数据,这套代码虽然小,但这一点做得是合格的。

3.3 初始数据与测试账号说明

初始化SQL脚本里面带了完整的测试数据,包括管理员账号、测试用户、若干商品和分类数据。我启动完系统直接登录进去,发现预置的数据量足够把整个流程走通。测试账号大概是:

  • 管理员:admin / admin123,登录后跳转的是后台管理界面
  • 普通用户:test / 123456,登录后是商城购物界面

两种账号的登录入口虽然是同一个页面,但登录之后前端根据后端返回的角色字段做了路由分流。这个逻辑在实际项目中很常见,也是你要二次开发时最先要弄清楚的部分。

4. 核心业务模块与关键实现细节

4.1 用户认证与Token机制实现

登录模块用的是JWT(JSON Web Token),用户登录成功之后,后端签发一个带过期时间的token,前端把它存到localStorage里。之后每次请求,前端通过请求拦截器把token放到Authorization请求头里,后端用拦截器解析token、识别用户身份。

后端这边实现了一个JwtInterceptor,在WebMvcConfig里面注册拦截,排除登录、注册、商品列表这些无需鉴权的接口,其余接口全部要过token校验。校验不通过直接返回401状态码,前端axios的响应拦截器统一处理,跳回登录页。

这里有一个SpringBoot的小配置坑要提醒:拦截器注册的时候,excludePathPatterns的路径一定要和后端Controller的请求路径写得一模一样,否则就会出现“登录成功后,用户信息接口还是被拦截”的bug。如果遇到这种问题,优先检查是不是路径写错了。

前端配合的axios封装也是标准写法:

// 请求拦截器——自动携带token service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = token } return config }) // 响应拦截器——统一处理错误码 service.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { if (res.code === 401) { router.push('/login') } ElMessage.error(res.msg) return Promise.reject(new Error(res.msg)) } return res } )

这套封装是Vue3项目里的通用写法,直接背下来用就行。

4.2 商品模块的查询、上下架与图片管理

商品模块是整个系统最基础的部分,也是页面最多的模块。用户端有商品列表页、商品详情页,管理端有商品维护页、分类管理页。列表页的搜索条件包括关键字模糊查询、分类筛选、价格排序,这些全部通过MyBatis-Plus的条件构造器完成,性能上对于几千条数据量完全够用。

商品图片这里想重点强调一下。系统的图片上传是走本地上传的方式,上传之后保存在后端的upload目录,通过一个静态资源映射暴露到URL。配置方式是在SpringBoot的WebMvcConfig中增加了一个addResourceHandlers方法,把本地目录映射成/uploads/**访问路径。

这个方案简单、本地跑没问题,但有一个致命弱点:上传的图片是保存在服务器磁盘上的,如果部署环境变了、磁盘清理了,图片就丢了。毕设答辩如果被问到,你可以提出升级方案——改造成OSS或者MinIO对象存储,这反而是个加分项。

4.3 购物车与订单提交的事务控制

购物车逻辑是典型的“非强一致”需求:用户往购物车加商品、修改数量、删除条目,这些操作不需要保证高并发一致性,只要数据不丢就行。实现上就是一张cart表,以userId和goodsId联合标记唯一记录,用户再次添加同一商品时做数量累加,而不是插入新记录。

订单提交这块就值得细看,因为涉及到多表操作,必须保证要么全部成功,要么全部失败。系统在@Transactional注解的基础上,在下单Service方法里一步步完成四件事:

  1. 幂等校验:检查购物车是否为空、商品是否上架、库存是否足够
  2. 订单主表落库:生成订单号,写入总金额、收货地址、状态为待付款
  3. 订单明细批量插入:遍历购物车,生成每一件商品的快照记录
  4. 清空购物车并扣减库存:把商品的stock字段减掉

这个流程用@Transactional包上之后,任何一步抛出异常都会触发整体回滚。我测试过手动在扣库存那一步抛一个异常,最后订单表、明细表、库存、购物车全部保持原样,事务生效。

唯一要注意的是,现在的代码是用户下单后立即扣库存,属于“下单锁库存”模式。如果要改成“支付锁库存”,那得把库存扣减挪到支付回调接口里去做,那又是另一套逻辑了。

4.4 管理员模块的功能分布与数据看板

后台管理员部分功能集中在商品管理、订单处理和数据统计。出乎意料的是,这套代码的统计看板并不是简单写死数据,而是用SQL聚合查出来的。首页看板展示了商品总数、订单总数、用户总数,还有一张按日期统计的成交额折线图——数据是后端按天聚合返回的。

订单管理这边,管理员可以对待发货订单做发货操作,发货后订单状态变为已发货等待用户确认。用户端点击“确认收货”后,状态变为已完成。整个状态流转是闭环的,不会出现某个订单卡死在中间状态的情况。

这块倒是可以给个优化建议:给订单管理页加一个导出Excel功能,用EasyExcel或POI,这是毕业设计里非常常见的加分点。

5. 环境搭建与本地运行完整记录

5.1 MySQL8.0的安装与初始化脚本执行

数据库是第一个要准备的环境。MySQL8.0你可以选两种方式装:Windows本机直接下载安装包,或者用Docker拉镜像一条命令跑起来。如果只是本地开发,推荐Docker方式,省心免安装。

# Docker方式,一行命令跑起MySQL8 docker run --name mysql8 -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=root \ -e MYSQL_DATABASE=campus_shop \ -d mysql:8.0

等容器启动后,用命令执行初始化脚本:

docker exec -i mysql8 mysql -uroot -proot < database/init.sql

如果你是手动安装的MySQL8.0,需要亲自主意一个坑——MySQL8.0默认的密码验证插件是caching_sha2_password,而老版本SpringBoot2.7以下的驱动可能存在兼容性问题,连不上会报表Public Key Retrieval is not allowed。解决方法是改连接串加个参数allowPublicKeyRetrieval=true,或者在创建用户时指定mysql_native_password。

这个系统的application.yml里用的连接串是标准写法:

spring: datasource: url: jdbc:mysql://localhost:3306/campus_shop?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root

注意driver-class-name必须写成com.mysql.cj.jdbc.Driver,这是MySQL8.0后的新驱动类,MySQL5.7时代的com.mysql.jdbc.Driver已经不推荐了。serverTimezone=Asia/Shanghai必须加,不然后端连接时会报时区错误。

5.2 后端项目启动配置与常见报错

数据库就绪之后,直接用IDEA打开后端目录,等Maven把依赖下载完就能启动。JDK建议用1.8或者11,千万别用17以上的版本跑SpringBoot2.7,Tomcat模块会有兼容性问题。Maven中央仓库下载完毕后,运行主启动类,看到控制台输出“Started ... Application”就说明后端起来了。

遇到的比较多的错误是数据库连不上,控制台报错像这样:

CannotGetJdbcConnectionException: Failed to obtain JDBC Connection

排查顺序是:MySQL服务有没有启动 → 端口是不是3306 → 账号密码对不对 → 数据库名有没有建错 → 连接串时区后面的编码对不对。大多数情况下都是数据库名或者密码不对,别一上来就怀疑配置有问题。

5.3 前端项目启动与Node版本注意事项

前端部分先确认Node环境,建议用Node.js 16或18的稳定版。Node版本太低会导致Vite跑不起来,太高(比如20+)有些老项目的依赖也会报错。执行安装依赖和启动命令:

npm install npm run dev

依赖安装比较慢,耐心等一等。启动成功后在浏览器访问http://localhost:5173就能看到商城首页了。

这里补充一个前端坑:如果npm install的时间特别长或者卡住,多半是因为网络问题导致的依赖解析缓慢。直接把npm源切到国内镜像再重装:

npm config set registry https://registry.npmmirror.com rm -rf node_modules npm install

5.4 打通前后端的最终验证流程

前后端都启动之后,用本来就有的测试账号走一遍完整流程验证系统是否健康:

账号起服务、登录、选一件商品加入购物车、去结算、填地址、下单,然后切管理员账号去后台看到这笔订单并完成发货。如果这整个链路都通了,那说明项目环境完全正常,可以进入开发模式了。

6. 实操中遇到的典型问题与排查技巧实录

6.1 数据库连接失败与登录报错

这个问题出现的频率最高。除了上面说的连接串问题,还有一个常见的是MySQL8.0的密码加密插件不兼容。解决方法是给MySQL8里的用户指定密码规则:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';

如果你数据库里已经建好了,执行完这句再刷新一下权限就行。另外,启动SpringBoot项目时如果报日期类型转换错误,记得检查serverTimezone这个参数,最好改成Asia/Shanghai,避免UTC时区导致数据库时间和本地时间相差8小时。

6.2 前端接口404与跨域问题的区分

页面能打开但数据加载不出来时,第一件事就是看浏览器F12的Console和Network面板。如果Network里请求状态是404,先去看请求路径是什么。推进顺序是:

前端请求发出来之后,如果走的是Vite代理,那Network面板里显示的还是前端地址/api/xxx,代理转发了8080端口的实际接口。404通常有两种:后端没有这个接口,或者代理路径没配对,导致请求落到了SpringBoot的404页面上。

如果请求状态是CORS error,说明代理没生效,你直接访问的是后端地址,前端页面的跨域策略拒绝了响应。遇到这个就去看vite.config.js的proxy配置是不是被注释了,或者后端端口是不是改成了8081但代理里还写的是8080。

6.3 列表页数据不显示但接口正常的排查

有时候接口返回是200,前端表格却空白。原因大概率是字段名对不上。MySQL表字段习惯用下划线命名(比如create_time),后端实体类用驼峰命名(createTime),MyBatis-Plus在默认配置下会自动做驼峰转换。但如果接口返回的JSON字段名是下划线风格,而前端组件里写的是驼峰取值,那就会空白。

我的排查经验是直接在Network面板看接口返回的JSON结构,跟前端页面上写的字段名对一遍。这一步虽然基础,但能解决大半“数据不显示”的问题。

7. 基于源码的二次开发方向建议

7.1 功能增强的四个潜力方向

源码本身已经完整,但如果你想做出差异化,可以考虑这四个方向:

  • 秒杀特价:给商品增加秒杀时段和秒杀价,用Redis预扣库存,这是非常经典的高并发案例
  • 商品评论系统:增加评论表和评分字段,订单完成后允许用户评价,界面可以参考电商应用的评论区
  • 订单导出Excel:给管理端加个导出功能,用EasyExcel两小时就能搞定
  • 首页推荐位:把商品表加个recommend字段,首页动态展示推荐商品,增加用户体验

7.2 性能优化与代码层面的改进空间

从代码严谨性的角度,有几个地方值得优化:

  • 统一异常处理:虽然代码有一个GlobalExceptionHandler,但可以补全更多业务异常类型,让错误信息更友好
  • 接口校验增强:在实体类上加上@NotBlank、@NotNull等JSR-303校验注解,避免脏数据进入数据库
  • Redis整合:把登录token状态、商品热度计数放进Redis,降低MySQL压力

我自己的体会是,这种校园店铺系统,真正的难点不在于功能多么复杂,而在于把每一步业务逻辑想清楚、把数据流转做严谨。整套源码跑下来,最值得学习的地方倒不是哪个炫技的技术点,而是它对主流程的完整闭环处理——从商品浏览到订单完结,没有断头路。如果你准备拿它做毕业设计,我的建议是不要急着改代码,先把数据库表之间的关联和订单状态流转看透,然后再动手做定制。等到二次开发做完,你会发现原来那些让人头疼的表设计和状态机,才是最值钱的经验积累。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询