diff --git a/docs/asset-detail-refactor-api-changes.md b/docs/asset-detail-refactor-api-changes.md index 15a5c32..719890a 100644 --- a/docs/asset-detail-refactor-api-changes.md +++ b/docs/asset-detail-refactor-api-changes.md @@ -126,6 +126,9 @@ | `real_name_status` | int | 实名状态(仅 card) | | `current_month_usage_mb` | float64 | 本月已用流量 MB(仅 card) | | `last_sync_time` | time | 最后同步时间(仅 card) | +| `last_data_check_at` | time | 最后一次流量检查时间(仅 card) | +| `last_real_name_check_at` | time | 最后一次实名检查时间(仅 card) | +| `last_card_status_check_at` | time | 最后一次卡状态检查时间(仅 card) | | `device_protect_status` | string | 保护期:`none` / `stop` / `start`(仅 device) | | `cards[]` | array | 所有绑定卡的状态(仅 device),同 resolve 的 cards 结构 | diff --git a/internal/model/dto/asset_dto.go b/internal/model/dto/asset_dto.go index 9301779..d9b1218 100644 --- a/internal/model/dto/asset_dto.go +++ b/internal/model/dto/asset_dto.go @@ -71,35 +71,42 @@ type AssetResolveResponse struct { // BoundCardInfo 设备绑定的卡信息 type BoundCardInfo struct { - CardID uint `json:"card_id" description:"卡ID"` - ICCID string `json:"iccid" description:"ICCID"` - MSISDN string `json:"msisdn,omitempty" description:"手机号"` - NetworkStatus int `json:"network_status" description:"网络状态:0停机 1开机"` - RealNameStatus int `json:"real_name_status" description:"实名状态:0未实名 1已实名"` - RealNameAt *time.Time `json:"real_name_at" description:"该卡最近一次完成实名的时间,未实名时为 null"` - SlotPosition int `json:"slot_position,omitempty" description:"插槽位置"` - IsCurrent bool `json:"is_current" description:"是否为当前使用的卡"` - CarrierID uint `json:"carrier_id,omitempty" description:"运营商ID"` - CarrierType string `json:"carrier_type,omitempty" description:"运营商类型"` - CarrierName string `json:"carrier_name,omitempty" description:"运营商名称"` - RealnamePolicy string `json:"realname_policy" description:"实名认证策略 (none:无需实名, before_order:先实名后充值/购买, after_order:先充值/购买后实名)"` - GatewayExtend string `json:"gateway_extend" description:"Gateway 卡状态扩展字段,原样返回上游 extend,用于展示运营商侧实际停机原因"` + CardID uint `json:"card_id" description:"卡ID"` + ICCID string `json:"iccid" description:"ICCID"` + MSISDN string `json:"msisdn,omitempty" description:"手机号"` + NetworkStatus int `json:"network_status" description:"网络状态:0停机 1开机"` + RealNameStatus int `json:"real_name_status" description:"实名状态:0未实名 1已实名"` + RealNameAt *time.Time `json:"real_name_at" description:"该卡最近一次完成实名的时间,未实名时为 null"` + SlotPosition int `json:"slot_position,omitempty" description:"插槽位置"` + IsCurrent bool `json:"is_current" description:"是否为当前使用的卡"` + CarrierID uint `json:"carrier_id,omitempty" description:"运营商ID"` + CarrierType string `json:"carrier_type,omitempty" description:"运营商类型"` + CarrierName string `json:"carrier_name,omitempty" description:"运营商名称"` + RealnamePolicy string `json:"realname_policy" description:"实名认证策略 (none:无需实名, before_order:先实名后充值/购买, after_order:先充值/购买后实名)"` + GatewayExtend string `json:"gateway_extend" description:"Gateway 卡状态扩展字段,原样返回上游 extend,用于展示运营商侧实际停机原因"` + LastSyncTime *time.Time `json:"last_sync_time" description:"最后同步时间"` + LastDataCheckAt *time.Time `json:"last_data_check_at" description:"最后一次流量检查时间"` + LastRealNameCheckAt *time.Time `json:"last_real_name_check_at" description:"最后一次实名检查时间"` + LastCardStatusCheckAt *time.Time `json:"last_card_status_check_at" description:"最后一次卡状态检查时间"` } // AssetRealtimeStatusResponse 资产实时状态(只读DB/Redis) type AssetRealtimeStatusResponse struct { - AssetType string `json:"asset_type" description:"资产类型:card 或 device"` - AssetID uint `json:"asset_id" description:"资产ID"` - NetworkStatus int `json:"network_status" description:"网络状态(asset_type=card时有效):0停机 1开机"` - GatewayExtend string `json:"gateway_extend" description:"Gateway 卡状态扩展字段,原样返回上游 extend,用于展示运营商侧实际停机原因"` - RealNameStatus int `json:"real_name_status" description:"实名状态(asset_type=card时有效):0未实名 1已实名"` - RealnamePolicy string `json:"realname_policy,omitempty" description:"实名认证策略 (none:无需实名, before_order:先实名后充值/购买, after_order:先充值/购买后实名)"` - CurrentMonthUsageMB float64 `json:"current_month_usage_mb,omitempty" description:"系统自然月累计流量(MB,asset_type=card时有效,不等于运营商周期口径)"` - LastGatewayReadingMB float64 `json:"last_gateway_reading_mb,omitempty" description:"运营商当前周期累计流量读数(MB,asset_type=card时有效,运营商口径展示建议读取该字段)"` - LastSyncTime *time.Time `json:"last_sync_time,omitempty" description:"最后同步时间(asset_type=card时有效)"` - DeviceProtectStatus string `json:"device_protect_status,omitempty" description:"保护期状态(asset_type=device时有效):none/stop/start"` - Cards []BoundCardInfo `json:"cards,omitempty" description:"绑定卡状态列表(asset_type=device时有效)"` - DeviceRealtime *DeviceGatewayInfo `json:"device_realtime" description:"设备实时状态(来自 Gateway sync-info,始终返回,失败时仅含 gateway_msg)"` + AssetType string `json:"asset_type" description:"资产类型:card 或 device"` + AssetID uint `json:"asset_id" description:"资产ID"` + NetworkStatus int `json:"network_status" description:"网络状态(asset_type=card时有效):0停机 1开机"` + GatewayExtend string `json:"gateway_extend" description:"Gateway 卡状态扩展字段,原样返回上游 extend,用于展示运营商侧实际停机原因"` + RealNameStatus int `json:"real_name_status" description:"实名状态(asset_type=card时有效):0未实名 1已实名"` + RealnamePolicy string `json:"realname_policy,omitempty" description:"实名认证策略 (none:无需实名, before_order:先实名后充值/购买, after_order:先充值/购买后实名)"` + CurrentMonthUsageMB float64 `json:"current_month_usage_mb,omitempty" description:"系统自然月累计流量(MB,asset_type=card时有效,不等于运营商周期口径)"` + LastGatewayReadingMB float64 `json:"last_gateway_reading_mb,omitempty" description:"运营商当前周期累计流量读数(MB,asset_type=card时有效,运营商口径展示建议读取该字段)"` + LastSyncTime *time.Time `json:"last_sync_time" description:"最后同步时间(asset_type=card时有效)"` + LastDataCheckAt *time.Time `json:"last_data_check_at" description:"最后一次流量检查时间(asset_type=card时有效)"` + LastRealNameCheckAt *time.Time `json:"last_real_name_check_at" description:"最后一次实名检查时间(asset_type=card时有效)"` + LastCardStatusCheckAt *time.Time `json:"last_card_status_check_at" description:"最后一次卡状态检查时间(asset_type=card时有效)"` + DeviceProtectStatus string `json:"device_protect_status,omitempty" description:"保护期状态(asset_type=device时有效):none/stop/start"` + Cards []BoundCardInfo `json:"cards,omitempty" description:"绑定卡状态列表(asset_type=device时有效)"` + DeviceRealtime *DeviceGatewayInfo `json:"device_realtime" description:"设备实时状态(来自 Gateway sync-info,始终返回,失败时仅含 gateway_msg)"` } // AssetPackageResponse 资产套餐信息 diff --git a/internal/service/asset/service.go b/internal/service/asset/service.go index 2a5fd19..d5cd1d8 100644 --- a/internal/service/asset/service.go +++ b/internal/service/asset/service.go @@ -188,19 +188,23 @@ func (s *Service) buildDeviceResolveResponse(ctx context.Context, device *model. } for _, c := range cards { resp.Cards = append(resp.Cards, dto.BoundCardInfo{ - CardID: c.ID, - ICCID: c.ICCID, - MSISDN: c.MSISDN, - NetworkStatus: c.NetworkStatus, - RealNameStatus: c.RealNameStatus, - RealNameAt: c.FirstRealnameAt, - SlotPosition: slotMap[c.ID], - IsCurrent: isCurrentMap[c.ID], - CarrierID: c.CarrierID, - CarrierType: c.CarrierType, - CarrierName: c.CarrierName, - RealnamePolicy: c.RealnamePolicy, - GatewayExtend: c.GatewayExtend, + CardID: c.ID, + ICCID: c.ICCID, + MSISDN: c.MSISDN, + NetworkStatus: c.NetworkStatus, + RealNameStatus: c.RealNameStatus, + RealNameAt: c.FirstRealnameAt, + SlotPosition: slotMap[c.ID], + IsCurrent: isCurrentMap[c.ID], + CarrierID: c.CarrierID, + CarrierType: c.CarrierType, + CarrierName: c.CarrierName, + RealnamePolicy: c.RealnamePolicy, + GatewayExtend: c.GatewayExtend, + LastSyncTime: c.LastSyncTime, + LastDataCheckAt: c.LastDataCheckAt, + LastRealNameCheckAt: c.LastRealNameCheckAt, + LastCardStatusCheckAt: c.LastCardStatusCheckAt, }) } @@ -368,6 +372,9 @@ func (s *Service) GetRealtimeStatus(ctx context.Context, assetType string, id ui resp.CurrentMonthUsageMB = card.CurrentMonthUsageMB resp.LastGatewayReadingMB = card.LastGatewayReadingMB resp.LastSyncTime = card.LastSyncTime + resp.LastDataCheckAt = card.LastDataCheckAt + resp.LastRealNameCheckAt = card.LastRealNameCheckAt + resp.LastCardStatusCheckAt = card.LastCardStatusCheckAt case "device": // 查绑定卡状态列表 @@ -389,18 +396,22 @@ func (s *Service) GetRealtimeStatus(ctx context.Context, assetType string, id ui } for _, c := range cards { resp.Cards = append(resp.Cards, dto.BoundCardInfo{ - CardID: c.ID, - ICCID: c.ICCID, - MSISDN: c.MSISDN, - NetworkStatus: c.NetworkStatus, - RealNameStatus: c.RealNameStatus, - SlotPosition: slotMap[c.ID], - IsCurrent: isCurrentMap[c.ID], - CarrierID: c.CarrierID, - CarrierType: c.CarrierType, - CarrierName: c.CarrierName, - RealnamePolicy: c.RealnamePolicy, - GatewayExtend: c.GatewayExtend, + CardID: c.ID, + ICCID: c.ICCID, + MSISDN: c.MSISDN, + NetworkStatus: c.NetworkStatus, + RealNameStatus: c.RealNameStatus, + SlotPosition: slotMap[c.ID], + IsCurrent: isCurrentMap[c.ID], + CarrierID: c.CarrierID, + CarrierType: c.CarrierType, + CarrierName: c.CarrierName, + RealnamePolicy: c.RealnamePolicy, + GatewayExtend: c.GatewayExtend, + LastSyncTime: c.LastSyncTime, + LastDataCheckAt: c.LastDataCheckAt, + LastRealNameCheckAt: c.LastRealNameCheckAt, + LastCardStatusCheckAt: c.LastCardStatusCheckAt, }) } } diff --git a/internal/task/polling_carddata_handler.go b/internal/task/polling_carddata_handler.go index 40da970..c3b8736 100644 --- a/internal/task/polling_carddata_handler.go +++ b/internal/task/polling_carddata_handler.go @@ -90,6 +90,9 @@ func (h *PollingCarddataHandler) Handle(ctx context.Context, t *asynq.Task) erro resetDay := h.carrierStore.GetDataResetDay(ctx, card.CarrierID) updates, flowIncrementMB, isCrossMonth := h.calculateFlowUpdates(card, gatewayFlowMB, now, resetDay) updates["last_data_check_at"] = now + if h.gateway != nil { + updates["last_sync_time"] = now + } if h.base.verboseLog && h.gateway != nil { h.base.logger.Info("流量轮询详情", diff --git a/internal/task/polling_cardstatus_handler.go b/internal/task/polling_cardstatus_handler.go index 02ec37d..dcc79dc 100644 --- a/internal/task/polling_cardstatus_handler.go +++ b/internal/task/polling_cardstatus_handler.go @@ -107,6 +107,7 @@ func (h *PollingCardStatusHandler) Handle(ctx context.Context, t *asynq.Task) er fields := map[string]any{"last_card_status_check_at": now} if statusQueried { fields["gateway_extend"] = gatewayExtend + fields["last_sync_time"] = now } if statusChanged { fields["network_status"] = newNetworkStatus diff --git a/internal/task/polling_realname_handler.go b/internal/task/polling_realname_handler.go index dff6a68..f67a8a2 100644 --- a/internal/task/polling_realname_handler.go +++ b/internal/task/polling_realname_handler.go @@ -121,6 +121,9 @@ func (h *PollingRealnameHandler) Handle(ctx context.Context, t *asynq.Task) erro now := time.Now() fields := map[string]any{"last_real_name_check_at": now} + if h.gateway != nil { + fields["last_sync_time"] = now + } if statusChanged { fields["real_name_status"] = newStatus }