Files
ring-health-docs/superpowers/specs/2026-09-10-ring-health-app-design.md
T
cuiluzhengandClaude Code b8d217e115 docs: 新增智能戒指健康 App 阶段一设计文档
- 仓库拆分:ring-health-app / ring-health-api(阶段一仅契约)
- 技术选型:Kotlin + Compose,工具链钉死本机已装版本
- 架构分层、页面地图、Mock 策略与里程碑 M0-M5
- 厂商 SDK 隔离复用策略(原样 vendor + 自写适配层)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-10 08:43:06 +08:00

13 KiB
Raw Blame History

智能戒指健康 App 设计文档(阶段一)

  • 日期:2026-09-10
  • 状态:待评审
  • 产品代号(占位):ring-health
  • 相关仓库:ring-health-app(Android App)、ring-health-api(后端 API)

1. 背景与目标

我们要为自有的智能戒指/手环做一款手机 App,用于后续与设备联调、并最终面向用户。 交互参照小米运动健康(Mi Fitness),但按我们戒指的真实能力裁剪。

阶段一目标(本文档范围):用原生 Android + Jetpack Compose 搭出完整可运行的 静态页面骨架,页面齐全、导航顺畅、真机可跑;所有数据来自本地 Mock。

不在阶段一范围:真实蓝牙连接、协议解析、本地数据库、账号登录、云端同步。 这些在 M2 及以后的里程碑中,通过预留的接口边界接入。

2. 现状:厂商 SDK 分析(已探明)

手上的 SmartRingF01_SDK 不是 .aar 库,而是厂商「鑫域隆 SmartRingF01/F02」的完整 Demo App。

项 现状
工程名 / 包名 SmartRingDCLT_SDK / com.sr.smartring
可复用核心 BluetoothLeClass(BLE 扫描/连接/GATT 收发,仅依赖 Android 框架,零第三方依赖)
协议收发 写在厂商 MainActivity 的静态方法:sendBleData / recvBleData / recvBleWriteData,帧格式 DC 0001 <cmd> <data> LT(下行)、DC 0002 …(上报)
辅助类 HEROUtils(OTA 下载 + CRC32)、Utils、BluetoothApplication、LeDeviceListAdapter
技术栈 Java + Android Support Library(非 AndroidX) + EventBus 3 + Gradle 6.7.1 / AGP 4.2.2(很旧)
协议文档 鑫域隆智能戒指协议.txt(GBK 编码,含全部指令码)

戒指能力(来自协议文档,决定我们能做哪些页面): 计步、心率、血氧、睡眠、血压、体温、压力、运动检测、查找戒指、电量、佩戴/充电状态、OTA 升级、自动监测开关配置、时间同步。

复用策略:SDK 内容不修改。M2 接 BLE 时,把 BluetoothLeClass 等纯通讯类原样拷贝进 App 的 vendor/ 包(保持 com.sr.smartring 包名与代码原样),我们只新写一层适配器去调用它。 UI 永远不直接依赖厂商代码。

3. 仓库与目录结构

遵循团队 docs/ + repos/ 惯例;仓库名 kebab-case,移动端 -app、后端 -api。

d:\cherry
├── SmartRingF01_SDK/        # 厂商原始工程(只读参考,不纳入任何 git 仓库)
├── docs/                    # 项目文档根(独立 git 仓库)
│   └── superpowers/specs/   # 设计/技术文档
└── repos/
    ├── ring-health-app/     # Android App(Kotlin + Compose)   ← 阶段一主战场
    └── ring-health-api/     # 后端 API                          ← 阶段一仅脚手架 + 接口契约
  • ring-health-app:阶段一全部实现在此。
  • ring-health-api:阶段一只放空骨架(README + gitignore)与 docs/api-contract.md 契约草稿, 不写业务接口。后端技术栈(Spring Boot / .NET)待 M4 真正做云同步时再定。

4. App 技术选型

项 选择 理由
语言 Kotlin Compose 唯一舒适选择
UI Jetpack Compose + Material 3 写卡片/图表类仿小米页面效率最高
导航 Navigation-Compose,单 Activity 多 Tab + 深层子页标准做法
架构 MVVM + StateFlow(UiState) 官方推荐,易测试
异步 Coroutines + Flow —
图表 Compose Canvas 手绘(今日大环、柱状/折线占位) 阶段一不引第三方图表库(YAGNI)
DI 手动组合根(Application 中构造仓库) 阶段一无需 Hilt,后续可平滑替换
构建 Gradle 8.14 + AGP 8.11.x + JDK 17 + libs.versions.toml 本机已缓存 Gradle 8.14,免联网下载
SDK minSdk 26 / targetSdk 35 / compileSdk 35,build-tools 35.0.0 对齐厂商 minSdk(26);compileSdk 35 与本机已装 platform/build-tools 一致

阶段一依赖清单(刻意精简):androidx.core:core-ktx、androidx.activity:activity-compose、 androidx.lifecycle:lifecycle-runtime-compose / lifecycle-viewmodel-compose、 androidx.navigation:navigation-compose、androidx.compose:compose-bom(ui / material3 / material-icons-extended / ui-tooling-preview)、kotlinx-coroutines。 不引 Hilt、Room、Retrofit、第三方图表库。

5. App 架构分层(单 :app 模块 + 严格分包)

com.placeholder.ringhealth
├── RingHealthApplication.kt      # 组合根:手动构造 Repository,注入 ViewModel
├── MainActivity.kt               # 唯一 Activity,承载 Compose NavHost
├── core/
│   ├── designsystem/             # Theme/Color/Type/Shape + 通用组件
│   │   └── component/            # HealthCard、RingProgress、MetricTile、SectionHeader…
│   ├── common/                   # 格式化与扩展(步数、时长、心率区间)
│   └── navigation/               # Route 常量与 NavGraph
├── domain/
│   ├── model/                    # 纯 Kotlin 数据模型(无 Android 依赖)
│   └── repository/               # 仓库接口(UI 只依赖这里)
├── data/
│   └── mock/                     # Mock 实现 + 造数器(阶段一)
│                                 # M2 将新增 data/sdk/ 作为厂商 SDK 适配层
├── feature/                      # 每个 Tab / 子页一个包:Screen + ViewModel + UiState
│   ├── home/  workout/  device/  profile/  detail/
└── vendor/                       # 预留:M2 原样存放厂商 com.sr.smartring 通讯类(不改一行)

核心边界原则:UI(feature/*)只依赖 domain/repository 的接口与 domain/model。 阶段一注入 Mock 实现;M2 新增 SDK 适配实现,接口不变则 UI 零改动。

5.1 关键接口(阶段一定稿)

// domain/repository
interface RingDeviceRepository {
    val deviceState: StateFlow<RingDeviceState>     // 连接态/电量/型号/固件版本
    suspend fun refreshDeviceState()
}

interface HealthDataRepository {
    fun observeTodaySummary(): Flow<HealthSummary>
    fun observeMetricHistory(metric: MetricType, range: DateRange): Flow<List<MetricPoint>>
    fun observeWorkouts(): Flow<List<WorkoutRecord>>
}

// domain/model(节选)
enum class MetricType { STEP, HEART_RATE, SLEEP, BLOOD_OXYGEN, STRESS, TEMPERATURE, BLOOD_PRESSURE }

data class RingDeviceState(
    val connected: Boolean, val name: String, val batteryPercent: Int,
    val firmwareVersion: String, val charging: Boolean,
)
data class HealthSummary(               // 今日概览
    val steps: Int, val stepGoal: Int, val calories: Int, val distanceKm: Double,
    val heartRate: Int?, val sleepMinutes: Int?, val bloodOxygen: Int?,
    val stress: Int?, val temperature: Double?, val bloodPressure: String?,  // 血压未开启时为 null
)
data class MetricPoint(val timestamp: Long, val value: Double)

约定:ViewModel 用 stateIn 把 Flow 转成 StateFlow<XxxUiState>;null 表示"该指标未开启/无数据", UI 统一走"未开启占位态",不显示 0。

6. 页面地图与路由

底部四 Tab(仿小米运动健康),并按戒指能力裁剪:

Tab 页面内容 路由
① 健康·首页 顶部问候 + 设备连接状态条;今日大环(步数/卡路里/距离);指标卡片网格:心率 / 睡眠 / 血氧 / 压力 / 体温(血压显示"未开启/需校准"占位) home
② 运动 "开始运动"入口(跑步/骑行/自由训练占位)+ 运动历史记录列表(静态) workout
③ 设备 戒指卡片(名称/电量/连接态/固件)+ 设置列表:健康自动监测开关(心率/血氧/血压/压力/睡眠,对应协议 0502 开关位)、查找戒指(050B)、智能提醒/闹钟占位、固件升级占位(OTA 17xx)、佩戴检测 device
④ 我的 头像/昵称(登录占位)、App 设置(单位/通知)、通知权限引导、关于/检查更新、帮助与反馈 profile

详情子页(全部静态 + Canvas 图表占位):

路由 内容
detail/step 今日步数详情:大环 + 7 天/月切换柱状图 + 说明文案
detail/heart_rate 心率详情:当日折线/区间统计(静息/运动)+ 历史区间
detail/sleep 睡眠详情:深睡/浅睡/清醒分段条 + 入睡/起床时间
detail/blood_oxygen 血氧详情:趋势折线 + 正常范围说明
device/settings 戒指功能开关详情(静态开关,M2 后写向设备)
workout/history 运动历史列表

明确不做(小米有但我们戒指不支持):跑步课程、商城、社区、多设备接入、体重秤/体脂秤。

7. 设计系统与视觉(仿小米运动健康)

  • 浅色为主,白底卡片 + 大圆角 + 轻阴影;大号数字(步数等)用等宽感强的字重。
  • 指标色板:步数=绿、心率=红/粉、睡眠=蓝/靛、血氧=青、压力=紫、体温=橙(在 designsystem/Color.kt 统一定义)。
  • 通用组件沉淀到 core/designsystem/component/:HealthCard、RingProgress(Canvas 环形进度)、 MetricTile(图标+数值+单位小卡)、SectionHeader、StatBar(睡眠分段条/柱状图)。
  • 深色模式:阶段一仅保证浅色可用,深色留到 M5。
  • 所有中文字符串进 strings.xml(默认中文,附英文),支持后续多语言;禁止硬编码文案。

8. Mock 数据策略

  • data/mock/MockDataProvider.kt 提供确定性演示数据(固定随机种子),保证每次运行截图一致、便于展示。
  • Mock 仓库模拟轻微异步延迟(~200ms),让加载态/占位骨架在阶段一就能跑通。
  • 开关类设置的状态用内存态(StateFlow)保存,重启重置;阶段一不引入持久化。

9. API 仓库(阶段一)

ring-health-api 阶段一只包含:

ring-health-api/
├── README.md            # 说明:后端栈待定,M4 落地
├── .gitignore
└── docs/api-contract.md # 接口契约草稿(登录、设备绑定、健康数据同步的字段雏形)

契约草稿仅为对齐未来前后端边界,不实现。后端技术栈(Spring Boot / .NET)在 M4 前单独定。

10. 里程碑与验收

阶段 内容 验收标准
M0 基建 建 docs/、repos/ 目录;初始化两个 git 仓库;gitignore/README 两仓库结构就绪,符合命名规范
M1 静态 App(阶段一) Compose 工程骨架、主题、四 Tab 导航、本文档第 6 节全部页面、Mock 仓库 真机/模拟器可跑;四 Tab 与全部子页可切换;无崩溃;assembleDebug 通过
M2 SDK 接入 vendor/ 原样放厂商通讯类;新增 data/sdk/ 适配层实现既有接口;BLE 权限、扫描、连接 真机扫描到戒指、连接成功、读到电量/固件
M3 数据打通 协议解析(帧 DC…LT)、Mock→真数据、ROOM 本地存储、真实图表 首页与详情显示真机数据
M4 云端/账号 定后端栈、实现登录与同步接口、App 接入 接口联调通过
M5 发布 签名、图标、深色模式、多语言、内测包 可分发 APK

本次实施范围 = M0 + M1。

11. 风险与决策记录

风险/决策 说明与对策
厂商 SDK 老旧(Support Library / EventBus)与 Compose 工程共存 BluetoothLeClass 零第三方依赖,风险低;M2 隔离在 vendor/ 包内,仅当必需才考虑 jetifier
占位命名需全局替换 替换清单见 §12;集中一处记录,替换时按清单执行
BLE 权限(Android 12+) M2 统一处理 BLUETOOTH_SCAN/CONNECT + 位置权限与引导 UI,阶段一不涉及
手绘 Canvas 图表工作量 阶段一只做环形 + 简单柱/折线,避免过度实现;复杂图表留 M3
后端栈未定 有意延迟到 M4,避免两仓库空转
本机构建环境版本 已探明:JDK 17、Android SDK(platform 34/35/36)、build-tools 仅 35.0.0 / 36.1.0、Gradle 缓存 8.12/8.14、无全局 gradle。故工具链钉死在已安装版本,避免构建时联网拉取缺失组件

已确认决策:① 前后端分离,两个仓库;② Jetpack Compose;③ 阶段一单 :app 分层模块; ④ 四个 Tab 与全部详情子页都做;⑤ 后端阶段一只留契约;⑥ 占位命名后续统一替换。

12. 占位命名与替换清单

待正式产品名/域名确定后,需统一替换:

位置 占位值
仓库名 ring-health-app、ring-health-api(含 git 服务器上名称)
applicationId / namespace com.placeholder.ringhealth
包路径 com/placeholder/ringhealth 目录树
app_name(strings.xml) RingHealth(占位)
文档与 README 中的产品代号 ring-health

替换为一次性全局操作,不涉及逻辑改动;执行时按上表逐项核对。