
最近刚把一套“适老化老人健康预警小程序”从零到一完整跑通。技术栈不花哨但很务实Python做后端uniapp写前端最终以微信小程序形态交付。为什么要加“适老化”三个字因为我在家里给外婆试用的第一周就明白了——她根本不会用超过三个以上按钮的应用。健康预警这件事从来不是给老人做一个“系统”而是给老人和子女一起装一根“保险丝”老人端只保留记录和求助分析、趋势、异常通知全部放到子女端和Python后端完成。这篇会把整个项目的完整链路写出来内容包括需求拆解、Python后端规则引擎、uniapp小程序开发以及一堆网上搜起来非常零散的坑包体积超限、订阅消息授权、顶部导航高度适配、日志不打印、后台定位不更新。如果你之前做过2048小游戏、校园食堂订餐这类小程序对这个项目的复杂程度肯定会有同感——它的功能不多但每一样都要求“不能错”。预警触达率是这个项目的命根子漏判一次、误报一次老人和子女都会产生“狼来了”的疲惫感所以整个项目我做得最重的就是规则判定和异常通知这两块。1. 需求拆解与系统架构设计1.1 老人健康预警的真实痛点先别急着写代码把场景想透。老年健康预警的核心使用场景不是“老人每天打开手机点几下”而是“子女不在身边时老人身体出现异常能不能及时被发现”。我梳理下来真正的痛点有三个第一个是记录门槛。让老人每天手动输入血压、心率、血糖本身就是反人性的。很多老人连测量设备都懒得开更别说在小程序里找到录入入口了。所以适老化第一个原则是录入路径必须短首页上就得有明晃晃的“记血压”“记血糖”大按钮字号要大点击区域要大。第二个是判断权该交给谁。老人自己看数值基本看不出趋势变化只觉得“哎今天好像高了”但高了多少、连续几天在上升他们没概念。这个判断逻辑应该放在后端不能靠老人自己记。第三个是通知不能依赖老人主动找人。异常出现的时候子女应该是收到消息的那个人而不是老人打电话告知。这也是为什么订阅消息推送是整个系统的核心链路不是锦上添花是刚需。1.2 技术选型的逻辑为什么不是原生小程序这个组合我权衡过好几轮。Python uniapp 微信小程序在技术社区里不算什么新东西但它非常适合这类“数据采集 规则判断 消息触达”的项目。后端选Python用的是FastAPI框架。原因很简单健康数据的字段校验多、类型杂FastAPI配合Pydantic做请求体校验非常方便而且它自带自动生成接口文档联调时候省了很多沟通成本。如果换Flask这些校验逻辑得自己手写时间成本高不少。另外Python生态后续做趋势分析和异常预测模型很方便pandas和sklearn直接就能接进来。前端选uniapp核心原因是它能把一套Vue代码编译成微信小程序、H5和Android/iOS App。我当时明确知道这项目大概率会从微信小程序扩展到App端如果直接用原生小程序开发后面等于是重写一套。uniapp的组件化写法配合Vue语法做页面和交互的效率高很多。微信小程序的载体优势不用多说——老人手机里微信普及率很高不需要额外安装App。这里有个很实在的细节很多老人拿到手机根本不知道“应用商店”是什么但你跟他说“在微信里点这个小程序”他能理解。1.3 整体架构与数据流向整套系统的链路是这样的老人端小程序uniapp负责录入体征、触发SOS、展示当天状态子女端在同一个小程序里以绑定身份查看趋势和接收通知。两者共用同一个后端FastAPI服务。后端连接MySQL存结构化数据Redis做缓存和连续异常计数。预警引擎在后端独立运行命中规则后调用微信订阅消息接口把异常情况推给子女。数据从产生到触达的完整路径是老人点“记血压”按钮输入三个数字提交到/api/health/record后端先做字段校验再写入数据库紧接着在内存和Redis里跑一次规则引擎。如果当前记录触发黄色或红色预警后端会尝试推送订阅消息。这套流程的要求是从采集到推送耗时尽量控制在500毫秒以内不然老人刚点完提交过了两三秒才收到本地提示体验就很差。架构上我特意把规则引擎独立成模块没有散落在接口里面写if else。这样后续加新指标、改阈值、接自适应基线都不需要动接口骨架只改引擎内部逻辑就行。2. 健康预警规则引擎与Python后端实现2.1 预警阈值怎么定才靠谱预警阈值是整个项目里最不能拍脑袋的部分。我的做法是先参考通行的高血压分级、血糖参考范围和静息心率标准然后结合工程实际做一个“提醒阈值表”。这里必须强调它不是医疗诊断标准只是健康提醒参考页面和推送里都明确标了“结果仅供参考如有不适请及时就医”。指标正常参考区间黄色预警区间红色预警区间收缩压90~140141~159≥160 或 ≤80舒张压60~9091~99≥100 或 ≤50静息心率60~10050~59 或 101~119≥120 或 ≤45空腹血糖3.9~6.16.1~7.0≥7.1 或 ≤3.8餐后血糖7.87.8~11.1≥11.2血氧饱和度95%~100%90%~94%90%单看阈值表还不够。血压计、血糖仪这种设备本身有误差老人测量时姿势不对也可能导致数值忽高忽低。如果每次红色预警都立刻推给子女一个月下来子女就麻木了。所以我在规则引擎里加了“连续异常确认”逻辑首次红警先触本地页面告警同时记录一次如果24小时内第二次记录仍为红色才升级推送给子女。这样误报率会降很多。2.2 规则引擎的实现思路规则引擎的核心就两件事单次记录阈值判断和历史记录趋势确认。我单独写了一个引擎模块接口调它它调Redis拿历史记录最后返回分级结果和推送动作。from datetime import datetime, timedelta from typing import Optional class HealthAlertEngine: RED_KEYS {systolic, diastolic, heart_rate, blood_glucose, blood_oxygen} def __init__(self, redis_client): self.redis redis_client def evaluate(self, record: dict, elderly_id: str) - dict: level self._threshold_check(record) if level normal: return {level: normal, need_push: False} # 连续异常确认红色需24小时内再次红警黄色需48小时内再次黄警 confirm_window {red: 24, yellow: 48}.get(level, 24) count_key fhealth:alert:{elderly_id}:{level} current self.redis.get(count_key) current int(current) if current else 0 current 1 expire timedelta(hoursconfirm_window) self.redis.setex(count_key, expire, current) if current 2: self.redis.delete(count_key) return {level: level, need_push: True} return {level: level, need_push: False} def _threshold_check(self, record: dict) - str: # 这里实际是查阈值表做判断代码从略 levels [] for key in record: if key in self.RED_KEYS: levels.append(self._check_single(key, record[key])) if red in levels: return red if yellow in levels: return yellow return normal这套逻辑的巧妙之处在于把“单日单次异常”和“持续异常”分开对待。单次红警只做记录给用户一个温和的本地警示连续两次才触发强通知让子女看到的信息是有分量的降低了骚扰感。我用Redis的setex设置窗口期好处是做计数同时能自动过期省去手动清理历史状态的麻烦。如果不用Redis直接查数据库里最近24小时的红警记录数量其实也能实现但每次判断都要多一次SQL查询高频录入时压力大。2.3 后端接口设计与订阅消息推送链路订阅消息是微信小程序推送的官方通道。2019年起模板消息下线后新的订阅消息机制做了很大的限制默认一次性订阅用户点一次授权只能收到一条消息如果用户没有再次点击授权下一次推送就会失败。针对健康预警场景我的策略是在老人每次成功录入健康数据后立刻在页面内拉起订阅授权弹窗请求“健康预警通知”模板。这样老人养成“记录完了顺手点一下允许”的习惯后子女才能稳定收到后续异常推送。如果等异常发生时再请求授权用户大概率已经在忙乱中忽略了推送就推不出去。Pythont后端推送的核心代码分两步先拿access_token再调用发送接口。access_token需要缓存微信接口有每日调用上限。router.post(/alert/send-subscribe) async def send_subscribe_alert(payload: SubscribeAlertIn): # 1. 拿access_token缓存2小时 token await get_access_token(appid, secret) # 2. 调用微信订阅消息接口 url https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token token data { touser: payload.openid, template_id: payload.template_id, page: pages/trend/trend, data: { thing1: {value: 血压异常提醒}, phrase2: {value: payload.level_name}, thing3: {value: payload.measure_value} } } async with httpx.AsyncClient() as client: resp await client.post(url, jsondata) return resp.json()这里有个细节订阅消息模板里的thing1、phrase2是模板字段占位符必须在微信公众平台申请模板后照抄不能自己命名。数据内容长度也有限制比如thing类字段最多20个字符超了会报错。2.4 接口设计清单接口路径方法用途备注/api/health/recordPOST记录一项体征数据支持血压、心率、血糖、血氧/api/health/trendGET查历史趋势支持分页返回近30天聚合数据/api/health/alert/listGET查询历史预警记录子女端查看/api/alert/sosPOST紧急求助触发后立即推送子女/api/elder/bindPOST子女绑定老人通过分享链接参数绑定/api/subscribe/sendPOST后端主动推送订阅消息由规则引擎触发接口设计上我坚持一点业务简单但字段校验不能省。健康数据如果录了一个负数、或者心率填了200多这些脏数据会直接污染趋势分析后端接口要用Pydantic严格限制数值范围。3. uniapp开发微信小程序的实操链路3.1 工程初始化与manifest配置uniapp项目我用HBuilderX创建选“默认模板”。创建后第一件事是改manifest.json里的微信小程序配置填写从微信公众平台拿到的AppID配置位置权限、蓝牙权限等必要声明。如果要用wx.startLocationUpdateBackground做后台定位必须在manifest.json里添加requiredPrivateInfos声明否则真机上接口直接fail。{ mp-weixin: { appid: wx你的appid, setting: { urlCheck: false }, requiredPrivateInfos: [ getLocation, startLocationUpdateBackground ], permission: { scope.userLocation: { desc: 用于获取老人位置信息以便在异常时通知家属 } } } }有个很常见的坑urlCheck在开发阶段关了之后微信开发者工具里随便填什么请求域名都能过。但当你要发布体验版、正式版时必须在公众平台配置request合法域名而且必须是备案过的HTTPS域名。我代码里到处用的是局域网IP联调一上正式环境全换了域名这步在开发期就得想好不要等到提交审核前才改。3.2 页面结构与适老化UI落地页面总共四张首页、记录页、趋势页、我的设置。记录页是整个项目的核心交互页适老化设计要求都体现在里面。首页是老人打开小程序后见到的第一个界面我做了几个大字卡片不搞那种目录式导航就是一眼能看懂当天状态。今天有没有记录过血压、上次测量时间、子女端最后一次上线时间。记录页里血压录入拆成了三个大输入框收缩压、舒张压、心率每个输入框下面是快速加减按钮老人可以点加号减号微调也可以直接大数字键盘输入。提交按钮占了半个屏幕宽按钮文案就俩字“记录”。字号是适老化的第一个硬指标。小程序里我基准字号用了32rpx关键数字区域用到56rpx按钮文案最小40rpx。第二个硬指标是点击区域微信官方建议可点击区域最小44x44逻辑像素我在记录页把这些大按钮都做到了至少88rpx高约等于两个官方建议高度实测老人点击准确率高很多。第三个细节是提示反馈。老人点了提交如果只是屏幕闪一下他们根本不知道成没成功。我在页面上加了明显的结果反馈条成功时显示绿色底白字的“记录成功辛苦了”并伴有震动反馈wx.vibrateShort。如果误触SOS会在5秒倒计时内提供“我不是故意的”取消按钮。顶部导航栏高度这个问题很多人踩坑。如果用了自定义导航栏状态栏高度要用uni.getSystemInfoSync().statusBarHeight动态获取导航栏整体靠胶囊按钮定位。不能写死一个像素值因为不同机型的刘海屏、灵动岛高度差异很大。const systemInfo uni.getSystemInfoSync() const menuButton uni.getMenuButtonBoundingClientRect() const navBarHeight (menuButton.top - systemInfo.statusBarHeight) * 2 menuButton.height3.3 包体积超限2MB极限生存编译微信小程序时uniapp控制台报过最恶心的错就是source size 2612kb exceed max limit 2mb。2612kb听着不算大但微信就给2MB的位置超了连预览版都上传不上去。我处理这个问题的顺序是先看构建产物里到底是什么占了大头。我遇到的第一大元凶是图片底图、图标全放在static/目录里还都是PNG。解决办法是把所有非必要图片传到对象存储代码里引用网络路径小程序里能用CSS画的图标就尽量不要用图片。第二招是配置分包。健康预警的所有业务页面可以拆成两部分主包放首页、绑定、基础公共组件分包放记录、趋势、SOS这些低频页面。微信小程序启动时只加载主包分包在用户进到对应页面时才下载总包体积限制瞬间就宽松了。{ pages: [ pages/index/index, pages/bind/bind ], subPackages: [ { root: packageRecord, pages: [ pages/record-input/record-input, pages/trend/trend ] }, { root: packageSos, pages: [ pages/sos/sos ] } ] }分包之后有个坑主包和分包之间的页面跳转路径要以分包root开头比如/packageRecord/pages/record-input/record-input。有些教程里uni.navigateTo写旧地址分包一启用就白屏报错。第三招是精简第三方库。初始我引了一个完整的图表库做趋势图结果未压缩的体积直接干到2MB以上。后来我把趋势图改成用canvas手绘折线整个图表相关代码不到10KB。如果数据量不大真没必要为了一个图表页拖一整个库进来。3.4 日志不打印、分页加载与联调那些事“uniapp不打印日志信息”这个问题我在群里见人问了好几次。其实分情况如果你在微信开发者工具的控制台看不到console.log先检查是不是真机调试的“自动预览”模式如果是HBuilderX内置浏览器能打日志、微信开发者工具里没有多半是ES6转ES5配置、基础库版本不一致导致的。最土但有用的办法是在小程序页面里放一个隐藏的调试入口把最近几条API请求和响应直接渲染成页面文本这样不管在哪端跑都能看到数据流。趋势页做分页加载时注意onReachBottom在设置了分包、启用下拉刷新后可能会触发乱序。我的做法是加一个loading状态锁防止快速滚动时重复触发加载请求。onReachBottom() { if (this.loading || this.hasMore false) return this.loading true this.page 1 this.fetchTrendList(this.page).finally(() { this.loading false }) }联调环节我强烈建议配Charles抓包。尤其是真机上跑微信小程序页面报错不够直观看一眼HTTPS请求的具体参数和返回体很多问题瞬间就明白了。手机和电脑连同一个WiFi电脑端装好Charles证书手机端代理填电脑IP和8888端口就能看到小程序发的每个请求。之前遇到的“10002”网络错误抓包一看就是后端返回了500跟小程序本身没关系。FastAPI后端联调时还要处理跨域问题。虽然微信小程序开发版被允许关闭域名校验但H5端调试、开发者工具内部请求都是跨域场景统一加CORSMiddleware最省事。from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins[*], allow_methods[*], allow_headers[*], )4. 踩坑实录开发中遇到的问题与排查4.1 高频问题速查表现象可能原因解决办法编译报 source size exceed max limit 2mb分包没配/图片太多/第三方库过大图片云端化分包加载精简依赖真机预览 console.log 打不出来开发工具与真机日志通道未开用vConsole或页面内渲染调试信息订阅消息推送失败用户没点允许/模板ID写错/字段名不符录入完成后立刻引导授权校准模板字段请求返回 10002 之类网络错误域名未备案/后端5xx/TLS版本过低抓包确认检查合法域名配置uniapp startLocationUpdateBackground 失败缺少 requiredPrivateInfos 声明manifest.json 中补齐并重新编译分包页面跳转白屏路径未以分包root开头核对uni.navigateTo新路径苹果设备防截屏导致功能异常iOS隐私策略小程序无法完全绕开敏感数据不明文展示离开页面自动隐藏这个表格里的问题我几乎每一条都亲手碰到过。特别是订阅消息推送失败第一次上线时死活推不出去后来发现是模板里的thing3字段值超了20个字符微信直接忽略了整条消息。这就是为什么我的后端推送代码里专门加了一个字段长度裁剪函数。4.2 微信能力边界相关的几个坑后台定位是个典型例子。uniapp里封装了uni.startLocation但如果你想让App退到后台还在持续更新位置小程序端就必须用微信原生的wx.startLocationUpdateBackground。这个接口不仅需要在manifest里声明还要在页面上先用uni.authorize请求用户授权。女儿端最关心的“老人离开常驻范围”场景就是靠这个定位能力做的。苹果防截屏这个需求要特别注意。有需求方提过“小程序里能不能防截屏”但iOS的私有防截屏API在小程序环境里根本调不到任何试图绕过的方案都有被审核拒绝的风险。合规的做法是不展示不必要的敏感信息检测到页面切到后台时自动清除关键界面内容。小程序签名这块也值得一提。微信开发者工具上传代码时对代码包有签名校验。uniapp打包上传如果出现签名不一致多半是用了自带云打包或本地打包配置的证书不对。我第一次上架时在“小程序后台-开发管理-开发设置”里重置了代码上传密钥重新配置后才顺利上传。4.3 老人操作场景的健壮性设计健康预警项目和小游戏不一样它的容错设计要格外谨慎。我专门为老人操作场景做了三个保护层第一是防重复提交。老人可能因为网络卡顿没看到成功反馈连续点了几次“记录”。如果后端不做幂等同一组数据会重复入库多次趋势分析直接被污染。我的解法是小程序端提交前生成一个request_id同一个老人60秒内的相同体征值直接返回上次结果不重复入库。第二是SOS误触保护。紧急求助按钮要够大、够显眼但又不能放在最容易碰到的地方。我把它放到了首页最下方点击后不立即触发先出现一个5秒倒计时和“取消”按钮倒计时结束才真正发送SOS消息。如果误触了老人可以轻松取消。第三是弱网兜底。老人的手机网络环境通常没年轻人好家里总有信号弱的位置。小程序端的所有数据提交操作都有本地暂存机制一旦请求失败数据先缓存到uni.setStorage等网络恢复后自动补传。这样即使刚才那会儿没连上数据也不会丢。5. 上线、交付与后续扩展5.1 认证、备案与类目审核上一轮交付2048小游戏源码的时候流程很简单个人主体就能过审。但健康预警这种涉及用户体征数据的项目类目审核要严格得多。公众平台里“医疗-健康管理”类目通常需要企业或机构主体个人主体可选类目很少基本走不通。如果确实只有个人主体产品设计上要弱化“医疗”属性避免出现诊断、治疗等敏感词重点突出“记录与提醒”。企业主体的微信认证费用是每年300元审核周期一般1到7个工作日。另外后端接口域名必须备案我用的国内云服务器ICP备案基本要花两到三周时间。所以这个项目的时间规划光认证加备案就得预留至少一个月别指望一周全部上线。5.2 从微信小程序扩展到Appuniapp最大的优势就是这套代码还能继续复用。后面如果想打包成Android/iOS App需要注意几个差异点。最核心的是推送通道微信小程序用订阅消息App根本没有订阅消息的概念需要换成厂商推送小米、华为、OPPO、vivo或者集成个推这类聚合推送SDK服务端推送逻辑也要相应调整。App端的定位能力比小程序强很多可以真正做前台服务和后台持续定位监测这也是为什么当初选型时我坚持用uniapp而不是原生小程序——一旦扩展到App端代码不需要推到重来。另外uniapp和uniappx的区别值得说一句。uniappx基于uvue性能和内存占用比uniapp更好但生态和第三方插件还在完善中。当前这个项目建议继续用uniapp等uniappx的社区组件成熟后再说迁移。热更新方面uniapp可以打wgt资源包实现App端的热更新不必每次发版都走应用商店审核。但要注意热更新不能更新原生插件和原生SDK只能更新前端页面逻辑。而且现在各大应用商店对热更新的审核越来越严格涉及用户健康数据的小程序扩展还是要以合规和安全为先。5.3 后续能力建设自适应阈值与AI预警预警引擎还有很大的升级空间。现在我用的是固定阈值表但每个人身体状况不一样老人有基础病时正常值可能本来就偏离标准区间。后续可以加入个人基线自适应根据老人最近30天的记录预测下一个测量时刻的期望区间当实际数值偏离个人基线超过一定幅度时触发预警而不是死磕通用阈值表。再往下走可以考虑接蓝牙设备。现在市面上很多血压计、血糖仪、手表都支持蓝牙传输数据老人不需要手动输入设备测量完自动同步到小程序录入门槛直接降到零。室内定位可以用蓝牙Beacon做室外定位继续用微信的定位接口。Python后端这里趋势预测模型也可以做起来。用历史数据训练一个简单的回归模型预测老人未来几天的血压走势等趋势即将越过阈值时提前提醒这就是更“智能”的预警了。我现在代码库里的规则引擎架构就是为了将来无缝接这些模型阈值判断和趋势预测可以并行跑谁命中谁触发。这个项目做完我的一个很深体会是适老化不是把字体调大一倍就完事而是要把“预警”两个字的闭环走到极致——采集要稳、判断要准、通知要达、操作要少。订阅消息授权和包体积这两个坑几乎每个第一次做健康类小程序的人都会遇到我把能绕的弯都尽量写在了上面。再做一个很个人的感觉健康预警的核心一定不能只在小程序里转圈Python后端规则引擎才是大脑小程序只是老人和子女手里最趁手的遥控器。如果后续再做一版我会把自适应基线和蓝牙设备接入放在第一优先级这两个能力提升价值是最明显的。