范围: 业务流程 / 用户角色 / 数据状态机 / 异常处理 / 入组研究协议 配套: ui-prototype-v10.md (UI 设计) + prototype/index.html (可点击原型) 目标: 把"采集设备 + 后端 + 多患者"的隐性逻辑显性化, 实施前对齐医院/课题组/医生认知
| 角色 | 谁 | 主要职责 | 在系统里能做什么 |
|---|---|---|---|
| 科室医生 | 主治大夫 | 决定哪些患者入组, 看趋势/异常, 调整方案 | 看数据趋势 / 导出报告 / 标注异常 |
| 病房护士 | 执行采集人 | 给病房患者轮流测量, 同一台手表多人用 | 切换门诊号 / 连蓝牙 / 触发测量 / 看实时值 |
| 患者 | 被采集对象 | 戴表 / 配合手动测血压等 | 不直接操作小程序 (一般是护士在操作) |
| 患者家属 | 居家代采集 | 出院后给患者持续采集 (例如妈妈给俩娃) | 输入门诊号 / 连蓝牙 / 触发测量 |
| 课题研究员 | 负责入组的研究人员 | 设计研究协议, 拉数据做统计 | 调 /api/data?patientNo= 拉数据 / 导出 |
| 运维管理员 | 你 + 客户运营 | 配置设备 / 维护服务 / 处理故障 | 数据库管理 / 后端部署 / 用户权限 |
关键认知: 主要操作者不是患者, 是医护。这决定了 UX 必须照顾"医护流水线"场景 (快速切换患者, 实时看连接状态, 不能丢数据)。
1. 患者 A 进诊室
2. 护士拿出手表给患者戴上 (或患者自己已戴)
3. 护士拿出手机扫体验版二维码进小程序
4. 输入患者 A 的门诊号 100234
5. 点 "设备扫描" → 选 S101 → 连接
6. 让患者按表测血压 (或表自动测心率)
7. 数据自动同步入库
8. 护士看一眼首页 "今日采集" 确认有数据
9. 摘表给下一个患者
难点: 切换到下一患者时, 必须切换门诊号. v9 已解决: 点"切换患者"输新号即可.
1. 护士 8:00 巡房, 同一台手表
2. 给患者 B (床位 1, 门诊号 100235): 戴 → 测 → 摘
3. 切换门诊号 100236 → 给患者 C: 戴 → 测 → 摘
4. ... 一轮巡房 8 个患者
5. 最后回护士站把数据汇总查看
难点:
1. 患者 D 出院, 表一并带走
2. 家属 (例如妈妈给俩娃) 用自己微信扫码进小程序
3. 输入孩子 A 门诊号 100237 → 测一周
4. 切换孩子 B 门诊号 100238 → 测一周
5. 数据周期性自动同步
6. 医生在后台 (Tab 3 数据视图) 看趋势
难点:
1. 课题组定义研究协议:
"高血压研究: 每位患者每天 2 次血压 (早 7:00 / 晚 19:00),
持续 14 天, 完成率 ≥ 80% 算有效样本"
2. 入组 50 位患者, 每人门诊号唯一
3. 系统应能告诉运营:
- 当前哪些患者按时测了 (今日完成 X/50)
- 哪些超过 24 小时没测了 (掉队 Y 人)
- 哪些数据异常 (高血压2级 N 例, 需医生干预)
4. 研究结束后导出整组数据做统计分析
难点:
┌──────────────┐
╔════►│ 未连接 │◄═════╗
║ └──────┬───────┘ ║
║ │ ║ adapter 关 / 用户主动断
║ │ 用户点扫描+选 ║
║ ▼ ║
║ ┌──────────────┐ ║
║ │ 连接中 │ ║
║ └──────┬───────┘ ║
║ │ ║
║ 12s 超时 │ SDK 回 connection=true
║ /握手失败 │ ║
║ ▼ ║
║ ┌──────────────┐ ║
║ │ 握手中 │ ║
║ └──────┬───────┘ ║
║ │ ║
║ 5s VPDevice 空 │ type=1 回包 (含 MAC/版本)
║ │ ║
║ ▼ ║
║ ┌──────────────┐ ║
║ │ 已连接 │═════╝
║ └──────┬───────┘
║ │ 90s 无回包 (心跳暗断)
║ │ 或 connection=false
║ ▼
║ ┌──────────────┐
╚═════│ 暗断/重连中 │
└──────────────┘
指数退避 1/2/4s 三次
失败 → 提示用户手动重连
saveData() 触发
│
▼
┌────────────────────┐
│ 立即写 storage 队列 │ (任何时候都先入队, 防丢数据)
└────────┬───────────┘
│
▼
deviceId 已知?
┌───┴───┐
│ 否 │ 是
▼ ▼
┌──────┐ ┌──────────────────┐
│ 留 │ │ 立即 POST /api/ │
│ pending│ │ health-data │
└──────┘ └────────┬─────────┘
等 type=1 成功? │ 失败?
回包后 flush ┌──┴──┐
▼ ▼
┌─────┐ ┌─────────┐
│ 入库 │ │ 留 pending│
└─────┘ └────┬────┘
│
6 重 flush 兜底:
- 2h 定时
- 23:59 兜底
- app.onShow
- 网络恢复
- BLE 重连
- 队列堆 80
小程序启动
│
▼
读 storage.patientNo
│
▼
patientNo 存在?
┌─┴─┐
否 是
│ │
▼ ▼
首页红警 首页蓝条
"未输入 "当前 100234"
门诊号"
│ │
▼ ▼
点[输入] 点[切换]
│ │
└──┬──┘
▼
wx.showModal editable
│
▼
输入新门诊号 → storage.patientNo = "100235"
│
▼
后续 saveData payload 带 patientNo: "100235"
│
▼
服务端 to_chinese_record 写入 record['门诊号']: "100235"
| 规则 | 实施 |
|---|---|
| 一台手表一条物理身份 (mac) | wearable_device.mac 优先匹配 |
| 同手表数据可归属多个门诊号 | 大 JSON 数组每条带 门诊号 字段 |
| 历史无门诊号数据保留 | NULL 字段视为"未分组", 不混淆 v9 数据 |
| 门诊号格式 | 客户当前用 10 位数字 (0012865682), 系统不强校验 |
| 指标 | 异常阈值 | 风险等级 | 视觉 |
|---|---|---|---|
| 血压收缩压 | ≥ 180 或 舒张压 ≥ 120 | 危急 (Crisis) | 🔴 红底白字 + 弹提醒 |
| 血压收缩压 | ≥ 140 或 舒张压 ≥ 90 | 高血压 2 级 | 🟠 橙色高亮 |
| 血压收缩压 | ≥ 130 或 舒张压 ≥ 80 | 高血压 1 级 | 🟡 黄色提示 |
| 心率 | ≥ 120 (静息) 或 ≤ 40 | 危急 | 🔴 红色 |
| 心率 | 100-120 (静息) | 偏高 | 🟡 黄色 |
| 血氧 | ≤ 90 % | 危急 | 🔴 红色 |
| 血氧 | 90-94 % | 偏低 | 🟠 橙色 |
| 体温 | ≥ 38.5°C 或 ≤ 35°C | 异常 | 🟠 橙色 |
| 血糖 | ≥ 11.1 mmol/L (餐后) 或 ≤ 3.9 | 异常 | 🟠 橙色 |
当前实施: 服务端 classify_bp
已经做血压风险等级 (写入大 JSON 的 风险等级 字段).
其他指标尚未实施. v10 应补齐.
研究协议示例:
name: "高血压晨晚血压观察"
duration_days: 14
daily_tasks:
- time_window: "07:00-08:30"
indicator: "bloodPressure"
times_per_day: 1
description: "晨起血压"
- time_window: "19:00-20:30"
indicator: "bloodPressure"
times_per_day: 1
description: "晚间血压"
completion_threshold: 0.8
alert_rules:
- if: "missed_consecutive >= 2"
action: "通知研究员"
- if: "高血压2级 数 >= 3 in 7 天"
action: "通知主治医生"| 数据类型 | 保留期限 | 清理触发 |
|---|---|---|
| 患者真实采集 (含门诊号) | 永久 | 不主动清, 仅手动按门诊号导出后归档 |
| 早期种子/演示数据 | N/A (应清) | 客户允许后调 DELETE /api/device/:id |
| pending 队列 (storage) | MAX_QUEUE=500 | LRU 自动丢最旧 |
| 调试 ble_event 表 | 90 天 | 服务端定时任务 (未实施) |
| 异常 | 触发条件 | 用户感知 | 系统行为 | 恢复路径 |
|---|---|---|---|---|
| 蓝牙没开 | adapter unavailable | 红色 toast "请打开手机蓝牙" | 跳过连接尝试 | 用户手动开 |
| 附近权限没开 (Android 12+) | 5s 内 0 设备扫到 | modal 引导去权限设置 | 弹 modal | 用户去系统设置开 |
| 表被其他手机占用 | 12s 握手超时 | modal "可能被其他手机连着" | 重试按钮 | 用户去那台手机断 |
| iOS pair 残留 | 第二轮扫描仍 0 个 | modal 引导忽略此设备 | 弹 modal | 设置→蓝牙→忽略 |
| 弱信号 / 距离过远 | RSSI < -75dBm | 列表过滤掉 | 静默 | 走近表 1 米 |
| 网络不通 | POST 失败 | 静默入 pending | 6 重兜底 flush | 网络恢复自动重传 |
| 表换电池 / 重启 | connection_lost | 状态条变红 | 自动重连 (指数退避) | 1/2/4s 重试 |
| 测量值异常 (高血压2级) | classify_bp 命中 | 视觉高亮 | 写入 风险等级 |
医生在 Tab 3 看到 + 标记 |
| 门诊号填错了 | UI 没输入约束 | 无 | 数据照入 (错号) | 暂无 (建议加患者列表二次校对) |
| 设备没电 | 表本身关机 | 扫不到 | 视为"没开机" | 用户充电后重连 |
| 同一时刻多次测量 | 表批量推送 | 见到 50 条相同步数 | 服务端原样接收 | 客户端可去重显示 (UI 层) |
| 操作 | 病房护士 | 家属 | 医生 | 课题研究员 | 运维管理员 |
|---|---|---|---|---|---|
| 输入/切换门诊号 | ✅ | ✅ | — | — | ✅ |
| 连接蓝牙 / 断开 | ✅ | ✅ | — | — | ✅ |
| 触发测量 (软件层) | ✅ | ✅ | — | — | ✅ |
| 查看本患者趋势 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 查看跨患者数据 | — | — | ✅ (本科室) | ✅ (本研究组) | ✅ |
| 标注异常 / 写病历 | — | — | ✅ | — | — |
| 导出 CSV/PDF | — | — | ✅ | ✅ | ✅ |
| 删除数据 | — | — | — | — | ✅ (危险) |
| 配置研究协议 | — | — | — | ✅ | ✅ |
| 调试 / OTA / 切换服务 | — | — | — | — | ✅ |
当前小程序: 无角色概念, 谁拿到二维码谁能用全部功能.
v10 不打算实施角色权限 (太重), 仅在 UI 层把"调试"功能折叠到 Tab 4 末尾,
正式版 IS_TEST_BUILD=false 时隐藏即可.
长期方向: 走微信小程序 wx.login 拿 openid → 服务端建用户表 → 角色与权限. 但客户当前不需要, 暂不做.
| 接口 | 用途 |
|---|---|
GET /api/patients |
列出所有出现过的门诊号 + 各自数据条数 + 最近活动时间 (Tab 3 患者切换 chip) |
GET /api/data?patientNo=xxx&from=xxx&to=xxx&type=bloodPressure |
时间窗 + 指标过滤 (Tab 3 趋势图) |
GET /api/export/csv?patientNo=xxx |
服务端生成 CSV 下载链接 (Tab 3 导出) |
GET /api/abnormal?patientNo=xxx&days=7 |
服务端预筛异常数据 (Tab 3 异常列表) |
| 谁 | 通知什么 | 时机 | 通道 |
|---|---|---|---|
| 护士 | "未输入门诊号"红警示 | 进首页 | 顶部状态条 |
| 护士 | "蓝牙断了, 数据无法同步" | adapter 关 | toast |
| 护士 | "测量值异常, 风险等级 X" | 测量后回包 | 弹 toast 或 modal |
| 家属 | "未上传 X 条" | 网络断时 | 首页角标 (待加) |
| 医生 | "异常数据" | 后台查看时 | Tab 3 高亮 |
| # | 决策点 | 选项 |
|---|---|---|
| 1 | 测量前是否加"门诊号二次确认"? | 加 (减少错号) / 不加 (减少打扰) |
| 2 | 异常数据是否弹 modal 拦截? | 拦截 (强制处理) / 仅高亮 (不打扰流程) |
| 3 | 数据 Tab 趋势图用什么库? | ECharts (功能强但接入麻烦) / F2 (蚂蚁出品, 小程序原生) / canvas 手画 |
| 4 | 是否在 v10 加"患者列表"概念? | 加 (能看到所有患者, 切换更方便) / 不加 (沿用 storage.patientNo 单值) |
| 5 | "调试"功能是否在 Tab 4 完全隐藏 (正式版)? | 完全隐藏 / 显示但加密码进入 / 始终可见 |
| 6 | 是否考虑离线缓存模式? | 加 (野外能用) / 不加 (依赖网络) |
| 7 | 入组研究协议引擎是否纳入 v10? | 纳入 (大工程) / 留 v12 (推荐) |
| 8 | 数据导出格式? | CSV (Excel 友好) / JSON (开发者友好) / PDF (医生友好) / 全要 |