第4周中期检查,答辩老师点开预约界面,连续选了同一间机房的同一时段,点了两次提交,系统弹了两个“预约成功”,数据库里躺着两条完全重合的记录。台上的同学想解释“后台正在加Redis分布式锁”,老师直接打断:“并发先放一边,你连基础的起止时间重合校验都没写。”

图:落地路径示意 · 毕设功能极简化替代方案(明确硬件与复杂算法降级为纯软件落地的替代物)

图:系统架构示意 · 预约极简四表数据模型(理清支撑预约闭环的4张核心实体表依赖关系)
这就是大量实验室预约类毕设的真实死胡同:任务书里写满了“人脸识别门禁联动”、“遗传算法排课”、“物联网温湿度监控”,结果到第5周,学生连本地单库的预约冲突检测都没跑通,硬件模块买来烧了两个板子,串口通信在演示机上天天抛 PortInUseException。
做毕设不是开硬件加工厂,商业接单也得先测算单人交付周期。把那些不切实际的外设全扒掉,整个实验室预约题目的心脏只有八个字:时段排他、状态流转。
硬件联动的死胡同与纯软件收敛
开题时最容易冲动的,是把实验室门禁和打卡机写进开题报告。真实情况是:校内公网不给开端口、寝室WiFi下ESP32连不上本地SpringBoot、门禁协议文档全是厂商私有hex码。一旦卡死在硬件握手,后半程连写论文的时间都没有。
| 模块维度 | 危险的“大而全”做法 | 答辩可演示的收敛做法 |
|---|---|---|
| 门禁与签到 | 树莓派人脸识别 + 继电器强电控制 | 前端生成动态核销码,管理员点击“核销打卡” |
| 资源调度 | 遗传算法多目标自动排课 | 用户自选可用时间槽,系统只做冲突校验 |
| 消息提醒 | 商业短信网关 + 微信公众号推送 | 站内信单表轮询,管理员/学生面板红点提示 |
| 冲突控制 | Redisson分布式锁 + Lua脚本 | 数据库事务 + 显式区间重叠排他查询 |
唯一的选型建议:在单体 Spring Boot 3.x + MySQL 8.0 里把时间区间校验算死,不要碰微服务,不要碰实体单片机。切换到硬件或消息中间件的唯一条件,是你的专业归属是物联网工程或自动化,且学院明确要求必须有硬件实物验收;如果是纯软件或计科方向,硬上硬件属于自己找死。
4张核心表卡死业务骨架
实验室预约系统不需要二十多张表,跑通主线只需要4张表:用户、实验室资产、预约单据、时段排期。
-- 1. 实验室基础表
CREATE TABLE `lab_room` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`room_no` VARCHAR(32) NOT NULL COMMENT '机房号如B402',
`capacity` INT NOT NULL DEFAULT 40 COMMENT '工位数',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '1正常 0维护中'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 2. 预约申请单(核心事务表)
CREATE TABLE `lab_reservation` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`user_id` BIGINT NOT NULL COMMENT '申请人ID',
`room_id` BIGINT NOT NULL COMMENT '预约实验室ID',
`start_time` DATETIME NOT NULL COMMENT '预约开始时间',
`end_time` DATETIME NOT NULL COMMENT '预约结束时间',
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '0待审核 1已通过 2已拒绝 3已取消 4已核销',
`reason` VARCHAR(255) DEFAULT '' COMMENT '申请借用事由',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_room_time` (`room_id`, `start_time`, `end_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
剩下的两张表是 sys_user(只留用户名、密码、角色)与 lab_rule(存储开放预约的时段范围,如8:00-21:00)。外键一律不建,业务约束全在Java逻辑层做断言。
时段防重叠SQL与预约接口实现
预约冲突的核心逻辑,不是简单的“等值查询”,而是“区间交集”。两个时间段 [startA, endA] 与 [startB, endB] 重合的充分必要条件是:
startA < endB AND endA > startB
在执行插入前,必须执行这条防穿透校验(标明可跑):
@Transactional(rollbackFor = Exception.class)
public Long applyReservation(ReservationDTO dto) {
if (!dto.getStartTime().isBefore(dto.getEndTime())) {
throw new BusinessException("开始时间必须早于结束时间");
}
// 核心断言:当前房间是否存在时间重合且有效的预约(排除了已拒绝和已取消)
// 只要有 1 条记录重合,直接阻断
int overlapCount = reservationMapper.countOverlap(
dto.getRoomId(),
dto.getStartTime(),
dto.getEndTime()
);
if (overlapCount > 0) {
throw new BusinessException("该时段实验室已被占用,请重新选择");
}
LabReservation record = new LabReservation();
record.setUserId(dto.getUserId());
record.setRoomId(dto.getRoomId());
record.setStartTime(dto.getStartTime());
record.setEndTime(dto.getEndTime());
record.setStatus(0); // 待审核
record.setReason(dto.getReason());
reservationMapper.insert(record);
return record.getId();
}
对应的 MyBatis XML 片段:
<select id="countOverlap" resultType="int">
SELECT COUNT(1) FROM lab_reservation
WHERE room_id = #{roomId}
AND status IN (0, 1) -- 待审核与已通过的时段均不可被抢占
AND start_time < #{endTime}
AND end_time > #{startTime}
</select>
可反驳判断:单体毕设无需在防重叠上搞任何 Redis 复杂 Lua 锁。只要接口挂上了数据库事务,并且在 MySQL 端通过上述条件查验,本地并发测试 20 次请求/秒时(本科演示极限压力),单单利用 MySQL InnoDB 的默认隔离级别,就能百分之百拦住脏数据;除非你的演示场景要求扛住全校万人整点秒杀抢机房,否则把精力花在 Redis 缓存同步上纯属浪费时间。
8周验收推进节奏
不要试图在第一周就把系统界面画完,按照工期推进能避开绝大部分延毕风险:
1. 第1-2周(数据层确界):安装 MySQL 8.0,建立上述4张核心表。用 Navicat 手动插入 3 个不同角色的测试账号和 2 间机房数据,手写 5 条 SQL 跑通预约重叠检测。
2. 第3-4周(流转接口打通):搭建 Spring Boot 基础工程,只写 3 个接口:学生提交预约、老师审核预约(通过/驳回)、个人取消预约。用 Postman 全部跑通并存入测试用例集合。
3. 第5-6周(前端界面串联):用 Vue3 + Element Plus 搭个极简单页面。学生端选日期、选时段、点提交;管理端看列表、点审核。把控制台所有 404 和 500 报错清零。
4. 第7-8周(异常演示与论文落盘):造一份冲突测试数据(如两人抢周三上午机房),作为论文“系统测试”章节的核心截图。写齐功能测试用例表,准备答辩PPT演示脚本。
今晚就可以执行的动作:打开你的任务书,找到“物联网设备接入”、“人脸门禁识别”和“遗传算法”,全部改成“基于时段互斥的排他控制”和“全流程预约审核流转”。边界压得越狠,中期就越稳,代码就越不容易穿帮。
实践备忘
技术细节到这里可以告一段落。若你还卡在题目能不能做完、栈怎么选、演示怎么兜底,可以把公开的选题自检与案例结构当参考——自己写代码、自己改论文,资料只作对照。

从环境、模块拆分到答辩叙事,按清单推进更稳

上线/演示前用清单自检,少返工
题目能不能做完,把专业和剩余周数发到 选题评估。
公开案例也可对照:课题工坊