SSM框架下的共享办公室预约系统:核心逻辑与工程实践 这套基于 SSM 的共享办公室预约系统是很多计算机相关专业学生会选的毕业设计题目。它的业务场景很好理解用户想临时租用一间办公室、会议室或工位先在系统里看哪个房间哪个时段空闲提交预约申请管理员审核通过后按时使用结束后系统留下记录方便后台统计和结算。核心流程就是一个“预约闭环”但越是这种看似简单的闭环越能把 SSM 框架里的请求流转、数据库设计、事务控制、权限过滤、状态管理这些核心知识点全部串起来。这篇内容我按实际开发顺序来写不是只贴一张功能清单而是从需求拆解、表结构设计讲到核心预约逻辑、前后端联调、部署演示和远程协助排查。适合两类人看一类是正在做这个选题的毕业生想搞清楚文档怎么写、代码怎么讲得明白另一类是想拿 SSM 练手但不想做太偏门系统的在校生这类“共享办公预约”比电商、二手交易平台更轻量数据库关系也更适合入门阶段吃透。1. 项目定位这个系统到底要解决什么问题1.1 共享办公室预约的核心业务场景共享办公在现实中有两种常见形态一种是入驻企业按月租下固定工位另一种是零散人员按小时、按天租用会议室或开放工位。预约系统的设计重心落在第二种因为它涉及“临时性”和“排他性”。一个房间在某个时间段只能被同一方占用如果预约冲突控制不好线下会出现两拨人同时开门进去的尴尬场面。把场景拆成事件流去理解会比较清楚。用户端的需求是注册登录、浏览办公室列表、查看未来某一天某个房间的可用时段、提交预约申请、在个人中心查看已约记录、取消尚未开始且通过审核的预约。管理员端的需求则是管理办公室基础信息、上架或停用房间、查看并审核预约订单、统计房间被占用的情况、导出或查看某段时间的使用数据。这个角色划分对应到系统实现前端用户权限和后端管理权限一定要分开。现实中很多毕设把用户和管理员混在一套页面里用隐藏链接去访问后台安全上很薄弱答辩时也容易被老师一句话问住普通用户如果猜到后台 URL是不是就能越权操作所以从第一版设计开始就需要给权限控制留出明确的代码位置而不是把管理员操作全写在普通用户的控制器里。1.2 为什么毕业设计选 SSM 而不是 Spring Boot现在企业项目大多直接用 Spring Boot但很多学校课程还在讲 SSM毕业设计也允许甚至鼓励用 SSM。这个问题每年都有学生问我的观点是如果你的目标是面试加分或直接展示前沿技术那 Spring Boot 没问题但如果是做课程设计、毕业设计并且时间排期紧张SSM 反而是更稳的选择。原因有三方面。第一SSM 框架分层非常明确表现层是 SpringMVC 的 Controller业务层是 Service持久层是 MyBatis 的 Mapper。论文结构几乎可以按照这三层直接展开不用额外堆砌无关内容。第二SSM 需要手写大量 XML 和配置代码中类似SqlSessionFactoryBean、MapperScannerConfigurer的声明能帮助你把框架的启动过程搞清楚面试时如果被问“Spring 和 SpringMVC 的关系”“MyBatis 怎么关联接口”不会心虚。第三毕设重点考察的是需求分析和系统设计能力而不是框架选型有多新。当然做 SSM 也要注意年代问题。JDK 尽量用 8Tomcat 用 8.5MySQL 用 5.7这三个版本组合最不容易出坑。如果你机器上装了 JDK 17 或更高版本SSM 项目反而会因为反射和模块化限制报一些经典但不好搜的错误没必要在环境上跟自己过不去。1.3 功能边界与角色划分一个标准共享办公室预约系统建议保留三个核心角色普通用户、管理员、超级管理员。普通用户负责“预约与取消”管理员负责“审核与房间管理”超级管理员只负责给管理员分配角色和查看系统日志。如果你的论文需要控制篇幅可以把超级管理员合并到管理员里如果篇幅偏少再加一个企业用户角色去提交多人预约申请顺便展示一对多关联查询。功能边界上要克制一点。不要为了撑功能去加支付、退款、发票、电子签章这些会让数据库关联变得复杂也让答辩变得不可控。我见过不少学生在系统里接支付宝沙箱支付回调相关的问题排查看半天最后答辩老师只问了一句“支付密钥怎么保管”就答不上来。预约类系统把“审核”和“取消”做成最核心流程配套上“统计报表”和“日志记录”已经足以拿一个不错的分数。2. 数据库设计先把关系理顺后面少熬夜2.1 核心表划分与字段设计SSM 项目一旦进入编码阶段再回头改表结构成本很高所以建表前一定要把关系理顺。共享办公室预约系统的核心表有四张用户表、办公室表、预约订单表、时段表。如果业务里需要释放被用户占用但没来使用的预约还要考虑一张操作日志表。前四张表先看字段定义。用户表我常用的设计如下CREATE TABLE tb_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录账号, password VARCHAR(100) NOT NULL COMMENT 密码建议MD5加盐, nickname VARCHAR(50) DEFAULT COMMENT 昵称, phone VARCHAR(20) DEFAULT COMMENT 手机号, role TINYINT NOT NULL DEFAULT 0 COMMENT 角色 0普通用户 1管理员, status TINYINT NOT NULL DEFAULT 1 COMMENT 状态 1正常 0禁用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;密码加密在当前很多毕设里只是形式但我建议至少做一层 MD5 加盐不要在数据库里存明文。理由是论文里“安全性设计”章节要有东西可写也是面试时顺手就能拿得出手的亮点。办公室表和预约表如下CREATE TABLE tb_room ( id INT PRIMARY KEY AUTO_INCREMENT, room_name VARCHAR(100) NOT NULL COMMENT 办公室名称如A座203, location VARCHAR(200) COMMENT 位置描述, capacity INT DEFAULT 1 COMMENT 可容纳人数, devices VARCHAR(200) COMMENT 设备如投影仪白板, price DECIMAL(10,2) DEFAULT 0 COMMENT 每小时价格, status TINYINT DEFAULT 1 COMMENT 1可预约 0停用, image VARCHAR(200) DEFAULT COMMENT 房间图片路径, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE tb_reservation ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL COMMENT 预约人, room_id INT NOT NULL COMMENT 办公室, use_date DATE NOT NULL COMMENT 使用日期, start_time TIME NOT NULL COMMENT 开始时段, end_time TIME NOT NULL COMMENT 结束时段, total_price DECIMAL(10,2) DEFAULT 0 COMMENT 最终费用可按小时价格计算, status TINYINT DEFAULT 0 COMMENT 0待审核 1审核通过 2已取消 3已拒绝 4已完成 5爽约, remark VARCHAR(255) DEFAULT COMMENT 备注, audit_user_id INT DEFAULT NULL COMMENT 审核人, audit_time DATETIME DEFAULT NULL COMMENT 审核时间, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这张预订表把日期和时间拆成三个字段use_date存具体哪天start_time和end_time存起止时间。这样便于 SQL 直接比较时间段也不需要在 Java 里反复解析字符串。status字段从 0 到 5 是一个状态机后续所有业务逻辑都围绕这个状态字段展开。2.2 预约冲突判断的 SQL 逻辑预约系统最核心的 SQL 不是增删改查而是“查冲突”。假设用户提交的预约请求是 room_id2use_date2025-05-20start_time10:30end_time12:00。系统需要判断该房间当天是否存在一个已生效的预约和它重叠。冲突判断逻辑用下面这段 MyBatis 映射完成SELECT COUNT(*) FROM tb_reservation WHERE room_id #{roomId} AND use_date #{useDate} AND status IN (0, 1) AND start_time #{endTime} AND end_time #{startTime}只要count大于 0就说明时间重叠。这里有个很多新手会写错的地方用start_time #{endTime} AND end_time #{startTime}看起来也对但把边界包含进去后会出现前一个预约 11:00 结束、后一个预约 11:00 开始被误判为冲突的情况。如果希望首尾相接的两个预约可以存在就必须用严格小于和严格大于。状态筛选里要包含待审核状态。原因是预约提交后还没被管理员审核如果管理员不处理数据库里这条记录对后一个用户依然有“占坑”作用。现实场景中用户只是提交了一个申请还不想马上付款那么从系统层面看该时段应该被认为不可约否则可能发生两个申请都通过、实际撞车的问题。2.3 状态机设计与流程闭环预约订单的status转换要画出一个明确的合法路径这样代码逻辑才不会出现“取消已拒绝的订单”之类的低级错误。合法路径大致如下用户提交预约状态 0 待审核。管理员审核通过状态 0 - 1 审核通过。管理员审核拒绝状态 0 - 3 已拒绝。用户申请取消只有待审核0和审核通过1状态可取消取消后状态变为 2 已取消。预约日期结束管理员确认完成状态 1 - 4 已完成。超时未使用管理员标记爽约状态 1 - 5 爽约。很多系统会加一个“自动取消超过指定时间未审核订单”的定时任务这个想法很好但实现成本超出普通毕设范畴。我的建议是在管理员端展示一个超时列表让手动操作成为兜底既简单又能体现在操作流程设计上考虑周全。表与表之间的外键关系可以考虑添加也可以只在 MyBatis 的关联查询中体现。实际项目中我建议保留数据库外键约束这样即使代码偶尔绕过 Service 层写非法订单数据库也会拦住。不过要注意有些学生习惯在应用里删表重建如果表很多外键会带来删除顺序的麻烦。作为取舍可以保留外键但在开发阶段用SET FOREIGN_KEY_CHECKS0临时关闭项目演示时再恢复。3. 后端实现细节SSM 三层架构怎么落地3.1 Maven 工程结构与 MyBatis 配置示例后端工程推荐用 Maven 打 war 包结构不要用 Eclipse 的默认 Dynamic Web Project 手工导 jar否则后期补依赖会非常痛苦。基础包结构建议按 controller、service、mapper、domain 四层组织再加一个 common 包存放公共拦截器、工具类和统一返回结果。applicationContext.xml 中需要配置数据源、事务管理器、MyBatis 的 SqlSessionFactory 和 Mapper 扫描器。核心配置如下context:property-placeholder locationclasspath:jdbc.properties/ bean iddataSource classorg.springframework.jdbc.datasource.DriverManagerDataSource property namedriverClassName value${jdbc.driver}/ property nameurl value${jdbc.url}/ property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ /bean bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property namemapperLocations valueclasspath:mapper/*.xml/ /bean bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.example.dao/ /bean bean idtxManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean配置文件里最容易被忽略的是连接 MySQL 时要加一行参数jdbc.urljdbc:mysql://localhost:3306/office_share?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai如果不加serverTimezoneMySQL 8.x 在 JDBC 连接时会把本地时区与服务器默认时区做对比抛出 CST 相关异常。这个报错信息不直观学生很容易在环境上卡一整晚。我一般建议直接用 MySQL 5.7并且把所有字符集都指定为 utf8mb4避免中文乱码。3.2 预约核心服务的实现思路预约服务是系统的核心它的 Service 层逻辑要点有三个时间合法性校验、冲突校验、事务写入。以下是一段可以放到项目里的核心逻辑片段Service public class ReservationServiceImpl implements ReservationService { Autowired private ReservationDao reservationDao; Autowired private RoomDao roomDao; Override Transactional(rollbackFor Exception.class) public boolean createReservation(Reservation req) { // 1. 基础参数校验防止非法时间 if (req.getStartTime().compareTo(req.getEndTime()) 0) { throw new BusinessException(结束时间必须晚于开始时间); } // 2. 房间是否存在且可用 Room room roomDao.findById(req.getRoomId()); if (room null || room.getStatus() ! 1) { throw new BusinessException(该办公室不可预约); } // 3. 冲突校验同一房间同一日期同一时段只能有一个有效预约 int conflictCount reservationDao.countConflict( req.getRoomId(), req.getUseDate(), req.getStartTime(), req.getEndTime()); if (conflictCount 0) { throw new BusinessException(该时段已被预约请选择其他时段); } // 4. 计算金额并插入记录 long minutes Duration.between(req.getStartTime(), req.getEndTime()).toMinutes(); BigDecimal hour BigDecimal.valueOf(minutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); req.setTotalPrice(room.getPrice().multiply(hour)); return reservationDao.insert(req) 0; } }这里要强调Transactional(rollbackFor Exception.class)的作用。如果不用这个事务注解冲突检查通过、插入记录时数据库报错预约 ID 虽然不会生成但代码可能已经占用了一个连接现场表现比较诡异。加了注解后只要方法内任何异常抛出前面的数据库操作都会回滚避免脏数据。3.3 时间段粒度问题现实中会议室预约经常按 30 分钟或 1 小时为单位。如果允许任意起止时间用户填 09:12 到 10:47系统虽然也能处理但线下服务人员根本没法排班。建议在前端用下拉框限定时间粒度比如每 30 分钟一个选项从 09:00 到 21:00。这样的好处是后端冲突判断不需要再解析自由文本数据也比较干净。有些毕设还把时间拆成固定编号的节次表比如“上午第一节 09:00~10:00”房间表和节次表做多对多关联预约单直接选节次。这种方式适合课程表式的固定排课不适合共享办公的按需预订。反而是在一张预约表里直接存放起止时间更灵活也方便后台统计每天的使用时长。3.4 接口与页面交互SSM 前端页面通常用 JSP 加 jQuery不建议再去套一套前端框架。后端 Controller 返回逻辑视图通过 AJAX 请求获取房间剩余时间。比如管理员进入审核页时页面加载后请求/admin/reservation/list后端返回 JSON 数据前端用 jQuery 渲染表格和分页。如果项目进度紧张房间时间选择页可以干脆用页面加载时后端直接查出“某天已被预订的时间段”并放入隐藏域用户选择时间时前端做一次过滤。这种方式减少异步接口数量代码也更容易顺着论文流程讲。4. 前端与权限这个系统最容易丢分的地方4.1 基于拦截器的登录与角色权限只做“登录后显示用户名”是不足以通过论文查重的系统设计里一定要把权限控制写成独立模块。SSM 中通常用 SpringMVC Interceptor 做登录过滤。具体做法是配置一个LoginInterceptor拦截所有/admin/**、/user/**请求未登录直接重定向到登录页再配置一个AdminInterceptor拦截/admin/**判断 session 里的role是否等于 1。配置示例mvc:interceptors mvc:interceptor mvc:mapping path/user/**/ bean classcom.example.interceptor.LoginInterceptor/ /mvc:interceptor mvc:interceptor mvc:mapping path/admin/**/ bean classcom.example.interceptor.LoginInterceptor/ /mvc:interceptor /mvc:interceptors注意配置的路径是基于项目部署根路径的相对路径不是页面上的请求地址。我之前看过一个项目把 interceptor 的 path 写成/office/user/**结果本地访问能拦部署到 Tomcat 后因为应用名是/ssm_office路径全部失效。解决办法是给项目设成 ROOT 或者使用${pageContext.request.contextPath}拼接绝对路径。4.2 日期选择与可视化排期预约系统的前端交互有两个地方做好了体验会提升非常多一个是日期选择一个是可视化排期。日期选择建议使用laydate或bootstrap-datetimepicker并且要设置最小日期为今天禁止选择过去日期。这既是体验问题也是数据合法性问题。很多毕设忽略这一步导致用户能约昨天的办公室后端如果没校验数据库里就会出现一条过期的历史预约。后端 Service 层同样需要再做一次日期判断不能只依赖前端。可视化排期可以用一张时间矩阵表格行为房间列为当天从 09:00 到 21:00 的时间离散段不可预约的格子用灰色填充。因为共享办公室数量通常不超过 20 个一天的时间段数量比如 24 个一张 20×24 的表格渲染起来压力很小。在论文的“系统实现”章节展示这张图比贴一堆表单截图更直观。4.3 参数校验与异常处理前端传参不可信是所有 Web 项目都要养成的习惯。预约时用户完全可以通过开发者工具改请求参数比如把 room_id 改成停用房间的 ID把 price 改成 0。若系统信任前端传的 price后台就会出现零元订单。正确做法是后端根据房间基础信息重新计算价格前端传的价格字段直接忽略。后端参数校验可以用 JSR 303 注解也可以用简单的手写判断。考虑到 SSM 配置 JSR 303 需要额外引入依赖我建议手写校验逻辑代码行数不多但异常信息可以做到非常友好。例如if (req.getUseDate() null) { throw new BusinessException(请选择使用日期); } if (req.getUseDate().isBefore(LocalDate.now())) { throw new BusinessException(不能预约过去的日期); }统一异常处理器也要做新建一个ControllerAdvice类捕获BusinessException后返回 JSON捕获其他异常后记录日志并返回“系统繁忙”。论文里的“系统健壮性设计”章节就有了实打实的内容。5. 部署、远程调试与演示准备很多毕设在这里翻车5.1 本地开发环境搭建毕设演示最怕在导师电脑上现场配环境耗时且容易出问题。建议先在自己的机器上固定一套可运行环境JDK 8、Maven 3.6.3、Tomcat 8.5、MySQL 5.7开发工具可以用 IntelliJ IDEA。IDE 里将 Tomcat 配好之后启动项目访问http://localhost:8080/项目名能正常打开首页说明本地链路是通的。数据库导入有两种方式。一种是在 IDEA 的 Database 面板直接连接本地 MySQL执行建库脚本。另一种是命令行导入mysql -uroot -p office_share.sql无论哪种方式导入完成后都建议用几条SELECT语句确认表中确实有初始管理员账号免得演示时登录不进后台。5.2 如何在导师面前顺利演示的关键配置导师看演示时最担心卡在登录界面。因此我把“演示前检查项”压缩成四条每条都是踩过坑换来的确保 Tomcat 的端口没有冲突如果 8080 被占用改掉后访问地址要同步更新。确保 MySQL 服务已启动且 jdbc.properties 里的账号密码与本地数据库一致。确保浏览器清缓存前访问地址是带上下文路径的完整地址不是直接打开某个 JSP。准备至少两组演示账号普通用户 1 个、管理员 1 个预先在系统里创建好一些“待审核”数据现场演示审核流程不用临时下单。在共享屏幕做远程演示时如果带宽不好可以先把系统跑在本地把需要展示的流程录成 3 分钟短视频作为备份。真正现场操作时一旦网络卡顿立刻切回本地页面从容衔接。5.3 远程调试与协助排查的通用套路“远程调试”这个词在不同语境下有完全不同的含义。有些人理解为 IDE 里的 Debug 模式远程连接 Tomcat 进程一般用来给线上的代码定位问题更多学生在毕业设计里其实是指“让我远程帮我看看哪里报错”也就是远程协助排查。后者常见的场景是学生在自己电脑上跑项目导师在微信另一端远程看代码或者学生把 war 包部署到云服务器上导师直接通过浏览器访问系统进行验收。对于学生自己电脑上的问题远程协助最稳妥的方式是直接使用视频会议的共享屏幕功能。排查时思路要固定不要东点一下西点一下。标准顺序是先看 Tomcat 控制台日志再看浏览器 Network 面板最后再看数据库数据。绝大多数启动失败、404、500 都属于在这三个环节里能找到原因的问题。如果是把系统部署到服务器上让导师远程访问核心注意点有三条项目打包成 war 后放在 Tomcat 的 webapps 下应用名尽量设成 ROOT访问时不带项目名演示地址更短也更好记。服务器安全组和系统防火墙都要允许对应端口访问只开放 80、443、8080 等必要端口即可其他端口保持关闭。MySQL 如果也在同一台服务器上一定不要对外暴露 3306 端口避免数据库被扫到。本地连接数据库用 localhost应用服务器里配置数据库地址也用 localhost 或内网地址不要用公网 IP。为了保证页面加载速度房间图片尽量压缩后再上传一张图控制在 200KB 以内。如果不做图片上传功能也可以直接用服务器本地静态路径方式保存图片文件名前端 image 字段只存文件名这样部署负担更小。5.4 常见问题与排查速查表把毕业设计阶段遇到的高频问题整理成一张速查表适合贴在自己电脑旁现象常见原因解决思路Tomcat 启动后页面 404应用名不对 / 未部署成功用http://localhost:8080/应用名访问检查 webapps 下是否有 war数据库连接失败服务未启动 / 账号错误 / 时区问题先在本机用命令行登录检测 MySQL再检查 jdbc.properties中文乱码JSP、数据库、连接参数不一致JSP 用 UTF-8MySQL 建库用 utf8mb4连接 URL 加 characterEncodingutf8登录后访问后台仍跳回登录页拦截器放行路径不对检查登录 Controller 路径是否在拦截器排除范围内预约明明有时间却提示冲突时间字段类型不一致检查是 String 比较还是 LocalTime 比较SQL 参数是否传对页面样式丢失静态资源被拦截器拦截在 SpringMVC 配置中放行/static/**、/css/**、/js/**我见过一个最典型的案例学生在本地能跑唯独下拉选择“开始时间”之后结束时间列表不会动态更新。最后发现是 jQuery 库版本太低.change事件绑定在动态节点上失效。这种问题在答辩当场很难解释建议提前把所有页面都点一遍尤其是多个时间框联动、审核状态按钮点击这类交互尽量多做几轮完整走查。6. 配套文档与答辩准备的实用建议6.1 毕业设计文档该写哪些章节很多学生代码写完了却在文档上拖延到最后一周。共享办公室预约系统的论文框架其实可以非常标准大致包括绪论、相关技术介绍、系统分析、系统设计、系统实现、系统测试、总结与展望。这几章和代码模块的对应关系如下系统分析章节写用例图、可行性分析、功能需求和非功能需求。系统设计章节画总体架构图、功能结构图、数据库 E-R 图和表结构设计。系统实现章节按用户模块、房间模块、预约模块、后台模块逐个展示界面截图和核心代码。测试章节写测试环境、测试用例含正常流程和异常流程。建议在写系统设计时就把 E-R 图画准确重点标出tb_reservation与tb_user、tb_room的关联关系。答辩老师最常顺着 E-R 图问问题表里的字段含义自己必须能解释清楚。6.2 测试用例和演示脚本怎么组织系统测试部分不要只写“功能正常”四个字而是要有可复现的测试步骤。比如预约冲突的测试用例可以写成编号测试步骤预期结果实际结果TC-01预约房间 A 的 10:00-11:00提交提示预约成功状态为待审核与预期一致TC-02再预约同一房间 A 的 10:30-11:30提交系统提示该时段已被预约与预期一致TC-03预约房间 A 的 11:00-12:00提交系统允许预约首尾相接与预期一致测试用例不用特别多但要有几条覆盖核心流程的异常用例证明你考虑过程序可能出错的情况。6.3 答辩会被问到的高频问题答辩前可以做一次模拟提问准备以下问题的回答“Spring 中 Bean 的生命周期是什么”结合项目里的 Service 实现讲比背概念更容易让老师认可。“MyBatis 中的 #{} 和 ${} 有什么区别”项目里写动态 SQL 时用了 #{} 防注入这句话要能说出来。“为什么预约冲突不使用悲观锁”可以把“使用条件查询判断是否冲突再加事务确保并发安全”这个思路讲清楚。“数据库里为什么 room 表不用外键关联预约表”如果不设置物理外键SQL 查询更快由业务层保证关联完整性。“如果用户预约后一直不来系统怎么处理”用管理员端手动标记爽约作为回答体现业务兜底。“系统最多能支持多少人同时访问”可以回应为“在部署层面通过 Tomcat 线程数和数据库连接池配置可以支撑小型办公空间的使用量”不要编造一个压力测试报告。准备这些问题的过程其实就是把项目从“能跑”提升到“能讲”的过程。代码不是你亲手写的也没关系但你必须能把表关系、请求链路、核心判断逻辑讲通。能讲通本质上就代表着已经把技术点消化成了自己的东西。我自己的习惯是在交付代码后先用半天把所有表导成数据字典文档再按用户、房间、预约的顺序把核心页面截图整理到论文里。遇到远程协助时也不急着一遍遍复制粘贴报错而是让学生把 Tomcat 的完整日志文件发过来看到第一行异常定位后再让他操作确认。这样反复几次对方对项目的理解也在同步加深。如果做这个系统的时间还算充裕最后留两三天做一次“破坏性走查”故意用非法参数提交预约、把密码输错五次、直接访问一个不存在的房间 URL、把同一时段的预约并发点两次提交。能把这些边界情况处理干净系统就远不止是一个能演示的作业而是一个结构完整、边界清晰的产品级练习项目。