Files
junhong_cmp_fiber/internal/polling/recalculate_task.go
邹进 98017ac053
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m37s
feat: 分层轮询、重置日前加密与激活前流量结算
- 轮询配置新增套餐状态、资产状态、重置日前窗口与窗口内流量间隔(迁移 248),间隔三态:正数按间隔、0 不检查、空继承
- 配置按 priority, id 排序合并;在库与已销售无套餐未匹配时告警不入队
- 新增优先级整体重排、匹配预览、立即重算到期时间(异步只提前)、分层统计与重置日前加密统计接口
- 已切换日快照计量的卡流量按观测时间归属,激活前用量只记已结算
- 订单购买与实名激活补齐资产级激活锁,激活前对已切换卡结算一次流量,失败只告警不阻塞,结算不触发通道阈值判定
2026-10-24 17:06:38 +09:00

84 lines
3.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}