Files
junhong_cmp_fiber/.planning/phases/03.1-sync-info-phase-3-dto/03.1-CONTEXT.md

171 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 03.1: 设备 sync-info 读取链路修复 - Context
**Gathered:** 2026-03-28
**Status:** Ready for planning
<domain>
## Phase Boundary
Phase 3 完成了设备数据的**写入侧**sync-info 调用 + DB 字段写入 + is_current 更新),但遗漏了**读取侧**的 DTO 映射和设备实时数据的架构对齐。
本 Phase 修复两类缺口:
1. **静态 DTO 映射缺口**DB 字段已存储但未通过 API 返回)
- `toDeviceResponse` 5 个字段未映射 → 影响设备管理列表/详情
- `ListBindings` IsCurrent 未映射 → 影响设备绑卡列表接口
- `BoundCardInfo` IsCurrent 未映射2 处)→ 影响资产 Resolve/Refresh 响应
- `AssetResolveResponse` 缺 5 个 DB 缓存字段 → 影响 admin 资产解析接口
2. **设备实时数据架构对齐**(统一视图的上游属性必须实时查询)
- 设备上游属性(在线状态/信号/电量/WiFi/当前卡)具有实时特性,存 DB 无意义,必须实时从 Gateway 拉取
- `GetRealtimeStatus` 对 device 类型实时调用 Gateway sync-info
- C 端 `GetAsset``DeviceRealtimeInfo` stub 替换为真实 Gateway 数据
**不在本 Phase 范围:**
- 卡资产的上游属性(走轮询系统,已由 DB 存储,无需改动)
- 设备管理列表(展示 DB 缓存字段即可,不需要实时 Gateway 调用)
- 设备 Refresh 流程(已有 updateDeviceFromSyncInfo保持现状
</domain>
<decisions>
## Implementation Decisions
### 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-07GetRealtimeStatus 实时 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 端**:新建 `DeviceGatewayInfo` struct位于 `internal/model/dto/asset_dto.go`
- **C 端**:保留现有 `DeviceRealtimeInfo` struct位于 `internal/model/dto/client_asset_dto.go`
- `AssetRealtimeStatusResponse` 新增 `DeviceRealtime *DeviceGatewayInfo` 字段
- **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
- `DeviceGatewayInfo` B 端结构体的字段是否包含所有 Gateway 字段或只包含关键字段agent 可参考 `DeviceRealtimeInfo`C 端)的完整字段集合决定
- `toDeviceResponse``GetRealtimeStatus` 调用的冷却 Key 策略(是否需要防止高频调用)由 agent 参考现有 Refresh 冷却 Key 逻辑自行决定
</decisions>
<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/15updateDeviceFromSyncInfo 实现细节D-08/09存储字段范围
### 核心修改文件
- `internal/service/device/service.go``toDeviceResponse()`:补全 5 个字段
- `internal/service/device/binding.go``ListBindings()`:补全 IsCurrent
- `internal/service/asset/service.go``GetRealtimeStatus()`:新增 Gateway 调用Resolve/Refresh 两处 BoundCardInfo补全 IsCurrent
- `internal/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 字段均需 `description` tag参考 `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>
<specifics>
## Specific Ideas
- 用户明确:设备上游属性(在线/信号/电量/WiFi/当前卡)**不应存 DB**,必须实时查询。现有的 `online_status` 等 DB 字段对于设备管理列表展示仍有价值(缓存上次同步值)。
- 用户明确:关键实时字段优先级 — 在线状态 > 信号强度 > 连接数 > 电量 > WiFi 名称 + WiFi 密码 > 当前使用卡 ICCID
- `DeviceGatewayInfo`B 端)与 `DeviceRealtimeInfo`C 端结构独立为未来管理端展示更多技术字段IMSI、LAN/WAN IP、DL/UL stats 等)预留空间
</specifics>
<deferred>
## Deferred Ideas
- 设备 Refresh 接口的语义重新评估(用户提到"刷新接口对设备而言意义不大")——当前 Refresh 仍保留(写回 DB 供管理列表使用),但未来可能弃用 —— Phase 4+ 评估
- `GetRealtimeStatus` 的防高频调用冷却 Key若 C 端每次进入详情页都触发 Gateway 调用,需评估频率)—— 留待 Phase 4 运营优化
</deferred>
---
*Phase: 03.1-sync-info-phase-3-dto*
*Context gathered: 2026-03-28 (updated: 2026-03-28, added D-12)*