All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m37s
- 轮询配置新增套餐状态、资产状态、重置日前窗口与窗口内流量间隔(迁移 248),间隔三态:正数按间隔、0 不检查、空继承 - 配置按 priority, id 排序合并;在库与已销售无套餐未匹配时告警不入队 - 新增优先级整体重排、匹配预览、立即重算到期时间(异步只提前)、分层统计与重置日前加密统计接口 - 已切换日快照计量的卡流量按观测时间归属,激活前用量只记已结算 - 订单购买与实名激活补齐资产级激活锁,激活前对已切换卡结算一次流量,失败只告警不阻塞,结算不触发通道阈值判定
84 lines
3.1 KiB
Go
84 lines
3.1 KiB
Go
package polling
|
||
|
||
import (
|
||
"context"
|
||
"strconv"
|
||
"time"
|
||
|
||
"github.com/bytedance/sonic"
|
||
"github.com/hibiken/asynq"
|
||
"github.com/redis/go-redis/v9"
|
||
"go.uber.org/zap"
|
||
|
||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||
)
|
||
|
||
// RecalculateDuePayload 是重算到期时间异步任务的载荷。
|
||
type RecalculateDuePayload struct {
|
||
TaskID string `json:"task_id"`
|
||
CarrierID *uint `json:"carrier_id,omitempty"`
|
||
}
|
||
|
||
// RecalculateTaskHandler 是重算到期时间的 Asynq 任务处理器;进度写入 Redis 供后台查询。
|
||
type RecalculateTaskHandler struct {
|
||
recalculator *DueRecalculator
|
||
redis *redis.Client
|
||
logger *zap.Logger
|
||
}
|
||
|
||
// NewRecalculateTaskHandler 创建重算任务处理器。
|
||
func NewRecalculateTaskHandler(recalculator *DueRecalculator, redisClient *redis.Client, logger *zap.Logger) *RecalculateTaskHandler {
|
||
if logger == nil {
|
||
logger = zap.NewNop()
|
||
}
|
||
return &RecalculateTaskHandler{recalculator: recalculator, redis: redisClient, logger: logger}
|
||
}
|
||
|
||
// Handle 执行重算;失败写入进度并返回 SkipRetry,避免按指数退避反复全量扫描。
|
||
func (h *RecalculateTaskHandler) Handle(ctx context.Context, task *asynq.Task) error {
|
||
var payload RecalculateDuePayload
|
||
if err := sonic.Unmarshal(task.Payload(), &payload); err != nil || payload.TaskID == "" {
|
||
return asynq.SkipRetry
|
||
}
|
||
key := constants.RedisPollingRecalcProgressKey(payload.TaskID)
|
||
h.write(key, map[string]any{"status": constants.PollingRecalcStatusRunning, "started_at": time.Now().Format(time.RFC3339)})
|
||
progress, err := h.recalculator.Run(ctx, payload.CarrierID, func(p RecalculateProgress) {
|
||
h.write(key, map[string]any{"total": p.Total, "processed": p.Processed, "advanced": p.Advanced})
|
||
})
|
||
fields := map[string]any{"total": progress.Total, "processed": progress.Processed, "advanced": progress.Advanced, "finished_at": time.Now().Format(time.RFC3339)}
|
||
if err != nil {
|
||
fields["status"] = constants.PollingRecalcStatusFailed
|
||
fields["error"] = "重算到期时间失败,请稍后重试"
|
||
h.write(key, fields)
|
||
h.logger.Error("重算轮询到期时间失败", zap.String("task_id", payload.TaskID), zap.Error(err))
|
||
return asynq.SkipRetry
|
||
}
|
||
fields["status"] = constants.PollingRecalcStatusCompleted
|
||
h.write(key, fields)
|
||
h.logger.Info("重算轮询到期时间完成", zap.String("task_id", payload.TaskID),
|
||
zap.Int("processed", progress.Processed), zap.Int("advanced", progress.Advanced))
|
||
return nil
|
||
}
|
||
|
||
func (h *RecalculateTaskHandler) write(key string, fields map[string]any) {
|
||
if h.redis == nil {
|
||
return
|
||
}
|
||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||
defer cancel()
|
||
pipe := h.redis.Pipeline()
|
||
pipe.HSet(ctx, key, fields)
|
||
pipe.Expire(ctx, key, constants.PollingRecalcProgressTTLSeconds*time.Second)
|
||
if _, err := pipe.Exec(ctx); err != nil {
|
||
h.logger.Warn("写重算到期时间进度失败", zap.String("key", key), zap.Error(err))
|
||
}
|
||
}
|
||
|
||
// carrierText 把可选运营商账户转为进度展示文本。
|
||
func carrierText(carrierID *uint) string {
|
||
if carrierID == nil {
|
||
return ""
|
||
}
|
||
return strconv.FormatUint(uint64(*carrierID), 10)
|
||
}
|