package cardobservation import ( "context" "strconv" "strings" "time" "github.com/google/uuid" "go.uber.org/zap" cardapp "github.com/break/junhong_cmp_fiber/internal/application/cardobservation" "github.com/break/junhong_cmp_fiber/internal/store/postgres" "github.com/break/junhong_cmp_fiber/pkg/constants" ) // BestEffortSeriesDispatcher 将读入口触发失败降级为中文安全日志。 type BestEffortSeriesDispatcher struct { trigger *cardapp.SeriesTrigger logger *zap.Logger jobs chan cardapp.SeriesRequest deviceJobs chan cardapp.DeviceCardsSeriesRequest deviceControlJobs chan cardapp.DeviceControlSeriesRequest realnameJobs chan cardapp.RealnameCapabilitySeriesRequest deviceSimBindingStore *postgres.DeviceSimBindingStore carrierStore *postgres.CarrierStore } const bestEffortSeriesQueueSize = 1024 // NewBestEffortSeriesDispatcher 创建读入口观测序列分发器。 func NewBestEffortSeriesDispatcher(trigger *cardapp.SeriesTrigger, logger *zap.Logger, deviceSimBindingStore *postgres.DeviceSimBindingStore, carrierStore *postgres.CarrierStore) *BestEffortSeriesDispatcher { dispatcher := &BestEffortSeriesDispatcher{ trigger: trigger, logger: logger, jobs: make(chan cardapp.SeriesRequest, bestEffortSeriesQueueSize), deviceJobs: make(chan cardapp.DeviceCardsSeriesRequest, bestEffortSeriesQueueSize), deviceControlJobs: make(chan cardapp.DeviceControlSeriesRequest, bestEffortSeriesQueueSize), realnameJobs: make(chan cardapp.RealnameCapabilitySeriesRequest, bestEffortSeriesQueueSize), deviceSimBindingStore: deviceSimBindingStore, carrierStore: carrierStore, } go dispatcher.run() return dispatcher } // DispatchDeviceControl 在后台解析设备控制前后的相关卡,避免增加原操作响应耗时。 func (d *BestEffortSeriesDispatcher) DispatchDeviceControl(_ context.Context, request cardapp.DeviceControlSeriesRequest) { if d == nil || d.trigger == nil || request.DeviceID == 0 { return } request.Request = normalizeRequest(request.Request) select { case d.deviceControlJobs <- request: default: d.logFailure(request.Request, "", false, nil, "后台设备控制观测队列已满") } } // Dispatch 尝试创建或合并序列,失败不向原查询调用方返回。 func (d *BestEffortSeriesDispatcher) Dispatch(_ context.Context, request cardapp.SeriesRequest) { if d == nil || d.trigger == nil { return } request = normalizeRequest(request) select { case d.jobs <- request: default: d.logFailure(request, "", false, nil, "后台观测队列已满") } } // DispatchDeviceCards 在后台展开设备当前有效绑定卡,避免请求协程逐卡查询数据库。 func (d *BestEffortSeriesDispatcher) DispatchDeviceCards(_ context.Context, request cardapp.DeviceCardsSeriesRequest) { if d == nil || d.trigger == nil || request.DeviceID == 0 || d.deviceSimBindingStore == nil { return } request.Request = normalizeRequest(request.Request) select { case d.deviceJobs <- request: default: d.logFailure(request.Request, "", false, nil, "后台设备观测队列已满") } } // DispatchRealnameWithCapability 在后台读取运营商实名能力,none 能力不触发观测。 func (d *BestEffortSeriesDispatcher) DispatchRealnameWithCapability(_ context.Context, request cardapp.RealnameCapabilitySeriesRequest) { if d == nil || d.trigger == nil || request.CarrierID == 0 || d.carrierStore == nil { return } request.Request = normalizeRequest(request.Request) select { case d.realnameJobs <- request: default: d.logFailure(request.Request, "", false, nil, "后台实名观测队列已满") } } func (d *BestEffortSeriesDispatcher) run() { for { select { case request := <-d.jobs: d.triggerRequest(request) case request := <-d.deviceJobs: d.expandDeviceRequest(request) case request := <-d.deviceControlJobs: d.expandDeviceControlRequest(request) case request := <-d.realnameJobs: d.expandRealnameRequest(request) } } } func (d *BestEffortSeriesDispatcher) expandDeviceControlRequest(request cardapp.DeviceControlSeriesRequest) { deviceRequest := request.Request deviceRequest.ResourceType = constants.CardObservationResourceTypeDevice deviceRequest.ResourceID = strconv.FormatUint(uint64(request.DeviceID), 10) deviceRequest.SyncType = constants.CardObservationSyncTypeDeviceInfo deviceRequest.ExpectedValue = strings.TrimSpace(request.TargetICCID) d.triggerRequest(deviceRequest) if strings.TrimSpace(request.TargetICCID) == "" { for _, cardID := range request.BoundCardIDs { d.triggerCardControlRequest(request.Request, cardID, constants.CardObservationSyncTypeNetwork) } return } d.triggerCardControlRequest(request.Request, request.SourceCardID, constants.CardObservationSyncTypeNetwork) if request.TargetCardID != request.SourceCardID { d.triggerCardControlRequest(request.Request, request.TargetCardID, constants.CardObservationSyncTypeNetwork) } if request.IncludeTargetTraffic { d.triggerCardControlRequest(request.Request, request.TargetCardID, constants.CardObservationSyncTypeTraffic) } } func (d *BestEffortSeriesDispatcher) triggerCardControlRequest(base cardapp.SeriesRequest, cardID uint, syncType string) { if cardID == 0 { return } base.ResourceType = constants.CardObservationResourceTypeCard base.ResourceID = strconv.FormatUint(uint64(cardID), 10) base.SyncType = syncType base.ExpectedValue = "" d.triggerRequest(base) } func (d *BestEffortSeriesDispatcher) triggerRequest(request cardapp.SeriesRequest) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) seriesID, merged, err := d.trigger.Trigger(ctx, request) cancel() if err != nil { d.logFailure(request, seriesID, merged, err, "后台观测序列触发失败") } } func (d *BestEffortSeriesDispatcher) expandDeviceRequest(request cardapp.DeviceCardsSeriesRequest) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) bindings, err := d.deviceSimBindingStore.ListByDeviceID(ctx, request.DeviceID) cancel() if err != nil { d.logFailure(request.Request, "", false, err, "查询设备有效绑定卡失败") return } for _, binding := range bindings { if binding == nil || binding.IotCardID == 0 { continue } cardRequest := request.Request cardRequest.ResourceType = constants.CardObservationResourceTypeCard cardRequest.ResourceID = strconv.FormatUint(uint64(binding.IotCardID), 10) d.triggerRequest(cardRequest) } } func (d *BestEffortSeriesDispatcher) expandRealnameRequest(request cardapp.RealnameCapabilitySeriesRequest) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) carrier, err := d.carrierStore.GetByID(ctx, request.CarrierID) cancel() if err != nil { d.logFailure(request.Request, "", false, err, "查询运营商实名能力失败") return } if carrier.RealnameLinkType == constants.RealnameLinkTypeNone { return } d.triggerRequest(request.Request) } func normalizeRequest(request cardapp.SeriesRequest) cardapp.SeriesRequest { request.RequestID = strings.TrimSpace(request.RequestID) request.CorrelationID = strings.TrimSpace(request.CorrelationID) if request.RequestID == "" && request.CorrelationID == "" { request.RequestID = uuid.NewString() request.CorrelationID = request.RequestID } else if request.RequestID == "" { request.RequestID = request.CorrelationID } else if request.CorrelationID == "" { request.CorrelationID = request.RequestID } return request } func (d *BestEffortSeriesDispatcher) logFailure(request cardapp.SeriesRequest, seriesID string, merged bool, err error, message string) { if d.logger == nil { return } fields := []zap.Field{ zap.String("scene", request.Scene), zap.String("resource_type", request.ResourceType), zap.String("resource_id", request.ResourceID), zap.String("sync_type", request.SyncType), zap.String("series_id", seriesID), zap.Bool("merged", merged), zap.String("request_id", request.RequestID), zap.String("correlation_id", request.CorrelationID), } if err != nil { fields = append(fields, zap.Error(err)) } d.logger.Warn(message+",已保持原查询结果", fields...) } var _ cardapp.BestEffortSeriesDispatcher = (*BestEffortSeriesDispatcher)(nil)