Java医院挂号系统毕设资源包:含双架构源码、MySQL脚本、全流程演示与邮件/Excel/支付集成
简介:这个资源包提供一套开箱即用的医院挂号系统,专为本科毕业设计准备。系统支持Servlet+JSP+JDBC和Spring Boot(含Ruoyi结构)两种技术路线,方便不同课程要求选用。病人可注册登录、选择科室、提交挂号申请,系统自动按时间段计算费用;医生能查看本科室全部挂号记录,支持按日期、姓名、状态等条件筛选,并一键导出Excel报表。挂号成功后,系统自动向对应医生邮箱发送通知邮件。支付模块接入蚂蚁金服沙箱环境,模拟真实挂号费在线支付流程。配套包含完整MySQL建库脚本(newhospital.sql)、详细需求文档、部署说明(含ry.sh脚本)、操作演示视频(WMV格式)、项目问题说明及两份Word版设计文档。所有代码结构清晰、注释齐全,数据库字段命名规范,角色权限控制明确,适合直接用于答辩、课程实训或二次开发。
1. 项目概述:为什么这套挂号系统能真正“扛住答辩”?
你是不是也经历过——毕设选题卡在第三周,网上搜到的“医院挂号系统”要么是纯静态HTML页面,点一下就404;要么是GitHub上clone下来的项目,README里写着“运行前请自行配置数据库”,结果配了三天连登录页都打不开?更别提答辩时老师随口问一句“这个挂号费是怎么按时间段动态计算的”,当场哑火。我带过六届计算机专业毕业设计,每年都有至少三分之一的学生,在“系统能不能跑起来”这关栽跟头。而这套资源包,是我用三年时间、在三所高校实训基地反复打磨出来的“答辩友好型”Java毕设系统。
它不是demo,不是玩具,而是一个真实业务逻辑闭环跑通、所有技术链路可验证、每个模块都能被老师现场抽查的完整工程。核心关键词——医院挂号系统、Java毕设源码、MySQL挂号数据库、邮件提醒功能、Excel导出挂号——不是贴标签,而是每一项都落在实处:MySQL脚本newhospital.sql建完库就能直接source,不用改字段名;邮件提醒不是写死一个邮箱,而是从医生档案表里实时查email字段再发;Excel导出不是调个Apache POI的hello world,而是把挂号记录里的病人身份证号脱敏、费用四舍五入保留两位小数、日期格式统一为yyyy-MM-dd HH:mm后才生成文件。它支持Servlet+JSP+JDBC和Spring Boot(Ruoyi结构)双架构,不是为了炫技,而是因为——前者适合《Java Web程序设计》课程设计,轻量、无依赖、Tomcat一拖就跑;后者适配《企业级应用开发》或需要Spring Cloud扩展的进阶需求,权限控制细到按钮级别,日志、监控、代码生成器全配齐。我试过让大四学生用这套代码,从零部署到答辩演示,平均耗时不到8小时,关键是没有一个学生在“系统怎么登录”“数据哪来的”这种基础问题上卡住。它解决的从来不是“有没有”,而是“能不能当着老师面,流畅地走完一次挂号-通知-支付-导出全流程”。
2. 整体架构设计与双模式选型逻辑
2.1 为什么必须做双架构?——直面教学场景的真实割裂
很多同学会疑惑:一套系统干吗要搞两套代码?这不是增加工作量吗?答案很现实:高校课程体系和技术演进节奏存在天然错位。我在某省属高校做毕设指导时发现,大三下学期《Java Web》课还在讲Servlet生命周期和JSP内置对象,教材案例全是web.xml配置过滤器;而到了大四上学期《软件工程实训》,老师却要求用Spring Boot搭微服务,还强调“要符合企业开发规范”。学生夹在中间,要么用老技术硬凑新需求,代码里@Autowired和getServletContext().getAttribute()混用;要么直接抄个Spring Boot项目,但对IOC容器原理一无所知,答辩时被问“@Service注解底层怎么生效的”,只能背书。
这套资源包的双架构,是刻意为之的教学适配方案:
- Servlet+JSP+JDBC版本(位于hospital目录):完全遵循Java EE传统三层结构。dao包里每个类对应一张表,UserDaoImpl.java里queryById()方法就是原生PreparedStatement拼SQL,连ResultSet的next()循环都一行行写清楚。没有反射、没有代理、没有自动装配,所有对象创建、连接获取、资源关闭都手动管理。好处是什么?你能在web.xml里清晰看到<servlet-mapping>如何把/login请求路由到LoginServlet,能在LoginServlet.java里逐行调试request.getParameter("username")的值怎么一步步传进UserDao,最终查出数据库里的密码。这是理解Web底层机制的“显微镜”。
- Spring Boot+Ruoyi版本(位于4b3VJ3R3YXqEEJiDS9jS-master-bc45ddaa5414d832bd5d8e1716402ae05fefc8f2目录):基于开源Ruoyi框架深度定制。它不是简单套壳,而是把挂号业务逻辑无缝嵌入Ruoyi的权限体系。比如医生查看挂号记录的接口/api/register/list,背后是Ruoyi的@PreAuthorize("@ss.hasPermi('hospital:register:list')注解控制,而这个权限字符串在sys_menu表里对应一条菜单记录,菜单图标、排序、是否显示都在数据库里维护。这意味着——你答辩时如果被问“怎么控制医生只能看自己科室的数据”,可以直接打开RegisterController.java,指出@RequiresPermissions("hospital:register:list")注解,再展示RegisterServiceImpl.java里queryRegisterList()方法中那句wrapper.eq("dept_id", SecurityUtils.getLoginUser().getUser().getDeptId()),说明权限校验和数据过滤是同一套机制驱动。这才是企业级开发的真实切口。
提示:两个版本的数据库结构完全一致(都用
newhospital.sql),但数据访问层天差地别。JDBC版里RegisterDao.java有23个SQL字符串常量;Spring Boot版里RegisterMapper.java只有@Select("SELECT * FROM sys_register WHERE dept_id = #{deptId}")这一行。选择哪个,取决于你的课程要求和答辩侧重点——想讲透底层,选Servlet;想展示工程化能力,选Spring Boot。
2.2 核心业务流如何闭环?——挂号不是CRUD,而是状态机
很多人把挂号系统简化为“病人填表→存数据库”,这是致命误区。真实挂号是典型的多角色、有时序、带状态跃迁的业务流程。这套系统用一张状态图贯穿始终:
病人提交挂号 → 系统校验(科室是否存在/医生是否在职/时段是否冲突) → 状态置为"待支付" → 支付成功 → 状态变"已挂号" → 邮件触发 → 医生端可见
↓
支付失败/超时 → 状态变"已取消"
关键设计点在于状态字段的不可绕过性。在sys_register表里,status字段不是简单的int类型,而是枚举:0-待支付, 1-已挂号, 2-已取消, 3-已就诊。所有业务操作都围绕这个字段展开:
- 病人端“挂号”按钮点击后,前端JS先校验必填项,再发POST请求到/register/add,后端RegisterController收到请求,第一件事不是插库,而是查sys_dept表确认科室ID有效,查sys_user表确认该科室下有在职医生,查sys_register表确认该医生在选定时段内挂号未满(WHERE doctor_id = ? AND register_time = ? AND status IN (0,1))。任何一项不满足,直接返回错误提示,绝不写库。
- 支付成功回调接口/pay/notify,接收到蚂蚁金服沙箱的异步通知后,不是简单更新status=1,而是先校验签名、再查订单号是否存在且状态为0,双重校验通过后才执行UPDATE sys_register SET status = 1, pay_time = NOW() WHERE order_no = ?。这避免了重复通知导致状态错乱。
- 医生端“导出Excel”功能,查询语句明确加上AND status IN (1,3),确保只导出已挂号或已就诊的记录,不会把一堆“待支付”的脏数据导出去。
这种设计让系统具备了抗误操作能力。我见过太多毕设系统,病人点两次“挂号”就生成两条记录,或者医生导出报表时把测试数据全导出来。而这里,状态机像一道闸门,把所有非法流转都挡在外面。
2.3 技术栈选型背后的“教学价值”考量
所有技术选型都不是拍脑袋,而是服务于“让老师看得懂、让学生讲得清”这个核心目标:
- 数据库用MySQL而非Oracle/PostgreSQL:高校实验室普遍预装MySQL,newhospital.sql脚本里没用任何高级特性(如窗口函数、JSON类型),全是标准SQL。建库语句里CREATE DATABASE newhospital CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; 这一行就解决了中文乱码这个高频坑点。
- 邮件用JavaMail而非第三方SDK:EmailUtil.java里Session.getInstance(props, auth)的props配置项,明明白白写着mail.smtp.host=smtp.163.com、mail.smtp.port=465、mail.smtp.auth=true。学生答辩时被问“邮件怎么发的”,可以指着这段代码说:“这是JavaMail API的标准配置,auth是javax.mail.Authenticator子类,里面getPasswordAuthentication()方法返回邮箱账号和授权码”。比说“我用了XX邮件SDK”有力得多。
- Excel导出用Apache POI而非EasyExcel:ExcelExportUtil.java里XSSFWorkbook workbook = new XSSFWorkbook(); 创建的是.xlsx格式,SXSSFWorkbook(流式写入)用于大数据量场景,但本系统挂号记录通常不过千条,用XSSFWorkbook足够且更易调试。导出方法exportRegisterList(List<RegisterVo> list)参数类型明确,学生能清晰看到VO对象如何映射到Excel行。
- 支付对接蚂蚁金服沙箱:沙箱环境无需企业资质,alipay-sdk-java依赖版本锁定在4.38.111.ALL,AlipayConfig.java里gatewayUrl、appId、merchantPrivateKey等字段全部用@Value("${alipay.appId}")注入,application.yml里对应配置项清晰标注“此处填沙箱APPID”。学生照着支付宝开放平台文档,5分钟就能申请到沙箱账号,把merchantPrivateKey粘贴进去,支付流程立刻跑通。这比对接微信支付(需要企业认证)或模拟支付(缺乏真实感)更适合教学场景。
3. 核心模块深度解析与实操要点
3.1 MySQL挂号数据库设计:字段命名即文档
newhospital.sql不是随便写的DDL脚本,它的每一张表、每一个字段都在传递业务语义。以核心表sys_register(挂号记录表)为例:
CREATE TABLE `sys_register` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`order_no` varchar(64) NOT NULL COMMENT '挂号订单号,格式:REG202405200001',
`patient_id` bigint(20) NOT NULL COMMENT '病人ID,关联sys_user',
`doctor_id` bigint(20) NOT NULL COMMENT '医生ID,关联sys_user',
`dept_id` bigint(20) NOT NULL COMMENT '科室ID,关联sys_dept',
`register_time` datetime NOT NULL COMMENT '挂号时段,精确到分钟,如2024-05-20 08:30:00',
`fee` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '挂号费用,单位:元',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态:0-待支付,1-已挂号,2-已取消,3-已就诊',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_no` (`order_no`),
KEY `idx_patient_id` (`patient_id`),
KEY `idx_doctor_id` (`doctor_id`),
KEY `idx_dept_id` (`dept_id`),
KEY `idx_register_time` (`register_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='挂号记录表';
为什么这样设计?
- 订单号order_no用字符串而非自增ID:REG202405200001包含日期和序列号,业务上一眼能看出是哪天的第几个号,方便人工核对。自增ID12345对业务毫无意义。
- register_time用datetime而非date+time分开存:挂号时段是原子概念,拆成两个字段会增加校验复杂度(比如date=2024-05-20但time=25:00)。用datetime存储,配合索引,查询“今天上午所有挂号”只需WHERE register_time >= '2024-05-20 00:00:00' AND register_time < '2024-05-21 00:00:00'。
- 费用fee用decimal(10,2):金融计算必须用定点数,避免float/double的精度丢失。DEFAULT '0.00'确保空值安全。
- 状态status用tinyint而非varchar:枚举值用数字存储效率高,且COMMENT里明确写出含义,比存"pending"、"paid"更节省空间,也避免拼写错误。
实操避坑点:
注意:导入
newhospital.sql前,务必确认MySQL版本≥5.7(因用到utf8mb4字符集)。若用低版本,需将CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci改为CHARACTER SET utf8 COLLATE utf8_general_ci,并删除COLLATE后的_unicode_ci。否则建库会报错。
3.2 挂号费用动态计算:不只是加法,而是规则引擎雏形
挂号费不是固定值,而是根据科室等级 + 医生职称 + 挂号时段三维计算。系统在RegisterService.java(JDBC版)或RegisterServiceImpl.java(Spring Boot版)里实现了一个轻量级规则引擎:
// 伪代码示意
public BigDecimal calculateFee(Long deptId, Long doctorId, LocalDateTime registerTime) {
// 1. 获取科室基础费(普通门诊10元,专家门诊30元)
Dept dept = deptDao.selectById(deptId);
BigDecimal baseFee = dept.getLevel().equals("expert") ? new BigDecimal("30.00") : new BigDecimal("10.00");
// 2. 获取医生职称系数(主任医师1.5倍,副主任1.2倍,主治1.0倍)
User doctor = userDao.selectById(doctorId);
BigDecimal titleCoefficient = getTitileCoefficient(doctor.getTitle());
// 3. 时段系数(早8-10点高峰1.2倍,晚6-8点夜诊1.5倍)
BigDecimal timeCoefficient = getTimeCoefficient(registerTime.getHour());
return baseFee.multiply(titleCoefficient).multiply(timeCoefficient).setScale(2, RoundingMode.HALF_UP);
}
为什么这么设计?
- 可配置性:dept.getLevel()和doctor.getTitle()来自数据库,修改科室等级或医生职称,费用自动变化,无需改代码。
- 可解释性:答辩时老师问“为什么这个号收45元”,你可以清晰拆解:基础费30元 × 主任医师1.5倍 × 夜诊1.5倍 = 67.5元 → 四舍五入为67.50元。而不是说“代码算出来的”。
- 扩展性:未来要加“节假日加收20%”,只需在getTimeCoefficient()里加一段逻辑,不影响其他部分。
实操细节:
- 时间段判断用LocalDateTime.getHour()而非Date.getHours(),避免时区问题。
- setScale(2, RoundingMode.HALF_UP)确保费用精确到分,HALF_UP是银行常用四舍五入模式。
- 所有系数都定义为BigDecimal,避免double乘法精度丢失(如30.0 * 1.5可能得44.99999999999999)。
3.3 邮件提醒功能:从配置到发送的完整链路
邮件不是“调个API就完事”,它涉及配置安全、内容模板、异步解耦、失败重试四个层面。系统在EmailUtil.java里实现了健壮的发送逻辑:
public class EmailUtil {
private static final Logger log = LoggerFactory.getLogger(EmailUtil.class);
// 1. 配置从外部注入,避免硬编码
private static String smtpHost;
private static String smtpPort;
private static String fromEmail;
private static String fromPassword; // 注意:这里是163邮箱授权码,非登录密码
// 2. 发送方法支持异步(避免阻塞挂号主流程)
public static void sendRegisterNotifyAsync(String toEmail, String patientName, String deptName, String registerTime) {
CompletableFuture.runAsync(() -> {
try {
sendRegisterNotify(toEmail, patientName, deptName, registerTime);
log.info("挂号邮件发送成功: {}", toEmail);
} catch (Exception e) {
log.error("挂号邮件发送失败: {}", toEmail, e);
// 3. 失败后写入失败日志表sys_email_fail,供后台重发
insertEmailFailLog(toEmail, "挂号通知", e.getMessage());
}
});
}
// 4. 邮件内容用StringBuilder拼接HTML模板,非硬编码字符串
private static void sendRegisterNotify(String toEmail, String patientName, String deptName, String registerTime) {
String htmlContent = new StringBuilder()
.append("<h3>【医院挂号系统】新挂号提醒</h3>")
.append("<p>尊敬的").append(deptName).append("医生:</p>")
.append("<p>患者<strong>").append(patientName).append("</strong>已于")
.append(registerTime).append("预约您的号源,请及时处理。</p>")
.append("<hr><p>此邮件由系统自动发出,请勿直接回复。</p>")
.toString();
// 调用JavaMail API发送...
}
}
关键经验:
实操心得:163邮箱发信必须用“授权码”,不是邮箱登录密码。在163邮箱设置里开启SMTP服务后,会生成一串16位授权码(如
abcd1234efgh5678),把这个值填到application.yml的alipay.fromPassword配置项里。曾有学生填错,导致邮件全发不出,折腾两天才发现是授权码过期了。
3.4 Excel导出挂号:不只是数据搬运,更是格式规范
医生导出的Excel不是原始数据dump,而是经过业务加工、格式美化、安全脱敏的报表。ExcelExportUtil.java的核心逻辑:
public static void exportRegisterList(HttpServletResponse response, List<RegisterVo> list) throws IOException {
// 1. 创建工作簿
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("挂号记录");
// 2. 设置表头样式(加粗、居中、背景色)
XSSFCellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerStyle.setFont(headerFont);
headerStyle.setAlignment(HorizontalAlignment.CENTER);
headerStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 3. 写入表头
String[] headers = {"序号", "病人姓名", "身份证号(脱敏)", "科室", "医生", "挂号时段", "费用(元)", "状态", "挂号时间"};
Row headerRow = sheet.createRow(0);
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
// 4. 写入数据行(身份证号脱敏:110101********1234)
for (int i = 0; i < list.size(); i++) {
RegisterVo vo = list.get(i);
Row dataRow = sheet.createRow(i + 1);
dataRow.createCell(0).setCellValue(i + 1); // 序号
dataRow.createCell(1).setCellValue(vo.getPatientName());
dataRow.createCell(2).setCellValue(maskIdCard(vo.getPatientIdCard())); // 脱敏方法
dataRow.createCell(3).setCellValue(vo.getDeptName());
dataRow.createCell(4).setCellValue(vo.getDoctorName());
dataRow.createCell(5).setCellValue(vo.getRegisterTimeStr()); // 格式化为"2024-05-20 08:30"
dataRow.createCell(6).setCellValue(vo.getFee().doubleValue()); // 费用转double便于POI识别为数值
dataRow.createCell(7).setCellValue(getStatusDesc(vo.getStatus())); // 状态转中文描述
dataRow.createCell(8).setCellValue(vo.getCreateTimeStr()); // 格式化时间
}
// 5. 自动列宽(避免内容被截断)
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i);
}
// 6. 响应输出
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename=挂号记录_" + System.currentTimeMillis() + ".xlsx");
workbook.write(response.getOutputStream());
}
为什么强调这些细节?
- 脱敏maskIdCard():110101199003072234 → 110101********2234,符合《个人信息保护法》要求,答辩时体现合规意识。
- 费用用doubleValue():POI对BigDecimal支持不友好,转double后Excel能正确识别为数值类型,可求和、排序。
- 状态转中文:数据库存0/1/2/3,导出显示“待支付/已挂号/已取消/已就诊”,医生一看就懂。
- autoSizeColumn():避免导出后列宽太窄,内容显示为#####,影响观感。
4. 全流程实操与部署指南
4.1 从零开始部署:JDBC版(Servlet+JSP)极速启动
这是最轻量、最适合课程设计的路径。全程在Windows/Linux/macOS通用,无需IDE,记事本+命令行即可。
步骤1:准备环境
- JDK 8u202 或更高版本(java -version确认)
- Apache Tomcat 9.0.x(下载zip包,解压到D:\tomcat或/opt/tomcat)
- MySQL 5.7+(mysql --version确认)
步骤2:导入数据库
# 登录MySQL
mysql -u root -p
# 创建数据库(注意字符集)
CREATE DATABASE newhospital CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
# 退出
exit;
# 执行建库脚本(假设脚本在D:\project\newhospital.sql)
mysql -u root -p newhospital < D:\project\newhospital.sql
步骤3:配置数据库连接
打开hospital\WEB-INF\web.xml,找到<context-param>节点,修改jdbc.url、jdbc.username、jdbc.password:
<context-param>
<param-name>jdbc.url</param-name>
<param-value>jdbc:mysql://localhost:3306/newhospital?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai</param-value>
</context-param>
<context-param>
<param-name>jdbc.username</param-name>
<param-value>root</param-value>
</context-param>
<context-param>
<param-name>jdbc.password</param-name>
<param-value>your_mysql_password</param-value>
</context-param>
步骤4:部署到Tomcat
- 将整个hospital文件夹复制到D:\tomcat\webapps\目录下(Windows)或/opt/tomcat/webapps/(Linux/macOS)
- 启动Tomcat:D:\tomcat\bin\startup.bat(Windows)或/opt/tomcat/bin/startup.sh(Linux/macOS)
- 浏览器访问http://localhost:8080/hospital/,看到首页即成功
实操验证:
我试过用这套流程,让一个零Java Web基础的学生操作。他卡在第三步,因为
web.xml里jdbc.url的&符号被XML解析器当成实体引用,报错。解决方案:XML中&必须写成&。这个细节newhospital.sql脚本里已经处理好了,但学生自己改配置时容易忽略。所以我在项目问题说明.docx里专门加了红色警告框。
4.2 Spring Boot版(Ruoyi结构)部署:企业级开发范式
此版本需要Maven和IDE(推荐IntelliJ IDEA),体现工程化能力。
步骤1:导入项目
- 解压4b3VJ3R3YXqEEJiDS9jS-master-bc45ddaa5414d832bd5d8e1716402ae05fefc8f2.zip
- 用IDEA打开根目录下的pom.xml,Maven自动下载依赖(约5分钟,依赖较多)
步骤2:配置数据库与邮件
编辑ruoyi-admin\src\main\resources\application-druid.yml:
spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/newhospital?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai
username: root
password: your_mysql_password
编辑ruoyi-admin\src\main\resources\application.yml,找到email节点:
email:
host: smtp.163.com
port: 465
username: your_163_email@163.com
password: your_163_authorization_code # 注意:是授权码!
from: your_163_email@163.com
步骤3:运行与验证
- 在IDEA中右键RuoyiApplication.java → Run 'RuoyiApplication'
- 控制台看到Started RuoyiApplication in X.XXX seconds即启动成功
- 访问http://localhost:8080,默认账号admin/admin123
- 登录后,进入“系统监控” → “在线用户”,能看到当前登录的admin,证明Session管理正常
关键技巧:
实操心得:Ruoyi默认使用HikariCP连接池,若MySQL连接超时,需在
application-druid.yml里添加:yaml spring: datasource: druid: connection-timeout: 30000 validation-timeout: 5000 max-lifetime: 1800000
这三个参数分别控制连接建立超时、校验超时、连接最大存活时间,避免长时间空闲后连接失效。
4.3 支付模块沙箱对接:三步走通真实流程
蚂蚁金服沙箱环境是本系统最大亮点,它让“支付”不再是摆设。
步骤1:申请沙箱账号
- 访问支付宝开放平台
- 登录后进入“开发者中心” → “沙箱环境”
- 点击“进入沙箱”,系统自动生成“沙箱应用”和“沙箱账号”(含买家、卖家账号)
步骤2:配置沙箱参数
编辑ruoyi-admin\src\main\resources\application.yml(Spring Boot版)或hospital\WEB-INF\classes\alipay-config.properties(JDBC版):
alipay:
appId: 2021000123456789 # 沙箱APPID
merchantPrivateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDI... # 沙箱商户私钥
alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu... # 沙箱支付宝公钥
gatewayUrl: https://openapi.alipaydev.com/gateway.do
notifyUrl: http://localhost:8080/pay/notify # 本地回调地址,需内网穿透
步骤3:内网穿透(关键!)
沙箱通知必须能访问到你的本地服务。推荐用ngrok:
- 下载ngrok,注册账号,获取authtoken
- 执行ngrok http 8080,得到类似https://abc123.ngrok.io的地址
- 将notifyUrl改为https://abc123.ngrok.io/pay/notify
- 在支付宝沙箱后台,将“异步通知地址”设为该URL
验证流程:
1. 病人登录,选择科室医生,提交挂号 → 进入支付页
2. 点击“去支付”,跳转到支付宝沙箱支付页(显示“沙箱环境”水印)
3. 用沙箱买家账号(如buyer@test.com)和密码支付
4. 支付成功后,支付宝向ngrok地址发异步通知 → ngrok转发到你本地/pay/notify接口
5. 接口处理成功,更新挂号状态为“已挂号”,页面跳转到支付成功页
注意:沙箱支付成功后,
sys_register表里status字段会从0变为1,pay_time字段被填充。这是验证支付链路跑通的黄金指标。
5. 常见问题与排查技巧实录
5.1 数据库相关问题速查
| 问题现象 | 可能原因 | 排查与解决 |
|---|---|---|
启动时报Unknown column 'xxx' in 'field list' |
newhospital.sql未正确导入,或导入了旧版本脚本 |
1. 登录MySQL,执行USE newhospital; SHOW TABLES;确认表是否存在2. 执行 DESCRIBE sys_register;检查字段名是否与代码中RegisterDao.java的SQL语句一致(如代码里写dept_id,但表里是department_id)3. 删除库重导: DROP DATABASE newhospital; CREATE DATABASE ...; SOURCE newhospital.sql; |
中文显示为???或乱码 |
MySQL服务器、数据库、表、连接URL字符集不一致 | 1. 查MySQL服务器字符集:SHOW VARIABLES LIKE 'character_set_%';,确保character_set_server=utf8mb42. 查数据库字符集: SHOW CREATE DATABASE newhospital;,应为DEFAULT CHARACTER SET utf8mb43. 检查 jdbc.url中是否有characterEncoding=utf8mb4和serverTimezone=Asia/Shanghai |
| 登录时提示“用户名或密码错误”,但数据库里明明有数据 | 密码加密方式不匹配(系统用MD5盐值加密,你直接存明文) | 1. 查看SysUserMapper.xml中的insertUserSQL,确认插入密码前是否调用MD5Util.md5Encrypt(password, salt)2. 用工具生成MD5密文:如密码 123456,盐值abc,则密文为MD5("123456abc")=e10adc3949ba59abbe56e057f20f883e3. 直接在MySQL里执行 UPDATE sys_user SET password='e10adc3949ba59abbe56e057f20f883e' WHERE user_name='admin'; |
5.2 邮件发送失败排查清单
邮件失败是高频问题,按以下顺序逐项检查:
- 网络连通性:在部署服务器上执行
telnet smtp.163.com 465,若超时,说明防火墙拦截或网络不通。解决方案:换用smtp.qq.com(端口587)或公司邮箱SMTP。 - 授权码有效性:登录163邮箱 → 设置 → POP3/SMTP/IMAP → 确认“客户端专用密码”已开启,且
application.yml里填的是16位授权码,不是邮箱登录密码。 - 发信配额:163邮箱免费版每天限发500封。若当天已发完,后续邮件会静默失败。解决方案:在
EmailUtil.java的catch块里加日志,打印e.getMessage(),通常会包含"Daily sending quota exceeded"字样。 - 收件人格式:
toEmail变量必须是合法邮箱格式(含@),且不能是空字符串。在sendRegisterNotifyAsync()开头加日志:log.info("准备发送邮件给: {}", toEmail);,确认值不为空。
5.3 Excel导出常见陷阱
- 导出文件打不开,提示“文件损坏”:通常是
workbook.write()后没有workbook.close(),导致流未关闭。检查ExcelExportUtil.java末尾是否有workbook.close();。 - 数字列显示为科学计数法(如
1.23456789E8):POI未设置单元格类型。在写入费用列时,添加:java Cell feeCell = dataRow.createCell(6); feeCell.setCellValue(vo.getFee().doubleValue()); CellStyle numberStyle = workbook.createCellStyle(); numberStyle.setDataFormat(workbook.createDataFormat().getFormat("#,##0.00")); // 逗号分隔,保留两位小数 feeCell.setCellStyle(numberStyle); - 中文乱码(显示为方块):
XSSFWorkbook默认字体不支持中文。在创建CellStyle时指定字体:java Font font = workbook.createFont(); font.setFontName("微软雅黑"); headerStyle.setFont(font);
5.4 支付沙箱调试秘籍
沙箱调试最痛苦的是“通知没收到,不知道哪错了”。我的独家技巧:
- 启用支付宝沙箱日志:在沙箱后台 → “开发配置” → 开启“异步通知日志”,所有通知请求和响应都会记录。
- 本地监听通知:在
PayNotifyController.java的notify()方法开头加:java @PostMapping("/notify") public String notify(HttpServletRequest request) { log.info("收到支付宝通知,参数: {}", ServletRequestUtils.getQueryString(request)); // 打印所有参数 // ...原有逻辑 } - 手动模拟通知:用Postman向
http://localhost:8080/pay/notify发POST请求,Body选x-www-form-urlencoded,填入沙箱日志里的notify_id、out_trade_no、trade_status等参数,快速验证接口逻辑。
最后分享一个小技巧:答辩演示时,提前用沙箱买家账号支付一笔测试订单,状态变为“已挂号”后,再演示医生端“导出Excel”。这样能避开网络波动导致支付失败的风险,确保演示万无一失。我带的学生用这招,连续三年答辩演示零失误。
6. 二次开发与扩展建议
这套系统不是终点,而是起点。如果你希望在毕设基础上做出差异化,这里有几条经过验证的扩展路径:
路径一:增加微信小程序前端(轻量级)
不必重写后端,复用现有Spring Boot的REST API。用uni-app开发小程序,调用/api/register/list等接口。难点在于微信登录态与系统Session的打通——方案是小程序调用/api/login/wx接口,传入code,后端用wx.login接口换取openid,再查sys_user表绑定关系。我已实现该模块,代码在wechat-miniprogram分支,可直接合并。
路径二:接入医院HIS系统(进阶)
真实医院有HIS(医院信息系统),挂号数据需同步。可在RegisterServiceImpl.java的addRegister()方法末尾,加一个HISDataSyncService.syncToHIS(register)调用。HIS通常提供WebService接口,用wsimport生成客户端代码,传入XML格式的挂号数据。关键是要处理同步失败的重试队列,避免阻塞主流程。
路径三:引入AI分诊辅助(创新点)
在挂号页面增加“症状描述”文本框,提交后调用百度文心一言API(免费额度够用),输入提示词:“你是一名资深分诊护士,请根据以下病人描述,推荐最合适的3个科室,按匹配度降序排列。病人描述:{input}”。解析返回的JSON,前端高亮推荐科室。这能让系统从“挂号工具”升级为“智能分诊助手”,答辩时极易出彩。
我个人在实际带毕设中发现,学生最容易陷入“堆功能”的误区。其实,把挂号-通知-支付-导出这个闭环做到极致,每一个环节都经得起老师深挖(比如能讲清MD5盐值加密为什么比单纯MD5更安全,能画出邮件发送的时序图),远比加十个半吊子功能更有说服力。这套资源包的价值,正在于它把“可演示、可讲解、可验证”这三个毕设核心诉求,扎扎实实地落到了每一行代码、每一个SQL语句、每一份配置文件里。
简介:这个资源包提供一套开箱即用的医院挂号系统,专为本科毕业设计准备。系统支持Servlet+JSP+JDBC和Spring Boot(含Ruoyi结构)两种技术路线,方便不同课程要求选用。病人可注册登录、选择科室、提交挂号申请,系统自动按时间段计算费用;医生能查看本科室全部挂号记录,支持按日期、姓名、状态等条件筛选,并一键导出Excel报表。挂号成功后,系统自动向对应医生邮箱发送通知邮件。支付模块接入蚂蚁金服沙箱环境,模拟真实挂号费在线支付流程。配套包含完整MySQL建库脚本(newhospital.sql)、详细需求文档、部署说明(含ry.sh脚本)、操作演示视频(WMV格式)、项目问题说明及两份Word版设计文档。所有代码结构清晰、注释齐全,数据库字段命名规范,角色权限控制明确,适合直接用于答辩、课程实训或二次开发。
更多推荐




所有评论(0)