尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SpringBoot+Vue毕设经典:大学生在线租房平台全流程实现指南

SpringBoot+Vue毕设经典:大学生在线租房平台全流程实现指南 每年三四月份总有一大批人被毕设选题卡住。如果你既想用 SpringBoot 写后端又想用 Vue 做前端还想找一个业务模型不复杂、但又能把两套技术栈都串起来的题目大学生在线租房平台绝对是最稳的选择之一。这个项目我前后带过几届学生做也在自己的开源项目里复现过一轮里面哪些地方容易翻车、哪些功能纯粹是给自己挖坑、论文和代码怎么对应着写我心里基本都有数。这篇文章不聊虚的就把从选题、建表、写接口、写前端、联调、写论文到答辩全流程里值得注意的地方一次性讲清楚。适合正在做 SpringBoot Vue 项目尤其是拿这个题目做毕业论文的同学参考。不少人以为这种“管理系统类”题目已经烂大街了但烂大街恰恰说明它的合理性技术覆盖面足够、业务需求大家都熟悉、数据关系清晰、论文好写而且只要在某个点上做得比默认版本深一点比如房源检索更细致、订单状态流转更完整、权限控制更严谨答辩时就有话可说。下面按我实际带项目的顺序来拆。1. 为什么“大学生在线租房平台”每年都是毕设爆款选题逻辑与需求收敛1.1 这个题目经久不衰的核心原因先说实话毕设选题最重要的不是“新”而是“你能完整闭环”。在线租房平台天然包含用户注册登录、角色区分、房源发布、检索筛选、收藏、预约、订单、合同状态流转等业务节点任何一个节点都能对应一组后端接口和前端页面整套系统从零到能演示的状态是可以通过两三个月时间独立完成的。另一个原因是技术栈契合度极高。SpringBoot 负责提供 RESTful APIVue 负责 SPA 页面交互两者之间的数据通信、跨域处理、身份认证、文件上传恰好就是 Java 后端和前端开发里最核心、最常被面试官考察的技能点。做完这个项目你等于把“单体应用前后端分离开发”这条主流路线完整跑了一遍写简历、准备面试都有实际素材。不过我也提醒一句正因为选它的人多如果你想拿高分千万不要只做一个“增删改查”的空壳。哪怕你只是在需求分析里把“租客求租”和“房东发房”两条主线理清楚把订单状态从简单的“已下单”扩展成“待看房—已看房—已签约—已退租”演示效果都会比默认版本强一大截。1.2 需求梳理三种角色、两大主流程和一条辅助线做需求分析时哪怕你是自己给自己定需求也要按正规项目的方式把角色和用例列一遍。这个题目的角色基本是三类管理员审核房东发布的房源、管理用户状态、处理举报、查看平台运营数据。房东发布房源、维护房源信息、处理租客的预约看房请求、确认签约、管理合同终止。租客学生注册登录、浏览/搜索房源、收藏房源、预约看房、申请租房、查看合同。两大主流程是“租客找房流程”和“房东发房流程”辅助线是管理员审核与用户管理。毕设答辩时老师很喜欢问“你的系统有哪些角色每个角色的核心用例是什么”如果你能当场把这条主线和用例说清楚比磕磕绊绊讲代码强一百倍。1.3 需求收敛有些功能看起来高级实际上会拖垮你我见过很多学生一上来就想做即时聊天、在线支付、地图找房最后统统卡死。这里我直接给一个收敛建议聊天功能能不做就不做或者用“留言 联系方式展示”代替。WebSocket 一旦涉及会话管理、离线消息、消息状态工作量会成倍增长。支付功能不要碰。大学生租房平台的毕设版本完全可以设计成“线下签约缴费”订单状态里留一个“已签约”节点即可。真要接支付宝/微信支付还得申请商户资质、处理回调这在毕设时间线里非常危险。地图找房可以用静态地图定位代替或者在房源详情里放一个经纬度字段通过一个静态地图图片展示。Vue 里集成地图 SDK 并不难但 Key 申请、样式适配、坐标转换都是额外麻烦属于典型的性价比低。需求收敛的目标是让主体业务闭环、演示流畅。你可以在《论文》的“未来展望”里写“后续可接入在线支付与地图找房”这样既不影响现有实现又显得有想法。2. 后端骨架搭建顺序先设计表再谈 SpringBoot 分层2.1 数据库先行六张核心表的字段设计思路我可以很肯定地说凡是后期返工严重的毕设八成是表设计阶段偷了懒。租房平台核心业务表就六张左右先把表定了后面的 Controller 和 Vue 页面都是按表走的。users 表用户表CREATE TABLE users ( id bigint NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 登录名, password varchar(100) NOT NULL COMMENT BCrypt加密后的密码, nickname varchar(50) DEFAULT NULL, phone varchar(20) DEFAULT NULL, role tinyint NOT NULL DEFAULT 1 COMMENT 1租客 2房东 3管理员, status tinyint NOT NULL DEFAULT 1 COMMENT 1正常 0禁用, create_time datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;house 表房源表CREATE TABLE house ( id bigint NOT NULL AUTO_INCREMENT, landlord_id bigint NOT NULL COMMENT 房东用户id, title varchar(100) NOT NULL, area varchar(50) DEFAULT NULL COMMENT 所在区域, address varchar(200) DEFAULT NULL, price decimal(10,2) NOT NULL COMMENT 月租金, house_type varchar(20) DEFAULT NULL COMMENT 户型如1室1厅, description text, status tinyint NOT NULL DEFAULT 0 COMMENT 0待审核 1上架 2下架, create_time datetime DEFAULT NULL, PRIMARY KEY (id), KEY idx_landlord (landlord_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;剩下的 house_image房源图片、favorite收藏、rent_order租房订单、message预约/留言按同样的思路设计。几个容易忽略的点外键要不要建物理外键毕设阶段我建议不加物理外键约束用逻辑外键加索引代替。原因很简单测试时删除数据、批量导入、后台上架下架操作物理外键会带来一堆约束麻烦。但表注释里要写清楚哪个字段是外键论文里还是按“实体关系”去描述。所有表都带 status 和 create_time。status 是做上下架、审核、订单状态流转的必要字段create_time 是所有列表页排序的依据。金额字段用 decimal 而不是 double这个坑不用我多说凡是涉及“钱”的字段浮点数一定出问题。2.2 SpringBoot 分层Controller、Service、Mapper 的职责边界项目结构我会直接按标准方式拆com.example.rental ├── controller ├── service │ └── impl ├── mapper ├── entity ├── dto ├── vo ├── config └── commonController 里只做三件事接收参数、调用 Service、包装返回值。不要把查询逻辑写在 Controller 里。我见过有学生把 SQL 拼接、状态判断全部堆在 Controller最后论文里的“详细设计”部分根本没法画时序图。举一个处理请求的例子RestController RequestMapping(/api/house) public class HouseController { Resource private HouseService houseService; GetMapping(/list) public Result? list( RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, HouseQueryDTO query) { return Result.success(houseService.pageQuery(pageNum, pageSize, query)); } }返回体统一用 Result 包装里面放 code、message、data 三个字段。前端 axios 拦截器统一判断 code这样所有接口的错误处理都只有一套逻辑不会一个接口一种返回格式。Service 接口建议单独抽一层而不是直接写实现类。理由可能听起来有点虚但实际上写论文时“模块接口设计”这一节可以直接引用 Service 接口的方法签名答辩时被问到“你的模块之间怎么解耦”也有话说。Mapper 层用 MyBatis-Plus 就够了常规增删改查加一个 LambdaQueryWrapper写条件查询非常快避免自己去拼一堆 XML。2.3 登录认证与角色权限的落地细节租房平台必须区分角色权限否则房东能改别人的房源、租客能下架房源整个系统就是纸糊的。我这里用的是 JWT 拦截器 ThreadLocal 的组合方案。JWT 的好处是状态无关后端不用存 Session前后端分离部署时天然友好。核心逻辑是登录成功后生成 tokentoken 里放 userId 和 role后端拦截器解析 token把用户信息放进 ThreadLocalController 里通过 UserContext 取当前登录用户。密码存储必须用 BCrypt。Spring Security 里虽然有 BCryptPasswordEncoder但如果你不想引入整套 Security单独引入 spring-security-crypto 依赖就够了。明文密码存储这种问题一旦被答辩老师发现印象分会掉一大截。权限控制不能少的一段代码思路public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 1. 从请求头取 token // 2. 解析失败返回 401 // 3. 解析成功把 userId、role 存入 UserContext // 4. 校验该接口需要的角色是否匹配不匹配返回 403 } }需要注意的是“越权访问”比如修改房源接口不能只校验“你是不是登录用户”还要校验“你是不是这个房源的房东”。很多学生只做到了第一层结果通过修改 URL 里的 id 就能改别人的数据。这个细节我在后面联防、答辩章节还会提。3. Vue 前端的三个关键决策路由、状态与列表性能3.1 为什么推荐 Vue3 Vite以及环境配置里最容易被绊倒的地方如果你现在刚起步我建议直接用 Vue3 Vite而不是 Vue2 Webpack。Vite 的冷启动速度比 Webpack 快太多开发体验完全不在一个量级。而且 Vue3 的组合式 API 在写列表查询、弹窗交互时比 Options API 更顺手。但 Vite 有几个环境坑很典型。一个是 Node 版本。Vite 4 以上要求 Node.js 18如果你机器上还是 Node 14/16跑npm create vitelatest就会报错或者起服务后热更新异常。建议先执行node -v确认版本不行就去官网装一个 LTS 版本环境问题不值得浪费时间。另一个是依赖安装速度。npm 在国内环境下经常卡在idealTree:npm: sill idealTree buildDeps用淘宝镜像能省很多时间npm config set registry https://registry.npmmirror.com还有一个是组件库选择。毕设项目我不建议从零手写所有 UI 组件直接用 Element Plus 就好表格、表单、分页、弹窗、消息提示都是现成的配上 Vite 按需自动导入开发效率提升非常明显。官方文档里的unplugin-vue-components和unplugin-auto-import两个插件配置一下就能自动按需引入不用在 main.js 里全量注册。3.2 路由守卫页面不是“能跳”就够了还要防住没权限的人前端路由一般包含登录页、注册页、首页房源列表、房源详情、个人中心、房东后台、管理员后台。你需要在路由配置里提前给每个页面打上 meta 标记{ path: /landlord/house-list, component: LandlordHouseList, meta: { role: landlord } }然后在router.beforeEach里做三步判断没有 token 且去的是需要登录的页面强制跳到登录页。有 token 但 token 已过期调一次刷新或直接踢回登录页。有 token 但页面要求角色和当前用户角色不一致跳 404 或首页。这里必须强调前端路由守卫只是用户体验层面的控制不是安全边界。因为所有接口数据还是从后端拿的真正防止越权必须靠后端接口的角色校验。你能看到隐藏的“删除按钮”不算大问题但如果直接请求接口能删掉别人的数据就是架构缺陷。状态管理方面项目规模不大用 Pinia 存一份用户信息就够了。不要把接口返回的所有数据都塞进 store那样既难维护刷新页面还会丢。普通列表数据放在组件里的 ref/reactive 即可store 只存 token 和当前用户对象。3.3 房源列表页的性能与交互分页、筛选、图片处理房源列表是整个系统最核心的页面每天演示、答辩都是从这里开始的。不要用“一次性查出所有数据然后前端分页”的方式数据量稍微一大页面就卡而且答辩老师很可能会问“如果房源有一万条你的方案还可行吗”。正确做法是后端用 MyBatis-Plus 的 Page 分页前端传 pageNum、pageSize 和筛选条件。筛选条件我给一个实用组合区域、户型、价格区间、关键词搜索。后端对应的 Service 实现大致是public PageResultHouseVO pageQuery(Integer pageNum, Integer pageSize, HouseQueryDTO query) { LambdaQueryWrapperHouse wrapper new LambdaQueryWrapper(); wrapper.eq(House::getStatus, 1); wrapper.like(StringUtils.hasText(query.getArea()), House::getArea, query.getArea()); wrapper.like(StringUtils.hasText(query.getHouseType()), House::getHouseType, query.getHouseType()); wrapper.ge(query.getMinPrice() ! null, House::getPrice, query.getMinPrice()); wrapper.le(query.getMaxPrice() ! null, House::getPrice, query.getMaxPrice()); wrapper.orderByDesc(House::getCreateTime); PageHouse page houseMapper.selectPage(new Page(pageNum, pageSize), wrapper); // 再补充封面图、房东信息 return convertToPageResult(page); }房源卡片图片处理也要注意两点。第一是图片懒加载Element Plus 的 el-image 有懒加载属性列表一屏只加载可见图片滚动到哪加载到哪体验差别很明显。第二是图片尺寸上传时后端最好压缩或限制大小卡片展示用小图详情页再展示原图。搜索框建议做防抖处理用户输入 300 毫秒后再请求接口。不然每敲一个字就发一次请求后端日志会被刷屏网络面板看起来也不专业。4. 前后端联调排坑实录跨域、日期精度、图片与打包部署4.1 跨域问题开发环境用代理生产环境最好同源前后端分离项目第一次联调十有八九会遇到跨域。控制台报Access-Control-Allow-Origin相关错误不要太慌原因很简单浏览器出于安全策略不允许前端页面去请求不同源端口不同也算的接口。解决办法有两个主流方向。开发环境最推荐用 Vite 代理配置在vite.config.jsserver: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }这样前端请求/api/house/list时Vite 开发服务器会帮它转发到http://localhost:8080浏览器感知不到跨域。生产环境我建议把 Vue 打包后的 dist 文件直接放进 SpringBoot 的resources/static目录里前后端同源部署根本不存在跨域问题。这种方式对毕设最友好——你不用另外配置 Nginx也少了一套环境变量要解释。如果非要使用跨域方式部署后端可以写一个 CORS 配置类Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE) .allowCredentials(true) .maxAge(3600); } }但注意allowCredentials(true)时 allowedOrigins 不能用*必须用allowedOriginPatterns(*)很多人卡在这里。4.2 两个让人抓狂的序列化问题日期变成数组、Long 精度丢失第一个问题是 LocalDateTime 默认序列化格式。如果你环境里没做任何配置接口返回的日期是2025-06-01T10:20:30或者干脆是[2025, 6, 1, 10, 20, 30]这种数组结构前端怎么解析都别扭。在 application.yml 里加一段配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8如果实体里字段是 LocalDateTime还需要在字段上加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)或在配置里注册 JavaTimeModule 的序列化器。不然你会发现 date-format 对 LocalDateTime 不生效java.util.Date 倒是正常。第二个问题是 Long 类型 id 传到前端丢失精度。如果主键是雪花 ID 或超过 2^53 的整数JSON 序列化后 JavaScript 解析会直接丢最后几位最终导致“编辑时 id 不对”“删除删到别的记录”这种诡异现象。解决办法有两种要么把主键改成数据库自增不太推荐但毕设里问题不大要么给 Long 字段配置转 String 序列化JsonSerialize(using ToStringSerializer.class) private Long id;我建议所有实体主键都加这个注解一劳永逸。4.3 图片上传保存路径、静态资源映射、文件重名房源图片上传是房东端必备功能。后端接收MultipartFile后不要直接把文件字节写进数据库BLOB 方案在毕设里可以运行但性能很差论文里也不好解释而是保存到服务器本地目录数据库只存文件路径。保存时的核心问题是重名。两个用户上传同样叫a.jpg的文件直接按原名保存会覆盖。用 UUID 重命名文件名是最省事的方案String originalFilename file.getOriginalFilename(); String ext originalFilename.substring(originalFilename.lastIndexOf(.)); String newFilename UUID.randomUUID().toString().replace(-, ) ext;然后用配置类把上传目录映射成静态资源 URLOverride public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file: uploadDir /); }这样前端就能通过http://localhost:8080/upload/xxx.jpg直接访问图片。注意 Windows 和 Linux 下路径分隔符不一样最好用配置文件统一维护上传路径不要硬编码。4.4 Vue 打包后打进 SpringBoot最省事也最容易被问到的部署方式毕设最常见的部署演示方式就是把前端 build 产物放进后端项目里一个 jar 包跑起整个系统。步骤很简单npm run build然后把生成的dist目录里的所有文件复制到 SpringBoot 的src/main/resources/static目录下。启动 SpringBoot 后访问http://localhost:8080/就是前端页面接口也是同一个域名。但这里有个大坑如果你前端路由用的是createWebHistory()直接访问http://localhost:8080/landlord/list时刷新页面会 404。因为 SpringBoot 对/landlord/list没有对应的 Controller 处理。破局办法有两个一是干脆使用createWebHashHistory()路由路径变成http://localhost:8080/#/landlord/list刷新时 hash 部分不会发给服务器天然不会 404。这种方式在毕设里完全够用也省很多解释成本。二是保持 history 路由后端加一个转发规则Controller public class PageForwardController { RequestMapping(value {/, /{path:[^\\.]*}, /{path:^(?!api|upload).*$}/**}) public String forward() { return forward:/index.html; } }我个人建议首次做毕设直接用 hash 模式把精力留给业务功能。答辩时如果老师问“为什么不用 history 模式”你可以如实说“为了兼容直接刷新场景hash 模式更简单可靠”这是一个站的住的工程决策不是技术缺陷。5. 论文写作与查重策略让代码变成能答辩的文字5.1 论文核心章节与项目内容的对应关系项目做完了论文才是把工作量可视化呈现的关键。本科论文一般包含摘要、绪论、需求分析、系统设计、详细设计与实现、系统测试、总结。这里我重点说容易空泛的几章怎么对应真实项目。需求分析章节不要只贴功能列表要画用例图整理用例描述表。比如“租客预约看房”用例可以写成前置条件已登录、房源已上架、主流程选择房源→提交预约→房东确认、异常流程预约时间冲突、房源已下架。这样写的好处是详细设计阶段可以直接按用例去对应接口。系统设计章节至少包含三块总体架构前后端分离部署图、功能模块划分、数据库设计。数据库设计是最好写工作量的一章把每张表的字段、类型、注释列成表格再加 ER 图内容一下就充实起来了。注意 ER 图用工具导出来不要手画歪歪扭扭的线条。详细设计与实现章节很简单每个核心功能模块先用文字描述业务逻辑再贴核心代码片段再放接口调用说明。不要贴大段完整代码那只会拉低观感。我建议每个模块挑 20-30 行“能讲出设计思想”的代码比如分页查询代码、JWT 拦截器代码围绕代码解释“为什么这么做”就足够了。5.2 测试用例怎么设计才不像凑字数系统测试永远是论文里水字数最多的地方。但老师见过太多“输入正确的数据系统正常显示”这种废话用例。比较好的写法是设计有业务含义的测试场景未登录用户访问需要认证的接口返回 401前端跳转登录页。租客尝试修改非本人房源信息的房源返回 403。管理员审核下架房源后租客端列表不再显示该房源。搜索条件不输入关键词时默认按时间倒序返回所有上架房源。上传超过大小限制的图片时后端给出明确提示。把这些用例整理成表格包含用例编号、测试项、前置条件、输入、预期结果、实际结果。能截几个接口测试工具的请求响应图更好。5.3 首轮查重前要改掉的三类写法我见过很多学生辛辛苦苦把系统做完论文查重却吃了亏。原因基本都是这三类写法第一类是需求分析里“系统研究与意义”写得太宏观整段全是“随着互联网的发展”之类的套话。这种东西查重率奇高而且老师一眼就看出来是拼凑的。建议直接砍掉换成“当前大学生租房痛点”的具体描述比如“信息不对称、房源真实性难验证、租住流程不规范”每个痛点对应你系统里的一个功能。第二类是技术介绍章节大段搬运 SpringBoot 和 Vue 官方文档的简介。框架介绍点到为止能说出“SpringBoot 提供自动装配能力简化配置Vue 通过组件化开发提升页面复用性”就行重点放在“你的项目是怎么用它们的”。第三类是代码注释直接抄别人的。查重系统确实会比对代码和分析文本我在审查项目时还发现有些学生的注释和网上开源项目一字不差。注释就用大白话写讲清楚参数和业务含义就好不要复制粘贴。6. 答辩现场的高频追问与回答思路这个项目答辩时被问最多的问题我在此做个汇总。问来问去基本都是下面这些。第一个是“你的系统怎么保证数据安全”千万别只答“密码加密了”。更完整的回答分三层传输层用 HTTPS演示环境可以不做但要提存储层密码 BCrypt接口层做了 JWT 过期校验与基于角色的权限拦截。能再补一句“越权操作在后端做了资源归属校验”就已经超过大部分人了。第二个是“如果用户量变大你的系统哪里会成为瓶颈”这是把“分页查询”反着问。你可以说目前列表接口是普通 MySQL 分页用户量大后 CPU 和 IO 压力会集中在数据库查询。可优化方向有三对高频筛选字段建索引、热门房源加 Redis 缓存、复杂搜索切换 Elasticsearch。答出来这三点老师基本就不会继续追问缓存击穿之类更深的细节了。第三个是“JWT 和 Session 有什么区别为什么选 JWT”最稳的回答是Session 需要服务端保存状态不利于横向扩展JWT 是无状态的token 自包含用户身份和过期时间后端不用存储会话适合前后端分离部署。但你也得知道 JWT 的短板比如注销不彻底、token 泄露后有效期内无法主动失效最好能说出“系统的短期 token 过期时间较短并且前端会在退出登录时清掉本地 token”这种缓解措施。第四个是“你论文里的创新点是什么”不要硬说“推荐算法”“人工智能”。对这个项目来说更诚实的创新点可以是房源审核机制与上下架状态机的设计、多条件组合查询与分页的接口抽象、基于枚举的状态流转校验。把这些讲透比乱扯大词更让人信服。最后再多说一句实操上的体会整个项目最容易拖时间的地方不是代码本身而是前端依赖安装和环境兼容性。如果学生在配置 Node、Vite、SpringBoot 版本这一步卡太久后面论文和答辩准备都会被压缩。建议拿到题目后第一天先跑通一个 hello world 级别的前后端联调再开始写业务代码。底子打好了后面基本就是照着表结构“填”功能的事。
返回列表