做酒店预订系统这个选题的十有八九是拿来当毕设、课程设计或者练手项目。标题里写得很清楚基于 Spring Boot Vue 的酒店预订系统附带源码、数据库脚本和文档。这个组合听起来很常规但真的把它从零搭起来、跑通、再到能答辩或面试时讲清楚其实有不少门道。我基于自己做过的一版相似系统把核心设计、实现过程、踩坑记录完整拆一遍你可以直接照着思路去复现。1. 先聊清楚这个系统到底该做成什么样1.1 从一句话需求到一个可演示的系统大家收到这类题目时最常犯的错误就是上来就写代码。实际上先把需求拆成一条一条可验证的功能点后面才不会做着做着就乱了。酒店预订系统核心用户有两类普通住客和管理员。住客能看到在售房间、下订单、查看自己的订单、取消未入住的订单也有人希望加一个简单的评论功能。管理员则需要维护房间信息、处理订单状态、管理用户。这一套东西捋完系统的边界就清楚了前台展示 用户操作 后台管理三个大模块。我在动手前会把功能列表整理成一张表格每做完一项就勾掉一项这样能控制开发节奏也方便最后写文档时对照着补内容。模块功能点角色用户端注册、登录、浏览房间、检索房间住客用户端提交预订订单、订单支付模拟、取消订单住客用户端查看订单历史、删除评论住客管理端管理员登录、房间信息管理管理员管理端房间类型管理、轮播图管理管理员管理端订单状态管理、用户封禁管理员1.2 为什么选 Spring Boot Vue能不能换别的这个组合之所以在近几年的毕设项目里霸榜不是因为跟风而是因为它确实在完成度和学习成本之间拿捏得最好。后端用 Spring Boot免去了繁琐的 XML 配置内置 Tomcat打成 jar 包就能跑。对于学生或者刚开始做全栈的人来说这比折腾 SSHSpring Struts Hibernate那套老古董要友好太多。配合 MyBatis-PlusCRUD 操作基本不用手写 SQL开发速度非常快。当然如果你对 JPA 更熟也可以换但 MyBatis-Plus 在中文社区资料多遇到问题好搜这是很实际的优势。前端选 Vue是因为它的响应式数据绑定和组件化开发在实现房间列表 - 详情 - 下单这套流程时非常顺手。Vue 3 的组合式 API 比 Vue 2 的 Options API 更简洁配合 Element Plus 组件库后台管理界面半天就能搭出个像样的框架。如果你已经熟练使用 React那也可以换成 React但如果你只是需要一个能快速出成果的方案Vue 是更稳的选择。说句实在话对于这种体量的系统前端用什么框架不影响核心业务重要的是你能解释清楚数据是怎么流转的。1.3 整个系统的架构一句话说明白架构上不用搞得太复杂前后端分离就够了。前端 Vue 项目通过 Axios 调用后端提供的 RESTful 接口后端 Spring Boot 接收请求后调用 Service 层处理业务逻辑再通过 MyBatis-Plus 操作 MySQL 数据库。用户登录之后后端签发一个 JWT Token前端把它存在本地每次请求时带上后端通过拦截器校验 Token 并识别用户身份。这套流程可以用这样一张数据流来描述Vue 页面 → Axios → Controller → Service → Mapper → MySQL ↑ ↑ JWT 拦截器 业务校验理解了这条链路后面写代码时心里就有数了。2. 数据库设计这个系统的心脏2.1 到底需要几张表数据库是很多新人最容易糊弄过去的地方但订单系统最怕的就是表结构不合理。等写到订单状态流转时你会为当初的草率付出代价。我最终设计的是一套 8 张表的方案逻辑比较顺分享给你参考用户表t_userid、用户名、密码加密存储、姓名、手机号、角色1 表示管理员0 表示普通用户、创建时间。房间类型表t_room_typeid、类型名如大床房、双床房、单价、面积、床型、可住人数、图片地址。房间信息表t_roomid、所属类型 ID、房号、房间状态0 表示空闲1 表示已入住2 表示打扫中。订单表t_orderid、订单编号、用户 ID、房间 ID、入住日期、退房日期、订单金额、下单时间、订单状态0 待支付1 已支付2 已入住3 已完成4 已取消。评论表t_commentid、用户 ID、房间 ID、评论内容、评分、评论时间。轮播图表t_bannerid、图片地址、跳转链接、排序。收藏表t_favoriteid、用户 ID、房间 ID。功能是让用户可以收藏心仪的房间。这张表结构设计好之后要记得在建表脚本里加上外键逻辑和索引。订单表中对用户 ID、房间 ID 建索引因为查询订单时基本都是按用户维度高频访问不加索引的数据量一上来就会卡。2.2 房间库存与价格的建模这里有一个比表结构更重要的问题库存到底该怎么算。很多人的直觉是给房间表加一个剩余数量字段下单时减一。但酒店的真实情况是库存不是简单的总数而是某一天是否有空房。一个房间连续预订 5 晚不代表它 5 天的库存都能卖出去要看你按什么维度去卖。对于练手项目为了控制复杂度和演示效果我建议按房间实体来管理库存每个房间号就是一个独立的可售卖单元下单时根据所选日期区间判断该房间在区间内是否有重叠的订单。这样做的好处是逻辑直观一个房间在某个时间段被订单占用后再有人想订同一时间段就会提示无房。虽然粒度比较粗但完全满足毕设演示和业务讲解的需求。如果你有时间后续可以升级为按天生成库存表的方案但那套会多出很多逻辑建议先不加。2.3 订单状态机从 A 状态到 B 状态订单状态是整个系统里最容易出 bug 的地方我见过很多人的代码里状态写成随意的字符串然后在 if 判断里手滑拼错排查了很久。我建议把所有状态定义成常量类或枚举项目中所有代码统一引用。订单状态我拆成了 5 个0待支付。下单后默认状态。1已支付。模拟支付完成后进入。2已入住。用户办理入住后由管理员标记。3已完成。退房后由管理员标记。4已取消。用户主动取消或者订单超时未支付自动取消。状态的流转方向是单向的待支付 → 已支付 → 已入住 → 已完成。已取消是终态不能再变更。后端在更新状态时必须校验当前状态是否允许迁移到目标状态。比如待支付订单不能直接变已完成。这个校验逻辑虽然简单但在答辩时讲出来会显得你的设计很专业。3. 后端核心逻辑从注册到下单的全链路实现3.1 注册、登录与权限控制你不能绕过的那道坎用户注册登录看着简单但要用规范的方式做而不是把密码明文存进数据库。我实际项目里用的方案是密码通过 BCrypt 加密后再入库这是 Spring Security 框架自带的能力调用起来非常方便。登录成功后后端生成一个 JWT Token 返回给前端。前端把 Token 存在浏览器的 localStorage之后每次发请求在拦截器里自动把 Token 放到请求头的 Authorization 字段里。后端通过自定义拦截器对需要登录的接口做统一校验。需要注意一个细节JWT 生成时可以携带用户 ID 和角色信息但不要携带密码等敏感数据。解析 Token 时如果过期或签名不一致直接返回 401 状态码前端收到后自动跳转到登录页。用户角色建议默认给普通用户管理员账号可以在数据库初始化脚本里直接写死一个密码也走 BCrypt 加密后的值。3.2 房间查询接口的两种搜索方式房间列表和查询是用户端的核心。真正的细节在于怎么处理根据条件筛选房间。我做了两个查询接口查询所有房间类型列表用于首页展示。根据条件搜索房间支持按房间类型、入住日期、退房日期筛选。第二个接口的核心难度在于日期筛选。用户的逻辑是我想入住日期是 A退房日期是 B那么系统要找出所有在 A 到 B 区间内没有冲突订单的房间。这里有一个很常见的误区直接用 SQL 判断订单的入住日期区间是否与用户选择区间重叠。如果只判断订单的开始日期不等于用户的开始日期是不够的因为重叠有多种情况用户的区间完全包含在已有订单区间内、用户区间与已有订单区间部分重叠、已有订单区间完全包含在用户区间内。正确的判断逻辑是找出所有存在冲突的订单条件是订单的入住日期小于用户的退房日期并且订单的退房日期大于用户的入住日期然后把这两个条件之间冲突的订单对应的房间 ID 剔除剩下的就是可订房间。你可能会问我这样写 SQL 会不会很绕确实有一点但这是数据库日期区间重叠问题的标准做法面试时也是高频考点。3.3 下单与防超卖事务和并发必须一起处理下单是整个系统最核心的业务逻辑处理不好会出现一房多卖的情况。我的具体实现流程是这样的前端把房间 ID、入住日期、退房日期提交到后端。后端根据房间 ID 查询房间信息计算出订单金额单价 × 天数。校验所选日期范围内是否已有冲突订单。如果没有冲突则插入订单记录初始状态为待支付。模拟创建一个预支付单实际系统中这一步会调用第三方支付接口。这里最容易踩的坑是并发问题。如果两个人同时点击预订同一个房间的同一个日期段两个请求同时通过了冲突检查然后都插入订单房间就被重复预定了。解决这个问题我用了两种手段叠加在订单表中对房间 ID 入住日期 退房日期三个字段建立联合唯一索引。插入订单前先执行一条 SELECT ... FOR UPDATE把对应房间的记录锁住确保同一时间只有一个事务能通过校验。对于毕设项目来说联合唯一索引这一招就足够展示你懂并发问题了双保险则更稳。在讲解时能主动提到防超卖这是一个实实在在的加分项。3.4 模拟支付精心设计的演示环节这位系统的支付不需要真的接入微信或支付宝那会涉及商户号、回调等很麻烦的资质问题。我采用的方案是模拟支付。用户提交订单后订单处于待支付状态系统同时生成一个支付二维码的模拟图前端展示一个确认支付按钮。用户点击后调用后端支付接口接口将订单状态从待支付修改为已支付。这个方案足以演示闭环流程。如果你想做得更有真实感可以加个 sleep 延迟制造一种正在处理中的等待效果但要注意别做得太久影响体验。核心目的是把状态流转演示出来而不是真的去对接第三方支付。4. 前端实现Vue 3 工程化的实践细节4.1 项目初始化和路由设计前端我使用的是 Vue 3 Vite。Vite 的启动速度和热更新体验比旧工具链好太多对开发效率提升很大。项目创建后第一件事是划分目录结构我大致会分成这些部分views页面组件按用户端和管理端分目录。components公共组件轮播图组件、房间卡片组件等。router路由配置。storePinia 状态管理。api接口请求封装。路由设计上分为两个部分。用户端的路由有首页、房间列表、房间详情、登录注册、个人中心、我的订单。管理端路由有仪表盘、房间管理、订单管理、用户管理统一挂载在一个管理后台布局组件下。管理端路由需要加一个前置守卫判断当前登录用户的角色是否为管理员不是就强制跳转首页。4.2 接口封装与状态管理Axios 封装是所有前端项目标配。我习惯创建一个 api/request.js对所有请求进行统一配置baseURL 设置后端的地址前缀。请求拦截器里从 localStorage 中取 Token 放到请求头。响应拦截器里对返回的状态码做统一处理比如 200 直接返回数据401 则清除 Token 并跳转登录页。这样为啥要封装因为如果你在每一处发请求都手动拼 Token 和状态处理代码会非常冗余而且后面对接真实接口时改动一处就行。这是前端工程化的基础操作必须会。状态管理我用了 Pinia主要是存储登录用户信息。用户登录成功后把用户对象存进去用户端展示头像、用户名、退出登录按钮都从这里读取。订单、房间列表这些数据不必放全局状态里因为在组件内部用 reactive 就够了没必要把所有数据都塞进全局 store 增加复杂度。4.3 几个关键页面的实现思路首页的布局一般包含轮播图、房间类型快捷入口、特价推荐房间。轮播图数据从系统调用接口获取接口返回图片路径列表前端用 Element Plus 的 Carousel 组件循环渲染。房间列表页要注意分页和筛选。筛选条件区放房间类型下拉框、入住日期和退房日期日期选择器用户选择后点击搜索触发列表接口重新请求。分页我用的是 El-Pagination 组件后端接口接收 pageNum 和 pageSize 参数返回总记录数和当前页数据。房间详情页展示了房间图片、价格、床型、面积和入住人数下面是一个预订表单用户选择入住和退房日期系统自动计算金额。顶部的日期选择器要禁用掉过去的日期避免用户选错。后台管理界面相对简单。房间管理表格展示房间号、类型、价格、状态支持新增、编辑、删除操作。新增和编辑共用同一个表单弹窗提交时根据是否传入 ID 来决定是新增还是更新。5. 从本地联调到实际部署把这套系统跑起来5.1 本地环境的准备本地开发时我使用的工具版本供参考JDK 1.8 或 17 都可以但建议保持统一MySQL 5.7 或 8.0Node.js 14 以上IDEA 或 VS Code 都行。第一步是导入后端项目用 IDEA 打开后等待 Maven 下载依赖。数据库方面需要先新建一个库然后执行项目提供的 SQL 脚本把表结构和初始数据导入。推荐用可视化工具执行方便检查每条语句是否正确执行。在 application.yml 里需要改两个地方数据库用户名和密码。这个改了之后启动 Spring Boot 项目如果控制台出现Started Application in x seconds说明后端已经跑起来了。前端项目用 npm install 安装依赖。如果慢就用国内镜像源。装完依赖后npm run dev 启动开发服务器默认地址是 5173 端口。这时候打开浏览器访问前端页面如果页面能正常显示就说明前后端环境都准备好了。5.2 前后端联调的关键配置前后端联调最容易出现的问题是跨域。前端地址是 5173 端口后端是 8080 端口端口不同便会触发跨域。解决方式我推荐在后端配置一个 Cors 过滤器允许来自前端地址的请求这个配置在后端代码里加上即可。另一个需要注意的问题是后端接口访问地址。我用的是相对路径比如 /api/room/list这个路径需要保证前后端配置一致。建议在后端所有接口加一个统一的 api 前缀后续调整路由或部署时不至于乱了套。联调时优先试一遍登录功能。能登录成功说明数据库配置、密码加密、Token 签发这一条完整链路都是通的。然后再测房间列表、下单、支付这三个核心链路依次排除问题。5.3 打包部署的两种常见方案到了验收阶段你需要把系统部署起来给别人演示。有三种常见的方案可以选。第一种是前后端分别打包部署。后端打包成 jar 包用 java -jar 命令运行前端执行 npm run build生成静态文件后扔到 Nginx 里托管。这种方式最接近真实项目环境适合在演示服务器上使用。第二种是对学生最推荐的方式直接用 IDEA 运行后端 npm run dev 运行前端用开发模式演示。虽然只适合本地环境但胜在简单稳定不会因为环境问题影响演示效果。我当年验收时就用的这种方式。第三种是把前端打包后的 dist 目录放到后端的 static 目录下后端一个 jar 包同时提供接口和页面服务。这样做的好处是只需要启动一个服务但容错性差一些不太建议作为首选。6. 联调与部署时的常见坑6.1 印象最深的 5 个问题及排查思路现象原因解决办法前端请求接口控制台报 404后端接口路径与前端请求路径不一致打开后端控制台看启动日志中接口映射的完整路径用 Postman 直接测试接口确认路径后再修改前端登录接口报 500密码错误一直登不上数据库密码字段长度不足BCrypt 加密后字符串超长被截断把密码字段长度改为 60 以上重新执行 SQL 脚本重新注册再测试前端页面加载出来但没有数据数据库连接失败或 SQL 脚本没执行成功检查后端日志中的数据库连接报错信息确认账号密码正确、库已创建提交订单时提示房间已被占用但明明没有该订单事务未提交时自己也查到了冲突订单检查是否有事务注解确认在冲突校验时使用的是当前事务级别查询前端登录成功后刷新页面用户信息丢失用户状态只存在内存中没有持久化将用户信息存到 localStorage刷新时从本地读取并恢复登录状态6.2 几个容易被忽视但很影响体验的细节第一房间日期选择器的禁用逻辑要处理到位。入住日期不能选今天之前退房日期不能早于入住日期。少数前端组件库对日期禁用提供了现成的属性但你还是要在提交时做一次后端校验防止有人绕过前端直接调接口。第二订单编号的生成要保持唯一。直接使用主键自增再做展示会暴露数据量可能显得项目很业余。我建议用时间戳 用户 ID 后四位 随机数的组合来生成订单号格式类似 20250515xxxx演示的时候也更好讲解。第三管理员账号的初始密码最好用一次性密码首次登录后提示修改不过如果在演示场景下保持简单密码会更方便现场操作。这个可以根据你的处境自己权衡。我个人在实际做这种项目的过程中体会最深的是千万不要小看数据库设计和订单状态管理这两块。它们在你最开始觉得无非就是写几个接口的时候会精准地把你打回原形。无论是毕设答辩时要解释的并发防超卖还是面试官可能会深挖的日期区间重叠查询本质上都是这两个核心点的延伸。把这套内容彻底吃透比你多写两个页面有用得多。