简介:一套基于SSM框架的医院预约挂号系统毕业设计源码包,面向Java方向毕业生、课程设计者及需要完整Web项目参考的开发者。系统围绕医院预约挂号业务,设计个人中心、用户管理、科室信息管理、医生管理、出诊时间管理、预约挂号、问题反馈与解答等完整模块,前后端代码齐全,附带数据库SQL脚本、设计说明文档(LW),导入Eclipse后即可运行。压缩包内共1386个文件,以Java类、JSP页面、JS脚本、CSS样式为主导,同时包含数据库初始化脚本、XML配置、HTML静态页面及工程配置文件,并涵盖图片、字体等静态资源,整体大小27.49MB,目录结构符合毕业设计常见分层。目前已有104人学习/下载,适合课程设计或毕设答辩参考,也可作为SSM单体架构项目的练手素材,帮助理解用户、科室、医生、预约之间的联动逻辑与分层开发思路。
1. 医院预约挂号系统是个什么毕业设计:它值不值得你花两周做完
很多 Java 毕设题库里,医院预约挂号系统属于那种“看着像个管理后台,实际做起来才算分”的选题。它的核心业务是患者通过科室找医生、看排班、抢号源,管理员维护医生数据和放号,医生查看自己的预约日程——听起来不复杂,但一旦涉及“同一个号源只能被一个人挂走”这种并发问题,就比普通增删改查有内容可挖。基于 SSM(Spring、Spring MVC、MyBatis)加 MySQL 来做,整套项目把 Java Web 的主线技术全部串了一遍,还附带说明文档和 LW(论文),对本科毕业设计的工作量和答辩可讲性都很友好。我按自己带学生和接手 SSM 项目时的习惯,把从环境搭建到防超卖的核心思路给你捋一遍。
2. 把 SSM 和 MySQL 的选型讲透:这套老技术栈为什么仍是毕业设计的安全牌
SSM 这个组合放到生产环境确实不算新,但在毕业设计场景里,它依然是安全牌。很多学生第一反应是想用 Spring Boot,结果被学校指定的“基于 SSM”的模板卡住;也有用 SSM 最后拿优的案例。原因很简单:SSM 手写配置多,Spring、Spring MVC、MyBatis 三者的边界必须自己理清,答辩老师问到的“启动流程”“请求生命周期”“SQL 映射”都能在代码里找到对应证据。另外,SSM 常用注解在 Java 面试题里出现频率也不低,做一遍等于提前复习了一遍框架基础。而医院预约挂号系统又是典型的“有实体关系、有状态流转、有并发边界”的业务,恰好能把 SSM 的每个部件都派上用场,所以这套选题能同时补足工作量、代码可读性和答辩可讲性。
2.1 SSM 的职责切分:Spring 容器、Spring MVC 与 MyBatis 各管哪一层
Spring 容器负责对象管理和事务,Service 里通过@Autowired拿 Mapper,自己不用new;Spring MVC 负责请求路由,浏览器来的请求先到 DispatcherServlet,再根据 URL 找到 Controller;MyBatis 负责持久化,把 Java 接口方法绑定到 XML 或注解里的 SQL。三者的协作关系就是一条调用链:请求从 DispatcherServlet 进入,经 HandlerMapping 找 Controller,Controller 调 Service,Service 调 Mapper,Mapper 执行 SQL,结果按原路返回。看 SSH 和 SSM 项目时,第一件事就是顺着这条线去拆。
拿“医生列表查询”这个最简单的功能举例,Controller 层的典型写法是:
@Controller @RequestMapping("/doctor") public class DoctorController { @Autowired private DoctorService doctorService; @GetMapping("/list") public String list(@RequestParam(value = "deptId", required = false) Integer deptId, Model model) { List<DoctorVO> doctors = doctorService.findByDeptId(deptId); model.addAttribute("doctors", doctors); return "doctor/list"; } }这段代码里的@Controller告诉 Spring MVC 这是一个控制器;@RequestMapping("/doctor")定义类级别的 URL 前缀;@Autowired让 Spring 把 DoctorService 注入进来;@GetMapping("/list")限定 GET 请求;@RequestParam接收前端传的 deptId,required = false表示这个参数可空。如果你拿到的源码里用的是@RequestMapping(value = "/list", method = RequestMethod.GET),效果一样,只是写法更老。
MyBatis 这一层要注意的是接口和 XML 的对应关系。接口方法名对应 XML 里的 statement id,参数通过@Param绑定。常见翻车点是接口返回实体类,但 XML 里查的是多表字段,映射不上,报IllegalArgumentException或字段查不到。所以项目里通常定义 VO 对象来承接连表查询结果,这个在第 4 章会具体展开。
2.2 数据库建模:预约挂号最关键的 5 张表
数据库设计是答辩时最容易被追问的部分。常见错误是一上来建十几张表,权限、科室、医生全都拆开。我的建议是先把核心定下来,用 5 张表表达这个业务:sys_user存所有登录用户的账号密码;department存科室;doctor_info存医生基本信息并关联科室;schedule存医生的出诊排班和号源数量;appointment_order存预约订单。
CREATE DATABASE hospital DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE TABLE sys_user ( id BIGINT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role TINYINT NOT NULL DEFAULT 1 COMMENT '1-患者 2-医生 3-管理员', real_name VARCHAR(50), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE department ( id BIGINT AUTO_INCREMENT PRIMARY KEY, dept_name VARCHAR(100) NOT NULL, intro TEXT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE doctor_info ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, dept_id BIGINT NOT NULL, title VARCHAR(30), intro TEXT ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE schedule ( id BIGINT AUTO_INCREMENT PRIMARY KEY, doctor_id BIGINT NOT NULL, work_date DATE NOT NULL, start_time TIME NOT NULL, end_time TIME NOT NULL, total_num INT NOT NULL DEFAULT 20, remain_num INT NOT NULL DEFAULT 20, status TINYINT DEFAULT 1, version INT DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE appointment_order ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, schedule_id BIGINT NOT NULL, appoint_date DATE NOT NULL, status TINYINT DEFAULT 0 COMMENT '0-已预约 1-已取消 2-已完成', remark VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这段 SQL 有四个设计要点。一是sys_user用role字段区分患者、医生、管理员,而不是建三张表,登录时只查一次即可;二是doctor_info通过user_id关联sys_user,医生既是用户又是医生;三是schedule把total_num和remain_num分开,remain_num才是真正要扣减的字段;四是appointment_order用status表达预约状态流转,取消订单不是删记录,而是改成已取消。最后一个version字段是给乐观锁预留的,第 4 章我会用数据库行锁演示防超卖,如果你想扩展乐观锁,这个字段可以直接用。参数上,id 统一用BIGINT自增主键,时间字段用DATETIME,如果涉及挂号费,金额用DECIMAL(10,2)而不是DOUBLE,答辩被追问精度问题时能答得上来。
2.3 MySQL 版本和字符集:5.7 与 8.0 的取舍、utf8mb4 和事务
我会建议本地直接用 MySQL 5.7。原因很实际:很多老 SSM 毕业设计的 pom 里用的是mysql-connector-java 5.1.x,MySQL 8.0 默认的认证插件是caching_sha2_password,老驱动握手失败,报错信息还很绕。MySQL 5.7 默认mysql_native_password,兼容性最好。
| 对比项 | MySQL 5.7.44 | MySQL 8.0 |
|---|---|---|
| 默认认证插件 | mysql_native_password | caching_sha2_password |
| 老驱动兼容性 | 好 | 需要升级驱动 |
| 毕设踩坑概率 | 低 | 中等 |
| 事务与行锁 | InnoDB 可用 | InnoDB 可用 |
如果你看的是 mysql 5.7.44 安装过程详细教程,会注意到安装时可以顺手把端口保持 3306、字符集选 utf8mb4。数据库层的字符集直接决定了后面中文乱码的概率,这比任何代码层面的编码配置都重要。连接层我一般这么配:
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/hospital?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4 jdbc.username=root jdbc.password=123456useSSL=false是为了避免本地开发时触发 SSL 握手版本不匹配;serverTimezone=Asia/Shanghai解决时区报错;characterEncoding=utf8mb4保证写入数据库的中文不乱码。如果老师机器上已经装了 MySQL 8.0,不需要卸载,把驱动升级到 8.0.x,并在 URL 里加allowPublicKeyRetrieval=true即可。事务方面,InnoDB 默认隔离级别是REPEATABLE READ,对毕设项目完全够用,不用去调;真正要控制的是事务边界,必须放在 Service 层方法上,而不是 Controller 里。
3. 在本地把系统跑起来:从 JDK 到 Tomcat 的最小部署清单
拿到医院预约挂号系统源码(ssm+mysql+说明文档+LW).zip 之后,我建议先别急着打开 IDEA,而是把压缩包里的文件列表看一遍:有没有.sql文件,有没有说明文档,pom.xml在哪个目录,源码文件是不是 UTF-8 编码。很多学生压缩包一解压就双击导入,结果项目结构不对,导入进来全是乱码,或者 Maven 根本不识别。先花两分钟看清结构,能省后面一天的时间。
3.1 环境选型:JDK 8 + Tomcat 8.5 + Maven 3.6 的匹配
这套 SSM 项目最稳的环境组合是 JDK 8、Maven 3.6.3、Tomcat 8.5 或 9。Tomcat 9 也能跑,但没必要;Tomcat 10 以后包名从javax.*换成了jakarta.*,很多老 SSM 项目直接NoClassDefFoundError。这不是玄学,是命名空间变更导致的,遇到时不用检查半天代码,先确认 Tomcat 版本就行。先确认本机环境:
java -version mvn -v echo $CATALINA_HOME第一条命令确认 JDK 版本,如果是 1.8 就继续;第二条看 Maven 版本和它运行时用的 JDK;第三条看 Tomcat 路径,Windows 终端里是echo %CATALINA_HOME%。注意环境变量配好之后要新开一个终端窗口,否则读不到新值。如果你机器上已经装了高版本 JDK,不建议卸载,在 IDEA 的 Project Structure 里给项目单独指定 JDK 8 就行。
3.2 导入数据库和修改配置:三个必改位置
数据库初始化是启动前的关键一步。项目压缩包里一般会带.sql文件,常见文件名是hospital.sql或db_hospital.sql:
mysql -u root -p < hospital.sql这条命令默认把文件里的 SQL 按顺序执行。前提是 sql 文件里已经包含CREATE DATABASE和USE hospital;如果没包含,你需要先在命令行执行CREATE DATABASE hospital DEFAULT CHARACTER SET utf8mb4;,再USE hospital;,然后source hospital.sql;。导入完成后用SHOW TABLES;数一下表数量,再对照说明文档里的数据字典,看少了哪张表。以我的经验,约有三成项目启动失败是数据库没导成功,并不是代码有问题。
数据库导入后,最常改的配置文件是数据源,名字可能是jdbc.properties、db.properties,也可能直接写在 Spring 的applicationContext.xml里。
jdbc.url=jdbc:mysql://localhost:3306/hospital?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4 jdbc.username=root jdbc.password=123456我一般会直接全局搜3306或jdbc:,找到数据源配置后改三个位置:URL 里的数据库名、用户名、密码。如果密码包含@、#这类特殊字符,先回命令行确认mysql -u root -p能连上,再改配置文件,否则容易把排查方向带偏。
3.3 用 IDEA 运行:加载 Maven 项目、配置 Tomcat 与第一次访问登录页
IDEA 打开项目时,要选择pom.xml而不是选整个文件夹硬导。首次导入会让你选择是否信任项目,选 Trust Project,然后等 Maven 下载依赖。如果右下角没有 Maven 面板,右键pom.xml-> Add as Maven Project。依赖下载完,打开左侧 External Libraries 能看到 spring-webmvc、mybatis、mysql-connector 这些常见包,说明依赖结构基本正常。
接着配置 Tomcat:Run -> Edit Configurations -> 左上角加号 -> Tomcat Server -> Local。在 Deployment 页签加 Artifact,选 war exploded 而不是 war,因为 war exploded 支持 JSP 热更新,改页面不用反复重启。Application context 填/hospital,如果源码里页面跳转写死的是/hospital/login,这里就必须保持一致,否则登录页跳不过去。
项目启动后,如果 IDEA 控制台没报错但页面打不开,去看 Tomcat 的本地日志:
tail -f /usr/local/tomcat/logs/catalina.outWindows 下在 Tomcat 安装目录的logs文件夹里找localhost.yyyy-MM-dd.log。catalina.out保存了标准输出和系统日志,很多NoClassDefFoundError、端口占用、数据库连接失败都只在这里出现。浏览器访问http://localhost:8080/hospital/login,能看到登录页就说明环境通了。默认管理员账号去说明文档或 LW 里找,常见是admin/admin123;如果数据库脚本里已经灌了测试数据,也直接去sys_user表里查一条真实可用的。到这一步,你已经完成了最小部署,后面再改代码就都在这个基础上迭代。
4. 核心业务实现:预约挂号流程中的 SSM 常用注解和事务边界
环境跑通之后,就进入核心环节。医院预约挂号系统能不能在答辩现场“讲出东西”,关键在两处:排班查询是不是用 MyBatis 动态 SQL 做的;预约扣号是不是有事务和并发控制。这两点讲明白,比堆十个 CRUD 接口有用。下面我把三个最值得讲的代码点展开:排班查询、事务扣号、登录拦截。
4.1 查询医生排班:MyBatis 动态 SQL 与多表关联
排班查询是预约的入口,患者先选科室,再选医生,再看日期。不同条件下请求的 SQL 不同,所以用 MyBatis 的<if>和<where>动态拼接条件。下面是常见的 Mapper XML 写法:
<select id="findScheduleByDept" resultType="com.hospital.vo.ScheduleVO"> SELECT s.id AS id, d.real_name AS doctorName, di.title AS doctorTitle, s.work_date AS workDate, s.start_time AS startTime, s.end_time AS endTime, s.total_num AS totalNum, s.remain_num AS remainNum FROM schedule s INNER JOIN doctor_info di ON s.doctor_id = di.id INNER JOIN sys_user d ON di.user_id = d.id <where> <if test="deptId != null and deptId != ''"> AND di.dept_id = #{deptId} </if> <if test="workDate != null"> AND s.work_date = #{workDate} </if> </where> ORDER BY s.work_date, s.start_time </select>resultType直接指向ScheduleVO,因为查询结果来自三张表,和一个实体类对不上,用 VO 接收连表查询结果是项目里最常见的处理方式。<where>的作用是:如果没有条件,SQL 里不拼 WHERE;如果有条件,它自动把第一个条件的多余 AND 去掉。#{deptId}是预编译占位符,不是字符串拼接,能预防 SQL 注入,这也是答辩时的加分点。参数说明:deptId为空时查所有科室,workDate为空时查所有日期。
Mapper 接口对应写法:
public interface ScheduleMapper { List<ScheduleVO> findScheduleByDept(@Param("deptId") Integer deptId, @Param("workDate") String workDate); }@Param的作用是给 XML 里的deptId和workDate参数命名。如果不写,MyBatis 默认按param1、param2引用,代码可读性很差。如果你发现 XML 里明明写了deptId却报There is no getter for property named 'deptId',基本就是接口方法少了@Param。
4.2 预约扣号与订单创建:@Transactional 和 MySQL 行锁
防超卖是预约系统的核心难点。这里不涉及分布式锁,毕业设计讲清楚数据库行锁就足够。前面schedule表设计了remain_num,核心思路是一条 UPDATE 语句把“检查剩余号”和“扣减号源”合成一步。Service 层写法如下:
@Service public class AppointmentService { @Autowired private ScheduleMapper scheduleMapper; @Autowired private AppointmentOrderMapper orderMapper; @Transactional(rollbackFor = Exception.class) public void book(int userId, Long scheduleId) { int rows = scheduleMapper.decreaseRemainNum(scheduleId); if (rows == 0) { throw new BusinessException("号源已满或已停诊"); } AppointmentOrder order = new AppointmentOrder(); order.setUserId(userId); order.setScheduleId(scheduleId); order.setStatus(0); orderMapper.insert(order); } }对应的 SQL 在 Mapper XML 里是这样:
<update id="decreaseRemainNum"> UPDATE schedule SET remain_num = remain_num - 1 WHERE id = #{id} AND remain_num > 0 </update>这个方法能防超卖的根因是:UPDATE语句在 InnoDB 下会对命中的行加排他锁,并发请求执行到这条 UPDATE 时会排队;remain_num > 0这个条件保证只剩 0 个号时不会再扣减,而rows == 0就是“没抢到号”的信号,直接抛异常回滚。要注意@Transactional(rollbackFor = Exception.class)里的rollbackFor不能省,因为 Spring 默认只在遇到RuntimeException时才回滚,如果项目里的BusinessException是继承Exception的,不加这个参数,就会出现扣号成功但订单插入失败,最终数据不一致。
还需要强调一点:不要在 UPDATE 之前先SELECT判断剩余号,那等于把原子操作人为拆成了两步,并发场景下两个线程都查到剩余 1 个号,然后各自 UPDATE,超卖就又回来了。这个点也是面试里经常被追问的“事务边界”问题。
4.3 登录与拦截:Spring MVC 注解与拦截器配置
医院预约挂号系统有患者、医生、管理员三种角色,登录后要分别进入不同首页,所以登录逻辑不能只查一次账号。Controller 层通常这样写:
@Controller public class LoginController { @Autowired private UserService userService; @PostMapping("/login") @ResponseBody public Result login(@RequestParam String username, @RequestParam String password, HttpSession session) { User user = userService.login(username, password); if (user == null) { return Result.error("用户名或密码错误"); } session.setAttribute("loginUser", user); return Result.success(user); } }@PostMapping("/login")限定 POST 请求;@ResponseBody把返回值直接序列化成 JSON,前端用 Ajax 拿code判断成功还是失败;Result是统一返回对象,三个字段code、msg、data。答辩时老师会问“如果不做统一返回行不行”,其实也行,但每个接口返回格式都不一样,前端维护成本高,算不上好设计。
有了登录之后,还要保证未登录用户无法访问预约接口。Spring MVC 拦截器比较直接:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/login"/> <mvc:exclude-mapping path="/register"/> <mvc:exclude-mapping path="/static/**"/> <bean class="com.hospital.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors>/**匹配所有请求;exclude-mapping放行登录、注册和静态资源,避免用户还没登录时页面 CSS/JS 全被拦截。LoginInterceptor 内部逻辑不复杂,就是从 session 里取登录用户,取不到就重定向到登录页。这里有一个隐藏细节:静态资源要放在webapp/static下,同时 Spring MVC XML 里配好<mvc:resources mapping="/static/**" location="/static/"/>,否则即使拦截器放行了,DispatcherServlet 也找不到这些文件。
5. 避坑排查:SSM 项目最常见的 5 个翻车点与解决套路
下面这 5 个坑是我帮学生调毕设项目时遇到频率最高的,每一个都按“现象 -> 原因 -> 解决”写。你自己排查的时候也可以照这个顺序记:先收集现象,再顺着调用链找根因,最后小步修改,改一次重启一次。
5.1 MySQL SSL 连接错误:报 SSL 错误或握手失败
现象:项目启动时日志出现Communications link failure,往下看是SSL connection error: protocol version mismatch;有时还会看到一个乱码时区信息The server time zone value 'Öйú' is unrecognized。
原因:MySQL 5.7 和 8.0 默认启用 SSL 连接,老驱动在握手阶段没谈拢加密协议;时区报错是驱动和数据库时区不一致。很多人从网上复制 JDBC URL 时漏掉了serverTimezone。
解决:本地开发在 JDBC URL 加useSSL=false&serverTimezone=Asia/Shanghai;如果是 MySQL 8.0,把驱动的 pom 依赖换成mysql-connector-java 8.0.33,再追加allowPublicKeyRetrieval=true。改完配置后重启 Tomcat,不要只重新编译。如果日志里继续报Unknown system variable 'query_cache_size',就不是 SSL 问题,而是驱动版本和 MySQL 版本错配,优先检查 pom。
5.2 中文乱码:页面、连接、数据库三层都要查
现象:注册页面填“张三”,保存后数据库里是乱码,页面回显变成???。
原因:数据库连接字符集、MySQL 建库字符集、Tomcat URI 编码至少有一层没设置。最常见的是只改 JSP 页面的charset,没改数据库连接。
解决:先确认数据库建库字符集是不是 utf8mb4:在 MySQL 命令行执行SHOW CREATE DATABASE hospital;,不是 utf8mb4 就要重新导入或转换。然后检查 JDBC URL 里的characterEncoding=utf8mb4。最后在 web.xml 里配置CharacterEncodingFilter,forceEncoding设为true。注意连接池配置里如果有useUnicode=true这个旧参数,要保留,不能删。改完这三个地方后,清浏览器缓存再看页面。
5.3 Maven 依赖冲突:Spring 版本被覆盖,启动报 AOP 错误
现象:启动 Tomcat 时看到BeanCreationException,Caused by 是ClassNotFoundException: org.springframework.aop...或NoClassDefFoundError。
原因:pom.xml 里 spring-webmvc 写了 5.2.22,spring-jdbc 又写了 4.3.30,Maven 依赖调解后实际生效的 spring-core 和部分模块版本不一致。这是 SSM 项目最常见的依赖问题。
解决:在 pom.xml 的<properties>里统一定义<spring.version>5.2.22.RELEASE</spring.version>,所有 Spring 依赖都不写版本号,用${spring.version}引用。改完执行mvn clean,然后在 IDEA 里右键 pom -> Maven -> Reload Project。如果还想确认冲突,执行mvn dependency:tree | grep spring,Windows 下用findstr代替 grep。看到同一组 artifact 出现多个版本号,就是冲突现场。
5.4 号源超卖:并发测试时剩余号变负数
现象:用 Jmeter 并发 50 个预约请求同一个 scheduleId,最终订单表插入了 50 条记录,schedule 表remain_num变成负数。
原因:Service 方法没加@Transactional,或者加了事务但扣减逻辑是“先 SELECT 再 UPDATE”。并发场景下两个会话查到同一个剩余值,各自都认为还有号可挂。
解决:把扣号和插入订单放进同一个 Service 方法,加上@Transactional(rollbackFor = Exception.class);UPDATE 写成SET remain_num = remain_num - 1 WHERE id = #{id} AND remain_num > 0,然后判断返回值。验证方式很简单:用一个并发测试工具同时发 20 个请求,看最终订单数和remain_num是否满足“扣减数 + 剩余数 = 总数”。如果你在代码里看到SELECT remain_num FROM schedule WHERE id=?,再在下一行执行 UPDATE,基本就是超卖隐患。
5.5 静态资源 404:登录页没有 CSS,JS 全部报错
现象:项目能启动,登录页也能打开,但页面是裸的,控制台里.css和.js请求全部 404,接口请求反而是 200。
原因:Spring MVC 的 DispatcherServlet 把静态资源请求也当成 Controller 处理了。最常见是拦截器配置了/**但没排除静态资源,或者 Spring MVC XML 里没有配静态资源映射。
解决:把静态资源放在webapp/static下,在 Spring MVC XML 里加:
<mvc:resources mapping="/static/**" location="/static/"/> <mvc:default-servlet-handler/>然后拦截器排除路径补上/static/**。如果项目用的是 Servlet 3.0 注解方式,也要保证 WebMvcConfigurer 里 overrideaddResourceHandlers,思路一模一样。这个配置不补上,答辩演示时页面会一直裸奔,很影响第一印象。
6. 答辩和扩展:让项目从“能跑”到“能被问住”的三个做法
跑通代码只是第一步。我见过不少项目代码是能跑的,但答辩时被一句“你这个预约挂号流程,数据是怎么流转的”问住。所以在交给老师之前,自己先过三关。
第一关,把数据流转讲清楚。从用户点击“预约”按钮开始,前端请求到哪个 Controller,Service 里做了什么事务,Mapper 执行了什么 SQL,最终哪些表被更新或插入。表述要控制在一分钟内:前端传 scheduleId 和 userId,Controller 调 book 方法,事务里先 UPDATE schedule 扣号,再 INSERT appointment_order,最后统一提交。答辩时能画出这条链路,基本就证明项目是你做的。
第二关,做一次代码走读。挑一个查询接口和一个写接口,分别说清 MyBatis 的 SQL 映射和 Spring 的事务边界。可以在 IDEA 里打断点 debug 跑一遍,记下每次调用的类名和方法名。老师问“这里为什么报错”时,你能从 Controller 一路说到 Mapper,比背任何理论都管用。
第三关,准备扩展点。老师几乎必问“还能怎么优化”,两个标准答案是:把号源库存放到 Redis 里做缓存,降低数据库压力;用 Spring Task 的@Scheduled定时扫描超时未支付的预约订单,自动置为已取消并回补号源。你不需要真的实现完整代码,但要把思路说清楚,这已经比大多数只做增删改查的学生高一个段位。
我当年做毕设时就吃过“表结构解释不清”的亏,代码是自己写的,但答辩前没把外键关系顺一遍,被问到“为什么 appointment_order 表里不直接存医生 ID”时愣了几秒。后来带学生,我都会让他们先在纸上把表关联画一遍,再上台。你把这个习惯养成,毕业设计和以后工作都能少踩很多坑。希望帮到你。
本文还有配套的精品资源,点击获取