☰
前后端分离租房平台:SpringBoot+Vue从设计到部署全流程实战
2026/10/17 4:09:57 网站建设 项目流程

前后端分离的大学生在线租房平台,是这两年出现频率很高的毕设和练手项目。搜索引擎里一搜一大把,但很多同学卡在同一个地方:源码拿到了,环境配不明白,数据库导不进去,前端代理配错,最后跑起来看到的不是登录页而是满屏报错。这篇文章我想换个方式,不贴一份所谓的"完整源码"就完事,而是把从技术选型、数据库设计、接口联调,到最后部署上线的全过程拆开讲,重点讲清楚每一步为什么要这么做。项目本身是SpringBoot + Vue + MyBatis + MySQL的组合,前后端分开部署,角色覆盖学生租客、房东和管理员三种身份。如果你正打算做类似的租房系统,或者刚接手一份源码想让它在本地和服务器上顺利跑起来,这篇内容可以直接当作参照。

1. 技术栈组合:这套开发框架为什么在租房类项目里经久不衰

1.1 后端用SpringBoot而不是其他框架的原因

我做这个项目时,后端基本没犹豫就选了SpringBoot。原因很直接:生态成熟,坑少,资料多。SpringBoot把过去Spring MVC时代繁琐的XML配置全部简化掉了,依赖管理交给Maven或Gradle,内嵌Tomcat直接让项目变成一个可以独立运行的Jar包。对于在线租房平台这种典型的CRUD加业务状态流转的Web系统,单体应用完全够用,不需要引入微服务体系给自己增加复杂度。

有人会问,现在主流不都是Spring Cloud那套微服务吗,怎么还在用单体?租房平台的业务量级和团队规模摆在那里,单体架构能把开发、测试、部署的成本压到最低。真到了需要拆分的时候,按用户、房源、订单这几个域去拆也不晚。SpringBoot的单体结构反而让整个项目显得干净,评审老师或者面试官问起来也好解释。

1.2 Vue负责的界面层与前后端分离的本质

前端我选了Vue,配合Vue Router做页面路由、Vuex或者Pinia做状态管理,再搭配Element Plus这一套组件库。选Vue的很大一部分原因是它对国内开发者太友好了,中文文档齐全,组件生态完善,学习曲线比React要平缓不少。

前后端分离这个概念,很多人理解成"前端一套代码,后端一套代码"。这没说错,但不完整。真正的分离是:前端只负责渲染和交互,通过Ajax请求后端接口拿JSON数据;后端只负责处理业务逻辑和读写数据库,不关心页面长什么样。两边用HTTP协议沟通,接口就是契约。开发过程中,只要接口文档定义清楚了,前端和后端可以并行开工,不必等对方完成。部署上两者也完全解耦,前端构建出静态文件扔给Nginx托管,后端打成Jar包独立运行。

这种结构放在在线租房平台里,最直观的体验就是页面响应速度快,因为静态资源由Nginx直接返回,后端只处理真正的业务请求。

1.3 MyBatis + MySQL:数据层选型理由与配合方式

数据层我用了MyBatis和MySQL。有些项目喜欢用MyBatis-Plus,确实能省不少代码,基础的增删改查不用手写SQL。但我做这个项目刻意用了原生MyBatis,原因是租房平台的查询逻辑不算简单:房源搜索需要多条件组合、排序需要动态拼接、统计报表要写聚合SQL。MyBatis的动态SQL能力在应付这种场景时非常顺手,而且SQL是自己写的,出问题时一眼就能定位。

MySQL的选择没什么悬念。它是开源数据库里最普及的关系型数据库,事务支持靠谱,性能在单机场景下完全够用,校园项目甚至中小型商用系统的数据量都不会构成压力。MyBatis和MySQL的配合方式很直接:MyBatis负责把Java对象和SQL语句映射起来,SQL写在Mapper.xml文件里,数据库连接用Druid或者HikariCP连接池管理。

对比维度SpringBoot + MyBatisSpringBoot + JPA
复杂查询控制力SQL自己写,完全可控需要折腾Specification或注解SQL
上手难度需要会写SQL,门槛略高简单CRUD极快
SQL调优路径直接看执行计划需要理解框架生成的语句
适合场景业务规则清晰、查询复杂快速原型、领域模型驱动

对租房平台这类业务来说,SpringBoot + MyBatis + MySQL是性价比最高的组合,这也是它成为"经典毕设四件套"的原因。

2. 三种角色与业务闭环:从账号注册到房源成交的完整链路

2.1 用户角色划分与功能矩阵

在线租房平台最核心的价值是撮合房东和租客。我的项目里把用户分成了三种角色,注册时通过下拉框选择,后端用角色字段区分权限。

学生租客端的核心功能是找房:登录注册、按城市区域价格搜索房源、查看房源详情和图片、收藏意向房源、提交看房预约、下单签约租房订单。房东端负责供给端:可以发布房源、管理自己名下的房源上下架、查看和处理租客预约、确认订单并生成合同信息。管理员是平台的裁判,负责用户管理、房源审核、公告发布和基础数据统计。

这三种角色形成了完整的业务闭环。没有管理员审核的房源平台,很容易被广告和虚假房源淹没;没有预约和订单流程,租客和房东之间就停留在"看个联系方式自己聊"的原始状态,谈不成真正意义上的平台交易。

2.2 权限控制的实现思路:JWT与路由守卫

多角色系统最怕权限漏洞。这个项目我用的是JWT + 后端拦截器 + 前端路由守卫的组合方案。

用户登录成功后,后端根据用户Id和角色生成一个带有效期的JWT字符串返回给前端。前端把Token存到localStorage里,每次请求在Axios拦截器中自动带上Authorization请求头。后端写一个拦截器,对除登录注册外的所有接口校验Token,解析出用户信息后放到ThreadLocal里,这样后续业务代码随时能拿到当前登录用户。

前端这边的路由守卫也很关键。Vue Router的beforeEach钩子会检查目标路由的meta信息,如果页面要求登录但没有Token,直接重定向到登录页。管理员的页面还会多校验一次角色,普通用户即使手动输入管理路径也会被拦下来。

下面是一个简化版的路由守卫写法,核心逻辑就这几行:

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.meta.requiresAuth && !token) { next('/login') } else if (to.meta.role && localStorage.getItem('role') !== to.meta.role) { next('/403') } else { next() } })

后端的Service层还会再做一次数据权限校验。房东只能修改和删除自己发布的房源,订单操作必须校验订单归属人。前后端双重校验,这是实打实的安全习惯,不能省。

2.3 房源审核机制与业务状态机

做租房平台之前我想过一个问题:房东随便发一套不存在的房子怎么办?所以房源表里必须有一个审核状态字段,我设计成了0待审核、1已上架、2已下架、3审核不通过四种状态。

房东提交房源后,状态是0,租客端搜不到,只有管理员在后台能看到。管理员审核通过变成1,房源才进入公开展示列表。房东可以主动下架房源变成2。如果审核被判不通过,状态是3,房东需要修改后重新提交。

订单模块也有自己的状态流转。预约看房后,记录状态从未处理到已联系、已完成。正式下单后,订单状态包含待支付、已支付、已完成、已取消。状态多的模块,我会在Java里定义一个枚举类来管理,Java和数据库里存的都是数字,展示名由前端根据状态值映射成文本。这样一来,逻辑清晰,后续如果要增加新状态只需要在枚举类里加一个值。

3. 数据库设计:用8张核心表支撑起整个租房平台

3.1 数据模型概览与E-R关系说明

数据库设计好坏,直接决定后续开发是顺风顺水还是到处打补丁。我在建表之前先在纸上画了实体关系图,梳理下来核心表一共8张:用户表、房源表、房源图片表、收藏表、看房预约表、租赁订单表、系统公告表、留言反馈表。

用户表和房源表是一对多的关系,一个房东可以发布多套房源。房源表和图片表也是一对多,因为一套房子会有封面图、卧室图、客厅图等多张照片。收藏表、预约表、订单表本质上都是不同场景下的关联表,分别把用户和房源以不同目的连接起来。

我在建表的时候特意没有加物理外键。原因很实际:物理外键会让表之间的耦合变得很重,插入数据时必须按顺序,批量导入也麻烦。更关键的是,租房平台的房源会做逻辑删除,如果外键存在,删除和改状态时处处受限制。用逻辑外键,也就是业务字段保存关联Id,配合代码层面的约束,灵活度更高,性能和可维护性都更好。

3.2 核心表的字段级设计详解

我把核心字段设计列出来,直接看表结构比空谈理论要直观得多。

用户表 user:

字段类型说明
idbigint主键,自增
usernamevarchar(50)登录名,唯一索引
passwordvarchar(100)BCrypt加密后的密码
nicknamevarchar(50)昵称
phonevarchar(20)手机号
avatarvarchar(255)头像图片路径
roletinyint1学生 2房东 3管理员
statustinyint1正常 0禁用
create_timedatetime注册时间

房源表 house:

字段类型说明
idbigint主键
user_idbigint房东用户Id
titlevarchar(100)标题
covervarchar(255)封面图路径
areadecimal(10,2)面积,单位平方米
pricedecimal(10,2)月租金
rent_typetinyint1整租 2合租
cityvarchar(30)城市
districtvarchar(30)区域
addressvarchar(255)详细地址
descriptiontext房源描述
statustinyint0待审核 1已上架 2已下架 3审核不通过

金额字段必须用Decimal类型,不能用Float或者Double,二进制的浮点数在金额运算中会丢精度,这属于基础常识但特别容易踩。状态字段统一用Tinyint,存数字而不是字符,省空间,查询也快。

房源图片表 house_image 单独建表的原因是图片数量不确定,一套房源可能三五张也可能十来张。如果全部拼进房源表里,字段会非常僵化。单独建表后查询时按house_id查全部图片,按sort字段排好序,主图用is_main字段或直接用表里第一条记录。

收藏表 favorite 设计了一个唯一索引,组合字段是user_id和house_id。这样同一个用户对同一套房子的收藏记录在数据库层面就不会出现重复,不需要在代码里做额外的存在性判断,安全性更高。

3.3 索引与查询优化:让列表页和搜索不卡顿

在线租房平台查询压力最大的场景是房源列表页和搜索功能。用户按照城市、区域、租金范围、租赁方式去筛房源,如果表里没有合适的索引,全表扫一遍,房源数据量稍微上来页面就会明显变慢。

房源表的索引我做了三个:status和city的联合索引,因为"筛选某个城市的已上架房源"是非常高频的查询;价格字段单独建索引,方便按租金排序和范围筛选;user_id上建普通索引,房东查询自己名下房源时会用到。

除了索引,列表查询还有个关键优化:上架状态的房源每次都要查,但审核中、已下架的数据不该出现在租客端的结果集里。SQL里始终带上status = 1这个条件,配合联合索引,就有很好的效果。

有一类常见错误是在SQL里写类似 WHERE status = 1 OR city = ? 这样的条件,OR一旦出现,索引优化器往往会放弃最优执行计划。我会习惯性地把OR拆成递归查询或者改成IN,这个细节在做列表页时最好留意一下。

4. 前后端联调实战:跨域、精度、日期与图片上传的四个坎

4.1 跨域问题的两种解法与各自适用场景

前后端分离项目跑起来之后,遇到的第一个大问题多半是跨域。浏览器的同源策略规定,页面和接口的协议、域名、端口只要有一个不一致,Ajax请求就会被拦截。本地开发时前端在Vite的5173端口起服务,后端在8080,端口不一致,天然跨域。

我的开发环境解决方案是配置Vite的Proxy代理,前端请求统一走 /api 前缀,Vite把它转发到后端的8080端口。浏览器始终认为自己在请求同一个源的地址,根本不会触发跨域。

// vite.config.js server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }

生产环境里的解法就交给Nginx反向代理。前端静态文件和后端接口都由Nginx对外统一暴露,同样是同源访问。很多人会在后端写一个CORS全局配置允许所有来源访问,开发时省事,但上线后等于给别的网站留了调用自己接口的口子,安全隐患放在那里。我用代理方案为主,后端CORS只配置成允许指定的前端域名,两边的优势都拿到。

4.2 主键精度丢失与日期序列化的统一处理

联调过程中最容易莫名出bug的,是数据库主键从后端传到前端后"变了"。MySQL的BIGINT类型是64位整数,而JavaScript的Number类型只能安全表示53位以内的整数。当主键值超过这个范围,前端的Id最后几位就会变成0。这个问题在自增主键数据量少时看不出来,一旦主键增长到千万级,或者用了雪花算法生成分布式Id,就会频繁出现"明明存在记录却改不动、删不掉"的情况。

解决方案是在后端统一把Long类型序列化成字符串:

@JsonSerialize(using = ToStringSerializer.class) private Long id;

在Jackson的ObjectMapper里做全局配置也可以,但直接在实体类字段上加注解更直观,同事接手代码时能马上看出来。

日期格式是另一处痛点。Java默认序列化的LocalDateTime输出是带T的ISO格式,比如 2024-06-01T10:30:00,前端拿到之后要么自己手动解析,要么展示出一堆奇怪的字符。统一的做法是,在后端配置统一的Jackson格式,或者在每个日期字段加上 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")。前端再配一个dayjs做格式化,全项目时间显示就保持一致了。

4.3 图片上传的本地存储方案与静态映射

房东发布房源时要上传封面和实拍图,这是租房平台不可少的功能。我选择了最简单的本地存储方案:图片上传到服务器的一个指定目录,比如 /data/rent/upload/,数据库里只存相对路径。

这里有一个必须处理好的点:用户通过浏览器访问图片,实际上是向服务器的某个URL发起请求。后端需要把本地上传目录映射成一个HTTP访问路径。在SpringBoot里用WebMvcConfigurer就能实现:

@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceLocations("file:/data/rent/upload/"); } }

配置完成后,访问 http://域名/upload/xxx.jpg 时,Nginx会直接去读取服务器磁盘上的文件。这样静态资源和动态接口完全分离,数据库里也只存字符串路径,查询压力小。

图片上传的大小限制我也踩过坑。默认的Spring Boot上传限制是1MB,手机拍的图一张动辄三兆,如果不是用Element Plus自带压缩,很容易出现"上传失败"这种毫无提示的错误。后来我在配置里把上传大小调整到10MB,才彻底解决问题。生产环境还可以在前端先做压缩,减少服务器存储压力。

5. 从本地到线上:完整部署流程的记录与验证

5.1 环境准备与后端打包

部署之前先把服务器的环境准备好。我用的是Linux系统,需要安装JDK、MySQL、Nginx。JDK版本要和本地开发一致,我用的Java 8,部署时安装对应版本,否则可能出现Class版本错误。

后端打包前,先检查配置文件。本地开发用application-dev.yml,数据库地址是localhost。生产环境用application-prod.yml,把数据库地址改成服务器内网地址,账号密码单独设置。启动时通过启动参数指定使用哪个profile:

java -jar rent-platform.jar --spring.profiles.active=prod

打包命令很固定:

mvn clean package -DskipTests

-DskipTests的意思是跳过测试用例。有些同学打包时没跳测试,本地测试代码连不上开发数据库,build直接失败。这一步可以省掉很多无谓的报错。

打包完成后,target目录下会生成一个可执行的Jar包。可以在本地先跑一下确认正常,再上传服务器。

5.2 前端构建与数据库初始化

前端构建没有太多技巧,关键是Node版本。前端项目我用的是Vite构建,Vite对Node版本有要求,版本太低会报错说不支持。建议用Node 16或者更高的版本。

npm install npm run build

构建完成后,dist目录里就是静态文件。把这个目录整个传到服务器的某个路径,比如 /data/rent/dist,后面交给Nginx指向这个目录就行。

数据库的部分,我会把完整的建表语句和初始数据放到一个 db.sql 文件里。在服务器上初始化数据库有两种常用方式。一种是登录MySQL后执行 source 命令,另一种是直接命令行导入:

mysql -uroot -p rent_db < /data/rent/db.sql

导入之前先在MySQL里创建好数据库,否则会报"数据库不存在"的错误。导入完成后,用几条简单的SQL验证一下表结构和数据条数,做到这一步确认无误,再继续往下走。

5.3 Nginx反向代理与Vue history路由配置

Nginx是整个部署链路里最关键的中间层。它能同时干两件事,托管前端静态文件,以及把接口请求反向代理到后端服务。我的nginx.conf配置思路如下:

server { listen 80; server_name your-domain.com; location / { root /data/rent/dist; index index.html; try_files $uri $uri/ /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; } location /upload/ { alias /data/rent/upload/; } }

有的同学会问,try_files那行是干什么的?这是专门给Vue Router的history模式准备的。页面在路由切换时路径是 /house/detail/123,如果Nginx找不到对应的物理文件,就会直接返回404。try_files会把这类的路径请求全部回退到index.html,由前端路由去解析和渲染页面。没有这行配置,刷新非首页路由就报404,这是前后端分离项目部署时最高频的翻车点之一。

location /api/ 里的代理也解释一下。前端Axios的baseURL统一配置成 /api,所有请求都带着这个前缀发到Nginx。Nginx收到后把前缀对应的请求转发给后端端口,同时保持Host等请求头不变。这样浏览器看到的请求地址始终是同一个域名,不会产生跨域问题。

5.4 启动服务与服务守护

后端Jar包的启动方式,基本是nohup配合日志输出:

nohup java -jar rent-platform.jar --spring.profiles.active=prod > /data/rent/logs/app.log 2>&1 &

nohup表示忽略挂断信号,关闭终端后进程继续跑。日志输出到app.log文件,排查问题直接看这个文件。

不过nohup有个缺点,服务器重启后Jar包不会自动启动。想省心,建议配置systemd服务。创建一个服务描述文件,把启动命令交给systemd托管,设置开机自启,这样比裸跑nohup稳定得多。

服务起来后,用下面的命令做一轮完整验证:

curl http://127.0.0.1:8080/api/user/login -X POST -d "username=test&password=123456"

有返回数据说明后端通。然后访问域名看前端页面,登录、搜索房源、上传图片,把核心链路走一遍。任何一个环节报错,先看app.log,再看Nginx的error.log。我用这条日志排查路径解决过绝大多数部署问题。

6. 项目上线前后值得关注的六个优化与避坑点

6.1 配置与依赖方面的常见坑

第一件要提醒的是MyBatis的Mapper.xml文件没有被打进构建产物。Maven默认只把resources目录下的文件打包进Jar,如果Mapper.xml放在src/main/java里面,打包后就会发现启动时报错说找不到SQL语句。规范的做法是把Mapper.xml放到src/main/resources/mapper/目录下,或者在pom.xml里显式配置资源目录。

第二件是驼峰映射。MySQL字段习惯用下划线命名,比如create_time,Java属性是createTime。MyBatis默认转换下划线为驼峰的功能没打开时,查询结果里创建时间一直是null。在配置文件里加上:

mybatis: configuration: map-underscore-to-camel-case: true

这个开关一行配置,能救很多人半天的排查时间。

第三件是分页。租房平台的房源列表必然要分页,我用了PageHelper插件,但它有个使用前提:分页参数必须和查询语句在同一个线程里立即执行。如果中间隔了一次其他查询,分页就会串到别的SQL上。小心使用PageHelper,或者干脆自己用LIMIT加参数算偏移量,也能稳定工作,只是需要多写几条SQL,控制权完全在自己手里。

6.2 业务层面的细节优化

房源列表的排序策略值得多说一句。最简单的方案是按发布时间倒序,但这会导致房源信息一直变化,用户每次刷新看到的位置都不一样。我加了一个权重机制,把新发布的房源和时间做加权排序。具体做法是热点房源加权值高,普通房源按创建时间加权,整体的排序结果兼顾了新鲜度和稳定性。实现成本不高,但带来的用户体验提升非常明显。

租赁订单生成时,一定要生成唯一的订单编号,不要用自增主键直接当订单号展示给用户。订单号我一般这样拼:当前时间戳加随机数加用户Id的末四位。这个编号作为平台和用户沟通的凭证,也方便客服按号查单。

6.3 安全与稳定性建议

密码存储这块,必须用哈希加密。BCrypt是Spring Security里自带的密码加密器,即使两个用户密码相同,加密后的结果也不同,因为每次都会加入随机盐。

接口层面,登录接口要加简单的限流防刷机制。可以用拦截器按IP统计短时间内的请求次数,超过阈值直接拒绝。网上教程里常见的那种不加任何防护的做法,上线后很容易被脚本刷爆。

MySQL的部署安全也很重要。生产环境不要用默认的3306端口,不要给应用用root账号连接数据库,单独建一个只拥有项目库权限的账号。这些操作在部署阶段多做一步,后续就能少操心很多。

最后说一点个人体会。租房平台看起来功能不算复杂,但它把用户角色、状态流转、文件上传、高德地图标记、订单结算这些常见模块全都覆盖到了。把这样一个项目从设计到部署完整走一遍,胜过看十遍教程。我给自己的要求是,每一步报错都要弄明白原因再继续。按照前面这些思路做下来,这个系统不仅能顺利跑通,还能在性能和安全性上达到可上线标准。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询