我前后做了三个SpringBoot的毕设项目,其中露营装备租赁系统这一个,是让我觉得业务闭环最完整、也最能体现Java后端开发核心能力的题目。先说说结论:如果你正在准备计算机毕业设计,又想要一个“看起来有工作量、答辩时能讲清楚逻辑、代码量适中”的题目,SpringBoot + 户外露营装备租赁这个方向是性价比很高的选择。
为什么这么说?因为租赁系统天然就是一个“麻雀虽小五脏俱全”的业务模型:它涉及用户管理、商品(装备)管理、库存状态流转、订单生命周期、押金计算、租用时段校验等一连串真实业务场景。相比普通的CRUD博客系统,它多了“状态机”的复杂性;相比电商秒杀系统,它又没有那么高的并发要求——正好落在毕业设计难度区间的甜蜜点上。
我自己在复现这个项目时,还额外做了不少工程化的处理:比如用Redis缓存热点装备信息、用Quartz做租用超时自动取消、用JWT做双端登录态隔离。这些点如果你也能在论文和答辩里讲出来,分数会明显不一样。这篇文章我会从项目整体拆解、数据库设计、核心模块实现、常见坑点几个维度完整走一遍,希望能给你一个能直接参考复现的路线。
1. 内容整体设计与思路拆解
1.1 为什么选SpringBoot做租赁平台后端
选SpringBoot做毕业设计,首先是一个“稳妥”的选择。市面上的Java岗位开发基本都在用SpringBoot,导师看到这个技术栈也不会觉得陌生。更重要的是,SpringBoot的自动配置机制大大降低了项目搭建的门槛:你不用再像SSH时代那样写一堆XML配置文件,一个spring-boot-starter-web依赖拉进来,一个@SpringBootApplication注解标上,就能起一个Web服务。
但这不意味着你就可以不关心原理。答辩时导师大概率会问你:“SpringBoot的自动配置是怎么实现的?”关于这一点我到后面实操部分会详细说。这里先有个概念:SpringBoot启动时会通过@EnableAutoConfiguration加载META-INF/spring.factories里配置的自动配置类,按条件注解(@ConditionalOnClass、@ConditionalOnMissingBean等)判断是否生效。理解了这个机制,你对SpringBoot的掌控感会上升一个台阶。
1.2 租赁系统比普通管理系统多出来的难点
如果只是做一个普通的物品管理,那就太单薄了。租赁系统的核心特征是:资源在时间维度上存在占用状态。一件帐篷被用户A租了7月1日到7月3日,那么同时间段内它就不能再租给用户B。这跟普通的商品下单完全不同——普通商品卖完就没了,而租赁装备是“暂时不可用”,到期后要释放库存。
这个特性直接决定了系统的几个核心设计:
- 装备库存不能用简单的“库存数量”字段来标识,需要结合“时间段”来判断可用性。
- 订单状态必须是一个状态机:待支付、待取用(待发货)、使用中(已取用)、待归还、已完成、已取消、超时未取自动取消。
- 押金计算、租金计算都要基于“租用天数”来动态结算。
这些逻辑链一旦理清楚,整个系统的骨架就出来了。
1.3 整体技术栈选型与分工
我最终采用的技术栈如下,都是Java生态里比较主流的方案:
| 层级 | 选型 | 说明 |
|---|---|---|
| 后端框架 | SpringBoot 2.7.x | JDK8兼容性好,毕业设计稳定性优先 |
| ORM框架 | MyBatis-Plus | 单表CRUD不用写SQL,复杂查询用注解SQL |
| 数据库 | MySQL 5.7 / 8.0 | 存储业务数据 |
| 缓存 | Redis | 装备热点数据缓存、验证码存储 |
| 权限认证 | JWT + Spring Security | 双端(用户端/管理端)登录态管理 |
| 定时任务 | Quartz / Spring Scheduled | 超时未支付、超时未取用自动取消 |
| 前端 | Vue2 + Element UI | 管理后台;用户端用轻量页面即可 |
| 接口文档 | Swagger / Knife4j | 方便自测,也方便答辩演示 |
有人可能会问,毕业设计用这么重的技术栈会不会被质疑“过度设计”?我的看法是:核心链路能说清楚就不算过度。比如Redis,你可以在装备列表接口上加缓存,并解释“装备信息读多写少,用缓存降低数据库压力”——这就是合理的性能优化,不是堆技术。但如果你的项目里Redis只用来存了个验证码,那答辩就会比较被动。
2. 核心细节解析与实操要点
2.1 数据库建模:把业务对象拆成表
数据库设计是整篇论文的基础,也是答辩最容易翻车的地方。表设计如果烂了,后面所有代码都在打补丁。我按业务域把表分成三组:
第一组:“人”相关的表
user:用户表,字段包括昵称、手机号(登录账号)、密码(BCrypt加密)、身份证号(实名认证)、信用分、注册时间。admin:管理员表,管理端登录账号,独立于用户表。
第二组:“物”相关的表
equipment_category:装备分类表(帐篷、睡袋、烧烤架、户外灯具、登山包等)。equipment:装备明细表,核心字段有装备名称、分类ID、品牌/型号、日租金、押金、库存总量、装备图片、描述信息、状态(上架/下架)。equipment_sku:这里我做了一个小扩展。因为同一款帐篷可能有不同规格(双人/三人),或者同一类装备有多个具体编号(独立编号便于区分是哪一套在库),分开存更清晰。
第三组:“事”相关的表,也就是租赁流程
rental_order:订单主表,字段包含订单编号、用户ID、总租金、总押金、订单状态、开始时间、结束时间、下单时间、支付时间、取用码、备注。rental_order_item:订单明细表,关联具体装备SKU、租赁数量、单价、小计。payment_record:支付流水表,记录支付金额、支付方式、支付时间、第三方流水号(毕业论文中可以用模拟支付)。inventory_lock:库存占用表,记录某件装备在某时间段的占用情况。这是租赁系统设计的关键点之一。
我在设计inventory_lock表时,是这么考虑的:每一笔订单在生成时,会同时插入“库存占用记录”,记录该订单占用了哪个SKU、数量多少、占用时间段。归还时删除对应记录。这样查询某装备在某个时间段是否可租,只需要查这张表有没有冲突记录即可。
2.2 时间段冲突判断的逻辑
这个逻辑是整个租赁系统最核心的算法,也建议你在论文里重点描述。假设装备A在7月1日至7月3日被订单X占用,现在用户B想租7月2日至7月4日,系统应该怎么判断?
我的实现方式是:
// 冲突判断:新租用时间段与已有占用时间段重叠 // 条件:新开始时间 <= 已有结束时间 且 新结束时间 >= 已有开始时间 SELECT * FROM inventory_lock WHERE sku_id = #{skuId} AND status = 1 AND locked_start_time <= #{endTime} AND locked_end_time >= #{startTime}如果这条SQL能查到记录,说明时间段冲突,不能下单。这个区间重叠判断条件看起来简单,但很多初学者容易写反。我当时就有一个毛病,写成了“开始时间在对方时间段内”或者“结束时间在对方时间段内”的OR条件,结果边界重叠时判断就出错。其实最严谨的还是上面这个“两个区间互不重叠的否命题”:只有新开始时间大于已有结束时间、或新结束时间小于已有开始时间,才不冲突;其余情况全部视为冲突。
2.3 订单状态机:让流程可追踪、可控制
租赁订单的状态流转,我在实现时花了比较多的时间梳理。状态机如果理不清,后面写取消、写归还功能时全是“黑盒”。最终我定义的状态有:
| 状态码 | 状态名 | 触发动作 |
|---|---|---|
| 0 | 待支付 | 用户提交订单后 |
| 1 | 待取用 | 用户支付完成后,或管理员确认订单后 |
| 2 | 使用中 | 用户到店/到点扫码取用后 |
| 3 | 待归还 | 用户发起归还申请,或装备超期未归还 |
| 4 | 已完成 | 管理员确认归还,押金原路退回 |
| 5 | 已取消 | 用户主动取消/超时未支付自动取消 |
| 6 | 已违约 | 超期未归还且长时间无响应 |
这里要特别注意:支付完成后到取用前的时间窗口,不能直接变成“使用中”。因为用户可能下单了但一直没来取货(很多露营地是线上预约线下取用)。我设计了一个“取用码”机制:用户支付后收到一个6位随机码,线下取装备时管理员输入取用码,装备才会变为“使用中”状态。这其实也模拟了线下业务闭环,答辩时讲出来会显得考虑很周全。
2.4 SpringBoot自动配置原理与项目分层
前面提到的SpringBoot自动配置原理,是简历和答辩的加分项。简单理解,SpringBoot是通过@EnableAutoConfiguration引入了一系列自动配置类。比如我们引入了spring-boot-starter-data-redis,SpringBoot会自动配置RedisTemplate、StringRedisTemplate这些Bean;引入mybatis-plus-boot-starter后,会自动配置SqlSessionFactory和MapperScannerConfigurer。
项目分层,我建议严格按照下面的结构来:
com.example.camping ├── controller // 接口层,接收参数,返回统一结果 ├── service // 业务逻辑层,事务控制都在这层 │ └── impl ├── mapper // MyBatis-Plus的Mapper接口 ├── entity // 数据库实体类 ├── dto // 接口入参/出参对象 ├── vo // 视图对象 ├── config // 配置类(Redis、JWT、拦截器等) ├── common // 统一返回结果、异常处理、常量 └── utils // 工具类(JWT工具、日期工具等)有些同学喜欢把Controller写得很厚,各种业务逻辑往里塞。短项目可能没事,但像租赁这种状态流转复杂的项目,后期调试会非常痛苦。我一般习惯在Service层做主要业务处理,Controller只负责参数接收和调用Service,同时用全局异常处理器兜底,接口对外只返回统一格式的数据。
3. 实操过程与核心环节实现
3.1 项目初始化与依赖配置
我用的是IDEA + Spring Initializr,选择SpringBoot 2.7.14版本。JDK用的1.8,不推荐一上来就上JDK17+,部分老版本的MyBatis-Plus和SpringBoot之间存在兼容性问题,没必要给自己找麻烦。
核心依赖如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency>我的application.yml关键配置如下:
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/camping_rental?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 redis: host: localhost port: 6379 database: 0 mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: true这里有个细节容易被忽略:数据库连接URL里必须指定serverTimezone=Asia/Shanghai,否则表里如果有DATETIME类型字段,查询出来会差8个小时。另外useSSL=false是指明不使用SSL连接,本地开发足够了。
3.2 用户注册登录与JWT签发
用户注册登录是系统的基础模块。注册接口的处理流程是:手机号是否已注册 → 密码BCrypt加密 → 保存用户信息 → 返回成功。密码加密一定不要用MD5,因为MD5是可破解的,BCrypt会自动加盐且每次哈希值不同,是目前的主流做法。
登录接口的流程是:手机号查询用户 → BCrypt密码校验 → 生成JWT令牌 → 返回令牌和用户信息。
JWT工具类核心代码:
public class JwtUtils { private static final String SECRET = "camping-rental-secret-key"; public static String generateToken(Long userId, String role) { return Jwts.builder() .claim("userId", userId) .claim("role", role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + 1000L * 60 * 60 * 24 * 7)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } public static Claims parseToken(String token) { return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody(); } }然后在拦截器中解析JWT,判断用户角色和登录状态。用户端和管理端的登录接口是分开的,我用role字段区分,拦截器里做校验。管理端接口路径以/admin/**开头,用户端接口路径以/api/**开头。
3.3 装备列表与缓存设计
装备列表是用户访问量最大的接口,也是我加Redis缓存的地方。我先设置了简单的缓存策略:KEY为equipment:list:{categoryId}:{page}:{size},过期时间60分钟。用户查询装备列表时先从Redis查,没有则查MySQL并写入Redis。
具体实现我用的是RedisTemplate<String, String>加Jackson2JsonRedisSerializer序列化。这里有个坑:如果不指定序列化器,默认使用的是JDK序列化,存进去的是一堆\xAC\xED\x00\x05开头的乱码。这时候不是Bug,是你没配置对序列化器。我用了一个简单的配置类解决:
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer<Object> jacksonSeial = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jacksonSeial.setObjectMapper(om); template.setValueSerializer(jacksonSeial); template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(jacksonSeial); template.afterPropertiesSet(); return template; } }3.4 下单核心流程实现
下单流程是整个系统最复杂的一环,我强烈建议在Service层加@Transactional注解,保证库存占用记录和订单记录的原子性。
核心步骤依次是:
- 接收用户提交的租用开始时间、结束时间、装备SKU和数量。
- 校验装备是否上架、时间段是否可租(调用前面提到的冲突判断SQL)。
- 计算租金与押金总额:租金 = 日租金 × 租赁天数;押金 = 装备押金 × 数量。
- 扣减库存,插入
inventory_lock占用记录,记录占用时间段。 - 生成订单主表和订单明细表,订单初始状态为“待支付”。
- 返回下单结果与订单号。
关键代码大体是这个形态:
@Transactional(rollbackFor = Exception.class) public RentalOrder createOrder(OrderCreateDTO dto) { // 1. 校验时间段 List<InventoryLock> locks = inventoryLockMapper.selectConflict( dto.getSkuId(), dto.getStartTime(), dto.getEndTime()); if (!locks.isEmpty()) { throw new BusinessException("该装备在此时间段已被预订,请选择其他时间"); } // 2. 计算费用 EquipmentSku sku = equipmentSkuMapper.selectById(dto.getSkuId()); long days = ChronoUnit.DAYS.between(dto.getStartTime(), dto.getEndTime()); BigDecimal rentAmount = sku.getDailyPrice().multiply(BigDecimal.valueOf(days)); BigDecimal depositAmount = sku.getDepositPrice(); // 3. 插入库存占用记录 InventoryLock lock = new InventoryLock(); lock.setSkuId(dto.getSkuId()); lock.setStartTime(dto.getStartTime()); lock.setEndTime(dto.getEndTime()); lock.setStatus(1); inventoryLockMapper.insert(lock); // 4. 生成订单 // ... 订单主表和明细表的插入 return order; }3.5 超时未支付自动取消的实现
系统里有几个“超时自动操作”的需求:订单生成后30分钟未支付自动取消;订单支付后24小时未取用自动取消。这些功能如果手动去数据库改,效率太低,也不专业。我采用Spring自带的@Scheduled定时任务来扫描。
先在启动类加@EnableScheduling,然后写一个定时任务类:
@Component @Slf4j public class OrderTimeoutTask { @Autowired private RentalOrderMapper orderMapper; @Scheduled(cron = "0 0/5 * * * ?") // 每5分钟执行一次 public void cancelTimeoutOrders() { LocalDateTime deadline = LocalDateTime.now().minusMinutes(30); // 查询待支付且下单时间早于deadline的订单 List<RentalOrder> timeoutOrders = orderMapper.selectTimeoutUnpaidOrders(deadline); for (RentalOrder order : timeoutOrders) { // 取消订单,释放库存占用 orderMapper.cancelOrder(order.getOrderId()); } } }你还需要注意:定时任务里做状态流转时要加上版本号或乐观锁,防止管理员手动操作订单和定时任务同时修改同一条记录。我在rental_order表里加了一个version字段,更新时带着WHERE version = #{oldVersion},影响行数为0说明已被其他操作改了,直接跳过。
3.6 归还与押金退回流程
归还流程是租赁系统区别于一般交易系统的又一大看点。我的设计是这样的:
用户端点击“申请归还”,系统记录申请时间为归还时间。管理员在后台看到归还申请列表,线下检查装备无损后点击“确认归还”。确认归还时:
- 删除对应订单的
inventory_lock占用记录。 - 将订单状态改为“已完成”。
- 将押金原路退回。因为毕业论文一般不接真实支付,我这里做了一个模拟:在
payment_record表插入一条“押金退回”记录,金额为负。
如果本意是做一个有野心的项目,还可以接入支付宝沙箱支付环境。支付宝沙箱可以提供真实支付流程体验,对论文的“支付模块”是一个很好的增强。
4. 常见问题与排查技巧实录
4.1 数据库时间差8小时的问题
这是我自己踩过最典型的坑。查询订单列表时,发现所有时间都比实际时间晚了8个小时。排查后发现是MySQL连接参数没加serverTimezone=Asia/Shanghai,同时JDBC驱动默认用的是系统时区,本地机器的时区没设置对,就产生了偏移。这类问题在答辩演示时出现,可以说是灾难级的。
解决方式就是前面提到的,在jdbc:mysql连接URL里明确指定时区。另外如果用的是Jackson序列化LocalDateTime,建议在application.yml配置:
spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+84.2 MyBatis-Plus 分页查询不生效
如果按网上教程配置了分页插件还是不行,大概率是没把分页插件注入到Spring容器。MyBatis-Plus的分页需要单独配置一个MybatisPlusInterceptorBean:
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }这个配置不加,Page对象会查出全表数据,分页完全失效,而且在控制台打印的SQL里可以看到没有LIMIT语句。发现问题时先看控制台SQL,这是排查的好习惯。
4.3 跨域问题
前端用Vue开发,默认端口是8080或5173,后端端口是8080,端口不同必然产生跨域。在SpringBoot中,我用了两种方式解决,选其一即可:
方式一:在Controller类上添加@CrossOrigin注解。但因为项目接口较多,我更推荐方式二。
方式二:写一个CORS全局配置类。
@Configuration public class CorsConfig { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }; } }4.4 循环依赖问题
在Service层互相调用治理的不够小心时,Spring启动会报“BeanCurrentlyInCreationException”这类的错误。比如我在用户Service里注入了订单Service,订单Service里又注入了用户Service,Spring就无法决定先创建哪个Bean(至少在构造器注入时不行)。解决方式很简单:把构造器注入改为@Autowired字段注入,或者拆分Service,让A调B而不是互相调。我后来把订单相关的库存操作抽到了InventoryLockService,用户Service和订单Service都只依赖它,循环依赖的问题就消失了。
4.5 订单取消后库存没释放
这是我复现项目时差点漏掉的一点。很多排错问题本质上都是“状态流转没有闭环”。比如用户取消订单后,inventory_lock表里的占用记录还留着,导致该装备在这段时间永远显示不可租。修改方案是在取消订单的方法里增加释放库存的操作:
// 释放库存占用 inventoryLockMapper.releaseByOrderId(orderId);这类问题建议你在写代码时先画出状态图和操作步骤,再动手编码,代码里每个状态流转都对应明确的库存操作,会大幅减少Bug。
4.6 常见问题速查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 控制台SQL打印中文乱码 | 数据库连接未指定utf8 | URL加characterEncoding=utf8,并确认数据库表和字段的字符集为utf8mb4 |
| Redis存了但取不到/读出来乱码 | 未配置序列化器 | 按前面的RedisConfig方式显式配置Jackson序列化器 |
| 接口返回时间差8小时 | 时区未指定 | 数据库URL和Jackson都配置Asia/Shanghai或GMT+8 |
| 项目启动端口被占用 | 本机端口冲突 | server.port修改,或查找占用进程释放端口 |
| 上传图片不能访问 | 静态资源路径未映射 | 配置WebMvcConfigurer的addResourceHandlers,映射本地磁盘路径 |
| 分页不生效查出全部数据 | 分页插件未注入 | 配置MybatisPlusInterceptorBean |
5. 论文与答辩中如何放大这个题目的价值
5.1 论文结构怎么安排更合理
做毕业设计,论文和技术实现同等重要。我比较推荐的章节安排是:
- 绪论:阐述露营产业发展背景、租赁行业的痛点,自然引出课题意义。这里不要通篇空谈“随着社会发展”,要落在具体场景上:露营业爆发式增长、装备单价高、普通用户购买不划算等,从而说明租赁平台的价值。
- 相关技术介绍:介绍SpringBoot、MyBatis-Plus、Redis、JWT在系统中的适用场景。
- 系统需求分析:画出用例图,分用户端和管理端梳理功能清单。
- 系统设计:包括总体架构图、功能模块设计、数据库E-R图与表结构设计、核心业务流程设计,尤其是订单状态机的描述。
- 系统实现:按模块配截图展示关键代码与界面。
- 系统测试:写功能测试用例、接口测试结果,甚至可以加一段压力测试说明系统性能。
5.2 答辩时建立个人技术认知的突破口
注意,以上的技术内容都没有问题,但你要想一想怎么让自己的表现在全场毕设中更突出。这方面我有几点建议:
一,把**“时间段冲突判断”**作为你研究的一个小亮点。你可以主动在黑板上画出重叠区间图,写出一段SQL,分析边界情况。很多同学都想不到这一点,能讲清楚说明你真的理解租赁业务。
二,把**“订单状态机”**作为另一个亮点。用一张状态流转图(注意不是Mermaid,而是你论文中的UML状态图)解释各状态间的触发条件,以及超时自动取消的实现。这展示的不只是写代码的能力,而是业务抽象能力。
三,把**“工程化细节”**作为加分项。比如统一返回结果Result<T>、全局异常处理器@RestControllerAdvice、JWT拦截器、参数校验@Validated等。这些都是企业开发中的标准做法,你主动提出来,答辩老师会认为你有实际工程经验。
5.3 几件需要注意避免的事情
第一,不要把代码贴在论文正文里太长。核心代码截取片段即可,控制在半页以内,重点是讲思路和流程。
第二,不要用在线Demo网站或直接用别人的开源项目源码。现在毕业设计查重和导师提问越来越细,答辩时如果被问到“这个模块的异常你怎么处理的”却答不上来,评分会非常难看。
第三,不要在答辩时只演示“页面好看”,要多点开数据库给大家看数据表的关联逻辑,或者打开Swagger接口文档现场测试某个接口。数据流通起来,技术含量自然就出来了。
6. 从SpringBoot到简历:这套项目的延伸价值
我个人在实际操作中的体会是,露营装备租赁系统不是一个“做完成绩归档就完事”的题目,它的延展性极强。如果你候选的岗位偏向物流、共享服务、平台型业务,那么这类系统项目在简历上比“学生管理系统”“图书管理系统”更能引起面试官的兴趣。
你可以从几个方向继续扩展它:
- 引入Redis分布式锁,解决高并发场景下同一装备被多人同时下单的竞态问题;
- 在归还超时场景中加入自动扣款+违约记录,模拟信用体系;
- 增加消息推送或站内信模板,把Spring整合ActiveMQ或RabbitMQ的链路打通;
- 用WebSocket实现订单实时进度推送,比如用户扫码取用后,管理端界面自动更新状态。
最后再分享一个小技巧:开发这套系统时,我自己习惯用Postman建一套完整的接口测试集合,从注册、登录、下单、支付、取用、归还、退押金全流程跑通。这看起来多花了半小时,但答辩演示时你只需要点一键运行整套流程,非常流畅,也能解释清楚每个环节的数据流转。做毕业设计不只是为了拿学分,在调试系统和梳理逻辑的过程中沉淀下来的工程化思考方式,对后面工作也是实实在在的帮助。