1. 项目概述
作为一名深耕医疗信息化领域多年的开发者,我最近完成了一个基于微信小程序的在线诊疗系统。这个项目让我深刻体会到移动互联网如何重塑传统医疗服务模式。在新冠疫情期间,我亲眼目睹了线下医疗机构的压力,也看到了患者对便捷医疗服务的迫切需求。这促使我着手开发这套系统,它整合了预约挂号、在线问诊、电子处方等核心功能,目前已稳定运行3个月,累计服务患者超过2000人次。
系统采用前后端分离架构,前端使用微信小程序实现患者端和医生端,后端基于Java的SSM框架开发管理平台。数据库选用MySQL 8.0,充分利用其事务特性和JSON字段支持。特别值得一提的是,我们在问诊模块实现了WebSocket长连接通信,确保医患沟通的实时性,实测消息延迟控制在300ms以内。
2. 技术架构解析
2.1 微信小程序端设计
小程序端采用MINA框架,这是微信官方提供的开发框架。我们在架构设计时特别注意了以下几点:
-
页面路由优化:采用分包加载策略,将问诊流程、个人中心等高频功能放在主包,其他功能按需加载。实测启动时间从2.1s降至1.3s。
-
数据缓存策略:
javascript复制// 科室信息缓存示例 wx.setStorageSync('deptCache', { data: res.data, expire: Date.now() + 3600000 // 1小时缓存 }); -
权限控制:通过
wx.getSetting获取用户授权状态,未授权时展示友好引导页。特别注意医疗类小程序需要获取scope.userInfo和scope.writePhotosAlbum权限。
注意:医疗小程序必须通过微信认证,且服务器域名需在微信公众平台配置,包括
request、uploadFile、downloadFile等合法域名。
2.2 后端服务架构
SSM框架组合各司其职:
-
Spring:管理Bean生命周期,处理依赖注入。我们特别配置了事务管理器:
xml复制<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> -
SpringMVC:RESTful接口设计遵循以下规范:
code复制GET /api/v1/appointments 获取预约列表 POST /api/v1/appointments 新建预约 PUT /api/v1/appointments/:id 更新预约状态 -
MyBatis:动态SQL提升查询效率。例如科室搜索接口:
xml复制<select id="searchDepartments" resultType="Department"> SELECT * FROM t_department <where> <if test="name != null">AND name LIKE CONCAT('%',#{name},'%')</if> <if test="type != null">AND type = #{type}</if> </where> ORDER BY create_time DESC </select>
2.3 数据库设计要点
MySQL表设计遵循第三范式,同时针对医疗业务特点做了优化:
-
患者信息表(t_patient):
sql复制CREATE TABLE `t_patient` ( `id` bigint NOT NULL AUTO_INCREMENT, `user_id` varchar(32) NOT NULL COMMENT '关联小程序用户', `name` varchar(50) NOT NULL, `id_card` varchar(18) COMMENT '加密存储', `medical_history` json COMMENT 'JSON格式病史', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `idx_user` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -
问诊会话表(t_consultation):
sql复制CREATE TABLE `t_consultation` ( `id` bigint NOT NULL AUTO_INCREMENT, `patient_id` bigint NOT NULL, `doctor_id` bigint NOT NULL, `status` tinyint DEFAULT 0 COMMENT '0-待接诊 1-进行中 2-已完成', `start_time` datetime, `end_time` datetime, `diagnosis_result` text, PRIMARY KEY (`id`), KEY `idx_doctor` (`doctor_id`,`status`), KEY `idx_patient` (`patient_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 核心功能实现
3.1 预约挂号流程
挂号流程采用状态机模式设计,确保状态流转可控:
-
状态枚举定义:
java复制public enum AppointmentStatus { PENDING(0, "待支付"), PAID(1, "已支付"), COMPLETED(2, "已完成"), CANCELLED(3, "已取消"); // 省略getter方法 } -
预约创建服务:
java复制@Transactional public String createAppointment(AppointmentDTO dto) { // 1. 检查医生排班 Schedule schedule = scheduleMapper.selectById(dto.getScheduleId()); if (schedule == null || schedule.getRemain() <= 0) { throw new BusinessException("该时段已约满"); } // 2. 创建预约记录 Appointment appointment = new Appointment(); BeanUtils.copyProperties(dto, appointment); appointment.setStatus(AppointmentStatus.PENDING.getCode()); appointmentMapper.insert(appointment); // 3. 减少余号 scheduleMapper.decreaseRemain(dto.getScheduleId()); return appointment.getId(); }
3.2 实时问诊实现
采用WebSocket+Redis实现实时通信:
-
WebSocket配置:
java复制@ServerEndpoint("/ws/consultation/{consultId}") @Component public class ConsultationEndpoint { private static final Map<Long, Session> sessions = new ConcurrentHashMap<>(); @OnOpen public void onOpen(Session session, @PathParam("consultId") Long consultId) { sessions.put(consultId, session); } @OnMessage public void onMessage(String message, @PathParam("consultId") Long consultId) { // 消息处理逻辑 } } -
消息时序处理:
code复制患者发送消息 → 服务端接收 → 存入MongoDB → 推送医生端 ↘ 若医生离线 → 存入Redis待推送队列
4. 安全与性能优化
4.1 医疗数据安全措施
-
敏感数据加密:
java复制// AES加密身份证号 public String encryptIdCard(String idCard) { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); return Base64.encodeToString(cipher.doFinal(idCard.getBytes()), Base64.DEFAULT); } -
权限控制矩阵:
资源 患者 医生 管理员 预约记录 自己 相关 全部 处方信息 自己 自己 全部
4.2 高并发场景应对
-
挂号秒杀方案:
java复制public boolean grabSchedule(Long scheduleId) { String lockKey = "schedule_lock:" + scheduleId; try { // 分布式锁 Boolean locked = redisTemplate.opsForValue() .setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS); if (!locked) return false; // 乐观锁更新 int affected = scheduleMapper.decreaseWithVersion( scheduleId, lastVersion); return affected > 0; } finally { redisTemplate.delete(lockKey); } } -
缓存策略对比:
策略 适用场景 实现复杂度 一致性保障 旁路缓存 读多写少 低 中 写穿透 写频繁 高 高 异步刷新 时效性要求不高 中 低
5. 部署与监控
5.1 服务器部署方案
我们采用Docker Compose编排服务:
yaml复制version: '3'
services:
app:
image: openjdk:11-jre
ports:
- "8080:8080"
volumes:
- ./logs:/app/logs
environment:
- SPRING_PROFILES_ACTIVE=prod
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=yourpassword
volumes:
- ./mysql-data:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
5.2 监控指标配置
Prometheus监控关键指标:
yaml复制# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
tags:
application: ${spring.application.name}
重点监控:
- 接口响应时间(http_server_requests_seconds)
- 活跃问诊会话数(custom.consultation.active_count)
- 数据库连接池使用率(hikaricp_connections_active)
6. 踩坑经验分享
-
微信登录会话失效:
问题现象:iOS设备偶尔出现登录状态丢失
解决方案:将session_key从内存缓存改为Redis存储,并设置合理过期时间 -
处方图片上传失败:
问题定位:微信临时路径读取权限问题
修复方案:javascript复制wx.uploadFile({ filePath: tempFilePath, name: 'file', formData: { type: 'prescription' }, success: (res) => { // 处理服务器响应 } }); -
MySQL连接池耗尽:
优化配置:properties复制spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.leak-detection-threshold=5000
这个项目让我深刻认识到医疗系统的特殊之处——它不仅是技术问题,更关乎生命健康。我们在开发过程中特别注重系统的可靠性和数据安全性,所有医疗操作都留有完整的审计日志。未来计划接入医保支付和AI预诊功能,进一步提升用户体验。