☰
Java毕设医院预约挂号系统:JSP+Servlet+MySQL全流程实现与排错
2026/10/9 3:47:40 网站建设 项目流程

简介:这是一套医院预约挂号系统基于Java语言实现的毕业设计完整源码资源,包含前端页面、后台逻辑、数据库脚本以及毕业设计文档说明,主要面向计算机、通信、人工智能、自动化等相关专业的学生和从业者,适用于期末课程设计、课程大作业或毕业设计环节。项目围绕预约挂号的真实业务场景,采用Servlet/JSP与MySQL等技术构建,实现了医生排班、患者注册、科室管理、挂号记录等典型功能模块,代码经过反复调试与测试,能够稳定运行,具有较好的学习价值与二次开发空间。资源包内共189个文件,包括55个Java源文件、39个JSP页面、17个CSS样式、9个XML配置、3个SQL数据库脚本以及多张界面设计图片素材,压缩包大小仅1.81MB,整体目录结构清晰,便于下载后按模块查阅和复用。目前已有126人学习浏览。配套数据库脚本和文档说明能够帮助读者快速理清系统架构与数据表关系,新手可借此上手Java Web项目开发,进阶者也能在此基础上修改扩展,实现不同预约流程或管理功能,为毕业设计答辩积累完整可演示的项目经验。

1. 医院预约挂号系统:一份能直接跑通的 Java 毕设长什么样

医院预约挂号系统是 Java 毕业设计里一个特别“稳”的选题:业务流程人人熟悉,功能边界清晰,演示效果直观。这套基于 JSP、Servlet、MySQL 的源码我完整拆过一遍,最大的感受是它不是一个只写了增删改查的壳子,而是把“科室—医生—排班—预约—取消”整条挂号链路都跑通了,前端用 Bootstrap 和 laydate 撑起界面和日期选择,后端用经典三层结构把业务讲得清楚。作者标注答辩评审 98 分,分数参考就好,真正值钱的是文档说明里需求分析、ER 图和核心流程都写全了,这几页恰恰是答辩时最扛问的部分。如果你正在找医院预约挂号系统的 Java 源码做毕业设计、课程设计,或者想拿一份带数据库脚本和答辩文档的项目做二次开发,这份资源值得仔细过一遍。下面按“结构—业务—部署—排错—扩展”的顺序拆。

2. 项目底细:JSP + Servlet + MySQL 的分层架构与五张核心表

2.1 技术栈为什么这样搭:好讲、好演示、好答辩

一套 Java Web 毕设最怕的不是不会写,而是答辩时说不清“为什么这样选”。这套项目的主体是 JSP + Servlet + MySQL 的经典三层结构:实体类放在 bean/entity 包,数据库操作集中在 dao 包,业务判断放在 service 包,请求入口是 servlet,页面渲染靠 JSP。前端的 bootstrap.min.css、laydate.css、admin.css、login.css、basic.css 几个静态文件体现得很清楚——Bootstrap 负责后台管理界面和登录页的布局,laydate 负责预约日期选择,admin.css 和 basic.css 是后台模板的通用样式。

为什么这个组合适合毕设而不是 Spring Boot?我说点实际场面:Spring Boot 项目一到答辩,老师顺着自动装配、依赖注入往下问,回答不深容易露馅;JSP + Servlet 的每一行代码都是自己写的,从 request 取值、调 service、执行 SQL 到转发页面,这条链路当场就能在黑板上画出来。技术不新,但胜在可讲。如果拿到手的版本是 SSM 或 Spring Boot 变体也没关系,文档说明里的需求分析、ER 图和模块划分是通用的,业务表结构完全一致,换框架只是把这些 Servlet 换成 Controller 而已。

2.2 功能模块拆解:患者端四条主流程与管理端三个管理面

从整体功能看,系统按用户角色天然分成两个入口。患者端的核心流程有四条:注册与登录、科室与医生浏览、排班查询与预约、我的预约与取消。注册时校验用户名是否重复,密码做摘要处理后入库,登录成功把用户信息写入 session;预约功能从科室列表进入医生列表,再通过 laydate 选日期查看当天各时段余号,选中时段后提交预约。管理端的核心功能有三个管理面:科室管理、医生管理、排班管理,外加预约记录查询,专门用于核对每个科室的出诊情况。

我把模块和对应页面、数据表整理成一张对照表,写文档和画系统结构图时可以直接借用:

角色功能模块对应页面/入口关键数据表
患者注册/登录login.jsp、register.jspuser
患者科室/医生浏览department.jsp、doctor.jspdepartment、doctor
患者排班查询与预约schedule.jsp、appointment.jspschedule、appointment
患者我的预约/取消myAppointment.jspappointment、schedule
管理员科室管理admin/deptList.jspdepartment
管理员医生管理admin/doctorList.jspdoctor
管理员排班管理admin/scheduleList.jspschedule
管理员预约记录查询admin/appointmentList.jspappointment

功能看起来多,但数据最终都落到五张表上。表关系理清了,前端页面只是不同查询条件的组合,这也是这套项目适合拿来学习分层设计的原因。

2.3 数据库设计:五张表的字段与关联关系

预约挂号系统的核心是“排班”。一个医生可能一周出诊三天,每天分上午、下午两个时段,每个时段放号量还不一样,所以排班必须单独建表,不能把出诊日期塞进医生表。很多初版设计翻车就翻在这里:医生表和排班混在一张表里,后面想支持一周排班,表结构完全撑不住。

五张表的核心关系是这样:department 1—N doctor,doctor 1—N schedule,schedule 1—N appointment,appointment N—1 user。下面给出核心建表脚本,以 MySQL 5.7 为例:

CREATE TABLE department ( id INT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(50) NOT NULL, intro VARCHAR(255), status TINYINT DEFAULT 1 ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE doctor ( id INT PRIMARY KEY AUTO_INCREMENT, dept_id INT NOT NULL, doctor_name VARCHAR(30) NOT NULL, title VARCHAR(30), intro VARCHAR(255), status TINYINT DEFAULT 1, CONSTRAINT fk_doctor_dept FOREIGN KEY (dept_id) REFERENCES department(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE schedule ( id INT PRIMARY KEY AUTO_INCREMENT, doctor_id INT NOT NULL, work_date DATE NOT NULL, time_slot VARCHAR(20) NOT NULL, total_num INT NOT NULL, reserved_num INT NOT NULL DEFAULT 0, CONSTRAINT fk_schedule_doctor FOREIGN KEY (doctor_id) REFERENCES doctor(id), UNIQUE KEY uk_doctor_date_slot (doctor_id, work_date, time_slot) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

逻辑说明:department.status 为 1 表示启用,0 表示停用,页面展示时用这个字段过滤,不需要物理删除数据。schedule 表用 doctor_id、work_date、time_slot 三个字段做联合唯一,保证同一个医生同一天同一时段只有一条排班记录,这是防止重复放号的第一道约束。reserved_num 默认 0,表示已被预约人数,余号 = total_num - reserved_num,不用额外存储。外键在毕设里建议保留,ER 图和数据关系更直观,答辩画图有东西可讲;生产环境可以去掉外键,但毕设保留利大于弊。

user 和 appointment 两张表在第三章的业务代码里直接体现字段,数据库脚本里都有完整建表语句,这里先不重复贴。

3. 预约挂号的核心业务:选号、占号与取消回退的实现细节

3.1 查询链路:科室、医生、排班三条 SQL 如何串起预约页

患者点击“去预约”时,页面数据按这个顺序加载:先选科室,再显示该科室医生,然后进入医生排班页选择日期,最后看到当天每个时段还剩几个号。后端对应的三条核心 SQL 如下:

-- 1. 按科室查医生 SELECT id, doctor_name, title, intro FROM doctor WHERE dept_id = ? AND status = 1; -- 2. 按医生和日期查当天排班及余号 SELECT id, time_slot, total_num, reserved_num, (total_num - reserved_num) AS remain_num FROM schedule WHERE doctor_id = ? AND work_date = ?; -- 3. 提交预约前再次校验该排班仍有余号 SELECT id, total_num, reserved_num FROM schedule WHERE id = ?;

逻辑说明:第一条 SQL 用 dept_id 关联 department 和 doctor,只查启用状态的医生;第二条是核心,remain_num 由 total_num 减去 reserved_num 动态算出来,前端用这个值决定按钮显示“可预约”还是“已约满”;第三条在提交预约时再查一次排班,防止用户在页面停留过久,看到的余号是半小时前的过期数据。

这里有个细节值得留意:第二条 SQL 的日期参数来自 laydate 组件的回传值。laydate 的 format 参数默认是YYYY-MM-dd,注意这里的 YYYY 是大写;后端 SimpleDateFormat 解析时要对应写小写的 yyyy-MM-dd。如果前端把 format 配成YYYY-M-d,后端没跟着改,解析会直接抛 ParseException,这就是典型的日期格式前后端没对齐。

3.2 号源防超卖:一条原子更新替代“先查再改”

很多初版项目把预约提交写成两步:先 select 查余号,if 有余号再 update reserved_num。这个写法在演示环境没问题,但一旦两个人同时对着同一时段点预约,两个请求可能读到相同的 reserved_num,都认为还有号,最后 reserved_num 只加了 1,却生成两条预约记录——这就是超卖。

常见做法是把“判断余号”和“占用余号”合并成一条原子更新:

UPDATE schedule SET reserved_num = reserved_num + 1 WHERE id = ? AND reserved_num < total_num;

逻辑说明:这条 SQL 在数据库层面完成两件事——只有当前 reserved_num 小于 total_num 时,reserved_num 才加 1。MySQL 对单条 UPDATE 的行锁保证同一个时刻只有一个请求能成功,另一个请求的影响行数为 0,由此判定号源已满。因为影响行数是判断成功与否的唯一依据,完全不需要先 select。

对应地,Java 侧的核心逻辑是这样:

// 假设已经拿到排班 scheduleId、当前用户 userId 和就诊人信息 String updateSql = "UPDATE schedule SET reserved_num = reserved_num + 1 " + "WHERE id = ? AND reserved_num < total_num"; String insertSql = "INSERT INTO appointment(user_id, schedule_id, patient_name, phone, create_time) " + "VALUES(?, ?, ?, ?, NOW())"; Connection conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交,保证两步在同一个事务里 try { PreparedStatement ps1 = conn.prepareStatement(updateSql); ps1.setInt(1, scheduleId); int rows = ps1.executeUpdate(); if (rows == 0) { conn.rollback(); throw new ServiceException("该时段已被约满,请选择其他时段"); } PreparedStatement ps2 = conn.prepareStatement(insertSql); ps2.setInt(1, userId); ps2.setInt(2, scheduleId); ps2.setString(3, patientName); ps2.setString(4, phone); ps2.executeUpdate(); conn.commit(); // 两条都成功再提交 } catch (SQLException e) { conn.rollback(); // 任一条失败,号源改动和预约记录一起回滚 throw new ServiceException("预约失败,请稍后重试"); } finally { DBUtil.close(conn, null, null); }

参数说明:conn.setAutoCommit(false) 是关键,占号 UPDATE 和预约 INSERT 必须同生共死,否则会出现号占上了但预约记录没写进去的情况。rows == 0 时直接 rollback 再抛“约满”异常,前端捕获后刷新排班页。DBUtil.close 里的 PreparedStatement 和 ResultSet 也要显式关闭,这里为了代码简洁省略了,实际项目里建议直接封装一个 close(conn, ps, rs) 工具方法。

3.3 取消预约的状态机:回退号源前先确认状态没被改过

预约记录不建议物理删除,用状态字段控制生命周期更稳妥。常见设计是 appointment.status:0 表示已预约,1 表示已完成,2 表示已取消,3 表示已停诊。患者取消预约时,核心动作是更新预约状态和回退号源两步:

-- 第一步:把预约置为已取消(仅当当前状态是已预约时才能取消) UPDATE appointment SET status = 2, cancel_time = NOW() WHERE id = ? AND status = 0; -- 第二步:回退号源(仅当第一步影响 1 行时才执行) UPDATE schedule SET reserved_num = reserved_num - 1 WHERE id = ? AND reserved_num > 0;

逻辑说明:第一步带 AND status = 0,防止同一张单子被取消两次。如果不带这个条件,第二次取消时预约状态已经变成 2,仍然会执行回退,号源被多还一次,reserved_num 出现负数。第二步带 AND reserved_num > 0 是最后一道保险,宁可不回退也不能把余号减成负数。这一步在不少毕设里被省略,但答辩时如果老师追问“重复点击取消怎么办”,能答出这两行条件的人明显更占优势。

3.4 排班数据初始化:让演示期有号可约

拿到源码第一次启动时,最尴尬的一幕是排班表是空的,页面选完日期一个号都没有。SQL 脚本里一般会带几条初始数据,但日期往往是固定的某一天,过时就过期了。正确做法是先确认脚本里 schedule 表的种子数据日期,再插入未来一周的排班:

INSERT INTO schedule(doctor_id, work_date, time_slot, total_num, reserved_num) VALUES (1, '2026-06-01', '上午', 20, 0), (1, '2026-06-01', '下午', 15, 0), (2, '2026-06-02', '上午', 25, 0);

逻辑说明:一次插入多条,让演示页面上有足够内容可点。total_num 按实际出诊时长设置,比如上午出诊 3 小时、平均 10 分钟一个号,放号 18 个左右;数值不宜过大,否则点预约时永远约得满,也就没有“已约满”的演示场景了。

4. 从资源到运行:导入数据库、改连接配置、在 Tomcat 下跑通

4.1 拿到资源先盘一遍目录,别急着导 IDEA

下载资源后,我建议先别看代码,先按文件夹过一遍。这套资源的标准构成是:源码工程、数据库脚本、文档说明三块。源码里按包名能看出分层,web 目录下能找到 bootstrap.min.css、laydate.css、admin.css 这些静态资源;数据库脚本一般是独立的 hospital.sql;文档说明里通常是需求分析、数据库设计、核心代码说明和答辩用材料。

目录/文件作用下一步动作
源码工程目录Java 源码与 JSP 页面用 IDEA/Eclipse 导入
hospital.sql建库建表与种子数据导入 MySQL
文档说明毕设论文与答辩材料按模板替换个人信息

先打开 hospital.sql 检查两件事:开头是否有 CREATE DATABASE 语句,字符集是不是 utf8。有 CREATE DATABASE 就不用手动建库,直接 source 即可;没有的话需要先建库再导。作者说代码都调试过能跑,但本机环境不同,我拿到手仍然会按完整步骤过一遍,这一步省不得。

4.2 数据库导入:一条 source 命令解决建库建表

以 Windows 环境为例,打开命令行进入 mysql:

mysql -uroot -p

登录后执行:

CREATE DATABASE hospital DEFAULT CHARACTER SET utf8; USE hospital; SOURCE D:/hospital.sql;

逻辑说明:SOURCE 后面必须写绝对路径,路径中不要带中文。如果脚本里已经包含 CREATE DATABASE 和 USE,前两步可省略。导入完成后用 SHOW TABLES 确认五张核心表存在,再 SELECT * FROM user 看看初始账号有没有进去。

参数说明:DEFAULT CHARACTER SET utf8 一定要带,否则后续页面写入的中文在数据库里全是乱码。MySQL 8.x 与 5.7 在这条语句上没有差异,可以直接复用。

4.3 数据库连接配置:重点改 DBUtil 里的三项

JavaWeb 项目里数据库连接通常集中在 util/DBUtil.java 或 jdbc.properties 里。找到配置后,核心是下面三项:

private static final String DRIVER = "com.mysql.jdbc.Driver"; private static final String URL = "jdbc:mysql://localhost:3306/hospital?useUnicode=true&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } }

逻辑说明:URL 里的 hospital 是库名,USER 和 PASSWORD 改成自己本机 MySQL 的账号密码。useUnicode=true 和 characterEncoding=utf8 必须保留,这两个参数决定 Java 程序往数据库写中文时用 UTF-8 编码,去掉后中文会变成问号。

这里说一个最常见的兼容性问题:如果本机装的是 MySQL 8.x,而项目里的驱动 jar 是 5.x 时代的 mysql-connector-java-5.x.jar,启动后大概率报驱动过时甚至直接连不上。处理办法是换成 mysql-connector-java-8.0.x.jar,并把 DRIVER 改成 com.mysql.cj.jdbc.Driver,URL 加上三个参数:

private static final String DRIVER = "com.mysql.cj.jdbc.Driver"; private static final String URL = "jdbc:mysql://localhost:3306/hospital" + "?useUnicode=true&characterEncoding=utf8" + "&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true";

参数说明:serverTimezone=Asia/Shanghai 解决 MySQL 8.x 的时区校验报错,useSSL=false 关闭 SSL 握手告警,allowPublicKeyRetrieval=true 解决 caching_sha2_password 认证插件下的公钥获取问题。这三个参数是 MySQL 8 连接最常见的组合,不加任何一个都可能看到连接失败。

提示:改完数据库配置后一定要重启 Tomcat,只改文件不重启是无效的。

4.4 Tomcat 部署三步走:导入、配置、启动验证

数据库就绪后,用 IDEA 打开源码工程。如果项目没有自带 .iml/.idea 配置,用 New → Project from Existing Sources 导入,保持默认的工程识别方式。然后做三件事:确认 Project Structure 里 Language Level 是 1.8;把 lib 下的 mysql 驱动 jar 加到 Libraries;添加 Tomcat Server,在 Deployment 里把项目 Artifact 名记为 hospital。

启动前建议核对两个地方。一是 Tomcat 版本,JSP/Servlet 项目用 Tomcat 8.5 或 9 都行,但不要用 Tomcat 10,因为 Tomcat 10 的 Jakarta EE 包名改动会让老项目里 import javax.servlet.* 全部编译失败。二是 Artifact 名,它决定访问路径,如果部署名是 hospital,浏览器访问地址就是 http://localhost:8080/hospital/。

启动后按这个顺序验证一遍:

  • 打开 http://localhost:8080/hospital/,能跳到登录页说明项目部署成功
  • 用管理端初始账号登录后台,账号密码在文档说明里,通常是 admin/admin123 这一类的组合
  • 新增一条科室和一名医生,再配置一条未来日期的排班
  • 从患者端注册、登录、选科室、选医生、选日期完成一次预约
  • 回到后台预约记录里看到刚才那条记录,排班页的余号减了 1,链路就完全通了

5. 避坑排查:404、乱码、号源错位的六个修复记录

5.1 启动后访问 404:Context Path 和部署名对不上

现象:Tomcat 启动日志没有报错,但浏览器访问 http://localhost:8080/hospital/ 显示 404,换根路径能打开 Tomcat 默认页。

原因:IDEA 的 Deployment 里 Artifact 名或 Application context 不是 hospital,访问路径和实际上下文路径不一致。Tomcat 启动成功只代表服务起来了,不代表项目映射到了预期的路径。

解决:打开 Run/Debug Configurations,找到 Deployment 标签页,把 Application context 修改为 /hospital,然后重启 Tomcat;也可以直接用浏览器访问配置里显示的路径。这个问题在第一次部署时出现的频率非常高,先检查部署名再看代码。

5.2 数据库连不上:驱动版本、时区和认证插件三座山

现象:项目启动后第一次访问登录页就报 SQLException,要么是 ClassNotFoundException: com.mysql.jdbc.Driver,要么是 Communications link failure,要么是 Access denied for user。

原因:驱动 jar 版本与本机 MySQL 版本不匹配,或 URL 缺少时区参数,或 MySQL 8 默认的 caching_sha2_password 认证方式与旧驱动不兼容。这三类报错表面都是“连接失败”,但背后的配置完全不同。

解决:先确认 MySQL 版本,再确认 lib 里驱动 jar 版本。MySQL 5.7 配 mysql-connector-java-5.1.x 最稳;MySQL 8.0 必须换 8.0.x 驱动,同时补 serverTimezone、useSSL、allowPublicKeyRetrieval 三个参数。如果用的是 MySQL 8 但项目密码是空的,检查用户表里 root 的 plugin 字段,必要时改成 mysql_native_password。

5.3 中文全变问号:三层编码必须一致

现象:注册的中文用户名在列表页显示为 ??,或者 JSP 页面标题乱码,数据库里存进去的就是问号。

原因:数据库连接 URL 没带 characterEncoding=utf8、SQL 脚本建表不是 utf8、JSP 页面本身没声明 UTF-8,三层里只要有一层不一致,中文就稳不住。很多情况是建库时用了默认 latin1,连接参数再全也救不回来。

解决:按顺序排查——先 SHOW TABLE STATUS LIKE 'user' 看表字符集,再看 DBUtil URL 是否带 characterEncoding=utf8,再看 JSP 头是否写了 <%@ page contentType="text/html; charset=UTF-8" %>。全部统一为 utf8 后,删除之前乱码的数据重新插入。改完数据库字符集后,已经存进去的乱码数据不会自动恢复,要重新造数据。

5.4 同一时段被同时约上:先查后改导致超卖

现象:演示时两个人同时点同一个时段预约,两个人都显示预约成功,后台出现两条预约记录,但排班的 reserved_num 只加了 1。

原因:代码是先去 SELECT reserved_num,判定小于 total_num 后再 UPDATE。两条请求同时读到相同的余号 17,都判定可约,于是各插一条记录。单机演示看不出来,但并发测试或多人同时操作时必现。

解决:把“判断余号”和“占用余号”合并成一条原子更新 UPDATE schedule SET reserved_num = reserved_num + 1 WHERE id = ? AND reserved_num < total_num,以 UPDATE 影响行数判断成败。改造后重复点击、并发点击都不会再超卖,这个坑在答辩演示时最容易翻车,务必提前改掉。

5.5 取消预约后余号变负数:状态条件没写全

现象:对同一条预约连续点击两次取消,第一次成功,第二次也返回成功,排班余号被多回退一次变成 -1。

原因:取消接口只写了 UPDATE appointment SET status = 2 WHERE id = ?,没有带 AND status = 0;号源回退也只写 UPDATE schedule SET reserved_num = reserved_num - 1 WHERE id = ?,没有带 AND reserved_num > 0。第二次取消时预约状态已经是 2,但 SQL 不认,照样执行回退。

解决:取消预约加状态条件,回退号源加正数条件。两次取消中第二次影响行数为 0,直接提示“该预约已取消”,号源不再回退。改完后可以在数据库里对同一条记录手动执行两次同样的 UPDATE 验证效果。

5.6 laydate 选完日期后查询不到排班:日期格式前后端没对齐

现象:laydate 日期控件能正常弹出来,但选完日期后点击查询,页面提示“暂无排班”,或者后端日志里出现 java.text.ParseException。

原因:laydate 的 format 默认是YYYY-MM-dd,其中 YYYY 是大写;如果用 laydate 的 format 配置了YYYY-M-d或YYYY/M/d,回传给后端的字符串就变成了 2026-6-1 或 2026/06/01 这种格式。后端 SimpleDateFormat 写死 yyyy-MM-dd,遇到 2026/06/01 直接解析失败,更不可能查到排班。

解决:检查两个地方——laydate 初始化代码里的 format 值,以及后端 SimpleDateFormat 的 pattern 字符串。最稳妥的做法是前端统一YYYY-MM-dd,后端统一yyyy-MM-dd,两边不要各自发挥。改完前端日期格式后,清一下浏览器缓存再验证。

6. 二次开发进阶:把手动排班改成按周模板自动生成

原系统的排班是管理员逐天手动创建的:选医生、选日期、选时段、填放号数,一天一天加。演示可以,但真正用起来管理员每天都要维护排班,这个痛点也适合作为二次开发的切入点。我拿到这套源码后最想改的就是这里——把排班做成“模板 + 自动生成”。

思路不复杂:新增一张排班模板表,字段为 doctor_id、day_of_week、time_slot、total_num,存“某医生周一上午放 20 号、周三下午放 15 号”这类规则;再写一个生成接口,传入起止日期,程序按日期区间扫描每一天是周几,匹配模板后批量 INSERT 排班。核心生成逻辑可以这样写:

// 按周模板生成某医生在 startDate ~ endDate 区间的排班 public void generateSchedules(int doctorId, String startDate, String endDate) throws Exception { String sql = "INSERT INTO schedule(doctor_id, work_date, time_slot, total_num, reserved_num) " + "SELECT doctor_id, ?, time_slot, total_num, 0 FROM schedule_template " + "WHERE doctor_id = ? AND day_of_week = ?"; LocalDate start = LocalDate.parse(startDate); LocalDate end = LocalDate.parse(endDate); for (LocalDate cur = start; cur.isBefore(end.plusDays(1)); cur = cur.plusDays(1)) { int dow = cur.getDayOfWeek().getValue(); // 周一=1 ... 周日=7 PreparedStatement ps = conn.prepareStatement(sql); ps.setDate(1, java.sql.Date.valueOf(cur)); ps.setInt(2, doctorId); ps.setInt(3, dow); int rows = ps.executeUpdate(); if (rows == 0 && cur.equals(start)) { logger.warn("医生 {} 在 {} 没有配置模板", doctorId, cur); } } }

逻辑说明:schedule_template 表保存模板,程序从起始日期循环到结束日期,取每天的周几去模板里匹配,有模板就生成一条排班。reserved_num 初始固定为 0,放号数完全由模板控制。DayOfWeek.getValue() 返回 1~7,与模板表的 day_of_week 保持一致;JDK 8 的 LocalDate 处理日期加减比 java.util.Date 干净得多,这也是我建议项目里保留 JDK 8 的原因。

这个改动对答辩的加分点是你能讲清楚“模板化”和“自动批量化”两个设计思路,比单纯演示预约流程更有话题性。配合资源里的数据库脚本和文档说明,把这段逻辑补进 service 层,管理员的排班效率会提升明显。注意自动生成前先做一次“日期区间是否已有排班记录”的查询,避免同一周跑两次脚本把排班插重复。从那以后我每次动排班相关代码,都会先跑一遍重复检查再执行生成逻辑,这已经成了习惯。希望这次拆解能帮你少踩几个坑,把项目跑顺、讲明白。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询