1. 项目概述:乡村医疗上门服务预约平台
去年参与某县域智慧医疗项目时,当地卫健局提出一个痛点:山区留守老人看病难,村医上门服务又存在调度混乱问题。我们基于这个需求开发了这套微信小程序预约系统,上线后服务覆盖23个行政村,月均完成300+次上门诊疗。这套系统核心解决了三个问题:一是通过地理围栏技术自动匹配最近村医,二是利用防刷单机制保障预约真实性,三是集成医保结算接口实现现场报销。
系统采用SpringBoot+Vue前后端分离架构,小程序端使用WXML+WXSS开发,特别针对农村网络环境做了请求重试和本地缓存优化。在数据库设计上,采用分表存储历史订单与活跃订单,确保高并发时查询效率。下面从技术选型到功能实现详细解析这套系统的开发要点。
2. 技术架构设计
2.1 后端技术栈选型
选择SpringBoot 2.7.x版本(非最新的3.x)主要基于两点考虑:一是农村卫生所电脑普遍安装JDK8环境,二是需要兼容老版本医保系统的WebService接口。关键依赖包括:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.7.12</version> <!-- 指定版本避免自动升级 -->
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>4.5.0</version> <!-- 微信小程序Java SDK -->
</dependency>
特别提醒:微信小程序服务端接口必须使用HTTPS,在内网测试时我们采用nginx反向代理+自签名证书方案,但正式环境务必购买CA机构证书,否则小程序无法调用接口。
2.2 小程序端关键技术
采用微信原生开发而非uniapp,主要考虑到:
- 需要调用微信原生地图组件实现位置展示
- 必须使用小程序原生医保支付接口
- 农村用户手机性能有限,原生组件性能更好
关键页面使用scroll-view实现长列表渲染,配合onReachBottom事件实现分页加载。实测数据显示,在红米Note9(3GB内存)上,列表滑动帧率能保持在50fps以上。
3. 核心功能实现
3.1 预约调度算法
核心调度逻辑在SchedulingService类中实现,采用责任链模式处理不同优先级:
java复制public class SchedulingService {
// 责任链模式处理不同调度策略
private List<SchedulingStrategy> strategies;
public Doctor assignDoctor(Appointment appt) {
for (SchedulingStrategy strategy : strategies) {
Doctor doctor = strategy.apply(appt);
if (doctor != null) return doctor;
}
throw new NoAvailableDoctorException();
}
}
// 示例策略:距离优先
@Component
@Order(1)
class DistanceStrategy implements SchedulingStrategy {
@Override
public Doctor apply(Appointment appt) {
return doctorRepository.findNearest(
appt.getLatitude(),
appt.getLongitude(),
10 // 最大距离10公里
);
}
}
策略执行顺序配置:
- 第一优先级:5公里内签约家庭医生
- 第二优先级:10公里内同专业医生
- 第三优先级:20公里内全科医生
3.2 医保结算对接
各地医保接口差异较大,我们抽象出统一适配层:
code复制医保结算流程:
1. 小程序端调用wx.requestPolymerPayment发起支付
2. 服务端验证医保资格(调用卫健委接口)
3. 生成混合支付订单(医保+自费)
4. 推送到村医APP端扫码确认
特别注意:医保结算必须做本地缓存,我们采用Redis存储临时结算凭证,设置15分钟过期时间,避免网络中断导致支付失败。
4. 性能优化实践
4.1 数据库分表策略
订单表按月份分表,采用MyBatis动态表名拦截器:
java复制public class TableNameInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) {
// 根据create_time自动路由到order_202307等表
String origTable = getTableName(invocation);
String newTable = origTable + "_" + DateUtil.format(createTime, "yyyyMM");
resetTableName(invocation, newTable);
return invocation.proceed();
}
}
同时建立热点数据视图materialized_view_current_orders,实时同步最近7天订单。
4.2 弱网优化方案
针对农村4G信号不稳定的情况,我们实现:
- 请求重试机制:axios拦截器实现三级退避重试
- 本地缓存:小程序端使用storage存储基础数据
- 数据压缩:启用SpringBoot的gzip压缩,配置如下:
properties复制server.compression.enabled=true
server.compression.mime-types=application/json,text/html
server.compression.min-response-size=1024
5. 安全防护措施
5.1 防刷单机制
采用三要素验证:
- 微信实名认证 + 2. 人脸核身 + 3. 预约地址校验
关键代码示例:
java复制public boolean antiCheating(String openId, String faceToken, Location loc) {
// 校验微信实名
WxUserInfo user = wxService.getUserInfo(openId);
if(!user.isRealNameVerified()) return false;
// 人脸比对
boolean faceMatch = faceService.verify(faceToken, user.getIdCard());
// 地理位置合理性检查(防止代预约)
boolean locationValid = locationService.checkHomeAddress(openId, loc);
return faceMatch && locationValid;
}
5.2 敏感数据保护
医疗数据采用字段级加密:
- 身份证号:AES加密存储
- 诊断记录:单独加密表存储
- 数据库开启TDE透明加密
加密密钥通过HSM硬件模块管理,实现密钥轮换。在application.yml中配置如下:
yaml复制security:
encryption:
key-version: v3
hsm-slot: 2
6. 部署实施要点
6.1 服务器配置建议
最低生产环境要求:
- 2核4G云服务器(农村并发量不高)
- 单独RDS实例(医疗数据必须独立部署)
- Redis哨兵模式(保障缓存可用性)
我们实际采用的阿里云配置:
plaintext复制ecs.c6.large (2vCPU 4GiB)
rds.mysql.s3.large (2vCPU 8GiB)
redis.amber.master.small.multithread (1G内存)
6.2 离线应急方案
针对网络中断情况开发了离线模式:
- 村医APP端缓存最近100条预约
- 使用SQLite存储临时诊疗记录
- 网络恢复后通过差异同步机制上传数据
关键同步逻辑:
java复制public void syncOfflineData(List<Record> records) {
records.forEach(record -> {
if(record.getStatus() == SYNC_PENDING) {
try {
remoteService.upload(record);
record.setStatus(SYNC_DONE);
localDb.update(record);
} catch (Exception e) {
logger.error("同步失败", e);
// 加入重试队列
retryQueue.add(record);
}
}
});
}
7. 踩坑实录与解决方案
7.1 微信登录态维护
初期采用的标准session方案在农村频繁掉线,后改进为双token机制:
- access_token:短期令牌(2小时)
- refresh_token:长期令牌(7天)
刷新流程:
mermaid复制graph TD
A[小程序启动] --> B{检查access_token}
B -->|有效| C[正常请求]
B -->|无效| D[用refresh_token获取新access_token]
D -->|成功| E[更新本地存储]
D -->|失败| F[跳转登录页]
7.2 高德地图偏移问题
农村地区常遇到坐标偏移,解决方案:
- 在小程序端获取原始GPS坐标
- 服务端调用高德API进行GCJ-02转换
- 存储转换后的坐标供业务使用
转换代码示例:
java复制public GeoPoint convertGPSToGCJ(GeoPoint point) {
String url = String.format(
"https://restapi.amap.com/v3/assistant/coordinate/convert?locations=%s,%s&coordsys=gps&key=%s",
point.getLongitude(),
point.getLatitude(),
amapKey
);
// 发送HTTP请求并解析结果...
}
特别注意:2023年起高德转换接口需要企业认证,个人开发者账号可能无法使用
8. 项目扩展方向
现有系统基础上,我们正在扩展三个功能:
- 物联网设备接入:血压仪、血糖仪数据自动上传
- 电子病历共享:与县级医院系统对接
- 药品配送跟踪:整合乡村物流系统
物联网设备接入示例协议:
json复制{
"deviceId": "BP_0001",
"type": "blood_pressure",
"values": {
"systolic": 125,
"diastolic": 82,
"pulse": 76
},
"timestamp": "2023-07-15T14:30:00Z"
}
这套系统经过一年运行,关键指标如下:
- 平均预约响应时间:3分12秒
- 医生接单率:92.7%
- 医保结算成功率:88.4%
- 用户满意度:4.8/5.0
在具体实施时,建议先与当地卫健部门确认医保接口规范,不同地区的对接要求差异很大。我们完整项目已开源,包含部署手册和接口文档,获取方式见仓库README说明。
