1. 校园便利平台管理系统的设计思路做这类校园便利平台管理系统说白了就是在校园这个半封闭、高密度、需求集中的场景里搭一套连接供需双方的在线交易与管理闭环。我最初接到这个项目需求时第一反应是这不就是一个典型的信息管理系统加电商交易流程的组合吗但真正动手之后才发现校园场景比其他电商平台多了不少隐藏需求比如订单履约如何跟校园配送结合货品如何做分校区库存管理学生身份怎么验证等等这些都会直接影响系统架构的设计。选用SpringBootVue这套组合本质上不是因为它“流行”而是它确实适合这个体量的项目。先说后端SpringBoot的优势在于让开发者不用再纠结大量XML配置内嵌Tomcat可以直接跑起来配合MyBatis做数据访问层整个链路非常清晰。Java语言本身的类型安全和面向对象设计在业务逻辑比较复杂的管理系统里特别有用编译期就能发现一大批低级错误这对一个需要长期迭代的课程设计或毕业设计项目来说是在最开始就应该明确的技术路线。前端选Vue核心原因是它组件化的开发方式特别适合这种功能模块多、页面之间有关联的管理平台。Vue通过数据驱动视图不用像jQuery那样手动去操作DOM这让开发效率成倍提升。同时Vue Router做前端路由控制配合Vuex或Pinia做状态管理整个前端代码结构非常干净模块与模块之间互相不打扰后期要加功能只动对应的组件就行。再说说为什么需要“平台管理系统”的逻辑闭环。校园便利平台如果只做前端页面那只是一个空壳展示真正有价值的是管理后台管理员要能看到所有订单、用户、商品、库存数据并且能对这些数据进行增删改查操作。这个“管理”的维度是整套系统的价值核心。通过SpringBoot提供的RESTful接口加上JWT做接口鉴权前台用户端和后台管理端共用一套接口只是根据登录用户角色返回不同的数据和权限这个设计在很多企业级项目中同样适用。从项目规模上看SpringBoot Vue MySQL MyBatis恰好是校园便利平台这类中等复杂度系统的最佳拍档。MySQL负责数据持久化MyBatis负责SQL层面的灵活控制SpringBoot负责业务逻辑调度Vue负责展示和交互。这个技术栈的选择不需要太多炫技每一步都是经过大量项目验证过的稳妥方案。1.1 需求分析校园场景下的特殊考量校园便利平台和其他电商系统最大的不同在于用户画像和消费场景非常集中。学生的消费习惯是高频、小额、就近而校园内的配送距离通常被限制在一个校区甚至一栋宿舍楼内。这意味着系统不需要复杂的物流追踪但一定要有清晰的分区域管理逻辑。我在做需求调研时专门走访了几家校园便利店发现大多数店铺还是靠微信群接龙下单用Excel管库存效率极低而且经常出现“已经下了单但到店没货”的情况。系统的用户角色我划分成三类普通学生用户前台消费者、商家/管理员商品与订单管理者、超级管理员系统维护者。学生用户能浏览商品、加购物车、下单、查看订单状态、申请售后商家能上架商品、修改库存、处理订单超级管理员负责审核商家入驻、查看全平台数据统计、处理投诉建议。这个角色划分不算复杂但有一个关键点必须注意校园便利平台涉及资金流转所以订单状态的机必须得严谨。我设计的状态机是待支付→已支付备货中→配送中→已完成中间穿插一个“退款/售后”的旁路状态。这样每个环节都有据可查管理员能及时发现卡在某一步时间过长的订单。1.2 技术选型全景为什么是这套组合而不是别的技术选型这件事网上争论很多。有人觉得用MyBatis-Plus就够了没必要手写Mapper XML有人觉得前端用Vue2老项目稳定没必要升级Vue3。我的看法是一切以项目实际需求为前提。这套校园便利平台用原生MyBatis是因为它的SQL控制粒度更细在写复杂多条件查询时比如“查询某一分类下销量大于50的所有商品并按库存量排序”XML里写动态SQL比用MP的LambdaQueryWrapper来得更直观。前端用Vue2还是Vue3实际取决于你手上拿到的参考项目和团队的熟悉度。如果是从零开始新写我更推荐Vue3 Vite的组合构建速度快组合式API写起来也舒服。但如果你的毕业设计参考的是老代码用Vue2 Vue CLI反而更稳因为网上现成的教程和踩坑记录远多于Vue3。数据库层面选择MySQL几乎不需要犹豫。MySQL 5.7至今仍然是社区里最稳定的版本之一InnoDB引擎支持事务对于订单、支付流水这类需要强一致性的核心表非常关键。如果非要选8.0也可以但在Maven依赖和连接驱动上要注意版本对应关系否则很容易碰到ssl连接报错之类的低级坑。技术栈选型原因使用场景SpringBoot 2.x稳定、社区资料多、内嵌Tomcat后端接口、业务逻辑Vue 2.x / 3.x组件化、路由管理成熟前台商城、后台管理页面MySQL 5.7轻量、事务支持好、教程最全网数据持久化存储MyBatisSQL细粒度控制、动态SQL灵活数据访问层编写JWT Jasypt无状态鉴权、配置文件加密登录认证、敏感信息保护这里最想多说两句的是“配置加密”。很多学生在项目里直接把数据库密码明文写在application.properties里平时交作业没什么但一旦项目被传到GitHub上等于是把自己的数据库裸奔了。用Jasypt对密码做一次加密处理成本极低但是整个项目的安全档次明显不一样这个细节面试官很看重。2. 核心功能模块拆解与数据库设计校园便利平台管理系统的数据库设计是整个项目的地基地基没打好后面写再多的业务代码都是空中楼阁。我在设计表结构时非常谨慎前前后后花了将近半天的时间梳理所有实体之间的关系。这个系统涉及的实体主要包括用户含学生和商家角色、商品分类、商品SPU/SKU、购物车、订单、订单明细、收货地址、公告信息、系统反馈。每一张表的设计都遵循了三个原则第一主键一律用自增ID不对业务字段做主键订单号就是订单号不应该被用作主键索引UUID做主键在大表场景下会导致随机IO和页分裂问题自增ID写性能是更好的第二所有时间字段用datetime类型精确到秒并用DEFAULT CURRENT_TIMESTAMP自动填充第三所有表都带create_time和update_time两个审计字段哪怕有些表用不到这也是后期排查数据问题的底牌。2.1 关键表结构从用户表到订单表的完整设计先说用户表。这张表是所有表里最早设计的因为它被其他所有业务表引用。字段包括id、username用户名唯一索引、passwordBCrypt加密后的密文、phone手机号、role角色1学生 / 2商家 / 3超级管理员、avatar头像URL、status状态1正常 / 0封禁、create_time、update_time。这里最值得一提的就是密码不能明文存储必须用BCryptPasswordEncoder做哈希理由很简单一旦数据库泄露明文密码会让用户在其他平台撞库这是一个开发者最基本的职业道德。商品表的设计稍微复杂一点。考虑到校园便利店可能出售同一商品的不同规格比如一瓶可乐分为500ml和1.25L我把商品拆成product和product_sku两张表。product表存放通用信息name、description、cover_image、category_id、status上架/下架product_sku表存放SKU粒度信息product_id、spec_name如“500ml”、price、stock、sales累计销量用于排序。两张表通过外键关联再配合sales累计销量字段在商品列表页面实现“按销量排序”时就能直接order by不用再做统计查询。订单相关表是整套系统复杂度的天花板。我设计了三张表orders主表、order_item明细表、order_status_log状态日志表。orders记录整体信息order_no订单编号唯一索引、user_id、store_id所属店铺、total_amount、pay_amount实付金额、status、receiver_name、receiver_phone、receiver_address、create_time、pay_time、delivery_time。order_item记录每一件商品的快照order_id、product_id、sku_id、product_name、product_image、price下单时的价格日后价格改了不影响历史订单、quantity。order_status_log则记录订单状态变化的每一次流转在后台管理页面的“订单详情”里可以完整追溯。最重要的一条设计经验订单明细里的商品名称和价格一定是从SKU表冗余过来的而不是下单时去实时关联查询。这样即使商家日后改了商品价格或名称历史订单依然能保持下单时的原始快照。这是电商系统里快照模式最核心的思想也是面试时能拉开差距的设计亮点。2.2 索引与事务让小项目也有高性能的底气校园便利平台虽然数据体量不大但索引设计从第一天就该按规范做。我的原则是查询频率高的字段建索引但绝不建过多每个表索引数控制在5个以内。用户表的username、订单表的order_no和user_id、商品表的category_id、SKU表的product_id这些都是理所当然的索引候选。特别是订单表如果未来数据量增长到百万级没有user_id上的索引每次查询都是全表扫描体验会非常糟糕。事务设计是订单模块的重中之重。用户下单这个动作涉及多个步骤校验SKU库存→扣减库存→生成订单主表记录→生成订单明细记录→清空购物车对应条目。这几步要么全部成功要么全部回滚绝不能出现“库存扣了但订单没生成”这种数据不一致的情况。在SpringBoot中最简单的做法就是在Service方法上标注Transactional注解。但有一个细节很多人没注意到事务方法一定不能是同一个类中的自调用否则事务会失效因为Spring事务是通过AOP代理实现的自调用不会经过代理。我把这个坑踩了之后专门拉了一段代码说明这也是不少MyBatis面试题中会考的Spring事务底层机制。2.3 详细建表SQL演示我在这里贴一段核心的、可直接执行的学生用户表与订单表的建表SQL转成文本后大家可以直接拿去MySQL里执行。其他数据表的逻辑类似都是基于该模板扩展而来-- 学生用户表角色区分与账户状态 CREATE TABLE tb_user ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键ID, username VARCHAR(50) NOT NULL COMMENT 用户名唯一, password VARCHAR(100) NOT NULL COMMENT BCrypt加密后的密码, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, avatar VARCHAR(255) DEFAULT NULL COMMENT 头像URL, role TINYINT NOT NULL DEFAULT 1 COMMENT 角色1-学生 2-商家 3-超级管理员, status TINYINT NOT NULL DEFAULT 1 COMMENT 状态1-正常 0-封禁, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表; -- 订单主表 CREATE TABLE tb_orders ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键ID, order_no VARCHAR(32) NOT NULL COMMENT 订单编号唯一, user_id BIGINT UNSIGNED NOT NULL COMMENT 下单用户ID, store_id BIGINT UNSIGNED NOT NULL COMMENT 所属店铺ID, total_amount DECIMAL(10,2) NOT NULL COMMENT 商品总额, pay_amount DECIMAL(10,2) NOT NULL COMMENT 实付金额, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0-待支付 1-已支付备货 2-配送中 3-已完成 4-已取消 5-退款中, receiver_name VARCHAR(50) NOT NULL COMMENT 收货人姓名, receiver_phone VARCHAR(20) NOT NULL COMMENT 收货人电话, receiver_addr VARCHAR(255) NOT NULL COMMENT 收货地址, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 下单时间, pay_time DATETIME DEFAULT NULL COMMENT 支付时间, finish_time DATETIME DEFAULT NULL COMMENT 完成时间, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单主表;执行的时候注意两点一是字符集必须用utf8mb4因为商品名称和收货地址中可能出现特殊Unicode字符比如表情符号utf8存不下会报错二是创建唯一索引和普通索引的选择order_no一定用唯一索引防止重复下单user_id上则用普通索引即可因为一个用户有多条订单是正常的。3. 后台核心功能设计与代码实现后台管理是整个系统的“方向盘”。没有后台用户端的商城页面再漂亮也只能看着商品得靠人手动改数据库订单得靠人手动改状态这完全不是现代软件该有的样子。SpringBoot负责把所有管理逻辑封装成接口Vue负责把这些接口渲染成有操作界面的页面这是整套系统的核心闭环。后台管理界面我建议用现成的Vue后台模板。市面上的vue-element-admin或者若依框架的RuoYi-Vue都是很成熟的方案不用重新造轮子。但这里有一个实用建议如果是毕业设计最好不要直接用别人的开源后台套在自己项目里就完事因为答辩时老师问到“这个路由怎么实现的”“权限控制怎么做”会答不上来。正确做法是理解模板的代码组织方式然后按自己项目的实际功能重构一遍哪怕代码写得不那么优雅但每一行都是自己写出来的答辩的时候站得稳。3.1 后端Controller层设计实践后端代码组织我延续了经典的分层架构Controller接收请求→Service处理业务逻辑→MapperMyBatis完成数据访问。Controller层不写任何业务逻辑只做参数接收、合法性校验和调用Service。这套分层规范看着像废话但实际项目里能严格遵守的人不多。我见过太多把查询语句写在Controller里的代码改一个逻辑要翻遍整个类维护成本极高。以商品管理为例GoodsController提供以下接口新增商品POST /api/goods、修改商品信息PUT /api/goods/{id}、删除商品DELETE /api/goods/{id}、分页查询商品GET /api/goods?page1size10keywordxxx、上下架PUT /api/goods/status。分页查询这里有个经验不要手写LIMIT offset, size直接用MyBatis的分页插件PageHelper一行代码就能完成分页而且不会出现越界问题。这类Controller代码写法上有一个很容易踩的坑就是参数校验。前端传入的参数必须做两层校验第一层在Controller入口手动用Validated注解加一些正则约束第二层在Service层做业务校验。比如新增商品时前端传了一个负数的价格或库存如果Controller层不拦截Service层也不校验脏数据直接进数据库后期统计报表的数值就乱了。3.2 前端Vue页面与交互层面实现前端的核心页面包含登录注册页、商城首页商品分类展示、商品详情页、购物车页、订单确认页、订单列表页、个人中心页以及后台管理的商品管理、订单管理、用户管理、数据统计等模块。Vue技术栈中路由守卫是面试时的高频考点在路由跳转前判断用户是否登录未登录则直接跳到登录页这个机制在前后端分离项目中非常重要既防止了页面被随意访问也为Ajax拦截、接口鉴权提供了兜底。我建议前端代码统一封装一个request.js基于Axios做二次封装。核心作用是三个一是自动附加Token到请求头二是统一拦截HTTP错误码比如401就清除本地登录状态跳转登录页500就弹出Toast提示服务异常三是统一从响应体中解构数据让页面代码只关心业务数据本身。这个封装能省下大量重复的错误处理代码这个习惯一旦养成后面写任何前后端分离的项目都能直接复用。3.3 接口鉴权与安全设计JWT的完整落地校园便利平台的接口鉴权我使用的是JWT方案流程是用户登录成功后后端生成一个包含用户ID和角色信息的Token返回给前端前端存在localStorage中每次请求带上Authorization: Bearer token后端通过拦截器验证Token合法性并解析出当前用户信息。JWT设计里有几个关键点值得展开。第一Token的有效期不宜过长一般设置2小时同时配合RefreshToken机制做自动续期。对于校园平台这种低频使用的场景我直接将Token有效期设置为24小时到期后用户需要重新登录倒也够用。第二登录时一定要把用户的角色信息写到Token里这样后续的权限判断就不用到数据库里再查一遍用户角色了。第三用户表里的密码要存BCrypt密文登录时用BCryptPasswordEncoder.matches()方法校验明文密码和密文是否匹配千万不要用MD5因为MD5已经被彩虹表打穿了加盐也没用这个判断标准可以放进任何Java面试题的回答中。注意JWT无状态鉴权带来的一个隐患是服务端无法主动让一个Token作废。如果学生用户被盗号了你只能等他Token自然过期。要解决这个问题可以把Token存入Redis并设置过期时间每次请求时先查Redis如果不存在就直接拒绝。这算是一个升级版的JWT落地方案但会增加系统复杂度校园便利平台用基础的JWT方案已足够了。4. 订单核心流程与购物车机制深度解析订单模块是整个系统业务逻辑最核心的部分。用户从点击“加入购物车”到最后“确认收货”中间经历了完整的状态流转任何一个环节处理不好都会直接影响用户体验甚至造成资损。我在设计该模块时参考了不少真实校园便利平台的订单处理逻辑并在此基础上给出了一套简洁可靠的实现方向。4.1 购物车设计临时表还是内存计算购物车在数据库层面的设计有两种方案一种是登录后把购物车数据存在数据库表中换设备也能同步配合前端Cookie或localStorage做本地缓存另一种是完全不建购物车表前端用localStorage存储下单时一次性把商品ID列表传给后端。校园便利平台我选了前者主要原因是学生在宿舍、教室、食堂等多个场景切换设备购物车需要跨端同步仅靠前端localStorage是做不到这点的。购物车表的核心字段是id、user_id、sku_id、quantity、checked是否选中、create_time。其中checked字段是一种设计巧思它让前端不必在页面交互时频繁向后端发起更新是否勾选的请求而是统一入库。下单时后端只处理checked1的购物车条目避免了下单前还需要复杂的遍历筛选逻辑。购物车的增删改查注意点加入购物车时要判断当前用户购物车中是否已存在同一个SKU如果已存在则做数量累加而不是新增一条记录修改数量时要防止负数以及超过库存上限的值校验放在Controller入口的DTO里做会更快拦截。4.2 提交订单与库存扣减的原子性处理下单接口是整套系统并发压力最大的地方。当多个学生同时购买同一款热销商品时如何保证库存不超卖是分布式系统级别的经典问题虽然校园平台并发量不大但实现上也不能马虎。我的实现方案是提交订单时用SQL原子操作扣减库存而不是先查询再更新。具体SQL是UPDATE tb_product_sku SET stock stock - #{quantity} WHERE sku_id #{skuId} AND stock #{quantity}通过stock #{quantity}这个条件配合InnoDB的行锁机制天然地防止了超卖。如果UPDATE影响的行数为0说明库存不足直接抛出异常“库存不足”即可。生成订单号的策略也不能随便用时间戳因为同一毫秒内可能出现重复。我使用的方案是yyyyMMddHHmmss 6位随机数字再在订单号的末尾拼上用户ID的后四位这样既保证了可读性也基本不会撞号。同时给order_no字段加了唯一索引即使极端情况撞上了数据库层的唯一约束还会兜底插入时报错后程序会重新生成订单号重试一次。4.3 订单取消与售后的状态机设计订单状态流转中取消订单和售后是导致数据不一致的重灾区。用户下单后如果想取消后端处理逻辑是校验订单状态必须是“待支付”才能取消否则拒绝操作。取消成功后将订单主表状态改为“已取消”并恢复对应的SKU库存——这个恢复操作也必须用原子SQL即UPDATE tb_product_sku SET stock stock #{quantity} WHERE sku_id #{skuId}和下单扣减库存的SQL正好是对称的。售后/退款流程我实现得相对简单因为校园便利平台基本不涉及真实支付网关退款逻辑就是在后台把订单状态改为“已退款”同时标记退款时间不做真实的资金返还操作。但如果项目要对接真实支付微信/支付宝退款逻辑就需要加上第三方退款接口的幂等处理和回调通知复杂度会提升一个量级。如果后续想从课程设计升级成创业项目这是第一个要重构的模块。我还增加了一个小机制当订单状态发生变化时自动写入一条order_status_log记录记录order_id、from_status、to_status、operator_id、operation_type、create_time。别小看这个表在后台查“为什么这个订单七天都没到配送中”时它帮你一眼定位出是卡在哪个环节、操作人是谁——可以说这个小小的日志表就是整个系统的黑匣子。5. 前端工程化与Vue实战记录前端部分的实操很多同学卡在“环境搭建”上比如Vue CLI运行不起来、npm装包太慢、Node版本冲突等。这部分的坑几乎每天都有学生在群里问我干脆在这里把整个前端环境准备和开发过程做一个完整梳理给还没有跑通Vue项目的同学一条明确的路径。我推荐直接使用Vite来构建Vue项目它比Webpack快得多。创建命令是npm create vitelatest campus-mall -- --template vue这个命令会创建一个基于Vue3Vite的最小项目骨架。如果你拿到的是参考代码是Vue2项目那用vue create campus-mall选Vue2预设也行。两个版本的核心逻辑差别不大区别主要在语法上Vue3的组合式API写起来更灵活Vue2的选项式API更直观。新手我更建议从Vue2开始因为网上能找到的完整教程和踩坑案例最多。5.1 Vue项目工程化与目录结构落地项目目录结构我习惯这样划分src/api放所有跟后端接口打交道的JS模块、src/router路由配置、src/store状态管理、src/views页面级组件、src/components公共复用组件、src/utils工具函数。这套目录结构看起来简单但它能保证项目在功能不断增加时仍然不乱。我在第一次做Vue项目时没这么规划结果页面组件和API调用混在一起后期维护真是痛不欲生。src/ ├── api/ │ ├── goods.js │ ├── order.js │ └── user.js ├── router/ │ └── index.js ├── store/ │ └── modules/ ├── views/ │ ├── Home.vue │ ├── GoodsDetail.vue │ ├── Cart.vue │ ├── OrderConfirm.vue │ ├── OrderList.vue │ └── admin/ │ ├── GoodsManage.vue │ └── OrderManage.vue ├── components/ │ ├── GoodsCard.vue │ └── OrderStatusTag.vue └── utils/ └── request.js路由配置时我用了懒加载component: () import(../views/GoodsDetail.vue)这样首屏不会把所有页面组件一次性下载只有访问到该路由时才动态加载对应的JS文件。用户端相对轻量但对于页面数很多的后台管理界面懒加载有非常直观的首屏性能提升页面切换也不会卡顿。5.2 Axios封装与Mock调试技巧前端开发过程中最大的痛点之一是后端接口还没开发完前端就没法继续写页面。我的经验是开发阶段用Mock.js拦截Axios请求直接返回模拟数据等到后端接口联调时再全局搜索Mock开关一键切换到真实接口。这个切换开关我放在request.js顶部的一个const USE_MOCK false变量里改一行代码就能切换整个项目的联调模式。import axios from axios import { ElMessage } from element-plus import router from /router const service axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截自动携带Token service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }, error Promise.reject(error)) // 响应拦截统一处理错误码 service.interceptors.response.use(response { const res response.data if (res.code ! 200) { ElMessage.error(res.message || 请求失败) if (res.code 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(new Error(res.message)) } return res.data }, error Promise.reject(error)) export default serviceMyBatis侧的对应处理是所有接口统一返回一个JSON结构{ code: 200, message: success, data: { ... } }其中data是业务数据。前端封装好之后页面里只需要const data await getGoodsList(params)就能直接拿到业务数据完全不用关心错误处理这正是成熟项目该有的前端开发体验。5.3 项目源码的完整打包与多人协作交付很多同学做完整套项目之后不知道源码怎么发给别人或者怎么部署。这里有一条非常成熟的线上标准流程后端代码推到Git、核心业务代码数据不出问题的同时将构建好的前后端产物部署到同一台服务器。当然对毕设而言更实用的是先把前后端代码在本地跑通再给对方一份演示视频或本地启动说明文档。前端构建用npm run build成功后会在dist目录生成一堆静态文件。后端代码直接mvn clean package -DskipTests打成可执行Jar包。部署时可以前端Nginx托管dist静态文件后端Jar包内嵌Tomcat监听8080端口Nginx把/api路径反向代理到http://localhost:8080。这个架构非常通用不仅对校园便利平台适用放到任何SpringBootVue的毕业设计项目中都能直接用。6. 常见技术坑与排查清单这套系统开发过程中我踩了不少坑很多问题网上搜半天也不一定有直接答案我把它们整理成一个速查表按后端和前端分别列出希望你能绕开。问题现象原因分析解决方案SpringBoot启动报Port 8080 already in use端口被其他进程占用netstat -ano中文乱码数据库连接URL未指定编码在JDBC URL中加入characterEncodingutf8useSSLfalseMyBatis查询结果始终为null实体类属性和数据库列在开启驼峰映射前不匹配在application.yml中开启mapUnderscoreToCamelCase: true前端请求报跨域错误前后端端口不同请求被浏览器的同源策略拦截后端配置CORS跨域放行或前端通过Vite代理转发修改数据库后查询不到最新数据MyBatis二级缓存或Redis缓存未失效排查配置或在关键写操作后清缓存开发环境直接关闭MyBatis二级缓存Vue项目npm run dev报错TS config not found参考项目的TypeScript配置文件路径不一致删除tsconfig相关扩展或重新安装vue/tsconfig依赖JWT Token过期后页面无感刷新失败只做了AccessToken没有RefreshToken刷新策略在拦截器中捕获401调用刷新接口更新Token后重放原请求6.1 后端问题SpringBoot版本选择与MyBatis配置SpringBoot版本的选择比很多人想象得更重要。目前最稳妥的选择是SpringBoot 2.7.x因为它同时支持JDK8和JDK11并且兼容MyBatis官方starter和PageHelper插件。如果一上来就选SpringBoot 3.x坑会非常多JDK版本强迫升到17、javax.*包全部改名成jakarta.*、MyBatis-Spring-Boot-Starter也出了专门的3.0适配版本很多老教程里的写法完全不适用。如果你是跟着网上教程做项目最好先确认教程用的SpringBoot版本再动手否则会在环境配置上多花很多时间。MyBatis的配置有一个小细节整体项目开启驼峰转换是必须的即configuration.map-underscore-to-camel-case设置成true。因为数据库列名都是create_time这种下划线风格而Java实体类习惯写createTime。不开启驼峰转换的话从数据库查出的数据映射到实体类上全是null而你根本不知道问题出在哪排查好久才发现是少了这个配置——这是几乎每个刚学MyBatis的人都会遇到的问题。6.2 前端问题Vue环境配置与路由守卫Vue项目最常见的运行问题是Node版本不兼容。Vite5要求Node版本在18以上而很多同学机器上装的是Node 14或16运行时报“You are using Node version xxx, but Vite requires Node 18”解决方式就是用nvm安装指定版本。此外npm默认源的下载速度在国内很慢装依赖动辄超时直接设置淘宝镜像npm config set registry https://registry.npmmirror.com装包速度立刻翻倍。这个步骤建议在第一次创建Vue项目时就配置好不然后面每个项目都要踩一轮。路由守卫的写法直接决定前端权限控制是否完备。在router.beforeEach中如果目标路由的meta.requiresAuth为true且本地没有token就跳转到登录页。但要注意一个容易忽略的地方登录页本身也要排除在鉴权之外避免“未登录用户访问登录页面却被重定向到登录页”这种死循环。逻辑看起来简单但很多人在项目上线测试时才发现这个问题。6.3 提权与部署问题从源码到可运行的完整交付很多同学做完项目后只知道在IDE里点运行要让他把项目部署到服务器上就一脸茫然。这里我给出一条最简单的上手路径先在一台云服务器上装好MySQL和JDK8把后端打成的Jar包用nohup java -jar campus.jar app.log 21 命令后台运行再把前端dist里的静态文件放进Nginx的html目录配置一个location / { try_files $uri $uri/ /index.html; }。至此一个前后端分离的校园便利平台就正在“公网”环境跑起来了访问服务器IP就能直接浏览。需要提醒的是生产环境部署时后端Jar包要放在独立的目录不能直接放在/root下数据库账号密码要使用独立创建的专用账号不要用root对外暴露的端口只保留80前端和8080后端API且最好通过Nginx反向代理而非直接暴露HTTPS证书如果条件允许建议加上浏览器地址栏的小锁会给项目加分不少。7. 缓存、性能优化与运维监控思路校园便利平台的并发量虽然不高但一套像样的性能优化方案写在论文里会非常加分面试也有得聊。我从三个维度做了优化缓存层、数据库层和接口层。缓存层是最直接见效的优化点。商品列表这种读多写少的接口非常适合加Redis缓存。实现思路是查询商品列表时先从Redis读如果没有称为缓存击穿就去MySQL查同时把结果写入Redis并设置5分钟过期。商品被修改或删除时主动删除对应缓存Key强制下次查询回源数据库。这样核心的商品浏览接口压力会小非常多用户打开首页的速度也会肉眼可见地提升。数据库层的优化主要是SQL本身。MyBatis里写SELECT *是很常见但很不好的习惯因为一旦表结构改了返回的字段就会变Java实体类的映射就可能出错。建议显式列出需要的字段只查不用的列浪费网络传输和内存。另一个优化点是在SQL里做尽量多的过滤把条件推给MySQL去筛而不是全表查出来再在Java代码里用Stream过滤。后者在小数据量时没问题但数据量一上来性能就垮了。接口层的优化集中在分页查询和列表查询。所有后台管理列表页都建议使用PageHelper做分页并且通过SQL层面的LIMIT start, size限制返回行数。另外要防的一个老问题是“深分页”即用户翻到第100万页时数据库要扫描前100万条记录效率急剧下降。解决方案是记录上一页最后一条记录的ID下一翻页用WHERE id lastId LIMIT size的键集分页写法。这个思路在MySQL面试题里也经常出现可以作为加分项好好讲讲。8. 总结与毕业设计答辩重点梳理整一套项目做下来核心不是写了多少行代码而是把“校园便利平台”的需求用一套严谨的技术方案完整落地。从需求分析、数据库建模、后端接口开发、前端页面编写到最后的联调部署每一步都踩出了值得记录的实战经验。SpringBoot Vue MySQL MyBatis这套组合不仅是校园便利平台的最优解也是后端初学者从小项目走向工业级项目的最佳成长路径。最后我分享一点个人做项目的体会一个能拿到高分的毕业设计代码量固然重要但更要紧的是把“为什么这么设计”讲清楚。比如为什么要给订单表单独建状态日志表为什么购物车要存数据库而不是只在浏览器里用localStorage为什么用JWT而不是简单的Session。这些问题只要你能在答辩现场对答如流老师给你的评价绝对不会低。
阅读完成 · 觉得有帮助?