10 KiB
Phase 03.1: 设备 sync-info 读取链路修复 - Context
Gathered: 2026-03-28 Status: Ready for planning
## Phase BoundaryPhase 3 完成了设备数据的写入侧(sync-info 调用 + DB 字段写入 + is_current 更新),但遗漏了读取侧的 DTO 映射和设备实时数据的架构对齐。
本 Phase 修复两类缺口:
-
静态 DTO 映射缺口(DB 字段已存储但未通过 API 返回)
toDeviceResponse5 个字段未映射 → 影响设备管理列表/详情ListBindingsIsCurrent 未映射 → 影响设备绑卡列表接口BoundCardInfoIsCurrent 未映射(2 处)→ 影响资产 Resolve/Refresh 响应AssetResolveResponse缺 5 个 DB 缓存字段 → 影响 admin 资产解析接口
-
设备实时数据架构对齐(统一视图的上游属性必须实时查询)
- 设备上游属性(在线状态/信号/电量/WiFi/当前卡)具有实时特性,存 DB 无意义,必须实时从 Gateway 拉取
GetRealtimeStatus对 device 类型实时调用 Gateway sync-info- C 端
GetAsset的DeviceRealtimeInfostub 替换为真实 Gateway 数据
不在本 Phase 范围:
- 卡资产的上游属性(走轮询系统,已由 DB 存储,无需改动)
- 设备管理列表(展示 DB 缓存字段即可,不需要实时 Gateway 调用)
- 设备 Refresh 流程(已有 updateDeviceFromSyncInfo,保持现状)
Plan 分组
- D-01: 全部修复合并为 1 个 Plan。所有变更同属"DTO 映射缺口 + 实时数据架构对齐",无依赖关系需拆分。
静态 DTO 缺口修复
-
D-02:
device/service.go:toDeviceResponse()补全 5 个 DB 缓存字段:OnlineStatus、LastOnlineTime、SoftwareVersion、SwitchMode、LastGatewaySyncAt- 这些字段对设备管理列表(List)仍有意义(展示 DB 缓存值),保留此映射
-
D-03:
device/binding.go:ListBindings()补全IsCurrent字段映射DeviceCardBindingResponse.IsCurrent直接从binding.IsCurrent取值
-
D-04:
asset/service.go中 2 处BoundCardInfo构建(Resolve path + Refresh path)补全IsCurrent- 实现方式:在
bindings遍历中同时构建isCurrentMap[b.IotCardID] = b.IsCurrent(与slotMap同构) - 构建
BoundCardInfo时追加IsCurrent: isCurrentMap[c.ID]
- 实现方式:在
-
D-12:
asset_dto.go:AssetResolveResponse新增 5 个 DB 缓存字段,同步更新asset/service.go:buildDeviceResolveResponse()映射- 新增字段:
OnlineStatus int、LastOnlineTime *time.Time、SoftwareVersion string、SwitchMode string、LastGatewaySyncAt *time.Time - 这些是 DB 存储值(Phase 3 写入,上次 sync-info 的快照),不触发 Gateway 调用
- 适用场景:admin 资产 Resolve 接口(
/api/admin/assets/resolve/:identifier)快速展示设备上次已知状态 - 与 D-07(GetRealtimeStatus 实时 Gateway 调用)互补:Resolve = 快速快照,Realtime = 精确实时
- 新增字段:
设备实时数据架构原则
-
D-05: 设备上游属性(在线/信号/电量/WiFi/当前卡)不缓存,必须实时查询 Gateway。原因:设备具有在线/离线实时特性,存 DB 的值在设备离线后立即过时,无法代表真实状态。
- 对比:卡资产的上游属性通过轮询系统有规律地同步到 DB,查询 DB 有意义。
-
D-06: Gateway 调用失败时(设备离线/超时/IMEI 为空)返回部分数据:
DeviceRealtime字段为 null,资产基础信息正常返回。不抛错误,与卡资产处理保持一致。
GetRealtimeStatus 改造
- D-07:
asset/service.go:GetRealtimeStatus()对 device 类型新增 Gateway 实时调用:- 调用
s.gatewayClient.SyncDeviceInfo(ctx, &gateway.SyncDeviceInfoReq{IMEI: device.IMEI})(gatewayClient 已注入) - 成功:将响应映射到新增的
AssetRealtimeStatusResponse.DeviceRealtime *DeviceGatewayInfo字段 - 失败:仅记录 Warn 日志,
DeviceRealtime返回 null
- 调用
DTO 结构
-
D-08: B 端和 C 端使用独立结构体,不共用。原因:未来管理端可能展示 C 端不展示的字段(如 IMSI、DL/UL stats、LAN/WAN IP 等技术字段)。
- B 端:新建
DeviceGatewayInfostruct,位于internal/model/dto/asset_dto.go - C 端:保留现有
DeviceRealtimeInfostruct,位于internal/model/dto/client_asset_dto.go AssetRealtimeStatusResponse新增DeviceRealtime *DeviceGatewayInfo字段
- B 端:新建
-
D-09:
DeviceGatewayInfo(B 端)至少包含以下字段(参考 Gateway sync-info 文档完整映射):- 关键:
OnlineStatus、BatteryLevel、Rsrp/Rsrq/Rssi/Sinr(信号)、MaxClients(连接数) - WiFi:
SSID、WifiPassword - 当前卡:
CurrentIccid - 附加:
SoftwareVersion、SwitchMode、RunTime、DailyUsage、LastOnlineTime
- 关键:
-
D-10:
gateway.SyncDeviceInfoResp当前缺少字段,需补全:- 补充:
Rsrp int、Rsrq int、Sinr int、WifiPassword string、IPAddress string - 补充:
WANIP string、LANIP string、MaxClients int、ConnectTime string - 补充:
Status int(设备状态)、IMSI string、ULStats string、DLStats string - 补充:
LimitSpeed int、SyncInterval int、MACAddress string
- 补充:
C 端 DeviceRealtime stub 替换
- D-11: C 端
client_asset.go:GetAsset()中对 device 类型的处理:- 删除
buildMockDeviceRealtime()调用 - 改为调用
h.assetService.GetRealtimeStatus("device", deviceID)获取实时数据 - 将返回的
AssetRealtimeStatusResponse.DeviceRealtime (*DeviceGatewayInfo)映射到ClientAssetResponse.DeviceRealtime (*DeviceRealtimeInfo) - Gateway 失败时
resp.DeviceRealtime = nil(已处理,不额外报错)
- 删除
Agent's Discretion
DeviceGatewayInfoB 端结构体的字段是否包含所有 Gateway 字段或只包含关键字段,agent 可参考DeviceRealtimeInfo(C 端)的完整字段集合决定toDeviceResponse中GetRealtimeStatus调用的冷却 Key 策略(是否需要防止高频调用)由 agent 参考现有 Refresh 冷却 Key 逻辑自行决定
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Gateway 接口文档
docs/第三方文档/gateway设备详情同步接口.md— sync-info 完整字段说明(data 字段映射表,包含 rsrp/rsrq/sinr/wifi_password 等当前 SyncDeviceInfoResp 缺失的字段)
Phase 3 上下文(继承决策)
.planning/phases/03-device-system/03-CONTEXT.md— Phase 3 所有决策(D-06:结构体位置;D-13/14/15:updateDeviceFromSyncInfo 实现细节;D-08/09:存储字段范围)
核心修改文件
internal/service/device/service.go—toDeviceResponse():补全 5 个字段internal/service/device/binding.go—ListBindings():补全 IsCurrentinternal/service/asset/service.go—GetRealtimeStatus():新增 Gateway 调用;Resolve/Refresh 两处 BoundCardInfo:补全 IsCurrentinternal/model/dto/asset_dto.go— 新增DeviceGatewayInfo;AssetRealtimeStatusResponse新增DeviceRealtime字段;AssetResolveResponse新增 5 个 DB 缓存字段(D-12)internal/model/dto/device_dto.go— 已有字段,无需新增(确认映射即可)internal/gateway/models.go—SyncDeviceInfoResp补全缺失字段internal/handler/app/client_asset.go— 删除buildMockDeviceRealtime()stub,改为调用服务 + 映射
当前 SyncDeviceInfoResp 位置
internal/gateway/models.go:147— 当前结构(缺 Rsrp/Rsrq/Sinr/WifiPassword/LANIP/MaxClients 等)
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
internal/service/asset/service.go:gatewayClient— 已注入,GetRealtimeStatus可直接使用internal/service/asset/service.go:updateDeviceFromSyncInfo()— Phase 3 已实现,展示了 SyncDeviceInfoResp → DB 的映射模式,可参考作为 Gateway 响应 → DTO 映射的参考internal/gateway/device.go:SyncDeviceInfo()— 已实现(Phase 3),直接复用internal/model/dto/client_asset_dto.go:DeviceRealtimeInfo— 完整的 C 端实时信息结构(46 字段),对应所有 Gateway 字段,作为DeviceGatewayInfo的参考
Established Patterns
- isCurrentMap 模式:参考
slotMap在asset/service.go的构建方式,isCurrentMap[b.IotCardID] = b.IsCurrent - Gateway 失败不阻断主流程:
logger.Warn("..."),继续返回部分数据 - B 端 DTO 字段均需
descriptiontag(参考asset_dto.go现有字段)
Integration Points
asset/service.go:GetRealtimeStatus()— device case 追加 Gateway 调用入口(现只有getDeviceProtectStatus和 bindings 查询)handler/app/client_asset.go:GetAsset()— 约第 187-191 行,device 类型的resp.DeviceRealtime = buildMockDeviceRealtime()替换入口internal/model/dto/asset_dto.go:AssetRealtimeStatusResponse— 新增DeviceRealtime *DeviceGatewayInfo字段位置
</code_context>
## Specific Ideas- 用户明确:设备上游属性(在线/信号/电量/WiFi/当前卡)不应存 DB,必须实时查询。现有的
online_status等 DB 字段对于设备管理列表展示仍有价值(缓存上次同步值)。 - 用户明确:关键实时字段优先级 — 在线状态 > 信号强度 > 连接数 > 电量 > WiFi 名称 + WiFi 密码 > 当前使用卡 ICCID
DeviceGatewayInfo(B 端)与DeviceRealtimeInfo(C 端)结构独立,为未来管理端展示更多技术字段(IMSI、LAN/WAN IP、DL/UL stats 等)预留空间
- 设备 Refresh 接口的语义重新评估(用户提到"刷新接口对设备而言意义不大")——当前 Refresh 仍保留(写回 DB 供管理列表使用),但未来可能弃用 —— Phase 4+ 评估
GetRealtimeStatus的防高频调用冷却 Key(若 C 端每次进入详情页都触发 Gateway 调用,需评估频率)—— 留待 Phase 4 运营优化
Phase: 03.1-sync-info-phase-3-dto Context gathered: 2026-03-28 (updated: 2026-03-28, added D-12)