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

5.4 KiB
Raw Permalink Blame History

产品决策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

snfirmwareUpgrade 在被动断开 / 主动 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 释放 BLEmutableStateOf<DuooomiBleSDK> 赋新实例。Compose collectAsState(sdk) 自动订阅新 SDK 的 flow。

来源:复刻对话 2026-05-08。