- 新增 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 标记完成
84 lines
3.7 KiB
Markdown
84 lines
3.7 KiB
Markdown
---
|
||
phase: 03-device-system
|
||
plan: "02"
|
||
subsystem: device
|
||
tags: [service, gateway, sync-info, store, bootstrap]
|
||
dependency_graph:
|
||
requires:
|
||
- Plan 03-01(tb_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 分支接入 SyncDeviceInfo(nil guard + Warn 日志)
|
||
- asset.Service 注入 gatewayClient
|
||
affects:
|
||
- DEVICE-04 需求(设备 Refresh + 详情接入 sync-info)
|
||
tech_stack:
|
||
added: []
|
||
patterns:
|
||
- GORM Transaction 两步原子更新模式
|
||
- Service nil guard 可选依赖注入模式
|
||
- ISO 8601 字符串 → time.Time(time.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
|