feat(03.1-01): 建立设备实时 Gateway 调用链路,替换 mock 数据
- GetRealtimeStatus() device case 新增 Gateway sync-info 实时调用(IMEI/SN 优先,gatewayClient nil 安全) - 新增 mapSyncRespToDeviceGatewayInfo() 将 SyncDeviceInfoResp 映射为 B 端 DeviceGatewayInfo DTO - 新增 strPtr() 辅助函数(空字符串→nil,防止 JSON 出现大量空字符串字段) - client_asset.go 删除 buildMockDeviceRealtime(),改为调用 assetService.GetRealtimeStatus() 获取真实数据 - 新增 mapDeviceGatewayInfoToClientInfo() 将 B 端 DeviceGatewayInfo 映射为 C 端 DeviceRealtimeInfo - Gateway 调用失败时 DeviceRealtime 返回 null,不阻断主流程
This commit is contained in:
@@ -185,9 +185,15 @@ func (h *ClientAssetHandler) GetAssetInfo(c *fiber.Ctx) error {
|
|||||||
BoundDeviceName: resolved.Asset.BoundDeviceName,
|
BoundDeviceName: resolved.Asset.BoundDeviceName,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Gateway 同步接口对接后,替换为真实设备实时数据
|
// 调用服务层获取设备实时 Gateway 数据(per D-11)
|
||||||
|
// Gateway 失败时 DeviceRealtime 为 null,不阻断主流程(per D-06)
|
||||||
if resp.AssetType == "device" {
|
if resp.AssetType == "device" {
|
||||||
resp.DeviceRealtime = buildMockDeviceRealtime()
|
realtimeResp, realtimeErr := h.assetService.GetRealtimeStatus(
|
||||||
|
c.UserContext(), "device", resp.AssetID,
|
||||||
|
)
|
||||||
|
if realtimeErr == nil && realtimeResp.DeviceRealtime != nil {
|
||||||
|
resp.DeviceRealtime = mapDeviceGatewayInfoToClientInfo(realtimeResp.DeviceRealtime)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, resp)
|
return response.Success(c, resp)
|
||||||
@@ -592,44 +598,76 @@ func packageStatusName(status int) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildMockDeviceRealtime 构建设备实时状态假数据
|
// mapDeviceGatewayInfoToClientInfo 将 B 端 DeviceGatewayInfo 映射为 C 端 DeviceRealtimeInfo
|
||||||
// TODO: Gateway 同步接口对接后移除此函数,改为调用 Gateway 接口获取真实数据
|
// B 端和 C 端结构独立,通过映射函数转换(per D-08)
|
||||||
func buildMockDeviceRealtime() *dto.DeviceRealtimeInfo {
|
func mapDeviceGatewayInfoToClientInfo(g *dto.DeviceGatewayInfo) *dto.DeviceRealtimeInfo {
|
||||||
onlineStatus := int64(1)
|
if g == nil {
|
||||||
batteryLevel := int64(85)
|
return nil
|
||||||
deviceStatus := int64(1)
|
|
||||||
runTime := "3600"
|
|
||||||
connectTime := "3500"
|
|
||||||
rsrp := int64(-80)
|
|
||||||
rsrq := int64(-10)
|
|
||||||
rssi := "-65"
|
|
||||||
sinr := int64(15)
|
|
||||||
ssid := "JunHong-WiFi"
|
|
||||||
wifiEnabled := true
|
|
||||||
wifiPassword := "12345678"
|
|
||||||
ipAddress := "192.168.1.1"
|
|
||||||
lanIP := "192.168.1.1"
|
|
||||||
dailyUsage := "0"
|
|
||||||
maxClients := int64(32)
|
|
||||||
switchMode := 0
|
|
||||||
|
|
||||||
return &dto.DeviceRealtimeInfo{
|
|
||||||
OnlineStatus: &onlineStatus,
|
|
||||||
BatteryLevel: &batteryLevel,
|
|
||||||
Status: &deviceStatus,
|
|
||||||
RunTime: &runTime,
|
|
||||||
ConnectTime: &connectTime,
|
|
||||||
Rsrp: &rsrp,
|
|
||||||
Rsrq: &rsrq,
|
|
||||||
Rssi: &rssi,
|
|
||||||
Sinr: &sinr,
|
|
||||||
SSID: &ssid,
|
|
||||||
WifiEnabled: &wifiEnabled,
|
|
||||||
WifiPassword: &wifiPassword,
|
|
||||||
IPAddress: &ipAddress,
|
|
||||||
LANIP: &lanIP,
|
|
||||||
DailyUsage: &dailyUsage,
|
|
||||||
MaxClients: &maxClients,
|
|
||||||
SwitchMode: &switchMode,
|
|
||||||
}
|
}
|
||||||
|
info := &dto.DeviceRealtimeInfo{}
|
||||||
|
if g.OnlineStatus != nil {
|
||||||
|
v := int64(*g.OnlineStatus)
|
||||||
|
info.OnlineStatus = &v
|
||||||
|
}
|
||||||
|
if g.BatteryLevel != nil {
|
||||||
|
v := int64(*g.BatteryLevel)
|
||||||
|
info.BatteryLevel = &v
|
||||||
|
}
|
||||||
|
if g.Status != nil {
|
||||||
|
v := int64(*g.Status)
|
||||||
|
info.Status = &v
|
||||||
|
}
|
||||||
|
info.RunTime = g.RunTime
|
||||||
|
info.ConnectTime = g.ConnectTime
|
||||||
|
info.LastOnlineTime = g.LastOnlineTime
|
||||||
|
info.LastUpdateTime = g.LastUpdateTime
|
||||||
|
if g.Rsrp != nil {
|
||||||
|
v := int64(*g.Rsrp)
|
||||||
|
info.Rsrp = &v
|
||||||
|
}
|
||||||
|
if g.Rsrq != nil {
|
||||||
|
v := int64(*g.Rsrq)
|
||||||
|
info.Rsrq = &v
|
||||||
|
}
|
||||||
|
info.Rssi = g.Rssi
|
||||||
|
if g.Sinr != nil {
|
||||||
|
v := int64(*g.Sinr)
|
||||||
|
info.Sinr = &v
|
||||||
|
}
|
||||||
|
info.SSID = g.SSID
|
||||||
|
info.WifiEnabled = g.WifiEnabled
|
||||||
|
info.WifiPassword = g.WifiPassword
|
||||||
|
info.IPAddress = g.IPAddress
|
||||||
|
info.WANIP = g.WANIP
|
||||||
|
info.LANIP = g.LANIP
|
||||||
|
info.MACAddress = g.MACAddress
|
||||||
|
info.DailyUsage = g.DailyUsage
|
||||||
|
info.DLStats = g.DLStats
|
||||||
|
info.ULStats = g.ULStats
|
||||||
|
if g.LimitSpeed != nil {
|
||||||
|
v := int64(*g.LimitSpeed)
|
||||||
|
info.LimitSpeed = &v
|
||||||
|
}
|
||||||
|
info.CurrentIccid = g.CurrentIccid
|
||||||
|
if g.MaxClients != nil {
|
||||||
|
v := int64(*g.MaxClients)
|
||||||
|
info.MaxClients = &v
|
||||||
|
}
|
||||||
|
info.SoftwareVersion = g.SoftwareVersion
|
||||||
|
if g.SwitchMode != nil {
|
||||||
|
// SwitchMode 在 B 端为字符串("0"/"1"),C 端为整型(0/1),需解析转换
|
||||||
|
if n, convErr := strconv.Atoi(*g.SwitchMode); convErr == nil {
|
||||||
|
info.SwitchMode = &n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if g.SyncInterval != nil {
|
||||||
|
v := int64(*g.SyncInterval)
|
||||||
|
info.SyncInterval = &v
|
||||||
|
}
|
||||||
|
info.DeviceID = g.DeviceID
|
||||||
|
info.DeviceName = g.DeviceName
|
||||||
|
info.DeviceType = g.DeviceType
|
||||||
|
info.Imei = g.IMEI
|
||||||
|
info.Imsi = g.IMSI
|
||||||
|
return info
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -300,6 +300,30 @@ func (s *Service) GetRealtimeStatus(ctx context.Context, assetType string, id ui
|
|||||||
}
|
}
|
||||||
resp.DeviceProtectStatus = s.getDeviceProtectStatus(ctx, id)
|
resp.DeviceProtectStatus = s.getDeviceProtectStatus(ctx, id)
|
||||||
|
|
||||||
|
// 实时查询 Gateway 设备状态(不缓存,per D-05)
|
||||||
|
// Gateway 失败不阻断主流程,DeviceRealtime 返回 null(per D-06)
|
||||||
|
if s.gatewayClient != nil {
|
||||||
|
device, devErr := s.deviceStore.GetByID(ctx, id)
|
||||||
|
if devErr == nil {
|
||||||
|
// IMEI 优先,无则用 SN(与 Refresh 中 updateDeviceFromSyncInfo 保持一致)
|
||||||
|
cardNo := device.IMEI
|
||||||
|
if cardNo == "" {
|
||||||
|
cardNo = device.SN
|
||||||
|
}
|
||||||
|
if cardNo != "" {
|
||||||
|
if syncResp, syncErr := s.gatewayClient.SyncDeviceInfo(ctx, &gateway.SyncDeviceInfoReq{
|
||||||
|
CardNo: cardNo,
|
||||||
|
}); syncErr == nil {
|
||||||
|
resp.DeviceRealtime = mapSyncRespToDeviceGatewayInfo(syncResp)
|
||||||
|
} else {
|
||||||
|
logger.GetAppLogger().Warn("GetRealtimeStatus: sync-info 调用失败,DeviceRealtime 返回 null",
|
||||||
|
zap.Uint("device_id", id),
|
||||||
|
zap.Error(syncErr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(errors.CodeInvalidParam, "不支持的资产类型,仅支持 card 或 device")
|
return nil, errors.New(errors.CodeInvalidParam, "不支持的资产类型,仅支持 card 或 device")
|
||||||
}
|
}
|
||||||
@@ -307,6 +331,57 @@ func (s *Service) GetRealtimeStatus(ctx context.Context, assetType string, id ui
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mapSyncRespToDeviceGatewayInfo 将 Gateway SyncDeviceInfoResp 映射为 B 端 DeviceGatewayInfo DTO
|
||||||
|
// 使用指针字段,未赋值的字段保持 nil(omitempty)
|
||||||
|
func mapSyncRespToDeviceGatewayInfo(r *gateway.SyncDeviceInfoResp) *dto.DeviceGatewayInfo {
|
||||||
|
info := &dto.DeviceGatewayInfo{
|
||||||
|
OnlineStatus: &r.OnlineStatus,
|
||||||
|
RunTime: strPtr(r.RunTime),
|
||||||
|
ConnectTime: strPtr(r.ConnectTime),
|
||||||
|
LastOnlineTime: strPtr(r.LastOnlineTime),
|
||||||
|
LastUpdateTime: strPtr(r.LastUpdateTime),
|
||||||
|
Rsrp: &r.Rsrp,
|
||||||
|
Rsrq: &r.Rsrq,
|
||||||
|
Rssi: strPtr(r.RSSI),
|
||||||
|
Sinr: &r.Sinr,
|
||||||
|
SSID: strPtr(r.SSID),
|
||||||
|
WifiEnabled: &r.WifiEnabled,
|
||||||
|
WifiPassword: strPtr(r.WifiPassword),
|
||||||
|
IPAddress: strPtr(r.IPAddress),
|
||||||
|
WANIP: strPtr(r.WANIP),
|
||||||
|
LANIP: strPtr(r.LANIP),
|
||||||
|
MACAddress: strPtr(r.MacAddress),
|
||||||
|
DailyUsage: strPtr(r.DailyUsage),
|
||||||
|
DLStats: strPtr(r.DLStats),
|
||||||
|
ULStats: strPtr(r.ULStats),
|
||||||
|
LimitSpeed: &r.LimitSpeed,
|
||||||
|
CurrentIccid: strPtr(r.CurrentIccid),
|
||||||
|
MaxClients: &r.MaxClients,
|
||||||
|
SoftwareVersion: strPtr(r.SoftwareVersion),
|
||||||
|
SwitchMode: strPtr(r.SwitchMode),
|
||||||
|
SyncInterval: &r.SyncInterval,
|
||||||
|
DeviceID: strPtr(r.DeviceID),
|
||||||
|
DeviceName: strPtr(r.DeviceName),
|
||||||
|
DeviceType: strPtr(r.DeviceType),
|
||||||
|
IMEI: strPtr(r.IMEI),
|
||||||
|
IMSI: strPtr(r.IMSI),
|
||||||
|
Status: &r.Status,
|
||||||
|
}
|
||||||
|
if r.BatteryLevel != nil {
|
||||||
|
info.BatteryLevel = r.BatteryLevel
|
||||||
|
}
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
// strPtr 将空字符串转为 nil,非空字符串返回指针
|
||||||
|
// 避免 JSON 响应中出现大量空字符串字段(omitempty 对空字符串无效)
|
||||||
|
func strPtr(s string) *string {
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh 刷新资产数据(调网关同步)
|
// Refresh 刷新资产数据(调网关同步)
|
||||||
func (s *Service) Refresh(ctx context.Context, assetType string, id uint) (*dto.AssetRealtimeStatusResponse, error) {
|
func (s *Service) Refresh(ctx context.Context, assetType string, id uint) (*dto.AssetRealtimeStatusResponse, error) {
|
||||||
switch assetType {
|
switch assetType {
|
||||||
|
|||||||
Reference in New Issue
Block a user