资讯详情

Spring Boot毕设之在线房屋出租系统:核心设计、实现与避坑指南

📅 2026/10/13 19:56:16 | 华诺云谱 👁 阅读
Spring Boot毕设之在线房屋出租系统:核心设计、实现与避坑指南
每年一到毕业季我就会被同一批题目包围校园二手交易、图书管理系统、在线点餐还有这个“基于Spring Boot的在线房屋出租系统”。说句实在话这类题目在计算机类毕设里已经属于“经典款”学校爱出学生爱查但真能把它讲明白、改顺手、扛得住答辩追问的并不算多。标题里的Spring Boot、房屋出租、毕设源码这些关键词拼出来的其实是一个非常典型的信息管理交易流程类平台房东发房源租客找房源平台做审核和订单管理。我写这篇内容的原因很简单。网上去搜这个题目能捞出一大把源码和文档但大部分资源都是“能跑就行”很少有人把里面的权限设计、状态流转、数据库取舍讲清楚而这恰恰是答辩时老师最喜欢问的地方。这篇文章我就按这些年带毕设的经验从需求拆解到落地实现把每个关键点都过一遍再穿插几个我在实际调试里容易翻车的坑。无论你是打算直接拿这套系统交差还是想在此基础上加点自己的东西这篇内容应该都能帮你少走不少弯路。1. 项目概览与设计思路拆解1.1 透过标题看清这个项目的真实需求很多人看到“在线房屋出租系统”第一反应是这不就是个发房源、看房源的网站吗如果把需求理解到这个程度项目做出来也就值一个及格分。真正拖垮这个题材的反而不是技术难点而是逻辑上的“流程缺失”。你要先理清这个系统里到底有哪些角色在干活。我一般会画一张三角关系图平台管理员管审核和全局数据房东负责发布房源和处理预约租客负责搜索、预约、签约。如果还有普通游客角色那游客只能看列表和详情注册登录之后才能做进一步操作。对应的功能需求大概有这么几块用户体系注册、登录、个人信息维护、密码修改房源模块发布、编辑、上下架、审核状态管理展示与检索列表、详情、关键字搜索、区域筛选、收藏预约流程发起预约、房东确认、租客取消订单模块生成租赁订单、跟进状态、到期退租后台管理用户管理、房源审核、公告发布、数据统计这些需求之间有强依赖关系。比如房源状态必须是“已上架”才能被搜索到被预约和签约之后又不能继续被其他人下单。你要是把房源状态设计成一个随便改的字符串字段后面写订单逻辑的时候必然会乱成一锅粥。1.2 为什么这种毕设项目选Spring Boot最“稳”现在很多高校指定Spring Boot作为毕设框架不是因为它是所有方案里性能最强的而是因为它恰好卡在“能学到东西”和“短期能做完”的中间地带。从开发效率角度看Spring Boot的自动配置把以前SSH时代那堆XML配置全砍掉了。以前配一个数据源要写半天现在application.yml里几行搞定。内嵌Tomcat让项目打成一个jar包就能跑省掉了单独装Tomcat、部署war包的步骤这对毕设阶段来说太友好了。从答辩角度看Spring Boot生态里的组件名称评委基本都听过MyBatis-Plus操作数据库、Redis做缓存、JWT做认证、Vue做前端页面。你只要能把每个组件解决什么问题说清楚不用深入源码也能把答辩应付过去。有一点要提醒你别为了赶新潮直接上Spring Boot 3.x加JDK 17。虽然新版本确实更现代但不少老电脑里装的是JDK 8一些第三方依赖对新版本的支持也有兼容成本。如果学校没有强制要求用Spring Boot 2.7.x加JDK 8是最稳的组合。做毕设的第一原则永远是“稳定跑通”而不是“版本最新”。1.3 功能模块怎么划分才不乱很多同学的毕设代码拿到手之后第一反应是想去改页面但一打开项目就懵了。原因就是一个字乱。UserController里堆了几百行订单逻辑塞在里面房源管理的代码也往里面挤。问题就出在模块划分没有按“角色”和“业务域”切开。我比较推荐的做法是后端按照角色和业务域拆出四条主线平台端接口用户管理、房源审核、公告、统计房东端接口房源发布、编辑、预约处理、订单查看租客端接口房源浏览、收藏、预约、下单公共接口登录注册、上传、字典查询对应到代码结构上controller、service、mapper都是成组成组出现的而不是一个UserController包打天下。你改房源逻辑就在HouseController和HouseService里改改订单逻辑就在OrderController和OrderService里改。这样不仅读起来舒服更重要的是答辩时老师问“订单模块在哪”你能十秒钟内指给他看。2. 技术选型与核心配置细节2.1 一套能顺利跑起来的技术栈清单网上同名项目成千上万技术栈细节其实差异很大。我这套清单是基于最主流、最容易排查问题的组合整理的你可参考着对照自己的项目层次技术选型说明后端框架Spring Boot 2.7.x稳定JDK 8可跑ORMMyBatis-Plus 3.5.x单表CRUD几乎不用写SQL数据库MySQL 8.0免费、文档多、适合毕设缓存Redis可选验证码、token可选方案安全认证JWT 拦截器比Spring Security轻量好讲工具库Lombok、Hutool简化代码处理日期和文件前端Vue 2 Element-UI后台管理系统最常用的组合构建工具Maven 3.6毕设项目的事实标准要是你的项目里已经有Spring Security那也别强行去掉只要能说清楚认证鉴权流程就行。但如果你是在网上找的源码恰好用的是JWT加拦截器方案我会更建议保留这个因为拦截器的代码逻辑直观答辩时三五句话就能讲完不用背一堆过滤器链配置。2.2 数据库表设计的几个关键决策房屋出租系统的数据库是整个项目的灵魂表结构设计不合理后面每个功能都做得难受。下面这五张核心表基本是标配用户表、房源表、房源图片表、预约表、订单表另外可以再加一张公告表和管理员表。先说最重要的房源表。我见过很多失败的作业问题都出在字段设计上CREATE TABLE house ( id bigint NOT NULL AUTO_INCREMENT, title varchar(120) NOT NULL COMMENT 房源标题, cover_url varchar(255) DEFAULT NULL COMMENT 封面图, lease_type tinyint NOT NULL COMMENT 1整租 2合租 3短租, price decimal(10,2) NOT NULL COMMENT 月租金, area decimal(8,2) DEFAULT NULL COMMENT 面积, layout varchar(50) DEFAULT NULL COMMENT 户型描述, province varchar(50) DEFAULT NULL, city varchar(50) DEFAULT NULL, district varchar(50) DEFAULT NULL, address_detail varchar(255) DEFAULT NULL, landlord_id bigint NOT NULL, status tinyint NOT NULL DEFAULT 0 COMMENT 0待审核 1上架 2已出租 3下架 4审核不通过, description text, view_count int DEFAULT 0, del_flag tinyint DEFAULT 0, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有几个决策要展开讲一讲。金额字段必须用decimal(10,2)不能用float或double。因为钱涉及精度float算出来的结果经常有误差租客看到的订单金额少一分钱都会出问题。地址要拆成省份、城市、区县、详细地址四个字段而不是一个长长的address。因为搜索功能通常要按城市和区县筛选拆开之后直接用等值查询就行。状态字段用int做常量映射在Java代码里定义枚举或常量类不要直接在service里写魔法数字1、2、3。不然过两周自己回来看代码都不知道2代表什么。房源图片单独建一张house_image表不塞进房源表里的一个字段。一个房源要支持多图轮播单独建表才方便而且封面大图和详情小图可以复用同一张表。预约表和订单表同样要注意状态字段。预约状态0待处理、1房东确认、2租客已看房、3已签约、4已取消。订单状态0待支付、1生效中、2已退租、3已取消。这些状态流转是有顺序的你不能让一个“已取消”的预约直接跳到“已签约”。2.3 application.yml 里最容易翻车的几个配置很多项目拿到手后第一步启动就挂在配置文件上。不要小看这几行配置我见过太多次因为时区问题导致查出来的时间差了八个小时。server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/house_rent?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: global-config: db-config: logic-delete-field: delFlag logic-delete-value: 1 logic-not-delete-value: 0 configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl spring: servlet: multipart: max-file-size: 10MB max-request-size: 20MB这里最容易被忽略的是serverTimezoneAsia/Shanghai。不配的话数据库连接不是报错就是时间偏移八小时。新版本MySQL驱动要求显式写明时区这行配置最好不动。map-underscore-to-camel-case要设成true不然数据库里的create_time字段映射不到Java类的createTime属性上查出来的对象永远是一堆null。mybatis-plus的logic-delete配置也很关键。用了逻辑删除之后你调用HouseMapper的deleteById实际执行的是update语句把del_flag置为1数据不会真正丢。如果项目里的表没设计del_flag字段这配置就不要乱开否则所有SQL后面都会自动拼上del_flag条件反而查不到数据。3. 核心业务实现用户认证、房源流转与订单闭环3.1 登录认证到底要不要用Spring Security每次有同学问我这个题目用不用Spring Security我的回答都是如果有现成源码且能跑通那就保留如果是从零手写建议用JWT加拦截器的轻量方案。为什么会这样Spring Security功能强大但概念多过滤器链、UserDetailsService、SecurityContext这一套下来很多同学自己写不明白。答辩时老师问一句“你的认证流程里请求是怎么被拦截的”要是答不上来反而露怯。JWT方案你可以这样设计登录接口接收用户名密码校验通过后生成一个token返回给前端。前端每次请求把token放在Header里后端写一个拦截器统一解析token把当前用户信息放到ThreadLocal里。public class JwtUtil { private static final String SECRET your-secret-key; public static String genToken(Long userId, String role) { return Jwts.builder() .claim(userId, userId) .claim(role, role) .setExpiration(new Date(System.currentTimeMillis() 2 * 60 * 60 * 1000)) .signWith(Keys.hmacShaKeyFor(SECRET.getBytes())) .compact(); } public static Claims parseToken(String token) { return Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(SECRET.getBytes())) .build() .parseClaimsJws(token) .getBody(); } }登录接口PostMapping(/login) public Result login(RequestBody LoginDTO dto) { User user userService.getOne(new LambdaQueryWrapperUser() .eq(User::getUsername, dto.getUsername()) .eq(User::getPassword, MD5Util.md5(dto.getPassword()))); if (user null) { return Result.error(用户名或密码错误); } String token JwtUtil.genToken(user.getId(), user.getRole()); return Result.success(token); }注意密码不要明文存库简单做法是MD5加盐更规范的做法是用BCrypt。虽然毕设阶段用MD5也能交差但老师问起来“为什么不推荐明文”这个问题你必须能答上来。拦截器里的核心逻辑是从Header里拿token解析失败就直接返回401解析成功就把userId和role放到请求上下文。部分接口还需要做角色校验管理员接口只有role为admin才能访问。3.2 房源检索的动态条件怎么优雅实现房源的搜索条件通常是动态的用户可能只输入关键字可能只选城市可能同时筛价格区间和租赁方式。这种场景最忌讳的就是手动用字符串拼接SQL一不留神就拼出SQL注入漏洞。用MyBatis-Plus的LambdaQueryWrapper可以非常清晰地解决动态条件拼接问题public IPageHouseVO searchHouse(HouseQueryDTO query) { PageHouse page new Page(query.getCurrent(), query.getSize()); LambdaQueryWrapperHouse wrapper new LambdaQueryWrapper(); wrapper.eq(House::getStatus, 1); if (StrUtil.isNotBlank(query.getKeyword())) { wrapper.and(w - w.like(House::getTitle, query.getKeyword()) .or().like(House::getDescription, query.getKeyword())); } if (StrUtil.isNotBlank(query.getCity())) { wrapper.eq(House::getCity, query.getCity()); } if (StrUtil.isNotBlank(query.getDistrict())) { wrapper.eq(House::getDistrict, query.getDistrict()); } if (query.getMinPrice() ! null) { wrapper.ge(House::getPrice, query.getMinPrice()); } if (query.getMaxPrice() ! null) { wrapper.le(House::getPrice, query.getMaxPrice()); } return houseMapper.selectPage(page, wrapper); }这里的eq、like、ge、le全部是带条件的只有当传入值不为空才会拼接进SQL。既保证了安全性又避免了写一长串if判断。还有一点列表页要用分页。MyBatis-Plus自带分页插件配置好之后selectPage就会自动生成limit语句。别自己写PageHelper也别一次性查出全表数据在内存里分页房源数据量虽然不大但演示时翻页卡顿很掉分。3.3 预约看房与租赁订单的状态机预约和订单是整个系统里最容易出逻辑漏洞的地方。我先把状态流转画出来虽然不能画图但用文字描述就是租客发起预约状态0到房东确认状态1租客到现场看房后标记已看房状态2双方谈妥签约之后生成订单状态3流程走到这预约闭环才算结束。这个过程中最容易犯的错是跳过房东确认环节。有的同学图省事预约成功后直接生成订单房东连确认都没有。你往大了说这叫业务流程缺陷。老师一眼就能看穿。订单生成时的并发问题也值得你提前思考。假设房源只有一套两个租客同时点击“立即下单”如果不加保护两个人可能同时把同一套房源签下来。解决办法很简单核心思路是“条件更新”。不要把整条house记录查出来再判断状态而是直接发一条带条件的update语句UPDATE house SET status 2 WHERE id #{houseId} AND status 1执行这条SQL后如果影响行数等于1说明房源成功从上架状态改成了已出租状态当前用户可以继续创建订单如果影响行数等于0说明别人已经抢先一步直接提示“房源已被约走”。这套方案不需要引入Redis分布式锁对毕设项目来说已经够了。你需要在service里这样写boolean success houseService.update(new LambdaUpdateWrapperHouse() .eq(House::getId, houseId) .eq(House::getStatus, 1) .set(House::getStatus, 2)); if (!success) { throw new BizException(房源已被预定请选择其他房源); }这条代码是你答辩时最能讲的点之一。你可以主动跟老师说我在订单生成前用了条件更新来防止并发下重复租赁。就这一句话比背十页业务功能介绍都管用。3.4 双角色权限控制的隐藏坑房屋出租系统里至少有房东和租客两个角色加上管理员就是三个。前端会根据角色隐藏按钮比如租客看详情页有“预约看房”房东看自己的房源才有“编辑”和“下架”。但很多同学漏了后端校验直接拿一个接口通吃所有角色导致越权漏洞。举一个高频案例租客明明没有发布过房源却直接请求一个删除房源的接口传入对方的houseId后端不做归属校验结果就把别人的房源删了。正确的后端校验思路是操作型接口必须校验资源归属。房东修改或删除房源时不仅要判断登录用户是房东还要判断这条房源的landlordId是不是当前登录用户。不是就直接抛异常。用一个简单注解加拦截器也完全可以做到。比如自定义一个PreAuthorize(role)拦截器解析出token里的角色字段再对比目标路径需要的角色不一致就拒绝访问。这个方案比引入Spring Security更直观也更适合在答辩时摊开讲。4. 实操过程从源码导入到顺畅运行4.1 拿到源码后的启动顺序与准备工作网上下载的Spring Boot毕设项目通常是这样一套东西一个后端文件夹、一个前端文件夹、一个SQL文件夹、一个README。不管你拿到的是什么样启动顺序基本一致。先准备环境JDK 8及以上、Maven 3.6以上、MySQL 8.0、IDEA或者Eclipse。然后打开MySQL创建一个空数据库比如house_rent再把SQL文件夹里的脚本导入进去。导入方式可以用Navicat的“运行SQL文件”也可以直接在命令行执行source命令。然后用IDEA打开后端项目等Maven把依赖全部下载完。这个过程慢的时候可能要十分钟别着急。接着修改application.yml里的数据库用户名和密码一定要改成你自己机器上的实际值。后端启动很简单找到主类类名一般是HouseRentApplication这种格式右键运行。启动日志里没有异常并且能看到Tomcat started on port 8080说明后端部分已经起来了。如果项目里还带前端那通常是Vue项目。进入前端目录执行npm install装依赖再执行npm run serve启动开发服务器。然后浏览器打开localhost:8081之类的地址通过前端页面的代理转发访问后端接口。4.2 我每次调试都必做的三项检查很多人反映项目明明按步骤操作了还是跑不起来。根据经验90%的问题集中在下面三个地方我每次拿到一套新源码都会先检查这三项。先检查MySQL服务有没有启动。Windows上经常出现MySQL服务没开Spring Boot启动时报错找不到数据库。打开服务管理器看一眼就能排除。再检查数据库连接串里的用户名密码是否匹配。很多下载来的项目数据库密码是root或123456你的本地MySQL可能设置了其他密码。配置不匹配时日志里会出现Access denied for user。最后检查前端代理转发配置。如果用的是Vue开发服务器需要在vue.config.js里配置devServer的proxy把/api开头的请求转发到后端的http://localhost:8080。很多人后端明明没问题但前端页面一直报404原因就是这个代理没配或者路径前缀不对。4.3 二次开发值得动手改的5个地方如果你想在答辩前给项目加一点“增量”我推荐下面这几个方向改动量从低到高排列性价比极高首页加一个数据统计区用ECharts展示房源总数、分区房源数量、订单趋势。后端提供聚合查询接口前端画几张图答辩时这块非常抓眼球。给房源的发布时间和到期时间做提醒功能租客后台显示合同即将到期。上传房源图片时加一个图片压缩处理避免大图直接存库导致加载慢。用Hutool工具类可以几行搞定。预留公告模块的后台编辑器把普通的文本域换成富文本编辑器。前端引入wangEditor后端按HTML保存。在做搜索时加入“最近浏览记录”用Redis存一个列表或者直接存数据库一张浏览记录表能体现你考虑过用户体验。选一个自己真正能讲清楚的功能做不要贪多。毕设答辩忌讳“我每个功能都改了一点但每个细节都说不明白”。5. 常见问题与排查技巧实录5.1 后端启动即报错数据库连不上这个报错是这类项目里出现频率最高的。常见现象是启动日志里出现Failed to configure a DataSource或者Access denied for user。第一步先用数据库客户端工具试着连接本地MySQL。如果能连接成功说明是Spring Boot配置问题重点检查application.yml里url、username、password这三项。如果客户端也连不上那就是MySQL服务本身没启动或者密码不对。第二步确认数据库名字是否正确。很多人导入SQL时建库叫house配置文件里写的却是house_rent或者反过来启动必然报找不到数据库。把配置文件里的jdbc:mysql://localhost:3306/后面的库名改成你实际创建的库名即可。5.2 项目启动成功但页面白屏或者请求404后端能启动不代表前端一定能拿到数据。先打开浏览器的开发者工具看Network面板里具体哪个请求返回了404。如果请求路径是http://localhost:8081/api/house/list这种那就先看8081端口是不是前端开发服务器。如果是前端服务器在处理这个请求它会把/api开头的请求转发到后端8080。如果vue.config.js里没有配置代理或者配置的target端口和实际后端端口不一致就会404。还要检查后端接口路径有些控制器的映射是/api/house/list有的则是/house/list。前后端只要错一个字就会404。这个是联调阶段的常规坑核对两边路径就行。5.3 图片上传成功但前端加载不出来这个问题很经典。图片上传到了本地磁盘文件夹比如D:/upload/house/1.jpg但前端页面访问http://localhost:8080/upload/house/1.jpg时返回404。原因很简单Tomcat并不会默认把磁盘上的D:/upload目录暴露成URL。解决办法是配置资源映射。Spring Boot里加一个WebMvcConfigurer实现Configuration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file:D:/upload/); } }这样前端就能用/upload/**路径访问到D:/upload目录下的文件了。别忘了路径末尾的斜杠写错的话映射不生效。5.4 订单时间和房源数据异常不少项目会涉及查询某个月份的租金统计。如果发现统计数据对不上先查数据库里的时间字段。常见问题是时间类型用varchar存储排序和区间查询都会不对。统一改成datetime类型并且Java实体类用LocalDateTime接收前端格式化交给Jackson配置来处理。还有状态字段脏数据的情况。比如房源状态被直接改成了2但订单表里根本没有对应的有效订单导致列表里显示“已出租”但实际不可查。这种情况多半是手工改了数据库。平时调试尽量通过系统的操作流程触发数据变化不要直接改库里状态。5.5 常见问题速查表问题现象可能原因解决办法启动报Access denied数据库账号密码错误检查yml配置和本地MySQL实际密码保持一致启动报Unknown database库名写错创建好数据库并改配置中的库名前端页面404前端代理没配好检查vue.config.js的proxy配置页面显示跨域请求端口不一致后端加CORS配置或全局跨域过滤器图片打不开本地磁盘路径未映射配置addResourceHandlers数据查出来全是null驼峰映射没开打开map-underscore-to-camel-case上传大图失败上传大小超限调大multipart相关参数删除数据后列表还能看到逻辑删除字段或配置缺失检查del_flag和logic-delete配置最后再分享一点实际经验我做了多年毕设指导发现真正能拿高分的项目并不在于功能多花哨而在于学生能不能把核心链路讲清楚。房屋出租系统看起来简单但里面藏着用户认证、权限隔离、状态机流转、并发控制、文件映射这些硬知识点。你随便挑一个比如预约状态为什么不能从0直接跳到3比如并发下单怎么防超卖只要你能说透答辩就已经赢了一半。我个人最推荐的做法是拿到源码后先别急着改页面。花一个下午把数据库表关系画清楚再用纸把租房全流程从注册到退租走一遍标出每个状态变化是在哪个接口触发的。这个过程做完你会发现这个系统在你眼里不再是一堆文件而是一条完整的数据流。然后再动手去改心里就有底了。做技术项目就是这样先懂再改永远比先改再懂省时间。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑