☰
适老化健康预警小程序全链路开发:Python后端与uniapp实战
2026/10/7 17:14:18 网站建设 项目流程

最近刚把一套“适老化老人健康预警小程序”从零到一完整跑通。技术栈不花哨,但很务实: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 = f"health:alert:{elderly_id}:{level}" current = self.redis.get(count_key) current = int(current) if current else 0 current += 1 expire = timedelta(hours=confirm_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, json=data) 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.height

3.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 从微信小程序扩展到App

uniapp最大的优势就是这套代码还能继续复用。后面如果想打包成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后端规则引擎才是大脑,小程序只是老人和子女手里最趁手的遥控器。如果后续再做一版,我会把自适应基线和蓝牙设备接入放在第一优先级,这两个能力提升价值是最明显的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询