Spring Boot校园闲置物品租赁系统:从设计到部署全攻略
又到了一年两度的毕设季节后台私信里一大半都是同一个问题Spring Boot 项目到底怎么做。说实话“基于 Spring Boot 的校园闲置物品租赁系统”这个题在我带过的学生项目里出现频率相当高属于典型的“性价比选手”——业务场景贴近学生生活、功能好拆解、技术栈主流、答辩时又有故事可讲。但它也不是那种随便抄个开源项目就能糊弄过去的题租赁流程的订单状态管理、物品发布与审核、用户权限控制这几个点如果没想清楚就急着写代码后期会反复返工。这篇文章我就以这个项目为主线把我实际带项目时的完整思路、核心实现、安装部署流程以及写文档、准备答辩的心得全部梳理一遍。项目本身不复杂但我会重点讲清楚“为什么这样做”还会把容易踩坑的地方单独列出来。不管你是准备拿这个题目做毕业设计还是刚学完 Spring Boot 想找个完整项目练手这份内容都能让你少走不少弯路。1. 项目整体设计与技术选型1.1 业务场景与核心功能校园闲置物品租赁说白了就是把学生手里那些“用不上但扔了可惜”的东西盘活。我接触过的实际场景里主力物品是这些自行车、相机镜头、专业课教材、游戏机、正装礼服甚至还有一些小家电。这些物品的特点是单价相对较高、使用频率低、学生之间又有天然的信任基础——都在一个校区出了问题跑得了和尚跑不了庙。所以这个系统的核心业务流程就要解决三个问题卖家怎么把闲置物品展示出来、买家怎么找到并租下物品、交易过程中双方怎么保障权益。我给出的模块划分是这样的用户端注册登录、物品浏览与搜索、发布闲置、发起租赁、订单支付确认简化版、订单管理、个人资料管理管理端用户管理、物品审核与下架、租赁订单查看与干预、系统公告发布、数据统计这里我特别强调一个点闲置物品不是上架就能租的得有一个“管理员审核”环节。很多同学觉得这步是多余的直接做“发布即上架”会省很多事。但从实际业务角度看如果没有审核广告、违规物品、重复信息会直接淹没真实物品。从毕设答辩角度看多一个审核环节你的系统就多了一个角色、多了一层权限控制技术亮点和业务完整性都能提上去。利大于弊值得做。1.2 为什么选 Spring Boot MyBatis-Plus MySQL这个技术组合在Java毕设里算是“标准答案”了但我还是想说说每个组件在项目里具体承担什么职责免得你只是听说过名字就往上堆。Spring Boot负责把整个应用的骨架搭起来。它最实用的地方是自动配置和 starter 机制——引入一个依赖框架自动帮你把对应的组件初始化好你只需要写业务代码。比如引入spring-boot-starter-web内嵌 Tomcat、MVC 配置、JSON 序列化这些都备好了引入mybatis-plus-boot-starter数据源和 MyBatis 的组装也不用你操心。相比传统 SSM 时代那一堆 XML 配置Spring Boot 对新手友好太多了。MyBatis-Plus选它的核心理由是开发效率。它是 MyBatis 的增强工具内置了通用的增删改查方法单表操作根本不用写 SQL直接调baseMapper.selectPage、baseMapper.insert就行。一个物品表、用户表、订单表的基础 CRUD 代码量能省掉一半以上。而且它支持分页插件、条件构造器QueryWrapper写复杂查询的时候比手拼 SQL 直观得多。MySQL这边不用多说关系型数据模型天然适合租赁系统这类结构化业务数据。我建库的时候统一用 utf8mb4 字符集因为商品描述里经常出现 emoji、特殊符号utf8mb4 才能完整存储。存储引擎选 InnoDB支持事务租赁订单这种涉及资金和状态变更的数据必须有事务兜底。1.3 前端方案的选择建议关于前端我知道这个问题一定有人纠结。我给你的建议很直接如果想快速把系统跑通、把精力集中在后端上用 Thymeleaf 模板引擎 Bootstrap jQuery 是最稳妥的方案。Spring Boot 对 Thymeleaf 的支持很成熟页面直接写在templates目录下Controller 返回视图名就能渲染前后端不用联动调试一个人就能搞定。如果你已经学了 Vue或者想给简历上加一个“前后端分离”的项目经验那可以用 Vue 3 Element Plus Axios后端只提供 JSON 接口。这个方案看着高级但代价是工作量明显增加跨域处理、Token 传递、异步渲染、打包部署每一步都会消耗时间。我的建议是预留充足时间、有一定前端基础就上前后端分离时间紧、求稳就 Thymeleaf。不要两头摇摆做毕设最忌讳中途换技术栈。2. 数据库设计与核心模型构建2.1 核心表结构的设计思路数据库设计是这个项目最先要打好的地基。我见过太多项目代码写到一半发现表结构不合理又回头改表连带改实体类、改 Mapper、改页面工作量翻倍。所以我会先花一整天时间把表设计反复推演清楚再动工。这个项目我最终设计了6张核心表user用户表id、username、password、nickname、phone、avatar、role0用户 1管理员、status、create_timecategory物品分类表id、name、sort、create_timeitem物品表id、user_id、category_id、title、description、price_per_day、deposit、images、status0待审核 1上架 2已下架 3已租出、view_count、create_timerental_order租赁订单表id、order_no、item_id、lessee_id承租方、lessor_id出租方、start_date、end_date、total_amount、deposit、status0待支付 1租赁中 2待归还 3已完成 4已取消 5申请归还、create_timemessage留言表id、item_id、from_user_id、to_user_id、content、create_timenotice公告表id、title、content、create_time有一点要提醒物品表里的images字段我设计的是 varchar 类型多张图片用逗号分隔存储例如url1,url2,url3。这种做法在规范化的数据库设计课里可能不够“正统”但在实际项目里非常实用——减少关联查询、读取快、实现简单。你要在文档里被别人问起为什么这么设计答一句“图片查询频率高、数量有限、逗号分隔可以避免多次 IO”就完全能站住脚。2.2 租赁订单状态机设计订单状态是这个系统里业务逻辑最复杂的部分也是答辩时老师最喜欢追问的地方。我按实际租赁流程把它拆成了6个状态状态值含义说明0待支付买家发起租赁申请生成订单等待支付1租赁中买家支付卖家确认租期开始计算2待归还买家点击“申请归还”等待卖家确认3已完成卖家确认归还订单结束4已取消买家支付前取消订单或超时未支付自动取消5已拒绝卖家不同意出租订单终止这个状态机的流转规则是这样的0 → 1支付成功→ 2申请归还→ 3确认归还其中 0 可以回到 40 可以跳到 5。我在代码里没有用复杂的状态机框架就写了一个OrderStatusUtil工具类专门负责校验状态流转是否合法。比如判断当前状态是 1租赁中才允许走“申请归还”状态不是 0 就不允许支付。这种轻量级的状态校验对毕设项目来说完全够用而且代码可读性比引入状态机框架好很多。这里有个容易忽略的小细节订单编号order_no要用时间戳 随机数生成别用数据库自增主键直接当业务编号展示给用户。原因有两个一是订单号会暴露系统的真实订单量这既不好看也不安全二是后续对接支付、对账业务号与主键分离会更灵活。我用的格式是yyyyMMddHHmmss 4位随机数实测下来并发场景下基本不会重复。2.3 表单校验与数据字典设计数据库层面的规范差不多后一个影响开发体验的细节是数据字典的统一。比如物品状态的枚举值我在表里用的是 0 到 3 的数字前端页面总不能直接显示“0”吧所以我在项目里建了一个ItemStatusEnum把数字和中文描述映射起来public enum ItemStatusEnum { PENDING(0, 待审核), ON_SALE(1, 上架中), OFF_SHELF(2, 已下架), RENTED(3, 已租出); private final Integer code; private final String desc; ItemStatusEnum(Integer code, String desc) { this.code code; this.desc desc; } public static String getDescByCode(Integer code) { for (ItemStatusEnum itemStatusEnum : values()) { if (itemStatusEnum.getCode().equals(code)) { return itemStatusEnum.getDesc(); } } return 未知状态; } public Integer getCode() { return code; } public String getDesc() { return desc; } }后端返回给前端的数据里除了放状态值再附带一个状态描述字段前端直接渲染描述不用在页面上写一大堆 if 判断。这个习惯在文档、答辩演示的时候观感也特别好说明你有“数据字典”和“枚举语义化”的意识。3. 后端核心代码实现详解3.1 四层架构与项目目录组织Spring Boot 项目里常说的四层架构就是 Controller控制层、Service业务层、Mapper持久层、Entity实体层。有同学会把它和“架构设计”这种高大上的词挂钩其实它就是告诉你写代码要分层每层各司其职别把业务逻辑全堆在 Controller 里。我建的项目目录结构是这样的你可以直接照抄com.campus.rental ├── common // 公共类返回结果、常量、异常处理 │ ├── Result.java │ ├── ResultCode.java │ ├── BusinessException.java │ └── GlobalExceptionHandler.java ├── config // 配置类MyBatis-Plus分页、拦截器、跨域 ├── controller // 控制层接收请求、参数校验、返回结果 ├── entity // 实体层与数据库表对应的JavaBean ├── mapper // 持久层MyBatis-Plus的BaseMapper接口 ├── service // 业务层接口 实现类 │ ├── ItemService.java │ └── impl │ └── ItemServiceImpl.java └── utils // 工具类JWT工具、文件上传工具等这个结构有几个好处。第一嵌套层次少从入口到数据库一目了然刚接手项目的人十分钟就能看懂。第二每层之间单向依赖Controller依赖Service接口Service依赖Mapper接口实现类细节被藏在接口后面改实现不影响上层。第三答辩时老师问“你项目怎么分层的”你对着目录就能讲得头头是道每个层作用、请求怎么流转、每一层做什么校验这些全都是送分题。3.2 统一返回结果与全局异常处理我特别建议大家在一开始就把“统一返回结果”这件事做了因为我见过很多项目是每个接口自己返回 Map 或者直接返回实体类到前端取数据的时候五花八门后期维护想哭。我定义了一个ResultT泛型类结构就三个字段code状态码、message提示信息、data业务数据。所有接口不管成功失败都返回这个格式public class ResultT { private Integer code; private String message; private T data; public static T ResultT success() { return new Result(ResultCode.SUCCESS.getCode(), 操作成功, null); } public static T ResultT success(T data) { return new Result(ResultCode.SUCCESS.getCode(), 操作成功, data); } public static T ResultT error(String message) { return new Result(ResultCode.ERROR.getCode(), message, null); } public static T ResultT error(Integer code, String message) { return new Result(code, message, null); } }然后是全局异常处理。Spring Boot 提供了RestControllerAdvice注解可以拦截所有 Controller 抛出的异常并统一处理。我在这里面写了三个ExceptionHandler一个处理业务异常BusinessException一个处理参数校验异常MethodArgumentNotValidException一个兜底处理Exception。这么做的好处是Service 层发现业务不合法比如“物品已被租出”“订单状态不允许支付”时直接throw new BusinessException(物品已被租出)就行不用在每个接口里写 try-catch异常信息能统一格式、统一状态码返回给前端。3.3 用户认证与权限控制登录认证我推荐用 JWT也就是 JSON Web Token。它的工作流程是用户登录成功后后端生成一个包含用户 id、角色等信息的 Token 返回给前端前端在后续请求的 Header 里带上这个 Token后端通过拦截器解析 Token拿到当前登录用户的信息。用 JWT 而不是传统 Session 的核心原因是它是无状态的服务端不需要保存登录状态水平扩展的时候不需要考虑 Session 同步问题。虽然毕设项目一般没有高并发场景但“无状态认证”这个点在简历上和答辩里都是个可以展开讲的亮点。后端拦截器做权限控制核心代码是这样的public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录、注册等不需要认证的接口 if (request.getRequestURI().contains(/user/login) || request.getRequestURI().contains(/user/register)) { return true; } // 从Header中获取Token String token request.getHeader(Authorization); if (StringUtils.hasText(token)) { try { // 解析Token把用户信息放进request上下文 Claims claims JwtUtil.parseToken(token.replace(Bearer , )); request.setAttribute(userId, claims.get(userId)); request.setAttribute(userRole, claims.get(role)); return true; } catch (Exception e) { // Token无效或过期 } } response.setStatus(HttpStatus.UNAUTHORIZED.value()); return false; } }管理员功能的校验也在这里做判断当前用户角色是否为 1如果不是就直接返回“无权限访问”。我用的是拦截器 自定义注解RequireAdmin的组合在需要管理员权限的接口上打一个注解代码会更整洁。3.4 租赁订单业务逻辑的实现要点租赁订单是系统里最核心的业务我把下单流程完整写一遍。用户在前端选好物品、选定租赁起始日期点“立即租赁”后端做这几件事Override Transactional(rollbackFor Exception.class) public ResultString createOrder(RentalOrderCreateDTO dto, Long userId) { // 1. 校验物品存在且状态为上架中 Item item itemMapper.selectById(dto.getItemId()); if (item null || !ItemStatusEnum.ON_SALE.getCode().equals(item.getStatus())) { throw new BusinessException(物品不存在或不可租赁); } // 2. 校验租赁时间合法开始时间不能早于今天结束时间不能早于开始时间 if (dto.getStartDate().isBefore(LocalDate.now()) || dto.getEndDate().isBefore(dto.getStartDate())) { throw new BusinessException(租赁日期不合法); } // 3. 校验物品在所选时间段内没有被其他订单占用 long conflictCount rentalOrderMapper.selectCount(new QueryWrapperRentalOrder() .eq(item_id, dto.getItemId()) .in(status, Arrays.asList(0, 1, 2)) // 待支付、租赁中、待归还 .and(wrapper - wrapper .le(start_date, dto.getEndDate()) .ge(end_date, dto.getStartDate()))); if (conflictCount 0) { throw new BusinessException(该物品在所选时间段已被预订); } // 4. 计算租金按天计算 long days ChronoUnit.DAYS.between(dto.getStartDate(), dto.getEndDate()); BigDecimal totalAmount item.getPricePerDay() .multiply(BigDecimal.valueOf(days)); // 5. 生成订单并修改物品状态为已租出 RentalOrder order new RentalOrder(); order.setOrderNo(generateOrderNo()); order.setItemId(item.getId()); order.setLesseeId(userId); order.setLessorId(item.getUserId()); order.setStartDate(dto.getStartDate()); order.setEndDate(dto.getEndDate()); order.setTotalAmount(totalAmount); order.setDeposit(item.getDeposit()); order.setStatus(RentalOrderStatus.PENDING_PAYMENT.getCode()); rentalOrderMapper.insert(order); item.setStatus(ItemStatusEnum.RENTED.getCode()); itemMapper.updateById(item); return Result.success(order.getOrderNo()); }这里我用了Transactional事务注解因为生成订单和修改物品状态是两个数据库操作必须保证“同生共死”否则会出现订单生成了但物品状态没改的脏数据。时间冲突校验我用了查重逻辑凡是待支付、租赁中、待归还状态的订单只要它的租期区间和新的租期区间有重叠就算冲突。这个查询是租赁系统业务正确性的关键也是答辩时可以重点讲的一个点。3.5 文件上传与图片存储物品图片上传是必做的功能。spring-boot-starter-web已经封装好了文件上传能力我只要写一个配置设置单个文件大小限制和 Controller 接收MultipartFile就行。存储路径我建议这样规划在项目的resources/static/upload/目录下按“日期”建文件夹存放比如upload/2025-05-20/xxx.jpg。后端把文件保存后返回给前端一个可直接访问的图片 URL。这里有个容易踩的坑如果你用了自定义的WebMvcConfigurer拦截静态资源一定要把/upload/**这个路径放行否则图片加载不出来。我实际带项目时还遇到过中文文件名乱码、图片尺寸过大等问题处理方式是保存文件名一律用 UUID 重命名后缀保留原始格式这样既避免乱码也避免重名覆盖上传前判断文件大小和类型图片只允许 jpg、png、gif 这三种格式单个文件限制在 5MB 以内。4. 安装调试、打包部署与常见问题4.1 本地开发环境搭建我在指导项目的时候最怕听到的话是“我环境都配好了就是项目跑不起来”。环境问题看着琐碎但排查起来特别浪费时间。这里我把标准配置列出来照着做基本一次通过组件版本建议说明JDK1.8 或 11Spring Boot 2.x 都兼容JDK 8 最稳Maven3.6管理依赖IDEA 自带也可MySQL5.7 或 8.05.7 资料多8.0 性能好均可IDEA2022 及以上社区版即可不用破解Navicat 或 DataGrip任意数据库可视化工具非必须SQL 命令行也行JDK 安装有一个特别容易被坑的点环境变量JAVA_HOME的路径不能有中文和空格。我之前见过一位学生的电脑用户名是中文导致 Tomcat 启动时 JDK 路径解析失败报错报得莫名其妙最后只能换一个纯英文路径的 JDK 重装一遍才解决。如果你遇到这种问题别暴躁先检查路径。Maven 依赖下载慢的问题也很好解决在 Maven 的settings.xml里配置阿里云镜像仓库把mirror指向https://maven.aliyun.com/repository/public下载速度能快几十倍。这一步强烈建议提前配置否则初次拉 Spring Boot 全家桶依赖的时候等项目启动可能已经过完一个午休时间了。4.2 项目导入与启动过程IDEA 里导入项目的步骤比较固定File → New → Project from Existing Sources选择项目根的pom.xml等着 Maven 把依赖下载完千万别中途中断。依赖下载完成后项目的 JDK 设置也要确认一下打开 Project Structure 看 Project SDK 是否选到本机安装的 JDK。然后是配置文件application.yml数据库连接这里是最容易出问题的环节server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/campus_rental?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的数据库密码 servlet: multipart: max-file-size: 5MB max-request-size: 20MB 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: 0如果 MySQL 是 8.0 以上版本driver-class-name必须写com.mysql.cj.jdbc.Driver这是新版驱动的类名老版本项目里常用的com.mysql.jdbc.Driver在 8.0 环境下会直接报ClassNotFoundException。serverTimezoneAsia/Shanghai这个参数也得加上不然时间字段在存取的时候会差 8 个小时。MyBatis-Plus 里那个log-impl配置是日志输出启动后控制台能直接看到每条 SQL调试查数据的时候非常有用。我第一次跑一个列表查询怎么都查不出数据就是靠这行配置看了真实 SQL发现是条件拼接多了个空串判断一分钟就定位了。4.3 启动报错的排查思路Spring Boot 项目启动报错90% 集中在三种情况。我按出现的频率排序你排查的时候也按这个顺序来第一种端口被占用。报错信息里有Port 8080 was already in use。处理方式很简单找出占用进程杀掉或者把server.port改成一个不常用的端口比如 8088。第二种数据库连接失败。报错信息通常是Cannot create PoolableConnectionFactory或者各种Communications link failure。检查三件事MySQL 服务有没有启动、数据库名campus_rental存不存在、用户名密码对不对。最后这个看起来蠢但实际情况是很多人把密码写在配置文件里时带了多余的空格或者密码里有这种特殊符号没做处理。第三种依赖版本冲突。报错信息里一堆NoClassDefFoundError或者BeanDefinitionStoreException最常见的场景是 Spring Boot 版本和 MyBatis-Plus 版本不兼容。我这边验证过比较稳的搭配是 Spring Boot 2.7.x MyBatis-Plus 3.5.x你如果用了 Spring Boot 3.x那就要配套用 MyBatis-Plus 3.5.3 以上的版本因为 Spring Boot 3 是基于 Jakarta EE 的很多包名都变了。4.4 打包与部署经验毕设项目一般有两种交付要求一种是只要求能在 IDEA 里跑起来另一种要求能生产一份可执行的 jar 包部署到服务器上。如果要求打包部署那就用 Maven 的 package 命令。打开 IDEA 右侧 Maven 面板双击package看到BUILD SUCCESS后target目录下就会生成一个可执行的 jar 包。命令行运行java -jar campus-rental-0.0.1-SNAPSHOT.jar只要本机能访问到 MySQL项目就能跑起来。这里有三个坑我替你踩过了第一打包前检查pom.xml里有没有加spring-boot-maven-plugin插件不加这个插件打出的 jar 包不能直接运行第二如果用的是前后端分离项目前端资源要先npm run build生成 dist 目录再把静态文件拷贝到后端resources/static下一起打包不然部署到服务器上页面是空的第三服务器上的 MySQL 数据库要提前把建表 SQL 执行一遍千万别指望 jar 包能自动建表项目里没有接 Flyway 之类的工具的话表不存在就是启动即报错。4.5 常见问题速查表我把这些年经常遇到的问题整理成一张速查表你可以直接收藏遇到问题先来这里比对一下排查场景典型报错或现象处理方式启动时端口被占Port 8080 was already in use改server.port或杀掉占用进程MySQL 连接失败Cannot create PoolableConnectionFactory检查 MySQL 服务、库名、账号密码驱动类找不到ClassNotFoundException: com.mysql.jdbc.DriverMySQL 8.0 改为com.mysql.cj.jdbc.Driver中文乱码数据库数据乱码/页面乱码统一 utf8mb4 字符集连接串加characterEncodingutf8图片上传后无法访问图片 404静态资源拦截配置放行/upload/**路径查询结果缺失字段数据库有数据但查出来是 null加map-underscore-to-camel-case: true开启驼峰映射打包后无法运行没有主清单属性pom.xml添加spring-boot-maven-plugin跨域请求被拦截Access to XMLHttpRequest has been blocked by CORS配置CorsFilter或CrossOrigin注解5. 代码讲解与文档报告的组织思路5.1 答辩时代码怎么讲这个项目的代码量在一万行左右答辩时不可能每一行都讲。老师的核心想知道的是这系统是不是你自己做的、你清不清楚每个模块的原理、你遇到问题有没有排查能力。所以我的建议是挑三条主线讲透比面面俱到强得多。第一条主线是请求流转路径。你拿起任意一个完整功能比如“用户浏览物品列表”从浏览器发起请求开始到 Controller 接收参数、Service 处理业务、Mapper 查询数据库、结果组装返回一条线走下来。讲的时候突出“分层设计”的思路各层之间职责是怎么区分的为什么这么分。第二条主线是权限控制。先演示普通用户访问管理员接口被拦截的效果再切到管理员账号访问同样的接口成功。讲的时候重点说 JWT 的构成、拦截器的工作原理、Token 的校验流程。这条线技术含量高老师基本都会往这里面问。第三条主线是租赁订单状态流转。拿一个真实订单从创建到完成的完整生命周期来说配合数据库里的状态字段变化截图把每一步业务规则讲清楚。这条线体现的是你对业务场景的理解深度非常加分。带上 id、角色等信息的 Token 返回给前端前端在后续请求的 Header 里带上这个 Token后端通过拦截器解析 Token拿到当前登录用户的信息。5.2 毕业设计论文的结构安排论文的框架大体上是固定的学校一般会给模板但在内容填充上我有些心得可以分享。论文的章节通常这样分布第一章绪论写研究背景和意义、国内外研究现状。写现状的时候不要空泛地抄别人的话可以具体写“目前校园内的闲置物品处理多依赖 QQ 群、跳蚤市场信息分散且缺乏交易担保”这样更有真实感第二章相关技术介绍写 Spring Boot、MyBatis-Plus、MySQL、JWT 这些你用到的技术。这里要注意每个技术写清楚“它是什么、为什么适合本项目”不要大段复制官方文档查重过不了第三章需求分析先画功能用例图再分角色说明功能需求最后补充非功能需求性能、安全、易用性第四章系统设计数据库设计主要表和字段说明附上 E-R 图、系统架构设计分层结构图、核心流程设计租赁流程图、权限验证流程图第五章系统实现按功能模块逐个展示页面截图和核心代码片段并配简要说明。页面截图配核心代码这个组合是论文里最占篇幅也最直观的内容第六章系统测试写功能测试用例表、测试结果、性能测试简单数据第七章总结与展望总结系统完成的功能再提几点未来的改进方向比如接入真实支付、增加信用评价体系、基于推荐算法的物品推荐等E-R 图我建议用 Draw.io 画免费而且画出来比较干净。画的时候不需要把字段全部列出来只要画出主要实体、关键属性、实体间的关联关系一对一、一对多、多对多就够了。流程图也可以用 Draw.io状态机流转图等都可以画答辩时放 PPT 里非常直观。5.3 项目如何做二次亮点扩展如果时间允许我强烈建议在基础上加一两个加分功能。这样在答辩时你可以多一个亮点也能避开“功能太简单”的评价。我推荐几个工作量和亮点都合适的扩展方向一个是数据分析可视化。管理员后台引入 ECharts做一个简单的数据统计页面按分类统计物品数量、按月份统计订单量趋势、展示热租物品 Top5。数据来源就是 order 表和时间字段一个聚合查询就能拿到但呈现效果非常直观展示“数据分析能力”比很多花哨功能都管用。另一个是消息通知。当物品被下单、订单状态变更时给相关用户站内信通知或者邮件通知。用 Spring Boot 的异步事件Async 邮件服务就能实现代码量不大但体现的是“用户体验”和“架构设计”的考量。还有一个是操作日志。用 AOP 切面记录用户的关键操作登录、下单、发布物品等到一张日志表管理员可以查看。这个功能体现了你对“系统安全”的理解技术上用到 Spring AOP也很有讲头。6. 写在最后几点掏心窝的建议带过这么多毕设项目我最想跟大家说的一句话是毕设项目的核心目标不是“做出一个多完美的商业产品”而是“把学校里学到的知识和技能完整体验地串起来”。所以真正重要的是投入的过程是你在动手过程中踩过的坑、翻过的源码、查过的资料这些才是答辩时你能脱口而出的底气。具体到时间规划上我建议不要把所有事情拖到最后一个月。合理的节奏是第一周完成需求分析和数据库设计第二周到第三周完成后端主要功能第四周做前端页面和联调第五周整理测试用例并开始写论文最后一周准备答辩 PPT 和演示环境。当然这个计划会按实际进度有出入但大致框架不要偏。另外在演示之前一定要留出时间做一次完整预演。把项目从零启动跑一遍测试数据准备充分页面切换的每个按钮都点一遍。我遇到过太多次演示环节翻车的情况——要么数据库没启动、要么演示账号密码忘了、要么图片加载不出来。提前演练一遍这些问题都是可以提前杜绝的。如果你在这个项目的实现过程中遇到具体的技术卡点欢迎在评论区把报错信息发出来我看到后会整理成问答形式更新在后续的文章里。