news 2026/11/5 0:37:29

SpringBoot+Vue房屋租赁管理系统:从设计到部署

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+Vue房屋租赁管理系统:从设计到部署

1. 为什么房屋租赁系统老用SpringBoot+Vue这套组合

先说个现象。在个人开发者圈子里,凡是涉及管理系统类型的练手项目,十个里有七八个都是SpringBoot+Vue这对组合。这次这个基于SpringBoot+Vue的房屋租赁管理系统也不例外,后端MyBatis国产化ORM,中间接MyBatis,底层MySQL存数据。之所以这个搭配能成为固定搭配,核心原因是这套技术栈把“能跑起来”的门槛压得非常低,同时又能覆盖到企业开发里的绝大部分真实场景。

一个合格的房屋租赁系统,本质上要解决的只有三件事:房源管理、租客合同、账单收租。听起来简单,但真正落库之后你会发现,这些业务之间的关联关系远比预想的复杂。一套房子可能经历过多个租客,一个租客名下可能有多份合同,每份合同又关联着多笔水电费和押金记录。如果只做表面的增删改查,那确实随便什么框架都能做,但要让这种系统真正被经纪人、房东、租客这三类人长期使用,就必须在权限、状态流转、费用计算这些地方下功夫。

我个人的看法是,选择SpringBoot+Vue并不是因为它有多高级,而是因为这组合生态成熟到了一种“傻瓜都能搜到答案”的程度。遇到任何报错,把错误信息往搜索框一贴,基本都能找到对应的解决方案。这种可维护性和周边支持,对独自开发、尤其是刚入门不久的学生党来说,比任何技术先进性都实在。

这里顺便聊下为什么不用Spring Cloud那套微服务,或者为什么不直接上Vue3+TS。房屋租赁这种业务量级的系统,单体应用完全够用,上微服务纯属给自己找麻烦。Vue2和Vue3的选择上,如果你是从零开始而且没有历史包袱,那就直接Vue3,生态已经很成熟了。不过这次项目如果沿用Vue2的写法,其实也不影响核心功能,后面想升级再整体迁移也行,前期别被工具选择卡住进度。

2. 业务模块拆解:房东、租客、房源、账单各自管什么

2.1 四类角色四个入口,权限边界要先画清楚

做管理系统之前,第一件事不是写代码,是把角色的权限边界想清楚。这个房屋租赁系统至少要区分四种角色:系统管理员、房东、租客、中介(或者叫运营人员)。很多半成品项目做出来被人吐槽“就是个数据库的外壳”,就是因为所有角色进去看到的菜单一摸一样,权限形同虚设。

我建议的权限模型是这样的:系统管理员管全局,能看到所有房东、租客、房源和账单数据,负责平台层面的配置;房东只能看到自己名下挂的房源,以及这些房源对应的合同和收款记录;租客只能看到自己签过的合同,以及待缴账单和缴费历史;中介作为可选角色,可以代操作房源挂牌和合同录入,但无权修改账单金额。

这套权限模型实现起来并不复杂。前端根据登录后返回的角色字段动态生成路由菜单,后端在Controller层用拦截器做接口级别的权限校验。真正的难点在于数据隔离,也就是房东只能查到自己名下的房子,这个不能只靠前端藏菜单,后端必须强制带上tenant_id或者owner_id的查询条件,否则直接构造HTTP请求就能越权拿到别人数据。

2.2 房源、合同、账单三大核心模块的状态流转

租赁系统的房源状态大致分这几档:空置中、已预定、已出租、已下架。每套房源从空置开始,经过租客看房下单后进入预定,签约成功后就变成出租中,合同到期退租后又回到空置状态。坑点在于,很多新手会把状态建模成简单字符串存放在房源表里,这样在展示层确实方便,但在修改状态时容易出现并发问题,两个操作员同时操作同一套房源就会出bug。

我的做法是给房源状态建一个独立的状态表,或者至少在代码里定义一个状态字段加上版本号,用乐观锁去更新状态。比如签约时先执行update room set status=2 where id=? and status=1,影响行数为0就说明有人抢先操作了,直接报错提示“该房源已被预定”。这种细微的并发处理,才是项目和课程作业拉开差距的地方。

合同的状态同样要细心:待生效、执行中、已完结、已违约。账单的状态:待支付、已支付、已逾期、已退款。系统里每一次状态变更都要在操作日志里留痕,记录操作人、操作时间、旧状态和新状态。这样以后房东说“我没点过退租”的时候,你能直接拉一条日志甩他脸上。

2.3 数据库表结构设计:九个表怎么把业务串起来

整个系统的核心表结构我大致设计成这样:用户表(区分角色)、房东表(如果房东需要额外的身份证信息等,单独拆表)、房源表、房源图片表(因为一个房源有多张图)、租客表、合同表、账单表、缴费记录表、操作日志表。如果还涉及小区楼栋管理,可以再加一栋楼表关联房源。

房源表最关键,和房源相关的核心字段包括:房源编号、所属房东ID、小区名称、楼栋单元门牌号、户型、面积、朝向、所在楼层、租金月价、押金方式、状态、配套设施、房源描述。其中租金和押金这两个字段我强烈建议用整数分存储,避免后期统计租金时出现浮点数精度问题。数据库里用decimal也行,但直接以“分”为单位的整数最稳,前端展示时再除以100转成元。

合同表要记录租客ID、房源ID、起租日期、结束日期、租金单价、押金金额、每月几号交租、违约金规则、备注。这里有个容易被忽略的字段,合同编号。别用自增ID当作合同编号暴露给用户,要单独生成一个业务编号,比如HT20250101001这样的格式。房间号同理,房源的业务编号最好用ZZ0001这种,看着正规,以后对接打印、导出的时候也更方便。

账单生成不要靠人手工点。到了每月缴费日,系统自动根据执行中的合同生成一条待支付账单,金额按照合同租金单价计算,关联缴费周期。这是我强烈推荐的懒人设计思路,后面你实际用的时候会发现省了巨多事。

3. 后端落地:Controller、Service、Mapper三层怎么分工

3.1 MyBatis的XML里写SQL,和代码里写注解SQL怎么选

这个项目用的MyBatis,确实很经典,现在很多工作中还在用。MyBatis有注解和XML两种SQL写法,我明确推荐XML。原因很简单,房屋租赁的查询条件特别多,房源列表光筛选条件就有小区、朝向、户型、价格区间、状态、房东,各种条件还可能任意组合。用注解写这种动态SQL,语句会膨胀到没法看,XML里配合if标签和where标签写动态SQL,清爽得多。

一个典型的房源条件分页查询XML大概是这个感觉:

<select id="selectRoomPage" resultType="com.example.entity.Room"> SELECT * FROM room <where> <if test="communityName != null and communityName != ''"> AND community_name LIKE CONCAT('%', #{communityName}, '%') </if> <if test="orientation != null and orientation != ''"> AND orientation = #{orientation} </if> <if test="status != null"> AND status = #{status} </if> <if test="minRent != null"> AND rent >= #{minRent} </if> </where> ORDER BY create_time DESC LIMIT #{offset}, #{pageSize} </select>

注意我这里的分页用的是LIMIT #{offset}, #{pageSize},因为我希望少引入一个分页插件依赖。如果项目复杂或者想省事,用PageHelper也完全没问题,就是多用了一个依赖。两种我都试过,简单项目手写LIMIT反而少些踩坑的环节,不用去管PageHelper和MyBatis版本兼容问题。

3.2 登录鉴权和拦截器:JWT多久过期得拍板

登录模块我用的是JWT方案,前端登录成功后在本地存token,之后每次请求在请求头里带Authorization字段,后端写一个拦截器统一校验。校验的逻辑不复杂:先判断请求路径是否在白名单里(比如登录接口、房源列表查询接口),不在白名单里就解析token,解析失败或者过期直接返回401状态码,前端收到401就做跳转登录页的处理。

Token的过期时间我建议设置为12小时或者24小时。设太久不安全,token丢掉会影响账号安全;设太短用户体验差,用着用着就被弹出登录页,还得重新输一遍密码。如果你有点追求,可以在Redis里做刷新机制,快过期的时候自动续期,但这个对租赁系统来说是锦上添花,不是必须。

拦截器注意一个细节:放行OPTIONS请求。前后端分离项目跨域时会先发一个OPTIONS预检请求,如果你拦截器把OPTIONS也拦了,前端就会发现所有带token的请求全都报跨域错误。这个坑我踩过,排查了半天最后发现是拦截器没有放行OPTIONS。

3.3 事务和批量操作的边角问题

合同签约这个操作是典型的需要事务保护的多表操作:第一步锁定房源(状态改成已预定),第二步创建合同记录,第三步把房源状态改成出租中,第四步生成一条当月租金账单。这四步任何一步失败,前面成功的步骤都要回滚,否则就会出现房源被占了但合同不存在的脏数据。

实现方式很简单,在Service层方法上加@Transactional注解,默认遇到RuntimeException就会回滚。这里提醒一下,事务是加在Service方法上的,不是加在Controller或者Mapper上的。另外要注意:同一个类的内部方法调用,事务注解是不会生效的,因为走了this调用而不是代理对象,这是个经典的坑。如果确实遇到这种情况,可以把内部方法抽到另一个Service类里,或者自己注入自身代理。

批量生成账单的时候还有个小技巧:不要在Java代码里循环调用单条插入SQL,几千条合同数据循环插入会明显拖慢性能。用MyBatis的foreach标签写一个批量insert,一次请求把整个月的账单全部插进去,效率高一个量级。MySQL连接串上记得加上rewriteBatchedStatements=true这个参数,否则批量操作优化不会真正生效,这个参数面试或者排查问题都经常被问到。

4. Vue端的关键页面和联调时的真实体验

4.1 页面结构:登录页、工作台、房源管理、合同管理、账单管理

前端这部分我用的是Vue加Element UI组件库。实际开发时如果你基于Vue3,那对应的是Element Plus,但页面逻辑整体差不多,组件名大部分也能对应上。这个系统的页面不用太花哨,把常用元素做扎实才是重点。

页面去重之后核心就这么几个:登录页面(带角色选择和记住密码功能)、工作台(展示当前视角的统计数据,比如房东端显示房源数量、出租中数量、本月应收、已收)、房源管理(卡片列表、筛选栏、新增编辑弹窗、图片上传)、合同管理(列表、详情弹窗、状态管理按钮)、账单管理(月度账单列表、收款确认、逾期标记)、还有租客管理。

我在做这个系统的时候,工作台那页给的管理员放了全部房东和租客的用户量柱状图,给房东放了名下房源的饼状图。本来想用ECharts,但为了控制体积,手写了几个CSS的简易柱状条,实际效果也还行。如果不是特别在意花哨的图表,这种轻量的方案完全够用。

4.2 前端路由动态生成和权限控制

前端需要根据用户的角色动态生成可访问的菜单。做法是登录后拿回用户信息和token,根据角色去查对应的菜单配置,前端用router.addRoute动态注册路由,然后根据路由结构渲染侧边栏。千万别把所有页面的路由一次性写死在代码里然后靠v-if隐藏,那样权限信息全暴露在浏览器端,而且路由太多也不利于打包体积。

动态路由的坑在于刷新页面时路由会丢失,因为路由是登录后在JS内存里动态加的,刷新就重新初始化了。解决办法是把用户角色存到localStorage里,在路由守卫生效的时候,先判断store里有没有路由数据,没有就根据角色重新拉一次路由并addRoute,然后再放行。这个思路网上有一堆教程,但实话说第一次自己写几乎都会踩坑。

4.3 图片上传:本地存储还是外链图床

房源图片上传我建议保守一点,直接传回后端,存到项目的upload目录或配置的静态资源目录,数据库里只存URL路径。这种方案部署最简单,一个jar包加一个静态目录就解决了,不存在防盗链和外链过期问题。如果你把图片传到第三方图床,图片下载速度是快了,但一方面可能有隐私顾虑,另一方面免费图床随时可能挂。

后端用一个专门的接口接收文件,保存后用UUID重命名文件,避免中文名乱码和同名覆盖问题,然后把可访问的URL返回给前端。SpringBoot里要配置资源映射,把本地某个文件夹映射成/upload/**这样的虚拟路径。打包部署时注意别把上传的图片塞进jar包,数据目录和程序目录要分开管理,不然每次发布更新版本,图片就被新jar包替换掉了。

4.4 前后端联调时最容易被时间格式拖垮

前后端分离联调时最大的坑之一,是时间格式。后端默认返回的日期格式是时间戳或者带T的ISO字符串,前端直接展示就会出现“2025-02-01T12:00:00.000+00:00”这种鬼样子。解决方法是统一约定时间格式,在SpringBoot里配置Jackson的日期序列化方式,全局设置成yyyy-MM-dd HH:mm:ss,日期参数接收也指定好格式。

另一个联调常见问题是跨域。直接在后端写一个CorsFilter或者用@CrossOrigin注解就能解决大部分场景。不过注意别把allowedOrigins写死成某个localhost端口,开发环境前端可能是8080,部署后域名可能又变,最好抽到配置文件里,按环境动态切换。

我实际联调中还经常遇到前端传参方式对不上的问题,前端用axios默认的application/json传对象,后端@RequestBody接收没问题;但如果前端用了form-data或者URLSearchParams,后端必须用@RequestParam接收,否则拿到的全是null。这个也很好排查,打开浏览器Network面板看请求负载类型,再对应后端接口参数注解,基本两三分钟能定位。

5. 部署阶段的环境准备与发布细节

5.1 从零把项目跑起来需要哪几步

你拿到这套源码之后,想最快速度在本地跑起来,大致流程是这样的:先装MySQL数据库,版本5.7或者8.0都行,把utf8mb4编码和时区参数设置好。然后创建一个空的数据库,比如叫rental_system,再导入项目里自带的SQL文件,目前这一步会比较顺,因为SQL文件里通常包含了建表和初始数据。接着改后端application.yml里的数据库用户名密码,确保能连接上。

后端启动前还要注意Redis是否用了。有些项目把验证码、token存Redis,那就必须要先装Redis并启动服务;如果项目里没有用到Redis,就省掉这一步。确认端口没被占用后,直接运行SpringBoot的启动类,看到Tomcat started的日志就说明后端起来了。前端部分,先执行npm install装依赖,如果因为网络原因安装很慢,就换国内镜像源,然后npm run serve启动开发模式,浏览器访问localhost:5173或者8080端口,看到登录页面就说明整体通了。

5.2 前端打包进SpringBoot的两种做法

如果你懒得单独部署前端静态资源,可以走全栈一体化部署:前端执行npm run build,把生成的dist目录拷到SpringBoot项目的src/main/resources/static下面,重新打包,前端页面就跟着jar包一起走了。这种方式个人项目和朋友小范围用完全没问题,一个jar包丢服务器就完事。

另一种做法是前后端分开部署,前端用nginx托管,静态请求走近nginx,后端接口走代理转发。好处是以后前端更新不用重新打包后端,发版只替换前端文件就行。配置上用nginx的proxy_pass把/api路径转发到后端的8080端口。我个人更推荐第二种,即便个人项目,也养成分开部署的好习惯,反正服务器装个nginx也就一条命令的事。

5.3 部署上线后必须检查的三件事

上线之后别忘了做三次检查。第一,数据库时区设置别出错,否则你看到的时间比真实时间晚8个小时,账单到期日全都不对。推荐连接串参数里显式加上serverTimezone=Asia/Shanghai。第二,检查配置文件里有没有把数据库密码、密钥等敏感信息直接写死在代码里,如果这是给别人演示的项目源码,至少把生产环境的密码放到环境变量里加载。第三,确认打包时前端路由是hash模式还是history模式。hash模式刷新不会404,history模式就必须在nginx里配try_files兜底,否则用户直接在非首页刷新页面会看到白屏。

这几条看着都是一两句话的小事,但真出问题时,排查的时间都是按小时计算的。

6. 项目开发中的常见问题排查清单

做这套系统过程中,我整理了印象最深刻的问题和排查方法,分享出来,能碰上就直接抄作业。

第一类是启动即报错,大概率是端口占用或者依赖冲突。看日志最下面的Caused by,定位到具体原因。数据库连不上基本就是账号密码错了、MySQL服务没启动、或者少了mysql-connector-java依赖。

第二类是接口返回500但控制台没打印SQL,这种多见于MyBatis的Mapper映射问题,XML里的resultType写错,或者数据库字段名和实体类属性映射不上。排查时先打开MyBatis的SQL日志,application.yml里配置mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl,把SQL和参数值打出来,看是不是SQL本身的问题。

第三类是前端白屏。原因无非三种:JS文件加载失败、路由匹配不到、依赖缺失。用浏览器F12看Console和Network,基本能定位。最常见的坑是Vue3中用的API和Vue2不同,混用导致报错,排查时看一下控制台具体报的是哪个方法未定义就行。

第四类是中文乱码。页面显示乱码多半是HTML或JS文件编码问题;接口返回中文乱码,检查数据库连接串是否加了characterEncoding=utf8;数据库本身乱码,检查建库建表的charset。整体记住一条原则:从数据库到后端到前端,链路每一段的编码都要是UTF-8。

这些排查经验不是一天积累起来的,项目做多了自然就能形成肌肉记忆。你多碰几次这些报错,以后看到日志前几行就知道怎么处理了。

7. 这套源码拿到手之后怎么改造成自己的项目

最后聊聊拿到源码之后怎么二次开发。先别急着改代码,第一件事是把项目完整跑通一遍,用管理员账号把所有页面都点一遍,知道每个页面在干什么,然后再动手。然后全局搜索项目里的公司名、版权信息等占位内容,改成自己的名字或学校信息,特别是页面的logo、侧边栏标题和浏览器Tab的标题,这些细节最容易被忽视。

改造的优先级我认为是这样:先改用户管理(这是系统的根基,把账号体系改成自己需要的角色),再改房源字段(根据实际项目里小区户型情况增删字段),然后改账单规则(比如把按月收取改成按季度、或者增加自定义应收项目),最后加导出功能(生成Excel账单明细,房东最需要的功能)。改完后再整体回归测试一遍,确认没有破坏原有功能。

整个项目做完之后,我最大的感受是:技术不难,难的是把所有模块的细节都考虑周全。你把这个项目完整的过一遍,从数据库设计到后端接口再到前端联调,抵得上刷好几十道面试题,因为面试问的项目经验、业务场景、问题排查,你全都有真实案例聊了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/3 11:59:33

PCS7 V6.1 SP1中文版安装全流程与避坑指南

简介&#xff1a;这份PDF文档面向工业自动化工程师、DCS系统实施人员及西门子PCS7初学者&#xff0c;系统整理了PCS7 V6.1 SP1中文版的安装步骤与注意事项&#xff0c;帮助读者在英文操作系统环境下顺利完成部署并规避常见兼容性陷阱。资源包共1个PDF文件&#xff0c;大小约1.2…

作者头像 李华
网站建设 2026/11/4 18:13:35

Postman接口测试进阶:从脚本自动化到CI/CD集成工作流

做接口测试这件事&#xff0c;越往后越会发现&#xff1a;单条请求的"点一点、看响应"根本撑不起真实项目的回归需求。尤其是当接口数量破百、环境从开发切到测试再切到生产、每次发版前要人工过二三十条核心链路的时候&#xff0c;效率和安全完全靠当时的专注度硬撑…

作者头像 李华
网站建设 2026/11/4 23:05:50

智慧工业园区落地指南:从PPT架构到工程实施的全链路拆解

简介&#xff1a;本资源是一份面向工业园区管理者、智能化系统集成商及数字化转型实践者的72页PPT课件&#xff0c;系统阐述智慧工业园区智能化整体解决方案&#xff0c;聚焦环境监测、能耗管理、智慧路灯与无人清洁车四大核心模块&#xff0c;助力工业区实现节能降耗、环保合规…

作者头像 李华
网站建设 2026/11/2 1:44:21

净化板选购核心要点:基材、芯材与密封性如何盯死

干活这么多年&#xff0c;跑过不少净化板生产厂家的车间&#xff0c;也帮朋友验收过几回洁净室材料。说句实在话&#xff0c;净化板这个行业门槛看着不高&#xff0c;但里面的门道比大多数人想的多得多。同样标着“岩棉净化板”的东西&#xff0c;不同厂家出来的价格能差三分之…

作者头像 李华
网站建设 2026/10/27 3:22:18

树莓派USB启动原理与工业级实践指南

1. 为什么不用SD卡&#xff1f;Pi USB Boot不是“换根线”那么简单Raspberry Pi USB Boot——这个标题里藏着一个被绝大多数人低估的底层能力&#xff1a;让树莓派跳过SD卡&#xff0c;直接从USB设备启动操作系统。关键词里没写&#xff0c;但所有实操者心里都清楚&#xff0c;…

作者头像 李华
网站建设 2026/10/23 22:57:05

WPF+SQLite个人记账系统:C#桌面端真实业务落地实践

简介&#xff1a;这是一套基于C#与WPF开发的完整个人记账系统源码&#xff0c;面向.NET初学者及桌面应用开发者&#xff0c;聚焦财务数据管理场景&#xff0c;帮助学习者掌握现代Windows桌面应用的工程化开发流程。资源共62个文件&#xff0c;包含31个C#业务逻辑文件、7个XAML界…

作者头像 李华