@@ -122,6 +122,10 @@ const (
122122 defaultBackgroundRefreshInterval = 2 * time .Minute
123123 defaultUsageProbeMaxAge = 10 * time .Minute
124124 defaultRecoveryProbeInterval = 30 * time .Minute
125+ premium5hUrgencyWindow = 4 * time .Hour
126+ premium5hUrgencyMaxBonus = 25.0
127+ premium5hUrgencyMinRemainingPct = 5.0
128+ premium5hUrgencyFullRemainingPct = 50.0
125129)
126130
127131// SchedulerBreakdown 调度评分拆解
@@ -134,6 +138,7 @@ type SchedulerBreakdown struct {
134138 SuccessBonus float64
135139 ProvenBonus float64 // 经过验证的账号(TotalRequests > 10)加分
136140 UsagePenalty7d float64
141+ UsageUrgencyBonus5h float64
137142 LatencyPenalty float64
138143 SuccessRatePenalty float64 // 滑动窗口成功率惩罚
139144}
@@ -388,8 +393,7 @@ func linearDecay(base float64, elapsed, window time.Duration) float64 {
388393 return base * (1.0 - float64 (elapsed )/ float64 (window ))
389394}
390395
391- func (a * Account ) schedulerBreakdownLocked () SchedulerBreakdown {
392- now := time .Now ()
396+ func (a * Account ) schedulerBreakdownLocked (now time.Time ) SchedulerBreakdown {
393397 breakdown := SchedulerBreakdown {}
394398 premium5hLimited := a .premium5hRateLimitedLocked (now )
395399
@@ -457,6 +461,51 @@ func (a *Account) schedulerBreakdownLocked() SchedulerBreakdown {
457461 return breakdown
458462}
459463
464+ func (a * Account ) premium5hUsageUrgencyBonusLocked (now time.Time ) float64 {
465+ if ! isPremium5hPlan (a .PlanType ) {
466+ return 0
467+ }
468+ if ! a .UsagePercent5hValid || a .Reset5hAt .IsZero () {
469+ return 0
470+ }
471+ if a .UsagePercent5h >= 100 || a .premium5hRateLimitedLocked (now ) {
472+ return 0
473+ }
474+ if a .AccessToken == "" || a .Status == StatusError || a .HealthTier == HealthTierBanned {
475+ return 0
476+ }
477+ if atomic .LoadInt32 (& a .DispatchPaused ) != 0 {
478+ return 0
479+ }
480+ if a .Status == StatusCooldown && now .Before (a .CooldownUtil ) {
481+ return 0
482+ }
483+ if a .usageExhaustedLocked () {
484+ return 0
485+ }
486+
487+ timeRemaining := a .Reset5hAt .Sub (now )
488+ if timeRemaining <= 0 || timeRemaining > premium5hUrgencyWindow {
489+ return 0
490+ }
491+
492+ quotaRemaining := 100 - a .UsagePercent5h
493+ if quotaRemaining <= premium5hUrgencyMinRemainingPct {
494+ return 0
495+ }
496+
497+ timeFactor := 1 - float64 (timeRemaining )/ float64 (premium5hUrgencyWindow )
498+ quotaFactor := quotaRemaining / premium5hUrgencyFullRemainingPct
499+ if quotaFactor > 1 {
500+ quotaFactor = 1
501+ }
502+ if quotaFactor < 0 {
503+ quotaFactor = 0
504+ }
505+
506+ return premium5hUrgencyMaxBonus * timeFactor * quotaFactor
507+ }
508+
460509func (a * Account ) effectiveBaseConcurrencyLocked (storeBaseLimit int64 ) int64 {
461510 if a .BaseConcurrencyOverride != nil && * a .BaseConcurrencyOverride > 0 {
462511 return * a .BaseConcurrencyOverride
@@ -501,7 +550,7 @@ func (a *Account) effectiveScoreBiasLocked(now time.Time, tier AccountHealthTier
501550
502551func (a * Account ) recomputeSchedulerLocked (baseLimit int64 ) {
503552 now := time .Now ()
504- breakdown := a .schedulerBreakdownLocked ()
553+ breakdown := a .schedulerBreakdownLocked (now )
505554 score := 100.0 -
506555 breakdown .UnauthorizedPenalty -
507556 breakdown .RateLimitPenalty -
@@ -545,7 +594,10 @@ func (a *Account) recomputeSchedulerLocked(baseLimit int64) {
545594
546595 baseConcurrencyEffective := a .effectiveBaseConcurrencyLocked (baseLimit )
547596 scoreBiasEffective := a .effectiveScoreBiasLocked (now , tier )
548- dispatchScore := score + float64 (scoreBiasEffective )
597+ if a .dispatchBonusEligibleLocked (now , tier ) {
598+ breakdown .UsageUrgencyBonus5h = a .premium5hUsageUrgencyBonusLocked (now )
599+ }
600+ dispatchScore := score + float64 (scoreBiasEffective ) + breakdown .UsageUrgencyBonus5h
549601
550602 a .HealthTier = tier
551603 a .SchedulerScore = score
@@ -1045,6 +1097,11 @@ func (a *Account) GetSchedulerDebugSnapshot(baseLimit int64) SchedulerDebugSnaps
10451097 defer a .mu .Unlock ()
10461098
10471099 a .recomputeSchedulerLocked (baseLimit )
1100+ now := time .Now ()
1101+ breakdown := a .schedulerBreakdownLocked (now )
1102+ if a .dispatchBonusEligibleLocked (now , a .HealthTier ) {
1103+ breakdown .UsageUrgencyBonus5h = a .premium5hUsageUrgencyBonusLocked (now )
1104+ }
10481105 return SchedulerDebugSnapshot {
10491106 HealthTier : string (a .HealthTier ),
10501107 SchedulerScore : a .SchedulerScore ,
@@ -1054,7 +1111,7 @@ func (a *Account) GetSchedulerDebugSnapshot(baseLimit int64) SchedulerDebugSnaps
10541111 BaseConcurrencyOverride : cloneInt64Ptr (a .BaseConcurrencyOverride ),
10551112 BaseConcurrencyEffective : a .BaseConcurrencyEffective ,
10561113 DynamicConcurrencyLimit : a .DynamicConcurrencyLimit ,
1057- Breakdown : a . schedulerBreakdownLocked () ,
1114+ Breakdown : breakdown ,
10581115 LastUnauthorizedAt : a .LastUnauthorizedAt ,
10591116 LastRateLimitedAt : a .LastRateLimitedAt ,
10601117 LastTimeoutAt : a .LastTimeoutAt ,
@@ -2665,6 +2722,7 @@ func (s *Store) PersistUsageSnapshot(acc *Account, pct7d float64) {
26652722
26662723 now := time .Now ()
26672724 acc .SetUsageSnapshot (pct7d , now )
2725+ s .fastSchedulerUpdate (acc )
26682726
26692727 if s .db == nil {
26702728 return
0 commit comments