基于Python和微信小程序的医院陪诊陪护系统设计与实现

发布时间:2026/10/14 23:22:44
基于Python和微信小程序的医院陪诊陪护系统设计与实现
做医院陪诊陪护这块业务让我最头疼的不是医疗资源本身而是线下流程的混乱。预约难、排班乱、陪诊师接单全靠群里吼家属想找个人看看进度都找不到地方。后来我基于Python后端 微信小程序前端从零搭了一套医院健康陪诊陪护系统把预约、接单、排班、支付、评价全部线上化这套系统跑通之后业务效率确实上了一截。这篇文章就围绕这套系统的设计和实现把我踩过的坑和沉淀下来的方案一次性讲清楚。这是一套面向陪诊服务公司、跑腿平台、医院导诊服务团队的可复用方案适合有Python基础、想快速搭建微信端预约服务系统的开发者参考也适合正在规划陪诊类业务的运营人员拿来对接技术实现。1. 系统核心思路与业务拆解1.1 陪诊陪护业务的真实痛点先说业务侧。陪诊陪护不是简单的“带着看病”它背后是一整套服务链路用户下单预约、系统分配陪诊师、陪诊师按时间上门、陪同就诊、记录医嘱、回访评价。这中间牵涉三类角色患者或家属、陪诊师、后台调度管理员。线下方式的最大问题一是信息不透明用户不知道陪诊师是谁、几点到、现在到哪一步了二是调度靠人肉管理员得在群里看谁有空、手动派单订单一多就乱了三是服务完成后缺少标准化的评价和客诉通道服务质量没法沉淀。这套小程序系统的核心目标就是把这三个问题解决掉让用户像点外卖一样看待诊服务下单、看进度、完成、评价全程可见、可追踪。这里有个设计思路上的关键取舍。我没有做独立App而是直接落在微信小程序上。原因很简单目标用户中很多是老年人或者帮老人挂号的中年子女他们使用微信的熟练度远高于安装新App的意愿小程序没有下载门槛分享给家属也方便。对陪诊师来说小程序上接单、打卡、填写服务记录同样不需要额外安装工具。1.2 系统角色与核心业务流程整个系统的角色分为三类每一类对应不同的功能边界用户端家属/患者本人微信授权登录、填写陪诊需求、选择陪诊师或智能匹配、在线支付、查看服务进度、服务完成后评价。陪诊师端微信授权登录、接收派单/主动抢单、服务开始与结束打卡、填写服务回执、查看收益明细。管理员后台Web端陪诊师管理、订单调度、价格规则配置、退款审核、数据统计。核心业务流程是一条完整的订单状态链待支付 - 待接单 - 已接单待服务 - 服务中 - 已完成 - 已评价其中还会穿插几个分支状态用户取消申请退款、陪诊师请假导致改派、超时未接单自动召回。每一个状态流转都要有明确的操作方和时间戳这是后面做状态机设计的核心依据。从技术角度看这个业务本质上是一个“多人协作的订单状态机”跟家政、跑腿类系统非常相似。想清楚状态流转后端逻辑就成功了一半。1.3 为什么选这套技术组合Python 微信小程序这套组合我个人的判断是这样的后端选Python不是因为性能天花板有多高而是这种业务型系统核心复杂度在业务逻辑编排和状态管理不在高并发。Python在这类场景下开发效率高框架生态成熟写CRUD和状态流转非常顺手。我一向认为业务工具类系统先求跑得通、改得快再到真正有流量压力的时候再做架构演进就行。前端选微信小程序除了前面说的用户习惯因素还有一点是微信生态天然解决了身份认证和支付。微信授权登录拿openid直接对接微信支付省掉自己搞账号体系和支付通道的精力对中小型服务团队来说划算得多。这套组合后面还可以平滑扩展小程序端可以加视频问诊、健康档案模块后端可以继续承载App接口甚至未来给第三方平台提供API服务都是同一条技术路线上的延伸。2. 技术选型与架构设计2.1 后端框架Django 还是 Flask我在这个项目上用的是 Django Django REST FrameworkDRF不是 Flask。原因有三点。第一这项目的数据模型不少用户、订单、陪诊师、排班、支付、评价都要建表Django 自带 ORM 和 Admin 后台直接节省掉大量重复代码。第二陪诊师管理和价格配置这类后台功能Django Admin 稍微定制一下就能用不用单独开发管理界面。第三DRF 的序列化、权限控制、视图集一套组合下来写接口的速度确实快。Flask 的优势是轻量灵活但当项目的实体关系复杂到一定程度时你会发现所有东西都得自己拼反而拖慢节奏。我的建议是小型Demo和原型可以用 Flask但凡是涉及多角色、多状态、有后台管理需求的业务系统直接上 Django你会省很多事。2.2 小程序端方案原生还是 uni-app小程序端我选的是原生微信小程序开发没有用 uni-app 或 Taro 这类跨端框架。这要从目标出发。这套系统主要跑在微信生态内短时间内没有同时发布支付宝小程序、抖音小程序的需求。用原生开发没有框架层转换带来的调试问题组件性能和兼容性也都是最好的。这个项目用到的页面包括首页、预约表单、订单列表、进度详情、个人中心、陪诊师端的工作台全部是常规页面用原生写完全够用。如果你的团队有跨端需求比如既要微信小程序又要支付宝小程序用 uni-app 也可以但要注意它的自定义组件生态和微信原生能力的对接在涉及地图定位、蓝牙打印这类设备能力时偶尔要写条件编译复杂度会起来。2.3 整体架构与中间件选型整体架构上我分为四层客户端层微信小程序用户端 陪诊师端通过角色切换进入不同页面模型接入层Nginx 反向代理 HTTPS 证书服务端层Django 应用 Celery 异步任务数据层MySQL 业务库 Redis 缓存为什么引入 Celery陪诊服务里有几个场景天然需要异步处理比如订单超时未支付自动关闭、派单后陪诊师超时未接单自动改派、服务开始前给用户发微信订阅消息提醒。这些任务如果全部靠请求线程里的 sleep 去定时触发不仅阻塞服务而且进程一重启任务就丢了。用 Celery Beat 做定时调度拿 Redis 当 Broker任务可靠性和可观测性都会好很多。Redis 在这个项目里还承担了另一个角色缓存用户登录态和陪诊师抢单的分布式锁。后面我会单独讲并发抢单的坑Redis 锁是绕不开的工具。架构上要留意的一个点是不要把业务逻辑写进 Django Signal 里。我早期的版本里试过用 Signal 做订单状态变更后的通知触发结果代码一复杂信号触发链路完全不可控最终全部改成了服务层显式调用。这个教训在后面排查问题时会详细展开。3. 数据库设计与核心表结构3.1 用户、陪诊师、管理员的关系模型数据库设计上我没有把用户和陪诊师做成一张大表加角色字段的方式而是拆成了两个核心身份模型用户表member和陪诊师表attendant。用户表的核心字段包括微信 openid、unionid、昵称、手机号、实名认证状态、默认就诊人信息姓名、身份证、紧急联系人。陪诊师表在用户信息基础上多了一套服务资料服务区域、可服务时段、评分、服务次数、技能标签、审核状态。为什么不合并成一张表因为用户和陪诊师的字段差异很大合并会造成大量空字段而且后续用户报名成为陪诊师时需要独立的资质审核流程。分开建模identity 关系清晰扩展也方便。用户申请成为陪诊师的场景通过一张关联表记录申请记录和审核进度即可。这里有一个设计细节值得提一下用户表中我没有直接存身份证号明文。实名认证信息安全要求高我在数据库里只保存身份证号的 SHA-256 哈希值和掩码版本敏感信息用于比对时只比对哈希结果。这个做法在后续对接实名认证接口时也方便迁移。3.2 订单表与状态机设计订单表是整个系统的核心字段设计上我按“业务字段 状态字段 时间字段”三块来组织业务字段用户ID、陪诊师ID、患者姓名、患者手机号、就诊医院、就诊科室、预约时间、服务时长、服务类型陪诊/取药/代排队、金额、优惠金额、支付金额、备注。状态字段order_status当前状态、cancel_reason取消原因、assign_type派单方式自动/手动/抢单、pay_status支付状态。时间字段创建时间、支付时间、接单时间、服务开始时间、服务结束时间、取消时间。订单状态我采用的是“显式状态码 时间戳”方案而不是只靠逻辑判断。每个状态变更操作都封装在 OrderService 里的一个独立方法中方法内部先校验当前状态是否允许跳转再更新数据库并记录变更日志。订单状态变更表单独建一张 order_status_log记录 from_status、to_status、operator_id、operator_type、create_time方便后续追踪异常状态和做数据统计。这种设计的价值在于当用户和陪诊师看到订单状态延迟或不一致时可以直接从状态日志里定位是哪一步出了问题而不是靠翻代码猜。3.3 支付记录表与价格配置支付这块我设计了 pay_order 表专门记录每一笔交易的支付流水字段包括订单号、微信支付交易号transaction_id、订单金额、实际支付金额、支付状态、回调时间、退款状态、退款金额。这里有一个容易被忽视的问题业务订单表和支付流水表一定要分开。一个业务订单可以包含多次支付操作比如先付定金、后付尾款也可能发生多次退款比如部分退款。如果只在业务订单表上放一个支付金额字段后续对账和退款都会很痛苦。分开设计之后pay_order 表和业务订单表是1对多关系每一笔资金变动都有独立记录对账时直接按 pay_order 表汇总即可。价格配置我单独做了一张 service_price 表按服务类型、时长档位、地区、陪诊师等级组合成价格。为什么不写死在小程序端因为价格策略变化频繁放在前端要发版放后端一张表改一下就能生效。运营人员调整一次价格用户端下单页立即生效。4. 核心功能模块实现4.1 微信登录与会话保持微信小程序的登录流程是这套系统所有接口访问的前提。用户进入小程序后前端调用wx.login获取临时 code然后通过后端接口传过去的流程如下# django 视图层代码 import requests def wx_login(request): code request.data.get(code) appid settings.WX_APPID secret settings.WX_SECRET url fhttps://api.weixin.qq.com/sns/jscode2session?appid{appid}secret{secret}js_code{code}grant_typeauthorization_code resp requests.get(url).json() if resp.get(errcode): return error_response(resp.get(errmsg)) openid resp.get(openid) session_key resp.get(session_key) # 查库或创建新用户 user User.objects.filter(openidopenid).first() if not user: user User.objects.create(openidopenid, nickname微信用户) # 生成服务端 token返回给小程序 token generate_token(user.id) return success_response({token: token, user_id: user.id})这里有几个关键的坑我第一次做的时候踩过第一小程序端拿到的 code 只能使用一次而且有效期很短。如果前端不小心把同一个 code 传给后端两次第二次一定会失败。所以后端接口要保证幂等处理前端也要避免重复调用登录接口。第二openid 要认准一个用户unionid 只有在同一主体下多个应用时才拿得到。如果你只有一个小程序用 openid 做唯一标识就够用了。第三session_key 不要返回给前端。这等于把微信的会话密钥暴露出去后续如果要解密手机号、解密微信运动数据都需要它。正确的做法是后端保存 session_key 并和用户ID关联前端只拿自定义 token 维持状态。4.2 下单流程与派单策略用户下单是整个系统最关键的用户路径。我这里用的是“先选服务-再填信息-后支付”的三步式设计每一步后端做一次校验第一步选择服务类型和时长后端返回该类型对应的可预约时段和价格。第二步填写患者姓名、手机号、医院科室、备注信息后端校验必填字段和手机号格式。第三步生成订单号并调用微信支付支付成功后订单才进入“待接单”状态。订单号生成我采用了一套自定义规则日期 随机数 服务类型编码。形如P2025061200012345。这种订单号一点要保证唯一数据库层要加唯一索引因为微信支付回调时依赖订单号关联支付记录。派单策略我做了两种模式自动派单和抢单派单。自动派单的逻辑是订单进入待接单后系统根据陪诊师的服务区域、当天排班时间和服务评分筛选出符合条件的陪诊师按评分排序后优先推送。这个逻辑里最容易出问题的是时间重叠判断同一个陪诊师不能在同一个时间段接两单。我这里的实现是查询该陪诊师已接订单中时间区间是否与目标订单冲突再决定是否可派。抢单派单的逻辑是订单进入待接单后系统推送给符合条件的所有陪诊师谁先点击“接单”算谁的。这里必须解决并发抢单问题后面我会详细展开Redis 锁是核心方案。派单动作要做成幂等操作。也就是说同一笔订单不论管理员点了几次“重新派单”最终结果都应该是同一笔有效指派不能让订单同时出现在两个陪诊师的名下。4.3 订单状态流转与消息通知状态流转这块我建议在代码中建立一个明确的规则映射表而不是写一堆 if-else。Django 中我用了django-fsm这个状态机库来管理订单状态迁移定义好每个状态允许跳转的目标状态以及跳转前后需要执行的钩子函数。订单状态变更时有两件事必须做一是记录状态日志二是发送微信订阅消息通知用户。微信订阅消息的发送机制要特别注意用户必须在小程序里点了订阅授权你才有权限给 ta 发一条订阅消息而且是一次性订阅。用户授权一次你只能发一条。所以我在下单页面做了连续订阅提示让用户一次订阅多个模板消息接单通知、进度提醒、完成提醒。随时消息通知的完整链路是陪诊师接单 - 后端更新订单状态 - 写入 MySQL - Celery 发送订阅消息 - 用户在小程序收到模板消息通知。这里有一个实际经验不要在下单支付请求的同步线程里直接调微信订阅消息接口。微信接口的响应时间不稳定如果每次都等微信响应接口延迟会变得不可接受。正确做法是订单状态变更后把通知任务丢给 Celery 异步执行。用户看到的响应永远都是快的。4.4 微信支付对接与退款处理微信支付走的是小程序支付前端通过wx.requestPayment拉起支付界面后端负责下单和回调处理。下单支付的核心流程是import time import random from wechatpayv3 import WeChatPay def create_pay_order(request): # 1. 根据业务订单生成支付单 business_order BusinessOrder.objects.get(order_norequest.data[order_no]) pay_order PayOrder.objects.create( order_nofPAY{business_order.order_no}, amountbusiness_order.pay_amount, statusPENDING ) # 2. 调用微信支付统一下单接口 wxpay WeChatPay(appidsettings.WX_APPID, mchidsettings.WX_MCHID, private_keysettings.WX_PRIVATE_KEY, cert_serial_nosettings.WX_CERT_SERIAL) resp wxpay.pay( descriptionf陪诊服务-{business_order.order_no}, out_trade_nopay_order.order_no, amountbusiness_order.pay_amount, payer_openidrequest.user.openid, notify_urlhttps://your-domain.com/api/wxpay/notify ) return success_response({payment_params: resp.to_wechatpay_dict()})支付回调处理是项目中最容易出问题的地方。微信支付回调是异步通知可能重复多次回调所以回调接口逻辑必须幂等。我的做法是这样的def wxpay_notify(request): # 验签和解析 wxpay get_wechatpay_instance() try: result wxpay.callback(request.body, request.headers) except Exception as e: # 验签失败直接返回失败微信会重新回调 return HttpResponse(status400) out_trade_no result.get(out_trade_no) transaction_id result.get(transaction_id) with transaction.atomic(): pay_order PayOrder.objects.select_for_update().get(order_noout_trade_no) if pay_order.status SUCCESS: # 已经处理过直接返回成功避免重复处理 return HttpResponse(status200) pay_order.status SUCCESS pay_order.transaction_id transaction_id pay_order.paid_time result.get(time_end) pay_order.save() # 同步更新业务订单的支付状态 business_order pay_order.business_order business_order.pay_status PAID if business_order.order_status 待支付: business_order.order_status 待接单 business_order.save() create_status_log(business_order, PAY_SUCCESS) return HttpResponse(status200)退款流程我走的也是微信支付V3接口。用户在管理员审核同意后管理员后台调用退款接口退款结果通过异步回调更新。退款这里有一个坑微信退款不支持全额多次退款退款金额加总不能超过原订单金额。所以我在退款审核时增加了一个校验累计退款金额加本次退款金额不能大于原支付金额。支付时间戳和金额校验也不要忽略。回调接口里收到的金额要和支付订单金额比对相等才处理。如果金额不一致说明数据有问题应该直接失败并记录告警。5. 常见问题与排查经验5.1 微信登录态过期与 token 失效微信小程序 token 失效是一个高频问题。我早期设计的 token 有效期是7天但用户可能隔一个月才打开一次小程序一打开发现接口全部报401体验非常差。后来我用的是一个“刷新重新登录”的双层机制token 有效期7天但每次从请求里解析 token 时如果 token 超过5天未使用自动刷新并下发一个新 token前端拦截到新 token 后更新本地存储。如果 token 完全过期则返回特定状态码前端统一跳转到登录页重新走wx.login。还需要注意到一个细节用户清理微信小程序缓存后本地存储的 token 会丢失但后端并不知道 token 已失效用户在旧设备上又登录了一次。这种情况建议在小程序启动时先调用一次wx.checkSession检查微信登录态如果微信侧过期主动清掉本地 token 并重新登录。5.2 陪诊师并发抢单的问题抢单功能上线第一天陪诊师反馈“我点了接单却提示已被抢”。这是因为多个陪诊师同时按接单并发请求打到后端系统无法保证同一订单只能被一个人接走。我的解决方案是用 Redis 分布式锁接单接口这样写def grab_order(request, order_id): lock_key forder:grab:{order_id} lock_value str(request.attendant.id) _ str(time.time()) # 获取锁5秒自动过期 got_lock redis_client.set(lock_key, lock_value, nxTrue, ex5) if not got_lock: return error_response(订单已被其他陪诊师抢走) try: with transaction.atomic(): order BusinessOrder.objects.select_for_update().get(idorder_id) if order.order_status ! 待接单: return error_response(订单状态已变化不能接单) order.order_status 已接单 order.attendant request.attendant order.accept_time timezone.now() order.save() create_status_log(order, GRAB_SUCCESS) # 释放锁 if redis_client.get(lock_key) lock_value: redis_client.delete(lock_key) return success_response(接单成功) except Exception as e: redis_client.delete(lock_key) return error_response(str(e))锁里的一层保险是数据库层的select_for_update()。Redis 锁解决“并发抢”的问题数据库行锁解决“状态重复更新”的问题两层配合才能万无一失。我见过不少人只用 Redis 锁结果 Redis 锁过期后依然出现重复接单这层兜底很重要。5.3 微信支付回调与对账问题微信支付回调虽然逻辑简单但排查起来非常烦。我遇到最典型的问题是支付成功了但订单状态没变。后来查原因是回调接口报了验签错误微信连续重试几次后回调请求次数用完了订单就永远停留在待支付。解决思路有两个方向。第一回调接口要在日志里完整记录微信回传的请求头Wechatpay-Signature、Wechatpay-Timestamp、Wechatpay-Nonce和请求体方便出问题时逐项排查。第二要有一套兜底的主动查询机制不能只依赖微信回调。我在后台加了一个定时任务每10分钟查询一次最近30分钟内支付状态为待支付的订单主动调用微信支付查询订单接口确认最终支付状态如果已支付但没有回调入库直接补齐支付状态。这样两套体系并行即使微信回调漏了主动查询也能兜底不会让用户付了钱但订单没生效。5.4 状态机不一致与多端同步陪诊师端和用户端看到的订单状态可能不一致这个问题本质上是状态机逻辑分散带来的。早期版本我在陪诊师端和用户端的接口里各写了一段状态过滤逻辑后端不同接口对同一状态的理解不一致结果就是用户那边订单显示“服务中”陪诊师那边还没开始服务。后来我把状态机收敛到后端一个统一的地方前端组件只做展示映射。具体做法是在 models.py 里定义一个状态机类所有状态变更都必须走这个类的方法。每次状态变更前校验前置状态变更后统一记录日志。这样无论哪个端调用接口最终的状态变更逻辑都是同一套代码所有端看到的状态自然一致。还有一个小坑要提醒前端写订单状态展示时不要自己根据某种条件推断状态。比如不要写“如果服务开始时间小于当前时间就显示服务中”这种逻辑在后端状态准确的情况下根本不需要。永远以后端返回的状态字段为准前端只负责把状态码映射成文案。5.5 微信订阅消息发送失败的排查思路微信订阅消息发送失败原因一般是这几类第一用户没有在该小程序内订阅过该模板消息调用发送接口会报用户未授权。这种情况在前端就需要做好订阅引导下单页弹窗提示用户点击订阅按钮。第二用户订阅的模板消息ID与发送时使用的模板ID不匹配。有时候前端改了模板ID后端没同步改就会一直报错。建议模板ID放在后end设置里统一配置前后端通过配置文件对应。第三订阅消息发送频率限制。微信对订阅消息的发送频次有限制同一用户同一模板一天内的发送量不能超过额度。服务类场景一般不会触碰这个限制但批量通知用户时要注意。我排查这个问题的经验是在 Celery 任务里把所有发送结果都记录下来包括返回的错误码和错误描述同时给消息发送任务加上重试机制暂时失败的投递到重试队列而不是直接丢弃。6. 一些值得留意的开发建议这套系统做完之后我梳理了几条对同类项目都有普适性的建议可以少走弯路。第一业务系统中状态机一定要先画清楚再写代码。画图的过程就是理业务的过程状态流转梳理不清后面改代码的成本会翻倍。哪怕不用状态机库至少也要把状态字段和合法跳转关系定义清楚。第二支付和业务解耦。即使一开始只有一笔全额支付的场景也建议按支付单和业务单分离的模型设计。后面业务方说“先付定金服务完成再付尾款”的时候你就知道当初没偷懒的好处了。第三异步任务要尽早引入。下单后的通知、超时关闭订单、对账任务这些都是天然的异步任务。尽早把 Celery 或类似机制搭起来一开始就把任务队列跑通比业务复杂之后再补要省心得多。第四所有涉及外部接口的地方都一定加日志。微信登录、支付回调、订阅消息发送这些外部接口的请求和响应详情都应该入库或者至少写日志文件。出了问题没有日志排查只能靠猜效率极低。最后我再提供一个后端接口设计上的建议。陪诊系统的接口路径设计尽量以资源为核心组织比如/api/orders/、/api/orders/{id}/progress/、/api/attendants/{id}/schedule/而不是以页面为核心写/api/orderPage。这样后期做管理后台和 App 端复用接口时不需要重新设计整套 API。我自己在这套系统上最终沉淀下来的经验就是一句话先把业务状态想明白再动手写代码先把基础架构搭好再填功能。陪诊陪护这类垂直服务系统的业务逻辑并不复杂绝大多数问题都出在细节的疏漏上而细节恰恰决定了运营效率和服务体验。希望这套系统的拆解过程能帮到正在做同类项目的朋友。