All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m33s
- 新增网关限流:按本系统运营商账户(卡 carrier_id)→ 限流组 → 操作限制每秒请求数,每次 HTTP 尝试计量,默认限流、仅后台人工点击/通道阈值/自动续费/手动刷新豁免;支持只统计与拦截两种模式,默认只统计 - Asynq 遇限流不计失败、固定短延迟重试;轮询任务与停复机业务重试遇限流短延迟重排 - 停机、复机不再使用网关内部重试,只发一次请求 - 网关返回结构化错误;新增上游错误分类规则表与测试接口 - 新增按月分区的停复机记录表:系统自动停复机只写记录,不再写统一审计与集成日志;普通轮询查询不再写集成日志 - 新增限流组、错误分类、停复机记录、按账户调用统计与枚举后台接口 - 迁移 000243~000246;同步 OpenSpec 提案(含后续 6 个 change)与技术方案、工程约束
62 lines
2.2 KiB
Go
62 lines
2.2 KiB
Go
package gatewaylimit
|
|
|
|
import (
|
|
"context"
|
|
stderrors "errors"
|
|
"time"
|
|
|
|
"github.com/hibiken/asynq"
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
|
)
|
|
|
|
// partitionDropLookbackMonths 是每次清理向前检查的过期月数,覆盖维护任务停摆一段时间后的补删。
|
|
const partitionDropLookbackMonths = 12
|
|
|
|
// PartitionMaintainer 维护停复机记录按月分区:提前创建未来分区,按保留期整区删除过期分区。
|
|
type PartitionMaintainer struct {
|
|
store *postgres.CardStopResumeRecordStore
|
|
settings *SettingsProvider
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewPartitionMaintainer 创建分区维护任务。
|
|
func NewPartitionMaintainer(store *postgres.CardStopResumeRecordStore, settings *SettingsProvider, logger *zap.Logger) *PartitionMaintainer {
|
|
if logger == nil {
|
|
logger = zap.NewNop()
|
|
}
|
|
return &PartitionMaintainer{store: store, settings: settings, logger: logger}
|
|
}
|
|
|
|
// Handle 是 Asynq 定时任务入口;任一分区操作失败时写错误告警并返回错误,由 Asynq 按既有规则重试。
|
|
func (m *PartitionMaintainer) Handle(ctx context.Context, _ *asynq.Task) error {
|
|
if m == nil || m.store == nil {
|
|
return stderrors.New("停复机记录分区维护未配置")
|
|
}
|
|
settings := m.settings.Current(ctx)
|
|
monthStart := currentMonthStart(time.Now())
|
|
var failed error
|
|
for i := 0; i <= settings.RecordPremakeMonth; i++ {
|
|
month := monthStart.AddDate(0, i, 0)
|
|
if err := m.store.EnsureMonthPartition(ctx, month); err != nil {
|
|
m.logger.Error("创建停复机记录分区失败,记录将落入默认分区", zap.String("month", month.Format("200601")), zap.Error(err))
|
|
failed = err
|
|
}
|
|
}
|
|
for k := 0; k < partitionDropLookbackMonths; k++ {
|
|
month := monthStart.AddDate(0, -(settings.RecordRetention + k), 0)
|
|
if err := m.store.DropMonthPartition(ctx, month); err != nil {
|
|
m.logger.Error("删除过期停复机记录分区失败", zap.String("month", month.Format("200601")), zap.Error(err))
|
|
failed = err
|
|
}
|
|
}
|
|
return failed
|
|
}
|
|
|
|
// currentMonthStart 返回上海时区当月 1 日 0 点。
|
|
func currentMonthStart(now time.Time) time.Time {
|
|
local := now.In(shanghai())
|
|
return time.Date(local.Year(), local.Month(), 1, 0, 0, 0, 0, shanghai())
|
|
}
|