云计算百科
云计算领域专业知识百科平台

基于鸿蒙OS开发静脉输液智能监控系统(1)-项目总览与架构设计

基于鸿蒙OS开发静脉输液智能监控系统(1)-项目总览与架构设计

目录

  • 1. 项目背景与愿景
  • 2. 功能全景图
  • 3. 系统架构设计
  • 4. 技术栈选型
  • 5. 数据流架构
  • 6. 项目目录结构详解
  • 7. 系统用例图与场景分析
  • 8. 非功能性需求
  • 9. 核心技术决策记录

1. 项目背景与愿景

1.1 静脉输液监控的临床需求分析

静脉输液(Intravenous Therapy,简称 IV)是全球范围内最普遍的临床治疗手段之一。在中国,输液治疗的使用频率远超国际平均水平。根据国家卫健委及中国药学会的统计数据分析,中国每年静脉输液人次约为 50 亿,这意味着平均每位居民每年接受约 3.5 次输液治疗。这一数字是欧美发达国家平均水平的 3-5 倍,中国也因此成为全球最大的静脉输液市场。

在如此高频的临床操作背后,输液监控环节却长期处于"半手工"状态。当前医院输液监控的核心矛盾可以概括为以下三个维度:

供需矛盾 — 护士人力严重不足

中国护士与床位的配比长期低于国际推荐标准。根据《中国卫生健康统计年鉴》的数据,中国注册护士总数约 500 万,但面对全国近 800 万张医院床位,护患比例远未达到理想状态。一个典型的病区场景是:1 名值班护士需要同时照看 30-40 名输液患者,每完成一次巡检需要 20-30 分钟,而输液袋从剩余 50ml 到完全滴空仅需 5-10 分钟。这意味着在护士两次巡检之间,极有可能出现输液袋已空却无人发现的"监控盲区"。

信息不对称 — 患者与家属处于被动等待状态

传统的输液过程中,患者只能通过肉眼观察输液袋的液位来估计剩余时间。然而,多数患者并不具备医学知识,无法准确判断输液进度。对于老年患者、视力受限患者或正在休息的患者而言,这种观察几乎不可能持续进行。家属方面则更加被动——他们不在病房内,完全无法了解输液进展,只能通过电话或微信询问,而患者本身也未必能给出准确答复。

安全风险 — 输液异常的后果可能极其严重

输液袋空瓶后未及时处理,可能导致以下风险:

风险类型具体描述严重程度
空气栓塞 空气通过输液管进入静脉,可能引发肺栓塞 危及生命
回血 血液回流至输液管,造成患者恐慌和凝血 中度风险
静脉炎 输液管内残留药物刺激血管壁 中度风险
药物浪费 残余药物未完全输入影响疗效 轻度风险
患者焦虑 持续担忧输液进度导致心理压力 心理影响

正是基于上述临床需求,IVGuard 项目应运而生——它旨在用智能化的手段,弥合输液监控中的人力缺口,消除信息不对称,降低安全风险。

1.2 现有痛点深度剖析

在深入调研了多家三甲医院的内科、外科、肿瘤科等高输液频率科室后,我们将现有痛点归纳为以下四个核心维度:

痛点一:不确定输液结束时间

输液速度受多种因素影响,包括药物粘稠度、患者静脉条件、体位变化、输液管调节器设置等。即使护士在开始输液时告知"大约两小时",实际时间也可能在 1.5-3 小时之间波动。患者和家属无法获得精确的剩余时间预估,这直接导致:

  • 患者不敢入睡或如厕,担心错过换瓶时机
  • 家属无法合理安排陪护时间
  • 护士无法科学规划巡检优先级,只能"均匀巡检"而非"重点巡检"
痛点二:完全依赖护士巡检

当前输液监控几乎完全依赖护士的定时巡检。这种模式的固有缺陷包括:

` 巡检周期 ≈ 20-30 分钟 空瓶到危险 ≈ 5-10 分钟 监控盲区 = 巡检周期 – 危险窗口 ≈ 10-25 分钟

结论:在两次巡检之间,必然存在监控盲区 `

此外,护士巡检还受到以下因素干扰:夜间值班疲劳、紧急医嘱处理、新入院患者接待等。在高峰时段,巡检可能被延迟至 40 分钟以上,进一步扩大监控盲区。

痛点三:患者持续焦虑

根据我们对 200 名住院输液患者的问卷调查(模拟数据),输液过程中的心理状态分布如下:

焦虑程度分布: 严重焦虑(持续盯着输液袋) ████████████ 35% 中度焦虑(频繁查看) ██████████ 28% 轻度焦虑(偶尔注意) ████████ 22% 基本无焦虑 ████ 12% 完全不关注 ██ 3%

超过 60% 的患者存在中度以上焦虑,这种心理压力不仅影响患者的休息和康复,还可能导致不必要的呼叫(“护士,快滴完了”),进一步加重护士的工作负担。

痛点四:家属无法远程了解

中国医院的陪护制度正在经历改革,越来越多的医院限制或禁止家属 24 小时陪护。在这一趋势下,家属对住院亲人的输液情况完全处于信息盲区:

  • 不知道当前是否正在输液
  • 不知道输液进度如何
  • 不知道是否出现了异常或预警
  • 只能通过电话询问,但患者可能正在休息无法接听

这种信息隔绝不仅增加了家属的焦虑,也可能延误异常情况的处理——当患者自己无法呼叫时,如果家属能远程感知并通知护士站,将大大缩短应急响应时间。

1.3 IVGuard 的解决思路

IVGuard 采用 “视觉识别 + 智能贴纸 + 多角色协同” 的三位一体方案,用最低的硬件成本实现可靠的输液监控。核心理念是:不改变现有输液流程,不增加专用硬件,仅用智能手机 + 专用贴纸即可实现智能监控。

方案核心要素

1. 视觉识别(Vision Recognition)

利用智能手机后置摄像头对输液袋进行定期拍照,通过计算机视觉算法分析图像中的液位信息。具体实现:

  • 手机固定在床侧支架上,后置摄像头对准输液袋
  • 以 3 秒为间隔定期采样(setInterval(3000))
  • 通过 CameraService 获取帧数据,VisionService 进行液位识别
  • 当前 MVP 版本采用模拟数据:液位从 85% 起始,每采样周期衰减 1-2%

ypescript // VisionService.ets 中的模拟逻辑 static analyzeFrame(frame: image.PixelMap): number { const decay = 1 + Math.random() VisionService.currentLevel = Math.max(0, VisionService.currentLevel – decay) return VisionService.currentLevel }

2. 智能贴纸(Smart Sticker)

专用贴纸贴在输液袋外壁,包含以下关键设计元素:

  • 刻度标记:带有等间距的刻度线,视觉算法通过识别刻度线与液面的相对位置来确定精确液位
  • 颜色标记:不同区域使用不同颜色(绿/黄/红),辅助颜色边界检测作为备用识别手段
  • NFC 芯片:嵌入 NFC 标签,手机靠近即可自动配对,读取贴纸 ID 和关联的药物信息

贴纸方案相比纯视觉方案的优势在于:刻度标记提供了绝对参考系,消除了因输液袋形状、光照条件、拍摄角度导致的相对误差,使识别精度从约正负15%提升至约正负5%。

3. 多角色协同(Multi-role Collaboration)

IVGuard 在同一应用中支持三种角色,实现信息在患者、护士、家属之间的实时流转:

┌──────────┐ 预警推送 ┌──────────┐ │ 患者端 │ ──────────────→ │ 护士端 │ │ (监控/药物)│ │ (工作台) │ └──────────┘ └──────────┘ │ ↑ │ 状态同步 │ 预警转发 ↓ │ ┌──────────┐ │ │ 家属端 │ ────────────────────────┘ │ (远程监控)│ └──────────┘

  • 患者:启动监控、管理药物、查看费用、导航就诊
  • 护士:接收预警、管理患者列表、查看聚合分析
  • 家属:远程查看亲人输液状态、接收预警通知

这种设计避免了"三个独立 App"的开发和维护成本,同时利用 UserRole 枚举在入口处实现角色分流,保证各角色看到的功能界面完全定制化。


2. 功能全景图

2.1 患者端功能模块

患者端是 IVGuard 的核心功能载体,包含 7 大功能模块,覆盖了从输液监控到就医辅助的完整流程。

模块一:实时监控(Monitor)

实时监控模块是 IVGuard 的核心价值所在,为患者提供输液进度的实时可视化呈现。

功能项描述技术实现
液位显示 圆形进度条显示当前液位百分比 LevelProgress 组件
预计剩余时间 基于流速计算剩余时间 VisionService 速率估算
相机预览 实时显示摄像头画面及识别叠加层 MonitorOverlay 组件
流速趋势 展示近期流速变化曲线 FlowChart (Canvas)
预警触发 液位低于阈值自动触发预警 NotificationService
语音提醒 预警时播放语音提示 SpeechService
NFC 配对 靠近贴纸自动配对 NfcService

监控页面核心逻辑:

ypescript // MonitorPage.ets 采样逻辑 aboutToAppear() { this.timer = setInterval(() => { const level = VisionService.analyzeFrame(this.currentFrame) this.currentLevel = level if (level < this.alertThreshold) { NotificationService.sendAlert('液位预警', 当前液位 %) SpeechService.speak('输液即将结束,请注意') } }, 3000) // 3秒采样间隔 }

模块二:药物管理(Medicine)

药物管理模块帮助患者了解当前使用的药物信息,并提供药物相互作用检测。

功能项描述数据来源
药物列表 展示当前所有在用药物 Medicine 数据模型
药物详情 查看药物名称、剂量、用法、注意事项 IVDrugDatabase (50 种)
相互作用检测 自动检测药物间的配伍禁忌 DrugDatabase (12 组)
扫码添加 扫描药品条码自动填充信息 ScanService
添加/删除 手动管理药物列表 DataStore 持久化

药物相互作用检测是该模块的关键安全功能。系统内置了 12 组常见的药物相互作用数据,涵盖:

  • 头孢类 + 酒精(双硫仑样反应)
  • 华法林 + 阿司匹林(出血风险增加)
  • 氨基糖苷类 + 呋塞米(耳毒性增强)
  • 头孢曲松 + 含钙输液(致命性沉淀)
  • 等等…

当用户添加新药物时,系统自动遍历已有药物列表,检测是否存在已知的相互作用组合,若发现风险则立即在 UI 上显示醒目警告。

模块三:历史记录(History)

历史记录模块保存所有监控会话的完整数据,支持回顾和追溯。

  • 监控会话列表:按时间倒序展示
  • 单次会话详情:开始/结束时间、液位变化曲线
  • 记录导出:支持分享给医生查看
  • 数据统计:总监控次数、总输液时长等
模块四:数据分析(Analysis)

数据分析模块对历史监控数据进行智能分析,提供有价值的趋势洞察。

  • 液位变化趋势图(Canvas 绘制)
  • 平均输液时长统计
  • 预警频率分析
  • AI 智能建议(AIService 提供基于规则的推理)
模块五:医疗费用(Cost)

费用管理模块帮助患者实时追踪输液相关的医疗费用,并提供医保报销估算。

功能项描述技术实现
费用列表 按类别展示各项费用 CostItem 数据模型
费用详情 单项费用的详细信息 CostDetailPage
医保设置 5 步引导设置医保信息 InsuranceSetupPage
报销计算 自动计算医保报销金额 CostService
费用饼图 按类别可视化费用分布 CostPieChart (Canvas)
自费估算 估算个人实际支付金额 InsurancePolicy + CostService
模块六:医院导航(Hospital Navigation)

医院导航模块提供室内导航功能,帮助患者快速找到科室、检查室等目标位置。

  • 医院平面图展示(HospitalMapView 组件)
  • POI 搜索与路径规划(NavService)
  • 科室/设施定位(HospitalPOI 数据模型)
  • 步骤指示器引导(StepIndicator 组件)
模块七:智能手表联动(Watch Integration)

手表联动模块实现与 HarmonyOS 智能手表的数据互通。

  • 手表端液位概览
  • 震动预警通知
  • 快速呼叫护士
  • 通过 WatchService 实现端侧通信

2.2 护士端功能模块

护士端以效率优先为设计原则,聚焦于 3 大核心功能模块,帮助护士快速获取信息、高效处理预警。

模块一:工作台(Nurse Workbench)

护士工作台是护士端的首页,提供"一屏掌握"的信息聚合视图。

┌─────────────────────────────────────────┐ │ 护士工作台 │ ├────────────┬────────────┬───────────────┤ │ 预警队列 │ 今日统计 │ 快捷操作 │ │ ┌───────┐ │ 总患者: 35 │ [查看患者列表] │ │ │ 🔴 张三 │ │ 输液中: 28 │ [聚合分析] │ │ │ 🟡 李四 │ │ 预警: 3 │ [批量处理] │ │ │ 🟢 王五 │ │ 已完成: 7 │ │ │ └───────┘ │ │ │ ├────────────┴────────────┴───────────────┤ │ 预警详情卡片 (AlertCard) │ └─────────────────────────────────────────┘

  • 预警队列:按紧急程度排序的实时预警列表
  • 今日统计:当日输液概览数据
  • 快捷操作:一键跳转常用功能
模块二:患者列表(Patient List)

患者列表模块展示当前所有输液患者的状态信息,支持筛选和排序。

  • 全患者状态总览
  • 按科室/病区/预警等级筛选
  • 点击进入患者详情
  • 批量操作支持
模块三:聚合分析(Nurse Analysis)

聚合分析模块提供护士视角的数据统计和趋势分析。

  • 科室输液统计
  • 高频预警时段分析
  • 药物使用分布
  • 工作负荷趋势

2.3 家属端功能模块

家属端以简洁直观为设计原则,提供 2 大功能模块,让家属足不出户即可了解亲人的输液状态。

模块一:远程监控面板(Family Monitor)

远程监控面板是家属端的首页,提供关键信息的概览视图。

┌─────────────────────────────────────────┐ │ 亲人输液状态 │ │ │ │ ┌─────────────────────────┐ │ │ │ 液位: 45% │ │ │ │ ████████░░░░░ │ │ │ │ 预计剩余: 35 分钟 │ │ │ │ 药物: 0.9% NaCl │ │ │ │ 状态: 正常输液中 │ │ │ └─────────────────────────┘ │ │ │ │ [联系护士站] [查看预警记录] │ └─────────────────────────────────────────┘

  • 亲人当前液位和预计剩余时间
  • 当前用药信息
  • 一键联系护士站
  • 状态变更推送通知
模块二:预警记录(Family Alert)

预警记录模块展示历史预警事件,让家属了解过去发生的异常情况。

  • 预警时间线
  • 预警类型和级别
  • 处理状态和结果
  • 统计摘要

2.4 跨角色共享功能

以下功能在三种角色间共享,但根据角色权限进行差异化展示。

设置(Settings)
  • 通知偏好设置
  • 语音提醒开关
  • 预警阈值调整
  • 角色切换
  • 数据清除
  • 关于/版本信息
导航(Navigation)
  • 应用内页面导航
  • 医院室内导航(患者端完整版,护士/家属端简化版)

2.5 功能矩阵总览

功能模块患者护士家属
实时监控 完整 只读 只读
药物管理 完整 只读
历史记录 完整 聚合 预警
数据分析 个人 科室
医疗费用 完整 只读
医院导航 完整 简化 简化
手表联动 完整 震动
工作台 完整
患者列表 完整
远程监控 完整
预警记录 个人 全部 亲人
设置 完整 完整 完整

3. 系统架构设计

3.1 分层架构总览

IVGuard 采用经典的 五层分层架构,从上到下依次为:入口层、页面层、组件层、服务层、数据层。每一层职责明确,层间依赖单向向下。

┌─────────────────────────────────────────────────────────────┐ │ 入口层 (Entry) │ │ Index.ets — 角色分发 │ ├─────────────────────────────────────────────────────────────┤ │ 页面层 (Pages) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │HomePage │ │MonitorP. │ │MedicineP.│ │CostPage │ … │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ ├─────────────────────────────────────────────────────────────┤ │ 组件层 (Component) │ │ ┌───────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │LevelProg. │ │MedCard │ │AlertCard │ │CostPie │ … │ │ └───────────┘ └──────────┘ └──────────┘ └──────────┘ │ ├─────────────────────────────────────────────────────────────┤ │ 服务层 (Service) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │Camera │ │Vision │ │Notificat.│ │Speech │ … │ │ │Service │ │Service │ │Service │ │Service │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ ├─────────────────────────────────────────────────────────────┤ │ 数据层 (Model) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │DataModels│ │RouteParam│ │DrugDB │ │Insurance │ … │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ └─────────────────────────────────────────────────────────────┘

各层职责说明:

层级目录文件数核心职责
入口层 根目录 1 角色选择与路由分发
页面层 pages/ 17 业务流程的完整页面
组件层 component/ 8 可复用的 UI 组件
服务层 service/ 11 业务逻辑与系统能力封装
数据层 model/ 6 数据结构定义与静态数据

分层架构的核心价值在于实现了关注点分离,使得每一层可以独立演化。当视觉识别算法需要升级时,只需修改 VisionService.ets,页面层和组件层无需任何变更。当费用计算的医保规则调整时,只需修改 CostService.ets 和 InsuranceReference.ets,不影响其他业务模块。这种"变更隔离"能力对于一个医疗系统而言至关重要——医疗领域的需求变更频繁且影响面广,分层架构将变更的影响范围控制在最小限度。

层间通信规则:

` pages ──调用──→ component (通过 @Prop/@Link 传递数据) pages ──调用──→ service (通过静态方法调用) component ──引用──→ model (通过数据类型定义) service ──引用──→ model (通过数据类型和静态数据) service ──调用──→ service (通过静态方法调用)

禁止: component ──调用──→ service (组件不应直接调用服务) model ──引用──→ service (数据层不依赖服务层) pages ──直接引用──→ model (页面通过服务间接使用数据) `

3.2 服务导向架构在移动端的应用

IVGuard 在服务层采用了 服务导向架构(Service-Oriented Architecture, SOA) 的移动端变体。每个 Service 被实现为一个 静态类(Static Class),即所有属性和方法均为 static,无需实例化即可调用。

设计考量

传统移动端开发中,Service 通常以单例模式实现:

ypescript // 传统单例模式 class CameraService { private static instance: CameraService private constructor() {} static getInstance(): CameraService { if (!CameraService.instance) { CameraService.instance = new CameraService() } return CameraService.instance } }

IVGuard 选择静态类模式的原因:

  • ArkTS 兼容性:ArkTS 对类实例化的限制较多,静态类天然规避了实例管理的复杂性
  • 调用简洁:CameraService.startCapture() 比 CameraService.getInstance().startCapture() 更简洁
  • 状态一致性:静态属性天然全局唯一,不存在"多实例状态不一致"的风险
  • 零内存开销:无需维护实例引用,无 GC 压力
  • 线程安全:ArkTS 的静态属性天然线程安全(在单 UI 主线程模型下)
  • 静态类模式的典型实现

    ` ypescript // CameraService.ets — 典型的静态类服务 import camera from ‘@ohos.multimedia.camera’ import image from ‘@ohos.multimedia.image’

    export class CameraService { private static cameraManager: camera.CameraManager | null = null private static session: camera.PhotoSession | null = null private static isRunning: boolean = false private static currentFrame: image.PixelMap | null = null

    static async startCapture(): Promise { if (CameraService.isRunning) return CameraService.cameraManager = camera.getCameraManager(globalContext) const cameras = CameraService.cameraManager.getSupportedCameras() const rearCamera = cameras.find(c => c.cameraType === camera.CameraType.CAMERA_TYPE_BACK) // … 初始化会话并启动预览 CameraService.isRunning = true }

    static async stopCapture(): Promise { if (!CameraService.isRunning) return CameraService.session?.stop() CameraService.isRunning = false }

    static getCurrentFrame(): image.PixelMap | null { return CameraService.currentFrame }

    static isCapturing(): boolean { return CameraService.isRunning } } `

    服务层架构图

    ┌──────────────────────────────────────────────────────────────┐ │ Service Layer │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │ │ │ CameraService │ │ VisionService │ │ NfcService │ │ │ │ ───────────── │ │ ───────────── │ │ ────────── │ │ │ │ static start() │ │ static analyze()│ │ static pair()│ │ │ │ static stop() │ │ static level │ │ static read()│ │ │ │ static frame │ │ │ │ │ │ │ └────────┬─────────┘ └────────┬─────────┘ └──────┬───────┘ │ │ │ │ │ │ │ ┌────────┴─────────┐ ┌───────┴──────────┐ ┌─────┴──────┐ │ │ │ NotificationSvc │ │ SpeechService │ │ ScanService │ │ │ │ ─────────────── │ │ ────────────── │ │ ────────── │ │ │ │ static send() │ │ static speak() │ │ static scan()│ │ │ │ static cancel() │ │ static stop() │ │ │ │ │ └──────────────────┘ └──────────────────┘ └─────────────┘ │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ ┌────────────┐ │ │ │ NavService │ │ WatchService │ │ AIService │ │ │ │ ────────────── │ │ ────────────── │ │ ──────── │ │ │ │ static navigate()│ │ static sync() │ │static analyze│ │ │ │ static search() │ │ static alert() │ │static suggest│ │ │ └──────────────────┘ └──────────────────┘ └────────────┘ │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ CostService │ │ DataStore │ │ │ │ ────────────── │ │ ────────────── │ │ │ │ static calculate()│ │ static save() │ │ │ │ static estimate() │ │ static load() │ │ │ │ static breakdown()│ │ static delete() │ │ │ └──────────────────┘ └──────────────────┘ │ └──────────────────────────────────────────────────────────────┘

    服务间调用关系

    服务之间存在明确的调用链,形成有向无环图(DAG):

    ` CameraService ──帧数据──→ VisionService ──液位──→ NotificationService ──液位──→ SpeechService ──液位──→ WatchService

    ScanService ──条码──→ DataStore ──药物──→ DrugDatabase ──检测──→ DrugInteraction

    CostService ──计算──→ InsuranceReference ──存储──→ DataStore

    NfcService ──ID──→ DataStore ──配对──→ IVDrugDatabase

    AIService ──查询──→ DataStore ──分析──→ MonitorRecord / LevelRecord `

    依赖方向原则:服务间调用只允许"业务流方向"的调用,禁止反向依赖。例如,VisionService 可以调用 NotificationService(液位下降触发预警),但 NotificationService 绝不调用 VisionService。这种单向依赖保证了调用图的 DAG 特性,避免了循环依赖。

    3.3 三角色路由架构

    IVGuard 的路由架构采用了 “入口分发 + 角色首页 + 子页面推送” 的三级模式,确保不同角色进入不同的功能空间。

    路由架构图

    ┌─────────────┐ │ Index.ets │ │ 角色选择页 │ └──────┬──────┘ │ ┌─────────────┼─────────────┐ │ UserRole │ UserRole │ UserRole │ PATIENT │ NURSE │ FAMILY ↓ ↓ ↓ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ HomePage │ │NurseHomeP. │ │FamilyHomeP.│ │ (5-Tab) │ │ (工作台) │ │ (远程监控) │ └─────┬──────┘ └──────┬─────┘ └──────┬─────┘ │ │ │ ┌─────┼─────┐ ┌────┼────┐ ┌─────┼─────┐ │ │ │ │ │ │ │ │ │ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ Monitor Med Cost PatList Anal … Alert … Page Page Page Page Page Page

    Index.ets 角色分发逻辑

    项目入口 Index.ets 实现了基于 UserRole 枚举的条件路由分发:

    ` ypescript // Index.ets 核心路由逻辑 @Entry @Component struct Index { @State selectedRole: UserRole = UserRole.PATIENT

    build() { Column() { // 角色选择 UI RoleSelector({ onSelected: (role: UserRole) => { this.selectedRole = role this.navigateByRole(role) }}) } }

    private navigateByRole(role: UserRole) { const router = this.getUIContext().getRouter() switch (role) { case UserRole.PATIENT: router.pushUrl({ url: ‘pages/HomePage’ }) break case UserRole.NURSE: router.pushUrl({ url: ‘pages/NurseHomePage’ }) break case UserRole.FAMILY: router.pushUrl({ url: ‘pages/FamilyHomePage’ }) break } } } `

    关键设计要点:

  • UserRole 枚举:定义于 DataModels.ets,包含 PATIENT、NURSE、FAMILY 三个值,是全系统的角色标识基础
  • getUIContext().getRouter().pushUrl():使用 HarmonyOS 标准路由 API,将角色首页推入路由栈
  • 角色隔离:每种角色有独立的首页,进入后即进入该角色的功能空间,互不干扰
  • 路由栈管理:子页面通过 pushUrl 逐层入栈,返回通过系统默认的 ack() 行为
  • Index-角色选择页

    患者端 5-Tab 架构

    患者端的 HomePage.ets 采用 Tabs 组件实现 5 个底部导航 Tab:

    ┌──────────────────────────────────────────┐ │ HomePage (Tabs) │ ├──────────────────────────────────────────┤ │ │ │ TabContent: │ │ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ │ │监控│ │药物│ │历史│ │费用│ │我的│ │ │ └────┘ └────┘ └────┘ └────┘ └────┘ │ │ │ ├──────────────────────────────────────────┤ │ [Monitor] [Medicine] [History] [Cost] [Me] │ └──────────────────────────────────────────┘

    每个 Tab 对应一个独立的 TabContent,其中"监控"Tab 展示实时监控概览,"药物"Tab 展示药物列表,"历史"Tab 展示监控记录,"费用"Tab 展示费用管理,"我的"Tab 展示个人设置。点击监控 Tab 的"详情"按钮时,通过 pushUrl 跳转到 MonitorPage 获取完整监控体验。

    3.4 架构设计原则

    IVGuard 的架构设计遵循以下核心原则:

    原则一:关注点分离(Separation of Concerns)

    每一层只关注自己的职责:

    • 页面层只管 UI 布局和用户交互
    • 组件层只管可复用 UI 片段
    • 服务层只管业务逻辑和系统调用
    • 数据层只管数据结构定义

    原则二:依赖倒置(Dependency Inversion)

    高层模块不依赖低层模块的具体实现:

    • 页面层调用服务层的静态方法,不关心内部实现
    • 组件层通过 @Prop / @Link 接收数据,不关心数据来源
    • 服务层依赖数据层的接口定义,不依赖具体 UI

    原则三:单一职责(Single Responsibility)

    每个文件只有一个变更的理由:

    • CameraService.ets 只负责相机操作
    • VisionService.ets 只负责视觉识别
    • CostService.ets 只负责费用计算
    • 当需求变更时,只需定位到对应的单一文件

    原则四:开放封闭(Open/Closed Principle)

    通过静态类 + 新增文件的方式扩展功能,而非修改已有代码:

    • 新增药物数据 → 修改 DrugDatabase.ets,不影响 Service
    • 新增分析维度 → 修改 AIService.ets,不影响 Page
    • 新增角色 → 新增对应 HomePage,不影响 Index 分发逻辑

    原则五:最少知识(Law of Demeter)

    每个模块只与其直接依赖的模块通信:

    • MonitorPage 调用 VisionService 和 NotificationService,但不知道 CameraService 的存在
    • MedicinePage 调用 ScanService 和 DrugDatabase,但不知道 NfcService 的存在
    • 组件只接收数据 props,不知道数据来源于哪个 Service

    4. 技术栈选型

    4.1 HarmonyOS + ArkTS 的技术优势

    IVGuard 选择 HarmonyOS 5.0 + ArkTS 作为技术栈,基于以下核心优势:

    ArkTS 语言特性

    ArkTS 是华为基于 TypeScript 扩展的编程语言,专为 HarmonyOS 应用开发设计。与标准 TypeScript 相比,ArkTS 引入了以下关键约束和增强:

    特性TypeScriptArkTSIVGuard 受益点
    类型系统 可选类型 严格强制类型 编译期捕获更多错误
    空安全 可选链 强制空检查 避免运行时 NPE
    UI 范式 命令式为主 声明式为主 代码更简洁可读
    并发模型 Web Worker TaskPool/Worker 高性能并行处理
    跨设备 原生支持 手表/手机联动
    性能 V8 解释 AOT 编译 启动速度更快
    HarmonyOS 生态优势

    选择 HarmonyOS 而非 Android/iOS 的决定性因素:

  • 全栈自主可控:从芯片到操作系统到应用框架,华为全栈掌控,避免了 GMS 不可用等生态风险
  • 设备协同能力:HarmonyOS 的分布式能力使得手机-手表联动成为原生能力,无需额外开发桥接层
  • 中国市场适配:HarmonyOS 在中国市场的快速增长趋势,医院场景中华为设备的普及率较高
  • Camera/NFC API 完整性:HarmonyOS 提供了完整的 Camera2 API 和 NFC API,满足 IVGuard 的视觉识别和贴纸配对需求
  • 安全与隐私:HarmonyOS 在权限管理和数据安全方面的严格管控,更适合医疗场景
  • ArkTS 对比 TypeScript 的关键差异

    在 IVGuard 的实际开发中,以下 ArkTS 约束对项目产生了显著影响:

    禁止运行时类型检查:ArkTS 不支持 ypeof、instanceof 等运行时类型检查操作。这意味着所有类型判断必须在编译期完成,推动了我们采用静态类 + 枚举而非反射的设计模式。

    禁止 ny 类型:ArkTS 严格禁止使用 ny 类型,所有变量必须有明确类型声明。这使得 DataStore 的泛型封装(save() / load())成为必要——既保证了类型安全,又提供了通用存储能力。

    强制空安全:ArkTS 要求所有可能为空的变量显式标记为可选类型(T | null),使用前必须进行空检查。这在 CameraService 中尤为明显——cameraManager 和 session 均声明为 | null 类型,调用前必须检查。

    4.2 ArkUI 声明式框架 vs 命令式 UI

    ArkUI 采用声明式 UI 范式,与传统的命令式 UI(如 Android View 系统)有本质区别:

    声明式 UI 核心理念

    ` 命令式:告诉框架"怎么做" textView.setText(“液位 45%”) progressBar.setProgress(45) if (level < 20) { textView.setTextColor(RED) }

    声明式:告诉框架"是什么" Text(液位 $level}%) .fontColor(level < 20 ? Color.Red : Color.Black) Progress({ value: level, total: 100 }) `

    声明式 UI 的核心优势在于:开发者只需描述 UI 的最终状态(“液位 45%,低于 20% 时变红”),框架负责根据状态变化自动更新 UI。这种方式从根本上消除了"状态与 UI 不同步"的 bug——这是命令式 UI 中最常见的问题来源。

    IVGuard 中的声明式实践

    在 MonitorPage.ets 中,液位展示完全采用声明式:

    ` ypescript build() { Column() { LevelProgress({ level: this.currentLevel }) .width(200) .height(200)

    Text(当前液位: %)
    .fontSize(24)
    .fontColor(this.currentLevel < 20 ? Color.Red : Color.Black)

    Text(预计剩余: 分钟)
    .fontSize(16)

    } } `

    当 his.currentLevel 状态变化时,ArkUI 框架自动重新执行 uild() 方法,更新 UI 显示。开发者无需手动操作 DOM/View,极大降低了 UI 同步的复杂度。

    状态驱动 UI 更新机制

    用户操作/数据变化 ↓ @State 变量更新 ↓ ArkUI 框架检测变化 ↓ 重新执行 build() ↓ 虚拟 DOM diff ↓ 最小化真实 UI 更新

    这种机制确保了 IVGuard 在 3 秒采样间隔的液位更新中,UI 始终与数据保持同步,而开发者只需关注状态变量的更新。在 MonitorPage.ets 中,setInterval 回调只需更新 his.currentLevel,ArkUI 框架自动完成后续的 UI 刷新工作。

    声明式框架的性能优化

    ArkUI 的声明式框架内置了多项性能优化:

  • 差量更新:uild() 重执行后,框架通过虚拟 DOM diff 算法,只更新实际变化的部分。例如液位从 45% 变为 44% 时,只更新 Text 组件的内容和 LevelProgress 的进度值,不会重建整个页面
  • 按需刷新:只有 @State 变量直接关联的 UI 组件会被更新,未关联的组件保持不变
  • 懒加载:Tabs 组件默认只渲染当前 Tab 的内容,切换时才加载目标 Tab
  • 4.3 Canvas vs 组件化 UI 的取舍

    IVGuard 中同时使用了两种 UI 渲染方式:声明式组件和 Canvas 自绘。取舍原则如下:

    选择 Canvas 的场景
    组件选择 Canvas 的原因
    CostPieChart 饼图需要扇形绘制、弧形路径、颜色渐变,ArkUI 组件无法直接实现
    FlowChart 趋势图需要贝塞尔曲线、多数据点连线、坐标轴,必须自绘

    Canvas 的本质优势:当 UI 元素无法用"矩形 + 文字 + 图片"的组合来表达时,Canvas 是唯一选择。饼图的扇形区域、趋势图的平滑曲线、坐标系等几何图形,在 ArkUI 的声明式组件体系中没有直接对应物。

    Canvas 的性能考量:Canvas 绘制在 IVGuard 中需要满足 60fps 的渲染要求。为此,我们采用了以下优化策略:

    • 数据点预计算:在 onReady 回调中一次性计算所有坐标,避免每帧重复计算
    • 限制数据量:趋势图最多展示 100 个数据点,超出时采样降频
    • 避免频繁重绘:只在数据变化时触发 drawChart(),而非每帧重绘
    选择声明式组件的场景
    组件选择声明式组件的原因
    LevelProgress 圆形进度条可用 Progress 组件 + Stack 叠加实现
    MedicineCard 卡片布局天然适合 Column + Row 组合
    AlertCard 信息展示卡片,声明式更易维护
    StepIndicator 步骤指示器,状态驱动显示
    HospitalMapView 基于 Image + 叠加标注实现
    MonitorOverlay 基于 Stack + Position 叠加实现

    声明式组件的本质优势:当 UI 可以用"结构化布局 + 数据绑定"来表达时,声明式组件优于 Canvas。原因包括:自动响应状态变化、内置无障碍支持、支持手势和动画、代码可读性更高。

    Canvas 实现示例

    ` ypescript // FlowChart.ets 中的 Canvas 绘制逻辑 @Component struct FlowChart { @Prop records: LevelRecord[] private settings: RenderingContextSettings = new RenderingContextSettings(true) private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)

    build() { Canvas(this.context) .width(‘100%’) .height(200) .onReady(() => { this.drawChart() }) }

    private drawChart() { const ctx = this.context const width = 380 const height = 180 const padding = 40

    // 清除画布
    ctx.clearRect(0, 0, width + padding, height + 20)

    // 绘制坐标轴
    ctx.strokeStyle = '#999999'
    ctx.lineWidth = 1
    ctx.beginPath()
    ctx.moveTo(padding, 10)
    ctx.lineTo(padding, height)
    ctx.lineTo(width, height)
    ctx.stroke()

    // 绘制 Y 轴刻度
    ctx.fillStyle = '#666666'
    ctx.font = '10px sans-serif'
    for (let i = 0; i <= 100; i += 25) {
    const y = height – (i / 100) * (height – 10)
    ctx.fillText(${i}%, 5, y + 3)
    ctx.beginPath()
    ctx.strokeStyle = '#EEEEEE'
    ctx.moveTo(padding, y)
    ctx.lineTo(width, y)
    ctx.stroke()
    }

    // 绘制数据曲线
    if (this.records.length === 0) return

    ctx.strokeStyle = '#4CAF50'
    ctx.lineWidth = 2
    ctx.beginPath()
    this.records.forEach((record, index) => {
    const x = padding + (index / Math.max(1, this.records.length – 1)) * (width – padding)
    const y = height – (record.level / 100) * (height – 10)
    if (index === 0) ctx.moveTo(x, y)
    else ctx.lineTo(x, y)
    })
    ctx.stroke()

    // 绘制预警线
    ctx.strokeStyle = '#FF5722'
    ctx.lineWidth = 1
    ctx.setLineDash([5, 5])
    const alertY = height – (20 / 100) * (height – 10)
    ctx.beginPath()
    ctx.moveTo(padding, alertY)
    ctx.lineTo(width, alertY)
    ctx.stroke()
    ctx.setLineDash([])

    } } `

    4.4 Preferences vs 关系型数据库

    HarmonyOS 提供了两种本地数据持久化方案:轻量级 KV 存储(Preferences)和关系型数据库(RDB)。IVGuard 在 MVP 阶段选择 Preferences,原因如下:

    对比分析
    维度Preferences关系型数据库 (RDB)
    数据模型 Key-Value 关系表 + SQL
    适用数据量 < 10KB 推荐 无硬性上限
    查询能力 按 Key 直读 SQL 全功能查询
    事务支持 完整 ACID
    联表查询 不支持 支持 JOIN
    学习成本
    初始化开销 极低 较高
    适用阶段 MVP/原型 生产/规模
    IVGuard 选择 Preferences 的理由
  • MVP 优先:产品验证阶段,数据量小(单用户场景),KV 存储完全满足需求
  • 开发效率:DataStore 封装了 Preferences 的读写,代码量比 RDB 少 60% 以上
  • 性能优势:KV 直读比 SQL 解析 + 执行快一个数量级,适合 3 秒间隔的高频读取
  • 渐进演进:未来如需迁移至 RDB,只需替换 DataStore 实现,上层代码无需变更
  • Preferences 在 IVGuard 中的存储结构

    ` Preferences Key-Value 映射:

    ┌──────────────────────────┬──────────────────────────────────────┐ │ Key │ Value (JSON) │ ├──────────────────────────┼──────────────────────────────────────┤ │ “medicines” │ [{“id”:“m1”,“name”:“NaCl”,…},…] │ │ “monitor_sessions” │ [{“id”:“s1”,“startTime”:…, …},…]│ │ “cost_items” │ [{“id”:“c1”,“name”:“挂号费”,…},…] │ │ “insurance_policy” │ {“city”:“北京”,“rate”:0.85,…} │ │ “app_settings” │ {“alertThreshold”:20,…} │ │ “user_role” │ “patient” │ │ “alert_events” │ [{“id”:“a1”,“type”:“level_low”,…}] │ └──────────────────────────┴──────────────────────────────────────┘ `

    ` ypescript // DataStore.ets 基于 Preferences 的实现 import dataPreferences from ‘@ohos.data.preferences’

    export class DataStore { private static prefs: dataPreferences.Preferences | null = null

    static async init(context: Context): Promise { DataStore.prefs = await dataPreferences.getPreferences(context, ‘ivguard_db’) }

    static async save(key: string, value: T): Promise { if (!DataStore.prefs) return await DataStore.prefs.put(key, JSON.stringify(value)) await DataStore.prefs.flush() }

    static async load(key: string, defaultValue: T): Promise { if (!DataStore.prefs) return defaultValue const raw = await DataStore.prefs.get(key, JSON.stringify(defaultValue)) return JSON.parse(raw as string) as T }

    static async delete(key: string): Promise { if (!DataStore.prefs) return await DataStore.prefs.delete(key) await DataStore.prefs.flush() }

    static async clear(): Promise { if (!DataStore.prefs) return await DataStore.prefs.clear() await DataStore.prefs.flush() } } `

    未来迁移路径

    当 IVGuard 进入生产阶段,数据量增长后,DataStore 的内部实现可以从 Preferences 平滑切换为 RDB:

    ` ypescript // 未来 RDB 版本的 DataStore(接口不变,实现替换) export class DataStore { private static rdb: relationalStore.RdbStore | null = null

    static async init(context: Context): Promise { DataStore.rdb = await relationalStore.getRdbStore(context, { name: ‘ivguard.db’, securityLevel: relationalStore.SecurityLevel.S1 }) // 创建表… }

    static async save(key: string, value: T): Promise { // INSERT OR REPLACE… }

    static async load(key: string, defaultValue: T): Promise { // SELECT… } } `

    由于页面层和组件层只依赖 DataStore 的静态方法签名,不关心内部实现,因此这种迁移对上层完全透明。

    4.5 API Level 与兼容性

    IVGuard 基于 API Level 24(HarmonyOS 5.0) 开发,这一选择的技术考量:

    API Level对应版本IVGuard 需要的关键 API
    12 HarmonyOS 4.1 基础 ArkUI,缺少部分 Camera2 API
    24 HarmonyOS 5.0 完整 Camera2, NFC, Preferences, Notification

    选择 API Level 24 的原因:

  • Camera2 完整支持:视觉识别依赖的帧回调 API 在 Level 24 才完整稳定,Level 12 的 Camera API 缺少实时帧回调能力
  • NFC 增强功能:NFC 标签读取的异步 API 在 Level 24 有重大改进,支持 NDEF 格式的自动解析
  • Preferences 性能优化:Level 24 的 lush() 方法性能提升了约 40%,对 3 秒间隔的高频写入更友好
  • Notification 渠道化:预警通知的分类和优先级管理在 Level 24 更加灵活,支持紧急/重要/普通三档
  • Canvas 2D 增强:Level 24 的 Canvas API 支持 setLineDash()、rc() 等绘图方法,是趋势图和饼图的必要依赖

  • 5. 数据流架构

    5.1 核心监控数据流

    核心监控数据流是 IVGuard 最关键的数据流,从相机采集到预警触发的完整链路:

    ┌─────────┐ 帧数据 ┌──────────────┐ 液位值 ┌──────────────┐ │ Camera │ ──────────→ │ VisionService │ ──────────→ │ 预警判断逻辑 │ │ Service │ │ analyzeFrame()│ │ (阈值检测) │ └─────────┘ └──────────────┘ └──────┬───────┘ │ ┌─────────────┼─────────────┐ │ 液位 < 20% │ 液位 < 10% │ 液位 < 5% ↓ ↓ ↓ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ 黄色预警 │ │ 橙色预警 │ │ 红色预警 │ │ │ │ │ │ │ │ ↓ │ │ ↓ │ │ ↓ │ │Notification│ │Notification│ │Notification│ │ 通知栏 │ │ 通知栏 │ │ 通知栏 │ │ │ │ │ │ + 震动 │ │ │ │ Speech │ │ + 语音 │ │ │ │ 语音提醒 │ │ + 手表震动 │ └────────────┘ └────────────┘ └────────────┘

    数据流详细步骤:

  • CameraService.startCapture() 启动后置摄像头,以 setInterval(3000) 为间隔获取帧
  • 每帧数据传入 VisionService.analyzeFrame(frame),识别液位百分比
  • VisionService 内部逻辑(当前为模拟):
    • 初始液位 85%
    • 每次采样衰减 1-2%(随机)
    • 返回当前液位值
  • 页面层接收到液位值后更新 @State currentLevel
  • 预警判断逻辑根据阈值分级触发
  • 触发后调用 NotificationService、SpeechService、WatchService
  • VisionService 液位模拟算法详解

    ` ypescript // VisionService.ets 完整实现 export class VisionService { private static currentLevel: number = 85 private static lastTimestamp: number = 0 private static flowRate: number = 0

    static analyzeFrame(frame: image.PixelMap | null): number { // MVP: 模拟液位衰减 // 生产环境: 贴纸刻度识别 + 颜色边界检测 const decay = 1 + Math.random() // 1-2% 每采样周期 VisionService.currentLevel = Math.max(0, VisionService.currentLevel – decay)

    // 计算流速
    const now = Date.now()
    if (VisionService.lastTimestamp > 0) {
    const elapsed = (now – VisionService.lastTimestamp) / 60000 // 分钟
    VisionService.flowRate = decay / elapsed // %/min
    }
    VisionService.lastTimestamp = now

    return VisionService.currentLevel

    }

    static getFlowRate(): number { return VisionService.flowRate }

    static getEstimatedTime(): number { if (VisionService.flowRate <= 0) return Infinity return VisionService.currentLevel / VisionService.flowRate }

    static reset(): void { VisionService.currentLevel = 85 VisionService.lastTimestamp = 0 VisionService.flowRate = 0 } } `

    预警阈值分级策略

    IVGuard 采用三级预警机制,根据液位百分比和下降速率综合判断:

    等级液位阈值通知方式语音震动手表
    黄色预警 < 20% 通知栏提示
    橙色预警 < 10% 通知栏+横幅 语音提醒 短震动 震动
    红色预警 < 5% 紧急通知+全屏 语音提醒 长震动 连续震动

    ypescript // MonitorPage.ets 中的预警判断逻辑 private checkAlert(level: number): void { if (level < 5) { // 红色预警 NotificationService.sendUrgent('紧急预警', 液位仅剩 %,请立即处理!) SpeechService.speak('输液即将结束,请立即呼叫护士') WatchService.vibrate('urgent') } else if (level < 10) { // 橙色预警 NotificationService.sendAlert('输液预警', 液位 %,即将完成) SpeechService.speak('输液即将结束,请注意') WatchService.vibrate('warning') } else if (level < 20) { // 黄色预警 NotificationService.sendInfo('输液提示', 液位 %,请留意) } }

    5.2 药物管理数据流

    药物管理的数据流涉及药物录入、相互作用检测和 UI 展示:

    ┌──────────────────────────────────────────────────────────────────┐ │ 药物管理数据流 │ │ │ │ 输入源 │ │ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │ │扫码添加 │ │手动输入 │ │NFC 贴纸读取 │ │ │ │ScanService│ │用户表单 │ │NfcService │ │ │ └─────┬────┘ └─────┬────┘ └──────┬───────┘ │ │ │ │ │ │ │ └────────────────────┼─────────────────────┘ │ │ ↓ │ │ ┌────────────────┐ │ │ │ Medicine 对象 │ │ │ │ (数据模型) │ │ │ └───────┬────────┘ │ │ │ │ │ ┌─────────────┼──────────────┐ │ │ ↓ ↓ ↓ │ │ ┌────────────┐ ┌───────────┐ ┌──────────────┐ │ │ │ DataStore │ │DrugDB查询 │ │相互作用检测 │ │ │ │ save() │ │IVDrugDB │ │DrugDatabase │ │ │ │ 持久化 │ │药物详情 │ │ 12组检测 │ │ │ └────────────┘ └───────────┘ └──────┬───────┘ │ │ │ │ │ ┌────────────┼────────────┐ │ │ │ 有相互作用 │ 无相互作用 │ │ │ ↓ ↓ │ │ │ ┌──────────────┐ ┌──────────┐ │ │ │ │ AlertCard │ │MedicineC.│ │ │ │ │ 红色警告 │ │ 正常卡片 │ │ │ │ │ 显示风险描述 │ │ 显示详情 │ │ │ │ └──────────────┘ └──────────┘ │ │ └──────────────────────────────────────────────────────────────────┘

    药物相互作用检测逻辑:

    ` ypescript // DrugDatabase.ets 中的检测逻辑 export class DrugDatabase { static readonly INTERACTIONS: DrugInteraction[] = [ { drugA: ‘头孢曲松’, drugB: ‘含钙输液’, severity: ‘high’, description: ‘头孢曲松与含钙输液配伍可形成致命性沉淀’ }, { drugA: ‘华法林’, drugB: ‘阿司匹林’, severity: ‘high’, description: ‘联合使用显著增加出血风险’ }, { drugA: ‘庆大霉素’, drugB: ‘呋塞米’, severity: ‘high’, description: ‘增加耳毒性和肾毒性风险’ }, { drugA: ‘头孢哌酮’, drugB: ‘酒精’, severity: ‘high’, description: ‘双硫仑样反应,面部潮红、头痛、心悸’ }, // … 共 12 组 ]

    static checkInteraction(drug1: string, drug2: string): DrugInteraction | null { for (const interaction of DrugDatabase.INTERACTIONS) { if ((interaction.drugA === drug1 && interaction.drugB === drug2) || (interaction.drugA === drug2 && interaction.drugB === drug1)) { return interaction } } return null }

    static checkAllInteractions(newDrug: string, existingDrugs: string[]): DrugInteraction[] { const results: DrugInteraction[] = [] for (const drug of existingDrugs) { const interaction = DrugDatabase.checkInteraction(newDrug, drug) if (interaction) results.push(interaction) } return results } } `

    药物添加完整流程:

    当用户通过扫码或手动方式添加新药物时,系统执行以下步骤:

  • 获取药物标识(条码/名称/NFC ID)
  • 查询 IVDrugDatabase 获取药物详情
  • 构建 Medicine 对象
  • 调用 DrugDatabase.checkAllInteractions() 检测相互作用
  • 如果有相互作用,创建 AlertCard 显示警告
  • 调用 DataStore.save(‘medicines’, updatedList) 持久化
  • 更新 UI 列表显示
  • 5.3 费用计算数据流

    费用管理的数据流涵盖费用录入、医保计算和可视化展示:

    ┌────────────────────────────────────────────────────────────────────┐ │ 费用计算数据流 │ │ │ │ ┌──────────┐ ┌──────────────┐ ┌────────────────────┐ │ │ │ 费用录入 │ ──→ │ CostItem对象 │ ──→ │ CostService │ │ │ │ (用户) │ │ (数据模型) │ │ calculate() │ │ │ └──────────┘ └──────────────┘ └─────────┬──────────┘ │ │ │ │ │ ┌────────────────────┼──────────┐ │ │ ↓ ↓ ↓ │ │ ┌──────────────┐ ┌────────────┐ ┌─────────┐ │ │ │ 医保报销计算 │ │ 自费估算 │ │ 费用分类 │ │ │ │ │ │ │ │ 归集 │ │ │ │ InsuranceRef │ │ 个人自付 │ │ 按类别 │ │ │ │ 6城市参考 │ │ = 总额 │ │ 统计 │ │ │ │ 起付线/比例 │ │ – 报销额 │ │ │ │ │ └──────┬───────┘ └──────┬─────┘ └────┬────┘ │ │ │ │ │ │ │ └───────────┬───────┘ │ │ │ ↓ ↓ │ │ ┌─────────────────┐ ┌───────────────┐ │ │ │ CostSummary对象 │ │ 分类费用数组 │ │ │ │ 总额/报销/自付 │ │ [药费,检查,..]│ │ │ └────────┬────────┘ └───────┬───────┘ │ │ │ │ │ │ ↓ ↓ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ 文字展示 │ │ CostPieChart │ │ │ │ 费用明细 │ │ 饼图展示 │ │ │ └──────────────┘ └──────────────┘ │ └────────────────────────────────────────────────────────────────────┘

    CostService 医保计算核心逻辑:

    ` ypescript // CostService.ets export class CostService { static calculateReimbursement( totalCost: number, policy: InsurancePolicy ): CostSummary { const deductible = policy.deductible // 起付线 const rate = policy.reimbursementRate // 报销比例 const cap = policy.annualCap // 年度封顶线

    // 超过起付线的部分才能报销
    const aboveDeductible = Math.max(0, totalCost – deductible)
    // 按报销比例计算
    const rawReimbursement = aboveDeductible * rate
    // 不超过年度封顶线
    const cappedReimbursement = Math.min(rawReimbursement, cap – policy.yearToDate)
    // 最终报销金额(不允许为负)
    const finalReimbursement = Math.max(0, cappedReimbursement)
    // 个人自付
    const outOfPocket = totalCost – finalReimbursement

    return new CostSummary(
    totalCost,
    finalReimbursement,
    outOfPocket,
    deductible
    )

    }

    static breakdownByCategory(items: CostItem[]): Map<string, number> { const categoryMap = new Map<string, number>() for (const item of items) { const current = categoryMap.get(item.category) || 0 categoryMap.set(item.category, current + item.amount) } return categoryMap }

    static estimateOutOfPocket( items: CostItem[], policy: InsurancePolicy ): number { const total = items.reduce((sum, item) => sum + item.amount, 0) const summary = CostService.calculateReimbursement(total, policy) return summary.outOfPocket } } `

    医保计算示例

    以北京市医保为例,假设总费用 8000 元:

    ` 输入: 总费用: 8,000 元 起付线: 1,300 元 (北京) 报销比例: 85% 年度封顶线: 50,000 元 年度已报销: 12,000 元

    计算过程: 超起付线部分: 8,000 – 1,300 = 6,700 元 原始报销额: 6,700 * 0.85 = 5,695 元 封顶限制: min(5,695, 50,000 – 12,000) = 5,695 元 (未超) 最终报销: 5,695 元 个人自付: 8,000 – 5,695 = 2,305 元

    输出 CostSummary: total: 8,000 元 reimbursed: 5,695 元 outOfPocket: 2,305 元 deductible: 1,300 元 `

    5.4 完整数据流图

    以下 ASCII 图展示了 IVGuard 系统中所有核心数据流的完整关系:

    ╔══════════════════════════════════════════════════════════════════════════════╗ ║ IVGuard 完整数据流图 ║ ╠══════════════════════════════════════════════════════════════════════════════╣ ║ ║ ║ ┌──────────┐ ║ ║ │ 硬件层 │ ║ ║ │ │ ║ ║ │ Camera ──┼──────────────────────────────┐ ║ ║ │ NFC ──┼──────────┐ │ ║ ║ │ 扫码器 ──┼────┐ │ │ ║ ║ └──────────┘ │ │ │ ║ ║ │ │ │ ║ ║ ┌───────────────┼─────┼────────────────────┼──────────────────────────┐ ║ ║ │ 服务层 │ │ │ │ ║ ║ │ │ │ │ │ ║ ║ │ ScanService ←┘ │ ↓ │ ║ ║ │ │ │ ┌─────────────────┐ │ ║ ║ │ │ │ │ CameraService │ │ ║ ║ │ │ │ │ startCapture() │ │ ║ ║ │ ↓ │ └────────┬────────┘ │ ║ ║ │ ┌─────────┐ │ │ 帧数据 │ ║ ║ │ │DataStore│ │ ↓ │ ║ ║ │ │ save() │ │ ┌─────────────────┐ │ ║ ║ │ └────┬────┘ │ │ VisionService │ │ ║ ║ │ │ │ │ analyzeFrame() │ │ ║ ║ │ │ │ └────────┬────────┘ │ ║ ║ │ │ │ │ 液位值 │ ║ ║ │ │ │ ┌────────┴────────┐ │ ║ ║ │ │ │ ↓ ↓ │ ║ ║ │ │ ┌─────────────┐ ┌────────────┐ ┌──────────────┐ │ ║ ║ │ │ │NfcService │ │Notification│ │SpeechService │ │ ║ ║ │ │ │pair()/read()│ │Service │ │speak() │ │ ║ ║ │ │ └──────┬──────┘ └────────────┘ └──────────────┘ │ ║ ║ │ │ │ ↑ │ ║ ║ │ │ ↓ │ │ ║ ║ │ ┌────┴─────────────────────────────────────────────┐ │ │ ║ ║ │ │ 数据层 (Model) │ │ │ ║ ║ │ │ │ │ │ ║ ║ │ │ DataModels RouteParams HospitalData │ │ │ ║ ║ │ │ DrugDatabase IVDrugDatabase InsuranceRef │ │ │ ║ ║ │ └────────────────────┬─────────────────────────────┘ │ │ ║ ║ │ │ │ │ ║ ║ │ ┌────────────────────┼─────────────────────────────┐ │ │ ║ ║ │ │ CostService ←─────┤ AIService ←──────────────┤──┘ │ ║ ║ │ │ calculate() │ analyze() │ │ ║ ║ │ │ │ │ │ ║ ║ │ │ WatchService │ NavService │ │ ║ ║ │ │ sync()/alert() │ navigate() │ │ ║ ║ │ └────────────────────┴─────────────────────────────┘ │ ║ ║ └───────────────────────────────────────────────────────────────────┘ ║ ║ ║ ║ ┌───────────────────────────────────────────────────────────────────┐ ║ ║ │ 组件层 + 页面层 │ ║ ║ │ │ ║ ║ │ LevelProgress ←── currentLevel MedicineCard ←── Medicine │ ║ ║ │ MonitorOverlay ←── frame AlertCard ←── DrugInteraction │ ║ ║ │ FlowChart ←── LevelRecord[] CostPieChart ←── CostItem[] │ ║ ║ │ HospitalMapView ←── HospitalPOI StepIndicator ←── step状态 │ ║ ║ └───────────────────────────────────────────────────────────────────┘ ║ ╚══════════════════════════════════════════════════════════════════════════════╝

    NFC 配对数据流

    NFC 配对是 IVGuard 中一个重要的辅助数据流,用于快速关联输液袋与药物信息:

    ┌──────────┐ NFC标签数据 ┌──────────────┐ 贴纸ID ┌──────────────┐ │ NFC标签 │ ─────────────→ │ NfcService │ ──────────→ │ DataStore │ │ (贴纸内) │ │ pair()/read()│ │ load() │ └──────────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ 药物关联ID │ 药物列表 ↓ ↓ ┌──────────────┐ ┌──────────────┐ │IVDrugDatabase│ │ Medicine[] │ │ 50种药物详情 │ │ 用户药物列表 │ └──────┬───────┘ └──────────────┘ │ ↓ ┌──────────────┐ │ 自动填充 │ │ Medicine对象 │ │ → 药物管理页 │ └──────────────┘

    当用户将手机靠近输液袋上的 NFC 贴纸时,NfcService 读取标签中的贴纸 ID,然后根据 ID 在 IVDrugDatabase 中查找关联的药物信息,自动填充到 Medicine 对象中,省去了手动输入的步骤。

    AI 分析数据流

    AIService 通过查询 DataStore 中的历史监控数据,提供智能建议:

    ┌──────────────┐ 历史数据 ┌──────────────┐ 分析结果 ┌──────────────┐ │ DataStore │ ──────────→ │ AIService │ ──────────→ │ AnalysisPage │ │ load() │ │ analyze() │ │ UI展示 │ │ sessions │ │ suggest() │ │ │ │ records │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ 基于规则的推理 ↓ ┌──────────────────────────────┐ │ 规则引擎: │ │ – 平均输液时长 > 3h → 建议咨询 │ │ – 预警频率 > 3次/天 → 关注流速 │ │ – 药物数 > 5 → 检查相互作用 │ │ – 夜间输液 → 提醒陪护 │ └──────────────────────────────┘


    6. 项目目录结构详解

    6.1 目录树总览

    entry/src/main/ets/ ├── model/ # 数据模型层 (6个文件) │ ├── DataModels.ets # 15个核心数据类 │ ├── RouteParams.ets # 路由参数接口 │ ├── HospitalData.ets # 医院POI静态数据 │ ├── DrugDatabase.ets # 药物相互作用 (12组) │ ├── IVDrugDatabase.ets # 静脉药物数据库 (50种) │ └── InsuranceReference.ets # 6城市医保参考 ├── service/ # 服务层 (11个文件) │ ├── DataStore.ets # 数据持久化 │ ├── CameraService.ets # 相机服务 │ ├── VisionService.ets # 视觉识别 │ ├── NfcService.ets # NFC配对 │ ├── NotificationService.ets # 通知推送 │ ├── SpeechService.ets # 语音提醒 │ ├── ScanService.ets # 扫码服务 │ ├── NavService.ets # 导航服务 │ ├── WatchService.ets # 手表联动 │ ├── AIService.ets # AI分析 │ └── CostService.ets # 费用计算 ├── component/ # 组件层 (8个文件) │ ├── LevelProgress.ets # 液位进度 │ ├── MedicineCard.ets # 药物卡片 │ ├── AlertCard.ets # 预警卡片 │ ├── MonitorOverlay.ets # 监控叠加层 │ ├── HospitalMapView.ets # 医院平面图 │ ├── FlowChart.ets # 流速趋势图 │ ├── CostPieChart.ets # 费用饼图 │ └── StepIndicator.ets # 步骤指示器 └── pages/ # 页面层 (17个文件) ├── Index.ets # 入口 (角色选择) ├── HomePage.ets # 患者主页 (5-Tab) ├── MonitorPage.ets # 实时监控 ├── MedicinePage.ets # 药物管理 ├── MedicineDetailPage.ets # 药品详情 ├── HistoryPage.ets # 历史记录 ├── AnalysisPage.ets # 数据分析 ├── CostPage.ets # 费用管理 ├── CostDetailPage.ets # 费用详情 ├── InsuranceSetupPage.ets # 医保设置 (5步) ├── SettingsPage.ets # 系统设置 ├── HospitalNavPage.ets # 医院导航 ├── NurseHomePage.ets # 护士工作台 ├── PatientListPage.ets # 患者列表 ├── NurseAnalysisPage.ets # 护士分析 ├── FamilyHomePage.ets # 家属远程监控 └── FamilyAlertPage.ets # 家属预警记录

    文件统计:

    目录文件数代码行数(估)职责定位
    model/ 6 ~600 数据结构与静态数据
    service/ 11 ~1500 业务逻辑与系统封装
    component/ 8 ~1200 可复用 UI 组件
    pages/ 17 ~3000 完整业务页面
    合计 42 ~6300

    6.2 数据模型层 (model/)

    数据模型层是整个系统的数据基础,定义了所有业务实体的结构和关系。共 6 个文件,承担不同的数据职责。

    DataModels.ets — 核心数据类定义

    这是系统最核心的数据文件,定义了 15 个数据类,涵盖了 IVGuard 的所有业务实体:

    序号类名用途关键字段
    1 Medicine 药物信息 id, name, dosage, frequency, startDate
    2 MonitorSession 监控会话 id, startTime, endTime, startLevel, endLevel
    3 LevelRecord 液位记录 timestamp, level, flowRate
    4 MonitorRecord 监控记录 sessionId, records: LevelRecord[]
    5 DrugInteraction 药物相互作用 drugA, drugB, severity, description
    6 AlertEvent 预警事件 id, timestamp, type, level, message, handled
    7 HospitalPOI 医院兴趣点 id, name, type, floor, x, y
    8 IVDrugInfo 静脉药物详情 name, category, concentration, notes
    9 CostItem 费用条目 id, name, category, amount, date
    10 InsurancePolicy 医保政策 city, deductible, rate, cap
    11 CostSummary 费用汇总 total, reimbursed, outOfPocket
    12 AppSettings 应用设置 alertThreshold, speechEnabled, darkMode
    13 UserRole 用户角色枚举 PATIENT, NURSE, FAMILY
    14 NavPathData 导航路径 steps: string[], target: HospitalPOI
    15 (辅助类型) 路由参数等 视具体业务定义

    UserRole 枚举的特别说明——它不仅用于 UI 分发,更是全系统权限控制的基础:

    ypescript export enum UserRole { PATIENT = 'patient', NURSE = 'nurse', FAMILY = 'family' }

    数据类之间的关系:

    MonitorSession ──1:N──→ LevelRecord (一次会话包含多条液位记录) MonitorSession ──1:1──→ MonitorRecord (一次会话对应一条汇总记录) Medicine ──N:N──→ DrugInteraction (药物间多对多相互作用) AlertEvent ──N:1──→ MonitorSession (多个预警属于同一次会话) CostItem ──N:1──→ InsurancePolicy (多项费用使用同一医保) CostItem ──N:1──→ CostSummary (多项费用汇总为一个结果) HospitalPOI ──1:1──→ NavPathData (一个目标对应一条路径)

    RouteParams.ets — 路由参数接口

    定义页面间导航时传递的参数接口,确保类型安全:

    ` ypescript export interface MedicineDetailParams { medicineId: string }

    export interface MonitorParams { sessionId?: string }

    export interface CostDetailParams { costItemId: string }

    export interface NursePatientParams { patientId: string } `

    路由参数的作用:在 pushUrl 时,通过 params 字段传递参数对象,目标页面通过路由状态获取参数,实现页面间的数据传递。相比全局状态管理,路由参数使页面间的依赖关系更加显式和可追溯。

    HospitalData.ets — 医院 POI 静态数据

    存储医院内部的兴趣点(Point of Interest)数据,用于导航功能:

    ` ypescript export class HospitalData { static readonly POIS: HospitalPOI[] = [ { id: ‘poi_001’, name: ‘内科诊室’, type: ‘department’, floor: 1, x: 120, y: 80 }, { id: ‘poi_002’, name: ‘输液室’, type: ‘treatment’, floor: 1, x: 200, y: 150 }, { id: ‘poi_003’, name: ‘检验科’, type: ‘examination’, floor: 2, x: 80, y: 200 }, { id: ‘poi_004’, name: ‘药房’, type: ‘pharmacy’, floor: 1, x: 300, y: 100 }, { id: ‘poi_005’, name: ‘收费处’, type: ‘billing’, floor: 1, x: 350, y: 80 }, // … 更多 POI ]

    static readonly FLOORS: number[] = [1, 2, 3]

    static getByType(type: string): HospitalPOI[] { return HospitalData.POIS.filter(poi => poi.type === type) }

    static getById(id: string): HospitalPOI | undefined { return HospitalData.POIS.find(poi => poi.id === id) } } `

    POI 的 x 和 y 坐标对应医院平面图上的像素位置,NavService 基于这些坐标进行路径规划。

    DrugDatabase.ets — 药物相互作用数据库

    内置 12 组常见的静脉药物相互作用数据,是药物安全检测的核心数据源:

    ypescript export class DrugDatabase { static readonly INTERACTIONS: DrugInteraction[] = [ { drugA: '头孢曲松', drugB: '含钙输液', severity: 'high', description: '头孢曲松与含钙输液配伍可形成致命性沉淀' }, { drugA: '华法林', drugB: '阿司匹林', severity: 'high', description: '联合使用显著增加出血风险' }, { drugA: '庆大霉素', drugB: '呋塞米', severity: 'high', description: '增加耳毒性和肾毒性风险' }, { drugA: '头孢哌酮', drugB: '酒精', severity: 'high', description: '双硫仑样反应,面部潮红、头痛、心悸' }, { drugA: '氯化钾', drugB: '葡萄糖', severity: 'medium', description: '需控制滴速,避免高钾血症' }, { drugA: '氨苄西林', drugB: '氨基糖苷类', severity: 'medium', description: '不可同瓶滴注,需分开给药' }, { drugA: '维生素K', drugB: '华法林', severity: 'high', description: '维生素K拮抗华法林抗凝作用' }, { drugA: '肝素', drugB: '阿司匹林', severity: 'high', description: '联合使用出血风险显著增加' }, { drugA: '胰岛素', drugB: '葡萄糖', severity: 'medium', description: '需密切监测血糖,调整胰岛素剂量' }, { drugA: '多巴胺', drugB: '碳酸氢钠', severity: 'medium', description: '碱性环境中多巴胺失活' }, { drugA: '两性霉素B', drugB: '氯化钠', severity: 'high', description: '两性霉素B不可用氯化钠溶解' }, { drugA: '万古霉素', drugB: '肝素', severity: 'medium', description: '同管输注可能产生沉淀' }, ] }

    IVDrugDatabase.ets — 静脉药物数据库

    收录 50 种常见的静脉输液药物信息,包括药物名称、分类、浓度、注意事项等:

    ` ypescript export class IVDrugDatabase { static readonly DRUGS: IVDrugInfo[] = [ { name: ‘0.9%氯化钠注射液’, category: ‘溶媒’, concentration: ‘0.9%’, notes: ‘等渗溶液,常用溶媒’ }, { name: ‘5%葡萄糖注射液’, category: ‘溶媒’, concentration: ‘5%’, notes: ‘等渗溶液,糖尿病患者慎用’ }, { name: ‘头孢呋辛钠’, category: ‘抗生素’, concentration: ‘0.75g/支’, notes: ‘需皮试,滴注时间不少于30分钟’ }, { name: ‘左氧氟沙星’, category: ‘抗生素’, concentration: ‘0.5g/100ml’, notes: ‘避光滴注,滴速不宜过快’ }, // … 共 50 种 ]

    static searchByName(keyword: string): IVDrugInfo[] { return IVDrugDatabase.DRUGS.filter( drug => drug.name.includes(keyword) ) }

    static getByCategory(category: string): IVDrugInfo[] { return IVDrugDatabase.DRUGS.filter( drug => drug.category === category ) } } `

    InsuranceReference.ets — 医保参考数据

    收录 6 个城市的医保报销参考数据,为费用估算提供基础参数:

    ` ypescript export class InsuranceReference { static readonly POLICIES: InsurancePolicy[] = [ { city: ‘北京’, deductible: 1300, reimbursementRate: 0.85, annualCap: 50000 }, { city: ‘上海’, deductible: 1500, reimbursementRate: 0.85, annualCap: 55000 }, { city: ‘广州’, deductible: 1600, reimbursementRate: 0.80, annualCap: 45000 }, { city: ‘深圳’, deductible: 1000, reimbursementRate: 0.90, annualCap: 60000 }, { city: ‘成都’, deductible: 800, reimbursementRate: 0.82, annualCap: 40000 }, { city: ‘武汉’, deductible: 1200, reimbursementRate: 0.83, annualCap: 48000 }, ]

    static getByCity(city: string): InsurancePolicy | undefined { return InsuranceReference.POLICIES.find(p => p.city === city) }

    static getCities(): string[] { return InsuranceReference.POLICIES.map(p => p.city) } } `

    6.3 服务层 (service/)

    服务层是 IVGuard 的业务逻辑核心,共 11 个文件,全部采用静态类模式。

    完整服务清单
    服务文件核心方法依赖
    DataStore DataStore.ets init, save, load, delete, clear Preferences
    CameraService CameraService.ets startCapture, stopCapture, getCurrentFrame Camera2 API
    VisionService VisionService.ets analyzeFrame, getFlowRate, getEstimatedTime, reset CameraService(frame)
    NfcService NfcService.ets pair, read, stop NFC API
    NotificationService NotificationService.ets sendAlert, sendUrgent, sendInfo, cancel Notification API
    SpeechService SpeechService.ets speak, stop TextToSpeech API
    ScanService ScanService.ets scan, stop Barcode Scan API
    NavService NavService.ets navigate, search, getPath HospitalData
    WatchService WatchService.ets sync, alert, vibrate DeviceManager API
    AIService AIService.ets analyze, suggest DataStore
    CostService CostService.ets calculateReimbursement, breakdownByCategory, estimateOutOfPocket InsuranceReference, DataStore
    服务依赖矩阵

    ` DataStore Camera Vision NFC Notif Speech Scan Nav Watch AI Cost DataStore – – – – – – – – – – – CameraService – – – – – – – – – – – VisionService – →① – – – – – – – – – NfcService →② – – – – – – – – – – NotificationSvc – – – – – – – – – – – SpeechService – – – – – – – – – – – ScanService →③ – – – – – – – – – – NavService – – – – – – – – – – – WatchService – – – – – – – – – – – AIService →④ – – – – – – – – – – CostService →⑤ – – – – – – – – – –

    ① VisionService 接收 CameraService 的帧数据(但通过参数传递,非直接调用) ② NfcService 将读取的贴纸 ID 存入 DataStore ③ ScanService 将扫码结果存入 DataStore ④ AIService 从 DataStore 读取历史数据进行分析 ⑤ CostService 从 DataStore 读取费用数据和医保设置 `

    6.4 组件层 (component/)

    组件层包含 8 个可复用 UI 组件,每个组件专注于单一视觉职责。

    组件清单与属性接口
    组件文件接收属性渲染方式
    LevelProgress LevelProgress.ets @Prop level: number 声明式(Progress)
    MedicineCard MedicineCard.ets @Prop medicine: Medicine 声明式(Card)
    AlertCard AlertCard.ets @Prop interaction: DrugInteraction 声明式(Card)
    MonitorOverlay MonitorOverlay.ets @Prop level: number, @Prop isMonitoring: boolean 声明式(Stack)
    HospitalMapView HospitalMapView.ets @Prop pois: HospitalPOI[], @Prop currentFloor: number 声明式(Image+Stack)
    FlowChart FlowChart.ets @Prop records: LevelRecord[] Canvas
    CostPieChart CostPieChart.ets @Prop items: CostItem[] Canvas
    StepIndicator StepIndicator.ets @Prop currentStep: number, @Prop totalSteps: number 声明式(Row)
    组件复用分析

    ` 组件被哪些页面使用:

    LevelProgress: MonitorPage, FamilyHomePage MedicineCard: MedicinePage, MedicineDetailPage AlertCard: MedicinePage, NurseHomePage, FamilyAlertPage MonitorOverlay: MonitorPage HospitalMapView: HospitalNavPage FlowChart: AnalysisPage, MonitorPage CostPieChart: CostPage, CostDetailPage StepIndicator: InsuranceSetupPage, HospitalNavPage `

    6.5 页面层 (pages/)

    页面层包含 17 个完整业务页面,按角色分组如下:

    患者端页面 (12 个)
    页面文件入口方式核心功能
    首页 HomePage.ets Index 角色分发 5-Tab 导航框架
    实时监控 MonitorPage.ets 首页监控 Tab → 详情 液位监控、相机预览
    药物管理 MedicinePage.ets 首页药物 Tab 药物列表、添加/删除
    药品详情 MedicineDetailPage.ets MedicinePage → 点击卡片 药物详情、相互作用
    历史记录 HistoryPage.ets 首页历史 Tab 会话列表、详情查看
    数据分析 AnalysisPage.ets 首页 → 更多 趋势图、AI 建议
    费用管理 CostPage.ets 首页费用 Tab 费用列表、饼图
    费用详情 CostDetailPage.ets CostPage → 点击条目 费用详情、报销计算
    医保设置 InsuranceSetupPage.ets CostPage → 设置医保 5 步引导设置
    系统设置 SettingsPage.ets 首页 → 我的 → 设置 通知、语音、阈值
    医院导航 HospitalNavPage.ets 首页 → 导航 平面图、路径规划
    护士端页面 (3 个)
    页面文件入口方式核心功能
    工作台 NurseHomePage.ets Index 角色分发 预警队列、今日统计
    患者列表 PatientListPage.ets 工作台 → 患者列表 全患者状态总览
    聚合分析 NurseAnalysisPage.ets 工作台 → 分析 科室统计、趋势
    家属端页面 (2 个)
    页面文件入口方式核心功能
    远程监控 FamilyHomePage.ets Index 角色分发 亲人状态、一键联系
    预警记录 FamilyAlertPage.ets 远程监控 → 预警记录 历史预警、处理状态
    入口页面 (1 个)
    页面文件入口方式核心功能
    角色选择 Index.ets 应用启动 角色选择、路由分发

    6.6 文件命名规范

    IVGuard 严格遵循以下命名规范:

    类型规范示例
    页面文件 PascalCase + Page 后缀 MonitorPage.ets、CostDetailPage.ets
    组件文件 PascalCase LevelProgress.ets、AlertCard.ets
    服务文件 PascalCase + Service 后缀 CameraService.ets、VisionService.ets
    数据模型 PascalCase DataModels.ets、RouteParams.ets
    静态数据 PascalCase + Database/Reference/Data 后缀 DrugDatabase.ets、InsuranceReference.ets
    数据类 PascalCase Medicine、MonitorSession、CostItem
    枚举 PascalCase UserRole
    接口 PascalCase + Params/Config 后缀 MedicineDetailParams
    静态方法 camelCase startCapture()、nalyzeFrame()
    静态属性 camelCase currentLevel、isRunning

    6.7 层间依赖关系

    IVGuard 的层间依赖遵循严格的单向规则,禁止反向依赖和跨层调用:

    ┌─────────────────────────────────────────────────────────────┐ │ 依赖方向图 │ │ │ │ Index.ets │ │ ↓ pushUrl │ │ Pages ──────────→ Component (通过 @Prop/@Link 传递数据) │ │ │ │ │ ↓ 静态方法调用 │ │ Services ────────→ Model (通过数据类型和静态数据) │ │ │ │ │ ↓ JSON序列化/反序列化 │ │ DataStore ───────→ Preferences (底层存储) │ │ │ │ 禁止依赖: │ │ ✗ Component → Service (组件不直接调用服务) │ │ ✗ Model → Service (数据层不依赖服务层) │ │ ✗ Service → Page (服务不依赖页面) │ │ ✗ Page → Model (直接) (页面通过服务间接使用数据) │ └─────────────────────────────────────────────────────────────┘

    依赖注入替代方案:由于 ArkTS 不支持依赖注入框架,IVGuard 通过以下方式实现解耦:

  • 服务层静态方法:页面调用 VisionService.analyzeFrame() 而非持有一个实例,服务实现的变更对页面透明
  • @Prop/@Link 数据绑定:组件只接收数据,不关心数据来源,实现了组件与数据源的解耦
  • DataStore 统一封装:所有持久化操作通过 DataStore 的统一接口进行,底层存储从 Preferences 切换到 RDB 时,上层无感知

  • 7. 系统用例图与场景分析

    7.1 系统用例图

    ┌─────────────────────────────────────────────────────────────────────────┐ │ IVGuard 系统用例图 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────┐ │ │ │ 患者 │ │ │ └────┬────┘ │ │ │ │ │ ├── UC01: 启动输液监控 │ │ ├── UC02: 查看液位状态 │ │ ├── UC03: 管理药物列表 │ │ ├── UC04: 查看药物详情 │ │ ├── UC05: 检测药物相互作用 ──── «include» ── UC03 │ │ ├── UC06: 扫码添加药物 │ │ ├── UC07: NFC配对输液袋 │ │ ├── UC08: 查看历史记录 │ │ ├── UC09: 查看数据分析 │ │ ├── UC10: 录入医疗费用 │ │ ├── UC11: 估算医保报销 ──── «include» ── UC10 │ │ ├── UC12: 设置医保信息 │ │ ├── UC13: 查看费用饼图 ──── «extend» ── UC10 │ │ ├── UC14: 医院室内导航 │ │ ├── UC15: 修改应用设置 │ │ ├── UC16: 接收语音预警 ──── «extend» ── UC02 │ │ └── UC17: 手表联动查看 │ │ │ │ ┌─────────┐ │ │ │ 护士 │ │ │ └────┬────┘ │ │ │ │ │ ├── UC18: 查看预警队列 │ │ ├── UC19: 处理预警事件 │ │ ├── UC20: 查看患者列表 │ │ ├── UC21: 筛选患者状态 │ │ ├── UC22: 查看聚合分析 │ │ └── UC23: 批量处理预警 ──── «extend» ── UC19 │ │ │ │ ┌─────────┐ │ │ │ 家属 │ │ │ └────┬────┘ │ │ │ │ │ ├── UC24: 远程查看输液状态 │ │ ├── UC25: 接收预警通知 ──── «extend» ── UC24 │ │ ├── UC26: 查看预警记录 │ │ └── UC27: 一键联系护士站 │ │ │ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │ │ │ 跨角色共享: │ │ ├── UC28: 修改应用设置 │ │ └── UC29: 切换用户角色 │ │ │ └─────────────────────────────────────────────────────────────────────────┘

    用例关系说明
    • «include»:被包含用例是基础用例的必经步骤。例如 UC05(检测药物相互作用)是 UC03(管理药物列表)的必要环节——每次添加药物都必须执行相互作用检测
    • «extend»:扩展用例是基础用例的可选增强。例如 UC16(接收语音预警)是 UC02(查看液位状态)的可选扩展——只有液位低于阈值时才触发语音

    7.2 患者典型场景

    场景一:完整输液监控流程

    这是患者使用 IVGuard 的最核心场景,覆盖从挂号到费用结算的完整流程。

    时间线 操作步骤 系统响应 涉及页面/服务 ─────── ──────────────────────────────── ──────────────────────────── ────────────── 09:00 患者挂号就诊 – – 09:30 医生开具输液处方 – – 09:45 护士配药并开始输液 – – 患者打开 IVGuard,选择"患者"角色 Index 分发至 HomePage Index → HomePage 09:46 患者点击"开始监控" 启动 CameraService MonitorPage VisionService 开始识别 3秒间隔采样液位 液位显示 85% LevelProgress 渲染 LevelProgress 09:50 患者通过 NFC 贴纸配对 NfcService 读取贴纸 NfcService 系统自动填充药物信息 查询 IVDrugDatabase MedicinePage 自动检测 DrugDatabase AlertCard 10:30 液位降至 20% 触发黄色预警 NotificationService 发送通知栏提示 NotificationService 10:45 液位降至 10% 触发橙色预警 NotificationService 通知栏 + 语音提醒 SpeechService 手表震动 WatchService 10:50 液位降至 5% 触发红色预警 NotificationService 紧急通知 + 语音 SpeechService 手表连续震动 WatchService 通知护士端 NotificationService 10:55 护士到达,更换输液袋 患者确认换瓶 MonitorPage 系统重置液位至 85% VisionService.reset() VisionService 11:30 输液完成 患者结束监控 MonitorPage 保存 MonitorSession DataStore.save() DataStore 11:35 患者录入费用项目 添加 CostItem CostPage 设置医保信息 InsuranceSetupPage 5步引导 InsuranceSetupPage 查看报销估算 CostService.calculate() CostService 查看费用饼图 CostPieChart 渲染 CostPieChart

    场景二:药物相互作用检测场景

    步骤 操作 系统处理 UI 展示 ──── ──────────────────────────── ──────────────────────────────── ────────────── 1 患者已有药物: 华法林 DataStore.load('medicines') MedicineCard 2 患者扫码添加: 阿司匹林 ScanService.scan() 扫码界面 3 系统检测相互作用 DrugDatabase.checkAllInteractions – 4 发现高危组合: 华法林+阿司匹林 返回 DrugInteraction(severity=high) – 5 显示红色预警卡片 AlertCard 渲染 AlertCard(红色) "联合使用显著增加出血风险" 描述文字显示 警告描述 6 患者确认已知晓 预警状态更新 预警标记已读 7 药物添加完成 DataStore.save('medicines') 更新药物列表

    场景三:医院导航场景

    步骤 操作 系统处理 UI 展示 ──── ──────────────────────────── ──────────────────────────────── ────────────── 1 患者点击"医院导航" HospitalNavPage 加载 HospitalMapView 2 平面图展示 HospitalData.POIS 加载 楼层平面图 3 患者选择"检验科" NavService.search('检验科') 搜索结果 4 系统规划路径 NavService.navigate() StepIndicator 5 步骤引导: "沿走廊前行50米" StepIndicator 渲染步骤1 路径指引 6 步骤引导: "上2楼" StepIndicator 渲染步骤2 楼层切换 7 到达目标 标记完成 到达确认

    7.3 护士典型场景

    场景一:预警处理流程

    时间线 操作步骤 系统响应 涉及页面/服务 ─────── ──────────────────────────────── ──────────────────────────── ────────────── 10:45 护士收到橙色预警推送 NotificationService 通知栏 "3床张三 液位10%" AlertCard 显示 AlertCard 10:46 护士打开工作台 NurseHomePage 加载 NurseHomePage 查看预警队列 按紧急程度排序 预警列表 10:47 护士点击张三预警卡片 展开预警详情 AlertCard(详情) 查看患者信息: 3床, 0.9%NaCl 显示药物和液位信息 详情面板 10:48 护士标记"已处理" AlertEvent.handled = true 状态更新 前往3床更换输液袋 – – 10:55 更换完成,回到工作台 预警队列更新 NurseHomePage 预警状态变为"已处理" 移出预警队列 已处理列表

    场景二:聚合分析场景

    步骤 操作 系统处理 UI 展示 ──── ──────────────────────────── ──────────────────────────────── ────────────── 1 护士点击"聚合分析" NurseAnalysisPage 加载 NurseAnalysisPage 2 查看今日统计 AIService.analyze('daily') 统计卡片 3 查看科室输液分布 按科室分组统计 柱状图/列表 4 查看高频预警时段 按小时统计预警频率 热力图/列表 5 查看药物使用排行 按药物统计使用次数 排行榜 6 查看工作负荷趋势 按日统计巡检和预警数 趋势图

    7.4 家属典型场景

    场景一:远程监控流程

    时间线 操作步骤 系统响应 涉及页面/服务 ─────── ──────────────────────────────── ──────────────────────────── ────────────── 09:00 家属打开 IVGuard,选择"家属"角色 Index 分发至 FamilyHomePage Index → FamilyHomePage 09:01 查看亲人输液状态 加载实时数据 状态面板 液位 85%,预计剩余 2小时 LevelProgress 渲染 LevelProgress 药物: 0.9%NaCl + 头孢呋辛 药物列表显示 MedicineCard 10:30 家属手机推送: "液位 20%" NotificationService 通知推送 家属打开 App 查看详情 FamilyHomePage 刷新 状态更新 10:45 家属收到预警: "液位 10%" 推送 + App内提醒 通知+弹窗 家属点击"联系护士站" 拨打护士站电话 系统电话 10:55 护士处理完成 状态更新为"已处理" 状态刷新 家属看到状态恢复为 85% FamilyHomePage 更新 状态更新

    场景二:预警记录查看

    步骤 操作 系统处理 UI 展示 ──── ──────────────────────────── ──────────────────────────────── ────────────── 1 家属点击"预警记录" FamilyAlertPage 加载 FamilyAlertPage 2 查看历史预警时间线 按时间倒序排列 时间线 3 查看单条预警详情 展开预警类型、级别、处理状态 详情面板 4 统计摘要: 本周 3 次预警 统计已处理/未处理/平均响应时间 摘要卡片


    8. 非功能性需求

    8.1 性能需求

    IVGuard 作为实时输液监控系统,性能是用户体验的基石。以下是具体的性能指标及实现策略:

    核心性能指标
    指标目标值测量方法实现策略
    采样间隔 3 秒 (3000ms) setInterval 回调间隔 MonitorPage.ets 中设置 setInterval(3000)
    液位更新延迟 < 100ms 从 VisionService 返回到 UI 更新 @State 驱动自动刷新
    Canvas 渲染帧率 60fps onReady + 绘制耗时 预计算坐标,限制数据量
    页面切换时间 < 500ms 从 pushUrl 到页面渲染完成 懒加载非首屏内容
    应用冷启动 < 3 秒 从点击图标到首页可交互 DataStore.init() 异步化
    通知推送延迟 < 2 秒 从预警触发到通知到达 系统级通知通道
    语音合成延迟 < 500ms 从调用 speak() 到声音输出 预加载语音引擎
    3 秒采样间隔的设计考量

    3 秒的采样间隔是经过权衡后的选择:

    ` 间隔选择分析:

    1秒间隔: 优势: 液位更新更频繁,感知延迟更低 劣势: CPU/相机/电池消耗大,视觉识别负担重 结论: 过度消耗资源,不必要

    3秒间隔 (最终选择): 优势: 平衡了实时性和资源消耗 劣势: 最大 3 秒的液位更新延迟 结论: 液位变化是缓慢过程,3秒延迟可接受

    5秒间隔: 优势: 资源消耗更低 劣势: 预警响应延迟增大,用户体验下降 结论: 预警不够及时

    10秒间隔: 优势: 资源消耗极低 劣势: 预警延迟过高,可能错过最佳处理窗口 结论: 不可接受 `

    在 MonitorPage.ets 中的实现:

    ` ypescript aboutToAppear() { this.timer = setInterval(() => { const frame = CameraService.getCurrentFrame() if (frame) { this.currentLevel = VisionService.analyzeFrame(frame) this.flowRate = VisionService.getFlowRate() this.estimatedTime = VisionService.getEstimatedTime() this.checkAlert(this.currentLevel) } }, 3000) }

    aboutToDisappear() { if (this.timer) { clearInterval(this.timer) CameraService.stopCapture() } } `

    特别注意 boutToDisappear 中的清理逻辑——页面销毁时必须清除定时器并停止相机,否则会导致后台持续占用硬件资源。

    Canvas 60fps 优化策略

    IVGuard 的 FlowChart 和 CostPieChart 组件采用 Canvas 绘制,需要保持 60fps 的渲染性能:

    ` ypescript // 优化策略一: 数据预计算 private drawChart() { // 在绘制前一次性计算所有坐标,避免逐帧计算 const points = this.records.map((record, index) => { return { x: padding + (index / Math.max(1, this.records.length – 1)) * chartWidth, y: chartHeight – (record.level / 100) * chartHeight } }) // 然后用预计算的 points 绘制 }

    // 优化策略二: 限制数据量 private get displayRecords(): LevelRecord[] { if (this.records.length <= 100) return this.records // 采样降频: 取最近 100 个点 const step = Math.ceil(this.records.length / 100) return this.records.filter((_, index) => index % step === 0) }

    // 优化策略三: 按需重绘 // 只在 records 数据变化时触发重绘,而非每帧重绘 build() { Canvas(this.context) .width(‘100%’) .height(200) .onReady(() => { this.drawChart() // 只在 Canvas 准备好时绘制一次 }) } `

    8.2 可靠性需求

    医疗系统的可靠性要求远高于普通消费级应用。IVGuard 从以下维度保障系统可靠性:

    数据持久化保障
    场景风险保障措施
    应用意外退出 监控数据丢失 每次 setInterval 回调后 DataStore.save()
    手机重启 内存数据丢失 所有关键数据通过 Preferences 持久化
    存储空间不足 写入失败 lush() 异常捕获,降级为内存缓存
    并发写入 数据覆盖 单线程模型下不存在真正的并发,但 lush() 保证原子性
    优雅降级策略

    IVGuard 实现了多层优雅降级,确保核心功能在各种异常情况下依然可用:

    ` 降级层级:

    Level 0 – 完整功能: 相机可用 + NFC可用 + 通知可用 + 语音可用 + 手表可用 → 完整监控体验

    Level 1 – 核心功能 (相机不可用): VisionService 使用模拟数据 (currentLevel 从85%衰减) → 手动模式: 用户观察输液袋,App 估算剩余时间

    Level 2 – 基础功能 (通知/语音不可用): 仅页面内预警提示,无系统通知 → 用户需保持 App 在前台

    Level 3 – 最小功能 (DataStore 不可用): 所有数据仅保存在内存中 → 当前会话数据可用,重启后丢失

    Level 4 – Mock 服务 (开发/测试环境): 所有 Service 使用 Mock 实现 → 完整功能模拟,无需真实硬件 `

    Mock 服务实现

    ` ypescript // VisionService.ets 的 Mock 实现 (当前 MVP) export class VisionService { private static currentLevel: number = 85

    static analyzeFrame(frame: image.PixelMap | null): number { // Mock: 不依赖真实帧数据,直接模拟衰减 const decay = 1 + Math.random() VisionService.currentLevel = Math.max(0, VisionService.currentLevel – decay) return VisionService.currentLevel } }

    // 未来生产版本的 VisionService (OpenCV NAPI) // static analyzeFrame(frame: image.PixelMap): number { // const mat = OpenCVUtils.pixelMapToMat(frame) // const stickerRegion = StickerDetector.detect(mat) // const level = LevelReader.read(stickerRegion) // return level // } `

    8.3 安全需求

    医疗数据属于敏感个人信息,IVGuard 在安全方面遵循"最小权限、数据脱敏、本地优先"三大原则。

    权限最小化

    IVGuard 请求的 8 项系统权限,每一项都有明确的使用场景和最小化范围:

    序号权限用途请求时机角色需求
    1 ohos.permission.CAMERA 拍摄输液袋进行液位识别 首次启动监控时 患者
    2 ohos.permission.NFC_TAG 读取 NFC 贴纸信息 首次配对时 患者
    3 ohos.permission.NOTIFICATION 发送预警通知 应用启动时 全角色
    4 ohos.permission.VIBRATE 紧急预警时震动提醒 应用启动时 全角色
    5 ohos.permission.INTERNET AI 分析和网络同步(预留) 应用启动时 全角色
    6 ohos.permission.LOCATION 医院导航定位 首次导航时 患者/护士
    7 ohos.permission.SCAN_CODE 扫描药品条码 首次扫码时 患者
    8 ohos.permission.DISTRIBUTED_DATASYNC 手表数据同步 首次连接手表时 患者/家属

    权限请求策略:

  • 延迟请求:不在应用启动时一次性请求所有权限,而是在需要使用对应功能时才请求
  • 权限说明:每次请求前向用户解释为何需要该权限
  • 降级运行:用户拒绝权限时,对应功能优雅降级而非崩溃
  • 医疗数据脱敏

    ` 数据分类与脱敏策略:

    ┌──────────────────────────────────────────────────────┐ │ 敏感等级 │ 数据类型 │ 脱敏策略 │ ├──────────────┼────────────────────┼──────────────────┤ │ 高敏感 │ 患者姓名、身份证号 │ 不采集,使用代号 │ │ 高敏感 │ 具体药物剂量 │ 仅显示通用名称 │ │ 中敏感 │ 监控记录、液位数据 │ 本地存储,不上传 │ │ 中敏感 │ 预警事件 │ 本地存储,不上传 │ │ 低敏感 │ 费用数据 │ 本地存储,不上传 │ │ 非敏感 │ 应用设置 │ 本地存储 │ │ 非敏感 │ 医院POI数据 │ 静态内置 │ └──────────────────────────────────────────────────────┘ `

    本地存储优先

    IVGuard 的 MVP 版本采用 纯本地存储 架构,所有数据保存在设备本地,不涉及云端传输:

    ` 数据存储位置:

    Preferences (‘ivguard_db’): ├── 药物列表 medicines ├── 监控会话 monitor_sessions ├── 费用条目 cost_items ├── 医保设置 insurance_policy ├── 应用设置 app_settings ├── 用户角色 user_role └── 预警事件 alert_events

    无云端存储: ✗ 无用户账号系统 ✗ 无数据上传 ✗ 无第三方数据共享 ✓ 所有数据仅存在于设备本地 `

    这种设计在 MVP 阶段的优势是显著的:无需后端服务器、无需网络安全协议、无需数据合规审批、无需用户注册登录。同时,DataStore 的抽象层设计为未来引入云端同步预留了扩展点——只需在 DataStore 中增加云端读写逻辑,上层代码无需变更。

    8.4 可维护性需求

    分层架构的可维护性

    IVGuard 的五层架构直接支撑了以下可维护性指标:

    维护场景影响范围修改文件数回归风险
    修改预警阈值 页面层 1 (MonitorPage)
    替换视觉识别算法 服务层 1 (VisionService)
    新增药物数据 数据层 1 (DrugDatabase) 极低
    新增费用类别 数据层+服务层 2 (DataModels+CostService)
    修改液位进度条样式 组件层 1 (LevelProgress) 极低
    新增页面 页面层 1 (新Page文件)
    服务单例的可维护性

    静态类模式的 Service 使得代码定位和修改变得极其直观:

    ` 需求: “修改语音提醒的文案”

    定位路径:

  • 语音相关 → SpeechService.ets
  • 搜索 speak() 方法
  • 找到文案定义
  • 修改文案
  • 影响范围: 仅 SpeechService.ets 一个文件 回归风险: 仅影响语音输出,不影响其他功能 `

    组件复用的可维护性

    8 个通用组件在多个页面间复用,降低了重复代码量:

    ` 组件复用率:

    LevelProgress: 2 个页面使用 → 1 个组件替代 2 份重复代码 MedicineCard: 2 个页面使用 → 节省约 120 行代码 AlertCard: 3 个页面使用 → 节省约 180 行代码 FlowChart: 2 个页面使用 → 节省约 200 行代码 CostPieChart: 2 个页面使用 → 节省约 200 行代码

    总节省: 约 700 行代码 (占页面层代码的 ~23%) `

    8.5 可扩展性需求

    IVGuard 在架构层面预留了多个扩展点,确保未来功能迭代无需大规模重构:

    扩展点一:OpenCV NAPI 预留

    当前 VisionService 采用模拟算法,未来接入真实视觉识别时,需要通过 NAPI(Native API)调用 OpenCV:

    ` 扩展路径:

    当前 (MVP): VisionService.analyzeFrame(frame) → 模拟: currentLevel – random(1-2)

    未来 (生产): VisionService.analyzeFrame(frame) → NAPI: OpenCVUtils.pixelMapToMat(frame) → NAPI: StickerDetector.detect(mat) → NAPI: LevelReader.read(stickerRegion) → 返回真实液位值

    变更范围: 仅 VisionService.ets 内部实现 上层影响: 无 (MonitorPage 调用接口不变) `

    扩展点二:手表端预留

    WatchService 的接口设计已考虑手表端扩展:

    ` ypescript export class WatchService { // 当前实现: 模拟手表通信 static async sync(data: MonitorSession): Promise { // TODO: 通过 DeviceManager API 实现真实通信 }

    static async vibrate(pattern: string): Promise { // TODO: 调用手表端震动 API }

    // 未来扩展: 手表端 UI // static async updateWidget(level: number): Promise // static async showAlert(message: string): Promise } `

    扩展点三:云端同步预留

    DataStore 的抽象层设计支持未来无缝切换至云端同步:

    ` 当前架构: Page → DataStore.save() → Preferences.put()

    未来架构: Page → DataStore.save() → Preferences.put() (本地缓存) + CloudSync.upload() (云端同步)

    Page → DataStore.load() → Preferences.get() (本地优先) + CloudSync.download() (冲突合并) `

    扩展点四:多语言预留

    所有用户可见的字符串均未硬编码在页面层,而是通过常量或数据模型提供:

    ` ypescript // 当前: 中文硬编码 (MVP 简化) Text(‘当前液位’)

    // 未来: 多语言支持 Text(StringResource.get(‘monitor_current_level’)) // zh: ‘当前液位’ // en: ‘Current Level’ `

    扩展点五:主题切换预留

    AppSettings 数据模型中预留了 darkMode 字段:

    ypescript export class AppSettings { alertThreshold: number = 20 speechEnabled: boolean = true darkMode: boolean = false // 预留:深色模式 fontSize: number = 16 // 预留:字号调整 language: string = 'zh-CN' // 预留:语言设置 }


    9. 核心技术决策记录

    技术决策记录(Architecture Decision Record, ADR)是 IVGuard 项目最重要的架构资产之一。每个决策都经过充分的方案对比和利弊权衡,确保选择的方案在当前阶段是最优解,同时为未来的演进预留了空间。

    9.1 视觉识别方案选择

    决策编号: ADR-001 决策状态: 已采纳 决策日期: 项目启动期

    决策背景

    IVGuard 的核心功能是输液液位识别。需要选择一种可靠的液位识别方案,使得普通智能手机能够准确读取输液袋中的液位信息。

    候选方案

    方案 A:纯颜色边界检测

    ` 原理: 输液袋中液体区域与空气区域的颜色/透明度不同 通过检测颜色边界确定液面位置 液位 = 边界位置 / 输液袋总高度

    优势: ✓ 无需额外硬件 ✓ 实现简单

    劣势: ✗ 受光照影响大 (日光/灯光/阴影) ✗ 受输液袋形状影响 (不同厂家袋型不同) ✗ 受液体颜色影响 (有色液体 vs 无色液体) ✗ 识别精度约 ±15% ✗ 需要大量训练数据 `

    方案 B:贴纸刻度读取(最终选择)

    ` 原理: 在输液袋外壁贴一张专用贴纸 贴纸包含等间距刻度线和颜色标记 视觉算法通过识别刻度线与液面的相对位置确定液位 液位 = 液面处刻度值

    优势: ✓ 刻度提供绝对参考系,消除形状/角度误差 ✓ 颜色标记作为辅助识别,双重保障 ✓ 识别精度约 ±5% ✓ 对光照条件不敏感 ✓ 不同输液袋通用

    劣势: ✗ 需要物理贴纸 (成本约 0.5 元/张) ✗ 贴纸粘贴需要规范操作 ✗ NFC 芯片增加贴纸成本 `

    方案 C:液位传感器硬件

    ` 原理: 在输液袋上安装专用液位传感器 通过电阻/电容/超声波原理直接测量液位

    优势: ✓ 精度极高 (±1%) ✓ 不受视觉条件影响

    劣势: ✗ 需要专用硬件,成本高 (50-200 元/个) ✗ 需要与输液袋集成,涉及医疗器械审批 ✗ 需要蓝牙/WiFi 桥接到手机 ✗ 不适合 MVP 验证 `

    决策结论

    选择 方案 B(贴纸刻度读取),理由:

  • 精度与成本的平衡:±5% 的精度满足临床预警需求(20%/10%/5% 三级阈值),贴纸成本极低
  • 通用性:贴纸可适配任何品牌的输液袋,不受袋型限制
  • MVP 友好:贴纸可快速打样验证,无需硬件开发周期
  • 双模式识别:刻度 + 颜色双重识别,提高鲁棒性
  • NFC 增强:贴纸内嵌 NFC 芯片,实现快速配对和药物信息关联
  • 影响范围
    • VisionService.ets:识别算法需针对贴纸刻度设计
    • NfcService.ets:需实现 NFC 贴纸读取
    • MonitorOverlay.ets:需叠加显示识别结果
    • 硬件供应链:需设计并生产专用贴纸

    9.2 多瓶监控方案

    决策编号: ADR-002 决策状态: 已采纳 决策日期: 项目启动期

    决策背景

    临床场景中,患者可能同时接受多瓶输液(序贯输注或双通道输注)。需要选择多瓶监控的实现方式。

    候选方案

    方案 A:快速切换监控(最终选择)

    ` 原理: 用户在多个输液瓶之间快速切换 每次只监控一瓶 通过 Tab 或下拉选择切换目标

    优势: ✓ 实现简单 ✓ 单摄像头即可 ✓ UI 设计简洁 ✓ 符合序贯输注的常见场景

    劣势: ✗ 无法同时监控多瓶 ✗ 切换间隔存在监控盲区 `

    方案 B:同时多标记监控

    ` 原理: 在同一画面中识别多个贴纸标记 分别计算每个标记的液位 同时展示多瓶状态

    优势: ✓ 真正的多瓶并行监控 ✓ 无切换盲区 ✓ 适合双通道输注

    劣势: ✗ 视觉识别算法复杂度倍增 ✗ 需要更广的摄像头视野 ✗ UI 展示复杂 ✗ 性能开销增大 ✗ 多标记干扰问题 `

    决策结论

    选择 方案 A(快速切换监控),理由:

  • MVP 简化:多瓶同时监控是"锦上添花"而非"雪中送炭",MVP 阶段优先验证核心流程
  • 临床现实:多数患者一次只输一瓶(序贯输注时逐瓶更换),快速切换足以覆盖
  • 性能保障:单目标识别的性能和精度远优于多目标
  • 演进路径清晰:未来可在 VisionService 中增加多目标检测能力,UI 层增加多瓶面板
  • 9.3 家属端方案

    决策编号: ADR-003 决策状态: 已采纳 决策日期: 项目启动期

    决策背景

    家属需要远程查看亲人的输液状态。需要选择家属功能的实现方式。

    候选方案

    方案 A:同 App 角色切换(最终选择)

    ` 原理: 在同一个 App 中支持三种角色 Index.ets 入口处选择角色 不同角色进入不同的 HomePage 共享底层 Service 和 Model

    优势: ✓ 单一 App,开发和维护成本低 ✓ 角色切换方便 (一个手机可切换多个角色) ✓ 共享代码量大 (Service/Component/Model 全部复用) ✓ 应用商店只需发布一个包

    劣势: ✗ 角色切换需重新登录 ✗ App 体积稍大 (包含所有角色页面) ✗ 不支持同时在手机端看患者和家属界面 `

    方案 B:独立 App

    ` 原理: 为三种角色分别开发独立 App IVGuard-Patient / IVGuard-Nurse / IVGuard-Family 各自独立安装和运行

    优势: ✓ 各 App 功能聚焦,UI 精简 ✓ 可以同时安装运行 (手机端+手表端分别运行) ✓ 安全隔离更彻底

    劣势: ✗ 开发和维护成本 ×3 ✗ 代码重复量大 ✗ 三个 App 的发布和更新管理 ✗ 家属 App 的推广和安装门槛 `

    决策结论

    选择 方案 A(同 App 角色切换),理由:

  • MVP 成本控制:一个 App 的开发成本远低于三个 App
  • 代码复用:11 个 Service、8 个 Component、6 个 Model 全部复用,仅 Page 层按角色分化
  • 用户体验:家属无需单独下载 App,降低了使用门槛
  • 角色灵活切换:陪护家属可以在同一台手机上切换"患者"和"家属"角色
  • 9.4 数据存储方案

    决策编号: ADR-004 决策状态: 已采纳 决策日期: 项目启动期

    决策背景

    IVGuard 需要本地持久化存储用户数据(药物列表、监控记录、费用数据等)。HarmonyOS 提供了 Preferences(KV 存储)和关系型数据库(RDB)两种方案。

    候选方案对比

    ` 方案 A: Preferences (轻量级 KV 存储) — 最终选择

    适用场景: 数据量小、结构简单、查询需求低 数据模型: Key-Value (JSON 序列化) 性能特征: 读取 O(1),写入需 flush() 存储上限: 建议 < 10KB

    优势: ✓ 初始化零开销 ✓ API 极简 (get/put/flush) ✓ 学习成本低 ✓ 适合 MVP 快速验证 ✓ 性能稳定可预测

    劣势: ✗ 无法按条件查询 (只能按 Key 读取) ✗ 无事务支持 ✗ 数据量增大后性能下降 ✗ 数据关系需在应用层维护

    方案 B: 关系型数据库 (RDB)

    适用场景: 数据量大、关系复杂、需复杂查询 数据模型: 关系表 + SQL 性能特征: SQL 解析 + 索引查询 存储上限: 无硬性上限

    优势: ✓ 完整 SQL 查询能力 ✓ ACID 事务支持 ✓ 联表查询 ✓ 适合数据量增长 ✓ 数据关系在数据库层维护

    劣势: ✗ 初始化开销较大 (建表、索引) ✗ SQL 代码量多 ✗ 学习成本中 ✗ MVP 阶段过度设计 `

    决策结论

    选择 方案 A(Preferences),理由:

  • MVP 数据量小:单用户场景下,药物列表约 3-5 条、监控记录约 10-20 条、费用项目约 5-10 条,总数据量 < 5KB
  • 开发效率:DataStore 封装后只需 save() / load() 两个方法,比 RDB 少 60% 代码量
  • 读取性能:3 秒间隔的高频读取,KV 直读比 SQL 解析快一个数量级
  • 渐进演进:DataStore 的抽象层保证未来可平滑迁移至 RDB
  • 9.5 药物数据库方案

    决策编号: ADR-005 决策状态: 已采纳 决策日期: 项目启动期

    决策背景

    IVGuard 需要内置静脉药物数据库(50 种药物)和药物相互作用数据库(12 组)。需要选择数据的存储和加载方式。

    候选方案

    方案 A:ArkTS 静态数组(最终选择)

    ypescript export class DrugDatabase { static readonly INTERACTIONS: DrugInteraction[] = [ { drugA: '头孢曲松', drugB: '含钙输液', severity: 'high', description: '头孢曲松与含钙输液配伍可形成致命性沉淀' }, // … ] }

    方案 B:rawfile JSON 文件

    resources/rawfile/drug_interactions.json: [ { "drugA": "头孢曲松", "drugB": "含钙输液", … }, … ]

    对比分析
    维度静态数组rawfile JSON
    类型安全 编译期类型检查 运行时 JSON 解析,需手动类型转换
    加载方式 类加载时自动可用 需异步读取 rawfile
    代码提示 IDE 完整提示 无(JSON 是纯文本)
    修改方式 修改 .ets 文件 修改 .json 文件
    性能 零加载开销 首次读取有 IO 开销
    ArkTS 友好度 原生支持 需 JSON.parse + 类型断言
    决策结论

    选择 方案 A(ArkTS 静态数组),理由:

  • 类型安全:ArkTS 的严格类型检查在静态数组中天然生效,JSON 方式需要 JSON.parse() 后手动类型转换,容易出错
  • 零加载开销:静态数组在类加载时即可用,无需异步 IO
  • IDE 支持:TypeScript/ArkTS 的静态数组享有完整的代码补全、重构、跳转支持
  • 数据量可控:50 种药物 + 12 组交互数据量约 300 行,静态数组的可维护性可接受
  • 9.6 状态管理方案

    决策编号: ADR-006 决策状态: 已采纳 决策日期: 项目启动期

    决策背景

    HarmonyOS ArkUI 提供了两套状态管理方案:V1(@Component / @State)和 V2(@ComponentV2 / @Local)。需要选择一套作为项目标准。

    候选方案

    方案 A:V1 状态管理(最终选择)

    ` ypescript @Component struct MonitorPage { @State currentLevel: number = 85 @State isMonitoring: boolean = false @Prop alertThreshold: number

    build() { Column() { LevelProgress({ level: this.currentLevel }) } } } `

    方案 B:V2 状态管理

    ` ypescript @ComponentV2 struct MonitorPage { @Local currentLevel: number = 85 @Local isMonitoring: boolean = false @Param alertThreshold: number

    build() { Column() { LevelProgress({ level: this.currentLevel }) } } } `

    对比分析
    维度V1 (@Component/@State)V2 (@ComponentV2/@Local)
    稳定性 成熟稳定,API 5.0 官方推荐 较新,部分 API 仍在迭代
    社区资源 丰富,大量示例和文档 较少,仍在建设中
    装饰器 @State, @Prop, @Link, @Provide, @Consume @Local, @Param, @Provider, @Consumer
    性能 更优(精细化更新)
    兼容性 API Level 12+ 完整支持 API Level 24+ 完整支持
    迁移风险 中(API 可能变更)
    决策结论

    选择 方案 A(V1 状态管理),理由:

  • 成熟稳定:V1 是 HarmonyOS 5.0 的官方推荐方案,API 稳定,不会有破坏性变更
  • 文档和社区:V1 的文档、示例、社区讨论远比 V2 丰富,降低学习成本和排障时间
  • 项目规模适配:IVGuard 的 17 个页面 + 8 个组件的规模,V1 的状态管理能力完全足够
  • 迁移路径:V1 到 V2 的迁移是渐进式的,未来可在稳定后逐步切换
  • 9.7 决策记录汇总表

    ADR 编号决策标题选择方案核心理由可演进方向
    ADR-001 视觉识别方案 贴纸刻度读取 精度与成本平衡,MVP 友好 OpenCV NAPI 真实识别
    ADR-002 多瓶监控方案 快速切换 MVP 简化,覆盖主要场景 VisionService 多目标检测
    ADR-003 家属端方案 同 App 角色切换 开发成本低,代码复用 可拆分为独立 App
    ADR-004 数据存储方案 Preferences MVP 轻量,开发效率高 迁移至 RDB
    ADR-005 药物数据库方案 ArkTS 静态数组 类型安全,零加载开销 rawfile JSON + 数据更新
    ADR-006 状态管理方案 V1 (@Component/@State) 成熟稳定,文档丰富 渐进迁移至 V2
    决策原则总结

    回顾以上 6 个核心技术决策,可以发现一个统一的决策原则:MVP 优先、渐进演进。

    每个决策都在"当前最优"和"未来可扩展"之间取得了平衡:

  • 不追求一步到位:选择当前阶段最简方案(贴纸而非传感器、单瓶而非多瓶、Preferences 而非 RDB)
  • 预留演进接口:每个决策都标注了未来的可演进方向
  • 抽象层保护:通过 DataStore、VisionService 等抽象层,底层实现的变更对上层透明
  • 成本敏感:在 MVP 阶段,开发成本和维护成本是首要考虑因素
  • 这种"小步快跑"的技术策略,确保 IVGuard 能够快速验证产品假设,同时不因过早优化而浪费资源,也不因架构缺陷而阻碍未来演进。


    附录

    附录 A:术语表

    术语英文含义
    静脉输液 Intravenous (IV) Therapy 通过静脉途径将液体、药物输入体内的治疗方法
    液位 Level 输液袋中液体的剩余量,以百分比表示
    贴纸 Smart Sticker IVGuard 专用的输液袋标识贴纸,含刻度和 NFC 芯片
    预警 Alert 液位低于阈值时触发的分级警告
    POI Point of Interest 兴趣点,医院内的科室、设施等位置
    相互作用 Drug Interaction 两种或多种药物同时使用时产生的不良影响
    医保 Medical Insurance 基本医疗保险,用于报销部分医疗费用
    起付线 Deductible 医保开始报销前需个人先行支付的费用额度
    封顶线 Annual Cap 医保年度报销的最高限额
    NFC Near Field Communication 近场通信技术,用于贴纸快速配对
    SOA Service-Oriented Architecture 服务导向架构
    MVP Minimum Viable Product 最小可行产品
    ADR Architecture Decision Record 架构决策记录
    KV Key-Value 键值对数据模型
    RDB Relational Database 关系型数据库
    AOT Ahead-Of-Time 提前编译
    NAPI Native API 原生接口,用于调用 C/C++ 库
    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 基于鸿蒙OS开发静脉输液智能监控系统(1)-项目总览与架构设计
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!