微信小程序+Spring Boot:医院核酸检测预约系统毕业设计实战复盘
基于微信小程序实现医院核酸检测服务系统从选题到落地的完整实战复盘每年到了毕业设计的季节总有学弟学妹过来问我小程序方向好不好做医院类系统会不会太复杂我的回答一直是选微信小程序 医院服务这个交叉方向踩中了好几个评审加分项——前后端分离架构、真实业务场景、移动端交互设计、数据状态流转。这套基于微信小程序实现的医院核酸检测服务系统就是一个典型样本用户端预约下单、现场核销采样、报告回传查看加上后台的管理与统计一条完整的业务闭环。这篇内容我会从选题价值、技术选型、数据库设计、核心流程实现到部署踩坑、论文结构完整复述一遍做这套毕设项目的思路和过程希望能给正在选型或正在卡壳的你一些参考。这套系统适合谁来参考如果你打算做 Java 方向、又想在移动端有所展示或者已经有 Web 开发基础、想往小程序方向横向扩一下都可以把这个选题作为蓝本。项目整体难度控制在中等偏上一点既不会让你两个月都写不完也不会因为太简单被答辩老师追问到无话可说。后面我提到的所有设计、表结构、接口划分都是我实际从零搭过的路径并非空谈理论。1. 选题拆解为什么毕业设计选核酸检测预约能拿高分1.1 从评审角度看这个选题的加分点毕业设计答辩时评委老师通常不会真的把每一行代码读完他们看的是几个关键维度系统是否存在真实业务问题、功能是否完整、数据流转是否闭环、有没有技术亮点。核酸检测服务系统恰好每个点都踩中了。先说业务闭环用户在小程序端注册登录、选择检测类型、预约时间段、生成预约订单到院后扫预约码完成签到核销采样完成后样本进入流转状态实验室上传报告后用户在小程序里查看结果。这个链条天然包含预约 → 支付 → 核销 → 检测 → 报告五个环节比单纯做一个增删改查的图书管理系统有血有肉得多。再说技术覆盖面。一个哪怕只做到中等完成度的系统也会涉及微信授权登录与 openid 机制、RESTful API 设计、数据库多表关联查询、订单状态机控制、二维码生成与识别、接口统一鉴权。这几样东西随便展开两三点答辩时都有充分的技术讨论空间。1.2 系统定位与角色划分这个系统我最终落地的形态是小程序客户端 Web 管理后台双端结构本质上是同一个后端服务在两个端面上的投射。系统的用户角色分三类普通用户通过微信授权进入小程序进行预约登记、数据填报、支付、查看报告和检测记录。采样人员使用后台或采样端对预约二维码进行核销录入采样管编号启动检测流转。系统管理员维护院区、检测类型、公告信息查看预约总览和统计图表处理异常订单。角色划分建议按用户、采样人员、管理员三种不必再细分到科室等层级。毕业设计最忌模型过重权限只要分清前台用户和后台工作人员两个大域业务上就不会打架。1.3 项目的合理工作量与范围边界在这里我要给所有打算做这个题目的同学一个非常明确的建议不要试图做真正的支付系统。小程序端对医院类目的支付资质要求非常高个人或普通学生根本申请不到微信支付接口权限。更合理的做法是做成模拟支付——前端生成一条支付成功的假回调后端记录订单支付状态而在论文里你只需说明正式环境中应接入微信支付统一下单接口。答辩时主动讲清楚这个替代逻辑反而会体现你对真实工程环境的理解。另一个边界是真实医院的核酸检测会涉及 LIS实验室信息系统对接难度极大。毕设层面我们只需内置一个报告上传入口由管理员录入检测结果文件即可不必编写 LIS 对接协议。这样既保留了业务完整性又避开了无法落地的外部系统依赖。2. 技术栈定型和项目整体架构2.1 前后端选型时的几个现实考量前端框架的选择上我只推荐两条路原生微信小程序或者 uni-app。如果你后续有打算把代码迁移到其他移动端比如支付宝小程序或 Appuni-app 会更香但如果只求稳定、少踩编译器坑就用原生小程序 Vant Weapp 组件库。我这次用的是原生小程序理由是毕业设计评审更认原生实现而且微信开发者工具本身配套已经很成熟。后端这里我在 Spring Boot 和 SSM 之间犹豫过一阵。最后选了 Spring Boot MyBatis Plus理由很朴素Spring Boot 的自动配置让环境搭建成本降到最低MyBatis Plus 的单表 CRUD、分页插件和逻辑删除特性可以省下大量重复代码。你如果对 SSM 更熟也不是不行只是 SSM 的 XML 配置和 jar 包版本兼容问题会让你在最不该浪费时间的阶段浪费时间。数据库就用 MySQL 8.0Redis 可以作为选装项。项目初期可以不引入 Redis用 Spring Session 存登录态后续如果想让 token 有过期刷新机制再引入 Redis 也不迟。2.2 后端工程结构怎么组织工程结构直接决定后面写论文时系统设计章节的展开方式。我是按常见的职责分包nucleic-acid-server/ ├── controller/ # 接口层接收参数并做基础校验 ├── service/ # 业务层处理预约状态、订单流转等 ├── mapper/ # MyBatis Plus 的 Mapper 接口 ├── entity/ # 数据库实体类 ├── dto/ # 接口入参数据传输对象 ├── vo/ # 接口返回视图对象 ├── config/ # 全局配置拦截器、WebMvc配置 ├── utils/ # 通用工具JWT、二维码生成、日期处理 └── common/ # 统一返回结果、异常处理、常量定义这里有个很多同学会忽略的点接口层与业务层之间务必用 DTO 作为入参不要直接把 Entity 暴露给前端。原因很简单——前端传的字段和后端表结构大概率不是一一对应的比如预约创建时前端传的是testTypeId和timeRange而表里存的是test_type和appointment_time。中间加一层 DTO 做适配会省掉你后期成堆的字段 null 判断。另外在返回结构上我从一开始就定义了一个统一响应类ResultTpublic class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } public static T ResultT error(Integer code, String message) { ResultT result new Result(); result.setCode(code); result.setMessage(message); return result; } }小程序端统一用code判断接口是否成功再也不用各自定义一套状态码。这也是很多企业级项目的标配写法答辩时把这条讲出来比较提气。2.3 小程序端页面与目录规划小程序前端我按底部 Tab 功能子页两层结构规划pages/index/首页展示公告、检测类型入口、预约按钮pages/appointment/预约填写页选院区、日期、时间段、人员类型pages/order/我的订单按状态筛选待签到、已采样、已出报告pages/report/报告详情展示 PDF 或报告卡片pages/user/个人中心登录信息、常见问题每个页面内部的逻辑尽量薄不要把复杂判断堆在onLoad里。预约页尤其要注意日期选择器的联动——日期一变可预约时间段就需要重新通过接口获取这个联动逻辑建议单独封装成一个方法避免代码写在onChange回调里一大坨。3. 用户端与后台管理的业务流程拆解3.1 用户端核心链路从登录到报告查看用户第一次打开小程序时先用wx.login拿到临时 code发给后端换取 openid。这里提醒一句wx.login的 code 有效期只有 5 分钟且只能用一次所以接口设计上必须由后端根据 code 调微信接口换取 session_key 和 openid然后由后端自行生成业务 token 返回给小程序。不要把 openid 明文存在前端缓存里那是一种省事但很业余的做法。预约主流程是这样的用户进入预约页选择单人检测或混检、采样院区、预约日期。系统根据选定日期返回当天各时间段余量。这里后端要做的是可用量 配置上限 - 已预约数而不是简单返回全部时段。用户提交预约填写姓名、身份证号、手机号生成预约单。预约成功后前端跳到预约成功页展示预约二维码同时后端生成一条带核销标识的加密串。报告查看是用户最关心的环节。我的实现方式是在样本状态变为已完成时由后台扫描并推送报告数据到报告表同时生成 PDF 文件存储到本地磁盘或 OSS小程序端通过文件地址预览。为了用户体验在报告还未发布时订单页会显示检测中状态并定时轮询每 10 秒请求一次订单详情接口一旦状态变化就自动刷新。3.2 后台管理端的核心操作流管理后台我是用 Vue Element UI 搭建的但这不是重点重点是后台的几条业务操作流预约审核流管理员可以查看当日预约列表异常状态可取消预约并释放名额。采样核销流用户到现场出示二维码采样人员用手机或扫码枪扫描核销成功后该预约状态变为已采样。核销接口必须做防重处理——同一个预约串不能核销两次。报告发布流实验室检测完成后管理员选择样本编号上传报告系统自动把报告状态置为已发布并记录发布时间随后小程序端轮询到状态变化。后台统计这一块别忽略。我没做复杂的 BI 图表只用了 ECharts 按日柱状图展示近 7 天预约趋势、按检测类型做饼图统计占比。这两个图足以撑起论文里系统测试与应用展示那部分的内容再多的就会显得画蛇添足。3.3 状态机设计预约单的每一步都要有迹可循这是整个系统最值得展开讲的技术点。预约单的状态不能只用一个字段硬改我设计了一套状态枚举状态值含义允许进入下一状态0待签到11已采样2, 42已出报告33已完成无4已取消无对应的 Java 枚举public enum OrderStatus { PENDING(0, 待签到), SAMPLED(1, 已采样), REPORTED(2, 已出报告), FINISHED(3, 已完成), CANCELED(4, 已取消); }每次状态流转都必须在代码中校验前置状态比如只有SAMPLED才能进入REPORTED如果当前状态是PENDING却收到上传报告请求就应该直接抛异常。这个设计的好处不只在逻辑严谨——论文里你可以专门画一张状态流转图作为系统设计的亮点之一。4. 数据库表设计关键表和字段的取舍逻辑4.1 核心表的全局设计思路先看整体设计思路。我把表分为三个域用户域用户表、预约域预约表、订单表、检测域样本表、报告表、公告表各域通过外键关联。这样划分的优点是领域边界清晰后续统计查询也方便。4.2 用户表与预约表用户表CREATE TABLE user ( id bigint(20) NOT NULL AUTO_INCREMENT, openid varchar(100) NOT NULL COMMENT 微信openid, nickname varchar(50) DEFAULT NULL, phone varchar(20) DEFAULT NULL, id_card varchar(30) DEFAULT NULL COMMENT 身份证号, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;openid一定要加唯一索引因为它是用户登录映射的根。手机号和身份证号是在预约时才更新的不需要在注册时强制填写——用户体验上不做强制数据上就不会因为用户临时退出而多出一堆半成品。预约表CREATE TABLE appointment ( id bigint(20) NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 预约编号, user_id bigint(20) NOT NULL, test_type tinyint(4) DEFAULT NULL COMMENT 1单人 2混检, hospital_id bigint(20) DEFAULT NULL, appointment_date date DEFAULT NULL, appointment_time varchar(20) DEFAULT NULL COMMENT 如08:30-09:00, status tinyint(4) DEFAULT 0, qr_code varchar(255) DEFAULT NULL, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;order_no是业务主键用户查询单号时用的就是它我生成的格式是NA yyyyMMdd 四位随机数。不建议用数据库自增 id 直接展示给用户——这个细节看起来小但在答辩时能展示出你有工程意识。4.3 报告表与公告表报告表CREATE TABLE report ( id bigint(20) NOT NULL AUTO_INCREMENT, appointment_id bigint(20) NOT NULL, result_type tinyint(4) DEFAULT NULL COMMENT 1阴性 2阳性 3待复查, file_url varchar(255) DEFAULT NULL, department varchar(50) DEFAULT NULL COMMENT 检测科室, doctor_name varchar(20) DEFAULT NULL COMMENT 审核人员, publish_time datetime DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有一点要注意result_type我用了数字枚举而不是直接存汉字。这既省空间也方便做条件查询展示时再通过字典映射成阴性/阳性/待复查。另外报告表不冗余存用户姓名和身份证因为通过appointment_id关联即可查全链路信息完全不需要反范式设计。公告表就简单很多id,title,content,create_time,sort。后台发布公告后小程序首页展示前三条按sort倒序取即可。4.4 时间段并发预约的唯一约束预约时间段有一个非常典型的并发隐患同一时间段名额只剩 1 个多个用户同时提交怎么办最稳妥的兜底方案是在预约表上加联合唯一索引UNIQUE KEY uk_date_time_hospital (appointment_date, appointment_time, hospital_id)这样哪怕业务层已经做了名额判断数据库层面也不可能出现两条完全相同的时段预约记录。双保险的意义在于业务层的校验可能因为高并发下的时间差失效但数据库的唯一索引永远兜底。这一点是生产环境里非常实际的玩法写在论文里也算一个不错的技术难点攻克。5. 预约核心链路实现状态机、二维码与报告推送5.1 微信登录换取 openid 的接口细节小程序端调用wx.login()拿到临时 code然后通过后端接口换取 openid。后端这段逻辑如下PostMapping(/user/login) public ResultUserVO login(RequestBody LoginDTO dto) { String url https://api.weixin.qq.com/sns/jscode2session?appid appid secret secret js_code dto.getCode() grant_typeauthorization_code; // 使用 RestTemplate 请求微信接口解析 openid 和 session_key String openid parseOpenid(url); User user userService.findOrCreate(openid); String token JwtUtil.generateToken(user.getId()); return Result.success(new UserVO(user, token)); }前端拿到 token 后存入wx.setStorageSync每次请求在 header 里带上Authorization: Bearer token后端做一次拦截器校验即可。JWT 的有效期我设成 7 天超过了就提示用户重新登录不用搞复杂的刷新逻辑。毕设阶段JWT 这套方案已经明显优于 session 方案主要是无状态、不需要在 Redis 里维护会话后端水平扩展时也友好。5.2 二维码生成与核销校验预约成功后的核销串我建议这样生成pre orderNo _ userId _ status然后对这个字符串做 MD5 校验加上订单号一起拼成一个核销码字符串。再通过 Google 的 zxing 库生成二维码图片存储为一个 Base64 字符串返回给前端渲染或者存图片地址由前端加载。核销接口需要做的事解析二维码内容拿到 orderNo 和 校验串。对 orderNo 重新生成校验串与前端传来的校验串比对。查预约记录判断状态是否为PENDING待签到。状态置为SAMPLED同时可能给用户推送一条服务通知。防重核销的关键在第二步和第三步缺一不可。别看简单我在实际开发中见过好几个同学因为只做了第三步导致同一个二维码可以反复核销闹出过不少笑话。5.3 报告状态轮询与推送小程序端要实时看到报告结果可以用 WebSocket也可以用轮询。毕设项目里用轮询完全够——每 10 秒请求一次订单详情接口共 6 次后停止。这块的设计成本极低却让用户体验上升一个档次值得写进论文。另外一个更工程化的选择是订阅消息推送。微信小程序可以给用户发送一次性订阅消息前提是用户主动订阅。可以做成这样用户提交预约后弹出是否允许接收结果通知一旦允许报告发布后就能收到微信服务通知。但订阅消息的模板申请需要小程序类目支持医疗类目个人主体无法开通。所以毕设中还是以轮询为主订阅消息作为扩展点叙述即可。6. 部署与联调中的高频坑位记录6.1 本地开发绕不开的合法域名问题这是每个小程序新手必踩的坑在微信开发者工具里请求本地接口默认会被拦截报错是不在以下 request 合法域名列表中。解决办法有两个开发期在微信开发者工具右上角详情 → 本地设置里勾选不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书或者直接把 request 的域名在公众号/小程序后台配置为合法域名。前者适合本地联调后者需要你有已经备案的域名。注意真机预览时如果还勾着不校验合法域名大部分安卓机可以跑通但 iOS 端有时会不生效。所以最终要发布体验版或者用于演示最好还是配一个真实域名 HTTPS 证书这是最稳妥的方式。6.2 开发工具的版本兼容陷阱小程序的基础库版本差异会直接影响 API 可用性。比如wx.getUserProfile接口在旧版本 SDK 中是拿不到用户头像昵称的现在已经被官方逐步收紧如果你在page.json里没有声明对应的 permission 字段弹窗授权也会异常。我的经验是开发时基础库调到 2.30.0 以上并在app.json里明确配置一个最低基础库版本低于该版本的手机走降级提示。这个处理虽然只有几行配置但能有效避免答辩现场因为学生的手机基础库过低出现白屏事故。6.3 后端跨域与文件路径问题如果你管理后台用 Vue业务接口用 Spring Boot跨域是逃不掉的。我建议别在单个 Controller 上加CrossOrigin那是治标不治本而是在 WebMvcConfig 里做全局跨域配置Configuration public class WebConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE) .allowCredentials(true) .maxAge(3600); } }注意allowCredentials(true)时allowedOriginPatterns不能直接用*号Spring 5.3 之后会校验用模式串更保险。文件路径方面报告 PDF 我存到服务器本地目录通过一个虚拟映射/files/**映射到磁盘路径避免静态资源被拦截器拦截导致前端加载不到文件。6.4 身份证校验与日期处理预约时如果不对身份证做校验会出现乱填的脏数据。前端用正则、后端用javax.validation写自定义注解校验。日期处理上我强烈建议所有表字段用date/datetime类型Java 侧用LocalDate/LocalDateTime避免使用java.util.Date和SimpleDateFormat的时区问题。这虽然是基础问题但很多同学在答辩现场被问到时区怎么处理时答不上来提前用LocalDate系列就不用担心了。7. 毕业论文的章节安排与素材留存7.1 论文结构的参照框架如果你做的是这套系统论文结构可以直接参照下面的框架这个结构与系统开发节奏是对应上的绪论研究背景与意义、国内外现状、论文结构安排。相关技术介绍微信小程序开发框架、Spring Boot、MyBatis Plus、MySQL。需求分析从业务流程出发画用例图、划分角色权限给出功能需求和非功能需求安全性、性能要求。系统设计总体架构图、模块设计、数据库设计、接口设计。系统实现按用户端功能、后台管理功能分小节配截图和核心代码片段。系统测试功能测试用例表、测试结果、兼容性测试、性能简述。总结与展望已完成的工作、不足与改进方向。这里面最容易被老师翻的重点是需求分析和系统设计两章——前者要画出完整的用例图后者要给出清晰的数据库 ER 图和状态流转图。平时开发过程中就要有意识地把这些图留下来不要等到论文季再补那时候补图的成本非常高。7.2 让论文内容更扎实的三个技巧第一核心代码不要在论文里整段贴长代码只贴最关键的 10 到 15 行比如状态校验、并发防重这类的代码片段配合小段文字解释逻辑即可老师看的是你懂不懂这段代码而不是代码贴了多少页。第二每个功能模块至少配一张运行截图截图最好统一分辨率、统一操作路径否则排版出来非常难看。建议每完成一个小模块就随手截图存档这是很多同学后悔没有提前做的事。第三测试章节别只写测试通过要写清楚测试环境、测试数据和预期结果。比如预约时段唯一性测试并发提交 5 次请求库中仅产生 1 条记录这种描述比一句带过有力得多。7.3 源码与论文配套的目录服务设计整个工程的目录建议分三层前端小程序包、后端服务包、文档资料包。文档包里再放数据库脚本、演示视频、答辩 PPT。这样做的好处有两个第一你自己后期维护和对照代码写论文都很方便第二如果指导教师要求以压缩包形式提交目录规范本身就是加分项。我当时就是把这些目录整理得很清楚指导老师直接拿来当作班级的标准示例省去了后续反复沟通的麻烦。8. 答辩环节容易被追问的几个问题与应答思路8.1 为什么不用现成的预约平台而要自研一套这个问题考的是需求分析能力。应答思路医院内部的检测业务存在多院区、多类型的差异通用平台的预约流程无法满足定制化的时段配置和报告回传要求自研系统可以灵活控制表结构、扩展字段和状态流转逻辑也更便于接入医院后续的其他信息化系统。可以再补充一句系统设计时预留了扩展接口就能堵住追问。8.2 用户信息安全如何保证身份证号、手机号属于敏感信息。应答思路数据库层面可以对身份证号、手机号做加密存储比如 AES接口层只做脱敏展示110***********1234传输层依赖 HTTPS 加密后端通过拦截器统一鉴权对越权访问返回 403。这四点说出来基本就能覆盖大多数追问。8.3 如果并发量上升到几千系统瓶颈在哪里这是开放性追问主要看你有没有全局思维。应答思路第一当前 MySQL 单库单表在几千并发下性能可能出现瓶颈可以引入 Redis 缓存热点数据如时段余量、公告列表第二预约时段接口的数据库唯一索引可以降低重复预约风险但更好的方案是引入分布式锁第三静态资源报告 PDF、图片可以迁移到 OSS并配置 CDN 加速。能把这三点讲清楚这个问题的分数就稳了。9. 复盘做完这套系统后我更推荐你去扩展的三个方向如果你做完基础版本还留有富余时间我的建议是先别急着加花哨功能优先做这三件事第一给系统加上简单的数据看板。预约量趋势、各检测类型占比、各时间段饱和度这些维度用 ECharts 展示出来后系统质感立马不一样论文里也多了一张很有说服力的应用截图。第二把订单模块改成真实的模拟支付 订单超时取消。用户预约后 15 分钟不支付系统自动释放名额这个机制需要用到定时任务或延迟队列体现的是真实业务中的超时一致性问题。第三给报告模块增加 PDF 导出和批量打印功能。这个功能不复杂但能体现对实际业务场景的理解——医院需要定期存档检测记录不只是给单个人看单个人的报告。我在实际完成这套系统后最大的感受是毕业设计的核心价值不在于做出一个完美可用的商业系统而在于通过一个完整的业务闭环把大学四年分散的课程知识串成一条线。小程序端锻炼了移动端交互和联调能力后端锻炼了接口设计和数据库建模论文写作则逼着你去复盘整个工程决策的来龙去脉。所以无论这一步此刻卡在哪——是登录接口没调通还是状态流转有 bug都别急着丧气顺着链路一截一截排查答案往往就在你整理过的日志和注释里。希望这篇复盘能帮你少走几步弯路做得比我当初更顺一些。