ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Spring Boot+Vue酒店管理系统开发实战:前后端分离与权限控制

Spring Boot+Vue酒店管理系统开发实战:前后端分离与权限控制 1. 项目概述与整体方案选型1.1 这个项目到底在解决什么问题酒店管理系统这个选题说实话在Java毕设和企业练手项目里都属于“常青树”。原因很简单它的业务闭环足够完整从前台订房、入住登记到退房结算再到后台的房间管理、订单统计核心流程清晰但又不像电商系统那样涉及复杂的库存扣减和支付对账。你做完它既能给自己的技能树补上前后端分离、接口设计、权限控制这些通用能力又不至于被业务逻辑绕晕。这套基于 Springboot Vue 的实现方案采用前后端分离架构前端负责页面渲染和用户交互后端通过 RESTful API 提供数据服务两边的代码仓库完全独立开发和部署也各自为战非常贴合目前企业里的主流开发节奏。我给你的建议是不管你手上拿的是毕设需求文档还是想在简历上多一个拿得出手的项目都不用把这个系统做得特别“大而全”。把订房、退房、入住、房间状态管理、客户管理这几个核心模块打通让数据在前端页面、后端接口、数据库三个环节里真实地流转起来就远比堆砌一堆中看不中用的功能有意义得多。1.2 为什么偏偏是 Springboot Vue先说后端。Springboot 在 Java 领域里的地位差不多类似于“开箱即用的标准件”。它通过自动装配机制把 Spring 生态里的各种组件组合好你不用再像以前的 SSM 项目那样写一堆繁琐的 XML 配置。比如我们要在系统里操作数据库引入 MyBatis-Plus 的依赖后加上数据源配置写一个继承 BaseMapper 的接口就能拥有单表增删改查能力。这种开发体验对新手非常友好也适合快速把项目跑起来而对准备找工作的同学来说Springboot 本身也是目前国内中小型后端岗位的绝对主流学它不会走弯路。前端选 Vue 而不是 React 或 Angular道理类似。Vue 的学习曲线相对平缓模板语法直观配合 Element UI 或 Element Plus 组件库你甚至不用亲自写复杂的 CSS 样式就能拼出一个像模像样的管理后台界面。组件化开发、响应式数据绑定、Vue Router 路由管理、Pinia(Vuex) 状态管理这几板斧在绝大多数管理类系统里完全够用。加上 Vue 的中文社区资料极其丰富你想查的每一个问题前人基本都踩过坑并留下了解决方案。再说前后端分离这件事。最早期的 Web 项目是后端用 Thymeleaf 或 JSP 直接渲染 HTML前端页面本质上是后端工程的“附属品”。这种模式的缺点在于前端改动一点点样式后端就得重新编译打包。前后端分离之后前端项目通过 axios 发 Ajax 请求调用后端接口两者只要约定好数据结构开发起来互不阻塞测试和生产环境下前端还能独立部署到 Nginx分担一部分并发压力。酒店系统的页面不算复杂但用它来完整走一遍前后端分离的流程后面接触更大规模的项目时思路就顺了。提示如果你是在 IDEA 里从零开始建项目不要选 Spring Initializr 里最新的快照版本。比如 Springboot 3.x 搭配 JDK 17很多老教程里的代码不能直接迁移。做这个酒店管理系统我推荐 Springboot 2.7.x JDK 8/11 的组合生态稳定、资料齐全、踩坑成本最低。1.3 系统的功能模块和角色权限怎么划分酒店管理系统通常涉及两类角色前台操作员和管理员。前台负责日常的订房、入住、退房操作管理员除了这些功能外还要维护房间信息、查看经营报表、管理员工账号。放在系统设计里就是两张有上下级关系的菜单结构。模块划分我建议这样切登录认证模块基于用户名密码登录后端生成 Token 或使用 Session 记录状态前端通过路由守卫拦截未登录用户。房间管理模块维护房型大床房、双床房、套房、门牌号、楼层、单价、状态空闲/已订/入住中/清洁中。这个模块是酒店系统的“地基”数据脏了后面所有订单都会跟着乱掉。订房管理模块支持预订、取消预订、入住登记、退房结算。核心逻辑是客房的状态流转每一步都要校验当前房间状态是否允许操作。客户管理模块记录客户姓名、证件号、手机号支持从订单里自动关联客户也可以在客户列表里直接发起订房。统计报表模块按日/月统计入住率、营收金额、各房型热度。报表数据可以从订单表里聚合查询不需要引入额外的大数据组件。权限控制是很多新手容易忽略的点。最简单的实现方式是后端用拦截器校验登录状态并用角色字段区分操作权限普通操作员不能访问员工管理接口管理员则全量放行。如果你用 Spring Security一时半会儿配不明白那先用拦截器做一个轻量级版本也能跑关键是别为了炫技把项目拖死。整个系统的数据流是这样的前端页面通过 axios 请求后端接口 → 后端 Controller 接收请求并校验参数 → Service 层处理业务逻辑 → Mapper 层操作数据库 → 结果逐层返回前端 → 前端更新页面状态。你把这个环节摸透了任何管理系统的开发套路都大差不差。2. Springboot 后端搭建与核心业务实现2.1 用 IDEA 创建工程并搞定依赖配置我默认你用的是 IDEA 2022 以上版本。打开 IDEA选择 New Project → Spring Initializr左侧勾选 Java右侧选 Maven 或 Gradle 构建工具。这里需要留意的有几个参数Java 版本选 8 或 11对应 Springboot 2.7.x 版本。Group和Artifact填你自己的组织名比如 com.hotel 和 hotel-system。依赖Spring Web、MyBatis Framework或 MyBatis-Plus后面细说、MySQL Driver、Lombok。需要做权限验证的话再加 Spring Security如果先不做就跳过用一个自定义拦截器也能实现基本登录校验。项目创建成功后默认会生成一个带有 SpringBootApplication 注解的主启动类。这个注解有三个核心作用SpringBootConfiguration 标记当前类为配置类EnableAutoConfiguration 开启自动装配ComponentScan 扫描当前包及子包下的组件。启动项目时你会看到 Spring 的 Logo 和一堆 INFO 日志到这一步最小的后端骨架就通了。接下来是配置文件。在 resources 目录下找到 application.yml把数据源、端口、日志格式都写进去server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/hotel_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl mapper-locations: classpath:mapper/*.xmlMySQL 8.x 的驱动类名是com.mysql.cj.jdbc.Driver而且必须要指定 serverTimezone否则连接池启动时会直接报时区错误。你要是用 MySQL 5.7驱动类名用com.mysql.jdbc.Driver也能跑但我建议直接用 8.x毕竟现在新装的环境大多是 8.0。注意如果你引的是 MyBatis-Plus推荐记得检查 Springboot 版本和 MyBatis-Plus 的兼容性。MyBatis-Plus 3.5.x 配 Springboot 2.7.x 是比较稳的组合直接引用mybatis-plus-boot-starter即可它会自动带上 MyBatis 相关依赖不需要手动额外引入。2.2 自动装配原理与开发配置类的正确姿势既然用 Springboot绕不开的一个面试考点就是自动装配原理。它在酒店系统里的直观体现就是你明明没有写任何代码去初始化 ObjectMapper、DataSource、SqlSessionFactory结果一启动项目这些东西全都“自己出现”并且能直接注入到你的 Controller / Service / Mapper 里。用一个生活化的类比你去饭店吃饭不用自己买锅买菜买调味料因为后厨Springboot在你点单之前就把基础食材备好了。自动装配的核心机制是spring.factories文件里声明了一堆EnableAutoConfiguration配置类比如DataSourceAutoConfiguration、MybatisPlusAutoConfiguration它们配合条件注解如 ConditionalOnClass、ConditionalOnMissingBean判断当前类路径下有没有对应的依赖 jar有就自动创建并配置对应的 Bean没有就跳过。所以你把 spring-boot-starter-data-redis 加进 pom 后RedisTemplate 就能直接注入原因就是自动装配帮你在背后干完了组装工作。在实际项目中自定义配置类也依赖这套机制。比如你要配置一个跨域过滤器可以写一个类加上 Configuration 注解方法用 Bean 返回 CorsFilter 实例或者在 application.yml 里加自定义参数再用 ConfigurationProperties(prefix hotel) 绑定到一个配置类上启动时设置值相关的 getter/setter 会被自动填充。这个功能在给系统预留“可配置项”时特别好用比如把房价折扣比例、可提前预订天数之类的业务参数外置运营调整时不用重新打包代码。写配置类我有几句经验要分享能用 Springboot 提供的 starter 就优先用别自己手写一堆 Bean。比如要用 Redis加spring-boot-starter-data-redis在 yml 里配好 host 和 port直接注入 StringRedisTemplate 就行。ConfigurationProperties 类建议配上 Lombok 的 Data再结合 Component 或通过 EnableConfigurationProperties 注册省去一堆样板代码。配置类里尽量别写业务逻辑它的职责是“组织 Bean”不是“实现功能”。把业务逻辑留在 Service 层后续排查问题会轻松很多。2.3 数据库表设计与实体建模数据库设计是整个系统里最不能马虎的环节。你想想如果房间表和订单表之间的关联字段没设计好后面做入住率统计的时候SQL 写起来会异常痛苦。酒店系统的表结构没必要搞得太复杂我按这套设计实测跑下来完全够用房间表hotel_room字段名类型说明idbigint主键room_numbervarchar(20)门牌号如 1201room_typevarchar(20)房型大床房/双床房/套房floorint所在楼层pricedecimal(10,2)每日房价statustinyint0-空闲 1-已订 2-入住中 3-清洁中created_timedatetime创建时间updated_timedatetime更新时间订单表hotel_order字段名类型说明idbigint主键order_novarchar(32)订单编号前端展示用room_idbigint房间ID关联 hotel_roomcustomer_idbigint客户ID关联 hotel_customercheck_in_datedate入住日期check_out_datedate退房日期total_amountdecimal(10,2)订单总额statustinyint0-已预订 1-已入住 2-已退房 3-已取消operator_idbigint操作员IDremarkvarchar(255)备注其余表包括客户表hotel_customer、系统用户表sys_user。客户表存姓名、证件类型、证件号、手机号系统用户表存账号、密码密码用 BCrypt 加密后存储、角色字段1-管理员 2-前台操作员。订单状态的变化是整个系统的核心枢纽预订成功时状态为“已预订”客户到店后办理入住变为“已入住”结账退房变为“已退房”超时未到或客户主动取消则置为“已取消”。在业务代码里我强烈建议用状态机思维来约束这些流转不要允许直接“跳过”某个状态比如从“已预订”一步跳到“已退房”这在真实场景里是说不通的。实体类直接用 MyBatis-Plus 的注解映射即可。例如Data TableName(hotel_room) public class Room { TableId(type IdType.AUTO) private Long id; private String roomNumber; private String roomType; private Integer floor; private BigDecimal price; private Integer status; private Date createdTime; private Date updatedTime; }TableName用来指定实体类对应的数据表TableId(type IdType.AUTO)表示主键自增。如果你的字段命名和数据库列名不一致比如数据库是下划线风格实体是驼峰风格MyBatis-Plus 默认开启了驼峰映射所以 room_number 能自动对应 roomNumber不用额外写 TableField。2.4 后端接口分层的落地实现后端代码分层是标准的 Controller → Service → Mapper 三层结构。Controller 只负责接收请求、参数校验、调用 Service、封装统一返回结果Service 处理业务规则Mapper 负责和数据库打交道。这样分层的好处是后续加需求或换数据库表结构时影响面可以控制得很小。统一返回结果类需要先写好保证前后端联调时的数据结构是一致的。我习惯用一个叫做 R 的类Data public class RT { private Integer code; private String message; private T data; public static T RT ok(T data) { RT r new R(); r.setCode(200); r.setMessage(success); r.setData(data); return r; } public static T RT error(String message) { RT r new R(); r.setCode(500); r.setMessage(message); return r; } }前端拿到这个结构后统一判断 code 是否为 200再决定是展示数据还是弹错误提示。这个约定一旦定了后面所有功能模块的 CRUD 接口都遵循同一模式前端封装的 axios 拦截器里也能统一处理会话过期、服务异常等情况。以订房这个核心流程为例接口路径可以这样设计POST /api/order/reserve提交订房请求参数里带 roomId、customerName、customerPhone、checkInDate、checkOutDate。后端会根据入住日期和退房日期算出总天数、乘以房费并生成唯一订单号同时把房间状态改为“已订”。PUT /api/order/checkin/{orderId}办理入住将订单状态从“已预订”改为“已入住”房间状态改为“入住中”。PUT /api/order/checkout/{orderId}办理退房计算实际入住天数和最终金额订单状态改为“已退房”房间状态改为“清洁中”清洁完成后由管理员或保洁人员改为“空闲”。这些接口写起来逻辑不算难但有几个坑要提前避开。第一是日期计算跨月或跨年的入住天数建议用 Java 8 的ChronoUnit.DAYS.between(checkInDate, checkOutDate)来算千万别自己去扣日历天数。第二是并发订房问题如果两个前台同时操作同一间房的预订后端有可能出现库存“超卖”一样的重复预订。最稳妥的做法是在 Service 层先执行一个条件更新UPDATE hotel_room SET status 1 WHERE id ? AND status 0通过受影响行数判断是否抢到房间如果不等于 1 说明房间已被别人订走直接提示“房间已被预订”。房间管理模块和客户管理模块的增删改查相对常规用 MyBatis-Plus 的save、removeById、updateById、page方法即可。需要写复杂统计 SQL 的报表模块我会单独建一个 StatsMapper用自定义 SQL 或 Select 注解实现分组查询不要把所有统计逻辑都压到 Java 代码里用内存算数据量一大就卡得一匹。3. Vue 前端工程搭建与页面开发3.1 环境准备、项目初始化和依赖安装前端工程脱离后端的 Java 代码单独用一个目录维护。第一步是确保本地 Node.js 已安装。可以在命令行执行node -v和npm -v检查版本。Node 版本建议不低于 16Vue 3 项目配合 Vite 构建时对 Node 版本有一定要求。我之前见过有人 node 版本停留在 12结果跑 vue create 或 npm install 时报各种兼容性错误最后不得不重装环境。初始化 Vue 3 项目我直接推荐用 Vite 脚手架npm create vitelatest hotel-ui -- --template vue运行后进入 hotel-ui 目录执行npm install安装基础依赖。如果你的网络环境拉取 npm 包很慢可以把镜像源切换为国内镜像实测下载速度能提升好几倍具体命令这里不多啰嗦了网上搜“npm 镜像配置”就有全套方案。项目装完以后还需要装几个业务必需的依赖。路由管理装 vue-router4状态管理装 piniaUI 组件库装 element-plusHTTP 库用 axios。一次性安装的命令npm install vue-router4 pinia element-plus axios安装过程中最常遇到的是依赖版本冲突。比如某个组件库要求 Vue 版本至少是 3.2而你的项目模板锁定在 3.1此时 npm 会直接报 ERESOLVE 错误。解决方案很简单升级模板依赖或删掉 package-lock.json 重新 install。别在这种问题上一根筋行政手段删锁文件重装是最快的。3.2 Vue Router 路由与页面结构设计酒店管理系统的前端页面按功能模块划分即可典型的单页后台管理页面结构是左侧菜单 顶部导航栏 右侧内容区。路由配置对应菜单结构我建议按下面这种方式组织/login登录页独立布局不带侧边栏。/layout整体布局组件内部包含侧边栏和主内容区所有需要登录后访问的页面都作为它的子路由。/layout/dashboard首页看板展示今日入住率、今日营收、近期订单等汇总数据。/layout/room房间列表支持按房型/状态筛选。/layout/order订单列表支持按日期/状态筛选。/layout/customer客户管理列表。/layout/stats统计报表页面。用 Vue Router 配置时有一点特别容易踩坑如果后端返回给前端的 URL 是 history 模式比如http://localhost:5173/layout/order直接刷新页面会出 404这是因为开发服务器没有做 history 路由的降级处理。开发模式下可以在 vite.config.js 里配appType: spa或开启 historyApiFallback 来规避生产环境部署到 Nginx 时需要加try_files $uri $uri/ /index.html;这条配置。如果你不想折腾就把路由模式设为createWebHashHistoryURL 里带#的 hash 模式不会触发服务器端路由解析虽然丑一点但省心很多。我在后面部署小节会再强调一遍。页面之间的导航跳转用到的是 Vue Router 的编程式导航。比如房间列表中点击“预订”按钮需要把房间 id 传到订房页面可以通过路由参数携带router.push({ path: /layout/reserve, query: { roomId: row.id } })然后订房页在 onMounted 里通过route.query.roomId拿到这个值回显到页面上。这种方式简单直观符合“页面间传递参数”的常见诉求。此外还有动态路由参数params的用法比如/layout/room/detail/12适合详情页这种场景但它和 query 各有各的适用场景不必混用过度。3.3 axios 请求封装与接口联调前端不能直接连数据库只能通过 HTTP 请求和后端交互。如果每一个页面都手动写一遍 axios代码冗余严重而且异常处理很难保持统一。我的做法是封装一个 request.js 模块作为全局的请求入口。import axios from axios import { ElMessage } from element-plus import router from ../router const request axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截器自动携带 token request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }) // 响应拦截器统一处理错误码 request.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res }, error { if (error.response error.response.status 401) { ElMessage.error(登录已过期请重新登录) router.push(/login) } else { ElMessage.error(网络异常请稍后重试) } return Promise.reject(error) } ) export default request关于 baseURL 的写法开发环境常用/api这样的相对路径配合 Vite 的代理配置把/api开头的请求转发到后端的http://localhost:8080上从而规避开发环境下的跨域问题。在 vite.config.js 里加这一段server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: path path.replace(/^\/api/, ) } } }这样前端代码里写的request.get(/order/list)实际发出的请求是/api/order/list经代理转发后到达后端的地址是/order/list。前端的改动在后端 Controller 里完全感知不到两边各自开发互不干扰。我在实践中发现很多人一遇到跨域就急着在后端加 CrossOrigin 或配置 CorsFilter这当然是一种方案但在前后端分离项目里前端开发服务器代理其实是更顺手的姿势。等部署上线后Nginx 那一层的反向代理配置也能复用同样的思路。3.4 核心页面的交互逻辑实现以订房页面为例它是整个系统里交互逻辑最复杂的一个页面。需求是这样的用户从房间列表点“预订”跳到订房页面页面展示房间号、房型、单价信息用户填写客户姓名、手机号、入住日期、退房日期系统自动计算总金额点击提交后调用后端接口。日期选择这块我用 Element Plus 的 el-date-picker限定typedaterange并设置disabled-date禁止选择今天以前的日期。比较关键的是金额计算单价来自房间数据总金额 单价 × 入住晚数。晚数用 JavaScript 的 Date 对象计算const nights Math.ceil((checkOutDate - checkInDate) / (1000 * 60 * 60 * 24))注意这里有个业务细节退房时间通常是在入住日的第二天中午所以 6 月 1 日入住、6 月 3 日退房实际是住两晚晚数等于日期差而非天数差加一。我建议在项目里统一约定“按日期差算晚数”前后端保持一致并在页面上提示用户避免后续扯皮。提交时先做表单校验Element Plus 的 el-form 自带 rules 校验机制比如手机号正则、入住人必填。校验通过后调用request.post(/order/reserve, form)。提交成功的反馈用一个 ElMessage.success 提示然后跳转回订单列表页。订单列表页的筛选逻辑也不复杂搜索条件可能有“订单状态”和“日期范围”把筛选条件作为查询参数传给后端分页接口后端用 MyBatis-Plus 的 lambdaQueryWrapper 拼装条件即可。前端展示时要注意后端返回的订单状态是数字页面要映射成对应的 tag 标签比如“已预订”是蓝色、“已入住”是橙色、“已退房”是灰色、“已取消”是红色。用 computed 或一个独立的函数处理映射别直接在模板里写一团三元表达式那样维护起来太难受了。3.5 Vue 打包后的常见布局与资源问题Vue 项目开发环境一切正常npm run build打包后却出现样式错乱、图片丢失、页面白屏这是高频问题。核心原因通常是打包路径配置不对。默认 Vite 的 base 是/如果你的项目部署在服务器根路径下的 Nginx 站点那没问题但如果你把打包产物放到www目录下的子路径里比如http://yourdomain/hotel-ui/那么资源文件会全部 404。解决方法是修改 vite.config.jsexport default defineConfig({ base: process.env.NODE_ENV production ? /hotel-ui/ : /, plugins: [vue()] })打包后的dist/目录里JS、CSS 资源路径会带上/hotel-ui/前缀。配合 Nginx 的 location /hotel-ui/ 配置就能正常访问。另一个布局异常和资源无关而是和路由模式有关。如果你用了 history 模式在 Nginx 配置里没有做 try_files 回退用户刷新子页面就 404此时页面部分组件加载不出来外观当然会“花”。我已经在路由小节提示过要么改用 hash 模式要么把 Nginx 的 fallback 配好。两个方案我实测都有效具体选哪个看你项目的对外需求内部管理后台我更倾向 hash 模式省心。4. 前后端联调、部署上线与常见问题排查4.1 联调阶段的两个关键准备工作后端接口写完、前端页面搭好之后就进入联调阶段。这里我强烈建议先别急着把 30 个接口一次性全部对接而是先打通一个最核心的“用户登录 → 获取房间列表 → 新建订单 → 查询订单”链路。链路通了说明网络代理、数据格式、权限校验机制都正常剩下的模块只是重复体力活。联调阶段最容易出问题的点我整理成一个速查表都是实际项目里反复踩过的坑你照着排查能省好几个小时问题现象常见原因解决方案前端请求报 404后端接口路径拼错或代理未生效打开浏览器 Network 面板看请求 URL对比后端 Controller 的 RequestMapping前端请求报 405请求方法类型不匹配GET/POST/PUT检查前端 method 与后端 GetMapping/PostMapping/PutMapping 是否一致请求报 401 Unauthorized登录 Token 缺失或过期检查路由守卫是否需要放行登录页确认 Token 是否写入请求头请求报 500后端代码异常查看 IDEA 控制台完整堆栈优先排查空指针和 SQL 语法错误前端能访问后端但数据为 nullJSON 序列化或字段名不一致确认后端实体是否缺少 getter是否配置了 JsonFormat 处理日期数据库插入中文乱码连接串未指定 characterEncoding在 JDBC URL 里加 useUnicodetruecharacterEncodingutf8订房时高并发重复预订房间状态更新不是原子操作用条件更新UPDATE hotel_room SET status1 WHERE id? AND status0防止超卖打包部署后刷新子页面 404history 路由模式未配置 fallback改造 hash 模式或 Nginx 加 try_files 规则前端排查问题时不要只盯着页面报错弹窗打开浏览器 DevTools 的 Network 面板直接看具体请求的请求头、请求体、响应体基本一眼就能定位是前端问题还是后端问题。后端排查时也不要只看“哦报 500 了”把 IDEA 控制台的异常堆栈从头看一遍大多数时候原因就直接写在第一行。4.2 项目部署前端 Nginx 托管后端 Jar 包运行联调通过后就到了打包部署环节。后端在 IDEA 的 Maven 面板里执行mvn clean package -DskipTests在 target 目录下会生成一个可执行的 Jar 包比如hotel-system-0.0.1-SNAPSHOT.jar。然后在服务器上执行java -jar hotel-system-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod生产环境的数据库地址、账号密码建议通过外部配置或环境变量注入不要硬编码在代码里。比如在 application-prod.yml 里配置独立的数据库链接启动时用--spring.profiles.activeprod指定。也可以用--spring.config.location指定外部配置文件这样改配置不用重新打包。前端在项目根目录执行npm run build把 dist 目录里的静态文件上传到服务器 Nginx 的 html 目录下。以 Linux 服务器 Nginx 为例一个典型配置如下server { listen 80; server_name your-domain.com; location / { root /usr/share/nginx/html/hotel-ui; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这个配置里最关键的是location /api/的反向代理。前端代码里请求的是相对路径/api/xxx经 Nginx 转发后到达后端的实际路径就去掉了/api前缀取决于你 proxy_pass 的写法。生产环境和开发环境的请求路径保持一致前端代码在两种环境下的行为就是一致的能避免不少低级问题。部署完成后的检查步骤也有讲究先访问 Nginx 的静态页面确认前端能打开再用浏览器直接访问http://your-domain.com/api/order/list如果能返回 JSON 数据说明反向代理通了最后再完整走一遍登录和订房流程。小步验证出问题了能很快定位在哪一层。注意服务器 Linux 环境默认防火墙可能拦截 8080 端口如果你把后端 Jar 直接暴露出来记得在安全组里放行或被 Nginx 统一代理到 80 端口。实际生产环境不要让用户直接访问 8080尽量让 Nginx 统一入口这样安全性和维护性都更好。4.3 项目扩展方向从毕设到完整商用系统的差距酒店管理系统做完基础功能还能往上加的东西其实不少。你完全可以在现有代码基础上往这两个方向扩展一是引入 Redis 提升性能和体验。酒店系统里房间状态是高频读取、中低频修改的数据可以用 StringRedisTemplate 把房间列表和房态缓存到 Redis前端查询房间时直接命中缓存退房或订房时再更新缓存。另外一个典型用法是并发订房时的分布式锁用 Redis 的 setnx 命令对 roomId 加锁避免两个请求同时操作同一房间的原子性问题比数据库条件更新更优雅也更能应对并发量稍大的场景。二是接入成熟的后台管理脚手架。现在国内用得非常多的若依RuoYi框架就是一套基于 SpringBoot Vue 的通用管理系统它已经把用户、角色、菜单权限、操作日志、定时任务这些通用功能做成了现成的模块。如果你不想从零开始搭权限体系直接基于若依二次开发酒店业务模块也是很多公司的实际做法。我的建议是你在这个项目里亲手把登录、拦截器、动态菜单这些基础能力实现一遍理解原理等到工作项目里再用若依这类框架提效两个阶段的目标完全不同。系统上线后最好再加一层操作日志记录。谁在什么时间对哪个订单做了什么操作全部落到一张操作日志表里这对排查问题和追责很重要。Spring 的 AOP 切面技术可以在不改动业务代码的前提下为所有方法自动记录操作日志性价比极高。比如定义一个 Log 注解写一个切面拦截带有该注解的 Controller 方法自动获取当前登录用户、请求参数、响应结果异步写入数据库。这个小功能看着不起眼但在真实的酒店管理场景里前台和上级之间、交接班之间经常需要回溯操作记录。4.4 使用 PostgreSQL 替换 MySQL 的差异说明最近不少同学问过我能不能把数据库换成 PostgreSQL当然可以。Springboot 对 PostgreSQL 的支持很好核心改动集中在依赖和连接配置上。首先在 pom.xml 中替换数据库驱动dependency groupIdorg.postgresql/groupId artifactIdpostgresql/artifactId scoperuntime/scope /dependency然后是 application.yml 的连接配置spring: datasource: driver-class-name: org.postgresql.Driver url: jdbc:postgresql://localhost:5432/hotel_system username: postgres password: 123456和 MySQL 相比这里有两点明显差异需要适应。第一点是自增主键的写法MySQL 是AUTO_INCREMENT而 PostgreSQL 用SERIAL或GENERATED ALWAYS AS IDENTITY。在建表语句里要相应调整。第二点是分页查询的方言不同MyBatis-Plus 内置了分页插件通常能自动识别方言但如果你的统计 SQL 里用了 MySQL 的LIMIT、GROUP BY方言特性迁移时就需要手动改写为 PostgreSQL 兼容的写法。日期时间函数也有差异比如 MySQL 的NOW()在 PostgreSQL 里是CURRENT_TIMESTAMPDATE_FORMAT要换成TO_CHAR。整体迁移难度不大但建议先把表结构和涉及日期统计的 SQL 列出来逐条做语法兼容测试。从选型角度看如果你的部署环境是企业里的统一基础设施已经提供了 PostgreSQL 实例那就没必要非装一个 MySQL。但如果你只是本地开发或做毕设用 MySQL 的参考资料明显更多遇到问题更容易搜到答案。技术选型要根据实际条件来不要为了显得“高级”而刻意换库。4.5 移动端适配问题的简单处理方案酒店管理系统的使用场景通常在前台电脑上但偶尔也会有人用平板或手机访问页面。Element Plus 组件库本身就做了响应式适配在一般尺寸的屏幕上表现还过得去。但管理后台的表格在手机窄屏上经常会被挤压出现横向滚动或错位。一个不折腾的解决思路在 index.html 的 meta 标签里设置widthdevice-width, initial-scale1.0保证移动端视口宽度正确页面布局用 flex 和 grid 组合侧边栏在窄屏自动收起只保留顶部导航和内容区。Element Plus 的 el-menu 组件自带collapse属性通过监听窗口 resize 事件动态切换折叠状态可以比较快地让菜单适应移动端。由于酒店后台操作频率高、字段多掌上体验完全跟桌面端一致不太现实。我的建议是后台管理页面保证“能用”真正需要为移动端精心打磨的应该是客户自助查询界面比如扫码查看房间价格、在线提交预订信息这类轻量页面可以再单独做一个 H5 项目用 Vant 这类移动端组件库来开发而不是强行让后台管理系统去适配小屏。5. Springboot 版本选择与面试级知识巩固5.1 版本选择策略别选太新稳定压倒一切这两年 Springboot 版本迭代非常快Springboot 3.x 已经全面普及JDK 17 也逐渐成了标配。但在我做这个酒店管理系统的过程中依然建议优先考虑 Springboot 2.7.x原因不是 3.x 不好而是两大现实问题第一是生态兼容性。很多老牌的第三方库比如一些非官方的 starter、旧版的 MyBatis-Plus还没有完全适配 Springboot 3。Springboot 3 迁移到 Jakarta EE 规范之后javax.*包变成了jakarta.*如果你参考的教程是两年前写的代码里大量import javax.servlet.*直接编译失败。第二是参考资料的可获取性。解决 Springboot 2.7 的报错脚本、博客文章、社区问答一搜一大片而 Springboot 3 的新问题往往只有官方文档和 GitHub Issue 能查对新手不算友好。所以版本策略一句话选主流不选最新。等你把 Springboot 2.7 上的项目吃透了再去迁移 3.x 也就是顺手的事核心思想完全是相通的。5.2 面试常问的 Springboot 与 Vue 核心知识点项目做完下一步大概率就是面试环节。面试官看到“酒店管理系统”这类项目常问的问题其实非常集中我帮你提前梳理一遍答题思路Springboot 自动装配原理面试官会问你“为什么加了 starter 就能直接用功能”。你可以从 SpringBootApplication 的三个组合注解切入重点讲 EnableAutoConfiguration 的机制它通过spring.factories或AutoConfiguration.imports文件加载候选配置类再配合 ConditionalOnClass、ConditionalOnMissingBean 等条件注解按需装配 Bean。再举个本项目的例子因为引入了 Redis 的 starter且类路径下有 RedisTemplate 相关类所以 RedisAutoConfiguration 生效直接注入即可。Springboot 配置加载顺序常见的考点有 bootstrap.ymlSpring Cloud 场景、application.yml、application-{profile}.yml、环境变量、命令行参数的优先级。实际开发里我们常用外部配置文件覆盖默认配置比如生产环境数据库密码放在启动命令里或配置中心既保证代码仓库不泄露密钥又能灵活调整。Vue 生命周期和响应式原理面试官想知道你用的是“框架”还是“会框架”。Vue 3 的 setup 语法糖、onMounted、computed、watchEffect 这些要能熟练说出使用场景。响应式原理则是基于 Proxy 实现数据劫持初始化时会给每个属性生成对应的 effect属性变化时触发依赖更新从而驱动视图重渲染。在酒店系统里表单数据绑定、订单状态切换后的列表刷新都用到了这套机制可以结合项目细节来讲。Vue 组件通信方式父子组件用 props 和 emit跨级组件用 provide/inject全局共享状态用 Pinia。在项目里登录后的用户信息、侧边栏菜单的折叠状态就是典型的 Pinia 场景。把这些真实用法讲清楚面试官比较容易信服你的项目是自己做出来的。如果把这些知识点都吃透了这套系统不仅是一个“能跑的项目”更是一份妥妥的“面试素材库”。我在实际开发过程中总结出一个习惯每做完一个模块顺手记一条该模块的“一句话总结”。比如“房间状态流转用条件更新防止并发脏数据”“前端路由守卫负责页面访问控制后端拦截器负责接口访问控制两者缺一不可”。这种总结积累多了无论是面试嘴替还是未来跳槽整理项目经验都特别省力。一套能跑通的酒店管理系统价值不在代码量有多大而在于它帮你把全栈开发的核心链路完完整整地走了一遍。你在找 bug、调接口、跑部署的过程中绞尽脑汁解决的那些问题才是这套系统给你留下的最值钱的东西。
返回列表