☰
Spring Boot + Vue全栈实战:从零搭建旅游民宿管理系统
2026/10/28 3:49:42 网站建设 项目流程

1. 项目概述与需求分析

1.1 为什么做旅游民宿管理系统

做这个系统的起因很直接:身边一个做民宿的朋友,旺季的时候每天要接几十个电话、回几十条微信消息,订单记在本子上还经常出错,房间有没有卖出去全靠脑子记。我当时就想,这不就是一个典型的业务管理系统吗?房源管理、订单管理、客户管理、价格管理,这些需求聚合到一起,用Spring Boot + Vue来做一套前后端分离的管理系统再合适不过。

市面上的旅游民宿管理系统不少,但要么是SaaS平台按年收费,要么是功能臃肿用不上。自己做一套的好处是可控性极强:想加字段就加字段,想改逻辑就改逻辑,数据在自己手里,后续接小程序、接公众号都方便。这套系统适合谁?适合有Java后端基础、想系统学习Spring Boot + Vue全栈开发的人,也适合民宿运营者想自己搭建一套小型管理后台。

1.2 核心需求拆解

旅游民宿管理系统,核心关键词是“旅游”和“民宿”。旅游意味着有淡旺季、有不同的客源渠道;民宿意味着房间数量少、房型多样、每间房的差异化定价。结合这些特征,我在做需求分析时把系统拆成了几个核心模块:民宿信息管理、房间房型管理、在线预订管理、订单管理、客户管理、评论管理、数据统计。

每个模块往下再拆:民宿信息管理要支持多门店,因为很多民宿老板不止一家店;房间管理要支持按日期设置价格,因为周六日和节假日价格完全不同;订单管理要处理预订、入住、退房、取消、退款这几个状态流转;数据统计要能按天、按月看入住率和营收。把这些需求梳理清楚,系统的大框架就出来了。

还有一个关键点:面向的管理者是谁?民宿的工作人员,不是程序员。所以操作界面必须简单直接,不能像给程序员用的后台那样满屏英文缩写。这也是我后来在Vue前端设计中重点考虑的因素。

2. 技术选型与系统架构设计

2.1 为什么选Spring Boot + Vue

Spring Boot和Vue的组合,放到今天已经是Java全栈开发的黄金搭档。Spring Boot解决了传统Spring配置繁琐的问题,内嵌Tomcat、自动配置、开箱即用,做一个管理系统的后端服务非常顺手。Vue则是渐进式JavaScript框架,组件化开发让页面拆解和维护变得清晰,配合Element UI组件库做后台界面效率极高。

相比用若依这类现成框架,我这次选择从零搭建,原因有两点:一是若依默认集成了一堆用不上的功能,二次开发时需要踩坑理解它的封装逻辑,反而费劲;二是从零搭一遍能对每个环节都心里有数,后期扩展也好控制。当然,如果是企业级快速交付,直接用若依改是明智的选择,这取决于你的目标是什么。

前后端分离的架构让我在开发时能并行工作:前端用Vite起开发服务器,后端用Spring Boot跑接口,通过代理转发解决跨域。部署时前端打包成静态文件交给Nginx托管,后端打成Jar包运行,整体结构干净利落。

2.2 系统整体架构设计

这套系统的架构分为四层:接入层、应用层、服务层、数据层。接入层是Nginx,负责托管前端静态资源、反向代理后端接口、配置HTTPS证书;应用层是Vue单页应用,负责页面渲染和用户交互;服务层是Spring Boot提供的RESTful API,处理业务逻辑;数据层使用MySQL存储业务数据,Redis做缓存。

我画一下数据流向:用户在浏览器打开管理系统,输入账号密码登录,Vue把凭证存到localStorage,之后每次请求在拦截器里自动带上Token。后端通过拦截器校验Token合法性,校验通过后分发到对应的Controller,Controller调用Service处理业务逻辑,Service访问Mapper操作数据库,返回结果逐层回传。整个链路清爽、职责清晰。

为了应对后续可能的并发量增长,我在设计时预留了扩展点:Redis缓存热点数据(比如房型价格和可订房量),减少数据库压力;MQ做异步解耦(比如订单创建成功后发短信通知),不至于让下单接口被外部系统拖慢。这些不是上线必须的,但架构上留好位置,后面加功能不用推倒重来。

2.3 数据库表结构设计

数据库是这套系统的地基,表结构设计直接决定业务逻辑的复杂程度。我梳理下来,核心表有这几张:用户表(管理员和前台共用一个表,用角色区分)、民宿门店表、房间表、房型表、房间价格表、订单表、订单状态记录表、评论表。

以订单表为例,我分享几个关键字段的设计考量:订单编号(order_no)用业务前缀加时间戳加随机数生成,保证唯一且可读性强;订单金额拆成原价(total_amount)、实付金额(pay_amount)、优惠金额(discount_amount)三个字段,方便后续对账和营销活动统计;状态字段(status)用整型存,0待支付、1已预订、2已入住、3已退房、4已取消、5已退款,可读性和扩展性都比较好。

房间价格表是这套系统的特色,因为民宿的房价在淡旺季相差很大,我设计成按天存价格,日期作为主键的一部分:room_id + date + price。这样在展示日历时可以直接查询某段日期每天的房价,在用户选日期时也能精确计算出总价。缺点是数据量会随日期增长,但民宿房间数量有限,一天最多几百条记录,完全撑得住。

3. 后端Spring Boot核心实现

3.1 项目初始化与依赖配置

后端我用Spring Initializr创建项目,几个关键的依赖必须讲清楚:spring-boot-starter-web(提供Web能力)、mybatis-plus-boot-starter(持久层框架,我们这里用的MyBatis-Plus,就是为了少写XML)、mysql-connector-java(数据库驱动)、spring-boot-starter-validation(参数校验)、jjwt(生成和解析JWT Token)、hutool(工具类库,处理日期、生成编号很方便)。

MyBatis-Plus的选择值得多说一句:民宿管理系统的CRUD操作占了绝大多数,MyBatis-Plus的单表操作几乎不用写SQL,直接把Mapper接口继承BaseMapper就能用。复杂查询我用注解或者XML写,省下的时间非常可观。它还内置了分页插件,管理后台的列表分页直接调用方法就行。

Spring Boot的配置文件我强烈建议用YAML格式,比properties清晰得多。关键配置项有:数据源连接信息、MyBatis-Plus的日志输出(开发环境开启方便调试)、JWT的密钥和过期时间、自定义的跨域配置。我在application.yml里把Redis和短信通知等外部服务的配置也预留了位置,方便后续接入。

3.2 统一返回结果与异常处理

一套好的接口体系,返回结果结构必须是统一的。我定义了一个Result类,包含code(状态码)、message(提示信息)、data(业务数据)三个字段。成功返回code为200,业务错误返回400,未登录返回401,服务器错误返回500。这样前端拿到结果后,先判断code再处理数据,逻辑非常统一。

异常处理我用@RestControllerAdvice做了全局拦截。自定义的业务异常(比如房间已被预订、订单状态不支持当前操作)抛出BizException,全局异常处理器捕获后返回对应的错误信息和状态码;系统异常(空指针、数据库连接失败)返回500,同时把详细异常信息打印到日志,方便排查。前端不用每个请求都写一堆错误判断逻辑,只管在拦截器里统一提示错误信息。

这一步很多新手容易忽略,觉得接口能跑通就行。但实际联调时,全局异常处理和统一返回真的能省无数扯皮的时间。前端拿到不明不白的异常堆栈时根本没法用,只有统一了错误格式,前后端协作才高效。

3.3 JWT登录认证实现

民宿管理系统的用户分两类:系统管理员和门店工作人员。考虑到后面可能开放给民宿用户自己下单,我直接在登录认证上就考虑了多角色。JWT的结构是Header.Payload.Signature三段式,我用jjwt库来生成和解析Token,密钥存在配置文件中,用HS256算法签名。

登录接口的逻辑是:接收用户名和密码,用BCrypt加密算法比对密码(千万别用MD5存密码,太容易被撞库了),比对成功后生成JWT返回给前端。Token里我放了用户ID和角色信息,过期时间设置为24小时。用户在有效期内的操作不需要重新登录,过期后前端拦截到401状态码,自动跳转到登录页。

后端考虑到接口安全,我写了一个JwtInterceptor拦截器,注册到Spring MVC的拦截器链中,配置excludePathPatterns放行登录接口和获取民宿公开信息的接口。拦截器里从请求头取Token,解析成功就放行,失败返回401。这里有个小技巧:解析完Token后把用户ID放到Request域的Attribute中,后续Controller和Service里就能直接取到当前操作人,不需要再查一次数据库。

3.4 房间管理与预订核心接口

房间管理模块的接口,我按照RESTful风格来设计:GET /api/rooms获取房间列表、POST /api/rooms新增房间、PUT /api/rooms/{id}修改房间信息、DELETE /api/rooms/{id}删除房间(逻辑删除,为的是保留历史订单数据)。查询条件支持按门店、按房型、按状态筛选,配合MyBatis-Plus的条件构造器,写起来非常顺手。

预订流程是这套系统的核心业务,我单独讲讲。用户提交预订请求时,前端传来民宿ID、房型ID、入住日期、离店日期、入住人信息。后端校验日期合法性(入住不能早于今天、离店必须晚于入住),然后计算出总房价,创建订单,状态设为待支付。这里的关键是防止超卖:同一房型在同一时间段内不能重复预订。

防超卖的实现我用了数据库层面的乐观锁。订单表里加了一个version字段,更新房间状态时带上条件UPDATE room SET status=1, version=version+1 WHERE id=#{id} AND version=#{version},如果影响行数为0说明已经被别人抢先了,就提示用户房间已被预订。这种方式比悲观锁性能好,对民宿这种低并发场景完全够用。

4. 前端Vue核心实现

4.1 项目创建与工程化配置

前端我用Vite来创建Vue 3项目,命令是npm create vite@latest tourism-hotel-admin。对比Webpack,Vite在开发环境的启动速度和热更新效率是碾压级的,配备Vue 3的Composition API写起业务来也更灵活。

项目创建完,第一件事是装依赖:vue-router(前端路由)、pinia(状态管理)、axios(HTTP请求库)、element-plus(UI组件库)、dayjs(日期处理)、echarts(图表统计)。Element Plus是Element UI的Vue 3版本,后台管理系统的表单、表格、弹窗、日期选择器这些组件直接拿来用,项目开发效率翻倍。

工程化方面我做了几个配置:路径别名@指向src目录,不用再写一长串相对路径;代理转发在vite.config.js里配置,开发环境把/api开头的请求转发到http://localhost:8080,解决开发时的跨域问题;eslint和prettier用来约束代码风格,多人协作时不至于代码风格乱七八糟。

4.2 前端页面结构与路由设计

页面结构上,我采用了后台管理系统最经典的布局:左侧是侧边栏菜单(菜单项根据地动态渲染,不同角色看到不同的菜单),顶部是导航栏(展示系统名称和管理员信息),主体区域是内容展示区,使用路由视图切换。这种布局对管理者来说非常直观,点击左边菜单,右边直接出现对应内容。

路由设计上,我做了路由懒加载,每个页面的组件单独打包成独立的Chunk,首屏只加载必要的资源,加载速度有明显的提升。公共页面(登录页)和需要登录的页面做了区分,路由守卫里判断用户是否已登录,没登录的强制跳转到登录页。同时做了一层简单的权限控制:路由的meta信息里标记需要的角色,守卫里比对当前用户角色,没有权限的直接跳转403页面。

页面组件拆分这块,我遵循一个原则:一个页面一个目录,目录里放index.vue(页面主组件)和components文件夹(页面内的子组件)。比如订单列表页,我把订单列表、订单详情弹窗、订单状态操作按钮分别拆成独立的子组件,每个组件只负责一块逻辑,改起来互不影响。

4.3 状态管理与接口联调

状态管理用的Pinia,比Vuex用起来简单太多。我建了三个Store:userStore存放用户信息(用户名、角色、Token)、roomStore存放房间和价格的缓存数据、orderStore存放订单的筛选条件和列表数据。UserStore里的Token配合LocalStorage持久化,刷新页面后能自动恢复登录状态。

接口封装这块,我在src/api目录下按业务模块拆分文件:room.js、order.js、statistics.js等,每个文件里导出一组请求函数。以room.js为例:export function getRoomList(params) { return request.post('/api/rooms/list', params) }。请求函数内部统一调用封装的axios实例,实例里配置了baseURL、请求超时时间、请求拦截器(自动加Token)、响应拦截器(统一处理错误码)。

联调过程中,最容易出问题的是参数格式。前端用JSON传参,后端用@RequestBody接收,对象里的日期字段是字符串还是时间戳、空字符串要不要传给后端、字段名是驼峰还是下划线,这些必须在接口文档里约定清楚。我们的约定是:日期传日期字符串(yyyy-MM-dd),时间戳传Long类型,字段命名统一用驼峰,后端通过Jackson配置自动处理。

4.4 核心业务页面实战

预订页面是前端开发中比较复杂的一块,因为要集成日历选择和价格联动。我用的Element Plus的日期选择器el-date-picker,类型设为daterange,选择入住和离店日期。选中日期后,前端请求后端获取这段时间的房价列表,用表格展示每天的房价,底部自动计算总价。价格联动用watch监听日期变化,重新计算总价并更新到页面上。

订单管理页面用el-table渲染订单列表,状态列用el-tag展示不同颜色的标签,待支付橙色、已预订蓝色、已入住绿色、已取消灰色。操作列根据状态动态渲染按钮:待支付可以取消,已预订可以办理入住,已入住可以办理退房。办理入住和退房时弹确认框,确认后调用对应的接口,操作成功后刷新列表。这里有个体验细节:表格加上了序号列和分页器,数据量大的时候不会卡。

数据统计页面用ECharts展示图表。一个折线图展示最近30天的营收变化,一个柱状图展示各房型的入住率对比,一个饼图展示预订渠道占比。首页的Dashboard用卡片展示几个核心指标:今日预订量、今日入住量、本月营收、当前可用房间数。这些数据的获取频率是每次进入页面时拉取,由于数据量不大,就不做轮询了。

5. 高频问题与避坑经验

5.1 跨域问题大全

前后端分离项目绕不过跨域。开发环境我用Vite的代理解决,配置server.proxy把/api转发到后端地址。生产环境我用Nginx配置反向代理,location /api { proxy_pass http://127.0.0.1:8080; }。这两层配置能覆盖90%的情况。

但是有一种情况要注意:后端主动跨域时,如果配置了自定义请求头(比如Authorization携带Token),前端浏览器会先发送OPTIONS预检请求。如果后端没有正确响应OPTIONS请求,浏览器就会报跨域错误。让我在这里补充一个细节:Spring Boot里需要注册CorsFilter,并加上allowCredentials(true),允许携带凭证。注意allowedOrigin不能设为*,否则带凭证的请求会被浏览器拦截,需要指定具体的域名或使用allowedOriginPatterns。

5.2 日期和时间处理的坑

民宿系统的日期处理贯穿业务始终,最容易踩坑的是时区问题。前端传给后端的是yyyy-MM-dd格式的日期字符串,如果后端接收时用Date类型,Jackson默认转成UTC时间,传到前端就会差8个小时。我的做法是:数据库里日期字段用date类型存,后端实体类对应字段用LocalDate类型接收,前端传参直接传字符串,不做隐式转换。

另一个坑是日期边界。计算入住天数时,我用ChronoUnit.DAYS.between(checkInDate, checkOutDate),这个方法算的是两个日期之间的整天数。如果用户订了1号到3号,结果是2天还是3天?不同的民宿规则不一样。我在系统里统一做成“按晚计价”:入住1号离店3号,住了2晚,价格为2晚的房费。前端日历组件默认入住当天不可选,这样避免很多歧义。

5.3 并发抢房与订单状态一致

民宿虽然并发不高,但热门房型在节假日还是会出现多人同时抢房的场景。我前面提到用乐观锁防超卖,但还有一个场景要考虑:用户A创建了订单但一直不支付,把房间锁定了,用户B想订同一房间却被提示无房。这就需要加一个订单自动取消机制。

我的方案有两种实现路径:第一种是Spring的@Scheduled定时任务,每5分钟扫描一次超时未支付的订单,把订单状态改成已取消,释放房间库存;第二种是用Redis的过期监听,下单时给订单号设置30分钟的过期时间,过期后自动触发取消逻辑。考虑到稳定性(Redis过期监听依赖 Redis 的 key 过期事件,不保证可靠性),我选择了定时任务方案,简单可靠,代码逻辑直白。

5.4 Vue项目启动后Network不可用

开发时发现Vite启动后Network栏显示不可用,局域网里的同事访问不到。这个问题的根源是Vite默认监听localhost,不监听局域网IP。解决方法是修改vite.config.js:server: { host: '0.0.0.0', port: 3000 },监听所有网卡接口。配置完成后,启动时终端会输出Network地址,局域网内的设备就能通过http://192.168.x.x:3000访问了。

如果配置了host: '0.0.0.0'后Network还是不可用,检查一下电脑的防火墙是否放行了3000端口,以及是否处于同一网段。这个情况在Windows系统的公司网络里很常见,我踩过一次后就把这个配置固化为新项目的默认模板了。

6. 系统部署与上线体验

6.1 后端打包与服务器配置

后端打包,我用的Maven的package命令,通过配置Spring Boot Maven插件,打出来的Jar包是可直接执行的fat jar,里面包含了所有依赖。部署到Linux服务器上,我用systemd配置了一个服务单元,启动命令是java -jar tourism-hotel.jar --spring.profiles.active=prod,这样可以方便地区分开发环境和生产环境的配置。

生产环境的配置文件我单独维护application-prod.yml,数据源指向云数据库,开启优雅停机,设置JVM参数止血堆内存512M、最大1G。系统比较轻量,资源占用不需要太高。

6.2 前端打包与Nginx配置

前端打包运行npm run build,生成dist目录。把dist目录上传到服务器的/opt/nginx/html目录下,修改Nginx配置:root指向dist目录,try_files配置为$uri $uri/ /index.html,这样Vue路由在刷新页面时不会404。

HTTPS证书我用的免费证书,配置完80端口转发到443后,强制所有HTTP请求跳转到HTTPS。这样用户通过https://hotel.example.com访问时,前后端都是HTTPS,不会出现混合内容警告。整体部署完成后,在浏览器里走一遍核心流程:登录、查看房间、预订、支付模拟、办理入住退房、查看统计报表,测试通过后就可以正式上线了。

实测跑下来的体验:首页响应时间在50ms以内,列表接口的响应时间在100ms左右,搜索和筛选没有明显的延迟感。Jar包占用300M内存左右,MySQL占用不到200M,部署在一台2C4G的小型云服务器上完全没有压力。

6.3 后续功能扩展方向

这套系统的扩展空间很大,我这里罗列几个想清楚的方向:小程序端用户自助下单(目前是管理员帮用户下单)、营销工具(优惠券、拼团、老带新)、对接支付网关(微信支付、支付宝)、对接OTA平台(美团、携程、飞猪的订单自动同步)、经营数据分析的报表深度挖掘。

我自己最想优先做的是小程序端。民宿的客人大多数是通过微信或者旅游平台来预订的,如果有一个小程序让他们自己选房间、下单、支付,民宿老板的接待压力会大大减少。而且小程序端的代码可以在现有Vue项目基础上复用大部分组件,工作量不会太大。

对接OTA平台的难度会高出不少,因为各家平台的接口协议、参数命名、鉴权方式都不同,需要逐个适配。但一旦打通,订单自动同步到本地系统,民宿前台就不用在电脑上装一堆渠道管理软件了。这类功能属于二手阶段,建议优先和自己熟悉的平台做对接,验证流程稳定了再继续扩展。

回到最初做这个项目的体会:一套管理系统能否真正落地,关键不在于用了多潮的技术框架,而在于是否真正理解业务场景、把一个一个细节踩平。Spring Boot + Vue是全栈开发者的基本功,但把它们灵活运用、和实际业务深度结合,才真正考验功力。这套民宿管理系统做完之后,我最大的感受是,简单的技术组合也能打造出可靠好用的系统。如果你也正在做类似的管理系统,多花时间理解业务流程、梳理清楚状态流转、把异常场景想全,会比盲目堆技术更有收获。

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

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

立即咨询