
做这个高校汉服租赁网站系统我一开始其实没想太多就是想把SpringBoot2、Vue3、MyBatis-Plus、MySQL8.0这一套技术栈完完整整地串起来。结果越做越发现汉服租赁这个业务场景还挺有意思——它既有普通电商的选品、下单、支付流程又带着线下实体租赁特有的“库存、档期、归还校验”逻辑。今天把这套系统的开发过程整理出来从需求拆解、技术选型、数据库设计到前后端联调和部署配置全流程复盘一遍希望能给正打算做类似Java Web项目的同学提供一份可以直接参考的方案。这套项目的技术组合在2024到2025年这个节点上是相当典型的后端用SpringBoot2.7.x稳定版本前端用Vue3全家桶持久层用MyBatis-Plus省掉大量单表CRUD代码数据库上MySQL8.0接入层配合RESTful API和JWT做无状态认证。整个项目还带了文档所以特别适合做毕业设计、课程设计或者作为你学习前后端分离架构的练手项目。下面我会把从零搭建这个系统时踩过的坑、总结出的经验按模块拆开讲清楚。1. 项目整体设计与技术选型思路1.1 业务需求分析和功能模块划分汉服租赁网站和普通电商网站有个本质区别普通电商卖的是“所有权”客户付款后商品直接归属客户汉服租赁卖的是“使用权”客户只在一定时间段内使用商品用完要归还。这就意味着系统的核心业务流不是“下单—发货—确认收货”而是“浏览—预约档期—下单—取衣—归还—验收”。我把整个系统拆成了六个核心模块用户端注册登录、汉服浏览与检索、租赁下单、订单查询、个人信息管理汉服管理汉服分类形制、尺码规格、库存数量、日租金、押金、图片展示租赁订单管理创建订单、支付记录、取衣登记、归还登记、超期处理管理员端用户管理、汉服管理、订单审核、归还验收、数据概览评论与收藏用户对穿过的汉服进行评价收藏喜欢的款式通知公告平台发布活动或节假日公告这样划分的好处是边界清晰每个模块都可以独立做成一张或多张数据表也方便后续做权限控制。管理员端和用户端共用一套后台接口只是接口路径和权限级别不同。1.2 技术栈选型背后的考量选型这事我简单说说我的理由。SpringBoot2为什么不用3因为3.0之后是Jakarta EE命名空间很多老依赖和教程都不兼容对于要交作业或者快速出项目的场景2.7.x的生态资料最全、踩坑成本最低。Vue3现在已经是绝对主流了配合Vite构建工具开发体验比Webpack快得多而且Composition API写业务逻辑比Options API清晰很多。MyBatis-Plus就更不用说了单表操作几乎不用写SQL内置分页插件、逻辑删除、自动填充这些功能非常实用。MySQL8.0用它的原因也很直接——已经是当前数据库的默认选择窗口函数、公用表表达式这些特性比5.7好用太多。没有引入Redis和消息队列这些中间件。原因是高校租赁场景的数据量级不大单机MySQL完全扛得住引入中间件反而增加了部署复杂度。做项目也好做毕业设计也罢技术选型首要标准是“能跑通、可解释、有亮点”而不是为了堆技术而堆技术。1.3 数据库表结构设计思路数据库设计是整个项目的地基设计得好不好直接决定后面写代码的心情。我设计了8张核心表user用户表字段包括username、password加密存储、nickname、phone、avatar、rolehanfu_category汉服分类表如唐制、宋制、明制、魏晋风hanfu汉服商品表关联分类包含name、style、size、stock、daily_price、deposit、cover_image、statushanfu_inventory库存明细表记录每一件汉服的可租档期orders订单表包含order_no、user_id、hanfu_id、start_date、end_date、total_price、statusorder_log订单状态流转日志表方便追溯用户操作路径comment评价表关联订单和汉服favorite收藏表关联用户和汉服这里特别说一下库存设计。汉服租赁的库存不能用简单的“剩余数量”一个字段表示因为同一件衣服在不同日期段的余量可能完全不同。比如五一前三天可能全租出去了五一后三天又全是余量。所以我把库存拆成了“库存明细表”每条记录代表某一件汉服在某一天是否可租下单时锁定对应日期的记录归还时释放。这套设计虽然增加了一点查询复杂度但避免了超卖问题。2. 核心功能模块的实操实现2.1 前端项目初始化与配置细节前端我用Vite创建Vue3项目。创建命令很简单但有几个细节需要注意。第一个是vitejs/plugin-vue版本和Vite版本要匹配否则启动时会报插件不兼容的错误。第二个是代理配置开发环境联调时必须把/api开头的请求代理到后端地址否则跨域问题会折腾你半天。我在vite.config.js里是这样配的export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })第三个细节是引入Element Plus按需导入我用的方式是unplugin-vue-components和unplugin-auto-import两个插件这样打包体积能小不少组件库的样式也不会全局污染。全局状态管理我用的Pinia比Vuex的API更简洁而且对TypeScript的支持更好。路由用Vue Router4配合动态导入实现路由懒加载。2.2 后端基础框架搭建与MyBatis-Plus集成后端项目我用Spring Initializr生成选好SpringBoot 2.7.18版本依赖勾选Web、MySQL、Lombok。MyBatis-Plus的依赖需要手动添加版本用3.5.3.1比较稳定。集成过程有几个关键配置我贴出来供参考spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/hanfu?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: yourpassword mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 id-type: auto这段配置里有三个坑我得专门讲一下。第一个是serverTimezone必须明确指定为Asia/ShanghaiMySQL8.0默认时区和中国差了8个小时不指定的话数据库连接时会直接报错。第二个是allowPublicKeyRetrievaltrue如果你用的MySQL账号需要SHA2密码认证不加这个参数也会报错。第三个是逻辑删除MyBatis-Plus的逻辑删除配置很完善就是需要在实体字段上加上TableLogic注解否则删除操作不会自动改成update语句。分页插件也必须单独配置一个MybatisPlusConfig类Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }不配置这个拦截器的话调用selectPage方法时分页SQL不会生效你会发现返回的结果全量查出来了。2.3 用户登录认证与JWT无状态会话设计登录认证我采用JWT方案。用户登录成功后后端生成一个包含用户ID和角色信息的Token返回给前端。前端把Token存到本地存储里后续每次请求都在请求头带上Authorization: Bearer token。后端用一个拦截器拦截需要认证的接口解析Token并把用户信息放到ThreadLocal里供业务代码使用。关于JWT我有一个建议过期时间不要设太长。之前我图省事把过期时间设成了7天结果用户改密码后老Token还能继续访问非常不安全。后来改成了2小时同时提供一个刷新Token的接口体验和安全性都能兼顾。密码存储这方面一定要用BCrypt加密别用MD5或SHA这两种算法现在用彩虹表一撞一个准。Spring Security的BCryptPasswordEncoder可以直接集成也可以用hutool工具包里的BCrypt类效果都一样。无论如何明文密码是绝对不能出现在数据库里的。2.4 汉服租赁核心流程下单、档期锁定与归还租赁下单是整个项目里业务最复杂的部分。前端提交订单时需要带三个关键参数汉服ID、取衣日期、归还日期。后端处理逻辑分这几步校验汉服状态是否正常防止已下架的汉服被下单查询该汉服在取衣日期到归还日期之间每一天的库存记录如果每一天都有余量则把所有涉及日期的明细记录标记为锁定计算总价规则是每日租金乘以天数加押金创建订单记录状态设为“待取衣”返回订单号给前端这里最需要小心的是并发问题。假设同一件汉服在同一个日期只剩一件存量两个用户同时下单理论上两个请求都能查到“有余量”然后同时更新库存就会导致超卖。解决方式我只能说这一步是拿MySQL的FOR UPDATE锁实现的。查询库存明细时加SELECT ... FOR UPDATE事务提交前锁是持有的第二个并发请求会阻塞等待从根本上杜绝超卖。归还流程相对简单。用户提交归还申请后管理员检查汉服有无损坏然后更新订单状态为“已完成”同时释放对应日期的库存锁定记录。如果有超期还要额外计算超期费用并追加到订单金额上。2.5 管理后台的数据统计与可视化管理后台我用的是纯前端方案没有引入ECharts只用了Element Plus的统计卡片布局。首页展示几个关键指标今日订单数、总用户数、汉服总数、待归还订单数。接口侧用几条简单的SQL就能查出来比如今日订单数SELECT COUNT(*) FROM orders WHERE DATE(create_time) CURDATE()毛利润统计我建议按周而不是按日因为租赁行业的周末效应非常明显按周统计能看到稳定的趋势。如果后续想把统计做得更专业可以引入ECharts做折线图和柱状图——不过让我说句实话项目最核心的是把流程跑通统计页有一两个核心指标足够毕业设计和课程交付了。3. 开发环境准备与数据库配置3.1 版本选择建议和安装注意事项环境这块我建议直接锁定一套避免隐藏的版本兼容问题。我自己用的这套配置单如下组件版本说明JDK1.8SpringBoot2.x对JDK8支持最稳定Maven3.8依赖管理阿里云镜像加速Node.js18 LTSVite5需要1618没问题MySQL8.0.36注意安装时选择utf8mb4字符集IDEIDEA 2023社区版够用旗舰版更顺手MySQL8.0安装时有一个特别容易踩的坑安装向导让选认证方式时如果选了“Use Strong Password Encryption”后面用老客户端连接就会报错。所以我推荐直接用Navicat或MySQL Workbench连接测试如果报caching_sha2_password相关的错误要么在创建用户时指定IDENTIFIED WITH mysql_native_password BY 密码要么让客户端升级支持新认证方式。这个坑在联网搜索里出现频率特别高。Linux服务器上安装MySQL8.0的话用Docker是最快的docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDyourpassword \ -e TZAsia/Shanghai \ mysql:8.0注意一定要设置TZAsia/Shanghai容器内的默认时区是UTC不设置的话你在代码里拿到的当前时间和数据库时间对不上排查半天你会发现问题出在容器时区上。3.2 MySQL8.0初始化脚本设计初始化脚本我按模块拆分每个模块一个SQL文件放在docs/sql目录。手动执行时按顺序跑schema.sql和data.sql。数据脚本里我会准备一些汉服数据比如“齐胸衫裙”“圆领袍”“马面裙”“飞鱼服”这些热门款式库存数据用随机日期生成方便前端展示。这里有个建议user表的密码字段预先存入几条BCrypt加密后的测试账号数据方便联调时直接登录。BCrypt加密结果每次都不一样所以在SQL脚本里写死几条哈希值是完全可以的。不过如果你直接拿明文密码往库里插登录永远验证不过这是新手最容易怀疑人生的问题之一。3.3 前后端接口规范与联调策略接口规范我遵循RESTful风格资源用名词复数动作交给HTTP方法。比如GET /api/hanfu获取汉服列表分页GET /api/hanfu/{id}获取汉服详情POST /api/orders创建租赁订单PUT /api/orders/{id}/return归还登记GET /api/admin/orders管理员获取订单列表所有接口统一返回格式{ code: 200, message: success, data: {} }前端封装Axios实例统一处理响应拦截遇到code ! 200时通过Element Plus的Message提示错误。这套约定极大减小了联调时沟通误差。还有一点接口文档一定要写不用特别正式手写Markdown表格即可把自己调试过的请求参数记下来能避免后面改需求时人肉翻代码。4. 开发中的常见问题与排查宝典4.1 前端联调跨域问题和代理配置做前后端分离项目联调是第一道坎。虽然我在vite.config.js里配了代理但有几个前置条件需要注意。代理只对开发环境生效生产环境部署时要把静态资源放在Nginx下并将API反向代理到后端地址。其次如果后端接口路径不是/api开头代理的目标路径和实际请求路径之间还需要做重写。举个例子后端接口实际路径是/hanfu/list前端请求的是/api/hanfu/list那代理规则需要把/api去掉用rewrite实现。排查跨域问题时先看浏览器控制台有没有CORS字样再看请求是否到了后端用Postman单独测一下后端接口是否正常两步能过滤掉90%的问题。4.2 MyBatis-Plus使用中的隐蔽问题MyBatis-Plus写起来很爽但有几个坑值得注意。第一个是selectPage返回的total总为0。这个问题要么是没配置分页插件要么是SQL里包含了多表JOIN。如果是多表JOIN分页插件不能处理复杂SQL需要自己手写分页查询或使用Select注解指定统计SQL。第二个是逻辑删除字段的坑。配置了逻辑删除后默认查询只会查deleted0的记录但如果你在XML里手写了SQL比如JOIN hanfu ON hanfu.id orders.hanfu_idJOIN出来的表不会自动带逻辑删除条件你会看到被删除的汉服信息仍然出现在订单列表里。解决方案是在手写SQL时手动加AND hanfu.deleted 0。第三个是字段映射的坑。数据库字段命名是employee_name这种下划线风格实体字段是employeeName这种驼峰风格虽然MyBatis-Plus默认开启了下划线转驼峰但XML里的resultMap如果手动定义了就必须把所有字段映射规则写完整。4.3 Vue3开发绕不开的10个细节这里我说说我在Vue3里遇到的、并且在搜索热词里反复出现的几个问题。Composition API里ref和reactive的选择。ref主要用于基本类型和需要重新赋值的引用reactive只能用于对象和数组。很多人把数组放到reactive里结果发现替换整个数组时视图不更新这就是把API用错了。正确的做法是const list ref([])更新时直接list.value newList。onMounted的异步请求问题。如果你在onMounted里调用异步函数记得写法是onMounted(async () { await fetchData() })别写成onMounted(fetchData())这个与是否加了括号有关加了括号函数会在赋值阶段立即执行而不会被当作生命周期回调。Element Plus的组件样式覆盖。改第三方组件样式时要用:deep()选择器穿透scoped样式比如:deep(.el-input__wrapper) { background-color: #f5f7fa; }Vue3项目在Edge浏览器上偶发无法关闭最小化按钮的问题我排查下来基本都属于浏览器插件冲突跟代码本身关系不大遇到时换个浏览器验证一下就可以排除项目影响。4.4 事务管理与并发场景的细节处理前面提到的库存锁定是基于数据库行锁实现的但这里有个大前提操作必须在同一个事务里执行。Spring的Transactional注解要加在Service层方法上而不是Controller层。还有一个细节是事务的传播行为如果createOrder方法内部又调用了lockInventory方法这两个方法默认就在同一个事务里因为传播行为默认是REQUIRED。另一种并发隐患是同一个用户短时间重复点击提交按钮导致生成了两笔相同的订单。单纯靠前端按钮防抖是不够的后端必须做幂等处理。我的方案是前端在点击提交时生成一个requestId后端在订单表里加一个unique_request_id唯一索引重复提交时数据库会直接报Duplicate异常配合全局异常处理器返回“请勿重复提交”的提示。这个方案实现成本低效果却很可靠。4.5 打包部署全流程记录项目开发完成后部署是最后一个环节。前端打包命令是npm run build产物在dist目录把dist目录里的文件复制到Nginx的html目录下。后端打包用Maven执行mvn clean package -DskipTests生成jar文件。然后通过java -jar启动。有一个关键配置别忘了Nginx的反向代理配置。前端页面加载时要请求/api必须让Nginx把请求转发到后端端口location /api/ { proxy_pass http://localhost:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }如果你直接把后端服务单独作为公网端口暴露会让服务器暴露在更大的风险中不建议这样操作Nginx在前面挡一层会更安全。另外proxy_pass末尾的斜杠和路径拼写要仔细检查我曾在上面栽过跟头——配错了就会返回404。4.6 文档整理与项目答辩要点最后聊聊配套文档。这类项目最终交付的价值有一半体现在文档里。我的文档分四部分需求分析说明书、数据库设计文档、接口文档、部署文档。数据库设计文档里除了ER图每张表都写了字段说明和数据字典。接口文档按模块分开每个接口写清请求参数、返回示例、错误码含义。这样整理完无论是答辩演示还是后续自己接手维护都能节省大量时间。答辩的时候对方最常问的几个问题我已经提前准备好了库存如何防超卖行锁、登录如何保证安全JWTBCrypt、为什么要用MyBatis-Plus减少样板代码分页插件、为什么选MySQL8.0性能改进和窗口函数。把自己的技术方案能讲清楚为什么比堆砌多少概念都管用。个人在做完整套项目后的真实体会是SpringBoot2加Vue3加MyBatis-Plus这套组合在做中小型管理系统时的开发效率确实高得离谱。大部分CRUD接口从建表到能跑通半小时就能完成真正花时间的反而是那些“业务规则”的细节——库存怎么扣、状态怎么流转、权限怎么控制。如果你也想做这个项目我的建议是先把租赁流程画成一张状态图理解清楚所有分支和边界再动手敲代码拿到源码后也别急着跑自己动手把数据库脚本跑一遍然后把管理员的后台操作逻辑完整过一遍绝对比光看代码有收获。最后再分享一个小技巧把MySQL8.0的sql_mode设置为ONLY_FULL_GROUP_BY这类严格模式开发初期虽然会增加一些SQL写作成本但能提前暴露很多字段依赖问题上了生产环境反而更稳。测试阶段多造点假数据把订单状态流转的每个分支都跑一遍你会对这个系统的掌控感强非常多。