每年带毕业设计,最容易收到的私信内容就是“老师,我想做民宿预定系统”,打开正文一看,普遍想法是把 SpringBoot+Vue 那套增删改查跑通,再往里面塞几张表、几个页面。这里我想先泼一盆冷水:如果只是做一个“有房间字段、有订单字段、能CURD”的管理后台,这个题目基本上浪费掉了。真正让“民宿预定与管理系统”值得作为计算机毕业设计去做的,是它背后那条贯穿房态、订单、价格、并发控制的业务链路。标题里那串关键词——“共享住宿智慧预订平台”“乡村民宿房态与订单一体化运营系统”——核心主语并不在前端的漂亮页面,也不在后端框架本身,而在“房态”和“订单”之间的关系上。你把这个关系处理清楚,论文有得写,答辩有得聊,代码也不再是套模板的产物。
所以这篇文章我打算按完整项目的顺序来复盘:先拆需求,再谈技术选型,然后落到数据库设计、核心业务逻辑、前端闭环,最后补充演示和论文准备的实操经验。你如果是拿这个题做毕设,可以直接按这个思路照着搭。
1. 先拆题:这道毕设真正要交付的不是管理后台,而是一个可下单的预订平台
1.1 题目里的三个关键词,其实对应三层功能
“民宿预定与管理系统”这个说法最宽泛,它只告诉你有两个动作:用户预定,管理端管理。你把它做成单机版小系统也能勉强糊弄过去,但接下来的“共享住宿智慧预订平台”就把层次抬高了。什么叫共享住宿?意味着平台上不止一家民宿,而是包含多个民宿主或房源供给方,用户是在平台上看房源、下单、支付,平台负责审核房源、汇总订单。这跟传统的“一家酒店自己做官网订房”有本质区别。
再往下拆,“乡村民宿房态与订单一体化运营系统”基本上把技术难点点名了。乡村民宿和连锁酒店的区别是什么?一个乡村民宿通常只有十几个房间甚至几间房,一旦被订出去,剩余可卖房态就必须实时变化,不能再出现同一间房同一天被两个人订走的尴尬。所以“房态与订单一体化”不是说页面上左边显示房态、右边显示订单就完事,而是要求预定行为必须反向影响房源在某个日期区间上的可售状态,订单结束后房态还要自动恢复或者转入待打扫状态。这就是我要在正文里反复强调的一条主线:不能把房态做成一个死字段,要把它理解成由订单、日历、维护计划共同推导出的动态结果。
1.2 给系统画功能地图,先分清楚三类角色
围绕这个需求,建议把系统用户拆成三类:游客/注册用户、民宿主/房东、平台管理员。我见过很多同学只拆用户和管理员两个角色,结果民宿主的所有操作都压在管理员身上,功能上没什么大问题,但答辩时一句话就被问住:“管理员为什么还要替房东维护房源?业务上实际吗?”
三类角色的功能边界可以这样划分:
| 角色 | 主要功能 | 典型页面 |
|---|---|---|
| 游客/注册用户 | 注册登录、浏览民宿与房源、按日期查询可订房态、提交订单、模拟支付、查看我的订单 | 首页搜索、民宿详情、房态日历、订单列表 |
| 民宿主/房东 | 维护名下民宿与房间、设置基础价格、设置某段日期暂停出租或维修、处理入住/退房 | 房源管理、房态日历管理、订单处理 |
| 平台管理员 | 审核民宿上架、管理用户、查看平台订单、运营数据统计 | 民宿审核、用户管理、数据看板 |
注册用户可以进一步细分成游客和会员,游客只能看不能订,注册完整信息后才能下单。这在很多酒店项目里都有,加一个用户状态字段就能控制,不增加多少复杂度。但如果你的毕设题目明确叫“会员”或“积分系统”,那就得单开模块,这里不展开。
1.3 模块数量要有边界:功能不是越多越好
很多同学喜欢给毕设加购物车、加收藏、加优惠券、加评论、加轮播图管理、加公告管理,觉得功能显得丰富。我必须说,这里有个非常大的误区:毕业设计的评估重点不是你堆了多少菜单,而是你如何把一个核心业务场景做深。
预订平台的核心闭环是“找房—选日期—锁房—下单—支付—入住—退房—房态恢复”。建议先把这条链路做得滴水不漏,再考虑锦上添花。我个人的建议优先级是这样的:
- 核心必做:登录注册、民宿与房间管理、日期维度房态查询、订单创建与支付模拟、订单状态流转、后台统计。
- 加分可做:民宿审核、价格日历批量设置、优惠券(但要注意幂等订单)、多图上传、数据看板、导出Excel。
- 强烈不建议做:聊天消息、拼团、分销、在线客服。这类功能一旦涉及WebSocket、状态同步,很容易做成一团乱麻,最后项目不稳定,演示现场崩溃,损失远大于收益。
范围控制不是逃避工作量,而是保证工程质量。你后面把房态并发和订单状态机的设计讲清楚,比多两个没用的菜单模块体面得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型复盘:SpringBoot+Vue这个组合怎么选版本才顺
2.1 后端:Spring Boot版本和JDK版本必须对齐,这是最大的坑
技术选型很多人只写“SpringBoot+Vue”,但真正装环境的时候,七八成同学卡在版本匹配上。现在的Spring Initializr默认下载的已经是Spring Boot 3.x,Spring Boot 3.x的最低要求是JDK 17。如果你电脑里装的是JDK 8,又照着老教程操作,启动就会报“java: 无效的目标发行版”或者“Unsupported class file major version”,到网上搜半天发现是JDK版本问题。
给毕设项目最稳妥的方案是 JDK 1.8 + Spring Boot 2.7.18。这个组合能跑的依赖最多,遇到的教程也最多,网上随便查都能找到答案。如果你电脑已经装了JDK 17,安装更高版本的Spring Boot 3.x也没问题,但要记住 Spring Boot 3.x 里原来的 javax.servlet 这一套全部改成了 jakarta.servlet,不少旧包的坐标也会变,例如很多分页插件的使用方式需要额外注意。对大多数只想稳稳当当做毕设的同学,我建议不要在这上面赌运气,老老实实 JDK 8 + Spring Boot 2.7。
ORM层选 MyBatis-Plus 而不是原生 MyBatis 或 Spring Data JPA。原因很简单:MyBatis-Plus 自带 BaseMapper 和 LambdaQueryWrapper,日常增删改查不用手写SQL,多表关联、分页查询又能按需自定义。它还有一个代码生成器,可以一次把实体、Mapper、Service、Controller都生成出来,极大节省初期开发时间。至于 Spring Data JPA,虽然自动建表很方便,但遇到复杂统计和房态判断这种动态SQL时,反而要写很多 @Query,不如 MyBatis-Plus 直接。
2.2 Spring Boot自动装配原理,提前准备一句答辩答案
做这个项目用SpringBoot,答辩老师几乎必问“为什么Spring Boot能简化配置”。你不要只回答“因为它是封装好的框架”,准备一下自动装配原理,哪怕三句话也能体现你真的用过。
核心逻辑是:Spring Boot 启动类上的 @SpringBootApplication 组合注解里包含了 @EnableAutoConfiguration。框架加载时会读取 META-INF/spring.factories(Spring Boot 2.7之前)或 AutoConfiguration.imports(Spring Boot 2.7及之后)中注册的自动配置类,再用 @ConditionalOnClass、@ConditionalOnMissingBean 这类条件注解去判断当前classpath里有没有对应依赖、容器里有没有用户自定义的Bean,满足条件才自动装配。所以你想覆盖默认行为,经常只需要自己定义一个同类型的Bean,或者写application.yml里配置项,原理就是条件注解在帮你做开关。
这一段在毕设论文里的“关键技术介绍”章节百试百灵,建议直接背熟。
2.3 前端:Vue3 + Vite + Element Plus是目前的主流起步
前端如果选Vue,一定要先确认生态版本:Vue 3对应Element Plus,Vue 2对应Element UI,两套不能混用。大量老教程写的是Vue 2语法,一个Vue 3项目里把 this.$router 当Vue2那么写会报错。所以建议看教程时先看是不是 Vue 3 组合式API,避免出现“看着教程敲,一启动全是警告”的情况。
开发环境可以这样搭:Node.js 建议 16 或 18 的 LTS 版本,用 Vite 创建项目,安装 vue-router@4、pinia、axios、element-plus。启动时如果遇到端口占用,Vite默认是5173,可以在 vite.config.js 里改 server.port。后端Spring Boot默认端口8080,如果同时跑多个后端进程也要留意端口占用问题。
部署前做一次前端构建 npm run build,把生成的Vue页面放到Nginx里,同时把 /api 开头的请求反向代理到后端。如果是本地演示,不走Nginx也行,直接在Vite配置里加个proxy,把 /api 代理到 http://localhost:8080,前后端联调用起来非常顺。
2.4 中间件要克制,但Redis可以用可插拔的方式引进
我见过不少同学为了“显得厉害”,硬塞Redis、RabbitMQ、Nacos,结果经常在答辩现场因为中间件没启动成功而翻车。这个项目其实可以没有中间件也能完整跑通:验证码用Hutool生成的图片存到本地缓存Map,支付是模拟操作,订单超时用Spring定时任务扫描,并发控制在数据库事务层面解决。
但如果你想在论文里写点有含金量的东西,把Redis作为可选模块加入是有价值的。常见做法有两个位置:一个是登录后把token存入Redis并设置过期时间,实现服务端主动下线;另一个是用Redis做验证码存储,设置5分钟过期,代替本地Map。这样的设计既不会给项目增加很多复杂度,又能在论文里体现出缓存和过期策略思想,属于性价比很高的改进。
如果你为了订单一创建就发消息、支付完成后通知其他模块,硬上RabbitMQ,那要写的配置、交换机、队列代码量会成倍增加,反而容易写出一堆连自己都讲不清的代码。毕设选题有“智慧预订”这几个字,不等于你必须把微服务全家桶都端上来。
3. 数据库表设计是这个项目的胜负手,核心表直接给你
3.1 为什么不能只在房间表里存一个“状态”字段
很多同学最先设计出来的表,是房间表里有 status 字段,0表示空闲,1表示已订。这样做单机版的“增删改查”很爽,但拿到这个题目里根本走不通。原因很简单:一间房是“按天”卖,不是“永久卖断”。同一间房7月1日被人订了,7月3日还能订,如果房间表只有一个status,你怎么表达“7月1日被订、7月2日可订、7月3日之后又可订”这种时间维度上的状态?所以房态不是房间的一个属性,而是“房间 × 日期”这个组合的属性。
正确方向是建立房间日历表,或者用订单记录的日期区间推导可售状态。实际业务里两种会结合:订单是记录事实的来源,日历表则承载可售状态、维护状态和每日价格。这里推荐的一个基础表结构如下。
- 民宿主/用户合表
user:存用户ID、昵称、手机号、密码密文、头像、角色标识。 - 民宿表
house:存民宿ID、民宿主ID、民宿名称、封面图、简介、地址、审核状态、创建时间。 - 房间表
room:存房间ID、所属民宿ID、房间名、类型、面积、可住人数、默认价格、状态。 - 房间日历表
room_calendar:存日历ID、房间ID、日期、当日价格、可售状态、更新时间。 - 订单表
orders:存订单ID、订单编号、用户ID、房间ID、入住日期、离店日期、间夜数、总金额、订单状态、住客姓名电话、创建和支付时间。 - 订单日价格表
order_day_price:存子单ID、订单ID、日期、当晚价格。
注意订单表里要包含 guest_name 和 guest_phone,因为民宿预订通常不是账号本人居住,联系人和账号是两回事。这在业务上符合真实场景,答辩时也能体现出你考虑过入住环节。
3.2 房态日历表:不是所有日期都需要提前生成记录
这里有个非常典型的取舍:要不要给每间房未来365天每一天都预生成一条日历记录?如果房间里只有十间房,乘365也就是几千条数据,问题不大;如果房间几百间,每条日期都预生成上十万条记录,反而维护麻烦。更常见的做法是只对“被锁定、被预定、被设置为维护或特殊价格”的日期写日历记录,其余日期用房间表的 default_price 作为默认价,视为可订状态。
查询某房间某个日期区间是否可订,判断逻辑来自两部分:
- 订单表中是否存在两个日期区间重叠且状态不是已取消/已退款的订单;
- 日历表中是否存在对应日期范围内被标记为“不可售/维护”的记录。
这样写,不用每次订单一进来就遍历几十万条日历记录去改状态,查询效率也更好。下面这个SQL是判断重叠订单的核心逻辑,属于必须理解的那种类型:
sql复制SELECT id
FROM orders
WHERE room_id = #{roomId}
AND status NOT IN ('CANCELED', 'REFUNDED')
AND check_in_date < #{checkOutDate}
AND check_out_date > #{checkInDate}
两个区间重叠的判断条件就是 start1 < end2 AND start2 < end1。这句话如果写成 >= 或者忘记排除取消单,都会导致边界日期判断出错,必踩。
3.3 订单状态机:不要用“已支付/未支付”两个状态走天下
订单是整个预订业务的载体,状态字段最好别用字符串硬塞,建议用整数枚举,定义清晰的状态机。给出一个可落地的标准定义:
| 状态值 | 含义 | 后续可流转状态 |
|---|---|---|
| 0 | 待支付 | 1已支付,4已取消 |
| 1 | 已支付/待入住 | 2已入住,5退款中 |
| 2 | 已入住 | 3已退房 |
| 3 | 已退房/已完成 | 无 |
| 4 | 已取消 | 无(若已支付则需退款) |
| 5 | 退款中 | 3已退房,4已取消 |
我在代码里通常还会配一个 order_status 处理器接口,把所有能执行的状态流转集中到一起,避免在Service里到处写 if (status == 1),后面加状态时改到自己都晕。比如“支付”动作只允许从“待支付”状态流转,“入住”只允许从“已支付”状态流转。
订单状态和房态操作必须放在同一个事务里,这点一定不要分开。例如用户支付成功后,要把状态从0改成1,同时更新日历表里对应日期区间从“锁定”变“已订”;如果状态改了日历没改,就会出现钱付了但后台显示房间还可订的天大Bug。
3.4 订单编号和价格快照:两个容易被忽略的细节
订单没有订单号会显得非常业余。订单号格式可以做成 yyyyMMddHHmmss + 随机数,或者在前面加上日期、后面加用户ID后四位,保证唯一即可。更稳妥的方案是在数据库订单号字段上加唯一索引,一旦数据库层面重复也能报错而不是悄悄插入两条。
价格处理上,千万不要只存一个总金额。如果你在创建订单时不做快照,之后民宿主把7月5日的价格从200改成300,用户已经下单的订单总价要不要跟着变?如果一起变就是严重事故。处理办法是订单创建时把每晚价格快照落到订单日价格表里,查询订单详情时用快照数据汇总。这样价格随便改,已经生成的订单不动,退款也能精确到某个夜晚。
4. 并发抢房、支付超时、改价联动,三块硬骨头一次讲透
4.1 两个用户同时订同一间房,怎么保证不超卖
这是很多毕设代码里最致命的Bug。业务流程看起来是“查询可订状态 → 下单”,但两个用户同时查到可订状态,再同时插入订单,最终就会出现超卖。所谓并发控制,就是要解决这个“检查与插入”中间时间窗口的竞争问题。
方法不唯一,但对这个规模的项目,最简单可靠的是“事务 + 悲观锁”。在创建订单的方法上加 @Transactional,先通过SQL把对应房间行的锁拿到,再做重复订单校验,最后插入订单和更新日历。MySQL的 SELECT ... FOR UPDATE 会锁住这一行,在事务提交前其他事务只能等待,所以同一间房的并发下单就被串行化了。
MyBatis-Plus里没有默认的FOR UPDATE方法,需要自定义Mapper方法:
java复制@Select("SELECT * FROM room WHERE id = #{roomId} FOR UPDATE")
Room selectRoomForUpdate(@Param("roomId") Long roomId);
Service里的伪逻辑大致如下:
java复制@Transactional(rollbackFor = Exception.class)
public Long createOrder(CreateOrderDTO dto) {
Room room = roomMapper.selectRoomForUpdate(dto.getRoomId());
// 若房间状态非上架,抛业务异常
// 检查 orders 中是否有与该房间日期区间重叠且有效的订单
// 输入参数校验:checkOutDate 必须晚于 checkInDate
// 根据日历或默认价格计算每日价格快照,得到总金额
// 插入订单(状态为待支付)
// 在 room_calendar 中把入住到离店前一天的日期标记为锁定
return order.getId();
}
这个方案能答得很好,但你在答辩时要清楚它有两个前提:房间行锁必须发生在事务内,而且尽量在事务开始后尽快执行;如果房间行不存在,FOR UPDATE不会锁任何东西,所以还要再判断房间是否存在。某些同学喜欢在事务提交前执行一堆耗时非数据库操作,导致锁持有时间过长,遇到这个问题可以把非数据库操作尽量放到事务前处理。
如果你喜欢写“乐观锁”,也能在房间表上加一个 version 字段,更新时 UPDATE room SET version = version + 1 WHERE id = ? AND version = ?,更新行数为0就重试。但乐观锁更适合更新同一行数据的场景,在这个“先校验区间再插入订单”的双步骤场景里,只锁version字段并不能彻底避免两个事务同时通过区间校验,所以想图省事就记住:房间级FOR UPDATE才是这个需求的直接解法。
4.2 用户下了单不支付,房态怎么自动释放
订单创建后如果用户一直不支付,被锁定的日期范围不能永久占着。常见方案是设定一个支付有效期,比如15分钟。到时间后把状态从“待支付”改成“已取消”,同时释放日历锁。
实现上最省事的是Spring的 @Scheduled 定时扫描:
java复制@Scheduled(fixedDelay = 30000)
public void cancelExpiredOrders() {
List<Order> expiredOrders = orderMapper.selectExpiredUnpaidOrders(
LocalDateTime.now().minusMinutes(PAYMENT_EXPIRE_MINUTES));
for (Order order : expiredOrders) {
cancelOrder(order.getId());
}
}
对应的SQL扫描条件为“订单状态是待支付,且创建时间早于当前时间15分钟”。每30秒扫一次,然后调用统一的取消逻辑。Cancel逻辑里要完成:把订单状态改成已取消,把订单对应房间日期范围内的日历锁定状态改回可售。这里必须要复用同一个 cancelOrder 方法,因为用户手动取消和系统超时取消会是同一套业务动作,在代码里重复写两遍容易漏掉释放房态。
定时扫描方式的缺点是非实时,用户可能在超时后的几秒内还会看到待支付状态,但毕设场景完全可以接受。你如果想体现出更强的设计意识,可以在答辩时主动说一句:这里如果引入Redis的延时队列,比如ZSet按过期时间排序,订单创建后把订单ID塞进ZSet里,到期的元素用另一个线程去处理,能更精确地实现超时取消。但当前项目里为了减少部署依赖,采用定时扫描+业务补偿已经足够。这句话说完,老师基本不会在这个环节为难你。
4.3 民宿主改了某天的价格,哪些订单会受影响
很多项目只做了“房间表里有默认价格”,订单一律按默认价格乘间夜数,这样民宿主无法对不同日期差异化定价,题目里的“智慧”就无从谈起。更合理的设计是房间表里存 default_price,日历表里存 date_price,如果某个日期在日历表里有特殊价格,就用特殊价,否则回退到默认价。
民宿主对单个日期批量改价时,前端传过来一批日期和价格,后端循环写入日历表。这里有个细节:如果某个日期已经被订单占用且订单已完成支付,日期价格改动不影响历史订单,因为订单详情读取的是 order_day_price 快照,不实时关联日历表。如果该日期只是被待支付订单短暂锁定,可以有两种选择,简单方案是不拦截,待支付订单如果最后支付了,价格也以订单创建时的快照为准。
改价服务还有一个容易被忽略的地方:批量写入时要考虑“覆盖写”而不是“追加”,因为同一天可能被重复设置多次。通常可以用 INSERT INTO room_calendar ... ON DUPLICATE KEY UPDATE date_price = VALUES(date_price), status = VALUES(status),按 room_id + calendar_date 建唯一索引,这样批量保存逻辑非常简单。
5. Vue前端怎么做,呈现出来的才是“平台”而不是“后台”
5.1 用户端主流程:从搜索到下单的路由设计
前端页面建议按角色拆成两套布局:一套是C端面向用户的 user-layout,另一套是B端面向民宿主和平台管理员的 admin-layout。用户浏览的一端,建议有这些页面:
- 首页:搜索框 + 民宿卡片列表,按地区、入住日期、离店日期联合搜索。
- 民宿详情页:图集、房型列表、房间信息、每个房间在选定日期区间的可订状态。
- 订单确认页:选择入住人数、填写住客姓名和手机号、展示每晚价格明细、总金额。
- 我的订单列表页:按状态区分待支付、待入住、已入住、已取消。
- 支付页:模拟支付二维码或支付按钮,点击后调后端支付接口并轮询订单状态。
C端页面和后台管理端页面放在两套不同布局下,既能让路由清晰,也能突出“平台”的观感。首页的搜索如果只想做到查得到,可以直接把地区字段放到民宿表地址模糊查询,但更贴近真实平台的增量做法是把民宿归属的省市区拆出来,存省市区代码而不是一整段地址字符串。
5.2 房态日历组件:怎么把可订与不可订表达清楚
前端最核心的组件其实是房态日历或日期状态列表。房源在一个时间段内的可用情况是一行一行的,每间房横向展开日期,日期上用颜色块表达状态。做这个组件之前,先向后端要一个接口,例如“获取某民宿在某月内所有房间的房态矩阵”,返回一个结构类似 [{date: '2025-07-01', rooms: [{roomId: 1, status: 'AVAILABLE'}]}] 的数据。
Vue 3里渲染状态块可以这么写:
javascript复制const statusMap = {
AVAILABLE: { color: '#67C23A', text: '可订' },
LOCKED: { color: '#E6A23C', text: '锁定' },
BOOKED: { color: '#F56C6C', text: '已订' },
MAINTENANCE: { color: '#909399', text: '维护' }
};
然后在模板里按房间和日期双重循环渲染单元格,颜色就来自statusMap。交互上要支持用户点击“入住日期”和“离店日期”,选完后对选中区间做高亮,并且底部同步显示合计价格,这部分因为涉及动态计算,建议用 computed 而不是在每个点击事件里手工维护一堆中间变量。
这类房态组件代码量并不大,但它非常出效果,演示时把鼠标从一个房间拖到另一个日期区间上,预订体验立刻就和普通管理后台拉开了差距。写这个功能时,请务必把“离店日不占用房间”这个规则做对,也就是说入住7月1日、离店7月3日,实际占用的是7月1日和2日两个夜晚,7月3日当天房间可被下一位客人预订。前端在颜色高亮、提交日期参数上都按这个逻辑处理,很多Bug其实都从“日期边界算错一天”开始。
5.3 前端调用后端时的统一处理
前后端分离项目,联调的时候最常见的报错是跨域。用Vite开发时可以配置proxy:
javascript复制export default defineConfig({
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
});
后端接口统一以 /api 开头,代理就把请求转发到后端8080端口,浏览器看起来请求是同源的,跨域问题直接消失。如果上线时代理失效,也可以在Spring Boot里统一配置Cors允许跨域,但个人更推荐开发阶段用Vite代理,简单不碰后端配置。
Axios拦截器通常要做两件事,一是把登录后保存在本地或内存里的token拼到请求头,二是判断响应码,如果后端返回401就跳转到登录页。还有一套约定最好前后端都遵循:后端返回统一结构 { code: 200, message: 'success', data: {...} },业务异常也返回HTTP 200但code非200,这样前端只需要在拦截器判断code,出错时统一用组件库提示。
5.4 管理端看板:房态与订单数据集中的呈现
运维看板不需要花哨,重点是把“订单数量、销售额、民宿审核数量、房间今日入住率”展示出来,并配上一张按日期的订单趋势折线图或近7日销售额柱状图。图表库用 ECharts 或者 AntV G2Plot 都可以。
后端的统计报表接口需要写一些聚合SQL。这里有个经验:尽量用数据库完成聚合,不要用 List<Order> 全量拉进内存再用Java循环计算。比如查询近7日每天的销售额,可以按 DATE(pay_time) 做 GROUP BY,再把Java的LocalDate与查询结果map对齐,日期缺失就补0。这样数据量再大也不至于把内存打爆。
6. 答辩演示和论文写作里的隐藏加分细节
6.1 演示前必须把“数据剧本”设计好
演示系统最怕临场造数据,最忌讳在答辩时现场注册三个账号、再吭哧吭哧录房源。我用这类系统做演示的经验是,提前建立一套完整的数据剧本:
- 准备一个好的默认管理员账号,密码明显易记;
- 预置一个民宿主账号,账号下面至少2个民宿、每个民宿3-5个房间;
- 预置一个普通用户账号,账号的“我的订单”里至少有一个待支付、一个待入住、一个已完成,方便演示订单状态流转;
- 在日历表里预置某几个房间近两天的“已订”状态,让详情页一进去就能看到红绿状态混排而不是一片绿。
演示顺序建议是:用户登录搜索某地区民宿,打开民宿详情,选中7月5日到7月7日两个夜晚,看到可选房间后下单,走一遍模拟支付,订单变成待入住;然后切民宿主账号,看到这笔新订单,执行“办理入住”,再切管理员端查看订单数据统计变化。整个过程不要超过8分钟,展示的是从C端到B端再到平台端的数据闭环,比只点菜单页有说服力得多。
6.2 论文架构建议:每章都能和技术亮点对应上
论文结构通常可以用:绪论、相关技术、需求分析、系统设计、系统实现、系统测试、总结。但最容易被老师挑刺的是“需求分析写了一张系统功能结构图就开始设计数据库,业务逻辑里的核心难点完全没有交代”。所以建议第4章系统设计中,一定留一节专门写核心业务场景的详细设计,比如:
- 房态日历模型的设计;
- 订单状态机的定义;
- 锁房流程中并发冲突的解决方案;
- 到期未支付订单的处理方案;
- 价格日历和订单快照的结合方式。
这样论文的目录一看就知道不是拼凑的。系统测试部分也可以针对核心场景写:
- 界面正常搜索并下单的流程测试;
- 两个事务并发下同一房间同一日期只能成功一个的测试;
- 超过15分钟未支付订单自动取消的测试。
能拿出并发抢房的测试过程,是很大的加分项。
6.3 答辩前把这三个问题提前想好
根据历届问答经验,老师大概率会围绕这几个点来问:
第一,“你的锁房是怎么实现的?”你应该能说出在创建订单的Service方法上加了事务,并且先执行了房间行级锁的FOR UPDATE查询,然后解释为什么这样能避免两个会话同时下单。
第二,“如果用户下单后一直不支付怎么办?”你要讲出定时扫描任务、支付有效期、取消时同时释放房态这个动作链。如果能在代码里把取消逻辑写成一个复用方法,回答就会更扎实。
第三,“民宿主修改了房间某天的价格,已经存在的订单怎么处理?”你要讲出订单创建时做了价格快照,而不是实时引用日历表。
这三个问题能顺利答上来,实际上就已经把你跟只会做增删改查的同学区分开来了。很多同学在答辩时低着头念PPT,就是因为这些核心实现根本没有过脑子。
6.4 演示环境的最后检查清单
出门演示前一天和当天早上,建议按这个清单过一遍:
- 数据库里预置数据是否还在,有没有被测试订单污染得太花;
- 启动后端时端口是否被其他软件占用,IDEA运行窗口是否报错;
- 前端和本地Java服务是否处于同一环境,浏览器访问的是不是代理后的地址;
- 时间是否充裕,尽量不现场编译,直接运行已经编译好的项目;
- 换一台电脑演示时,数据库的账号密码、MySQL是否启动,有没有把配置文件里的绝对路径写死。
后台启动失败的最大来源基本都是“MySQL没起来”或者“Redis没起来”。如果你选择了Redis做token或验证码存储,记得要把Redis的开机自启也配置好,否则演示现场redis连接失败,登录都登不进去,这是最尴尬的情况。
最后说一点我的个人体会。做民宿预定类的毕设,代码量不是最大难点,难的是你要站在“预订平台”角度理解业务,而不仅仅是站在写代码的角度搬几个页面。哪怕你最后只实现了订单和房态的联动,只要这个联动经得起推敲,答辩效果就会远好于做得花里胡哨却没有一条完整闭环的系统。如果时间允许,在基本功能完成之后,我建议你再额外做一次“压力自测”,开两个浏览器,两个账号同时抢同一个房间的最后一个夜晚,看系统是不是真的只让一个人下单成功。这道自测题通过的那一刻,你才算真的把这个题目做透了。
