☰
Spring Boot+微信小程序医院挂号预约系统:从数据库设计到联调部署全解析
2026/10/1 3:24:09 网站建设 项目流程

简介:医院挂号预约系统是一套面向毕业设计、课程设计场景的微信小程序全栈项目,包含管理员与普通用户双角色,基本覆盖预约挂号业务的完整闭环。管理员端涵盖个人中心、用户管理、医生信息管理、医院信息管理、科室信息管理、预约信息管理、预约取消管理、留言板与系统管理;小程序端支持注册登录、浏览医院与医生信息、查看公告资讯,并可在科室模块中提交预约或取消预约。资源提供完整前后端源码、数据库文件与运行脚本,基于Java JDK1.8、MySQL 5.7、Tomcat7及微信开发者工具构建,环境说明清晰,适合有Java基础的学生参考、修改与二次开发。压缩包共1321个文件,以Vue页面、Java后端类、WXML/WXSS小程序页面、JSON配置、PNG图片和SQL数据库脚本为主要类型,内置安装、运行、构建脚本,便于本地部署调试;整体约27.14MB。目前已有78人浏览学习,可作为医院预约类毕设项目的代码范本、模块拆分参考与排错对照。

1. 医院挂号预约小程序毕业设计:这套 java+小程序+mysql 源码到底能帮你省多少事

每年到了毕设季,医院挂号预约系统都是微信小程序方向里出现频率最高的题目之一。它不像商城系统那样业务堆得没边,也不像图书管理那类选题单薄到撑不起一篇论文,恰好卡在“有完整的预约流程、有角色权限、有数据表关联、有状态流转”这个甜点上——java 写后端接口、微信小程序做患者端、mysql 存业务数据,三样全占,对应着你简历上最常被问到的三个技术词。拿到这套源码,你的目标不是“解压后能打开”就交差,而是搞清楚它怎么跑通、改哪里、答辩时怎么讲。这篇就按我从导入 IDEA 到小程序联调、再走到答辩演示的完整路径来讲,顺便把最容易翻车的几个细节提前给你排掉。

2. 看懂系统骨架:Spring Boot 后端、微信小程序前端与 MySQL 的三层数据流

2.1 后端选型为什么是 Spring Boot 而不是传统 SSM 或 Servlet 项目

现在市面上流传的医院挂号预约毕设源码,早几年还是 SSM(Spring + SpringMVC + MyBatis)打天下,最近两年基本都换成了 Spring Boot。原因很实际:Spring Boot 内嵌 Tomcat,打包成 jar 之后一条java -jar就能把服务拉起来,不用额外装 Tomcat、不用配 server.xml;它自带了 SpringMVC 和默认的 JSON 序列化,写 Controller 时不用碰一坨 XML 配置。对一个要同时写论文、调代码、准备答辩的毕业生来说,少一个环节就少一个坑。

选型时还有个现实问题:你的导师或答辩老师可能更熟悉 SSM 那一套。如果题目文字里写的是“基于 SSM 的医院挂号预约系统”,而源码是 Spring Boot,这不冲突——论文里把 Spring Boot 表述为“Spring 技术栈的快速开发脚手架”就能接住,底层还是 Spring + SpringMVC 的请求处理模型,老师的追问你照样能答。至于要不要换回 SSM 老架构,我的建议是别换。Spring Boot 在简历项目和面试场景里是当前主流,答辩老师通常不会因为你用了更新的技术而扣分,反而会问你“为什么选它”,这就是个送分题。

还有一类源码是用 uniapp 写的,一套代码同时编译到微信小程序、安卓、iOS 甚至鸿蒙。如果你拿到的版本是 uniapp 工程,它的目录结构和原生微信小程序差别很大:pages.json替代了app.json,<template>替代了<view>那套写法。这两种我都跑过,就毕设而言原生小程序更省事,因为题目要求是“微信小程序”,原生工程直接导入微信开发者工具就能跑,不需要先过一遍 uniapp 的编译链。uniapp 的跨端能力在这里反而是多余复杂度,答辩时还容易给自己挖坑。

2.2 MySQL 核心表拆解:用户、医生、科室、排班、挂号订单

这套系统的数据库设计一般是 5 到 7 张表,核心五张是:用户表(患者)、医生表、科室表、排班表、挂号订单表。毕设答辩高频问题之一是“表之间怎么关联”,所以这张关系网你得能自己画出来,不能只指着源码说“老师你看这有个外键”。

我见过的常见建表结构大致是这样的,用户表和排班表最能体现设计思路:

-- 用户表(患者 + 管理员复用,靠 role 区分) CREATE TABLE `t_user` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', `username` VARCHAR(50) NOT NULL COMMENT '登录名', `password` VARCHAR(100) NOT NULL COMMENT 'MD5 后的密码', `real_name` VARCHAR(50) DEFAULT NULL COMMENT '真实姓名', `phone` VARCHAR(20) DEFAULT NULL COMMENT '手机号', `role` TINYINT NOT NULL DEFAULT 0 COMMENT '0 患者 1 管理员', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; -- 排班表:医生、科室、日期、时段、号源数量绑定在一行 CREATE TABLE `t_schedule` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `doctor_id` INT UNSIGNED NOT NULL COMMENT '医生 ID', `dept_id` INT UNSIGNED NOT NULL COMMENT '科室 ID', `work_date` DATE NOT NULL COMMENT '出诊日期', `time_slot` TINYINT NOT NULL COMMENT '时段:1 上午 2 下午', `total` INT UNSIGNED NOT NULL DEFAULT 30 COMMENT '总号源数', `remain` INT UNSIGNED NOT NULL DEFAULT 30 COMMENT '剩余号源数', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0 未放号 1 放号中 2 已约满', PRIMARY KEY (`id`), UNIQUE KEY `uk_doc_date_slot` (`doctor_id`, `work_date`, `time_slot`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='排班表';

看明白这两张表,挂号系统的核心逻辑就通了一半。t_schedule把医生、科室、日期、时段、号源绑在一行里,remain就是余号。每次挂号成功,remain减 1,减到 0 时status置为 2。用户从选科室到最后确认挂号,所有查询最终都收敛到这表上的一个更新操作,业务边界非常清晰。

uk_doc_date_slot这个唯一索引值得单独记一笔。它的作用是保证同一个医生在同一天同一个时段不会生成两条排班记录。很多初版源码里没有这个索引,就会出现重复排班导致余号错乱,查都查不出来。论文的数据表设计说明里把这个索引的作用讲一段,属于实打实的加分项。

挂号订单表也要能讲明白,它是整个系统里数据量增长最快的一张表:

CREATE TABLE `t_appointment` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `order_no` VARCHAR(32) NOT NULL COMMENT '订单号,业务编号', `user_id` INT UNSIGNED NOT NULL COMMENT '患者用户 ID', `schedule_id` INT UNSIGNED NOT NULL COMMENT '排班 ID', `doctor_id` INT UNSIGNED NOT NULL COMMENT '医生 ID,冗余字段', `appoint_date` DATE NOT NULL COMMENT '就诊日期,冗余字段', `time_slot` TINYINT NOT NULL COMMENT '时段,冗余字段', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0 已预约 1 已取消 2 已完成', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`), KEY `idx_schedule_id` (`schedule_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='挂号订单表';

注意这里我把doctor_id、appoint_date、time_slot冗余进了订单表。理由很直接:列出“我的挂号记录”时,小程序端一次请求就要把医生姓名、科室、日期、时段、状态全展示出来,如果这些字段都要通过schedule_id去 join 排班表再 join 医生表,SQL 写起来复杂,接口响应也慢。用空间换查询性能,是这类业务里很常规的做法,答辩时讲清楚“为什么冗余”,比背概念有用得多。

2.3 前后端数据契约:统一返回格式与小程序请求封装

后端接口和小程序端之间走的是 HTTP + JSON。我见到的毕设源码大多会把返回结果包装成一个Result对象,格式是 code + message + data 三段式。找到这个Result类,你就能顺藤摸瓜定位所有业务接口的入口。

{ "code": 200, "message": "操作成功", "data": { "scheduleId": 12, "remain": 15 } }

小程序端的对应物通常是utils/request.js里的wx.request封装。把url、method、data、header抽成公共参数,这样页面里不用每个地方都写一遍完整的wx.request。常见封装长这样:

// utils/request.js 公共请求封装 const BASE_URL = 'http://localhost:8080/api'; // 联调时用本地地址,真机预览要换成局域网 IP 或已备案域名 function request(url, method = 'GET', data = {}) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + url, method: method, data: data, header: { 'Content-Type': 'application/json', 'token': wx.getStorageSync('token') || '' }, success: (res) => { if (res.statusCode === 200 && res.data.code === 200) { resolve(res.data.data); } else { wx.showToast({ title: res.data.message || '请求失败', icon: 'none' }); reject(res.data); } }, fail: (err) => { wx.showToast({ title: '网络异常', icon: 'none' }); reject(err); } }); }); } module.exports = { request, BASE_URL };

这段封装里最容易被忽略的是token从wx.getStorageSync读取。毕设系统的登录态通常就是“登录成功后后端返回一个 token,小程序存进 storage,后续请求带在 header 里”。这个机制答“你怎么做登录鉴权”时是三句话能讲完的完整闭环:小程序端存 token、后端用拦截器校验 token、未登录返回 401 让前端跳登录页。

3. 把源码跑起来:从 IDEA 导入后端到微信开发者工具联调

3.1 环境准备清单与版本匹配是第一个坑

跑这套源码之前,先把环境对齐。后端是 java + mysql,前端是微信小程序,三样东西的版本差异是头号麻烦——尤其是 JDK 和 Spring Boot 版本不匹配时,启动报错会让你误以为源码是坏的。

我建议按这个组合准备环境:

  • JDK 1.8。大多数毕设源码的 pom 里指定的编译版本是 1.8,你本机如果是 JDK 17,大概率会遇到javax.annotation包不存在之类的问题
  • Maven 3.6 以上,IDEA 2021 之后任意版本
  • MySQL 5.7 或 8.0。注意 8.0 的 JDBC 驱动类名是com.mysql.cj.jdbc.Driver,5.7 用com.mysql.jdbc.Driver,两者混用会直接报ClassNotFoundException,这也是 mysql ssl 连接错误之外最常出现的启动报错
  • 微信开发者工具稳定版,以及一个能用的 AppID。没有正式 AppID 时,开发者工具里可以选测试号,但测试号在真机预览上有权限限制

装 mysql 时有一个高频翻车点:初始化完 root 密码后,驱动连接串里要记得带useSSL=false或者serverTimezone=Asia/Shanghai这类参数。报SSL connection error十有八九是连接串没带useSSL=false,这个问题在第 5 章展开讲。

3.2 后端启动步骤:建库、改配置、跑起来

拿到源码包后,推荐按下面顺序操作,每步做完再进下一步,别跳:

# 1. 建库。源码包的 sql 目录下一般有 init.sql 之类的脚本 mysql -uroot -p < init.sql # 2. 用 IDEA 打开后端目录,等 Maven 依赖下载完 # 3. 修改 application.yml 里的数据源配置

application.yml里的数据源配置是最关键的改动点,常见的坑集中在url和driver-class-name上:

spring: datasource: url: jdbc:mysql://localhost:3306/hospital?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 你的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver

jdbc:mysql://localhost:3306/hospital这一段要跟第 1 步建的库名完全一致,密码改成你本机的。useSSL=false是必须的,否则 MySQL 8.0 默认开启 SSL 会导致连接报错;serverTimezone=Asia/Shanghai是为了解决时区差 8 小时的问题,不写的话插入的create_time经常会比你本地时间晚 8 个小时。

配置改完,在 IDEA 里找到启动类,类名通常是HospitalApplication,右键 Run。看到Tomcat started on port(s): 8080这行日志,后端就算起来了。如果中间报错,把日志往上翻,90% 的情况要么是数据库连接失败,要么是 Maven 依赖没下全,先别怀疑源码本身。

启动后端时顺手做一件事:把项目的打包配置确认好。答辩前我会建议你打成 jar 包再看一遍能否独立运行,因为现场演示如果用 IDEA 跑,万一投影机器上没装 IDEA,就只有java -jar xxx.jar这一条路。pom 里spring-boot-maven-plugin的配置决定打包后能不能直接运行,这个在第 6 章增量验证部分细说。

3.3 小程序端导入与 AppID 配置联调

后端跑通之后,前端反而简单。用微信开发者工具导入源码包里的miniprogram目录,等依赖和编译完成,先做两件事:改request.js里的BASE_URL,再确认 AppID。

本地联调时BASE_URL用http://localhost:8080/api就能通,但要注意微信开发者工具里默认勾选了“不校验合法域名...”,这个选项在本地开发时一定要开着,否则请求直接被拦。真机预览时localhost就失效了,因为手机访问的是你的电脑,要把BASE_URL改成电脑在局域网里的 IP,比如http://192.168.31.24:8080/api。这是新手最容易卡住的点:模拟器里好好的,一上真机全部请求失败,报网络错误。

AppID 的问题分两种。用测试号时,开发者工具顶部会有“测试号”标识,部分需要用户授权的能力不可用;用你自己的 AppID 时,要保证该小程序账号主体状态正常。微信小程序年审这个事经常被忽略:个人主体的小程序每年都要年审,如果账号到期未审,AppID 会被限制使用,登录态和请求都会出问题。你要做的就是在答辩前一周确认 AppID 还能正常编译预览。

联调的标准动作是:在小程序端触发一次登录,看后端控制台有没有收到请求、数据库t_user表里有没有多出一条记录。收到请求且数据落库,说明三层链路已经通了,剩下的是业务逻辑的逐个验证。

4. 核心业务逻辑的代码实现:排班生成与挂号锁号

4.1 排班生成:管理端最核心的录入逻辑

排班生成是管理员的活,放在管理后台的排班管理页面里。这个页面一般是这样一条链路:先选科室,再选医生,然后设定日期段和每天号源数,后端一次性生成多天的排班记录。用代码表示就是先查医生归属,再按日期循环插入:

@Service public class ScheduleService { @Autowired private ScheduleMapper scheduleMapper; public void generateSchedule(GenerateScheduleDTO dto) { // dto 包含 doctorId、startDate、endDate、total、timeSlots List<LocalDate> dates = dto.getStartDate() .datesUntil(dto.getEndDate().plusDays(1)) .collect(Collectors.toList()); for (LocalDate date : dates) { for (Integer slot : dto.getTimeSlots()) { Schedule schedule = new Schedule(); schedule.setDoctorId(dto.getDoctorId()); schedule.setDeptId(dto.getDeptId()); schedule.setWorkDate(date); schedule.setTimeSlot(slot); schedule.setTotal(dto.getTotal()); schedule.setRemain(dto.getTotal()); schedule.setStatus(0); try { scheduleMapper.insert(schedule); } catch (DuplicateKeyException e) { // 唯一索引 uk_doc_date_slot 防重复 log.warn("排班已存在,跳过: doctorId={}, date={}, slot={}", dto.getDoctorId(), date, slot); } } } } }

这里的datesUntil是 Java 8 之后LocalDate自带的生成日期序列方法,左闭右开,所以末尾要plusDays(1)。timeSlots是传入的时段列表,比如[1, 2]表示上午下午各放一轮号。捕获DuplicateKeyException是为了让重复生成不中断整体流程,这在管理员误操作时能保住已生成的数据。

排班接口的返回值设计也有讲究。生成完成后,前端要能立即看到最新排班列表,所以这个接口返回生成数量和跳过数量最合理,页面据此提示“成功生成 20 条,跳过 3 条”,比单纯提示“操作成功”信息量大得多。

4.2 挂号接口的幂等与锁号处理

挂号是整个系统里最要小心的操作。用户点“确认挂号”时,前端会向后端提交scheduleId和userId,后端要做两件事:把remain减 1,同时插入一条预约订单。这两个动作必须在一个事务里完成,否则会出现“订单插进去了但号源没扣”或者反过来的脏数据。

常见源码里有两种处理方式。第一种是简单做校验再更新:先查remain > 0,再执行UPDATE t_schedule SET remain = remain - 1 WHERE id = ? AND remain > 0,然后插入订单。第二种更稳妥,直接依赖数据库的行锁和受影响行数:

@Transactional(rollbackFor = Exception.class) public AppointmentResult book(AppointmentDTO dto) { // 1. 原子扣减号源:受影响行数为 0 说明号没了 int updated = scheduleMapper.decreaseRemain(dto.getScheduleId()); if (updated == 0) { throw new BusinessException("该时段已约满"); } // 2. 查询排班信息用于生成订单 Schedule schedule = scheduleMapper.selectById(dto.getScheduleId()); // 3. 插入挂号订单 Appointment appointment = new Appointment(); appointment.setOrderNo(generateOrderNo()); appointment.setUserId(dto.getUserId()); appointment.setScheduleId(schedule.getId()); appointment.setDoctorId(schedule.getDoctorId()); appointment.setAppointDate(schedule.getWorkDate()); appointment.setTimeSlot(schedule.getTimeSlot()); appointment.setStatus(0); appointmentMapper.insert(appointment); return new AppointmentResult(appointment.getOrderNo(), schedule.getRemain()); }

对应 mapper 里的那句关键 SQL 是:

UPDATE t_schedule SET remain = remain - 1, status = IF(remain - 1 <= 0, 2, status) WHERE id = #{scheduleId} AND remain > 0

这段逻辑的精髓在于:remain > 0放在 WHERE 条件里,数据库层面天然保证了不会扣成负数。两个用户同时抢最后一个号,数据库的行锁会让后执行的那条UPDATE受影响行数为 0,接口直接抛“已约满”,不会出现超卖。毕设答辩问“你怎么处理并发多个人同时挂号”,把这段逻辑讲出来,再补一句“靠数据库行锁和条件更新,而不是靠 Java 代码里的 if 判断”,基本就过关了。

@Transactional(rollbackFor = Exception.class)这个注解要带rollbackFor,否则 Spring 默认只在抛出RuntimeException时回滚,自定义的BusinessException如果不继承运行时异常,事务不会生效。你可以在答辩时说这是“事务边界的一个细节”,老师会觉得你确实写过。

4.3 我的挂号列表与取消挂号的状态流转

用户端“我的挂号”页面展示三类状态:待就诊(已预约)、已取消、已完成。对应t_appointment的status字段:0 是已预约,1 是已取消,2 是已完成。

查询“我的挂号”时,因为订单表里已经冗余了doctor_id、appoint_date、time_slot,小程序端只需要再 join 一次医生表拿姓名和职称就能拼出完整列表。如果源码里这个接口没有冗余字段,你会发现 SQL 很长、join 三张表,并且排班改期后历史订单也跟着变,这都是设计缺陷,答辩被问到可以主动提“我这里做了字段冗余”。

取消挂号的逻辑更考验细节。用户能取消的前提是还没到就诊日期,并且至少提前半天;取消时要改两处数据:订单status置 1,排班表remain加回 1。这两步同样要放在事务里,不然会出现“订单取消了但号源没释放”,后面的用户永远挂不上这个号。

@Transactional(rollbackFor = Exception.class) public void cancel(Long appointmentId, Long userId) { // 先锁定订单行,防止重复取消 Appointment appointment = appointmentMapper.selectByIdForUpdate(appointmentId); if (appointment == null || !appointment.getUserId().equals(userId)) { throw new BusinessException("订单不存在"); } if (appointment.getStatus() != 0) { throw new BusinessException("当前状态不可取消"); } if (appointment.getAppointDate().isBefore(LocalDate.now())) { throw new BusinessException("已过就诊日期,无法取消"); } // 释放号源 scheduleMapper.increaseRemain(appointment.getScheduleId()); // 订单置为已取消 appointment.setStatus(1); appointmentMapper.updateById(appointment); }

selectByIdForUpdate里用SELECT ... FOR UPDATE把订单行锁住,是为了防止两个请求同时走到“取消”分支,导致余号重复加回。实际上小程序端不太容易触发这种并发,但多写这一层,答辩讲“乐观锁和悲观锁”的区别时就有活例子。

状态流转这里,答辩时建议画一条线辅助说明:已预约 -> 就诊日到达自动变已完成(由定时任务或被动判断实现);已预约 -> 已取消(由用户操作实现)。源码里如果是被动判断(查询时算状态),就直说;如果有@Scheduled定时任务把过期订单状态刷成已完成,这在论文里单独是一节亮点。拿到源码后先确认这个系统用的是哪种方式,因为论文的功能模块描述里必须写清楚。

5. 部署与调试避坑:答辩前最容易翻车的 5 个问题

5.1 MySQL 8.0 的 SSL 连接错误与驱动类名不一致

现象:后端启动时报java.sql.SQLNonTransientConnectionException: SSL connection error或者ClassNotFoundException: com.mysql.jdbc.Driver。

原因:MySQL 8.0 默认开启 SSL,连接串里没加useSSL=false;或者application.yml里驱动类名写的是旧版com.mysql.jdbc.Driver。

解决:把url改成jdbc:mysql://localhost:3306/hospital?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8,driver-class-name改成com.mysql.cj.jdbc.Driver。改完重启后端,看到数据源初始化成功的日志就过了。

5.2 小程序真机预览全部请求失败,模拟器却正常

现象:开发者工具里页面渲染正常,点登录、点挂号都通;一换真机预览,所有请求秒报request:fail,页面白屏或提示网络异常。

原因:BASE_URL写的localhost,手机访问的是自己,不是你的电脑;加上真机预览模式下“不校验合法域名”的选项不生效。

解决:查电脑局域网 IP(macOS 用ipconfig getifaddr en0,Windows 用ipconfig),把BASE_URL改成http://192.168.x.x:8080/api,手机和电脑连同一个 Wi-Fi,再重新编译预览。如果后端配了冷却、拦截器等安全配置,还要确认局域网 IP 没有被拦截规则挡住。

真机预览的另一个隐藏坑是请求域名校验。微信对于http://明文请求,在真机上通常直接拦截,即便开了“不校验合法域名”,某些特定接口也会被吞掉。本地演示可以用局域网 IP,但如果要发布体验版,就必须用 HTTPS 且在小程序后台配置合法域名,毕设答辩一般到真机预览为止,这一步要心里有数。

5.3 挂号余号变负数或者订单重复插入

现象:用户快速连续点击“确认挂号”,最终数据库里出现两条相同排班 ID 的订单,或者remain变成负数。

原因:前端没有防重复点击,后端又没做幂等控制。两个并发请求同时进接口,都通过了if remain > 0的判断,然后各自执行了插入。

解决:把扣减号源的 SQL 改成UPDATE t_schedule SET remain = remain - 1 WHERE id = ? AND remain > 0,靠受影响行数判断是否放行;前端在提交时加一个submitting标志,请求期间按钮置灰。改完之后再次连续点击,数据库里最多只会有一单成功。

5.4 数据库中文乱码,插入后显示问号

现象:注册用户、添加医生时,中文名称在管理后台或小程序端显示成???,英文数字正常。

原因:建库时字符集不是utf8mb4。MySQL 默认latin1或建库脚本里漏写了CHARSET=utf8mb4,而连接串里虽然写了characterEncoding=utf8,但utf8在 MySQL 里并不支持所有四字节字符。

解决:重建数据库,建库语句显式指定:

CREATE DATABASE hospital DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

或者直接改现有库的默认字符集:

ALTER DATABASE hospital DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

同时确认连接串里characterEncoding=utf8保持不动(在 Java 侧配 utf8 即可,MySQL 会映射到 utf8mb4)。改完库,已经乱码的历史数据要么删掉重录,要么用CONVERT转换,毕设数据量小,直接重导脚本更省事。

5.5 8080 端口被占用,后端一直起不来

现象:IDEA 启动日志报Port 8080 was already in use,但前面 5.1 到 5.4 都排查过了。

原因:本机有别的进程占了 8080,或者上次后端没关干净。

解决:先查占用再决定杀进程或改端口。

# macOS / Linux:查占用 8080 的进程 lsof -i :8080 # Windows netstat -ano | findstr :8080

查到 PID 后直接kill -9 PID杀掉,这是最高效的做法。如果你更想改端口,把application.yml里server.port改成 8081,同时记得小程序端BASE_URL里的端口也要一起改,漏改的话又是一个“前后端怎么连不上”的假象。

这个端口问题在答辩现场特别容易手忙脚乱。老师一坐下,你说“稍等我启动一下项目”,结果端口被占卡了五分钟,印象分直接打折。我自己的习惯是:提前一天专门演练一遍完整启动顺序——开 mysql、起后端、开小程序开发者工具、真机预览,每个步骤 2 分钟内要完成,全流程控制在 5 分钟以内。

5.6 AppID 失效导致预览和请求权限异常

现象:开发者工具能编译,但真机预览提示“无法获取用户信息”或者体验版打不开,控制台报invalid appid类错误。

原因:个人主体微信小程序年审过期,或者源码包里写的 AppID 是原作者的小程序,你没有权限使用。

解决:在微信公众平台登录自己的小程序账号,检查主体状态和年审是否正常;源码如果用的是别人 AppID,改成自己的。这块有个前置条件:注册小程序账号是免费的,但个人主体每年要年审,答辩前两周最好就确认好,不要等到前一天才发现账号停了。

6. 从能跑到能答辩:给这套源码做增量验证和演示铺垫

源码能跑只是及格线,答辩时让老师觉得“这系统是你自己做的”才是目标。我每次带毕设都提醒一句话:代码里可以留着瑕疵,但你需要知道瑕疵在哪,并且能讲出为什么这样收口。老师看重的不是你写得多完美,而是你对自己代码的掌控程度。

一个很实用的增量验证方案是:准备一套演示数据,专门覆盖“预约、取消、再预约、约满”四种状态。具体做法是先定义一个专门演示的医生账号和一个患者账号,用t_schedule手动插入未来三天的排班,其中某一天的某个时段remain设成 1,这样演示时现场点一次挂号,就能给老师展示“成功挂号、余号变 0、再点提示已约满”的完整链路。这套数据脚本要单独放在项目的sql/demo_data.sql里,别跟初始化脚本混在一起,这样论文里还能写一笔“本系统提供演示数据脚本,便于功能验证”,这比空谈测试用例真实得多。

验证接口是否还有隐藏问题时,我习惯用 Postman 把主要接口按业务顺序打一遍:注册、登录、查科室、查医生、生成排班、挂号、取消挂号。任何一步返回异常,都在答辩前解决。用curl也可以,但 Postman 的集合导出截图可以直接放到论文的测试章节里,一举两得。

答辩现场演示有个固定套路:先打开数据库让老师看到表结构,再开后端接口文档或 Postman 展示接口,最后才开小程序走业务流程。很多人一上来就点页面,老师一头雾水,其实先花 30 秒说清楚“这是患者端、这是管理端、数据存 MySQL”,后面再演示就不会被中途打断追问基础问题。这个小顺序,我用过很多次,省掉了大量答辩危机。

最后一个提醒:很多源码包里的 LW 文档是当年作者写的原稿,你可以读它、参考它的结构和测试数据,但不要直接交。论文的一次查重就能把它暴露。真正稳妥的做法是把文档当成需求说明书,按自己的系统实现重新组织结构、画自己的流程图和时序图,代码截图换成自己跑通的截图。毕竟答辩现场的演示和追问只能靠你自己,文档写得再漂亮,不如能在现场打开系统走一遍流程。

这行我踩过太多同龄人的坑,习惯是每次拿到毕设源码,先花一晚上把整体结构摸清、把数据库脚本重新导一遍,再决定从哪开始改。你只要愿意在这一周里多花点时间验证,不把“能打开”当成“能毕业”,答辩台上你最稳。希望这篇能帮到你,祝顺利。

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

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

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

立即咨询