Uni-app + Node.js 多端疫苗预约:1套代码覆盖微信/支付宝/H5
Uni-app Node.js 多端疫苗预约系统开发实战最近在开发一个疫苗预约系统时遇到了一个棘手的问题如何在微信小程序、支付宝小程序和H5三个平台上实现一套代码统一管理经过多次尝试和优化最终采用Uni-app框架结合Node.js后端成功实现了多端适配的解决方案。本文将分享这个过程中的关键技术和实践经验。1. 技术选型与架构设计选择合适的技术栈是项目成功的关键。经过对比分析我们最终确定了以下技术组合前端框架Uni-app基于Vue.js的跨端开发框架后端语言Node.jsKoa2框架数据库MongoDB文档型数据库适合快速迭代部署环境Docker容器化部署这种组合的优势在于开发效率高Uni-app支持一次编写多端发布性能优异Node.js非阻塞I/O适合高并发场景扩展性强MongoDB的Schema-free特性便于后期功能扩展系统架构图如下用户端(微信/支付宝/H5) → Uni-app前端 → Node.js API网关 → 业务微服务 → 数据库集群2. Uni-app多端适配实战2.1 条件编译处理平台差异不同平台有各自的API和组件差异Uni-app提供了条件编译机制// #ifdef MP-WEIXIN wx.login({ success(res) { console.log(微信登录成功, res.code) } }) // #endif // #ifdef MP-ALIPAY my.getAuthCode({ scopes: auth_user, success(res) { console.log(支付宝授权成功, res.authCode) } }) // #endif2.2 统一API封装为简化调用我们封装了统一的网络请求模块// utils/request.js export default function request(options) { return new Promise((resolve, reject) { uni.request({ url: config.baseUrl options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: uni.getStorageSync(token) }, success: (res) { if (res.statusCode 200) { resolve(res.data) } else { reject(res.data) } }, fail: (err) { reject(err) } }) }) }2.3 多端UI适配技巧虽然Uni-app提供了跨端组件但各平台样式仍需微调/* 通用样式 */ .vaccine-card { border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.1); /* 微信小程序特有样式 */ /* #ifdef MP-WEIXIN */ margin-bottom: 12px; /* #endif */ /* 支付宝小程序特有样式 */ /* #ifdef MP-ALIPAY */ border: 1px solid #eee; /* #endif */ }3. Node.js后端核心实现3.1 API接口设计采用RESTful风格设计API主要接口包括接口路径方法描述/api/vaccinesGET获取疫苗列表/api/appointmentsPOST创建预约/api/appointments/:idGET获取预约详情/api/hospitalsGET获取接种点列表3.2 预约业务逻辑预约是核心功能需要考虑并发控制和库存管理// controllers/appointment.js const createAppointment async (ctx) { const { vaccineId, hospitalId, date, timeSlot } ctx.request.body const userId ctx.state.user.id // 使用事务确保数据一致性 const session await mongoose.startSession() session.startTransaction() try { // 检查库存 const vaccine await Vaccine.findOne({ _id: vaccineId, hospitals: hospitalId }).session(session) if (!vaccine || vaccine.stock 0) { throw new Error(疫苗库存不足) } // 检查时间段是否可用 const existing await Appointment.countDocuments({ hospitalId, date, timeSlot, status: { $ne: CANCELLED } }).session(session) if (existing MAX_SLOT_CAPACITY) { throw new Error(该时间段已约满) } // 创建预约 const appointment new Appointment({ userId, vaccineId, hospitalId, date, timeSlot, status: PENDING }) // 扣减库存 await Vaccine.updateOne( { _id: vaccineId }, { $inc: { stock: -1 } } ).session(session) await appointment.save({ session }) await session.commitTransaction() ctx.body { code: 0, data: appointment } } catch (err) { await session.abortTransaction() ctx.body { code: -1, message: err.message } } finally { session.endSession() } }3.3 性能优化措施为提高系统响应速度我们实施了以下优化Redis缓存高频访问数据如疫苗列表、医院信息数据库索引为查询字段添加适当索引接口合并使用GraphQL减少请求次数CDN加速静态资源部署到CDN4. 关键问题解决方案4.1 多平台用户体系整合不同平台的用户标识不同我们设计了统一的用户映射表// models/User.js const userSchema new mongoose.Schema({ unionId: String, // 唯一标识 wechatOpenId: String, // 微信openId alipayUserId: String, // 支付宝userId h5Token: String, // H5端token profile: { name: String, idCard: String, phone: String } // 其他字段... })4.2 高并发预约处理采用队列机制处理高峰期预约请求// services/queue.js const Queue require(bull) const appointmentQueue new Queue(appointment, { redis: { host: redis-server, port: 6379 } }) // 处理任务 appointmentQueue.process(async (job) { const { userId, vaccineId, hospitalId, date, timeSlot } job.data // 这里包含预约的核心逻辑 return createAppointment(userId, vaccineId, hospitalId, date, timeSlot) }) // API接口中 router.post(/appointments, async (ctx) { const job await appointmentQueue.add(ctx.request.body) ctx.body { code: 0, data: { jobId: job.id } } })4.3 消息通知系统集成多平台消息推送能力// services/notification.js async function sendAppointmentNotification(userId, message) { const user await User.findById(userId) // 微信模板消息 if (user.wechatOpenId) { await wechat.sendTemplateMessage({ touser: user.wechatOpenId, template_id: TEMPLATE_ID, data: { // 模板内容 } }) } // 支付宝服务通知 if (user.alipayUserId) { await alipay.sendNotification({ user_id: user.alipayUserId, template: NOTIFICATION_TEMPLATE, // 其他参数 }) } // 短信通知 if (user.profile.phone) { await sms.send({ phone: user.profile.phone, content: message }) } }5. 部署与监控5.1 Docker容器化部署使用docker-compose编排服务version: 3 services: app: build: . ports: - 3000:3000 depends_on: - mongo - redis environment: - NODE_ENVproduction - MONGO_URImongodb://mongo:27017/vaccine - REDIS_URLredis://redis:6379 mongo: image: mongo:4 volumes: - mongo-data:/data/db redis: image: redis:6 volumes: - redis-data:/data volumes: mongo-data: redis-data:5.2 性能监控方案集成APM工具监控系统健康状态// 监控初始化 const apm require(elastic-apm-node).start({ serviceName: vaccine-booking, serverUrl: http://apm-server:8200, environment: production }) // 异常捕获 process.on(unhandledRejection, (err) { apm.captureError(err) }) // API性能监控 app.use((ctx, next) { const span apm.startSpan(HTTP Request) return next().then(() { span.end() }).catch(err { apm.captureError(err) span.end() throw err }) })6. 经验总结与避坑指南在实际开发中我们遇到并解决了以下典型问题微信小程序审核问题医疗类小程序需要特殊资质提前准备《互联网医疗信息服务资格证书》支付宝沙箱环境差异部分API在沙箱环境行为与生产环境不一致需尽早进行真机测试H5端微信授权问题在非微信浏览器中需要降级处理改用手机号验证时间处理时区问题统一使用UTC时间存储前端按需转换本地时间性能优化方面以下几点特别值得注意小程序端注意setData调用频率避免一次传递过大对象合理使用分包加载控制主包体积在2MB以内接口设计遵循最小数据原则只返回必要字段启用HTTP/2提升多请求场景下的性能这套技术方案已在多个城市的疫苗接种点实际应用高峰期支撑了单日超过10万次的预约请求。开发过程中最大的体会是选择合适的技术组合比追求最新技术更重要良好的架构设计能显著降低后期维护成本。