Files
junhong_cmp_fiber/openspec/changes/archive/2026-04-09-add-asset-realname-time/design.md
huang 6dfc5c0301
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m5s
feat: 资产解析接口新增实名时间字段
- AssetResolveResponse 新增 real_name_at 字段:card 类型取 first_realname_at,device 类型取绑定卡中最小的实名时间
- BoundCardInfo 新增 real_name_at 字段:各卡自己的 first_realname_at
- RefreshCardDataFromGateway 手动刷新路径补写实名时间:检测到 0→1 变化时同步写入 first_realname_at
- 同步更新 asset-resolve 规格文档

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 12:12:28 +08:00

2.5 KiB
Raw Blame History

Context

IotCard 表已有 first_realname_at 字段记录卡每次从未实名0变为已实名1的时间戳轮询路径在每次 0→1 时覆盖写入,字段名为"首次"但实际是"最近一次")。该字段目前仅用于内部业务触发(首次实名激活任务),未对外暴露。

当前问题

  1. AssetResolveResponseBoundCardInfo DTO 没有 real_name_at 字段
  2. 手动刷新路径(RefreshCardDataFromGateway)在更新 real_name_status 时漏写 first_realname_at
  3. Device model 无实名时间,设备视角需从绑定卡聚合

Goals / Non-Goals

Goals:

  • GET /api/admin/assets/resolve/:identifier 响应中暴露实名时间
  • 卡视角直接返回 IotCard.first_realname_at
  • 设备视角返回当前绑定卡 first_realname_at 的最小值
  • BoundCardInfo 列表中每张卡返回各自的实名时间
  • 修复手动刷新路径漏写 first_realname_at 的 bug

Non-Goals:

  • 不新增数据库字段(first_realname_at 已存在)
  • 不修改实名检查轮询逻辑
  • 不对设备 model 新增字段(运行时聚合,不持久化)
  • 不改变 triggerFirstRealnameActivation 的触发条件

Decisions

决策1复用 first_realname_at 而非新增字段

first_realname_at 在每次 0→1 时覆盖写入,语义上等同于"最近一次实名时间"。新增字段需要迁移,收益不成比例。

替代方案:新增 realname_at 字段,明确"最近一次"语义。 放弃原因:需要数据库迁移,且历史卡数据无法回填,反而不如 first_realname_at 完整。

决策2设备实名时间运行时聚合不持久化

设备无实名时间字段,从绑定卡内存聚合(buildDeviceResolveResponse 已查询全部绑定卡),只需遍历取 MIN(first_realname_at) 即可,无需额外查询。

决策3手动刷新路径与轮询路径对齐

RefreshCardDataFromGatewayreal_name_status 由非已实名变为已实名时,同步写入 first_realname_at,逻辑与 polling_realname_handler.goisFirstRealname 条件完全一致。

Risks / Trade-offs

  • 字段名语义不对齐first_realname_at 叫"首次"但实为"最近一次"。→ 通过 DTO 字段命名为 real_name_at 对外屏蔽内部字段名API 消费者不感知。
  • 设备实名时间仅限当前绑定卡:历史上曾绑定过的卡不计入。→ 与业务定义一致(设备换卡后旧卡脱离关系)。