ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于微信小程序与SSM的医院预约挂号系统设计实践

基于微信小程序与SSM的医院预约挂号系统设计实践 1. 项目概述与核心需求拆解1.1 这个项目到底要做什么医疗挂号这件事经历过的人都懂。三甲医院挂号窗口永远在排队缴费窗口更是能绕三个弯早上七点出门、八点挂号、十点看上医生这已经算效率高的。而微信小程序形态的挂号系统解决的正是“跑腿、排队、信息不透明”这三座大山让患者在家里、在地铁上、在工位上花三十秒就能挂到号、看到号源余量、知道医生排班看完病还能在线缴费、查报告。“基于微信小程序的医院挂号系统ssm”这个题目是计算机类本科毕业论文里最常见的组合之一。它拆开来看是两层东西前台是一套运行在微信生态里的预约挂号小程序后台是一套基于SSM框架的管理系统两层通过接口通信。前台解决患者“挂号难、缴费烦”的问题后台解决医院管理人员“号源维护难、数据统计难”的问题。适合参考这个项目的读者分三类第一类是正在做毕业设计、想找个稳扎稳打题目的计算机学生这套系统的技术栈不偏门、工作量可控、答辩有东西可讲第二类是打算学习微信小程序后端接口联动开发的自学者它可以作为完整的全栈练习案例第三类是医院信息科或相关创业团队的技术人员虽然生产环境的挂号系统远比这复杂但核心流程和业务模型是相通的有一定借鉴价值。1.2 为什么选微信小程序而不是App或H5在技术选型上微信小程序几乎是这类医院系统的当下最优解它是“寄生”在微信生态里的轻应用天生具备三个优势。第一是免安装。患者不需要去应用商店搜索、下载、注册、登录微信里搜一下或者扫个码就能直接使用。医院场景下用户年龄跨度极大从二十岁的学生到七十岁的老人都有让老年人去下载一个App再注册账号这个门槛会劝退一大批人。而小程序的使用成本极低——点开即用。第二是微信授权体系。微信提供 wx.login() 接口和 getUserProfile 能力用户一键授权手机号或微信身份服务端就能拿到OpenID作为用户的唯一标识。OpenID是微信生成的用户唯一标识同一个用户在同一个小程序里的OpenID是恒定的它天然适合作为预约挂号系统的用户主键不需要患者单独输账号密码体验上少了一个步骤技术上少了一张用户密码表。第三是合规性与开放能力。微信小程序有医疗类目资质审核体系合规挂号小程序可以申请开通“微信支付”用于缴费“订阅消息”用于预约成功通知和就诊提醒。这些能力闭环起来整个就医流程都能在微信内完成转化率高、留存也好。至于H5网页主要问题是入口深且没有微信原生能力比如无法稳定通过wx.login获取用户身份原生App虽然功能更强但开发和审核成本高、用户获取难度大对医院这类偏传统行业的单位来说并不是划算的选择。所以综合下来微信小程序SSM后端这套组合在毕设和工作实践中都非常典型。2. 整体架构设计与技术栈选择解析2.1 系统分层思路这类挂号系统的整体架构按功能域划分可以拆成三个端患者端微信小程序、管理端SSM后台网页、服务端接口层SSM框架提供RESTful API。患者端跑在微信开发者工具和真机上不做复杂业务逻辑核心职责是展示数据、收集用户操作、调用后端接口。管理端跑在PC浏览器上是医院工作人员的日常工作台负责科室管理、医生排班、号源配置、预约审核、数据统计。服务端是系统的中枢所有业务规则都在这层实现包括号源扣减、预约状态流转、防重复预约、数据统计等。这种三层结构的好处是职责单一、便于并行开发。学生可以先把后端接口定义好小程序端按接口文档一步步对接管理端则独立走表单页面的开发流程。答辩时无论是讲架构设计还是讲某个功能的完整链路从用户点击到数据库落库都能把来龙去脉说清楚。技术栈选型上后端Spring SpringMVC MyBatis即SSM组合这是国内Java后端最经典的组合。Spring负责Bean管理和事务SpringMVC处理HTTP请求路由MyBatis负责SQL操作。虽然Spring Boot在业界更主流但毕业设计选SSM有两个现实考量一是很多学校课程体系还在教SSM用自己学过的框架做答辩更稳妥二是SSM的配置过程web.xml、Spring配置、MyBatis配置本身能体现对框架原理的理解。数据库MySQL 8.x主流稳定支持事务医院的号源扣减和预约记录插入必须走事务MySQL的InnoDB引擎能保证这一点。前端小程序原生微信小程序。选原生而不选uniapp是因为靠这个项目完成毕设没必要额外引入一套跨端框架的学习成本原生小程序在真机调试、微信API调用wx.login、wx.request、wx.requestSubscribeMessage上最直接。前端管理端不引入Vue全家桶用简单HTML JavaScript Layui即可。管理后台本来就是给内部用的重点是功能齐全、开发效率高Layui的表格、表单、弹窗组件开箱即用。2.2 表设计与业务模型规划这套系统的数据库表核心是七张表用户表、科室表、医生表、排班表、号源表、预约表、公告表。每张表的职责和设计逻辑如下。用户表字段包括用户ID、OpenID、昵称、头像、手机号、姓名、身份证号、创建时间。这里有个关键点OpenID是平台级唯一的但用户真实身份需要另行绑定。挂号涉及实名制就医所以系统里患者第一次挂号前要弹出一个表单要求填写姓名和身份证号。身份证号要做格式校验这一步很多学生容易忽略——不做校验的话垃圾数据会直接影响后续的医院对接流程。科室表字段包括科室ID、科室名称、科室简介、所属院区等。一个医院有多个院区是常态后面扩展排班时要用到院区维度所以建议提前把院区字段加进去而不是等到后期变更表结构。医生表字段包括医生ID、姓名、性别、职称主任医师、副主任医师、主治医师、所属科室ID、简介、头像。医生表关联科室表一个科室有多个医生一对多关系。排班表字段包括排班ID、医生ID、排班日期、出诊时段上午、下午、晚间、门诊类型普通门诊、专家门诊、放号总数、已约数量、排班状态。排班表解决的核心问题是“医生哪天在哪里坐诊”一个医生一天可能有多个时段排班表一行对应一个时段。号源表这张表可以并入排班表也可以单独拆出来。拆出来的好处是预留扩展能力比如上午时段分为8:00-8:30、8:30-9:00等细分时段。业务简单时并入排班表即可用一个“剩余号数”字段做减法。预约表字段包括预约ID、用户ID、排班ID、医生ID冗余、就诊日期、时段、就诊序号、状态、创建时间、取消时间。状态字段是关键一般设计为0已取消、1待就诊、2已完成就诊后标记、3爽约未就诊且未取消。就诊序号按“该时段第几位”生成例如上午第3号会让患者心里更有底。公告表字段包括公告ID、标题、内容、发布时间。医院网站上最常见的模块就是公告停诊通知、节假日门诊安排、新医生介绍都从这里发布。索引设计上预约表要建**(排班ID, 状态)联合索引因为频繁查询“某个排班已约多少人”用户查询预约记录要建(用户ID, 创建时间)**索引。号源扣减时使用UPDATE t_schedule SET remain_count remain_count - 1 WHERE schedule_id ? AND remain_count 0这样的原子操作防超卖。2.3 接口协议与会话方案前端和后端通信统一走JSON格式的HTTP请求。接口路径设计成RESTful风格例如POST /api/user/login微信登录换取TokenGET /api/schedule/list?deptId1date2025-06-01获取排班列表POST /api/appointment/create提交预约POST /api/appointment/cancel取消预约GET /api/appointment/list我的预约列表会话方案上微信小程序通过wx.login()拿到临时code发送到后端后端调用微信的code2Session接口换取OpenID和SessionKey然后把OpenID存入用户表生成一个自定义Token可以用UUID也可以简单点用MD5(openid时间戳)返回给小程序。小程序后续请求在header里带Authorization: Bearer token后端通过拦截器验证Token有效性并从中解析出用户ID。这个方案比简单地把OpenID明文传参要稳得多能防止接口被直接抓包后伪造请求操作他人数据。很多毕设系统只在登录时做了验证后续接口全裸奔答辩时会被问到。3. 后端SSM框架落地的核心细节3.1 常用注解与配置清单SSM框架的常用注解建议按层次理清楚。控制层用Controller或RestController、RequestMapping、PathVariable、RequestBodyService层用Service、TransactionalDAO层用Repository、MyBatis的Mapper工具类或第三方组件用Component。依赖注入是另一个必问的点。构造器注入比字段注入更好。Autowired直接打在字段上虽然代码少但会导致类与Spring容器耦合单测不方便。用Autowired打在构造器方法或RequiredArgsConstructorLombok方式更好依赖关系显式化。配置上有几个容易踩的坑Spring配置文件和SpringMVC配置文件要分清楚spring-context.xml管Service、DAO、数据源spring-mvc.xml只管Controller和视图解析器两者用context:component-scan的use-default-filters配合include-filter把扫描范围切开。否则会出现Controller被Spring父容器重复加载导致事务失效的问题。MyBatis方面application.properties或jdbc.properties里配置数据源注意MySQL 8.x驱动要用com.mysql.cj.jdbc.DriverURL里加上useSSLfalseserverTimezoneAsia/Shanghai不然会报时区错误。mybatis-config.xml里开启下划线转驼峰配置setting namemapUnderscoreToCamelCase valuetrue/这样SQL里查出的dept_name能自动映射到JavaBean的deptName省去大量手工映射。3.2 号源扣减与防超卖设计预约挂号系统的核心业务是“扣号源”和“生成预约单”。如果两个患者在同一秒同时请求最后一个号用非原子操作就会出现超卖一个人挂到号、另一个人实际上也“挂到”了但数据库里没号了。正确做法是数据库层面的原子更新。伪代码如下UPDATE t_schedule SET remain_count remain_count - 1 WHERE schedule_id #{scheduleId} AND remain_count 0;UPDATE执行后返回影响行数如果影响行数为1说明扣减成功继续插入预约记录如果影响行数为0说明没有号源直接返回“号源已满”。整个操作放在一个Transactional事务里。扣减和插入预约表必须同事务要么都成功要么都回滚。这个方案比“先SELECT数量再UPDATE”高一个档次后者在并发下必然出问题。可以在答辩时主动讲述这个防超卖设计导师和评委一般都会眼前一亮。取消预约的逻辑则相反先取消预约单更新状态为0再对排班表的remain_count做加一操作。如果用户在就诊当天取消要注意医院的取消规则比如“就诊前2小时不可取消”这属于业务规则放Service层的接口里做时间判断。3.3 接口防重复提交患者手指快网络慢点击“确认挂号”按钮后页面没反应再点一次这就造成了重复请求。防重复提交在挂号系统里是硬需求。一种朴素但有效的方式是前端做按钮loading提交后按钮置灰禁止二次点击。更稳妥的是后端加防重机制。共享内存方式在单体应用里够用在ConcurrentHashMap里维护“用户ID日期排班ID”的Key预约接口处理前先putIfAbsent存在则拒绝处理完成后清除。实现简单论文里也好解释。进阶做法是引入Redis的SETNX分布式锁但SSM毕设项目引入Redis会增加配置复杂度非必要不升级。3.4 事务边界与常见异常事务边界是SSM开发里最容易被忽视的点。Transactional默认只对RuntimeException回滚检查异常比如IOException默认不回滚。你在Service层的预约方法里写了new Exception(业务错误)事务不会回滚号源扣了但预约单没生成数据就出问题了。建议在Service层方法上显式声明Transactional(rollbackFor Exception.class)。另外事务只能作用在Spring容器管理的Bean上如果自己new了一个Service对象调用方法事务不生效。拦截器里不要调用Service层的事务方法因为拦截器在SpringMVC容器中和Service的事务管理器是两个上下文。MyBatis的懒加载在SSM里默认是关闭的select查出的关联对象比如Schedule里的Doctor如果没配置association返回就是null。先在本地把SQL在Navicat里跑通再对照Mapper.xml的resultMap排查。4. 微信小程序端实现要点4.1 项目组织与页面规划小程序前端建议按页面功能拆分成五组首页/科室列表页展示医院公告、科室分类入口排班查询页选择科室、日期后列出可预约医生及号源状态预约确认页展示排班详情、实名信息确认、提交预约我的预约页展示当前登录用户的预约记录列表加载更多个人中心页用户绑定信息、就诊人管理、设置页面与页面之间通过URL参数传值比如从科室列表跳到排班查询页时带上deptId。小程序页面的onLoad(options)里接收参数再调用后端接口渲染数据。4.2 wx.login与登录态处理微信小程序登录推荐流程是wx.login({ success: res { if (res.code) { wx.request({ url: https://your.domain.com/api/user/login, method: POST, data: { code: res.code }, success: res { wx.setStorageSync(token, res.data.data.token); } }); } } });特别注意wx.login()拿到的code一次性有效且有效期很短官方文档说五分钟后端拿到code后要立即调用微信接口换取OpenID。不要在页面里缓存code。用户身份绑定逻辑上首次登录时小程序拿不到用户的手机号或姓名需要在个人中心或首次预约时引导用户填写就诊人信息。这里有两个选择一是用wx.getPhoneNumber获取微信绑定的手机号需要小程序认证且类目包含医疗二是简单做一个表单让用户手动输入。毕设场景下第二种更可控。4.3 列表加载更多与分页处理“加载更多”是列表页的基础交互在很多项目中依赖onReachBottom页码触底或点击“加载更多”按钮触发。分页参数要遵循约定current表示当前页码从1开始size表示每页条数建议10后端返回PageResult对象里面包含list、total、current、pages。小程序端维护三个变量pageNo、pageSize、loading每次请求前判断loading防止并发加载。核心实现data: { pageNo: 1, pageSize: 10, hasMore: true, list: [] }, loadMore() { if (!this.data.hasMore || this.data.loading) return; this.setData({ loading: true }); wx.request({ url: /api/appointment/list, data: { pageNo: this.data.pageNo, pageSize: this.data.pageSize }, success: res { const list res.data.data.list; const hasMore this.data.pageNo * this.data.pageSize res.data.data.total; this.setData({ list: this.data.list.concat(list), pageNo: this.data.pageNo 1, hasMore, loading: false }); } }); }这里有个体验细节不要用concat后把整个列表推到setData里就完事还可以在高频更新场景下用setData的路径写法比如this.setData({[list[ index ]]: item})来更新单条避免全量渲染导致卡顿。4.4 顶部导航栏与安全区适配小程序顶部导航栏的高度在不同机型上有差异。很多页面需要自定义导航栏比如首页的搜索框置顶此时要处理状态栏高度和胶囊按钮位置。官方方案是用wx.getWindowInfo()获取statusBarHeight状态栏高度再根据wx.getMenuButtonBoundingClientRect()拿到胶囊按钮信息计算出导航栏实际高度。const menuRect wx.getMenuButtonBoundingClientRect(); const statusBarHeight wx.getWindowInfo().statusBarHeight; const navBarHeight (menuRect.top - statusBarHeight) * 2 menuRect.height;这个navBarHeight就是自定义导航栏的总高度。直接写死高度在iPhone和安卓上会错位必须按这个公式动态计算。在开发工具里看起来是对的真机上一跑就可能顶出屏幕。4.5 订阅消息与预约通知预约成功后给用户发一条微信订阅消息能显著提升系统的“真实感”。要注意以下事实wx.requestSubscribeMessage必须在用户点击行为比如点击“确认预约”按钮之后、步骤中途调用不能是进入页面时或者预约完成后再询问。而且用户选择“总是保持以上选择”后后续弹窗就不会再出现一次性订阅消息只能发送一次。实现要点在预约按钮的点击事件里先发起wx.requestSubscribeMessage请求模板ID用户授权后再调用后端创建预约接口。后端在预约成功那一分支里调用微信“统一服务消息”接口下发订阅消息。如果用户拒绝授权预约流程照常走完只是没有通知系统设计上要允许这种回退。4.6 真机调试与分享试用代码写好后在真机上调试是必经环节。开发者工具能编译不代表真机没问题常见问题包括request的域名未配置、IP不能作为正式版请求域名开发阶段可以在详情-本地设置勾选不校验合法域名、HTTPS证书过期、某些API在低版本微信客户端不可用。小程序开发版、体验版和正式版三者的关系要搞清楚开发版仅开发者本人可见体验版需要把微信号加到项目成员列表里生成体验版二维码后其他人扫码可用正式版需要提交审核。要收集别人的试用反馈让测试者扫体验版二维码不需要经过发布审核非常方便。5. 管理端功能模块与管理台设计5.1 管理端页面规划管理端不需要复杂的前端工程化用HTML Layui AJAX就能在最短时间内完成全部功能。核心页面包括登录页、控制台数据概览、科室管理页、医生管理页、排班管理页、预约管理页、公告管理页。管理端的权限不一定要做得很重一个admin用户表登录判断即可。不要在管理端引入多角色权限系统会给毕设增加无意义的复杂度。重点是每张管理页都要有搜索、分页、增删改查这是工作量的大头也是答辩时能展示完整度的地方。5.2 排班管理里的日期与时段处理排班管理是管理端相对复杂的模块。管理员选医生、选日期、选时段上午/下午/晚间、填放号总数然后提交。这里有几个细节第一默认放号数应该按医生职称给出默认值比如专家门诊30、普通门诊50避免每次手输。第二同一个医生同一天不能有两个相同时段的排班记录后端要有唯一性校验数据库层面也可以对(doctor_id, date, period)建唯一索引。第三排班创建之后号源已经被人预约此时不应允许直接修改放号总数或删除排班必须先处理预约单。比较合理的状态机是排班有未发布/进行中/已结束三个状态只有未发布状态可改可删。5.3 数据看板与统计报表管理端首页放一个简易统计看板今日预约量、本周预约量、各科室预约占比、热门诊室Top5。这些数据用几条SQL就能查出来。展示用ECharts的CDN方式引入画柱状图和饼图视觉效果立刻上一个档次。答辩时可以拿着统计页讲“系统能辅助医院做资源调配决策”这是加分项。6. 常见问题与排查技巧实录6.1 微信小程序请求报错 fail: url not in domain list这是最常见的错误。小程序要求request的域名必须是HTTPS并且要在小程序后台配置到白名单里。在开发阶段打开开发者工具的“详情-本地设置-不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”就能在开发者工具里不配置域名直接调试。但真机上无法绕过必须配置。毕设阶段如果没有正式域名可以把后端跑在本地用内网穿透或反向代理工具把本地接口映射成https域名做临时验证。http://localhost:8080这种地址在真机上绝对不能使用小程序端请求的url必须是能公网访问的域名。这一点在论文的“测试环境”章节里要写清楚。6.2 本地接口能通但真机连不上排除了域名问题后常见原因是电脑防火墙拦截了内网端口、手机和电脑不在同一局域网、后端启动时监听的是127.0.0.1而不是0.0.0.0。用SpringMVC内置Tomcat时检查启动日志里是否显示端口绑定在0.0.0.0。真机上的调试强烈建议直接用https公网域名而不是折腾局域网。6.3 并发环境下号源变负数排查顺序先看扣减SQL是否用了WHERE remain_count 0条件再看Service方法是否加了Transactional且是否真的生效最后看是否存在多实例部署理论毕设只有单实例。如果用了“先查remain_count再update”的方式换成原子UPDATE即可。也可以给remain_count字段加CHECK (remain_count 0)约束做数据库兜底。6.4 微信登录一直失败或code无效检查后端接收code后调用微信接口时用的appid和secret是否匹配检查wx.login是否在onLoad里被调用了多次每次调用都会生成新code用了旧code会导致失败检查后端接口的编码格式微信返回的是JSON在原样转发时不要对其做二次转义。6.5 HTTP状态码与后端异常追踪前后端联调时看Network面板里请求的Status Code。404说明接口路径不匹配核对RequestMapping的value405说明请求方法不对前端POST后端写成了GET500一般是后端代码抛异常去IDEA控制台看堆栈信息。SpringMVC的全局异常处理器RestControllerAdvice提前写好所有未捕获异常返回统一格式{code: 500, msg: 系统繁忙}日志里保留堆栈这样可以避免把异常细节直接暴露给前端。6.6 小程序页面加载数据慢页面加载慢多数不是后端慢而是前端做了太多串行请求。首页如果需要同时拿科室列表和公告用Promise.all并行请求别一个回调里再嵌一个请求。另外小程序setData的数据量不要一次塞太多列表类数据做分页而非全量加载。7. 这两个环节最容易被答辩老师追问7.1 如何证明系统具备并发能力很多学生的毕业论文写“系统支持高并发”但真问起来又说不出所以然。号源防超卖是最大的并发矛盾点把「原子UPDATE 事务 影响行数判断」这套机制讲透是自证并发能力的关键。还能补充说明单体应用下用synchronized或ConcurrentHashMap做进程内锁分布式场景下才需要引入Redis或Zookeeper分布式锁。7.2 数据库表为什么这么设计每张表都有它的存在理由。排班表和预约表为什么要拆成两张因为一个排班会对应多条预约记录不拆的话会产生数据冗余。预约表里为什么要冗余医生ID和医生姓名一是为了查询我的预约时不用每次都join医生表二是预约记录应该保留医生当时的信息快照如果医生后面改了姓名历史预约记录不受影响。这就是“适度冗余提升查询性能”。8. 扩展方向和个人实践体会8.1 这套系统后续能往哪些方向扩展如果时间有余以下几个方向能显著提升项目的完整度和答辩质量接入微信支付把线下缴费搬进线上引入Redis缓存科室列表和排班信息降低数据库压力给预约模块增加消息通知队列预约成功后异步下发短信和订阅消息导出Excel报表方便医院做月度统计引入在线问诊功能让医生可以线上看图文咨询扩展系统能力。8.2 踩过几次坑后的心得我在实际开发这类系统时最大的体会是先定接口文档再写前后端代码。很多同学一边写小程序一边写后端改了前端又去改后端接口经常对不上。先花半天把接口定义好路径、方法、参数、返回值示例两边照着实现联调阶段的痛苦会少一大半。还有一件事值得提前做把后端项目的包名和类名规划清楚controller、service、mapper、entity、dto、common包名清晰后代码量再大也不会乱。最后答辩前一定要自己完整走一遍“用户注册 - 选科室 - 查排班 - 挂号 - 取消挂号 - 管理员改排班”的流程任何环节有bug都还来得及修别等到了答辩现场才发现功能跑不通。
返回列表