From b8d217e11595149093ff3e379803bf5506be8ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B4=94=E9=B2=81=E6=AD=A3?= <13780741225@163.com> Date: Thu, 10 Sep 2026 08:43:06 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E6=88=92=E6=8C=87=E5=81=A5=E5=BA=B7=20App=20=E9=98=B6=E6=AE=B5?= =?UTF-8?q?=E4=B8=80=E8=AE=BE=E8=AE=A1=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 仓库拆分:ring-health-app / ring-health-api(阶段一仅契约) - 技术选型:Kotlin + Compose,工具链钉死本机已装版本 - 架构分层、页面地图、Mock 策略与里程碑 M0-M5 - 厂商 SDK 隔离复用策略(原样 vendor + 自写适配层) Co-Authored-By: Claude Code --- .../2026-09-10-ring-health-app-design.md | 228 ++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 superpowers/specs/2026-09-10-ring-health-app-design.md diff --git a/superpowers/specs/2026-09-10-ring-health-app-design.md b/superpowers/specs/2026-09-10-ring-health-app-design.md new file mode 100644 index 0000000..1fa78af --- /dev/null +++ b/superpowers/specs/2026-09-10-ring-health-app-design.md @@ -0,0 +1,228 @@ +# 智能戒指健康 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 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 关键接口(阶段一定稿) + +```kotlin +// domain/repository +interface RingDeviceRepository { + val deviceState: StateFlow // 连接态/电量/型号/固件版本 + suspend fun refreshDeviceState() +} + +interface HealthDataRepository { + fun observeTodaySummary(): Flow + fun observeMetricHistory(metric: MetricType, range: DateRange): Flow> + fun observeWorkouts(): Flow> +} + +// 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`;`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` | + +替换为一次性全局操作,不涉及逻辑改动;执行时按上表逐项核对。