
最近在整理客栈管理系统这类项目时发现很多做毕设或者小规模商业项目的朋友都会把技术栈定在 Spring Boot 加 Vue 上。“岳记客栈管理系统”这个名字听起来像个具体案例但它背后其实代表了当下 Web 项目里最典型的一类需求中小型住宿业态的信息化管理。这篇文章我会从需求拆解、技术选型、数据库设计、后端接口、前端页面到最终部署把这个系统的完整设计思路和落地过程讲清楚。项目本身不算复杂但胜在链路完整适合想系统掌握前后端分离开发的读者作为参考模板。这套系统解决的核心问题是传统客栈经营里那些靠纸笔和口头沟通带来的麻烦。前台要手工登记入住、退房财务要对着 Excel 核对订单老板想看今天的入住率还得等前台下班后手动汇总。换成 Web 系统之后前台在浏览器里就能完成开房、退房、换房、结账订单数据实时落到数据库里老板随时打开手机或电脑就能看到经营报表。对于没有专职 IT 人员的小客栈来说这种部署简单、上手门槛低的浏览器访问模式是性价比最高的方案。1. 项目整体设计与技术选型思路1.1 客栈管理的核心需求到底有哪些很多人拿到这类题目第一反应是“做个增删改查”但实际上客栈管理系统的核心难点不在 CRUD而在业务流程的完整性。我自己在梳理需求时会把整个系统拆成五个业务域客房管理、预订管理、入住退房、订单结算、经营统计。客房管理不只是维护房型和价格还要处理房间状态。一个房间的状态可以是空闲、已预订、已入住、打扫中、维修中这五种状态之间的流转是有业务规则的。比如只有空闲和打扫中的房间才能被预订维修中的房间不能被分配。这个状态机如果不在设计阶段理清楚后面实现时很容易出现“房间被重复预订”这种致命问题。预订管理是客栈和酒店最大的区别点。酒店的预订通常按夜计算但客栈经常出现钟点房、半天房、包月房多种计费方式共存。岳记客栈这种中小型客栈还会遇到客人电话预定后到店时间不确定、口头保留房间到几点这类模糊需求。系统里必须支持预订单的创建、确认、取消、未到店自动释放每个操作都要有对应的状态变化记录。入住退房流程要联动押金和房费。入住时可以收押金退房时根据实际入住时长计算房费如果是钟点房还要判断是否超时。这些计算逻辑看起来很基础但在实际开发中日期时间跨天的处理、不同房型的计费规则叠加很容易出 bug。我在做这个项目时有一个体会把费用计算的逻辑独立成一个服务不要散落在各个界面代码里后续加规则会轻松很多。1.2 为什么选 Spring Boot 加 Vue 这套组合技术选型这件事很多初学者容易纠结但对我来说基本不用犹豫。后端用 Spring Boot前端用 Vue这是一个在中小型团队中被验证过无数次的黄金组合。Spring Boot 的优势在于它把 Spring 生态的复杂度封装掉了大部分。建立一个 Web 项目以前要配一堆 XML现在一个启动类加几个注解就能跑起来。内置的 Tomcat 省去了单独部署服务器的步骤打成一个 JAR 包丢到服务器上就能运行。对于客栈这种数据量不大但需要稳定运行的场景单机部署加定时备份就足够了完全不需要引入微服务那套重架构。Vue 的优势在于渐进式开发。客栈管理系统里客房展示页面、后台管理页面、订单处理页面交互复杂度差别很大。Vue 的单文件组件方式让页面代码的组织非常清晰Element Plus 这类组件库又提供了现成的表格、表单、日期选择器让开发效率翻倍。我遇到过一些项目用传统模板引擎做页面和接口代码混在一起改个样式都要小心翼翼前后端分离之后这种问题直接消失了。对比一下其他方案。如果全用 JSP 加 Servlet 做开发快但后期维护比较痛苦尤其是页面样式和前端交互稍多一点就非常吃力。如果上若依这类快速开发平台确实能省很多事但项目结构太固定很多代码是自动生成的出了问题时排查成本反而高。自己从零搭一个 Spring Boot 加 Vue 的项目虽然前期会多花一点时间但每一行代码都是自己写的逻辑可控性最强对学习者的提升也最大。1.3 从业务需求到数据模型的设计过程数据库表设计是整个项目的地基。我习惯先画一份简单的表关系图再开始写建表语句。岳记客栈管理系统里最主要的表是这几张房间表、房型表、预订表、入住表、订单表、客户表、用户表、操作日志表。房型和房间是分开的两张表原因很简单一个房型下可能有多个房间比如大床房这个类型下有 101、102、103 三个房间。如果只建一张房间表把房型信息直接写在每条记录里后面房型价格调整时要改很多条数据而且容易出现数据不一致。分开建表后只要改房型表里的价格所有该房型的房间自动生效。预订表和入住表是很多初学者容易搞混的地方。我的理解是预订是意向确认入住是实际发生。客人电话订了明天的一间大床房此时只有预订记录房间状态变为已预订。明天他到店办理入住才生成入住记录房间状态变为已入住。退房时关闭入住记录同时生成或更新订单记录。这里之所以不用一张表解决是因为一个预订单可以拆成多个入住记录比如客人订了两个晚上但中间一天换了一个房型这种场景必须要两张表才能表达清楚。订单表和入住表之间是一对多的关系。一次入住产生的费用包括房费、押金、可能的加床费、赔偿金最终汇总到一张订单里。设计订单表时我会预留一个明细表的位置也就是订单明细表用来记录每一项收费的构成。这样万一发生费用争议前台可以逐项核对老板也能看到营收的来源结构。客户表在客栈系统里承担的是会员信息管理的职责。姓名、手机号、身份证号、历史入住次数、累计消费金额这些字段既能用于登记也能支撑“回头客”营销。我在设计时加了用户等级字段在实际项目中可以直接和会员折扣联动。2. 后端核心模块实现与业务逻辑2.1 Spring Boot 项目结构与公共配置后端项目我习惯按功能模块分包而不是按技术层次分包。所谓按技术层分包就是把 Controller 全放一个包、Service 全放一个包、Mapper 全放一个包。按功能模块分包则是把和客房相关的 Controller、Service、Mapper 放在同一个包下订单相关的也放在一起。项目规模小的时候体会不到区别一旦业务逻辑变多按功能模块分包会让代码的可维护性明显更好。公共配置部分有几个要点必须处理。第一个是跨域配置。前端 Vue 开发服务器默认跑在 8080 端口后端 Spring Boot 跑在 8081 端口两边端口不同浏览器就会拦截带自定义头的请求。解决方式是在后端加一个 WebMvcConfigurer配置允许的跨域来源、请求头和请求方法。开发环境可以用通配符放行生产环境一定要指定具体的域名。第二个是统一返回结果。我定义了一个 Result 类包含 code、message、data 三个字段。所有的接口都返回这个结构成功时 code 是 200失败时是其他业务码。前端 axios 拦截器里统一判断 code这样处理错误就非常集中不会出现每个页面都写一遍报错处理逻辑的情况。统一返回结果看着是小事但联调时能省下大量沟通成本。第三个是全局异常处理。用 RestControllerAdvice 注解定义一个全局异常处理类业务异常、参数校验异常、数据库异常分别定义不同的处理方法。这样后端代码里只需要抛出异常不需要每个方法里都写 try-catch返回给前端的信息也能保持一致。2.2 JWT 登录认证和权限控制的落地实现客栈管理系统的用户角色比较简单主要是管理员和前台操作员。管理员可以查看报表、维护房价、管理用户前台操作员主要负责日常的开房退房操作。虽然角色少但权限控制仍然是必须的不然任何一个收银员都能改房价业务上就说不过去了。我用的是 JWT 加拦截器的方案。用户登录成功后后端根据用户 ID、用户名和角色生成一个 tokentoken 的有效期我设成 12 小时这样前台早班晚班各一个工作日期间不需要反复登录。前端把 token 存在 localStorage 里每次请求时在请求头里带上。后端写一个拦截器从请求头中解析 token校验签名和过期时间再把用户信息放到当前请求的上下文中。密码存储方面直接存明文是绝对不行的。我使用的是 BCrypt 加密算法它的特点是同一个密码每次加密后的结果都不一样因为每次加密都会随机生成一个盐值。这样即使数据库泄露攻击者也无法通过彩虹表反推出原始密码。注册用户时加密登录时校验Spring Security 的加密工具类可以直接用不需要额外引入整套安全框架。权限控制上用了一个比较轻量的方式在需要管理员权限的接口上使用自定义注解拦截器里检查当前用户的角色。其实客栈这个场景用拦截器做粗粒度控制就足够了不需要引入 Shiro 或 Spring Security 这种重量级框架杀鸡不用牛刀。2.3 预订和入住环节的日期冲突处理这是整个项目里最容易出问题的业务逻辑。一个房间在同一个时间段内只能有一个有效的预订或入住记录。数据库层面要防住并发下的重复预订业务层面还要处理好取消、换房、提前退房这些变动。我的方案是在预订表里加一个唯一约束字段是房间 ID 和日期的组合。房间预订的最小时间单位设计为“间夜”也就是一个房间一晚。预订表里记录入住日期和离店日期每次新增预订时用一条 SQL 检查该房间在目标日期区间内是否已有冲突记录。SQL 的判断逻辑不复杂但要注意边界条件已有的预订是 10 号到 12 号新的预订如果是 12 号到 13 号这是允许的因为 12 号中午旧客人退房后新客人可以入住。所以判断条件是新增的入住日期要大于等于已有记录的离店日期或者新增的离店日期要小于等于已有记录的入住日期两者满足其一就不冲突。日期时间处理上有个容易踩的坑Java 里的日期格式化。前端传给后端的时间通常是字符串比如“2024-05-01”如果实体类字段类型是 Date需要在字段上加 JsonFormat 注解指定时区否则默认会用 UTC 时区解析导致结果差 8 小时。我第一次做的时候没注意所有订单的日期都提前了一天查了半天才发现是时区问题。2.4 费用计算的设计思路费用计算这块我单独建了一个 FeeCalculator 类专门负责算钱。为什么单独拆出来因为客栈的计费方式太杂了不是简单的单价乘以天数。首先要支持不同房型的阶梯价格。比如某个大床房的日常价格是 158 元每晚周五周六是 188 元每晚。客人住了三晚其中一晚是周五那这晚就按 188 算其他按 158 算。实现方式很简单房型表里增加周中价和周末价两个字段计算时遍历日期区间判断每一天是周中还是周末。其次要支持钟点房。钟点房通常按 3 小时或 4 小时计费超过时间后按小时加收费用。这种计费方式不能用“间夜”来计算所以我在房间表里加了一个房型类别字段标准房和钟点房用不同的计算逻辑。押金管理这里我建议单独记录不要直接和房费混在一起。客人入住时缴纳押金退房时先计算房费和其他消费用押金抵扣多退少补。如果放在同一个字段里后面查账会非常混乱。3. 前端页面搭建和核心交互实现3.1 Vue3 加 Element Plus 搭建后台基础框架前端的技术选型我用的是 Vue3 加 Vite 加 Element Plus。Vue2 目前已经停止维护了新项目没有必要再用。Vite 的冷启动速度和热更新体验比 Webpack 好很多尤其对于客栈管理这种页面数量不少的项目Vite 能明显提升开发效率。Element Plus 是 Vue3 对应的组件库版本表格、表单、弹窗、日期选择器这些后台系统常用的组件都很完善能少写很多重复代码。项目目录按视图、组件、路由、状态管理、工具函数来组织。views 目录下放页面级组件比如客房管理、订单管理、登录页、首页仪表盘。components 目录放复用组件比如房间状态卡片、订单表格。router 单独一个文件配置路由包含嵌套路由和路由守卫。状态管理用的是 Pinia比 Vuex 更轻量在 TypeScript 环境下类型推导也更友好。路由守卫这一步很重要。未登录的用户访问后台的任何页面时需要被重定向到登录页。判断逻辑不复杂读取 localStorage 里的 token没有就跳登录页。已登录用户访问登录页时直接重定向到首页。这两个判断在 beforeEach 里做就行。3.2 客房列表和预订流程的交互细节客房列表页面是整个系统里最常用的页面前台操作员一天要看无数遍。我的设计是默认展示所有房间每个房间做成一张卡片卡片上显示房间号、房型、床型、价格和当前状态。空闲的房间高亮显示已入住的房间置灰这样操作员一目了然。房间卡片上的点击事件要区分角色。管理员点击卡片进入房间详情可以修改价格或设置维修状态。前台操作员点击空闲房间的“预订”按钮弹出一个预订表单包含入住人姓名、手机号、入住日期、离店日期。这里有个交互细节日期选择器要默认设置为今天和明天因为大部分到店客人都是即时入住的减少操作步骤能明显提升前台的工作效率。预订提交时前端要做一个双重校验。第一层是表单校验检查手机号格式是否正确、离店日期是否晚于入住日期。第二层是业务校验提交前要再次确认房间状态还是空闲防止另一个窗口已经抢先预订了。平行业务里预订和入住是两个独立的按钮点击后都会向后端发起请求后端根据房间当前状态决定是接受还是拒绝。这个冲突处理在前后端都要做前端提升体验后端保证正确。3.3 经营报表页面的数据可视化老板最关心的是挣了多少钱、入住率是多少。经营报表页面就是给老板看的。我的实现方式是首页放四个核心指标卡片今日房费收入、今日入住率、今日预订单数、本月累计营收。下面再接一张近七天的订单趋势折线图一张房型收入占比的饼图。图表我用的 ECharts。ECharts 的引入方式要注意如果按需引入只用折线图、饼图、柱状图这几个核心组件打包体积会小很多。Vue3 项目里用 ECharts 时最好对图表初始化做一个封装组件销毁时销毁图表实例避免内存泄漏。我在最初写的时候没注意这个问题页面频繁切换后浏览器内存不断上涨后来查资料才知道是图表实例没有销毁。报表的数据接口是纯查询接口参数是开始日期和结束日期。前端默认展示最近七天提供一个日期范围选择器。后端接口返回的数据直接用聚合查询算出结果不在 Java 代码里做循环求和。能在数据库层面完成的计算就尽量在数据库完成效率和代码简洁性都好很多。4. 系统部署上线与常见问题处理4.1 前后端分离项目的部署方式开发环境下前端跑在 Vite 开发服务器后端跑在 Spring Boot两边通过代理联调。但生产环境只需要一台服务器和一个域名前端构建成静态文件由 Nginx 托管后端作为一个 S