SpringBoot2+Vue3校园店铺系统全栈实战:架构、表设计与部署

📅 发布时间:2026/10/8 19:55:51
SpringBoot2+Vue3校园店铺系统全栈实战:架构、表设计与部署
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拼条件 LambdaQueryWrapperGoods 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之后所有PageGoods请求都会自动拼上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机制实现登录模块用的是JWTJSON 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方法里一步步完成四件事幂等校验检查购物车是否为空、商品是否上架、库存是否足够订单主表落库生成订单号写入总金额、收货地址、状态为待付款订单明细批量插入遍历购物车生成每一件商品的快照记录清空购物车并扣减库存把商品的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_PASSWORDroot \ -e MYSQL_DATABASEcampus_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。解决方法是改连接串加个参数allowPublicKeyRetrievaltrue或者在创建用户时指定mysql_native_password。这个系统的application.yml里用的连接串是标准写法spring: datasource: url: jdbc:mysql://localhost:3306/campus_shop?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/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已经不推荐了。serverTimezoneAsia/Shanghai必须加不然后端连接时会报时区错误。5.2 后端项目启动配置与常见报错数据库就绪之后直接用IDEA打开后端目录等Maven把依赖下载完就能启动。JDK建议用1.8或者11千万别用17以上的版本跑SpringBoot2.7Tomcat模块会有兼容性问题。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 install5.4 打通前后端的最终验证流程前后端都启动之后用本来就有的测试账号走一遍完整流程验证系统是否健康账号起服务、登录、选一件商品加入购物车、去结算、填地址、下单然后切管理员账号去后台看到这笔订单并完成发货。如果这整个链路都通了那说明项目环境完全正常可以进入开发模式了。6. 实操中遇到的典型问题与排查技巧实录6.1 数据库连接失败与登录报错这个问题出现的频率最高。除了上面说的连接串问题还有一个常见的是MySQL8.0的密码加密插件不兼容。解决方法是给MySQL8里的用户指定密码规则ALTER USER rootlocalhost 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后端实体类用驼峰命名createTimeMyBatis-Plus在默认配置下会自动做驼峰转换。但如果接口返回的JSON字段名是下划线风格而前端组件里写的是驼峰取值那就会空白。我的排查经验是直接在Network面板看接口返回的JSON结构跟前端页面上写的字段名对一遍。这一步虽然基础但能解决大半“数据不显示”的问题。7. 基于源码的二次开发方向建议7.1 功能增强的四个潜力方向源码本身已经完整但如果你想做出差异化可以考虑这四个方向秒杀特价给商品增加秒杀时段和秒杀价用Redis预扣库存这是非常经典的高并发案例商品评论系统增加评论表和评分字段订单完成后允许用户评价界面可以参考电商应用的评论区订单导出Excel给管理端加个导出功能用EasyExcel两小时就能搞定首页推荐位把商品表加个recommend字段首页动态展示推荐商品增加用户体验7.2 性能优化与代码层面的改进空间从代码严谨性的角度有几个地方值得优化统一异常处理虽然代码有一个GlobalExceptionHandler但可以补全更多业务异常类型让错误信息更友好接口校验增强在实体类上加上NotBlank、NotNull等JSR-303校验注解避免脏数据进入数据库Redis整合把登录token状态、商品热度计数放进Redis降低MySQL压力我自己的体会是这种校园店铺系统真正的难点不在于功能多么复杂而在于把每一步业务逻辑想清楚、把数据流转做严谨。整套源码跑下来最值得学习的地方倒不是哪个炫技的技术点而是它对主流程的完整闭环处理——从商品浏览到订单完结没有断头路。如果你准备拿它做毕业设计我的建议是不要急着改代码先把数据库表之间的关联和订单状态流转看透然后再动手做定制。等到二次开发做完你会发现原来那些让人头疼的表设计和状态机才是最值钱的经验积累。