
1. 为什么房屋租赁系统老用SpringBootVue这套组合先说个现象。在个人开发者圈子里凡是涉及管理系统类型的练手项目十个里有七八个都是SpringBootVue这对组合。这次这个基于SpringBootVue的房屋租赁管理系统也不例外后端MyBatis国产化ORM中间接MyBatis底层MySQL存数据。之所以这个搭配能成为固定搭配核心原因是这套技术栈把“能跑起来”的门槛压得非常低同时又能覆盖到企业开发里的绝大部分真实场景。一个合格的房屋租赁系统本质上要解决的只有三件事房源管理、租客合同、账单收租。听起来简单但真正落库之后你会发现这些业务之间的关联关系远比预想的复杂。一套房子可能经历过多个租客一个租客名下可能有多份合同每份合同又关联着多笔水电费和押金记录。如果只做表面的增删改查那确实随便什么框架都能做但要让这种系统真正被经纪人、房东、租客这三类人长期使用就必须在权限、状态流转、费用计算这些地方下功夫。我个人的看法是选择SpringBootVue并不是因为它有多高级而是因为这组合生态成熟到了一种“傻瓜都能搜到答案”的程度。遇到任何报错把错误信息往搜索框一贴基本都能找到对应的解决方案。这种可维护性和周边支持对独自开发、尤其是刚入门不久的学生党来说比任何技术先进性都实在。这里顺便聊下为什么不用Spring Cloud那套微服务或者为什么不直接上Vue3TS。房屋租赁这种业务量级的系统单体应用完全够用上微服务纯属给自己找麻烦。Vue2和Vue3的选择上如果你是从零开始而且没有历史包袱那就直接Vue3生态已经很成熟了。不过这次项目如果沿用Vue2的写法其实也不影响核心功能后面想升级再整体迁移也行前期别被工具选择卡住进度。2. 业务模块拆解房东、租客、房源、账单各自管什么2.1 四类角色四个入口权限边界要先画清楚做管理系统之前第一件事不是写代码是把角色的权限边界想清楚。这个房屋租赁系统至少要区分四种角色系统管理员、房东、租客、中介或者叫运营人员。很多半成品项目做出来被人吐槽“就是个数据库的外壳”就是因为所有角色进去看到的菜单一摸一样权限形同虚设。我建议的权限模型是这样的系统管理员管全局能看到所有房东、租客、房源和账单数据负责平台层面的配置房东只能看到自己名下挂的房源以及这些房源对应的合同和收款记录租客只能看到自己签过的合同以及待缴账单和缴费历史中介作为可选角色可以代操作房源挂牌和合同录入但无权修改账单金额。这套权限模型实现起来并不复杂。前端根据登录后返回的角色字段动态生成路由菜单后端在Controller层用拦截器做接口级别的权限校验。真正的难点在于数据隔离也就是房东只能查到自己名下的房子这个不能只靠前端藏菜单后端必须强制带上tenant_id或者owner_id的查询条件否则直接构造HTTP请求就能越权拿到别人数据。2.2 房源、合同、账单三大核心模块的状态流转租赁系统的房源状态大致分这几档空置中、已预定、已出租、已下架。每套房源从空置开始经过租客看房下单后进入预定签约成功后就变成出租中合同到期退租后又回到空置状态。坑点在于很多新手会把状态建模成简单字符串存放在房源表里这样在展示层确实方便但在修改状态时容易出现并发问题两个操作员同时操作同一套房源就会出bug。我的做法是给房源状态建一个独立的状态表或者至少在代码里定义一个状态字段加上版本号用乐观锁去更新状态。比如签约时先执行update room set status2 where id? and status1影响行数为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 idselectRoomPage resultTypecom.example.entity.Room SELECT * FROM room where if testcommunityName ! null and communityName ! AND community_name LIKE CONCAT(%, #{communityName}, %) /if if testorientation ! null and orientation ! AND orientation #{orientation} /if if teststatus ! null AND status #{status} /if if testminRent ! 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连接串上记得加上rewriteBatchedStatementstrue这个参数否则批量操作优化不会真正生效这个参数面试或者排查问题都经常被问到。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.00000: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个小时账单到期日全都不对。推荐连接串参数里显式加上serverTimezoneAsia/Shanghai。第二检查配置文件里有没有把数据库密码、密钥等敏感信息直接写死在代码里如果这是给别人演示的项目源码至少把生产环境的密码放到环境变量里加载。第三确认打包时前端路由是hash模式还是history模式。hash模式刷新不会404history模式就必须在nginx里配try_files兜底否则用户直接在非首页刷新页面会看到白屏。这几条看着都是一两句话的小事但真出问题时排查的时间都是按小时计算的。6. 项目开发中的常见问题排查清单做这套系统过程中我整理了印象最深刻的问题和排查方法分享出来能碰上就直接抄作业。第一类是启动即报错大概率是端口占用或者依赖冲突。看日志最下面的Caused by定位到具体原因。数据库连不上基本就是账号密码错了、MySQL服务没启动、或者少了mysql-connector-java依赖。第二类是接口返回500但控制台没打印SQL这种多见于MyBatis的Mapper映射问题XML里的resultType写错或者数据库字段名和实体类属性映射不上。排查时先打开MyBatis的SQL日志application.yml里配置mybatis.configuration.log-implorg.apache.ibatis.logging.stdout.StdOutImpl把SQL和参数值打出来看是不是SQL本身的问题。第三类是前端白屏。原因无非三种JS文件加载失败、路由匹配不到、依赖缺失。用浏览器F12看Console和Network基本能定位。最常见的坑是Vue3中用的API和Vue2不同混用导致报错排查时看一下控制台具体报的是哪个方法未定义就行。第四类是中文乱码。页面显示乱码多半是HTML或JS文件编码问题接口返回中文乱码检查数据库连接串是否加了characterEncodingutf8数据库本身乱码检查建库建表的charset。整体记住一条原则从数据库到后端到前端链路每一段的编码都要是UTF-8。这些排查经验不是一天积累起来的项目做多了自然就能形成肌肉记忆。你多碰几次这些报错以后看到日志前几行就知道怎么处理了。7. 这套源码拿到手之后怎么改造成自己的项目最后聊聊拿到源码之后怎么二次开发。先别急着改代码第一件事是把项目完整跑通一遍用管理员账号把所有页面都点一遍知道每个页面在干什么然后再动手。然后全局搜索项目里的公司名、版权信息等占位内容改成自己的名字或学校信息特别是页面的logo、侧边栏标题和浏览器Tab的标题这些细节最容易被忽视。改造的优先级我认为是这样先改用户管理这是系统的根基把账号体系改成自己需要的角色再改房源字段根据实际项目里小区户型情况增删字段然后改账单规则比如把按月收取改成按季度、或者增加自定义应收项目最后加导出功能生成Excel账单明细房东最需要的功能。改完后再整体回归测试一遍确认没有破坏原有功能。整个项目做完之后我最大的感受是技术不难难的是把所有模块的细节都考虑周全。你把这个项目完整的过一遍从数据库设计到后端接口再到前端联调抵得上刷好几十道面试题因为面试问的项目经验、业务场景、问题排查你全都有真实案例聊了。