
简介这份基于SpringBoot的高校汉服租赁网站论文是一份面向计算机专业毕业设计场景的完整文档适合Java学习者、高校学生及需要参考系统开发流程的读者围绕传统租赁管理效率低、信息难维护等痛点提供了一套从需求分析到系统测试层层落地的解决方案。文档共1个doc文件压缩包约2.84MB除摘要、关键词和目录外主体涵盖绪论、开发环境与技术选型、可行性分析、系统概要设计、详细设计及测试部分目录结构完整清晰。已有106人学习下载内容兼具项目复现价值和论文写作参考价值。读者可借此了解Spring BootJavaMySQL的项目整合思路掌握管理员与用户双端功能模块、数据库设计方法、MVC架构在Web系统中的落地方式以及毕业设计论文的章节组织与测试策略能够直接用于项目开发、文档编写或答辩准备。1. 高校汉服租赁网站的SpringBoot落地路线从毕设选题到可答辩的实现每年三月底校园里穿汉服拍照的人多起来租赁需求也跟着涨。《基于SpringBoot高校汉服租赁网站》这类题目在毕设中很常见但多数实现只是把普通商城改了个名字订单字段里多了个“租几天”押金和归还流程却完全没有闭环。汉服租赁的难点其实在业务模型上一件衣服按尺码计库存租出后库存要锁定归还时要算天数、算逾期费用这比标准CRUD更适合用状态机来驱动。下面按 SpringBoot MyBatis-Plus Redis 这条最稳的路线把工程骨架、数据库、下单归还、权限验证和压测数据串起来新手能照着做熟手也能直接抄走几段关键代码。2. 先固定版本再谈功能SpringBoot 2.7.x 项目骨架与数据访问层毕设项目最大的启动障碍不是业务复杂度而是环境不一致。很多人用 IDEA 创建 SpringBoot 项目时直接点了默认的 3.x 版本系统装的是 JDK 8结果依赖下载失败、启动报UnsupportedClassVersionError。这类题目的技术栈没必要追新统一用 SpringBoot 2.7.18这是 2.x 的最后一个版本对 JDK 8 完全兼容网上能搜到的资料也最多。2.1 IDEA 创建 SpringBoot 项目的版本选型与三个必查配置创建项目时Server URL 选https://start.spring.io如果网络不稳定就换阿里云镜像https://start.aliyun.com。Spring Boot 版本选 2.7.18JDK 选择 1.8。如果遇到 IDEA 不能创建 SpringBoot 项目不能使用 JDK 1.8 的情况按三个位置排查Project Structure 里的 Project SDK、Maven Runner 的 JRE、Java Compiler 的 Target bytecode version。这三个地方不一致项目能创建但启动必挂。SpringBoot 版本选型可以简单分成两组适合毕设的场景直接按左边这组走项目推荐组合可选组合JDK 版本817SpringBoot2.7.183.2.xMyBatis-Plus3.5.3.23.5.5数据库MySQL 8.0MySQL 5.7缓存Redis 6.x不用 RedisSpringBoot 2.7.x 和 3.x 的核心区别在javax包名换成了jakarta代码写法有差异网上 2.x 的代码片段多答辩前想快速改 bug 也方便。数据库连接池不用额外引依赖2.7.x 默认带 HikariCP性能足够。缓存如果直接引spring-boot-starter-data-redisSpringBoot 会自动装配 RedisTemplate省掉一半配置工作。2.1.1 最容易忽略的 Maven 仓库配置创建完项目后先打开 Maven 的settings.xml把镜像配成阿里云。毕设现场的公共网络经常连不上 Maven Central卡在依赖下载会浪费大量时间。配置完镜像后执行一次mvn clean compile确认所有依赖包都能拉下来再开始写代码这个步骤能提前暴露 90% 的环境问题。2.2 数据访问层选型MyBatis-Plus 而非 Spring Data JPA汉服租赁的业务查询条件很杂比如按分类查服饰、按尺码查库存、统计近七天租赁趋势这些查询用 MyBatis-Plus 的 LambdaQueryWrapper 写起来直观SQL 也能自己控制。JPA 上手快但多表关联和动态条件查询时生成的 SQL 不可控答辩时一旦被问到底层 SQL 是什么很难讲清楚。MyBatis-Plus 的代码生成器还能直接根据数据库表生成实体类、Mapper 和 Service这个特性在赶论文的时候非常实用。pom.xml 里的核心依赖这样配parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.2/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependenciesspring-boot-starter-parent锁定了 SpringBoot 版本子模块不需要重复写版本号。MyBatis-Plus 3.5.3.2 兼容 SpringBoot 2.x如果换成 SpringBoot 3.x需要同步升级到 3.5.5 以上否则启动时会报ClassNotFoundException: javax.sql.DataSource。MySQL 驱动在 SpringBoot 2.7 里必须写runtime作用域这个驱动类不会被编译期引用但运行期必须存在。对应地application.yml 里的几个 SpringBoot 配置参数直接决定项目能不能连上数据库spring: datasource: url: jdbc:mysql://localhost:3306/hanfu_rent?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: root hikari: maximum-pool-size: 10 minimum-idle: 5 connection-timeout: 30000 redis: host: localhost port: 6379 timeout: 3000ms mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0map-underscore-to-camel-case将数据库的create_time自动映射到实体的createTime字段不需要写一堆 TableField 注解。logic-delete-field配置了逻辑删除删除记录时 MyBatis-Plus 自动生成UPDATE ... SET deleted 1保证订单表的历史数据能追溯。HikariCP 的maximum-pool-size在毕设项目里设为 10 就够不是越大越好连接数超过数据库实际处理能力反而增加等待时间。2.3 包结构与一个最小的查询链路项目内部分层按四个包走答辩画架构图时直接对应四层com.example.hanfu ├── controller # 接收HTTP请求参数校验 ├── service # 业务逻辑事务边界 ├── mapper # MyBatis-Plus的Mapper接口 └── entity # 数据库实体类定义一个统一响应体ResultT包含 code、message、data 三个字段。所有接口返回这个结构前端 Axios 拦截器统一处理 code 来判断业务是否成功。健康检查接口可以快速验证整条链路RestController RequestMapping(/api/health) public class HealthController { GetMapping public ResultString health() { return Result.success(ok); } }这个接口走通说明 Web 层和序列化正常。再写一个查询服装列表的 Mapper 方法连上数据库说明 Mapper 层正常。两个接口都通了项目骨架才算真正立起来。3. 汉服租赁的领域模型与数据库设计一张租赁订单怎么串起四张表数据库设计是这个题目的核心也是论文里 ER 图要重点画的部分。汉服租赁和普通商城的最大区别是商品是“被借走”而不是“被买走”因此库存模型、订单状态和归还记录都要单独设计。3.1 从“租一件汉服”推导出五个基础实体用户在小程序或网页上浏览汉服选一件衣服、选尺码、选租期提交订单。这里至少涉及这几个对象用户、汉服商品、尺码库存、租赁订单、归还记录。注意汉服商品和尺码库存要拆成两张表。同一款汉服有 S、M、L 码每码的库存不一样下单时锁定的也是具体某一个尺码的库存。普通商城把库存数字直接存在商品表里租赁场景不建议这么做否则每次修改尺码库存都要改商品表统计也麻烦。实体之间的关系可以这样理解用户和订单是一对多订单和商品是多对一订单通过订单明细关联到具体的尺码 SKU归还记录与订单是一对一。这样拆完之后数据库表和实体类一一对应MyBatis-Plus 的代码生成器可以直接产出基础代码。3.2 核心表结构与字段设计用一张 SKU 表说明库存锁定的思路库存表是整个系统的关键字段设计上要给并发操作留足余地CREATE TABLE costume_sku ( id BIGINT PRIMARY KEY AUTO_INCREMENT, costume_id BIGINT NOT NULL COMMENT 关联的汉服SPU ID, size_code VARCHAR(10) NOT NULL COMMENT 尺码编码: S/M/L/XL, stock INT NOT NULL DEFAULT 0 COMMENT 可用库存, locked_stock INT NOT NULL DEFAULT 0 COMMENT 预占库存(已下单未支付), version INT NOT NULL DEFAULT 0 COMMENT 乐观锁版本号, status TINYINT NOT NULL DEFAULT 1 COMMENT 1上架 0下架, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_costume_id (costume_id) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4 COMMENT 汉服尺码库存表;stock是实际可用库存locked_stock是用户下单未支付期间锁定的数量。为什么要两个字段分开因为用户下单后可能不支付如果不锁定库存别人就能把最后的库存买走如果直接把stock减一用户不支付库存又回不来。拆两个字段订单超时未支付时只需要把locked_stock减一、stock加一不用改主库存。version字段给乐观锁用并发扣减库存时靠它判断是否冲突。其他三张表的关键字段如下表名关键字段说明costumeid, name, category_id, daily_price, deposit, cover_url, status汉服SPU信息deposit是押金rent_orderid, order_no, user_id, sku_id, rent_days, total_amount, deposit, status, start_date, end_date, actual_return_time, overdue_fee订单主表status是订单状态机return_recordid, order_id, user_id, return_time, damage_desc, damage_fee, refund_amount一条归还记录对应一笔订单rent_order里的total_amount是租金按daily_price * rent_days计算。押金单独存deposit字段不参与租金计算。overdue_fee在归还时计算后回填这样用户查看订单详情时能直接看到总费用构成租金 押金 逾期费。3.3 租赁订单状态机的定义与迁移条件订单状态是整个系统的业务核心用数据库字段status表示论文里建议配一张状态机表格状态码含义可迁移到的状态0待支付待取衣、已取消1待取衣租赁中、已取消2租赁中待归还、逾期3已归还终态4已取消终态5逾期已归还状态转移全部在后端 Service 层控制不允许前端传 status 直接修改。比如取消订单的接口里要先判断当前状态是 0 还是 1只有这两种状态允许取消。将状态机的遍历条件写明答辩时系统设计部分会有实质内容可讲。3.4 归还记录与逾期费用的数据模型归还不是简单地把订单状态改成已归还。用户填回归还申请管理员检查衣服是否有损坏然后计算出实际费用。归还记录表保存扣款信息损坏费、逾期费、应退押金。这些字段如果不单独建表后续统计收入时只能翻订单表类型都混在一起。归还记录表建出来后每天的财务流水就清晰了。4. 租赁订单核心流程用预占库存防超卖用状态机算逾期费订单模块是整套系统的业务核心。租赁和购买在代码层面最大的区别在于租赁订单有关键的时间维度租几天、什么时候归还、超时怎么算费。这一章的流程做扎实论文的“系统实现”部分就能站得住。4.1 下单流程预占库存与订单创建的事务边界先看一个典型的接口调用过程。用户从前端提交 SKU ID 和租用天数后端要做四件事验证服装在架、锁库存、生成订单、返回支付参数。库存锁定要放在事务里执行避免多个用户同时下单时超卖。使用数据库行锁实现库存锁定Transactional(rollbackFor Exception.class) public RentOrderDTO createOrder(Long userId, Long skuId, Integer rentDays) { // 1. 校验租期最长30天避免长期占用公共库存 if (rentDays null || rentDays 1 || rentDays 30) { throw new BizException(租期必须在1~30天之间); } // 2. 锁定SKU行悲观锁防止并发重复扣减 CostumeSku sku costumeSkuMapper.selectByIdForUpdate(skuId); if (sku null || sku.getStatus() ! 1) { throw new BizException(该服装已下架); } if (sku.getStock() 0) { throw new BizException(该尺码暂无库存); } // 3. 扣减可用库存增加预占库存 costumeSkuMapper.updateStockToLocked(skuId); // 4. 组装订单信息 Costume costume costumeMapper.selectById(sku.getCostumeId()); LocalDate today LocalDate.now(); RentOrder order new RentOrder(); order.setOrderNo(generateOrderNo()); order.setUserId(userId); order.setSkuId(skuId); order.setRentDays(rentDays); order.setTotalAmount(costume.getDailyPrice() * rentDays); order.setDeposit(costume.getDeposit()); order.setStatus(0); order.setStartDate(today); order.setEndDate(today.plusDays(rentDays)); rentOrderMapper.insert(order); return RentOrderDTO.fromEntity(order); }selectByIdForUpdate在 MySQL InnoDB 引擎下会对这一行数据加排他锁另一个事务执行同样的操作时必须等待防止两个请求同时读到库存为 1 然后都下单成功。锁行时带上rollbackFor Exception.class任何一步抛异常整个事务回滚预占库存的修改也随之撤销。updateStockToLocked是自定义 SQL核心语句是UPDATE costume_sku SET stock stock - 1, locked_stock locked_stock 1 WHERE id #{id} AND stock 0这样库存为 0 时更新影响行数为 0从 SQL 层面再兜底一次。4.1.1 为什么不用 Transactional 的默认配置Transactional默认只在运行时异常时回滚受检异常不会回滚。如果rentOrderMapper.insert抛出一个 SQL 受检异常事务可能会提交出现库存扣了但订单没生成的脏数据。毕设项目里直接写成Transactional(rollbackFor Exception.class)把所有异常都纳入回滚范围这是最稳妥的写法。4.2 Redis 在租赁场景下的两个典型用法预占标记与超时释放数据库行锁能防超卖但热点汉服被并发下单时数据库的行锁等待会成为瓶颈。常见做法是在数据库操作之前先用 Redis 做预占标记。用 StringRedisTemplate 处理public boolean preLockSku(Long skuId, Long userId) { String lockKey rent:lock: skuId; String lockValue userId.toString(); Boolean success stringRedisTemplate.opsForValue() .setIfAbsent(lockKey, lockValue, Duration.ofMinutes(10)); return Boolean.TRUE.equals(success); }setIfAbsent对应 Redis 的 SETNX 命令同一 SKU 同时只有一个请求能设置成功。设置 10 分钟过期时间用户下单后卡在支付页面超时后锁自动释放其他用户才能继续租这件衣服。如果用户在 10 分钟内完成了支付需要主动删除这个 key。这里要注意删除时校验 value 是不是当前用户的 ID避免一个用户超时后另一个用户的锁被误删。释放锁时用 Lua 脚本保证“比对值再删除”这两个操作是原子的否则在并发场景下会出现删了别人锁的问题。4.3 支付回调与订单状态推进支付模块在真实项目中对接微信支付或支付宝毕设里多数用模拟支付代替。模拟支付接口接收订单号完成状态推进Transactional public void mockPay(String orderNo) { RentOrder order rentOrderMapper.selectByOrderNo(orderNo); if (order null || order.getStatus() ! 0) { throw new BizException(订单不存在或状态错误); } order.setStatus(1); rentOrderMapper.updateById(order); // 预占库存转为正式租出locked_stock减一 costumeSkuMapper.updateLockedToRented(order.getSkuId()); }状态从 0待支付变为 1待取衣预占库存locked_stock减一这单库存就彻底“租出去了”。这个接口在论文里属于核心业务方法时序图直接按上面的调用顺序画。4.4 归还流程与逾期费用计算归还流程包含两个主动行为用户申请归还、管理员确认归还。用户点击申请归还后订单状态从租赁中变为待归还同时记录申请时间。管理员确认收到衣服后系统计算费用public ReturnRecord confirmReturn(Long orderId, String damageDesc, BigDecimal damageFee) { RentOrder order rentOrderMapper.selectById(orderId); if (order.getStatus() ! 2 order.getStatus() ! 5) { throw new BizException(当前状态不允许归还); } LocalDate actualReturnDate LocalDate.now(); long rentDays ChronoUnit.DAYS.between(order.getStartDate(), actualReturnDate); long overdueDays Math.max(0, ChronoUnit.DAYS.between(order.getEndDate(), actualReturnDate)); // 日租金 总租金 / 租期向上取整保留两位小数 BigDecimal dailyRent order.getTotalAmount() .divide(BigDecimal.valueOf(order.getRentDays()), 2, RoundingMode.HALF_UP); BigDecimal overdueFee dailyRent.multiply(BigDecimal.valueOf(overdueDays)); order.setStatus(3); order.setActualReturnTime(LocalDateTime.now()); order.setOverdueFee(overdueFee); rentOrderMapper.updateById(order); // 归还库存 costumeSkuMapper.releaseStock(order.getSkuId()); // 生成归还记录 ReturnRecord record new ReturnRecord(); record.setOrderId(orderId); record.setReturnTime(LocalDateTime.now()); record.setDamageDesc(damageDesc); record.setDamageFee(damageFee); record.setRefundAmount(order.getDeposit().subtract(damageFee).subtract(overdueFee)); returnRecordMapper.insert(record); return record; }逾期天数通过endDate和实际归还日期的差值计算这里注意必须用Math.max(0, ...)提前归还不产生负费用。应退押金 押金 - 损坏费 - 逾期费如果结果为负数说明押金不够扣需要记录用户欠费状态。整个流程放在一个事务里订单状态变更和库存归还必须同时成功否则会出现衣服还了但订单还在租赁中的问题。归还流程有一个容易漏掉的细节租期跨月或跨年的情况。LocalDate之间的ChronoUnit.DAYS.between会自动处理跨月和跨年不需要手写日期差逻辑这是 JDK 8 的日期 API 比Date类更好用的原因。5. 权限控制与管理端接口用 SpringBoot 拦截器实现双角色体系汉服租赁网站有用户端和管理员端需要对接口做角色控制。很多毕设项目选择引入 Spring Security但它的过滤器链和认证流程对于这个小项目来说过于复杂答辩时如果对源码不熟悉反而容易被追问到底层实现。常见做法是用 SpringBoot 拦截器加自定义注解自己实现一个轻量级权限控制。5.1 自定义注解与拦截器的实现自定义一个RequireRole注解标注在 Controller 方法上Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequireRole { String value(); }然后注册拦截器通过 preHandle 方法校验登录态和角色Component public class AuthInterceptor implements HandlerInterceptor { Autowired private StringRedisTemplate stringRedisTemplate; Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true; } HandlerMethod handlerMethod (HandlerMethod) handler; RequireRole requireRole handlerMethod.getMethodAnnotation(RequireRole.class); if (requireRole null) { return true; } String token request.getHeader(Authorization); String userId stringRedisTemplate.opsForValue().get(token: token); if (userId null) { throw new BizException(登录已过期); } String role stringRedisTemplate.opsForValue().get(user:role: userId); if (!requireRole.value().equals(role)) { throw new BizException(无权限访问); } return true; } }代码逻辑说明请求进入 Controller 方法之前先判断方法上有没有RequireRole注解。没有注解的接口直接放行有注解的属性校验 token 和角色。token 存 Redis过期时间与用户登录态一致。用户登录时把 userId 存入 Rediskey 是token:{token}value 是 userId。用户在管理端被禁用时直接删除这个 key该用户的所有请求立刻失效这个能力比 JWT 更可控也是论文里可以写的一个细节。再在 WebMvcConfig 里注册这个拦截器指定要拦截的路径/api/**排除管理员登录接口。注册时有一个坑要注意拦截器是 Spring 容器管理的 Bean但addInterceptors方法注册时如果通过new AuthInterceptor()创建实例会导致Autowired注入失败出现空指针。必须通过Component注解让 Spring 管理这个拦截器在配置类里注入后直接使用注入的实例。5.2 用户端与管理端的功能边界权限通过RequireRole注解按角色划分角色功能范围代表接口USER浏览服装、下单、支付、申请归还、查看自己的订单/api/order/createADMIN服装上下架、库存管理、订单审核、归还确认、数据统计/api/admin/costume/update一个方法可以同时标注多个角色比如用户查看自己的订单和管理员查看所有订单可以共用一个 Controller不同方法标注不同权限码。这种设计在论文的功能模块划分里非常清晰截两张接口权限表放上去即可。5.3 管理端数据统计报表的 SQL 落地管理端首页通常要展示三个统计图表租赁分类占比、近七天租赁趋势、收入排行。这些数据接口的 SQL 直接作为论文的“系统实现”重点-- 按服装分类统计租赁笔数 SELECT c.category_name, COUNT(*) AS rent_count FROM rent_order o JOIN costume_sku cs ON o.sku_id cs.id JOIN costume c ON cs.costume_id c.id WHERE o.status IN (2, 3, 5) GROUP BY c.category_name ORDER BY rent_count DESC; -- 近七日租赁趋势注意按天分组时用DATE函数 SELECT DATE(o.create_time) AS order_date, COUNT(*) AS order_count FROM rent_order o WHERE o.create_time DATE_SUB(CURDATE(), INTERVAL 7 DAY) GROUP BY DATE(o.create_time) ORDER BY order_date;第一个 SQL 按分类聚合统计状态为租赁中、已归还、逾期的有效订单。第二个 SQL 用DATE_SUB(CURDATE(), INTERVAL 7 DAY)取出七天窗口按日期分组。前端用 ECharts 调用这两个接口柱状图和饼图的数据源就齐了。注意统计查询不能用select *只需要返回聚合字段查询结果直接映射到 DTO避免把大对象都查出来再内存里过滤。这个统计模块做完管理端的首页就可以展示数据了。同时注意要在查询的 Mapper 方法上加上Select注解直接在注解里写 SQL保持代码可读性。接口设计上注意统一返回Result前端根据状态码判断是否需要重新登录。管理端还涉及一个常见的操作强制下线用户。在 Redis 里删除用户的 token 记录即可下一次请求到达拦截器时会发现 token 不存在返回登录失效。这也是把 token 放在 Redis 里而不是做成 JWT 的额外收益。管理员确认归还这个操作也应该有权限控制。只有 ADMIN 角色才能调用confirmReturn接口用户提交的归还申请只是创建一条归还记录最终的费用结算必须由管理员确认。这样设计在论文中属于业务流程闭环的一部分面试官问“为什么用户不能自己确认归还”时可以说这是为了防止用户单方面提前结束租赁导致费用纠纷。6. 论文数据从哪来压测接口、参数记录与答辩前的验证清单功能做完论文里需要“系统测试”章节的数据支撑。不要编数据直接跑一轮压测把结果截图放进去既真实又能展示工程能力。压测工具用 JMeter网上模板多配置也简单。6.1 三个必调的 SpringBoot 参数参数推荐值作用spring.datasource.hikari.maximum-pool-size10数据库连接池上限过高会增加数据库负担spring.redis.timeout3000msRedis 连接超时防止 Redis 故障拖垮下单接口mybatis-plus.configuration.log-implorg.apache.ibatis.logging.stdout.StdOutImpl控制台输出 SQL压测前必须关闭压测前把 MyBatis-Plus 的 SQL 日志关掉否则每个请求都会打印大量 SQLIO 会拖慢测试结果数据不真实。6.2 JMeter 压测的三个步骤创建一个线程组线程数设为 100Ramp-Up Period 设为 10 秒循环次数设为 10这样模拟 100 个用户同时访问。添加 HTTP 请求取样器协议选 HTTP服务器填 localhost路径填/api/costume/list添加查看结果树和聚合报告两个监听器。跑完后聚合报告里会显示 Average、Throughput、Error% 三个指标把这些数据记录下来。第一轮不启用 Redis 缓存直接压数据库查询接口。第二轮在 Service 层加上Cacheable(cacheNames costumeList, key all)注解再压一次。两轮数据对比写进论文一张表格说明缓存带来的性能提升。这里提醒一个细节Cacheable默认会序列化 Java 对象实体类必须实现Serializable接口并且LocalDateTime字段的序列化要引入 jackson-datatype-jsr310 依赖否则压测时会报序列化异常。6.3 答辩时最后的验证清单启动项目后按这个顺序手动走一遍完整流程用户注册登录、浏览汉服列表、选择尺码租 3 天、模拟支付、管理员后台看到订单、确认发货、用户申请归还、管理员确认归还、系统自动计算逾期费。每个步骤对应的数据库变化都记录下来特别是costume_sku表的stock和locked_stock字段变化这张截图放在论文里非常能说明问题。所有接口都验证过后用 Postman 导出一份接口测试集合连同数据库设计 SQL 一起放进附录。之前 Redis 锁释放的场景也值得验证下单后不支付等 10 分钟看 Redis 的 key 是否自动消失库存有没有回滚。这组实验数据填到论文的“系统测试”章节比任何文字描述都有说服力。本文还有配套的精品资源点击获取