做了几年民宿管理系统相关的开发,接手的项目从单体小工具到分布式系统都有,但这套基于 SpringBoot + Vue 的民宿平台管理系统,是我觉得在“业务复杂度”和“代码可维护性”之间平衡得比较好的一套。源码、数据库脚本、部署文档都齐整,拿来学习或者直接二次开发都很合适。
这个系统解决的问题其实很典型:民宿老板要管房态、管订单、管客户、管评价,还要做简单的统计分析。如果靠 Excel 和纸质登记,房间状态很容易漏更新、订单容易重复、统计更是灾难。用这套系统,前台接待、老板管理、后续扩展渠道对接,都有一个清晰的抓手。
我写这篇文章的目的,是把这套系统的设计思路、核心模块的实现逻辑、数据库怎么设计、前后端怎么对接,以及我实际跑通之后遇到过哪些坑,一次性讲清楚。不管你是刚学 SpringBoot 和 Vue 的开发者,还是准备做毕业设计、接私活、或者真有民宿运营需求,这篇文章应该都能帮你省不少时间。
1. 民宿平台的核心业务场景与功能边界划分
**民宿管理系统的第一件事,不是写代码,而是把业务流程捋清楚。**我自己见过太多项目,上来就建表,结果做到一半发现订单状态流转不对,房态和价格算不明白,推倒重来。这套系统的做法值得参考,先把角色和动作分清楚。
1.1 三类使用者与各自的权限边界
民宿平台常见的用户角色就三种:平台管理员、民宿老板/管家、普通住客。这套系统里,权限控制做得比较细,每个角色看到的东西完全不同。
- 管理员:管所有民宿的审核状态、下架违规房源、查看全平台订单流水、处理投诉。在系统里对应最高的角色权限,能访问后台管理的所有菜单。
- 老板/管家:只看自己名下的民宿,能修改房间信息、设置房价、处理预订请求、登记入住、退房结算。对应的是民宿管理员角色,数据层面做了隔离,A 民宿的老板改不了 B 民宿的房价。
- 住客:通过前台页面查房源、看评价、下单预订、支付后生成订单、入住后可以评价。对应的是普通用户角色,只在 C 端操作,进不了后台管理界面。
这个权限设计是这套系统的一个亮点,它没有把用户表做得特别复杂,而是通过 角色字段 + 菜单路由权限(前端)+ 接口鉴权(后端) 三层来控制。
1.2 核心业务闭环:找房、下单、入住、评价、统计
整个系统的业务逻辑是围绕一条主线走的,我梳理下来就是五步闭环:
- 房源展示:前台根据城市、入住日期、退房日期、人数去检索可用的民宿,列表按价格、评分、距离排序。
- 在线下单:住客选定房源,填写入住人信息,系统生成待付款订单。这里的关键逻辑是“锁房”——在订单未支付前,房源要标记为预占状态,防止超卖。
- 支付与确认:支付成功后订单状态变为已支付,房东端收到新订单提醒。这套系统用的是模拟支付,真实项目接入微信/支付宝只要替换支付回调接口就行。
- 办理入住/退房:房东在后台确认入住,登记实际入住人数、收取押金;退房时结算房费外消费,释放房源。
- 评价与统计:住客离店后可以对卫生、位置、服务、性价比打分,系统根据订单金额、入住率、评分生成经营报表。
这套闭环看起来简单,但实现时对数据库的事务、状态机、并发处理都有要求。后面我会重点讲几个容易出问题的地方。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型:为什么是 SpringBoot + Vue,而不是别的组合
现在前端的方案这么多,有人用若依,有人用微服务,为什么这套系统坚持 SpringBoot + Vue 这种“经典款”?我个人的观点是:对于民宿这种中等复杂度的管理系统,越成熟的技术栈越稳,出问题的概率越低,招聘和找人接手也容易。
2.1 后端选型:SpringBoot 2.x + MyBatis Plus
选择 SpringBoot 2.x 而非 3.x,是因为当时生态最稳,各种 starter 的兼容性问题最少,JDK 8 也能跑。现在的 3.x 强制 JDK 17,对很多还停留在老环境的服务器不友好。
ORM 选 MyBatis Plus 而不是 JPA/Hibernate,核心原因是:
- 单表 CRUD 不用手写 SQL,
BaseMapper提供现成的增删改查。 - 复杂的多表联查(比如订单关联民宿、房间、用户信息)可以写自定义 SQL,可控性比 Hibernate 的自动 SQL 强得多。
- 逻辑删除、自动填充(创建时间、更新时间)、分页插件都是现成的,开发效率很高。
项目是标准的 Controller → Service → Mapper → MySQL 分层,没有引入分布式中间件,因为单机 MySQL + Redis(做缓存和验证码)已经完全够用。实际部署时一台 4核8G 的云服务器就能轻松扛住一个小型民宿平台的流量。
bash复制# 核心依赖清单(pom.xml 中的关键部分)
spring-boot-starter-web
spring-boot-starter-validation
mybatis-plus-boot-starter
mysql-connector-java
lombok
jjwt(JWT token 生成与解析)
hutool(工具类)
2.2 前端选型:Vue2 还是 Vue3?
这套系统当时用的是 Vue2 + Element UI,放在现在来看,如果你是刚开新项目,我更建议直接用 Vue3 + Element Plus + Vite。但如果你拿到的源码是 Vue2 的也不要急着升级,Vue2 的生态足够成熟,网上资料最多,遇到问题最好搜。
我列举一下两者的差异,方便你决定:
| 对比维度 | Vue2 + Element UI | Vue3 + Element Plus |
|---|---|---|
| 构建速度 | Webpack 较慢,大型项目几十秒 | Vite 秒级启动,开发体验好很多 |
| 组合式 API | 需要引入 @vue/composition-api | 原生支持 setup 语法 |
| 长期维护 | Vue2 已停止维护,但存量项目多 | 当前主流,持续更新 |
| 上手难度 | 选项式 API 更直观,适合新手 | 组合式 API 更灵活,需要一点适应 |
| 组件库 | Element UI 够用 | Element Plus,部分组件用法有差异 |
我自己的习惯是:接手老项目用 Vue2,新起项目直接 Vue3。这套系统源码里如果带的是 Vue2,学习完核心逻辑后,后续想升级可以只改视图层,接口不用动——前后端分离的好处就在这里。
3. 数据库设计:民宿系统的表结构与关键字段解析
很多人做管理系统,重页面轻数据库,这是大忌。民宿平台的业务逻辑一旦跑起来,订单、房态、价格、评价之间的数据关系比页面复杂得多。这套系统的数据库脚本我完整看了一遍,共 10 张核心表,设计思路比较清晰,我拆成几组来讲。
3.1 用户与民宿基础信息表
用户表(user)和民宿表(homestay)是整个系统的基础。用户表除了常规的用户名、密码、手机号,还加了一个 role 字段,用数字区分管理员、房东、住客。密码存的是 MD5 加密后的密文(实际项目建议改成 BCrypt,后面踩坑部分会细说)。
民宿表是信息重灾区,字段多但都有必要:
title:民宿标题,搜索时关键字匹配。cover_url:封面图 URL,列表页展示用。detail_urls:详情图 JSON 数组,用字符串存储,方便前端解析轮播图。city、address:定位和筛选必需。price_per_night:每晚价格,单位用“分”存储可以避免浮点数误差,展示时再除以 100。status:0=待审核,1=已上架,2=已下架,3=违规封禁。管理员审核通过才能上架。rating:综合评分,由评价表聚合算出。
这个表设计整体选的是“宽表”策略,冗余了一些统计字段(比如评论数、评分),虽然修改时要多维护,但查询时省了很多联表操作,对中小项目来说利大于弊。
3.2 订单、房态与评价的业务关联
订单表、订单详情表、评价表和房态日历表,这四个表之间是民宿系统最核心的业务关系。
订单表(order)的关键字段:
order_no:唯一订单号,生成规则是时间戳 + 随机数,展示给用户和客服核对。user_id:下单用户。homestay_id:关联民宿。check_in_date/check_out_date:入住日期和退房日期。total_price:订单总价(每晚价格 × 天数 + 额外消费)。status:0=待付款,1=已支付,2=已入住,3=已退房,4=已取消,5=已退款。
这里有个容易被新手忽略的问题:“待付款订单”到底要不要锁房? 这套系统的做法是,生成订单时检查房态日历表(room_calendar),如果目标日期段没有冲突,就把对应日期标记为“预占”,此时其他用户还能看到房源,但下单时会被提示“该日期已被预订”。预占状态保留 30 分钟,超时释放。这样既避免了同时下单超卖,又不会因为用户不付款浪费太多库存。
评价表(comment)相对简单:关联订单、民宿、用户,带上评分、内容和图片。这里比较关键的是,评价的分数要同步更新到民宿表的 rating 字段,如果评论改了评分,民宿的总评分也要重新计算。为了简单,这套系统是在用户提交评价时后端同步对民宿表做一次 AVG 聚合然后更新。
房态日历表的设计我觉得很实用。它记录每个民宿在某一天的剩余房间数、是否被锁房等。查询可用房源时,不是直接查订单表,而是通过这个日历表做日期范围匹配:
sql复制SELECT homestay_id FROM room_calendar
WHERE date BETWEEN #{startDate} AND #{endDate}
AND remaining_count > 0
GROUP BY homestay_id
这样查询效率非常高,也不用在订单表里做复杂的排除逻辑。
4. 后端核心模块实现:权限控制、订单流程与并发防护
光有数据库还不够,SpringBoot 后端的实现才是这套系统的重头戏。我从源码里提炼了三个比较有代表性的模块,也是面试或者做项目时最容易被问到的部分。
4.1 使用 JWT 实现无状态登录认证
这套系统的登录认证用的是 JWT(JSON Web Token),具体流程是:
- 用户输入用户名密码,后端校验通过后,生成一个带用户 ID、角色、过期时间的 token 返回给前端。
- 前端把 token 存到 localStorage 或 Vuex,每次请求在
Authorization请求头带上。 - 后端通过拦截器校验 token 合法性,从 token 里解析出用户信息,放入
ThreadLocal供后续业务使用。
核心代码如下,我简化了一下主流程:
java复制@Component
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
throw new BusinessException("未登录,请先登录");
}
Claims claims = JwtUtil.parseToken(token.replace("Bearer ", ""));
if (claims == null) {
throw new BusinessException("登录已过期,请重新登录");
}
// 将用户信息放入 ThreadLocal,方便后续从任意 Service 里取当前用户
UserContext.setUserId(claims.get("userId", Integer.class));
UserContext.setRole(claims.get("role", Integer.class));
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
UserContext.clear(); // 防止线程池复用导致的内存泄漏
}
}
JWT 相比 Session 的好处是:后端不需要维护会话状态,天然支持分布式部署,多个服务实例之间不需要同步登录态。缺点是无法主动让 token 失效,所以这套系统在用户修改密码后会重新生成 token,变相让旧 token 失效。
4.2 订单状态流转与“锁房”的并发处理
订单状态流转是最容易写成一团乱麻的地方。这套系统用了一个状态机枚举类来管理,清清楚楚:
code复制待付款(0) → 已支付(1) → 已入住(2) → 已退房(3)
| |
| |
取消(4) 取消/退款(5)
简单说就是:待付款可以取消;已支付可以申请退款取消;已入住只能走正常退房流程;退房后不能再做状态变更。
并发防护是这套系统里最需要强调的部分。在“用户 A 和用户 B 同时下单同一间房”的场景下,如果代码是先查房态、再生成订单,很可能两个人都查到有房,然后都下单成功。解决办法有两个,这套系统用的是数据库层面加 SELECT ... FOR UPDATE 悲观锁:
java复制@Transactional
public Order createOrder(CreateOrderRequest request) {
// 锁定这间民宿在日期段内的房态记录,防止并发下单
List<RoomCalendar> calendars = roomCalendarMapper.selectForUpdate(
request.getHomestayId(), request.getStartDate(), request.getEndDate());
if (calendars.isEmpty() || calendars.stream().anyMatch(c -> c.getRemainingCount() <= 0)) {
throw new BusinessException("该日期段房源已满,请更换日期");
}
// 生成订单、扣减日历表的剩余房数、保存订单明细
...
}
为什么这里不用乐观锁?因为民宿系统的房态操作频率不高,但一旦冲突后果很严重(超卖要赔付),所以悲观锁更可靠。selectForUpdate 会锁住这组日期记录,直到事务提交,B 用户会阻塞等待,A 事务完成后 B 再查询时就拿不到可用的房了,直接抛出异常。
4.3 房东端数据隔离与自定义 SQL 联表查询
房东登录后台后,只能看到自己名下的民宿和订单,这个数据隔离在实现时不能只靠前端隐藏菜单,后端接口也必须做校验。这套系统的做法是:
- 民宿表加了
merchant_id字段。 - 房东操作民宿时,Service 层先检查当前登录用户的 ID 是否等于民宿的
merchant_id,不一致直接拒绝。 - 列表查询时,MyBatis Plus 的
LambdaQueryWrapper会强制带上merchant_id条件。
在订单列表分页查询时,需要联查订单、民宿、用户三张表,这时 MyBatis Plus 的分页插件配合自定义 XML 的 SQL 就很方便了。比如查询民宿订单列表,核心 SQL 大概是:
sql复制SELECT o.order_no, o.total_price, o.status, o.create_time,
h.title AS homestay_title, u.nickname AS user_nickname
FROM `order` o
LEFT JOIN homestay h ON o.homestay_id = h.id
LEFT JOIN user u ON o.user_id = u.id
WHERE o.homestay_id IN (
SELECT id FROM homestay WHERE merchant_id = #{merchantId}
)
ORDER BY o.create_time DESC
这种 SQL 一眼就能看明白,也方便以后做分页优化。很多新手用 MyBatis Plus 习惯了只调现成方法,遇到联表就卡住,其实自定义 SQL 才是日常开发的主旋律。
5. 前端 Vue 页面设计:后台管理端与 C 端展现的交互逻辑
前端部分我拆成两个视角写:一个是给老板用的后台管理端,一个是给住客用的前台页面。两者的交互逻辑和设计重点完全不同。
5.1 管理后台:侧边菜单 + 表格 + 弹窗表单的经典组合
后台管理端用的是典型的 Vue2 + Element UI + vue-router + axios 方案。布局分三块:顶部导航(用户信息、退出登录),左侧菜单(订单管理、民宿管理、评价审核、统计报表),右侧主内容区(表格、筛选条件、分页)。
订单管理页是最核心的页面,它做到了一下几个交互:
- 顶部筛选区:通过状态、民宿名称、下单时间做组合筛选,筛选条件通过
this.queryParams绑定,点击“查询”按钮后把参数拼到 URL query 上。 - 表格列展示:订单号、民宿、住客、入住/退房日期、金额、状态标签、操作列。
- 操作按钮按状态动态显示:已支付订单可以“确认入住”,待付款的可以“取消订单”,已入住的可以“办理退房”。
- 状态标签用 Element UI 的
el-tag,不同状态用不同 type 区分颜色,方便老板一眼看出问题。
民宿管理页的设计更偏向表单密集型。房源信息字段多,需要做分步表单(基本信息 → 图片展示 → 房态与价格),每个步骤校验不同内容。图片上传用了 Element UI 的 el-upload + 阿里云 OSS 直传,前端拿到 URL 后回填到表单,这样不用经过后端服务器转发,速度快,也不会让后端进程背着大文件传输的压力。
vue复制<template>
<el-form :model="form" :rules="rules" ref="homestayForm" label-width="100px">
<el-form-item label="房源标题" prop="title">
<el-input v-model="form.title" placeholder="一句话介绍房源亮点" />
</el-form-item>
<el-form-item label="每晚价格" prop="pricePerNight">
<el-input-number v-model="form.pricePerNight" :min="0" :precision="2" />
</el-form-item>
<el-form-item label="城市" prop="city">
<el-input v-model="form.city" placeholder="如:大理" />
</el-form-item>
<el-form-item label="封面图">
<el-upload
:action="uploadUrl"
:on-success="handleUploadSuccess"
:headers="uploadHeaders">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</el-form-item>
</el-form>
</template>
5.2 C 端前台:房源卡片流 + 详情页 + 订单确认
前台页面相对简单,但用户体验的点不一样。首页是房源卡片流,每个卡片突出封面图、价格、标题、评分、城市,点击进详情。详情页除了大图轮播,还有整个页面最关键的“日期选择 + 实时价格计算”模块。
这个模块的交互逻辑是:用户选择入住日期和退房日期,前端立即通过接口查询该房源在这个时间段内的总价,而不是下单时才计算。好处是用户能提前感知总花费,减少下单环节的“价格突然变了”的负面体验。实现上,用 Vue 的 watch 监听日期变化,变化后防抖调用后端价格计算接口:
javascript复制watch: {
dateRange(newVal) {
if (!newVal || newVal.length !== 2) return;
this.fetchPrice(newVal[0], newVal[1]);
}
},
methods: {
async fetchPrice(start, end) {
const { data } = await axios.get(`/api/homestay/price`, {
params: { homestayId: this.homestayId, start, end }
});
this.totalPrice = data.data;
}
}
订单确认页会把用户选的日期、人数、手机号、入住人姓名收集起来,后端生成订单后返回订单号,前端跳转到订单详情页展示“待付款”状态和支付按钮。
5.3 前端路由守卫与 axios 拦截器的权限配合
前端光有页面还不够,权限要能在路由层体现出来。这套系统的路由分两块:
- 前台路由:首页、房源详情、登录注册、订单中心,任何人都能访问。
- 后台路由:包含
/admin开头的所有页面,必须登录且角色为管理员或房东。
vue-router 的全局前置守卫配合后端返回的用户信息做校验:
javascript复制router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token');
if (to.path.startsWith('/admin') && !token) {
next('/login');
return;
}
// 从 store 里拿用户角色,如果角色不匹配后台权限,跳回首页
if (to.path.startsWith('/admin') && store.state.user.role > 1) {
next('/');
return;
}
next();
});
axios 拦截器则是全局处理 token 注入和 401 未授权跳转,这样一个请求失败时,用户不会看到一堆乱七八糟的报错弹窗,而是被统一带到登录页。
javascript复制axios.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = 'Bearer ' + token;
}
return config;
});
axios.interceptors.response.use(
res => res,
err => {
if (err.response && err.response.status === 401) {
localStorage.removeItem('token');
router.push('/login');
}
return Promise.reject(err);
}
);
6. 部署上线与联调:从本地环境到云服务器的完整路径
系统开发完,最终要跑在真实的服务器上让人用。这个环节我遇到的坑比写代码时还多,所以单独拿出来说说。
6.1 本地环境配置:JDK、Maven、Node 版本匹配
本地跑这套系统的前提是版本匹配,我列一个我常用且验证过的环境组合:
| 工具 | 版本 | 说明 |
|---|---|---|
| JDK | 1.8(如果你用 SpringBoot 2.x) | 不要装 17 或 21,可能编译报错 |
| Maven | 3.6+ | 3.8 即可,不要用太新的 4.x |
| Node.js | 14.x 或 16.x(Vue2 项目) | 18+ 会有 node-sass 编译兼容问题 |
| npm/yarn | npm 6 或 7 | 依赖安装有问题先换镜像源 |
| MySQL | 5.7 或 8.0 | 8.0 注意驱动版本用 mysql-connector-java 8.0.x |
| Redis | 5.x+ | 验证码、缓存用,可选但建议装 |
启动顺序:先启动 MySQL 和 Redis,然后导入数据库脚本,再启动后端 SpringBoot,最后 npm run serve 启动前端。
后端启动时如果报端口被占用,用 lsof -i:8080 排查;前端如果 node-sass 装不上,建议直接删掉 node_modules 和 package-lock.json,用 npm install 重装一次,很多时候能解决问题。
6.2 云服务器部署:jar 包 + nginx 反向代理
生产环境部署我建议用最朴素但可靠的方式:后端打成 jar 包,用 nohup 启动,nignx 托管前端静态文件并做反向代理。
第一步,前端执行 npm run build,生成 dist 目录,把整个目录上传到服务器 /usr/share/nginx/html。
第二步,后端执行 mvn clean package -DskipTests,把生成的 homestay-admin.jar 上传到服务器,比如放在 /home/app 目录。
启动命令:
bash复制nohup java -jar homestay-admin.jar \
--spring.profiles.active=prod \
--server.port=8080 \
> /home/app/logs/app.log 2>&1 &
确认启动成功后,配置 nginx 把 /api 前缀的请求代理到后端:
nginx复制server {
listen 80;
server_name your-domain.com;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
这里最关键的一点是 try_files $uri $uri/ /index.html;。Vue 用 history 模式路由时,刷新 /admin/homestay 页面如果没这行,nginx 会返回 404,因为服务器上根本没有这个物理路径。加上这行,所有非 /api 的请求都会回退到 index.html,由前端路由接管。
6.3 联调时最容易踩的四个坑
联调阶段我几乎每次都会遇到下面这些问题,写下来帮你避雷:
第一个是跨域问题。 本地开发时前端跑在 8080 端口(Vue 默认),后端跑在 8081,如果不做代理,前端请求后端一定会跨域。解决方案是在 vue.config.js 里配置 devServer 代理,而不是在后端强行加 @CrossOrigin。代理配置如下:
javascript复制const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8081',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
})
这样做的好处是,前端代码里请求路径可以统一写成 /api/xxx,部署后 nginx 也代理 /api,前后端环境切换时不用改前端代码里的域名。
第二个是数据库时区问题。 useSSL=false&serverTimezone=Asia/Shanghai,这个如果不加,连接 MySQL 8 的时候会报时区错误。配置项在 application.yml 的 JDBC URL 里需要带上:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/homestay_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
第三个是文件上传路径问题。 如果民宿图片上传走的是本地存储(不是 OSS),部署后一定要确保 application.yml 里配置的上传目录存在,并且 nginx 对静态资源目录有读取权限。否则前端上传成功(实际上传到一半就失败)后,图片显示不出来。最诡异的是这种错误有时候不报异常,只会在浏览器 network 里看到 403。
第四个是 Hutool 的工具类版本冲突。 我遇到过因为 hutool 版本里 DateUtil 方法签名不一致导致编译报错,解决办法是统一用 pom.xml 里的 <dependency> 管理版本,不要用传递依赖。如果项目里引了其他模块,建议在 dependencyManagement 里锁定 hutool 版本。
7. 这套系统的可扩展方向:从单体到多民宿平台的演进思路
最后想聊一下这个项目的边界和它的扩展空间。严格来说,这套系统更像是一个“单店版 + 轻度平台化”的模板,它解决的是一家民宿或一个小型平台的基础管理问题。但如果你有不同的业务诉求,改造成本其实可控。
7.1 如果要做多商户入驻,需要动的核心表
目前的系统里,房东和民宿是简单的归属关系。但如果要做成“携程民宿”“途家”那种多商户入驻的平台,需要增加:
- 商户资质表(营业执照、法人信息、联系方式),民宿表里的
merchant_id改成关联商户表。 - 结算系统:订单完成后,平台抽佣,把剩余金额打入商户账户。需要增加商户余额表、提现记录表、结算规则配置表。
- 审核流程:商户入驻材料审核、房源上架审核,目前这套系统的审核基本是管理员手动点击,流程可以扩展成“提交材料 → 平台初审 → 平台终审”多级状态。
7.2 营销和会员体系可以怎么加
当前系统的基础订单流程完整,但几乎没有营销能力。如果想提高订单转化和用户粘性,可以加:
- 优惠券表:面额、使用门槛、有效期、适用民宿范围。下单时校验优惠券有没有过期、满不满足门槛,抵扣后计算应付款。
- 会员等级:按累计消费金额把人分为普通会员、黄金会员、钻石会员,对应不同折扣率。price计算时,会员折扣在商详页实时展示。
- 分享裂变:生成带有 user_id 参数的分享链接,新用户通过链接注册并下单后,老用户获得返现。需要增加分享记录表和返现记录表。
这些功能是标准的 CRUD + 状态机,基于现有代码结构,都是一张新表 + 一个 Controller + 一个 Service 的事,扩展起来比想象中快。
7.3 性能优化空间
如果民宿数量涨到几千家,订单量一天上千单,当前直接查 room_calendar 表的方式可能会遇到查询变慢的问题。这时候有两个优化方向:
- 日期段索引:在 room_calendar 表的
homestay_id和date字段上建联合索引,SQL 走索引后性能提升明显。 - Redis 缓存热数据:把最近 7 天的房态数据缓存到 Redis,用位图或者 sorted set 存储日期和剩余房量,查询可用房源时先查缓存,缓存 miss 再查数据库。这个方案做到后面其实是一个小型的库存系统设计。
- 分库分表不需要太早做:多数民宿平台的瓶颈不在数据库,而在图片带宽和支付回调。数据库能扛住几千 QPS 的查询,不要一开始就引入 ShardingSphere 这类中间件,徒增复杂度。
就我实际使用的体验来说,这套基于 SpringBoot + Vue 的系统框架,非常适合作为中小型民宿项目的起步底座。它的代码风格清晰,分层明确,表设计也覆盖了核心业务。如果你拿到源码和数据库文档,建议先把整体跑通,然后按自己的业务场景去加功能,比从零开始写要高效得多。如果后续在部署或者二开过程中遇到问题,也可以按我在文章里划的这几个方向去排查,大概率能解决大部分常见的坑。
