From 28ac58ea184a659b0dafed1464496e185d9513de Mon Sep 17 00:00:00 2001 From: huang Date: Sat, 18 Apr 2026 09:30:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20PollingRealnameHandler=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E8=AE=BE=E5=A4=87=E7=BA=A7=E5=A5=97=E9=A4=90=E5=AE=9E?= =?UTF-8?q?=E5=90=8D=E6=BF=80=E6=B4=BB=E8=81=94=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 结构体新增 deviceSimBindingStore 字段,构造函数参数列表末尾追加 - 新增 triggerDeviceRealnameActivation:卡首次实名(0→1)时查询所属设备, 若有绑定则提交 carrier_type=device 的 TaskTypePackageFirstActivation 任务 - 独立卡(无绑定设备)静默跳过,任务提交失败仅记录 Warn 日志不阻断流程 - registerPollingHandlers 传入 h.workerResult.Stores.DeviceSimBinding - 修复「购买后某张卡实名」场景下设备级套餐永不激活的缺陷 Co-authored-by: Sisyphus --- internal/task/polling_realname_handler.go | 63 +++++++++++++++++++---- pkg/queue/handler.go | 2 +- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/internal/task/polling_realname_handler.go b/internal/task/polling_realname_handler.go index 36bd49f..647d33b 100644 --- a/internal/task/polling_realname_handler.go +++ b/internal/task/polling_realname_handler.go @@ -18,11 +18,12 @@ import ( // 职责:调 Gateway 查实名状态 → 写 DB → 触发首次实名激活任务 // 不做:停复机决策(实名状态 0→1 时通过 EvaluateAndAct 触发 not_realname 复机) type PollingRealnameHandler struct { - base *PollingBase - gateway *gateway.Client - iotCardStore *postgres.IotCardStore - asynqClient *asynq.Client - stopResumeSvc iot_card_svc.StopResumeServiceInterface + base *PollingBase + gateway *gateway.Client + iotCardStore *postgres.IotCardStore + asynqClient *asynq.Client + stopResumeSvc iot_card_svc.StopResumeServiceInterface + deviceSimBindingStore *postgres.DeviceSimBindingStore } // NewPollingRealnameHandler 创建实名检查任务处理器 @@ -32,13 +33,15 @@ func NewPollingRealnameHandler( iotCardStore *postgres.IotCardStore, asynqClient *asynq.Client, stopResumeSvc iot_card_svc.StopResumeServiceInterface, + deviceSimBindingStore *postgres.DeviceSimBindingStore, ) *PollingRealnameHandler { return &PollingRealnameHandler{ - base: base, - gateway: gw, - iotCardStore: iotCardStore, - asynqClient: asynqClient, - stopResumeSvc: stopResumeSvc, + base: base, + gateway: gw, + iotCardStore: iotCardStore, + asynqClient: asynqClient, + stopResumeSvc: stopResumeSvc, + deviceSimBindingStore: deviceSimBindingStore, } } @@ -138,6 +141,7 @@ func (h *PollingRealnameHandler) Handle(ctx context.Context, t *asynq.Task) erro if isFirstRealname { // 0→1:首次实名,触发套餐激活并立即复机评估(不等待下一个 carddata/package 周期) h.triggerFirstRealnameActivation(ctx, cardID) + h.triggerDeviceRealnameActivation(ctx, cardID) if h.stopResumeSvc != nil { freshCard, loadErr := h.iotCardStore.GetByID(ctx, cardID) if loadErr == nil { @@ -192,3 +196,42 @@ func (h *PollingRealnameHandler) triggerFirstRealnameActivation(ctx context.Cont h.base.logger.Warn("提交首次实名激活任务失败", zap.Uint("card_id", cardID), zap.Error(err)) } } + +// triggerDeviceRealnameActivation 卡首次实名后,触发其所属设备的设备级套餐激活任务 +// 若卡不属于任何设备(独立卡),静默跳过 +func (h *PollingRealnameHandler) triggerDeviceRealnameActivation(ctx context.Context, cardID uint) { + if h.asynqClient == nil || h.deviceSimBindingStore == nil { + return + } + binding, err := h.deviceSimBindingStore.GetActiveBindingByCardID(ctx, cardID) + if err != nil { + if isNotFound(err) { + return + } + h.base.logger.Warn("查询卡绑定设备失败,跳过设备级套餐激活", + zap.Uint("card_id", cardID), zap.Error(err)) + return + } + deviceID := binding.DeviceID + payload := map[string]any{ + "carrier_type": "device", + "carrier_id": deviceID, + "activation_type": "realname", + "timestamp": time.Now().Unix(), + } + payloadBytes, err := sonic.Marshal(payload) + if err != nil { + h.base.logger.Warn("序列化设备实名激活任务载荷失败", + zap.Uint("card_id", cardID), zap.Uint("device_id", deviceID), zap.Error(err)) + return + } + task := asynq.NewTask(constants.TaskTypePackageFirstActivation, payloadBytes, + asynq.MaxRetry(3), + asynq.Timeout(30*time.Second), + asynq.Queue(constants.QueueDefault), + ) + if _, err := h.asynqClient.EnqueueContext(ctx, task); err != nil { + h.base.logger.Warn("提交设备实名激活任务失败", + zap.Uint("card_id", cardID), zap.Uint("device_id", deviceID), zap.Error(err)) + } +} diff --git a/pkg/queue/handler.go b/pkg/queue/handler.go index 021fd07..c6bd85c 100644 --- a/pkg/queue/handler.go +++ b/pkg/queue/handler.go @@ -156,7 +156,7 @@ func (h *Handler) registerCommissionCalculationHandler() { func (h *Handler) registerPollingHandlers() { realnameHandler := task.NewPollingRealnameHandler( h.pollingBase, h.gatewayClient, h.workerResult.Stores.IotCard, - h.asynqClient, h.pollingStopResumeSvc) + h.asynqClient, h.pollingStopResumeSvc, h.workerResult.Stores.DeviceSimBinding) carrierStore := postgres.NewCarrierStore(h.db) carddataHandler := task.NewPollingCarddataHandler( h.pollingBase, h.gatewayClient, h.workerResult.Stores.IotCard,