资讯动态

基于SpringBoot+Vue+MySQL的房屋租赁管理系统全栈实战

发布时间:2026/10/20 3:11:48 来源:尧图企业网站定制
房源挂出去大半个月没人问租客来了又一间一间带看合同签没签全靠本子记——这是很多二房东和中小房屋中介的真实日常。我做的这个房屋租赁系统信息管理系统后端用SpringBoot、前端用Vue、数据库用MySQL就是用来解决房源信息散乱、租约状态不透明、账单容易漏掉这三件头疼事的。整个项目可以直接运行代码结构不复杂非常适合拿来学习前后端分离的开发流程或者作为毕业设计、个人作品的基础框架。先说清楚这个项目能干什么租客可以在前台浏览房源、提交预约看房管理员能维护房源上下架、录入租客信息、生成电子合同、记录每期水电费和租金账单系统自带统计面板当月出租率、待收款项一眼就能看到。前后端通过JSON接口通信登录采用Token认证权限上区分管理员和普通租客。整个项目打包完大概只有几个文件导入数据库、改一下数据库密码就能跑起来。这篇文章的目标读者很明确正在学SpringBoot和Vue、想找一个完整项目练手的同学或者马上要做毕业设计、需要一个业务逻辑完整且能演示的系统的同学。我会把项目从设计思路、数据库表结构、后端接口实现、前端页面交互到最终的启动步骤和常见报错完完整整拆开讲一遍保证你能看懂也能照着跑通。1. 项目整体设计与技术选型思路1.1 为什么选定SpringBootVueMySQL这套组合现在做Web项目技术选型最怕的不是不会用而是选择太多。有人上来就是微服务、Redis、消息队列一套全家桶最后光搭建环境就折腾了两周业务代码一行没写。我这个项目刻意走了最稳妥的路线SpringBoot负责后端接口Vue负责前端页面MySQL存数据。为什么这么选三点理由。第一SpringBoot解决了传统SSM框架大量XML配置的繁琐问题内置Tomcat一个main方法就能启动服务极大地降低了后端入门门槛。第二Vue作为目前国内使用率最高的前端框架生态成熟配Element UI组件库做后台管理系统几乎是现成的积木开发效率极高。第三MySQL是关系型数据库里最普及的无论是安装、可视化工具还是网上资料都比其他数据库更容易找到。这个组合还有一个隐性的好处它代表了当前中小型Web系统最主流的技术形态。你在简历上写这个项目面试官不需要你解释“为什么用这个框架”大家默认这就是标准答案。而换成SSHStrutsSpringHibernate那种老古董或者换成还没稳定下来的新技术沟通成本反而高。1.2 功能模块与整体架构拆解整个系统我分成了三层前端展示层、后端接口层、数据存储层。前端是Vue单页应用通过axios调用后端RESTful接口后端按Controller、Service、Mapper三层分包处理业务逻辑和数据读写数据库负责最终的数据落地。功能模块上我拆成以下六个核心模块房源管理房源的增删改查、上下架、租金和押金设置、房源图片展示。这是整个系统的核心数据所有其他模块都围绕它展开。租客管理租客信息的录入和维护包括姓名、手机号、身份证号、紧急联系人。这里要注意身份证号属于敏感信息演示项目可以保留但真正上线需要加密存储。合同管理线上生成租赁合同记录起止日期、租金、押金、付款方式支持合同状态跟踪履行中、已到期、已退租。账单管理每期租金账单和水电费账单的生成、缴纳状态标记支持账单筛选和逾期提醒。预约看房租客在前端提交看房申请管理员在后端审核确认看房时间这是连接房源和成交的重要环节。统计面板用图表展示房源出租率、月收入、租约到期提醒等关键指标。做这个模块最大的价值在于毕业设计答辩时能直观展示系统的“数据价值”而不只是增删改查。角色权限上我分了管理员和租客两档。管理员能操作所有模块租客只能浏览房源、提交预约、查看自己的合同和账单。这里没有做更细的房东和租客分离对于中小租赁场景来说管理员就等同于运营方一张角色表足够。1.3 不做微服务的原因有人可能会问为什么不用微服务说实话对于这个量级的系统微服务就是在给自己挖坑。分布式事务、服务注册发现、配置中心、链路追踪每一个问题都比租赁业务本身复杂得多。单体应用把业务代码写清楚、接口设计规范已经能覆盖99%的租赁管理需求。真要扩展这个项目预留了清晰的模块边界后续把账单模块独立成服务也不难。2. 数据库设计一张表撑起一个模块2.1 核心表结构详解数据库设计决定了业务的上限这块我花的时间最多。整个数据库一共建了8张表用户表、租客信息表、房源表、预约表、合同表、账单表、图片表和系统配置表。下面挑几张核心表详细说明字段设计的思路。用户表userCREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 登录账号, password varchar(255) NOT NULL COMMENT 密码(MD5加密), role tinyint(4) NOT NULL DEFAULT 2 COMMENT 角色 1管理员 2租客, real_name varchar(50) DEFAULT NULL COMMENT 姓名, phone varchar(20) DEFAULT NULL COMMENT 手机号, status tinyint(4) NOT NULL DEFAULT 1 COMMENT 状态 1启用 0禁用, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这张表是所有登录认证的基础。角色用tinyint而不是varchar查询效率更高也方便前端映射“1-管理员2-租客”。密码字段我故意留了255的长度因为哪怕后续把MD5升级成BCrypt哈希结果的长度也完全放得下。房源表houseCREATE TABLE house ( id int(11) NOT NULL AUTO_INCREMENT, title varchar(100) NOT NULL COMMENT 房源标题, address varchar(255) NOT NULL COMMENT 详细地址, area decimal(10,2) DEFAULT NULL COMMENT 面积(平米), rent decimal(10,2) NOT NULL COMMENT 月租金, deposit decimal(10,2) DEFAULT NULL COMMENT 押金, house_type tinyint(4) DEFAULT 1 COMMENT 户型 1一室 2两室 3三室, status tinyint(4) NOT NULL DEFAULT 1 COMMENT 状态 1已上架 0已下架, description text COMMENT 房源描述, landlord_id int(11) DEFAULT NULL COMMENT 对应房东用户ID, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_status (status), KEY idx_rent (rent) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里要特别说一下金额字段。租金和押金必须用decimal(10,2)绝对不能使用float或double。浮点数在计算机中是近似存储哪怕只有几分的误差在租金计算上也是无法接受的。我见过有人用double存金额最后账单汇总差了0.01元怎么都对不上排查半天才发现是数据类型的问题。合同表contract合同表是整个租赁系统的核心因为它关联了用户、房源、时间段三个维度。设计时我把合同状态单独做成整型字段而不是通过日期去反推状态这样查询“哪些合同即将到期”就变得非常简单status tinyint(4) DEFAULT 1 COMMENT 合同状态 1履行中 2已到期 3已退租 4已解约, start_date date NOT NULL, end_date date NOT NULL用DATE类型存日期能直接调用MySQL的日期函数做计算比如查询30天内到期的合同就是end_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 30 DAY)。2.2 字段类型与索引的经验性取舍数据库设计里的很多选择都是踩坑换来的经验。我在这里集中讲几个关键点。第一字符集必须统一用utf8mb4。如果用了老旧的utf8遇到生僻字或者某些emoji字符存入数据库就直接报错整个接口崩溃。utf8mb4是utf8的超集能覆盖所有Unicode字符这是现在唯一合理的选择。第二状态字段全部用tinyint而不是varchar。比如房源状态“已上架/已下架”用0和1表示存储空间小、查询快而且在代码里写枚举比比较字符串要安全得多。前端展示时再通过映射表把数字翻译成中文完全不影响可读性。第三索引不是越多越好。我给房源表的status和rent分别建了索引这是为了支撑“按状态筛选房源”和“按价格区间排序”这两个高频查询。但像description这种长文本字段绝对不要建索引索引占用空间大、更新慢还容易导致索引失效。3. 后端SpringBoot核心实操解析3.1 统一返回结构与全局异常处理后端这块最重要的不是某个业务怎么写而是从第一个接口开始就建立统一的规范。我用了R类作为统一返回结构所有接口的返回值格式完全一致public class RT { private Integer code; private String msg; private T data; public static T RT ok(T data) { RT r new R(); r.setCode(200); r.setMsg(success); r.setData(data); return r; } public static T RT error(String msg) { RT r new R(); r.setCode(500); r.setMsg(msg); return r; } }配合RestControllerAdvice做全局异常处理业务代码里完全不需要到处写try-catch。比如参数校验失败、业务逻辑错误直接throw一个自定义异常全局处理器统一转成R.error返回。这样做有三大好处前端axios响应拦截器只需要判断code是否为200处理逻辑统一后端业务代码非常干净排查问题时候看返回的msg就够了。3.2 JWT登录认证与接口权限拦截登录认证我用的是JWT方案。流程是用户提交用户名密码后端校验通过后生成一个带有效期的Token返回给前端前端把Token存到localStorage后续每个请求都在请求头里带上Token后端通过拦截器校验Token是否合法。核心拦截器代码public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口和预检请求 if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } String token request.getHeader(token); if (token null || token.isEmpty()) { throw new BusinessException(401, 未登录); } // 解析Token把用户信息塞到ThreadLocal里 User user JwtUtil.parseToken(token); UserContext.set(user); return true; } }这里有几个细节值得注意。第一Token的密钥不要硬编码在代码里要放到application.yml配置文件中方便不同环境切换。第二拦截器必须放行登录接口和所有OPTIONS预检请求否则前端跨域请求会直接失败。第三解析Token后把用户信息存到ThreadLocal业务层任何地方都能获取当前操作人不用每个接口都传用户ID。权限控制上我在拦截器里加了一个简单的角色判断逻辑如果请求的URL以/admin开头而当前用户角色不是管理员就直接返回无权限。这种基于URL前缀的粗粒度控制在后台管理系统里完全够用比Shiro、Spring Security等安全框架轻量太多。3.3 房源检索与订单状态流转的关键实现房源检索是前端用得最多的接口。我用MyBatis-Plus的条件构造器来实现动态查询前端传什么参数就拼接什么条件参数为空就不参与过滤Override public PageHouse searchHouse(int pageNum, int pageSize, String keyword, Integer houseType, Integer status, BigDecimal maxRent) { PageHouse page new Page(pageNum, pageSize); LambdaQueryWrapperHouse wrapper new LambdaQueryWrapper(); if (StringUtils.hasText(keyword)) { wrapper.like(House::getTitle, keyword) .or().like(House::getAddress, keyword); } if (houseType ! null) { wrapper.eq(House::getHouseType, houseType); } if (maxRent ! null) { wrapper.le(House::getRent, maxRent); } wrapper.eq(House::getStatus, 1); // 默认只查上架房源 wrapper.orderByDesc(House::getCreateTime); return houseMapper.selectPage(page, wrapper); }订单或者说合同的状态流转是这个项目里最容易写乱的业务。我的处理方式是把状态定义成枚举所有状态变更必须走统一的方法禁止业务代码里直接对status字段赋值。合同的核心流转是待签约 - 履行中 - 已到期/已退租。每一次状态变更都会同时更新房源的出租状态比如合同变成履行中对应房源自动改成“已出租”不可再被预约。这个联动逻辑放在Service层统一处理避免了多个地方改状态导致的数据不一致。4. 前端VueElement UI的工程化落地4.1 项目初始化与目录结构划分前端用的是Vue 2.6 Element UI Vuex Vue Router这些都是Vue生态里最成熟的方案。项目用vue-cli脚手架创建目录结构按功能划分src/ ├── api/ # 接口请求函数每个模块一个文件 ├── assets/ # 静态资源 ├── components/ # 公共组件 ├── router/ # 路由配置 ├── store/ # Vuex状态管理 ├── views/ # 页面组件 │ ├── admin/ # 管理员后台页面 │ ├── front/ # 租客前台页面 │ └── login.vue # 登录页 ├── utils/ # 工具函数axios封装、token处理 ├── App.vue └── main.js这种目录划分看起来很常规但实际开发中非常顺手。api目录里每个文件对应后端的Controller比如house.js里放所有房源相关请求前后端联调时找接口特别快。views下面区分admin和front把管理员后台和租客前台界面隔离开权限控制也直观。4.2 axios封装与路由守卫的权限控制axios封装是整个前端项目的基石。我统一在utils/request.js里创建axios实例配置baseURL和超时时间然后在请求拦截器里自动加Token在响应拦截器里统一处理错误码import axios from axios const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[token] token } return config }) service.interceptors.response.use( response { const res response.data if (res.code ! 200) { if (res.code 401) { localStorage.removeItem(token) window.location.href /login } return Promise.reject(new Error(res.msg)) } return res }, error { return Promise.reject(error) } ) export default service路由守卫这边我在src/router/index.js里配置了全局前置守卫做登录状态检查和角色权限检查。核心逻辑很简单没有Token的直接踢到登录页有Token但访问管理员页面的再检查一下store里存的是不是管理员角色不是就阻止跳转。这个写法比在每个页面里手动判断权限要优雅得多而且很好扩展。4.3 核心页面与交互细节的实现房源管理页面是整个后台的颜值担当。我用Element UI的el-card组件做房源卡片列表每个卡片显示房源图片、标题、租金配合分页组件。关键交互在于“上架/下架”操作用户点击后前端弹窗确认确认后调接口成功后局部刷新当前数据。这里我踩过一个坑一开始直接调接口后调用了整个列表的查询接口导致分页跳回第一页用户体验很差。后来改成更新当前页数据才解决这个问题。租客前台的预约看房流程我用了一个el-dialog弹窗里面套一个预约表单。表单里有三个关键字段看房时间、联系人、联系电话。提交前用Element UI自带的表单校验规则手机号必须匹配/^1[3-9]\d{9}$/的格式否则不给提交。前端校验的意义在于能过滤掉80%的无效请求减轻后端压力。统计面板这块我用Echarts画了两张图一张饼图展示房源户型分布一张折线图展示近六个月租金收入趋势。Echarts的Vue封装不复杂只需要在mounted里初始化实例然后从后端拉取统计数据setOption更新数据。这里有个性能优化点组件销毁时必须调用dispose释放实例否则页面切换多了会导致内存泄漏。5. 环境配置与一键运行指南5.1 本地环境要求与初始化SQL导入既然项目标题里写了“可直接运行”这部分就是很多同学最关心的。先亮出我的运行环境照着准备基本不会踩坑组件推荐版本说明JDK1.8或11SpringBoot 2.x兼容性最好Maven3.6后端依赖管理Node.js14.x或16.xVue2项目不建议用太新的NodeMySQL5.7或8.0两种版本都可以8.0注意时区设置IDEIDEA VSCode按个人喜好数据库初始化很简单。先用Navicat或命令行创建一个数据库比如叫rent_db字符集选utf8mb4然后执行项目里的db.sql脚本所有表结构、初始数据就自动建好了。脚本里有预置的管理员账号admin / 123456方便首次登录。5.2 后端启动与前端联调的关键配置后端的配置集中在application.yml里需要修改的主要是数据源部分server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/rent_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 你的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver注意连接URL里serverTimezoneAsia/Shanghai必须加MySQL 8.0如果不指定时区启动时会直接报The server time zone value...错误这是新手最常见的坑之一。驱动类也要注意MySQL 8.0用com.mysql.cj.jdbc.Driver而5.7版本用com.mysql.jdbc.Driver项目里默认配的8.0驱动。后端的启动方式有两种第一种是在IDEA里直接运行RentApplication启动类的main方法第二种是命令行进入项目根目录执行mvn spring-boot:run。推荐用第一种IDEA里能看到更完整的日志和调试信息。启动成功的标志是日志出现Started RentApplication in x.x seconds然后浏览器访问http://localhost:8080/api/health能返回正常结果。前端启动稍微慢一些因为第一次要下载依赖。进入frontend目录后依次执行npm install npm run serve这里有个提速技巧npm install如果特别慢把npm源换成国内镜像一行命令搞定npm config set registry https://registry.npmmirror.com。启动成功后控制台会提示App running at Local: http://localhost:8081/同时注意看有没有显示Proxy: http://localhost:8080。我前端配了8081端口、接口请求代理到8080后端这样完美避开同源策略限制。5.3 新手最容易踩的5个坑这部分我直接做成表格都是真实遇到过、并且经常有人在群里问的问题。报错现象根本原因解决办法启动报The server time zone valueMySQL时区未指定连接URL加serverTimezoneAsia/Shanghai前端请求接口报404后端上下文路径不对确认vue.config.js里proxy的target端口是否和后端一致登录接口返回401Token没传或已过期检查axios拦截器是否从localStorage正确读取Token前端页面白屏路由history模式部署问题开发环境用hash模式上线运维配置try_files数据库导入SQL失败字符集不一致建库时明确指定DEFAULT CHARSETutf8mb4这里重点展开讲一下跨域问题。开发环境我推荐用Vue CLI的proxy代理在vue.config.js里配置module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这样前端代码里的请求地址只用写/api/house/list根本不需要写完整的服务器地址。如果不用proxy而选择在后端加CrossOrigin或者CorsRegistry开发时也可以但上线后前后端部署在不同域名时还得重新配所以从一开始就用代理方案更省心。另外再提醒一句前后端代码里的端口、数据库账号这些配置千万记得按照自己的环境改。我遇到过不止一个同学代码逻辑全会结果数据库密码写错就启动不了还以为是代码有问题。环境配置永远是最先要排查的。说实话把这个项目完整跑起来你收获的不只是一套能演示的系统。从SpringBoot的接口规范、JWT认证、MyBatis-Plus的使用到Vue的组件化开发、路由守卫、Echarts可视化再加上MySQL表设计里的各种取舍这些就是全栈开发的完整闭环。我个人在实际操作中最大的体会是这种业务系统真正的复杂度不在某个技术难点而在于把房源、合同、账单、权限这些业务模块之间的关系梳理清楚。哪怕你现在只是照着代码跑了一遍也一定要亲手改一个功能——比如给房源加个“朝向”字段——从头跟一遍数据从数据库到前端展示的完整链路收获绝对比看十遍教程都大。最后再分享一个小技巧如果答辩或者演示时需要展示系统的完整性提前准备一组有对比性的测试数据比如10套房源里5套已出租、5套在租统计面板的图表效果会漂亮很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑