
1. 系统设计思路与技术选型1.1 为什么是SpringBoot Vue MyBatis MySQL这个组合说实话房屋租赁管理系统算是前后端分离项目里最经典的一类了需求清晰、业务链路完整、做起来不会太复杂但又能把主流技术栈的要点都覆盖到。如果你正在找一套能真正跑起来、能写进简历里、也能作为毕设或课程设计的完整项目这个方向非常合适。整套系统的技术底座就是标题里那四样SpringBoot、Vue、MyBatis、MySQL。先说SpringBoot。它解决了传统SSM项目里大量XML配置的问题起步依赖、自动配置、内嵌Tomcat这三个特性让项目在几分钟内就能启动起来。做单体服务端开发SpringBoot基本是事实标准。房屋租赁系统的核心后端逻辑——用户权限、房源CRUD、合同状态流转、账单生成——本质上都是标准的业务接口用SpringBoot来承载再合适不过。再说前端Vue。前后端分离之后前端不再是后端的模板文件而是一个独立的工程有自己的路由、状态管理、组件系统。Vue的响应式机制和组件化开发方式让房源列表、筛选条件、表单弹窗、分页操作这样的交互做起来非常顺手。Vue Router管理页面跳转Vuex或Pinia管理登录状态、用户信息这些全局数据。对于这类中小型管理系统Vue的渐进式特性让团队既能快速上手又能保持工程化结构的清晰。至于MyBatis和MySQL这里我要多说一句。很多初学者会纠结“MyBatis还是MyBatis-Plus”“用不用JPA”但实际做业务系统MyBatis最大的优势是SQL写在自己手里尤其是房源多条件查询、账单统计、合同明细联查这种场景SQL的可控性太重要了。配合MySQL这种稳定成熟的关系型数据库只要表结构设计合理这套组合应付中小型业务量绰绰有余。整个项目如果想要一个通俗理解后端是餐厅的厨房负责处理业务规则前端是门面负责接待用户数据库是储物间所有食材和账本都归它管。三者通过提前约定好的接口协议配合缺一不可。1.2 前后端分离架构到底在解决什么问题很多人第一次听到“前后端分离”这个词以为只是前端和后端分两个文件夹写代码其实没这么简单。真正的分离指的是页面渲染和逻辑处理彻底解耦。传统的JSP或Thymeleaf方式前端页面由后端渲染HTML里嵌着Java代码改了页面要重启服务前端设计师和后端工程师被迫耦合在一个工程里。前后端分离之后前端是纯静态资源运行在用户的浏览器里通过HTTP协议调用后端的JSON接口双方只靠接口文档约定来协作。这样做的好处非常直接。首先是分工协作更顺畅前端专注于页面交互后端专注于业务逻辑两边可以同时开发。其次是部署更灵活前端资源丢到Nginx之类的静态服务器上就能跑后端服务可以独立横向扩展。最后是体验更好路由切换、局部刷新、加载动效这些交互不再需要整个页面刷新Vue这类SPA框架天生擅长做这种事。对应到房屋租赁系统里典型的工作方式是Vue前端启动后会加载登录页用户输入账号密码前端把数据POST到/api/auth/login接口后端校验通过后返回一个token前端把token存下来带上后续每个请求。后端不关心页面长什么样只关心接口被人以正确的方式调用。走通这一整套交互链路你就真正理解了前后端分离的价值而不是只会在IDE里分别启动一个前端项目和一个后端项目。1.3 系统功能全景与角色划分做项目之前先把功能边界理清楚是保证项目不失控的第一步。房屋租赁管理系统按照业务流程来看核心参与者有三种角色管理员、房东经纪人、租客。管理员负责审核房源、管理用户、查看平台运营数据房东负责发布房源、管理自己的房产、跟进租约和账单租客负责浏览房源、预约看房、发起租赁申请、在线签订合同、查看账单并缴费。围绕这三种角色系统功能可以划分成几个大的模块功能模块核心功能点对应角色用户与权限注册登录、角色分配、用户管理、密码重置管理员房源管理房源发布、编辑上下架、区域筛选、租金和户型条件检索房东、租客看房预约在线提交预约、房东确认、状态流转租客、房东合同管理合同生成、签署确认、状态变更待签/生效/到期/终止三方账单管理月度账单自动生成、缴费记录、逾期标记租客、房东平台管理房源审核、数据统计、用户反馈处理管理员这套功能划分在代码层面也能形成清晰的模块边界后端每个模块对应一个Controller前端每个模块对应一个页面目录调试和扩展都会方便很多。2. 核心模块分解与数据库设计2.1 数据库表结构设计要点数据库设计是业务系统的基础这个环节如果出问题后面所有的查询和统计都会很难受。我在这套系统里设计了这样几张核心表用户表、房源表、租客信息表、合同表、预约记录表、账单表、收藏表。用户表承担的是认证和权限功能字段包括id、username、password、role、phone、create_time。密码字段一定要存加密后的结果不能明文落库。这里我用了BCrypt加密它在校验时能自动从密文中提取盐值对开发者来说几乎是无感的。房源表是整个系统的核心表字段设计上要兼顾检索和展示两方面的需求。title存房源标题cover_img存封面图URLaddress存详细地址area存建筑面积price_month存月租金house_type存户型描述status存房源状态待审核、已上架、已出租、已下架另外还要有owner_id跟用户表关联表示这套房子的房东是谁。这里有一个经验租金、押金这类金额字段务必用DECIMAL(10,2)不要用FLOAT或DOUBLE否则精度问题很容易在账单计算时爆发。合同表和账单表是连接业务闭环的关键。合同表记录哪套房、哪个租客、租期起止、月租金、押金状态字段维护合同的整个生命周期。账单表可以由定时任务在每个月月初根据“生效中合同”自动生成包含账期、金额、状态待缴费、已缴费、逾期。索引方面房源表的status、area、price_month会被频繁放在WHERE条件里适合建普通索引合同表的house_id和tenant_id因为有大量联查需求也要建索引。设计时请记住一个原则宁可少建几个不常用的索引也别给每个字段都加索引写多读少时会拖垮性能。2.2 登录鉴权逻辑的实现与思考登录鉴权是前后端分离项目里绕不开的话题。传统单体Web应用依赖Session服务器保存会话状态客户端靠Cookie传递会话ID。但前后端分离之后接口可能被多个端调用而且要求后端接口最好是无状态的方便独立部署和扩展所以这里普遍采用JWT方案。JWT的工作流程是用户登录成功后后端生成一个包含用户ID、用户名、角色等信息的token字符串返回给前端前端存进localStorage或sessionStorage之后每个请求在请求头里带上Authorization: Bearer token。后端通过拦截器解析token确认身份和权限后放行。这么做的好处是服务器不需要保存会话状态天然支持横向扩展。还要注意一个细节密码校验不能直接在Controller里做正确的做法是登录接口接收用户名和密码后调用Service层先从库里查出用户用BCrypt校验密码校验通过再生成token。整个过程即使校验失败也要返回统一的错误对象避免把内部异常直接抛给前端。如果有“记住我”的需求只需把token的有效期调长比如设置30天过期即可。2.3 房源多条件检索MyBatis动态SQL实战房源列表页看起来只是一个列表但它的后端接口是整个系统里最体现MyBatis功力的地方。租客筛选房源时经常同时输入多个条件城市、区域、最低租金、最高租金、户型、朝向、房源状态而且这些条件是可选的。如果用传统的SQL拼接你会被各种and、where、空值判断搞得头大。MyBatis的动态SQL机制就是为了解决这个问题。核心写法是where标签配合if test...条件判断select idsearchHouses resultTypecom.example.entity.House SELECT h.*, u.nickname AS owner_name FROM house h LEFT JOIN user u ON h.owner_id u.id where if testcity ! null and city ! AND h.city #{city} /if if testminPrice ! null AND h.price_month gt; #{minPrice} /if if testmaxPrice ! null AND h.price_month lt; #{maxPrice} /if if testhouseType ! null and houseType ! AND h.house_type #{houseType} /if if teststatus ! null AND h.status #{status} /if /where ORDER BY h.create_time DESC LIMIT #{offset}, #{pageSize} /select这里最关键的是where标签它会智能判断如果所有条件都不满足它不会生成WHERE关键字如果有条件满足它会自动去掉第一个条件的AND前缀。很多人写动态SQL时遇到“条件不生效”的报错十有八九是参数没有正确传入Mapper或者test里的属性名跟实体类字段名不一致排查时先打印SQL确认入参再看结果。这套写法配合PageHelper分页插件就能实现一个稳定、高效的房源筛选接口也是把热词“mybatis条件不生效”的坑提前帮你踩平了。2.4 合同和账单的流程流转设计合同模块是房屋租赁业务里最容易出需求变更的地方所以在设计时要留足状态字段。我的做法是给合同表增加一个status字段枚举值为0待签署、1生效中、2已到期、3已终止。租客发起签约请求后合同状态是待签署房东确认后变为生效中合同到期时由定时任务自动置为已到期如果中途退租则手动改为已终止。这种状态机设计的好处是后续做统计报表比如在租合同数、到期提醒会非常顺手。账单则是合同生效后自动生成的。可以写一个定时任务每个月1号扫描所有“生效中”的合同按合同月租金生成当月账单记录。账单表包含contract_id、period账期、amount、status、pay_time。租客在账单列表看到待缴费记录点击缴费后更新状态并记录支付时间。如果不想接第三方支付可以用“手动标记已缴费”的方式由管理员或房东在后台确认收款。逾期判断也很简单账单的账期加上宽限期如果当前时间晚于截止时间且状态仍是待缴费列表页把这个账单标红提醒租客尽快处理。3. 后端到前端核心链路实操过程3.1 后端工程搭建与分层规范后端工程的骨架非常重要直接决定后续开发效率。我推荐按这种分包方式组织代码com.example.rental ├── controller // 接收请求、返回结果 ├── service // 业务逻辑层 │ └── impl // Service实现类 ├── mapper // MyBatis数据访问层 ├── entity // 实体类对应数据库表 ├── dto // 数据传输对象接收前端参数 ├── vo // 视图对象返回前端数据 ├── config // 配置类跨域、拦截器、全局异常 └── common // 统一返回体、常量、工具类Controller层只负责参数接收和结果封装不在里面写业务逻辑Service层做业务编排比如发布房源时要校验租房者身份、判断房子状态、写房源表Mapper层只做最简单的数据访问。这种分层的核心目的是清晰和可测试性这也和SpringBoot倡导的规范是一致的。工程创建我建议直接用Spring InitializrJava版本选8或11如果选择的SpringBoot版本太高导致启动报错回退到2.7.x通常是最稳妥的。关键的配置有三个一是MyBatis的驼峰映射在application.yml里设置map-underscore-to-camel-case: true数据库的create_time字段才能正确映射到实体的createTime属性二是SQL日志打印设置mybatis.configuration.log-impl: org.apache.ibatis.logging.stdout.StdOutImpl开发时可以直接在控制台看到完整SQL排错效率翻倍三是端口配置比如server.port: 8080如果你的本机8080被占用了记得改掉。3.2 发布房源核心接口开发从参数到SQL以“发布房源”这个接口为例完整链路可以拆成五步。前端表单提交JSON数据到/api/house/add后端Controller用RequestBody接收转成HouseDTO。DTO里要带上JSR303参数校验注解比如NotBlank、DecimalMin这样非法参数在进入业务逻辑前就被拦下了。校验通过后交给Service层的addHouse实现方法先根据当前登录用户获取房东ID再设置房源初始状态为“待审核”最后调用Mapper的insert方法写入数据库。如果一切顺利返回统一结果对象Result.success()前端收到后清空表单并提示“发布成功等待管理员审核”。这套流程看起来有点绕但每一步都有足够的理由。DTO和Entity分离是为了不让前端传参直接污染数据库实体统一返回体Result是为了让前端判断响应时只要看code字段即可规范接口风格全局异常处理器可以统一捕获业务异常返回用户看得懂的提示而不是一长串堆栈错误。我写项目时都会把统一返回体、全局异常处理、跨域配置这三个基础设施先搭好后面的接口开发就只是机械而快速的业务填充。3.3 Vue前端工程搭建与路由配置前端工程我选Vue 3 Vite Element Plus这套组合如果项目本身用的Vue 2那Element UI也挺好。Vite的冷启动速度比Webpack快很多开发体验非常好。安装依赖用npm install遇到版本冲突就删掉node_modules和package-lock.json重新装一次这个操作能解决绝大多数依赖问题。路由配置是前端的骨架我的一组典型配置长这样const routes [ { path: /login, component: Login }, { path: /, component: Layout, children: [ { path: , component: Home, meta: { requiresAuth: true } }, { path: house/list, component: HouseList }, { path: house/detail/:id, component: HouseDetail } ] } ]路由守卫里做登录校验访问Vue页面之前先检查localStorage里有没有token如果没有跳回登录页。这种守卫逻辑配合后端拦截器就构成了双层防线。前端拦截器靠router.beforeEach后端拦截器靠Spring拦截器即使有心人绕过前端直接调用接口也会被后端拦下来。axios封装也是必做的。请求拦截器统一注入token响应拦截器统一处理业务码和HTTP错误状态比如token失效返回401时就强制退出登录避免用户带着过期凭证在系统里反复点击。这部分代码量不大但对工程规范性的提升是质的飞跃。3.4 前后端联调与跨域问题实录联调阶段是前后端分离项目最磨人的部分最大的坑基本集中在跨域、参数格式、日期格式三个方面。开发环境下前端跑在http://localhost:5173后端跑在http://localhost:8080端口不同浏览器会触发跨域拦截。解决方案有两种一种是后端开启CORS在Spring配置类里addCorsMappings另一种是前端Vite配置代理把/api开头的请求转发到http://localhost:8080。这两种我都试过开发期更推荐前端代理因为它不需要后端改代码也不会影响生产环境。部署阶段则交给Nginx统一做反向代理这个后面会细说。参数格式问题主要集中在日期时间上。后端返回的LocalDateTime默认格式是带T的ISO格式跟前端组件里的显示要求对不上。我一般有两种处理方式一是在实体类的日期字段上加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)注解简单直接二是全局配置Jackson的日期格式一处生效。推荐第二种不用每个字段都加注解。联调时每次改完接口前端要能立刻看到效果。建议后端把改了哪些字段、请求返回是什么样都先在控制台里看清再跟前端确认。我整理过一张联调高频问题速查表列几个最常见的现象排查方向前端请求报404检查后端的Controller路由前缀是否加了/api前端请求路径是否匹配前端请求报401检查请求头有没有带token、token是否过期后端收到JSON但字段全是null检查前端是否设置了Content-Type: application/jsonDTO字段名是否匹配跨域报CORS错误后端CORS配置或前端代理是否生效端口是否一致查询时间不对检查Jackson时间格式配置、数据库时区和JVM时区是否一致4. 打包部署、上线运行与避坑经验4.1 从开发环境到生产发布的环境准备项目在自己电脑上能跑跟部署到服务器上能跑中间隔着几条很深的河。最基础的一步是数据库环境。如果你在Windows上安装MySQL 8.0建议装好之后打开命令行验证一下mysql -uroot -p能不能正常连接常见问题是忘记初始化密码、3306端口被占用、字符集没有设置成utf8mb4。新建数据库后用Navicat或命令行执行SQL脚本把表和初始数据一次性导入。强烈建议在项目源码里同时准备一份init.sql包含建表语句和基础测试数据这样换一台电脑部署时不用从头造数据。然后是后端打jar包。在项目根目录执行mvn clean package -DskipTests在target目录下会生成可执行的jar包。本地用java -jar xxx.jar验证一次能正常启动再往服务器上丢。打包时有个细节application.yml里的数据库地址、账号密码可以通过环境变量覆盖默认值比如写成${DB_HOST:localhost}部署时就不用改代码直接在启动命令里注入生产环境参数。4.2 前后端分离部署Nginx与Java服务配合正式环境部署我的标准做法是后端jar包直接跑在服务器的Java环境里用nohup命令后台运行前端打包后的dist目录放到Nginx的静态目录下并把所有/api请求反向代理到后端地址。Nginx的核心配置片段长这样server { listen 80; server_name your-domain.com; root /usr/share/nginx/html; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { try_files $uri $uri/ /index.html; } }这里最关键的配置是最后那个try_files指令。Vue Router默认用history模式如果直接访问/house/detail/5这个地址后端其实没有这个页面Nginx会返回404所以要统一重写到index.html由前端路由接管渲染。这个坑几乎每个部署前后端项目的人都会遇到一次配置里写上try_files $uri $uri/ /index.html问题就解决了。后端进程管理方面用nohup java -jar rental-system.jar app.log 21 可以启动并记录日志但更推荐用systemd做服务托管开机自启、崩溃重启都由系统管理省心很多。4.3 常见启动错误与全家桶排查手册我把这类项目从开发到上线最容易踩的坑汇总成了一张速查表对照排查能省下大量时间错误现象可能原因解决方案Java服务启动后立即退出端口被占用换端口或netstat -ano找占用进程并杀掉启动时连接数据库失败MySQL未启动、密码错误、数据库名不对先命令行本地连接测试再检查application.ymlMyBatis报SQL语法错误动态SQL拼接条件写错打开控制台SQL日志把实际生成的SQL复制到数据库执行验证前端运行npm run build失败依赖版本冲突删除node_modules用固定的依赖版本重新安装前端页面访问404Nginx未配置try_files在location /下补充配置请求/api返回504Nginx代理地址写错或后端服务没起来检查proxy_pass服务器本地curl后端地址验证登录后接口返回401token过期或拦截器配置问题检查token有效期和拦截器排除路径配置图片上传显示不了静态资源路径配置错误确认图片上传目录和Nginx静态资源映射一致中文乱码字符集配置不一致数据库连接URL加useUnicodetruecharacterEncodingutf8上面每一个我都实地踩过其中数据库连接失败的发生率是最高的十次部署有六次是栽在这里。建议无论什么时候先把数据库连接这个环节单独跑通再去启动Java服务会少走很多弯路。4.4 个人实操心得与这套系统的扩展方向老实说我在做这个项目之前已经见过太多“源码下载下来跑不起来”的案例。根源往往不在代码本身而在版本、配置和路径这三样东西上。所以如果你也是刚拿到这套源码我的建议是别急着改功能先把原封不动的项目跑起来从前到后走通一遍“注册、登录、发房源、看房源、签合同”的完整流程再对照源码去理解每个模块是怎么协作的最后才动手加自己的需求。整个过程里SpringBoot和Vue的版本选型一定要保持一致用项目自带的版本号最保险数据库严格按项目里的SQL脚本初始化别擅自改表名和字段名否则MyBatis映射会直接罢工。项目跑通之后的下一步你可以考虑把这些方向加进去用Redis缓存热门房源列表、给合同模块增加PDF预览、制作一个管理后台的数据看板、把文件上传接到对象存储服务。这套系统的骨架足够清晰往任何一个方向扩展都不会太别扭。