1. 项目概述
新冠疫苗预约小程序是一个基于Python和Uniapp技术栈开发的公共卫生服务应用。作为一名参与过多个医疗信息化项目的开发者,我深知这类系统在特殊时期的重要性。这个小程序的核心目标是解决传统线下预约方式存在的排队时间长、信息不对称、资源分配不均等问题。
在实际开发中,我们采用了前后端分离的架构模式。后端使用Python+Django构建RESTful API服务,前端采用Uniapp框架实现跨平台兼容。这种技术组合既保证了开发效率,又能满足多终端适配的需求。系统上线后,单日最高处理了超过2万次预约请求,验证了架构的可靠性。
2. 系统架构设计
2.1 技术选型考量
选择Python+Django作为后端主要基于三个考量:
- 开发效率:Django自带Admin后台、ORM等组件,能快速构建管理系统
- 社区支持:丰富的第三方库如Django REST framework简化API开发
- 性能平衡:虽然不及Go等语言,但配合Redis缓存完全能满足预约场景需求
前端选用Uniapp则是因为:
- 一套代码可编译到微信小程序、H5和App多个平台
- 基于Vue.js的语法对前端开发者友好
- 丰富的插件市场可快速集成地图、扫码等能力
2.2 核心架构图示
code复制[客户端] ←HTTPS→ [Nginx] ←uWSGI→ [Django]
↑
[Redis缓存]
↓
[MySQL主从集群] ←→ [定时任务]
这个架构中:
- Nginx负责负载均衡和静态文件服务
- uWSGI作为应用服务器运行Django
- Redis缓存热点数据如疫苗库存
- MySQL主从分离提高查询性能
3. 关键功能实现
3.1 预约流程设计
完整的预约流程包含以下步骤:
- 地理位置授权获取用户坐标
- 查询5km内可用接种点(使用Haversine公式计算距离)
- 展示可选时间段(每30分钟为一个slot)
- 提交个人信息并验证
- 生成唯一预约码(UUID+时间戳哈希)
重要提示:时间段库存需要使用Redis原子操作保证一致性,示例代码:
python复制with redis.lock('time_slot_lock'): remaining = redis.decr(f'slot:{date}:{time}') if remaining >= 0: # 创建预约记录 else: raise Exception("该时段已约满")
3.2 高并发处理方案
针对预约高峰期的并发问题,我们实施了以下措施:
- 库存预扣:先扣减Redis缓存库存,再异步同步到数据库
- 读写分离:查询走从库,写入走主库
- 限流策略:Nginx层限制单个IP的请求频率
- 队列缓冲:使用Celery处理非实时操作如发送短信
实测数据显示,这些优化使系统在1秒内能处理约500个并发请求,平均响应时间控制在200ms以内。
4. 数据库设计要点
4.1 核心表结构
sql复制CREATE TABLE `vaccination_site` (
`id` int PRIMARY KEY AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`address` varchar(255) NOT NULL,
`longitude` decimal(10,7) NOT NULL,
`latitude` decimal(10,7) NOT NULL,
`contact_phone` varchar(20) NOT NULL
);
CREATE TABLE `time_slot` (
`id` int PRIMARY KEY AUTO_INCREMENT,
`site_id` int NOT NULL,
`date` date NOT NULL,
`start_time` time NOT NULL,
`end_time` time NOT NULL,
`max_capacity` int NOT NULL,
FOREIGN KEY (`site_id`) REFERENCES `vaccination_site` (`id`)
);
CREATE TABLE `appointment` (
`id` char(32) PRIMARY KEY, -- 预约码
`user_id` int NOT NULL,
`slot_id` int NOT NULL,
`status` enum('pending','completed','canceled') NOT NULL DEFAULT 'pending',
`created_at` datetime NOT NULL,
FOREIGN KEY (`slot_id`) REFERENCES `time_slot` (`id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
);
4.2 索引优化实践
为提高查询性能,我们为以下字段创建了复合索引:
vaccination_site(longitude, latitude)- 加速附近接种点查询time_slot(date, site_id)- 优化时间段检索appointment(slot_id, status)- 快速统计各时段预约量
通过EXPLAIN分析,这些索引使关键查询的性能提升了8-10倍。
5. 前端实现细节
5.1 跨平台适配方案
Uniapp的条件编译让我们可以针对不同平台做微调:
javascript复制// #ifdef MP-WEIXIN
wx.getLocation({
type: 'gcj02',
success: (res) => {...}
})
// #endif
// #ifdef H5
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(...)
}
// #endif
5.2 性能优化技巧
- 图片懒加载:接种点列表中的图片使用
<image lazy-load> - 数据分页:每次加载20条记录,滚动到底部再加载更多
- 本地缓存:使用uni.setStorage缓存用户基本信息
- 组件按需引入:仅导入当前页面需要的UI组件
这些措施使小程序包体积控制在1MB以内,冷启动时间<800ms。
6. 部署与监控
6.1 服务器配置建议
对于日活1万左右的系统推荐配置:
- 前端:2核4G服务器(静态资源托管)
- 后端:4核8G ×2(负载均衡)
- 数据库:8核16G(SSD存储)
- Redis:2核4G(持久化开启)
使用Supervisor管理进程,配置示例:
ini复制[program:celery]
command=/path/to/venv/bin/celery -A core worker -l info
autostart=true
autorestart=true
6.2 监控指标设置
我们通过Prometheus监控以下关键指标:
- API响应时间(P99 < 1s)
- 数据库连接池使用率(<80%)
- Redis内存占用(<70%)
- 5xx错误率(<0.1%)
当这些指标异常时,会触发企业微信机器人告警。
7. 踩坑经验分享
-
微信登录会话失效:发现iOS设备上有时无法保持登录状态。解决方案是在uni.login()之后立即调用uni.checkSession()验证有效性。
-
地图选点偏差:不同平台返回的坐标系不同(微信用GCJ-02,H5用WGS84)。需要统一转换:
python复制# 使用coordtransform库进行坐标转换
from coordtransform import wgs84_to_gcj02
lng, lat = wgs84_to_gcj02(lng, lat)
-
二维码生成性能:直接在前端生成大量二维码会导致卡顿。改为后端生成后返回URL,前端直接显示图片。
-
时间戳冲突:发现两个用户可能获得相同的预约码。最终采用UUID4 + 时间戳的SHA256哈希作为唯一标识。
这个小程序从设计到上线历时6周,期间最大的体会是:公共卫生类应用必须把稳定性和易用性放在首位。我们在压力测试阶段模拟了10倍于预期的流量,确保系统在真实场景中不会崩溃。