ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

SpringBoot+Vue+MyBatis构建高校汉服租赁管理系统完整解析

SpringBoot+Vue+MyBatis构建高校汉服租赁管理系统完整解析 开头这套高校汉服租赁网站管理系统源码是我用SpringBootVueMyBatis架构配合MySQL数据库从需求分析、数据库建模到前后端联调、部署上线完整走下来的一套企业级项目。它解决的实际问题很具体高校里汉服社、国学社、传统文化节活动频繁每次办活动要租上百套汉服靠Excel表格登记根本扛不住。尺码对不上、押金退错、衣服逾期不还、库存丢失这些问题在纸质流程里几乎是标配。我把这套系统从零写到了完整交付前后花了三周时间。对于正在做Java课程设计、毕业设计或者想学习企业级项目完整开发流程的同学来说这套源码的参考价值在于它覆盖了一条完整的业务链路角色权限、商品库存、订单流转、财务结算、统计报表每一步都是真实业务场景里会遇到的。本文会把这套系统的业务拆解、数据库设计、核心代码实现、部署排坑过程完整写出来直接照着搭就能跑通而且你能看懂每一步为什么这么设计。1. 项目业务拆解这套系统到底管了哪些事1.1 三个实际使用角色高校汉服租赁业务不像普通电商那么单纯它带着明显的校园场景特点。我把使用角色拆成了三类普通学生用户、社团管理员、系统运营人员。普通学生用户通过网页端浏览汉服库存、查看尺码和租金、在线提交租赁申请、支付押金、查询订单状态活动结束后归还并申请退还押金。社团管理员负责审核租赁申请、办理出库、登记返还、处理逾期和押金结算。系统运营人员管理用户权限、商品上下架、租金定价、数据统计和系统配置。这三类角色对应了三种完全不同的操作视图。学生端是选衣下单管理端是审核出库运营端是配置和统计。在权限设计上我用了角色加菜单的经典模型后端接口用拦截器做权限校验前端路由也用动态路由做了菜单隔离。这样每个角色登录进去看到的界面和接口权限都是独立的不会出现学生账号能打开管理页面的低级问题。1.2 十个核心功能模块这套系统实际落地的功能模块一共有十个我按业务归属整理如下用户模块注册、登录、个人信息维护、押金账户管理汉服管理模块汉服分类、单品维护、尺码库存、上下架状态租赁订单模块提交申请、管理员审核、待取货、租赁中、待归还、已完成、已取消押金财务模块押金缴纳、租金计算、押金退还、异常扣款消息通知模块订单状态变更通知、逾期提醒评价模块归还后对汉服和服务的评价数据统计模块租赁排行、收入统计、逾期统计系统管理模块用户管理、角色权限、菜单配置公告模块活动通知、节假日闭馆通知日志模块操作日志、登录日志这十个模块不是拍脑袋堆出来的每一个都对应线下租赁业务里的一个真实环节。比如消息通知线下场景是靠微信群人工通知线上就必须在订单状态变更时自动触发。日志模块则是为了出问题能追溯谁在什么时候改了汉服价格谁审核了哪个订单全部留痕。1.3 一条完整的租赁业务链路我用一个具体场景把整个业务流程串起来学生小张要在校园文化节穿汉服他登录系统浏览库存选了明制袄裙一套XL码租期三天系统显示押金300元、租金45元。小张提交租赁申请并在线支付押金管理员收到待审核订单核实库存并确认尺码后点击审核通过。系统自动生成出库单并更新库存小张收到取货通知到线下门店凭订单号取衣。三天后小张归还汉服管理员检查衣服无破损后确认归还系统计算租金从押金中扣除并退回剩余金额。小张收到退款通知对本次租赁进行评价。如果逾期未还管理员可手动标记逾期系统按超时天数计算滞纳金。整个流程涉及六个状态变更、三次库存变化、两次资金操作每一步在数据库里都有记录。2. 为什么是这套技术组合SpringBootVueMyBatis的匹配逻辑2.1 后端选型的核心逻辑SpringBoot在这个项目里是主框架选它几乎没有悬念。第一SpringBoot内置Tomcat一个java -jar命令就能启动服务部署成本极低对高校机房环境特别友好。第二它的自动配置机制把SpringMVC、事务管理、参数校验这些基础能力全部封装好了我只需要专注写业务代码。第三Spring生态的整合能力很强接入MyBatis、Redis、定时任务都非常顺滑。更重要的是SpringBoot这种基于注解的开发模式和这套系统的业务节奏非常匹配。租赁系统的核心是订单流转和库存管理涉及大量事务操作和状态判断Spring的声明式事务只需要在Service层加一个Transactional注解就能保证数据一致性。项目里库存扣减、押金退还、订单状态更新这三个操作必须同时成功或同时失败没有事务控制必然出问题。2.2 前端技术栈的匹配性Vue在这个项目里承担的是整个前端交互层。选择Vue 2.6版本基于三个考量组件化开发让页面复用程度大幅提升汉服列表卡片、订单卡片、状态标签这些组件在多处共用响应式数据绑定让表单验证和动态交互的代码量减少了将近一半Vue生态里的Element UI组件库和后台管理系统的契合度非常高表格、弹窗、表单校验都是现成的。前端工程的搭建用的Vue CLI配合Vue Router做路由管理、Axios做HTTP请求。Vuex我没有直接用于全部状态而是只用来存用户信息和权限菜单这类全局数据订单数据保持在组件内部管理。这样避免了Vuex状态混乱的问题也不至于在刷新页面时丢数据。2.3 MyBatis与MySQL的协作细节MyBatis这套持久层框架放在这个项目里有一个非常实际的理由租赁业务的SQL逻辑复杂度不低尤其是多表联查和动态查询MyBatis能把SQL完全掌控在开发者手里。比如汉服列表页的筛选条件可能有分类、价格区间、尺码、上架状态四个条件同时存在而且可空可填这种场景用MyBatis动态SQL处理非常直观一段 标签配合 判断就能搞定。MySQL在数据库层面提供了整个系统的数据存储底座。InnoDB引擎支持事务和外键这是订单、库存这类业务的基本前提。我设置了utf8mb4字符集避免用户昵称里出现生僻字时产生乱码。数据库连接池用的是HikariCPSpringBoot 2.x默认集成性能稳定我实测最大连接数50完全够校园场景下几百人同时访问。2.4 这套组合的边界与替代方案没有哪套技术选型是万能的我也要说清楚这套组合的边界。如果你做的是高并发抢购类项目SpringBoot单机部署加MySQL会顶不住需要引入Redis做缓存和限流。但高校汉服租赁是典型的低并发业务峰值也就是文化节前几天日活几百人这个量级下这套架构完全够用。如果后续要扩展优先考虑的是Redis缓存和MQ异步通知而不是换框架。我在代码里已经预留了缓存接口和消息发送接口后续接入Redis替换内置缓存、接入消息队列做异步通知都不需要改动业务代码。这也是我在写这套源码时特意做的设计取舍。3. 数据库设计四张核心表如何撑起一套租赁业务3.1 用户表角色权限怎么落库用户信息表是整个系统的数据起点我把用户的基础信息、认证信息和财务信息放在了同一张表里。核心字段包括用户ID、用户名、密码、姓名、学号工号、手机号、邮箱、角色类型、状态、注册时间。密码字段用BCrypt加密存储不存明文这是基本的合规要求。角色权限方面没有做复杂的五表模型而是采用了用户表加角色表加菜单表的设计。角色表里定义了三种角色菜单表存了前端路由和后端接口的权限标识中间用用户角色关联表连接。这样设计的原因是这套系统的角色是固定的不需要频繁增删角色简化模型能少两张关联表查询效率也更高。3.2 汉服信息表库存、SKU与状态管理汉服商品的设计是这套系统里比较出彩的部分。我拆成了汉服基本信息表和汉服库存表两张表。基本信息表存名称、朝代风格、分类、适用性别、图片、描述、租金、押金、上架状态库存表存SKU编码、颜色、尺码、库存数量、已租数量、破损数量、状态。为什么要拆两张表因为同一款汉服可能同时有男女款、多个尺码、不同颜色。如果把SKU信息直接塞进基本信息表上架一款汉服带了6个尺码就要插入6条重复的基础信息记录维护起来非常痛苦。拆表之后基本信息一条记录对应库存表多条记录通过汉服ID关联这就是电商里标准的SPU和SKU关系。3.3 订单表主表与明细表的设计思路订单设计采用了主表和明细表分离的结构。订单主表存订单编号、用户ID、订单状态、租赁开始日期、结束日期、租赁天数、总租金、押金、实付金额、创建时间、支付时间、审核时间、出库时间、归还时间、经办管理员ID。明细表存订单ID、汉服ID、SKU信息、数量、租赁单价、小计金额、归还状态。这个设计解决了一个核心问题一个订单可以同时租多套不同款式的汉服。订单主表记总账明细表记项目对账和统计都方便。比如统计某款汉服的出租次数直接按明细表分组汇总就行不需要去主表做JSON字段解析。3.4 索引、外键与状态字段的取舍数据库优化方面我给订单表加了三个关键索引用户ID加创建时间联合索引支持用户查询自己的历史订单订单状态加创建时间联合索引支持管理员按状态筛选归还日期索引支持逾期订单定时扫描。汉服库存表按汉服ID和SKU编码建了唯一索引避免并发插入重复SKU。外键我在设计时保留在了建表脚本里但实际运行阶段关闭了外键检查。原因是订单明细和汉服库存之间如果启用外键约束你做库存更新时MySQL会额外做一致性检查在高并发写入时有一定性能损耗而且一旦数据需要批量订正外键会变成一个很大的阻碍。项目里通过代码逻辑保证数据一致性外键只在建表脚本里作为注释保留。4. 后端核心实现登录、库存扣减与订单状态机4.1 JWT登录与全局权限拦截登录模块用了JWT做无状态认证用户登录成功后后端签发一个有效期为24小时的Token前端在请求头里携带这个Token访问受保护接口。后端用一个拦截器统一校验Token的合法性并解析出用户ID和角色信息放入ThreadLocal上下文。拦截器里对不同角色的接口访问做了细粒度控制。学生角色只能访问用户端接口管理员角色才能访问管理端接口运营人员拥有全部权限。实现上采用了路径匹配规则/api/admin/**前缀的接口要求管理员角色/api/user/**前缀的接口要求登录状态/api/public/**前缀不需要登录。这样权限判断的代码集中在一个类里维护成本极低。4.2 库存扣减的事务与并发控制库存扣减是这套系统里最需要严谨对待的业务操作。我写这段代码时采用了乐观锁机制库存表增加版本号字段更新时同时带上版本号条件如果影响行数为0则说明并发冲突重新查询再重试。代码如下Transactional public Boolean reduceStock(Long skuId, Integer num) { int count 0; for (int i 0; i 3; i) { ClothesStock stock stockMapper.selectBySkuId(skuId); if (stock.getStockCount() num) { throw new BusinessException(库存不足); } count stockMapper.reduceStock(skuId, num, stock.getVersion()); if (count 0) { return true; } } throw new BusinessException(系统繁忙请重试); }这里有一个细节值得展开说说为什么不直接用update clothes_stock set stock_count stock_count - num where sku_id ? and stock_count num这种写法确实没问题但缺少版本号的话后续如果业务上需要记录每次库存变化的操作人、原因、时间就缺少了追溯能力。加了版本号的乐观锁既保证了并发安全又为后续做库存操作审计留了余地。4.3 订单状态机从预约到归还的九种状态订单状态是整个系统最复杂的部分。我设计了九个状态待审核、待取货、租赁中、待归还、已完成、已取消、已拒绝、逾期、已退款。每个状态之间的流转关系不是任意的我在代码里用一个StateMachine类统一管理核心逻辑是定义状态流转映射表每一步流转都校验合法性。public class OrderStateMachine { private static final MapInteger, ListInteger TRANSITIONS new HashMap(); static { TRANSITIONS.put(0, Arrays.asList(5, 6)); // 待审核 - 已完成(通过) / 已取消 TRANSITIONS.put(1, Arrays.asList(2, 5)); // 待取货 - 租赁中 / 已取消 TRANSITIONS.put(2, Arrays.asList(3)); // 租赁中 - 待归还 TRANSITIONS.put(3, Arrays.asList(4, 7)); // 待归还 - 已完成 / 逾期 TRANSITIONS.put(4, Arrays.asList(8)); // 已完成 - 已退款 TRANSITIONS.put(6, Arrays.asList(8)); // 已拒绝 - 已退款 } }状态机的价值在于它把非法流转拦截在了代码层面。比如已完成的订单不允许再被修改为租赁中已经退款的订单不允许再次审核这些逻辑如果散落在各个Service方法里很容易漏掉某条分支。集中到状态机之后所有的状态变更必须通过统一入口后续加状态流转规则只需要改这一张表。4.4 MyBatis动态SQL与分页查询实战租赁订单的列表页是全系统查询条件最多的地方我实际列一下用户ID、订单状态、下单时间范围、租赁日期、经办管理员、关键字搜索。六个条件任意组合零个条件时也要能查出全部数据。这种场景用MyBatis动态SQL是最合适的select idselectOrderPage resultTypeOrderVO SELECT o.*, u.nickname FROM orders o LEFT JOIN user u ON o.user_id u.id where if testuserId ! null AND o.user_id #{userId} /if if teststatus ! null AND o.status #{status} /if if teststartDate ! null AND o.create_time gt; #{startDate} /if if testendDate ! null AND o.create_time lt; #{endDate} /if if testkeyword ! null and keyword ! AND (o.order_no LIKE CONCAT(%, #{keyword}, %) OR u.nickname LIKE CONCAT(%, #{keyword}, %)) /if /where ORDER BY o.create_time DESC /select分页我用的PageHelper插件使用上要特别注意顺序问题PageHelper.startPage()必须是紧接着查询语句之前的第一个MyBatis方法如果中间穿插了其他查询否则分页参数会作用到错误的那条SQL上。这个坑我在下面的排查章节会详细说。5. 前端实现Vue页面组件与交互闭环5.1 路由、布局与权限菜单前端工程按角色分成了两套布局用户端面向学生走的是简洁明快的风格首页是汉服展示和活动公告租赁流程尽量三步完成管理端面向社团管理员和运营人员左侧是功能菜单右侧是内容区用的是Element UI的栅格系统。动态路由的实现思路是用户登录后后端返回该角色可访问的路由列表前端用router.addRoutes方法动态注册。菜单同样由后端返回前端渲染成侧边栏。这样做的好处是权限配置统一在后端维护前端不写死菜单项运营人员在后台改了菜单权限用户下次登录刷新页面就能生效。5.2 Axios封装与Token注入Axios请求封装虽然代码不复杂但直接影响整个项目的健壮性。我做了三层封装第一层是实例创建配置baseURL和请求超时时间第二层是请求拦截器从localStorage取出Token并注入请求头第三层是响应拦截器统一处理后端返回的数据结构遇到401状态码自动跳转登录页遇到业务异常弹提示信息。这里有一个踩过的坑response拦截器里不能盲目地return response.data因为文件下载接口返回的是Blob对象你统一解包之后Blob对象就丢失了。我的做法是判断response.headers里的Content-Type如果是application/json就正常解包如果是application/octet-stream就直接返回整个response对象。5.3 选衣、下单与支付押金流程用户端最核心的流程是选衣下单。汉服列表页支持按朝代风格、价格区间、租金排序筛选每个商品卡片上显示可租状态和剩余库存。点击进入详情页后选择尺码和租期前端根据后端返回的价格规则自动计算总租金和所需押金。下单页面做了双重确认提醒第一重确认租赁时间和数量第二重确认押金金额和支付方式。押金支付这块考虑到高校场景的特殊性没有接真实的第三方支付而是设计成了模拟支付接口用户点击确认支付后直接标记支付成功。源码里预留了微信支付、支付宝支付的接口适配层真实上线时补上对应的支付网关参数即可。5.4 管理后台审核、出库与归还操作管理端的订单处理流程我是按照线下实际操作顺序设计的。待审核列表里管理员可以看到用户的租赁申请详情包括租期、款式、数量、押金缴纳状态。点击审核通过后系统自动扣减库存、变更订单状态为待取货。此时用户端刷新页面就能看到取货提醒。归还操作设计了一个归还登记弹窗管理员选择本次归还的明细项勾选衣服状态是否完好。如果存在破损可以选择扣除押金金额并填写说明。提交后系统按实际租赁天数计算租金将剩余押金原路退回。整条流线走完管理员端和用户端的状态实时同步不需要任何人工同步操作。6. 实战排坑缓存、分页、XSS过滤与视频回放6.1 MyBatis二级缓存导致的脏数据问题我在写这套源码时第一次用MyBatis二级缓存就踩了坑。当时给汉服分类表加了二级缓存结果管理后台修改了分类名称后用户端列表页一直显示旧的分类名刷新浏览器也没用。排查了半天才发现是二级缓存的脏数据问题。原因在于MyBatis的二级缓存是以namespace为单位的默认的缓存策略是只要这个namespace下的任意增删改操作执行了整个缓存就会清空。但我当时用了自定义的缓存策略没有监听到其他Service的更新操作导致数据不能同步失效。解决方案其实很简单缓存范围内事务提交后自动清空同时业务上对于实时性要求高的操作统一不走二级缓存。在后续的代码里我只有汉服分类、公告这种不经常变更的配置数据开启了二级缓存订单、库存这类敏感数据一律不走缓存避免出现数据不一致的问题。6.2 PageHelper分页插件在多表关联下的坑PageHelper是分页神器但用不好就是分页陷阱。我遇到的最典型的问题是在一条查询SQL里做了LEFT JOIN关联查询然后PageHelper把limit加到了错误的位置导致返回的数据量和查出的总条数对不上。还有一次是startPage方法和查询语句之间隔了一条无关查询结果分页参数作用到了无关查询上页面直接报了SQL语法错误。这里给大家一个很实用的建议在写分页查询时先单独调试原始SQL确认查询结果和计数结果都正确再套PageHelper分页。另外使用PageHelper的startPage时务必让它紧跟目标查询语句绝不能在这个空隙里insert其他查询代码。如果项目里没有严格的规范可以考虑不用PageHelper直接在Mapper里写limit参数传值代码虽然多几行但稳定性可控。6.3 全局XSS过滤器处理上传文件的安全隐患系统上线前做安全测试时发现了一个XSS漏洞用户提交租赁申请时的备注字段可以被注入script脚本如果管理员在后台查看申请详情时打开备注内容脚本就会在管理员浏览器中执行这是一个典型的存储型XSS攻击。本来计划写一个全局的XSS过滤器来处理所有请求参数但在处理上传PDF文件时出了问题。问题出现在全局过滤器会读取请求体里的内容做清理过滤而PDF文件是二进制流数据做字符串清洗时会把二进制数据截断或转成乱码导致上传的PDF文件损坏。最后的解决方案是把请求参数按处理类型分开处理JSON和表单文本类型走XSS过滤逻辑multipart类型请求直接跳过过滤器只对文件名做安全校验。这个方案一直运行稳定没有出过问题。6.4 m3u8视频回放在Vue项目中的接入很多高校社团会在活动结束后上传活动视频会员在系统内可以点播回看。技术选型上采用了m3u8格式的流媒体切片播放方案视频文件提前用FFmpeg切片成TS文件并生成m3u8索引文件前端用video.js配合hls.js插件播放。接入过程中有个线索要提醒大家m3u8播放的跨域问题比普通视频更复杂因为浏览器请求TS分片时每个分片都是独立的HTTP请求必须保证服务端对m3u8文件和TS文件都返回正确的CORS响应头。我在nginx配置里加了一行location /video/ { add_header Access-Control-Allow-Origin *; }否则浏览器会报获取分片失败的错误视频只能加载索引文件却无法播放。这个细节让我排查了一个下午写出来避免大家重复踩坑。7. 部署上线本地、云服务器与数据库迁移7.1 前后端分离打包部署系统部署采用的是经典的前后端分离方案。后端代码用Maven打包成jar包命令很简单mvn clean package -DskipTests java -jar hanfu-server.jar --spring.profiles.activeprod前端代码用npm run build构建生成的dist目录部署到nginx。nginx配置里需要做两层代理静态资源直接指向dist目录接口请求反向代理到后端jar包监听的端口。这样浏览器访问页面走nginx接口请求走nginx再转发给Java服务前后端完全解耦。7.2 数据库初始化与定时备份数据库初始化我写了两个脚本schema.sql负责建库建表data.sql负责插入初始分类数据和管理员账号。上线时只需要执行一次后续系统自动运行。为了保证数据安全我在服务器crontab里配置了每天凌晨两点的自动备份任务备份文件保留最近7天0 2 * * * mysqldump -uroot -p123456 hanfu /backup/hanfu_$(date %Y%m%d).sql这个备份策略对高校场景是足够的如果数据量很大可以考虑用binlog增量备份但就这套系统的数据规模来说全量备份已经非常稳妥。7.3 上线前必查的安全清单上线前的安全检查我列了一个清单每一条都是实际踩过坑才加进去的修改数据库默认密码并创建专用账号关闭服务器SSH的root登录并改用密钥登陆SpringBoot配置里的数据库密码用jasypt加密存储nginx配置referer防盗链避免图片和视频被外部站点盗链HTTPS证书在云服务商免费申请强制跳转HTTPS访问后台管理接口增加IP白名单限制。这套清单的意义在于很多课程设计项目重功能轻安全上线两天就被人攻击数据库被删、密码被改。我在这套源码里把安全防线设计好了哪怕你不完全理解每一层的原理照做也能挡住绝大多数常见的攻击手段。8. 拓展方向这套源码还能怎么用这套系统做完之后我其实没有止步于高校汉服租赁这一个场景。它从本质上是一个基于SpringBootVueMyBatis架构的通用租赁管理系统把汉服换成摄影器材、演出服、体育器材业务流程完全通用。我自己实测过的扩展方向有三个第一把数据库和商品模型改成通用SKU结构就能直接转型成设备租赁平台第二增加校园门店扫码取货功能对接一卡通接口可以把租赁流程延伸到线下扫码场景第三给数据统计模块增加大屏展示文化节期间挂在活动现场的大屏幕上实时滚动出租数据这个视觉效果非常好而且实现成本很低。如果你要用这套源码做二次开发我的建议是先跑通整个流程再动数据库结构。源码里我做了比较完善的注释从Controller到Service到Mapper三层结构清晰改起来很顺手。我自己在实际使用中最大的体会是业务系统的价值不在于用了多先进的技术而在于把真实流程跑通、用户用得顺、数据不出错。这套系统在校园场景下卫生、结算、库存三件事做得明明白白我觉得就足够了。
返回列表