Files
duooomi-android-sdk/.wiki/product-decisions.md
km2023 8d83588e0b feat(sdk): 绑定时上报设备 log 到 update-metadata 并内聚双层绑定
1:1 复刻 iOS commit 4bc8fdb。

- bind(userId) 内聚为硬绑 + getVersion + 软绑 + updateMetadata 一次原子调用,
  软绑失败自动硬解绑回滚 + 清状态;集成方无需感知双层绑定
- fetchLatestFirmware 简化为对账上报 + checkUpgrade 两步,不再重复软绑
- VersionInfo 增加 log: JSONObject?,fromJson 用 optJSONObject 解析;
  SDK 暴露只读 versionLog: StateFlow<JSONObject?>,断连/解绑/软绑回滚时清空
- ShipmentSnDeviceService 新增 updateMetadata(sn, data: JSONObject),
  HttpHelper.post 已支持 JSONObject 作为 body 子节点透传
- 老固件不返回 log → versionLog == null → 自动跳过 updateMetadata,前向兼容
- 同步更新 .wiki/tech-architecture.md(状态清理表 + OTA 编排图)
  + product-decisions.md(新增决策条目)+ README.md(versionLog 状态)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 16:38:03 +08:00

102 lines
5.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 产品决策duooomi-android-sdk
## owner / scanNamePrefix 设为必填(破坏性)
**决策时间**2026-05-08commit `2dde274`
`DuooomiBleConfig` 删除 `firmwareIdentifier` / `firmwareStatus`,改 `owner` / `scanNamePrefix` 为必填。
**原因**
- iOS HEAD 已经这么做1:1 复刻要求一致
- 多租户 `x-owner` header 是后端鉴权前提,给默认值反而隐患(默认填到错的租户)
- `scanNamePrefix` 给默认值会扫到非预期设备
**影响**:集成方升级时构造函数签名报错,必须显式传两个参数。可接受,因为 demo / iOS / Harmony 都同步改了。
## bind 改瘦 + fetchLatestFirmware 串三步(已废弃,见下条)
**决策时间**2026-05-08commit `2dde274`
旧设计:`bind(userId)` 一键搞定 BLE 硬绑 + 软绑 + 拉最新固件 + 对账上报。
新设计:`bind` 只做硬绑 + getVersion软绑/对账/拉固件搬到 `fetchLatestFirmware` 内部串。
**原因**
- 与 expo `bindDeviceWithOrchestration` 行为对齐,便于跨平台一致排查
- bind 速度更快(不卡 HTTP 软绑),用户感知更自然
- fetchLatestFirmware 失败可重试,但 bind 失败重试要回滚 BLE 状态——分开后职责清晰
**集成方迁移指南**bind 之后必须显式调 `fetchLatestFirmware(sn, currentVersion, userId)` 才能拿到固件信息和触发对账。
## bind 内聚双层绑定 + updateMetadata 上报
**决策时间**2026-05-18同步 iOS commit `4bc8fdb` 复刻)
把软绑 + updateMetadata 从 `fetchLatestFirmware` 重新搬回 `bind` 内部,集成方一次调用完成"完整绑定"。
| 阶段 | 上一版2026-05-08 | 本版2026-05-18 |
|---|---|---|
| `bind(userId)` | BLE 硬绑 + getVersion | BLE 硬绑 + getVersion + **软绑** + **updateMetadata** + 失败回滚 |
| `fetchLatestFirmware(sn, currentVersion, userId)` | 对账 + 软绑 + checkUpgrade | 对账 + checkUpgrade不再软绑 |
**原因**
- 软绑是"绑定"语义的一部分,集成方不该感知双层之分(与 iOS HEAD 对齐)
- updateMetadata 在 bind 时 versionLog 刚新鲜,语义上"绑定时一并上报状态"更连贯
- 上一版"bind 改瘦"是过度拆分;事实证明拆开反而让集成方容易遗漏软绑
**集成方迁移指南**bind 调用方式不变,但**不再需要**调 `fetchLatestFirmware` 才完成软绑。检查更新仍调 `fetchLatestFirmware`,但它只做对账+checkUpgrade。
**新增 SDK 状态**`versionLog: StateFlow<JSONObject?>` —— 设备 BLE getVersion 响应中 `log` 字段(任意 JSON 对象,老固件不返回则 null。bind 时 fire-and-forget 透传到 `POST /api/auth/loomart/shipment-sn/device/update-metadata`。**前向兼容**:老固件 versionLog == null → 自动跳过 updateMetadata。
**JSON object 选型**:直接用 `org.json.JSONObject?` 透传(不解析、不转 MapHttpHelper.post 已支持 `JSONObject` 作为 body 子节点序列化。对应 iOS 的 `[String: Any]?` 方案。
## disconnect 时不清 sn / firmwareUpgrade
**决策时间**2026-05-08commit `0dbfab9`
`sn``firmwareUpgrade` 在被动断开 / 主动 disconnect 时**不清空**,与 iOS HEAD 严格对齐。
**原因**
- `sn` 是设备身份。断线重连同设备时仍有效,避免业务方每次都要重新 bind 拿 sn
- `firmwareUpgrade` 是 UI 展示数据,断线时 UI 突然空白体验差
- 想清空 → unbind 即可unbind 会清 sn
**潜在风险**:如果集成方在 disconnect 后**换了一台设备 connect**,但没 unbind 也没 bind 直接调 fetchLatestFirmware会用错 sn。判定靠集成方约束SDK 不强行守卫(与 iOS 一致)。
## UpgradeRecord 30 天 TTL + 对账 4 状态
**决策时间**:跟随 expo 设计2026-05-08 复刻)
每条 UpgradeRecord 持久化于 SharedPreferences状态机
| reported | outcome | 含义 | 触发 |
|---|---|---|---|
| false | null | 待上报 / HTTP 失败重试中 | 默认 / report 网络错误 |
| true | SUCCESS | 升级正确 | 设备实际版本 == 目标版本report-success 成功 |
| true | VERSION_MISMATCH | 升级失败 | 设备实际版本 ≠ 目标版本report-failure 成功 |
| true | null | 30 天 TTL 过期未上报 | 触发 OTA 后 30 天内仍未上报,标记审计轨迹 |
**原因**
- 30 天 TTL防止持久化数据无限膨胀30 天后用户感知低,可放弃
- 状态保留:对账成功后**不删 record**,便于 UI 展示历史
- `outcome=null` 与 "reported=false" 区分意图(一个是过期放弃,一个是网络在等重试)
## scanNamePrefix 由集成方约定(不再硬编码 "Duooomi-"
**决策时间**:跟随 iOS2026-05-08
旧版扫描自动按 `Duooomi-` 前缀过滤。新版作为必填 Config 字段。
**原因**:同一 SDK 代码可服务多个 brandduomi / xmly 等广播名前缀不同硬编码只服务一家。Demo 默认填 `Duooomi-`(可改)。
## Demo 内运行时编辑 Config + Reload SDK
**决策时间**2026-05-08commit `417012c`
Demo 顶部 Config 区可编辑 owner / apiKey / scanPrefixSharedPreferences 持久化,点击 Reload SDK 重建实例。
**原因**:调试时切换租户 / 切换 apiKey 不用重装 app。iOS demo 已这么做。
**实现细节**:旧 SDK fire-and-forget disconnect 释放 BLE`mutableStateOf<DuooomiBleSDK>` 赋新实例。Compose `collectAsState(sdk)` 自动订阅新 SDK 的 flow。
来源:复刻对话 2026-05-08。