ARTICLE DETAIL

资讯详情

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

微信小程序+Python Django医院门诊挂号系统设计与实践

微信小程序+Python Django医院门诊挂号系统设计与实践 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完善自动建表迁移需自行集成SQLAlchemyAdmin后台开箱即用适合运营管理无需自己搭建项目结构有强制规范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自动签发Lets 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 DjangoCORS跨域问题小程序前端和后端域名不同会产生跨域问题。虽然微信小程序的网络请求不受浏览器同源策略限制但管理后台的网页版可能会踩。配置Django的django-cors-headers把允许的域名列表填进去加上CORS_ALLOW_CREDENTIALS True支持携带凭证。这个坑通常在开发阶段不出现部署后前端一上就报跨域错误排查起来特别费时间。6.3 并发扣减号源还是超卖怎么办如果用了select_for_update和条件更新还是有偶发超卖我建议把扣减操作收口到同一个服务节点处理配合Redis分布式锁做二次校验。Redis锁设计成锁的key是号源IDvalue是请求唯一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 微信支付的退款接口退款也是高频需求。用户挂错号要退医生停诊要退。微信支付退款接口是同步返回受理结果的但实际退到用户账户零钱是实时的退到银行卡会T1或更久。后端处理退款时有两个注意点一是退款需要退款单号确保退款单号唯一二是退款接口同样要做幂等防止重复退款把用户的钱退两次。退款回调通知同样要验签处理逻辑照搬支付回调那套。7. 我的实操体会与后续扩展建议7.1 这个项目做下来我最大的感受是什么先说结论一个看似简单的门诊挂号小程序真正开发的难点不在某一个技术点上而在把医院里的人、流程、规则翻译成代码的过程。需求表面上是挂号背后却是医院排班规则停诊改诊怎么处理、号源容量每个时段放多少号、医生资质哪些科室必须线下首诊不能在线挂号这些业务规则的深度耦合。写得再漂亮的代码不贴合这些真实规则上线也是废的。我的建议是无论你是练手还是真接项目动手写代码之前至少去医院挂号窗口蹲半天观察患者的问题、窗口人员的操作步骤、退号改号的流程。这半天给你的业务理解比看十篇技术博客都管用。7.2 后续可以怎么扩展这个系统做完之后的扩展空间很大。方向上可以加患者的电子健康档案模块医生可以查看患者历史就诊记录和历次处方检验检查结果查询患者做完检验后小程序直接推送报告省得打印窗口排队候诊排队叫号大屏的对接把虚拟叫号搬到候诊区的电视屏幕上在线问诊图文咨询模块把复诊续方这个需求承接住患者在线上跟医生聊几句就能拿到续方。技术上还可以把Django后端拆成微服务挂号服务单独部署用消息队列削峰填谷处理号源抢购的突发流量。7.3 最后分享一个压箱底的小技巧如果你发现用户经常在选择时间段的页面流失不要急着改UI。先把用户的操作日志打出来看看他到底卡在哪一步。我当时查日志发现很多用户是选择了日期但当天医生正好停诊整个排班区是空的页面看起来像坏了。后来我在空状态加了一句话今日无号源请选择其他日期流失率立刻降了三分之一。很多时候产品的转化问题不是技术问题而是信息反馈的问题。这句话送给所有正在做这类系统的朋友。
返回列表