Files
junhong_cmp_fiber/internal/task/polling_realname_handler.go
2026-07-24 16:07:18 +08:00

103 lines
4.5 KiB
Go

package task
import (
"context"
"strings"
"time"
"github.com/hibiken/asynq"
"go.uber.org/zap"
cardapp "github.com/break/junhong_cmp_fiber/internal/application/cardobservation"
carddomain "github.com/break/junhong_cmp_fiber/internal/domain/cardobservation"
"github.com/break/junhong_cmp_fiber/internal/gateway"
"github.com/break/junhong_cmp_fiber/internal/infrastructure/integrationlog"
"github.com/break/junhong_cmp_fiber/pkg/constants"
)
// PollingRealnameHandler 负责实名轮询编排,查询结果统一交给卡观测应用服务。
type PollingRealnameHandler struct {
base *PollingBase
gateway *gateway.Client
observation *cardapp.Service
integration *integrationlog.Repository
}
// NewPollingRealnameHandler 创建实名检查任务处理器。
func NewPollingRealnameHandler(base *PollingBase, gw *gateway.Client, observation *cardapp.Service, integration *integrationlog.Repository) *PollingRealnameHandler {
return &PollingRealnameHandler{base: base, gateway: gw, observation: observation, integration: integration}
}
// Handle 处理实名检查任务。
func (h *PollingRealnameHandler) Handle(ctx context.Context, task *asynq.Task) error {
startedAt := time.Now()
cardID, ok := parseTaskPayload(task.Payload(), h.base.logger)
if !ok {
return nil
}
if !h.base.acquireConcurrency(ctx, constants.TaskTypePollingRealname) {
h.base.logger.Debug("并发已满,重新入队", zap.Uint("card_id", cardID))
return h.base.requeueCard(ctx, cardID, constants.TaskTypePollingRealname)
}
defer h.base.releaseConcurrency(ctx, constants.TaskTypePollingRealname)
card, err := h.base.getCardWithCache(ctx, cardID)
if err != nil {
if isNotFound(err) {
return nil
}
return h.failAndRequeue(ctx, cardID, startedAt, "获取卡信息失败", err)
}
if card.CardCategory == constants.CardCategoryIndustry || h.gateway == nil {
return h.base.requeueCard(ctx, cardID, constants.TaskTypePollingRealname)
}
attemptStartedAt := time.Now()
attempt, err := startGatewayAttempt(ctx, h.integration, cardID, constants.IntegrationOperationGatewayRealname, constants.CardObservationSceneRealnamePolling)
if err != nil {
return h.failAndRequeue(ctx, cardID, startedAt, "建立 Gateway 实名 Integration Log 失败", err)
}
result, err := h.gateway.QueryRealnameStatus(ctx, &gateway.CardStatusReq{CardNo: card.ICCID})
if err != nil {
_ = completeGatewayAttempt(ctx, h.integration, attempt, false, attemptStartedAt)
return h.failAndRequeue(ctx, cardID, startedAt, "查询实名状态失败", err)
}
if strings.TrimSpace(result.ICCID) == "" {
_ = completeGatewayAttempt(ctx, h.integration, attempt, false, attemptStartedAt)
return h.failAndRequeue(ctx, cardID, startedAt, "实名查询响应缺少 ICCID", nil)
}
if logErr := completeGatewayAttempt(ctx, h.integration, attempt, true, attemptStartedAt); logErr != nil {
return h.failAndRequeue(ctx, cardID, startedAt, "完成 Gateway 实名 Integration Log 失败", logErr)
}
if h.observation == nil {
return h.failAndRequeue(ctx, cardID, startedAt, "卡实名观测能力未配置", nil)
}
now := time.Now()
decision, err := h.observation.ApplyCardObservation(ctx, carddomain.RealnameObservation{
CardID: cardID, Verified: result.RealStatus,
Metadata: carddomain.ObservationMetadata{
ObservationID: attempt.IntegrationID, Source: constants.CardObservationSourcePolling,
Scene: constants.CardObservationSceneRealnamePolling, ObservedAt: now, CorrelationID: attempt.IntegrationID,
},
})
if err != nil {
return h.failAndRequeue(ctx, cardID, startedAt, "应用实名观测失败", err)
}
if h.base.verboseLog {
h.base.logger.Info("实名状态轮询详情", zap.Uint("card_id", cardID), zap.String("iccid", card.ICCID),
zap.Bool("real_status", result.RealStatus), zap.Int("new_status", decision.AfterStatus),
zap.Bool("changed", decision.StatusChanged), zap.Bool("reversal_pending", decision.ReversalPending))
}
h.base.updateStats(ctx, constants.TaskTypePollingRealname, true, time.Since(startedAt))
return h.base.requeueCard(ctx, cardID, constants.TaskTypePollingRealname)
}
func (h *PollingRealnameHandler) failAndRequeue(ctx context.Context, cardID uint, startedAt time.Time, message string, err error) error {
fields := []zap.Field{zap.Uint("card_id", cardID)}
if err != nil {
fields = append(fields, zap.Error(err))
}
h.base.logger.Warn(message, fields...)
h.base.updateStats(ctx, constants.TaskTypePollingRealname, false, time.Since(startedAt))
return h.base.requeueCard(ctx, cardID, constants.TaskTypePollingRealname)
}