☰
微信小程序+Python Django医院门诊挂号系统设计与实践
2026/9/26 17:48:00 网站建设 项目流程

1. 项目概述与需求拆解

1.1 这个项目到底在解决什么问题

先聊一个让我印象很深的场景。我有个朋友在某三甲医院信息科,每天接到的投诉电话里,至少有三分之一跟挂号有关:窗口排队太久、挂错科室、号源被黄牛抢走、复诊找不到同一个大夫。这些问题的根源,其实是传统门诊流程里那套"到院-排队-窗口挂号-候诊-看诊"的线性模型,把所有人在同一时间挤进了同一个物理空间。

所以当他来找我,说想做一个"医院门诊挂号系统"时,我第一反应不是给他列技术栈,而是问了他三个问题:你的真实用户是谁?医院现有的信息化条件能支撑到什么程度?这个系统是要替代现有HIS系统,还是做增量补充?

聊完之后项目目标就很清晰了:做一个基于微信小程序的在线医患交互预约平台,实现从"查科室-看医生-约号源-在线支付-到院签到-就诊-复诊预约"的完整闭环,同时给医生端留一个简单的排班和患者视图入口。技术上用Python做后端,框架在Django和Flask里选型;小程序端用原生微信小程序开发。整个系统的技术底座,本质上是"微信生态前端 + Python Web框架后端 + 关系型数据库"的组合。

这个项目适合谁参考?三类人:一是刚学完Python基础想做实战项目的新手,能从中理解一个真实业务系统是怎么从需求落到代码的;二是在医院信息科或者医疗软件公司工作、想了解小程序端如何跟现有系统对接的从业者;三是打算接医疗类外包项目的开发者,这套流程能帮你少踩很多坑。

1.2 为什么选择微信小程序而不是App或H5

这是项目启动前最关键的决策点,直接决定后面所有开发方向。我当时给朋友画了一张对比表,这才是最终拍板微信小程序的依据。

维度微信小程序H5移动网页原生App
用户获取成本扫码即用,无需下载需保存链接或搜索应用商店下载安装
用户留存入口微信聊天列表下拉,入口明显收藏夹容易被遗忘桌面图标稳定
支付能力微信支付原生集成需额外对接需申请支付资质
开发成本一套代码适配两端低但体验受限双端分别开发
医院推广阻力低,微信生态内触达中高
消息触达订阅消息通知弱需要推送资质

对医院这种场景来说,小程序"用完即走、下次从聊天列表下拉就能进"的特性是杀手级优势。患者在大厅扫码挂完号,不用记网址、不用装App,下次复诊直接下拉微信就能找到入口。再加上微信支付让在线缴费闭环变得极其顺畅,这体验是H5给不了的。原生App虽然体验最好,但双端开发成本、医院信息科后续维护人力、患者下载意愿这三个障碍叠在一起,基本可以直接否决。

技术上另一个考虑是:小程序前端和公众号生态、企业微信体系天然互通,这为后续接入医院的导诊机器人、健康宣教推送、满意度调查留了接口。选型不是看哪个技术最酷,而是看哪个方案能在现有资源下跑得最稳、维护成本最低。

1.3 技术选型:Django还是Flask,这是个好问题

后端框架选型是这个项目里讨论最久的话题。网上有个段子:Django像五星级酒店,什么都给你配好了;Flask像毛坯房,怎么装修全靠自己。对这个项目来说,答案其实取决于你打算把系统做成什么样。

对比维度DjangoFlask
开发速度快,内置Admin后台、ORM、认证慢,核心只有路由和模板
学习曲线陡峭,概念多平缓,易上手
数据库操作ORM完善,自动建表迁移需自行集成SQLAlchemy
Admin后台开箱即用,适合运营管理无,需自己搭建
项目结构有强制规范(app模式)自由灵活
适合场景完整业务系统轻量API服务
社区生态大而全,第三方包丰富小而精

我个人的建议分两种情况。如果你要做的系统包含管理后台、号源管理、排班管理、患者档案这些完整的业务模块,直接选Django。它的Admin后台在项目早期就是神兵利器,医院科室、医生排班、号源池这些数据录入和调整,Django Admin改几行代码就给你生成一套可用的管理界面,不用从头写。但如果你后续打算走前后端完全分离的微服务架构,或者只需要给小程序端提供纯JSON接口,那Flask更轻巧,部署也简单。

这个项目最终选了Django,原因有三个。第一,医院门诊系统天然是"数据密集型"业务,Django的ORM在做号源查询、余号扣减、重复预约校验这些操作时,代码量比Flask路线少30%以上。第二,Django自带用户认证系统,虽然不能直接用,但扩展出医生、患者两种角色模型的成本很低。第三,项目可能需要二次开发的人接手,Django的规范性让任何有Python基础的开发都能快速上手,不会出现Flask项目常见的"每个人的代码风格都不一样"问题。

2. 系统核心模块设计与数据建模

2.1 门诊系统的角色模型:不止是"患者"和"医生"两个角色

很多人第一次做这类系统,习惯性地建两张表:一张用户表、一张医生表。但你真正跑一遍医院的业务流程就会发现,这个系统至少涉及四类角色,而且每个角色对系统的权限诉求完全不一样。

患者端要的是:查科室、找医生、挂今天或明天的号、支付挂号费、查看就诊提醒、挂完号之后能自助取消。医生端要的是:查看自己的排班安排、看到底有哪些患者挂了号、一键点击"开始就诊"来更新候诊状态。医院管理员要的是:维护科室信息、维护医生基本信息、给医生排班、设置每个时间段放多少号源、查看每天各科室的挂号和退号统计。系统超级管理员则是最高权限,负责账号管理、数据备份、参数配置。

这种角色拆分决定了你Django项目里User模型的扩展方案。不要直接在默认User表上乱加字段,而是用一对一扩展Profile模式。核心User表只保留手机号、微信openid、姓名、密码这些通用字段;再建PatientProfile和DoctorProfile分表存各自特有信息,比如医生的职称、擅长领域、所属科室,患者的医保卡号、过敏史。这样做的好处是:未来如果要在同一个微信账号下绑定多个就诊人(很多患者帮父母挂号),可以把"就诊人"和"登录账号"彻底解耦,扩展起来不用动底层结构。

2.2 号源模型:核心难点不在表结构,在锁号和防超卖

号源系统是这个项目里最容易做崩的地方。我看到很多初学者写的代码,预约逻辑就是简单的"先查余号,大于0就插入一条预约记录"。这在单人测试时没问题,一旦上线并发上来,两个患者同时提交最后一个号位的预约请求,就会同时查到"余号=1",都插入成功,最后超卖。

解决超卖问题的核心思路,从业务层面要结合医院的实际情况来设计。我采用的方案是号源按时间段生成 + 数据库事务锁。具体做法是:为每个医生的每个出诊班次,预先根据排班时间生成固定数量的号源记录。例如一个上午班次从8点到12点,每20分钟一个时段,就生成12条号源记录,每条记录有一个唯一状态字段,可能是"空闲""锁定""已预约",这样可以把并发压力从行级竞争分散到号源记录本身。

患者点击预约时,后端执行的不再是"查询余号再插入",而是直接执行一条条件更新语句:更新该号源记录的状态为"锁定"或"已预约",但更新条件里必须包含"当前状态为空闲"。以Django ORM为例,用update()方法配合条件过滤,可以在一句SQL里完成原子操作,从代码层面避免超卖。

关于锁的补充说明:真正高并发场景下,Django里的select_for_update()配合事务,可以对号源行加数据库行级锁,锁住后再检查状态、创建订单、扣减号源。这套组合拳有几层防护,建议有精力的读者都掌握。实测门诊系统的并发量级,单台数据库服务器完全扛得住。真正的瓶颈反而不是数据库,而是支付环节的重复回调处理,这部分后面单独讲。

2.3 数据表设计:一张图看懂核心表关系

回到Spring那套思路,用Django的模型定义,核心表就这些:

科室表Department:科室名称、科室编码、科室位置、是否支持在线挂号。注意科室编码一定要唯一,这是后续跟医院HIS系统对接时的关键关联字段。

医生表DoctorProfile:姓名、职称、擅长领域、简介、所属科室外键、是否出诊状态。这里有个我踩过的坑:医生的头像图片不能直接存数据库,要存图片URL路径,文件放对象存储或本地静态目录,否则数据库体积膨胀后备份和查询都会变慢。

排班表Schedule:关联医生、科室、出诊日期、午别、开始时间、结束时间、放号总数、剩余号数。这个表是系统的心脏,所有查询入口几乎都要先落到排班上。

号源表Slot:关联排班、时段开始/结束时间、状态、锁定的订单号。每个时段一个号源记录。

挂号单表Appointment:关联患者、医生、排班、号源、就诊状态(待就诊/已就诊/已取消/爽约)、挂号的订单号、支付状态。这是贯穿全流程的核心业务记录。

支付订单表PaymentOrder:关联挂号单、支付金额、支付渠道、支付状态、回调通知状态。这里要特别留一个唯一交易号字段,用来处理支付回调的幂等性。

系统数据字典表:比如常见问题类型、号源状态字典等,防止硬编码写死在代码里。

表设计时我个人强烈建议所有核心表都带上created_at和updated_at两个时间字段,以及逻辑删除字段,这样后端排查问题(看时间是几点操作的、哪些数据被逻辑删除了)会方便很多,医院上线后运维价值很大。

3. 小程序前端与后端API的完整实现

3.1 小程序端整体架构与页面路由设计

小程序端我从零开始搭,用的是原生微信小程序框架,没有上uniapp或者Taro。原因一是医院信息科后续维护的人比较熟悉原生语法,二是原生开发在真机调试、微信版本适配上的稳定性都更好。

页面结构按TabBar分四个入口:首页(顶部搜索框+科室宫格导航+今日出诊医生推荐)、预约(我的挂号和待就诊列表,支持取消操作)、消息(系统消息和就诊提醒)、我的(个人信息、就诊人管理、支付记录、帮助中心)。另外还有两层嵌套页面:科室详情页(展示科室下所有出诊医生)、医生详情页(展示医生排班日历和号源时段,这是用户操作的核心页面)。

关键设计原则是让用户三步内完成挂号:第一步选科室,第二步选医生和日期,第三步选时间段并确认支付。超过三步,中年用户群体的流失率会显著上升。我在医生详情页做的是日历横滑组件,展示未来一周的可预约日期,日期下面直接列时段格子,绿色代表可约、灰色代表约满、红色代表停诊,颜色语义贴合用户直觉,完全不用看文字说明。

3.2 核心流程:预约挂号的完整链路

这部分的代码实现是整个项目里最重要的闭环。搭好了它,其他功能都是在这个模式上做的加减法。我直接说关键实现。

第一步,登录与OpenID换取。小程序端调用wx.login()拿到临时code,传给后端,后端拿这个code加上小程序的AppID和AppSecret,调用微信的code2Session接口,换回用户的openid和session_key。OpenID是用户在微信生态里的唯一身份标识,后端拿它去匹配本地用户表,首次登录自动注册,完成绑定手机号的用户就直接进首页。这个流程要注意:APP_SECRET绝对不能暴露在小程序代码里,所有换取操作必须走服务端,源码一旦泄露,攻击者可以伪造任意用户的登录身份。

第二步,查询排班和号源。后端提供两个接口:GET /api/doctors/<id>/schedule?date=...返回指定日期范围内的排班;GET /api/schedules/<id>/slots返回某个排班下所有时段的号源状态。小程序拿到号源列表后,只渲染状态为可约的时段。

第三步,提交挂号请求。前端提交的数据包括排班ID、号源ID、患者就诊人ID。后端在事务里执行"锁定号源、创建挂号单、生成支付订单、返回待支付订单号"。这里有个很关键的幂等设计:同一个号源ID在300秒内的重复提交请求,直接返回已有订单,防止前端超时重试导致重复下单。

第四步,调起微信支付。前端拿到后端生成的预支付订单参数后,调用wx.requestPayment()弹出支付面板。用户输入密码完成支付,微信服务器向你的支付回调地址推送异步通知,后端验证签名后把支付状态改为"已支付",再把挂号单状态从"待支付"改为"待就诊"。

第五步,就诊状态流转。患者到院后可以在小程序里看到自己的候诊序号,医生点击"开始就诊"后,患者的就诊状态变为"就诊中",结束后变为"已完成"。整个链路闭合,每一步都有操作记录可追溯。

3.3 医生端与Admin管理后台:最小可用版本

医生端我偷懒直接复用了小程序前端的一部分页面,没有单独做App。医生登录后看到的是当天的排班卡片,点击进入后展示患者列表:每位患者显示姓名脱敏(保留姓+末位)、挂号时段、候诊序号。医生按顺序点击"叫号",患者小程序端会通过订阅消息收到叫号提示。这个功能量级对一家医院来说基本够用,医生不需要额外培训,会点手机就会操作。

管理后台用Django Admin做二次开发,重点在三个页面。排班管理页面支持按周视图批量生成排班,选择一个医生、设置周一到周五的出诊时间段,系统自动在后台生成对应时段的号源记录,这一步把医院排班员从手工一条条录号源中解放出来了。数据统计页面直接写SQL聚合,统计各科室每日挂号和退号量、医生退号率、患者爽约率。导出功能用pandas加openpyxl生成Excel报表,支持按项目周期筛选,支持权限控制。

4. 技术难点复盘:支付回调、消息推送与并发控制

4.1 微信支付回调:幂等性是我被坑得最惨的地方

微信支付回调是整个系统里最容易出线上事故的环节。微信服务器的策略是:你的回调接口如果在5秒内没返回成功,或者返回的不是合法JSON,它会自动重试,最多重试15次,间隔时间递增。很多人第一次写回调接口时直接写了"修改订单状态为已支付",然后一上线就发现两个问题:第一,回调重复触发导致状态被反复翻改;第二,用户支付成功了,但你没处理完回调就超时,用户钱扣了挂号单还是待支付。

正确的回调处理姿势是:第一步,验签。用微信支付平台证书验证回调消息的签名,防止伪造通知。第二步,按微信支付商户订单号和交易号去查本地支付订单是否存在,不存在则返回失败,让微信重试。第三步,幂等校验——如果订单状态已经是"已支付/已退款",直接返回成功,不再重复处理。第四步,开启事务,同时更新支付订单状态和挂号单状态。第五步,返回微信约定的成功报文{"code": "SUCCESS"}。这五步顺序错了任何一步,都会在你上线后变成线上事故。

4.2 订阅消息:小程序里最实用的触达手段

患者挂完号之后怎么提醒?怎么告诉医生来新患者了?微信对小程序TemplateMessage做了改版,现在叫订阅消息。规则是一次订阅授权,只能向用户下发一次消息。用户若想每次挂号后都收到提醒,就得在挂号确认前反复用wx.requestSubscribeMessage()引导授权订阅。

实操中我的做法是:挂号确认页设计两个授权触达点。第一个是在用户点了"确认预约"但还没支付时,弹一个订阅授权,请求"就诊提醒"的消息模板;第二个是支付成功页面再请求"预约成功通知"。这样用户一次挂了号,就能合法下发两条消息。很多产品的订阅率低,问题往往出在授权时机不对——不是在用户最需要消息触达的时刻弹窗,而是在用户根本不知道这条消息会怎么骚扰他的时候直接弹了授权框,用户自然一拒了之。

微信对订阅消息的使用也有频率审查,如果你的系统恶意引导或者滥用,会被封模板配额,得不偿失。认真设计授权时机,比多申请几个模板资质的价值大得多。

4.3 页面秒开与弱网容错:门诊大厅的wifi可能比你家宽带差多了

医院的网络环境是我之前没预料到的坑。三甲医院门诊大厅高峰期的无线网络质量很差,人多、墙体厚、基建老旧,小程序页面经常加载慢甚至白屏。这部分的优化做了三件事。

第一,页面静态资源本地化。图片、图标、公共组件库尽量走小程序分包或本地包,不发网络请求。首页的科室图标全部本地化,只有医生头像这种必须传到服务器的才走CDN。

第二,本地缓存策略结合过期时间。科室列表、医生排班表这类变更频率低的数据,在本地缓存中存一份,并带上时间戳。下次进入页面先渲染缓存,同时异步请求接口,数据有变化再刷新视图。参数上,科室列表存24小时,当天排班存5分钟。这样患者第二次打开小程序,首页几乎秒开。

第三,请求封装统一处理弱网。给所有wx.request封装一层:自动带token、统一错误提示、超时时间设置。网络出错时把错误信息可视化地反馈给用户,让用户知道是"网络问题"而不是"小程序坏了",能少一大半的客服电话。

5. 部署上线与运维实战经验

5.1 服务器部署:阿里云ECS + Nginx + uWSGI(Gunicorn)

Python后端部署,Django项目我推荐Nginx + uWSGI(Gunicorn) + 阿里云ECS + MySQL的组合。原因很简单:可靠、资料多、排障容易。我的习惯是先用uWSGI把Django应用跑在本地端口(比如8001),再用Nginx做反向代理和静态文件服务。部署前按惯例先关了Debug模式、配好ALLOWED_HOSTS、把SECRET_KEY换掉、开启HTTPS证书。

这里要特别提醒一个新手最容易漏掉的步骤:Django静态文件收集。Django开发环境能自动处理静态文件,但一到生产环境,所有静态文件推荐用python manage.py collectstatic统一收集到STATIC_ROOT目录,再交给Nginx直接服务。第二步是设置CSRF_TRUSTED_ORIGINS,加上自己的域名,否则小程序里的POST请求会被403。

数据库方面,项目推荐用MySQL,字符集一定设utf8mb4,排序规则设utf8mb4_unicode_ci,这样存用户姓名的生僻字和特殊表情符号不出乱码。后端ORM连MySQL时别忘了配置CONN_MAX_AGE持久连接参数,否则每个请求都新建数据库连接,高峰期会直接把MySQL连接数打爆。

5.2 HTTPS证书与小程序合法域名

微信小程序有个铁律:所有请求域名必须是HTTPS,而且必须在小程序管理后台配置合法域名。开发时可以在开发者工具里勾选"不校验合法域名",但上线前必须把Request域名、UploadFile域名、Download域名都配置全。证书我推荐直接用阿里云免费版DV证书,或者用Certbot自动签发Let's Encrypt证书,一年一续,别忘了加个定时任务自动续期。

一个很少人提的细节是,微信小程序对请求域名还有速率和频率限制。如果你的某个接口被频繁调用,微信会直接拦截并提示"request:fail url not in domain list"或者网络异常。上线后一定要做接口限流,防止个别用户的异常操作拖垮整个服务。

5.3 运维监控:日志、报警、备份

医院系统的特点是业务不能断。运维层面的三件套:日志集中收集、监控报警、数据库自动备份。

日志我用loguru做Python日志,配合cron定时切割归档,避免日志文件无限膨胀。监控报警用阿里云的云监控,对CPU、内存、磁盘、数据库连接数做告警阈值设置。数据库备份我写了两个脚本:每天凌晨2点全量备份一次MySQL,中午再增量备一次,备份文件自动同步到OSS(对象存储)。上线后发生过一次实例宕机,2天内靠备份恢复到了丢失数据前1小时的状态,这是真实教训。

6. 常见问题与避坑总结

6.1 小程序真机调试的坑:本地环境与线上域名切换

开发时后端跑在本机(HTTP),小程序开发者工具能正常访问,但真机预览就全崩。原因是手机无法访问你电脑上的localhost。解决方法是:开发阶段在工具里勾选"不校验合法域名",真机调试时用局域网的电脑IP,或者干脆直接用内网穿透工具暴露本地后端服务。上线前记得把这些配置都统一切到线上域名,不要留在代码里写死。

6.2 Django+CORS跨域问题

小程序前端和后端域名不同,会产生跨域问题。虽然微信小程序的网络请求不受浏览器同源策略限制,但管理后台的网页版可能会踩。配置Django的django-cors-headers,把允许的域名列表填进去,加上CORS_ALLOW_CREDENTIALS = True支持携带凭证。这个坑通常在开发阶段不出现,部署后前端一上就报跨域错误,排查起来特别费时间。

6.3 并发扣减号源还是超卖怎么办

如果用了select_for_update和条件更新还是有偶发超卖,我建议把扣减操作收口到同一个服务节点处理,配合Redis分布式锁做二次校验。Redis锁设计成:锁的key是号源ID,value是请求唯一ID,设置过期时间防止死锁。每次扣减前先拿锁,拿到锁后再次检查号源状态,然后扣减释放锁。数据库层做最终一致,Redis层做并发控制,双保险。

6.4 小程序审核被拒的常见原因

医疗类小程序审核很严格。如果小程序涉及在线支付,类目必须是"医疗-就医服务",需要提供医疗机构执业许可证等资质文件。另外,小程序内不能出现"首诊"承诺、不能做疾病诊断结论、不能宣传疗效。你的内容不要绕过审核、不要做虚假宣传。提前把这些合规问题处理了,审核周期能缩短一半。

6.5 Django版本选择:别用最新版,用稳定版

很多教程一上来就是最新版Django,但我个人强烈建议项目用Django 4.2 LTS或5.0 LTS版本,不要追新。LTS版本有长期安全补丁支持,社区生态兼容性最好。Flask同理,选2.x稳定版。Python版本用3.10或3.11。这个组合在2024年到2025年之间是稳定且资料最多的,踩坑时能搜到的答案也最丰富。

6.6 微信支付的退款接口

退款也是高频需求。用户挂错号要退,医生停诊要退。微信支付退款接口是同步返回受理结果的,但实际退到用户账户(零钱)是实时的,退到银行卡会T+1或更久。后端处理退款时有两个注意点:一是退款需要退款单号,确保退款单号唯一;二是退款接口同样要做幂等,防止重复退款把用户的钱退两次。退款回调通知同样要验签,处理逻辑照搬支付回调那套。

7. 我的实操体会与后续扩展建议

7.1 这个项目做下来,我最大的感受是什么

先说结论:一个"看似简单"的门诊挂号小程序,真正开发的难点不在某一个技术点上,而在把医院里的人、流程、规则翻译成代码的过程。需求表面上是"挂号",背后却是医院排班规则(停诊改诊怎么处理)、号源容量(每个时段放多少号)、医生资质(哪些科室必须线下首诊不能在线挂号)这些业务规则的深度耦合。写得再漂亮的代码,不贴合这些真实规则,上线也是废的。

我的建议是:无论你是练手还是真接项目,动手写代码之前,至少去医院挂号窗口蹲半天,观察患者的问题、窗口人员的操作步骤、退号改号的流程。这半天给你的业务理解,比看十篇技术博客都管用。

7.2 后续可以怎么扩展

这个系统做完之后的扩展空间很大。方向上可以加:患者的电子健康档案模块,医生可以查看患者历史就诊记录和历次处方;检验检查结果查询,患者做完检验后小程序直接推送报告,省得打印窗口排队;候诊排队叫号大屏的对接,把虚拟叫号搬到候诊区的电视屏幕上;在线问诊图文咨询模块,把"复诊续方"这个需求承接住,患者在线上跟医生聊几句就能拿到续方。

技术上还可以把Django后端拆成微服务,挂号服务单独部署,用消息队列削峰填谷处理号源抢购的突发流量。

7.3 最后分享一个压箱底的小技巧

如果你发现用户经常在"选择时间段的页面"流失,不要急着改UI。先把用户的操作日志打出来,看看他到底卡在哪一步。我当时查日志发现,很多用户是选择了日期,但当天医生正好停诊,整个排班区是空的,页面看起来像坏了。后来我在空状态加了一句话"今日无号源,请选择其他日期",流失率立刻降了三分之一。很多时候,产品的转化问题不是技术问题,而是信息反馈的问题。这句话送给所有正在做这类系统的朋友。

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

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

立即咨询