docs(03-02): 完成设备体系 sync-info 接入计划(DEVICE-04)

- 新增 03-02-SUMMARY.md,记录 updateDeviceFromSyncInfo 实现细节
- STATE.md:Phase 3 完成标记,追加 DEVICE-04 决策,Next Action 更新为 Phase 4
- ROADMAP.md:Phase 3 进度更新为 Complete(2/2 plans)
- REQUIREMENTS.md:DEVICE-04 标记完成
This commit is contained in:
2026-03-28 11:40:04 +08:00
parent 15dbf8dc66
commit 129a38f942
4 changed files with 98 additions and 12 deletions

View File

@@ -0,0 +1,83 @@
---
phase: 03-device-system
plan: "02"
subsystem: device
tags: [service, gateway, sync-info, store, bootstrap]
dependency_graph:
requires:
- Plan 03-01tb_device 5字段 + tb_device_sim_binding.is_current + Gateway SyncDeviceInfo 方法)
provides:
- DeviceSimBindingStore.UpdateIsCurrentByDeviceID事务原子更新 is_current
- asset.Service.updateDeviceFromSyncInfo私有函数同步 5 个设备字段 + is_current
- asset.Service.Refresh device 分支接入 SyncDeviceInfonil guard + Warn 日志)
- asset.Service 注入 gatewayClient
affects:
- DEVICE-04 需求(设备 Refresh + 详情接入 sync-info
tech_stack:
added: []
patterns:
- GORM Transaction 两步原子更新模式
- Service nil guard 可选依赖注入模式
- ISO 8601 字符串 → time.Timetime.RFC3339
key_files:
created: []
modified:
- internal/store/postgres/device_sim_binding_store.go
- internal/service/asset/service.go
- internal/bootstrap/services.go
decisions:
- "gatewayClient 使用 nil guard而非断言非 nil允许 bootstrap 传入 nil 依然安全运行"
- "sync-info 失败只记 Warn 日志,不阻断 Refresh 主流程非关键步骤D-15 决策)"
- "UpdateIsCurrentByDeviceID 事务两步:先全清 false再按 ICCID 设 true原子保证唯一 is_current"
metrics:
duration: "10min"
completed_date: "2026-03-28"
tasks: 2
files: 3
---
# Phase 03 Plan 02: Gateway sync-info 接入 Refresh 流程 Summary
**一句话总结:** 在设备 Refresh 流程末尾接入 Gateway SyncDeviceInfo通过 updateDeviceFromSyncInfo 原子更新在线状态、固件版本等 5 个设备字段及 is_current 当前卡标识DEVICE-04
## Tasks Completed
| Task | Name | Commit | Files |
|------|------|--------|-------|
| 1 | DeviceSimBindingStore 新增 UpdateIsCurrentByDeviceID 方法 | `ba1886c` | device_sim_binding_store.go |
| 2 | Asset Service 注入 gatewayClient + 实现 updateDeviceFromSyncInfo + Refresh 接入 sync-info | `15dbf8d` | service.go, services.go |
## Artifacts Delivered
### Store 层
- **UpdateIsCurrentByDeviceID**:事务两步原子更新。第一步将设备所有活跃绑定的 `is_current` 清为 false第二步通过子查询定位 `current_iccid` 对应的 `iot_card_id`,将该绑定设为 `is_current = true``currentIccid` 为空时仅清空。
### Service 层
- **Service 结构体**:新增 `gatewayClient *gateway.Client` 字段(允许 nil
- **New() 构造函数**:新增 `gatewayClient *gateway.Client` 参数
- **Refresh device 分支**:在设置冷却 Key 之后、`GetRealtimeStatus` 返回之前,增加 sync-info 调用逻辑nil guard 保护,失败只记 Warn 日志)
- **updateDeviceFromSyncInfo 私有函数**
1. 解析 `last_online_time`ISO 8601 → `*time.Time``time.RFC3339`
2. GORM Updates 更新 5 个字段:`online_status``software_version``switch_mode``last_gateway_sync_at``last_online_time`(非空时)
3. 调用 `UpdateIsCurrentByDeviceID` 原子更新 `is_current`
### Bootstrap 层
- **services.go**`asset.New()` 调用末尾追加 `deps.GatewayClient` 参数
## Deviations from Plan
None — 计划完全按预期执行。
## Verification
-`go build ./...` 零新增编译错误
-`updateDeviceFromSyncInfo` 方法存在于 `internal/service/asset/service.go`
-`Refresh` device 分支含 `gatewayClient.SyncDeviceInfo()` 调用nil guard + Warn 日志)
-`UpdateIsCurrentByDeviceID` 存在于 `internal/store/postgres/device_sim_binding_store.go`
-`internal/bootstrap/services.go` asset.New() 传入了 `deps.GatewayClient`
- ✅ sync-info 失败时只记录 Warn不影响 Refresh 主流程
## Self-Check: PASSED