SpringBoot+Vue+MySQL+MyBatis民宿租赁系统设计与实现全解析

发布时间:2026/10/10 21:50:53

SpringBoot+Vue+MySQL+MyBatis民宿租赁系统设计与实现全解析 做民宿租赁管理系统的人十有八九是冲着一件事去的交一份能跑、能讲、能过的课设或毕设。SpringBoot Vue MySQL MyBatis这一套组合这几年几乎成了这类项目的事实标准——后端用SpringBoot省去一堆XML配置前端用Vue做交互数据库交给MySQL持久层用MyBatis写SQL灵活不绕弯。我这个月刚完整复盘了一套民宿租赁系统正好把整体设计、核心代码逻辑、运行过程和踩坑点全部梳理一遍。这套系统解决的核心问题说白了就是民宿房东的日常管理需求房源信息维护、房间状态查询、租客下单、订单记录、以及后台的简单统计。它不像电商那样复杂但边界清晰、流程完整非常适合拿来当作前后端分离项目的入门样板。这篇内容适合三类人看一是正在做课设/毕设需要参考架构的同学二是拿到源码但不知道怎么跑起来、不知道怎么讲解的初学者三是想快速了解SpringBootVue项目标准写法的转行者。下面我按自己的开发习惯从需求拆解到部署运维一条线讲完。1. 项目全貌与需求拆解1.1 这套系统实际上在管什么先说业务模型。民宿系统和酒店系统不一样民宿更强调房源分散、房东直租、租期灵活所以表设计上通常围绕“房源-房型-订单-用户”展开。一个最小可用版本需要包含五个核心模块用户管理、房源管理、订单管理、评价管理、统计看板。用户管理不只是登录注册还要区分角色。常见做法是给用户表加一个role字段管理员、房东、租客三种角色各看各的界面。房源管理是核心中的核心字段至少要覆盖房源名称、位置、户型、每晚价格、最多入住人数、状态空闲/已租/维护、封面图、房间简介。订单管理负责生成预订、取消预订、入住退房流程状态机是重点。评价模块相对独立但能体现整个系统的完整度。统计看板通常就是房源的订单量、收入趋势、热门房源排行。我在做这类系统时第一步永远是先把这些业务对象画出来确定每个对象的状态流转再落数据库表。很多同学一上来就急着写代码结果订单状态全靠大脑记过两天就乱了。先想清楚再动手后面写Controller和Service会顺畅很多。1.2 为什么非要选前后端分离架构早几年的课设项目普遍是JSP Servlet 三层架构所有页面在后端渲染一个Tomcat搞定一切。现在再看前后端分离已经是主流原因很实在一是Vue的组件化开发让页面维护体验好太多二是前端打包后丢进Nginx或者直接塞进SpringBoot的static目录都行部署弹性大三是调试效率高前端用Mock数据、后端用Postman两边同时开发互不阻塞。前后端分离并不等于工程变复杂。我习惯把前端项目和后端工程分开文件夹存放前端用 npm 管理依赖、用 Vite 或 Vue CLI 构建后端用 Maven 管理依赖、通过 jar 包运行。开发环境下前端通过 Vite 代理把/api请求转发给后端的 8080 端口从而避免跨域问题。生产环境下可以直接把前端 dist 目录复制到 SpringBoot 的静态资源目录这样只需要跑一个 Java 进程就能提供完整服务省去单独部署 Nginx 的麻烦。1.3 技术选型不是凑的每个都有理由SpringBoot 的作用是快速搭建后端骨架内置Tomcatstarter机制减少依赖冲突。MyBatis 负责持久层和 JPA 相比SQL 由自己掌控适合中国开发者长期积累的 SQL 思维习惯尤其适合这个项目里房源模糊查询、订单多条件筛选这类定制SQL。MySQL 作为存储层免费、文档多、同学之间交流问题也方便。Vue 作为前端框架响应式数据绑定和对数组、对象的操作都很顺手比较适合做这种中后台管理系统。Java 本身是这门课程的主线语言和后面找工作面试的知识体系也挂钩。提示如果项目里有多张表、多个条件查询别用纯注解SQL硬拼容易出错。MyBatis 的 XML 文件写动态SQL是更成熟的做法后面我会详细说。2. 数据库设计6张核心表决定业务底盘2.1 表结构规划与字段说明我按自己做过的一套民宿系统为例核心是6张表用户表、房型表、房源表、订单表、评价表、收藏表。当然如果你要凑复杂度还可以加公告表、优惠券表甚至支付流水表但我建议先保证主链路清晰。用户表字段一般包括id、username、password、phone、email、role、avatar、create_time。密码不要明文存至少用MD5加盐或者Spring Security自带的BCrypt。房型表用于区分标准间、大床房、家庭套房等字段包括id、name、price、area、bed_info、max_people、description。房源表保存具体的房屋实例和房型是多对一关系字段包括id、house_type_id、house_name、location、status、cover_img。订单表承接用户、房型两方信息字段包括id、order_no、user_id、house_id、check_in_date、check_out_date、days、total_price、status、create_time。评价表和订单关联记录评分、内容、回复。收藏表最简单user_id和house_id双字段即可。设计的时候有两点很容易被忽略。第一订单金额不要只存单价还要存应付总额不然以后统计报表要现算麻烦。第二状态字段用 int 或 tinyint用注释写清每个值对应的业务含义比如订单状态 0待支付、1已支付、2入住中、3已退房、4已取消、5已评价。枚举值写清楚前端下拉框、后端逻辑判断都能保持一致。2.2 建表SQL示例下面我给出一个精简版的核心建表脚本实际项目中可以自己扩展。注意索引的添加订单表按 user_id 建索引房源表按 house_type_id 建索引。CREATE TABLE user ( id int NOT NULL AUTO_INCREMENT COMMENT 用户ID, username varchar(50) NOT NULL COMMENT 用户名, password varchar(200) NOT NULL COMMENT 密码, phone varchar(20) DEFAULT NULL COMMENT 手机号, role tinyint DEFAULT 2 COMMENT 0管理员 1房东 2租客, avatar varchar(255) DEFAULT NULL COMMENT 头像地址, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;order 表注意一点order 不是保留字可以用但为了保险我习惯加反引号。MySQL 里表名不区分大小写字段命名统一小写下划线风格前后端联调时直接映射驼峰命名即可。另外建议所有表都带 create_time、update_time 两个字段MyBatis-Plus 可以自动填充普通 MyBatis 也可以在实体类里用数据库默认值处理。别小看这两个字段做统计排序、排查数据问题都靠它们。2.3 多表关联还是冗余字段民宿系统的订单列表经常需要同时展示房源名称、房型名称、用户昵称这时候有两种做法一种是关联查询JOIN三张表一种是下单时直接把冗余字段快照到订单表。我的建议是展示型字段冗余金额型动态计算。比如订单列表页绝大多数情况希望一条SQL直接返回所有展示数据不需要层层查询。价格这种字段下单后就不该变必须做成快照。房东改了房价历史订单显示的还是下单时的价格这符合业务直觉。但如果做成实时关联查询房租价格那历史订单金额会被改掉对不上账。注意MySQL的默认事务隔离级别是 Repeatable Read在做金额相关的更新操作时要留意并发问题。简单项目用乐观锁或者状态判断就够了不要一开始就搞分布式锁没必要。3. 后端核心实现分层结构与关键代码3.1 为什么Controller越薄越好后端代码结构我习惯按controller、service、mapper三层拆包再加上entity、dto、vo、config、common等辅助包。Controller层的职责只有一个接收参数调用Service返回统一结果对象。业务逻辑全部放到Service层这样写的好处是可测试性好逻辑清晰。统一返回结果对象是每一套项目的标配。我平时的做法是定义一个Result类里面放 code、message、data 三个字段。成功时 code 为200失败时 code 为500。前端axios封装时统一拦截看一眼code就能决定是否弹出错误提示。Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } }3.2 房源列表分页查询实战分页查询是民宿系统的核心接口。前端传当前页pageNum、每页条数pageSize以及可选的筛选条件关键词、房型、价格区间、状态。后端返回总条数和列表数据。手写MyBatis分页其实非常简单不用引入PageHelper这种额外依赖自己在SQL里拼LIMIT即可。public PageResultHouseVO queryHouseList(HouseQueryDTO dto) { Integer pageNum dto.getPageNum(); Integer pageSize dto.getPageSize(); int offset (pageNum - 1) * pageSize; ListHouseVO list houseMapper.selectHouseList(dto, offset, pageSize); Long total houseMapper.countHouseList(dto); return PageResult.of(list, total); }这里有个小经验查询和count必须用同一套筛选条件否则分页数据会不一致。我见过不少项目查询用了价格区间count却忘加结果总页数算错数据全乱了。建议把筛选条件单独封装成一个DTO对象查询和count方法都传同一个对象。MyBatis的XML里用动态SQL处理可选条件核心写法如下SELECT h.id, h.house_name, ht.name AS type_name, h.price FROM house h LEFT JOIN house_type ht ON h.house_type_id ht.id where if testkeyword ! null and keyword ! AND h.house_name LIKE CONCAT(%, #{keyword}, %) /if if testtypeId ! null AND h.house_type_id #{typeId} /if if testminPrice ! null AND h.price gt; #{minPrice} /if if testmaxPrice ! null AND h.price lt; #{maxPrice} /if /where ORDER BY h.create_time DESC LIMIT #{offset}, #{pageSize}注意XML中大于小于号必须转义成 和 不然XML解析会直接报错。这个坑几乎每个第一次写MyBatis XML的人都会踩一次包括我自己。3.3 订单状态流转的Service设计下订流程是我觉得最值得细讲的部分。前端选了入住日期和退房日期后端要做三件事校验房间在日期范围内是否可订、计算总价、生成订单编号。校验可订状态的SQL要按日期段做不重叠判断不能只查当前状态。订单编号我习惯用时间戳加随机数比如String.valueOf(System.currentTimeMillis()) RandomUtil.randomNumbers(6)。这个方案简单、够用、不需要额外建序列表。要更严谨可以用雪花算法但做课设确实没必要那么复杂。状态流转我写一个枚举类把所有状态变化限制在合法路径里。例如已取消的订单不允许直接改成已入住已退房的订单不允许再改成已取消。代码里能防就防别全靠前端按钮控制。public enum OrderStatus { UNPAID(0, 待支付), PAID(1, 已支付), CHECKED_IN(2, 入住中), CHECKED_OUT(3, 已退房), CANCELLED(4, 已取消), FINISHED(5, 已完成); private final int code; private final String desc; // 构造方法和getter省略 }4. Vue前端搭建与前后端联调4.1 前端工程目录和路由Vue项目我用Vite构建目录划分比较固定api目录放所有请求方法router目录放路由配置views目录放页面组件components目录放公共组件store目录做状态管理。民宿系统页面不算多管理端大概七个页面登录、首页看板、用户管理、房源管理、订单管理、评价管理、个人中心。路由需要做权限控制最简单的方法是给路由的meta字段添加requiresAuth标记然后在路由守卫里检查本地token。路由守卫的写法如下router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else { next() } })4.2 Axios请求封装和跨域处理前端请求不直接写axios地址而是统一封装成request模块这样改造后端地址、统一拦截错误、统一加token都方便。核心思路是设置baseURL请求拦截器从localStorage取出token放进请求头响应拦截器统一处理code码。const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) service.interceptors.response.use(response { const res response.data if (res.code 200) { return res } if (res.code 401) { router.push(/login) } return Promise.reject(new Error(res.message || 请求失败)) })开发环境跨域解决方式是在vite.config.js里配置proxy把/api开头的请求代理到http://localhost:8080。不需要后端配跨域注解也不需要用代理插件这是目前Vue开发中最舒服的方案。很多同学直接用全文CORS注解虽然能解决问题但上线之后安全性比较差生产环境一定要用同源部署方式。4.3 前端页面中的几个交互细节民宿系统中房源列表页是整个系统的门面重点不是花哨样式而是信息清晰。卡片上展示房源图片、名称、价格、可住人数和状态标签。我建议用el-card el-tag组合价格加粗突出状态用不同颜色区分空闲绿色、维护灰色、已租红色一眼就能识别。表单校验不能只靠后端前端也做一层。Vue Element Plus的form表单自带规则校验比如用户名必填、手机号格式、价格必须是数字且大于0。这些规则配置简单但能拦住很多低级错误提升整体体验。还有一个细节是订单提交时的二次确认弹窗。用户点击提交订单后前端应该弹一个确认框把入住人、入住日期、晚数、总价列清楚。等用户确认后再发起请求。这一步看似多余实际能避免很多误操作也符合真实业务习惯。提示Vue项目里修改数组下标方式更新数据按钮不生效是常见问题。用 this.houses[index] newValue 或 splice 更新数组才能触发响应式重绘。这个不是Bug是Vue响应式的设计特征。5. 完整启动流程与常见运行错误5.1 环境准备清单拿到源码第一步永远是检查环境别急着双击运行。我的检查顺序是JDK版本、Maven版本、Node版本、MySQL版本、IDE编码。这一套组合最常见的是JDK8 Maven3.6 Node16 MySQL8。如果你的JDK是17要注意SpringBoot版本和pom里java.version是否匹配不然会报UnsupportedClassVersionError。MySQL前后版本差异也要注意。MySQL8的驱动类是 com.mysql.cj.jdbc.DriverMySQL5是 com.mysql.jdbc.Driver连接URL也不一样。如果数据库是MySQL8连接字符串最好带上serverTimezoneAsia/Shanghai否则会出现差8个小时的时区问题。5.2 MySQL数据库初始化流程初始化数据库一般有两种方式一种是用Navicat等客户端手动执行SQL脚本一种是用命令行source导入。我更推荐命令行方式因为在服务器上部署时没有图形界面也能操作。先创建数据库再设置字符集然后导入脚本mysql -u root -p create database if not exists homestay default character set utf8mb4; use homestay; source /Users/yourname/Desktop/homestay.sql;这里有一个坑如果SQL脚本里已经有CREATE DATABASE语句那你后续就不要再手动创建一次直接source整个脚本就行重复创建会报错。另外检查脚本里的表名和代码里Mapper注解或XML里的表名是否完全一致大小写不一致、多余空格都会导致Table doesnt exist这是最常出现的低级错误。5.3 后端启动和前端启动的关键点启动后端前先检查 application.yml 的数据库配置。密码是重点很多源码发出去时把密码写成了root但接收者本机密码不是root就会连接失败。推荐做法是修改为自己的账号密码或者用环境变量方式配置避免硬编码。启动命令我就不多说了后端在IDEA里直接运行main方法或者用mvn spring-boot:run前端在项目目录下先npm install再npm run dev。npm install经常因为网络问题失败可以切换国内镜像源npm config set registry https://registry.npmmirror.com后端启动成功的标志是看到形如 Tomcat started on port(s): 8080 的日志。前端启动成功的标志是控制台出现一条本地访问地址一般是 http://localhost:5173 。这里要注意端口冲突如果8080被占用后端会直接启动失败日志第一行就告诉你端口被占用了需要改yml里的 server.port或者杀掉占用进程。5.4 常见问题速查表我把这几年带项目时遇到的高频错误整理成一张表照着查能省很多时间。错误现象直接原因解决办法前端请求接口404代理配置没生效检查vite.config.js的proxy路径是否与baseURL一致后端启动报端口占用8080被其他进程占用换端口或lsof -i:8080找进程杀掉数据库连接 refusedMySQL没启动或密码错误先确认MySQL服务启动再检查yml账号密码登录接口返回500用户表字段映射问题检查实体类字段是否与表字段对应中文乱码IDE编码和数据库编码不一致统一UTF-8URL加characterEncodingutf8MyBatis报Invalid bound statementMapper接口和XML没绑定检查namespace是否完全等于接口全限定名本地启动正常部署后404前端路由是history模式改为hash模式或用Nginx做try_files回退6. 源码改造与功能扩展建议6.1 登录鉴权从简单token升级为JWT很多课设源码里的登录就是验证用户名密码后返回一个随机字符串存储在内存Map里。这种做法演示可以但讲项目时容易被老师追问。如果想把项目往上拔一个档次建议引入JWT。SpringBoot集成JWT并不复杂引入jjwt依赖登录成功后生成token在拦截器里解析校验即可。JWT的好处是无状态服务端不需要保存会话信息对分布式部署友好。但也要提醒一点JWT一旦签发在有效期内无法主动失效所以退出登录时前端删除token就够了不用做服务端黑名单。课程设计里讲清楚JWT的组成部分Header、Payload、Signature以及签名校验原理是一个很好的加分点。6.2 图片上传模块民宿房源系统必然涉及房源图片管理。开发环境最简单的方式是把图片保存到本地目录然后在SpringBoot里配置静态资源映射把/upload/**映射到磁盘目录。比如上传的图片保存在 D:/home/upload访问路径是 http://localhost:8080/upload/xxx.jpg用WebMvcConfigurer配置一下addResourceHandlers即可。生产线环境建议使用对象存储服务MinIO是当前非常流行的私有存储方案可以部署在本机或局域网。SpringBoot集成MinIO主要就是引入依赖、配置endpoint和密钥、上传时生成对象名并返回访问URL。很多同学听到MinIO就紧张其实它就是一个支持S3协议的独立文件服务接入方式和写一个普通Service没有本质区别。6.3 数据统计从SQL到图表展示管理后台的看板页是提升项目质感的利器。统计图表我推荐用ECharts数据源就是后端的一个统计接口。比如统计每月订单量、订单收入、房源预订排行后端分别写对应的SQL查询。-- 月度订单数统计 SELECT DATE_FORMAT(create_time, %Y-%m) AS month, COUNT(*) AS order_count FROM order WHERE status IN (1, 2, 3, 5) GROUP BY DATE_FORMAT(create_time, %Y-%m) ORDER BY month;统计时要注意SQL里的日期函数在不同数据库版本中的兼容性。另外统计数据接口返回的字段名最好约定成chart需要的格式比如 month 和 order_count前端拿到之后直接塞进ECharts的 series 数据里整个过程一气呵成。这样做出来的看板远比表格截图更有说服力。6.4 订单超时自动取消的时效性优化真实民宿平台都有“待支付订单30分钟后自动取消”的机制。课程设计如果加上这个功能可以让项目在技术答辩时多一个亮点。实现思路不复杂在订单表加一个 expire_time 字段下单时设置 create_time 30分钟然后定时任务每1分钟扫描一次把超时待支付的订单改为已取消。SpringBoot自带的Scheduled注解就能搞定这个定时任务在主类上加EnableScheduling然后在任务类里写一个带Scheduled(fixedDelay 60000)的方法。这里有个需要留意的点扫描时只处理状态为“待支付”且 expire_time 小于当前时间的订单避免误更新其他状态。7. 写完这套系统的几点心得这套民宿租赁系统做下来最深的感受是一个“刚刚好”的项目应该具备三个特征业务链路完整、技术栈主流、可扩展空间大。完整链路指的是从用户注册到下单评价每个环节都有数据留痕主流技术栈意味着你面试时讲的东西别人听得懂可扩展性则体现在换JWT、加OSS、上Redis都能顺理成章地展开。拿源码跑通只是第一步真正有价值的是把每一层的设计意图弄懂。比如为什么订单金额要做快照为什么动态SQL用XML而不是注解为什么前端代理能解决跨域。这些“为什么”想通了代码哪怕换一版你也能快速上手。最后分享一个我个人写SSM/SpringBoot项目的习惯永远先从数据库表设计开始表结构定了业务边界就定了后面写代码只是体力活。希望这篇内容对正在折腾民宿租赁系统的你有帮助有问题可以在评论区留言。
延伸阅读

更多相关文章

2026/10/10 21:45:53

部署Claude Code并接入deepseek大模型:TaoToken统一Key配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 21:45:53

STM32启动地址之谜:0x08000000背后的复位流程与内存映射

我刚学STM32的时候,调试器上看到PC跑在0x08000000,总习惯把它当成“芯片的某种暗号”。后来带某小型项目时,被A同学问“CPU复位明明从0开始,为什么程序却写在0x08000000”,我才认真把这条线理清楚。它牵扯的不只是“厂…

2026/10/10 22:50:59

机器学习量化策略demo源码分享:从特征工程到回测的完整实现

简介:这是一份面向具备一定Python基础、对炒股与量化投资尚不熟悉的初学者的入门级demo源码,围绕机器学习在A股量化策略中的应用展开。资源以完整项目形式呈现,涵盖数据获取与清洗、特征工程、模型构建与训练、策略回测及风险管理等关键环节&…

2026/10/10 7:31:36

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/9 20:15:56

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/8 6:05:44

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 0:04:53

从逻辑门到计算机:数字电路核心原理与全加器搭建实战

如果你拆过一台旧电脑的主板,盯着那些黑乎乎的小芯片看上一会儿,可能会冒出同一个疑问:这堆引脚密集的元件,到底是怎么“变”出那么复杂的应用的?答案并不在某个神秘的部件里,而是在所有芯片内部都在反复使…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑