mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-04-18 12:27:26 +00:00
Add plan-level quota reset periods and display/reset cadence in admin/UI Enforce natural reset alignment with background reset task and cleanup job Make subscription pre-consume/refund idempotent with request-scoped records and retries Use database time for consistent resets across multi-instance deployments Harden payment callbacks with locking and idempotent order completion Record subscription purchases in topup history and billing logs Optimize subscription queries and add critical composite indexes
79 lines
1.8 KiB
Go
79 lines
1.8 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/QuantumNous/new-api/common"
|
|
"github.com/QuantumNous/new-api/logger"
|
|
"github.com/QuantumNous/new-api/model"
|
|
|
|
"github.com/bytedance/gopkg/util/gopool"
|
|
)
|
|
|
|
const (
|
|
subscriptionResetTickInterval = 1 * time.Minute
|
|
subscriptionResetBatchSize = 300
|
|
subscriptionCleanupInterval = 30 * time.Minute
|
|
)
|
|
|
|
var (
|
|
subscriptionResetOnce sync.Once
|
|
subscriptionResetRunning atomic.Bool
|
|
subscriptionCleanupLast atomic.Int64
|
|
)
|
|
|
|
func StartSubscriptionQuotaResetTask() {
|
|
subscriptionResetOnce.Do(func() {
|
|
if !common.IsMasterNode {
|
|
return
|
|
}
|
|
gopool.Go(func() {
|
|
logger.LogInfo(context.Background(), fmt.Sprintf("subscription quota reset task started: tick=%s", subscriptionResetTickInterval))
|
|
ticker := time.NewTicker(subscriptionResetTickInterval)
|
|
defer ticker.Stop()
|
|
|
|
runSubscriptionQuotaResetOnce()
|
|
for range ticker.C {
|
|
runSubscriptionQuotaResetOnce()
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
func runSubscriptionQuotaResetOnce() {
|
|
if !subscriptionResetRunning.CompareAndSwap(false, true) {
|
|
return
|
|
}
|
|
defer subscriptionResetRunning.Store(false)
|
|
|
|
ctx := context.Background()
|
|
totalReset := 0
|
|
for {
|
|
n, err := model.ResetDueSubscriptionItems(subscriptionResetBatchSize)
|
|
if err != nil {
|
|
logger.LogWarn(ctx, fmt.Sprintf("subscription quota reset task failed: %v", err))
|
|
return
|
|
}
|
|
if n == 0 {
|
|
break
|
|
}
|
|
totalReset += n
|
|
if n < subscriptionResetBatchSize {
|
|
break
|
|
}
|
|
}
|
|
lastCleanup := time.Unix(subscriptionCleanupLast.Load(), 0)
|
|
if time.Since(lastCleanup) >= subscriptionCleanupInterval {
|
|
if _, err := model.CleanupSubscriptionPreConsumeRecords(7 * 24 * 3600); err == nil {
|
|
subscriptionCleanupLast.Store(time.Now().Unix())
|
|
}
|
|
}
|
|
if totalReset > 0 && common.DebugEnabled {
|
|
logger.LogDebug(ctx, "subscription quota reset: reset_count=%d", totalReset)
|
|
}
|
|
}
|