diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go
index 84c3b72..49d548d 100644
--- a/backend/cmd/server/main.go
+++ b/backend/cmd/server/main.go
@@ -175,8 +175,8 @@ func main() {
log.Printf("[Rock] Token 检查失败: %v", err)
}
rockSvc.CheckStockAndAutoPurchase(context.Background())
- // 绑定错误自动刷新:内部会检查 auto_refresh_mismatch_enabled 开关,关闭时直接跳过。
- go rockSvc.RefreshMismatchTokens(context.Background())
+ // 异常 Token 自动刷新:内部会检查 auto_refresh_mismatch_enabled 开关,关闭时直接跳过。
+ go rockSvc.RefreshStaleTokens(context.Background())
}
run()
ticker := time.NewTicker(5 * time.Minute)
diff --git a/backend/internal/handler/rock.go b/backend/internal/handler/rock.go
index c778c0d..415bb69 100644
--- a/backend/internal/handler/rock.go
+++ b/backend/internal/handler/rock.go
@@ -416,14 +416,14 @@ func (h *RockHandler) ReleaseToken(c *gin.Context) {
response.OK(c, gin.H{"message": "已释放回可用库存,该提货链接可以再次分配"})
}
-// RefreshMismatchTokens 一键刷新全部绑定错误的 Token(同步执行,单次最多 20 条)。
-func (h *RockHandler) RefreshMismatchTokens(c *gin.Context) {
- result, err := h.rockSvc.RefreshAllMismatchTokens(c.Request.Context())
+// RefreshStaleTokens 一键刷新异常 Token(绑定错误 + 使用超时 + 绑定超时,同步执行,单次最多 20 条)。
+func (h *RockHandler) RefreshStaleTokens(c *gin.Context) {
+ result, err := h.rockSvc.RefreshAllStaleTokens(c.Request.Context())
if err != nil {
response.BadRequest(c, err.Error())
return
}
- message := fmt.Sprintf("已扫描 %d 条绑定错误 Token,修复 %d 条", result.Scanned, result.Fixed)
+ message := fmt.Sprintf("已扫描 %d 条异常 Token,修复 %d 条", result.Scanned, result.Fixed)
if result.Failed > 0 {
message += fmt.Sprintf(",失败 %d 条", result.Failed)
}
diff --git a/backend/internal/router/router.go b/backend/internal/router/router.go
index ae45711..5d377eb 100644
--- a/backend/internal/router/router.go
+++ b/backend/internal/router/router.go
@@ -299,7 +299,7 @@ func Setup(h *Handlers) *gin.Engine {
platform.POST("/platform/rock/tokens/:id/reset", h.Rock.ResetToken)
platform.POST("/platform/rock/tokens/:id/release", h.Rock.ReleaseToken)
platform.POST("/platform/rock/tokens/:id/refresh-binding", h.Rock.RefreshBinding)
- platform.POST("/platform/rock/token-batches/refresh-mismatch", h.Rock.RefreshMismatchTokens)
+ platform.POST("/platform/rock/token-batches/refresh-stale", h.Rock.RefreshStaleTokens)
platform.GET("/platform/rock/tokens/:id/package-link", h.Rock.TokenPackageLink)
platform.GET("/platform/rock/token-batches/preview", h.Rock.PreviewTokenDelete)
platform.POST("/platform/rock/token-batches/batch-delete", h.Rock.BatchDeleteTokens)
diff --git a/backend/internal/service/rock_mismatch_scan.go b/backend/internal/service/rock_mismatch_scan.go
deleted file mode 100644
index 93010be..0000000
--- a/backend/internal/service/rock_mismatch_scan.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package service
-
-import (
- "context"
- "errors"
- "log"
- "sync/atomic"
- "time"
-
- "affiliate_dash/internal/model"
-)
-
-const (
- // 每次巡检/一键刷新最多处理 20 条,避免手动请求超时。
- rockMismatchScanLimit = 20
- // 同一 Token 的处理冷却:10 分钟内不重复调用上游。
- rockMismatchScanCooldown = 10 * time.Minute
- // 逐条刷新间隔,避免触发上游限流。
- rockMismatchScanGap = 1500 * time.Millisecond
-)
-
-// errRockMismatchScanBusy 表示已有巡检/一键刷新在跑,本次直接跳过。
-var errRockMismatchScanBusy = errors.New("绑定错误刷新任务正在执行")
-
-var rockMismatchScanRunning atomic.Bool
-
-// RockMismatchRefreshResult 是绑定错误刷新(巡检/一键)的汇总结果。
-type RockMismatchRefreshResult struct {
- Scanned int `json:"scanned"` // 本次扫描条数
- Fixed int `json:"fixed"` // 已同步为正常状态的条数
- Failed int `json:"failed"` // 调用上游失败的条数
- Remaining int `json:"remaining"` // 仍停留在绑定错误的条数
-}
-
-// RefreshMismatchTokens 定时巡检入口:受「自动刷新」开关控制,关闭时直接跳过。
-func (s *RockService) RefreshMismatchTokens(ctx context.Context) {
- cfg, err := s.GetConfig(ctx)
- if err != nil {
- log.Printf("[Rock] 绑定错误巡检读取配置失败: %v", err)
- return
- }
- if !cfg.AutoRefreshMismatchEnabled {
- return
- }
- result, err := s.refreshMismatchTokens(ctx)
- if err != nil {
- if errors.Is(err, errRockMismatchScanBusy) {
- return
- }
- log.Printf("[Rock] 绑定错误巡检失败: %v", err)
- return
- }
- if result.Scanned == 0 {
- return
- }
- log.Printf("[Rock] 绑定错误巡检完成: 扫描=%d 修复=%d 仍异常=%d 失败=%d",
- result.Scanned, result.Fixed, result.Remaining, result.Failed)
-}
-
-// RefreshAllMismatchTokens 手动「一键刷新绑定错误」:忽略自动开关,同步执行并返回结果。
-func (s *RockService) RefreshAllMismatchTokens(ctx context.Context) (*RockMismatchRefreshResult, error) {
- return s.refreshMismatchTokens(ctx)
-}
-
-// refreshMismatchTokens 扫描绑定错误 Token,逐条刷新上游状态。
-//
-// 选取条件:status=mismatch 且 updated_at 早于 10 分钟前,按最久未处理优先,单次最多 20 条。
-// 刷新失败或上游仍未绑定时,RefreshTokenBinding 会写 updated_at,从而进入下一轮冷却。
-func (s *RockService) refreshMismatchTokens(ctx context.Context) (*RockMismatchRefreshResult, error) {
- if !rockMismatchScanRunning.CompareAndSwap(false, true) {
- return nil, errRockMismatchScanBusy
- }
- defer rockMismatchScanRunning.Store(false)
-
- cutoff := time.Now().Add(-rockMismatchScanCooldown)
- var tokens []model.RockToken
- if err := s.db.WithContext(ctx).
- Where("status = ?", model.RockStatusMismatch).
- Where("updated_at <= ?", cutoff).
- Order("updated_at ASC").
- Limit(rockMismatchScanLimit).
- Find(&tokens).Error; err != nil {
- return nil, err
- }
-
- result := &RockMismatchRefreshResult{Scanned: len(tokens)}
- for i, token := range tokens {
- if i > 0 {
- select {
- case <-ctx.Done():
- return result, ctx.Err()
- case <-time.After(rockMismatchScanGap):
- }
- }
-
- res, err := s.RefreshTokenBinding(ctx, token.ID)
- if err != nil {
- result.Failed++
- // 失败也推进 updated_at,保证同一 Token 至少 10 分钟后才会再次尝试。
- _ = s.db.WithContext(ctx).Model(&model.RockToken{}).
- Where("id = ?", token.ID).
- Update("updated_at", time.Now()).Error
- log.Printf("[Rock] 刷新绑定错误 Token %d 失败: %v", token.ID, err)
- continue
- }
- if res.Status == model.RockStatusMismatch {
- result.Remaining++
- continue
- }
- result.Fixed++
- }
- return result, nil
-}
diff --git a/backend/internal/service/rock_overdue_test.go b/backend/internal/service/rock_overdue_test.go
index 2116067..e9a0b80 100644
--- a/backend/internal/service/rock_overdue_test.go
+++ b/backend/internal/service/rock_overdue_test.go
@@ -98,7 +98,7 @@ func TestListTokens_OverdueFilters(t *testing.T) {
}
}
-func TestRefreshMismatchTokens_SkipsWhenAutoRefreshDisabled(t *testing.T) {
+func TestRefreshStaleTokens_SkipsWhenAutoRefreshDisabled(t *testing.T) {
db := newRockTestDB(t)
var calls int32
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -123,7 +123,7 @@ func TestRefreshMismatchTokens_SkipsWhenAutoRefreshDisabled(t *testing.T) {
t.Fatalf("调整 updated_at 失败: %v", err)
}
- svc.RefreshMismatchTokens(context.Background())
+ svc.RefreshStaleTokens(context.Background())
if atomic.LoadInt32(&calls) != 0 {
t.Fatalf("自动刷新关闭时不应调用上游")
@@ -137,7 +137,7 @@ func TestRefreshMismatchTokens_SkipsWhenAutoRefreshDisabled(t *testing.T) {
}
}
-func TestRefreshAllMismatchTokens_FixesMismatch(t *testing.T) {
+func TestRefreshAllStaleTokens_FixesMismatch(t *testing.T) {
db := newRockTestDB(t)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@@ -168,7 +168,7 @@ func TestRefreshAllMismatchTokens_FixesMismatch(t *testing.T) {
t.Fatalf("调整 updated_at 失败: %v", err)
}
- result, err := svc.RefreshAllMismatchTokens(context.Background())
+ result, err := svc.RefreshAllStaleTokens(context.Background())
if err != nil {
t.Fatalf("一键刷新失败: %v", err)
}
@@ -184,3 +184,129 @@ func TestRefreshAllMismatchTokens_FixesMismatch(t *testing.T) {
t.Fatalf("应修复为 bound 并清空 mismatch 标记,实际 %+v", saved)
}
}
+
+// 手动一键刷新除了绑定错误,还要覆盖「使用超时」和「绑定超时」两类 Token。
+func TestRefreshAllStaleTokens_IncludesOverdueTokens(t *testing.T) {
+ db := newRockTestDB(t)
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ switch r.URL.Path {
+ case "/api/redeem/CODE-USING-OLD/refresh":
+ _ = json.NewEncoder(w).Encode(map[string]interface{}{
+ "token": map[string]interface{}{
+ "status": "bound",
+ "bound_account": "新角色",
+ "bound_account_id": "10001",
+ },
+ })
+ case "/api/redeem/CODE-BOUND-OLD/refresh":
+ _ = json.NewEncoder(w).Encode(map[string]interface{}{
+ "token": map[string]interface{}{
+ "status": "redeemed",
+ "bound_account": "旧角色",
+ "bound_account_id": "10002",
+ "redeemed_at": "2026-11-07 12:00:00",
+ },
+ })
+ default:
+ w.WriteHeader(http.StatusNotFound)
+ }
+ }))
+ defer server.Close()
+
+ svc := NewRockService(db, NewRockClient(server.URL))
+ setRockTestBearer(t, svc, "bearer-test")
+
+ old := time.Now().AddDate(0, 0, -10)
+ usingToken := model.RockToken{
+ UpstreamTokenID: "stale-using",
+ RedeemCode: "CODE-USING-OLD",
+ Status: model.RockStatusUsing,
+ UsingAt: &old,
+ }
+ boundToken := model.RockToken{
+ UpstreamTokenID: "stale-bound",
+ RedeemCode: "CODE-BOUND-OLD",
+ Status: model.RockStatusBound,
+ BoundAt: &old,
+ }
+ for _, token := range []*model.RockToken{&usingToken, &boundToken} {
+ if err := db.Create(token).Error; err != nil {
+ t.Fatalf("创建 Token 失败: %v", err)
+ }
+ if err := db.Model(&model.RockToken{}).Where("id = ?", token.ID).
+ UpdateColumn("updated_at", time.Now().Add(-time.Hour)).Error; err != nil {
+ t.Fatalf("调整 updated_at 失败: %v", err)
+ }
+ }
+
+ result, err := svc.RefreshAllStaleTokens(context.Background())
+ if err != nil {
+ t.Fatalf("一键刷新失败: %v", err)
+ }
+ if result.Scanned != 2 || result.Fixed != 2 || result.Remaining != 0 || result.Failed != 0 {
+ t.Fatalf("一键刷新结果不符: %+v", result)
+ }
+
+ var savedUsing, savedBound model.RockToken
+ if err := db.First(&savedUsing, usingToken.ID).Error; err != nil {
+ t.Fatalf("读取使用超时 Token 失败: %v", err)
+ }
+ if savedUsing.Status != model.RockStatusBound {
+ t.Fatalf("使用超时 Token 应同步为 bound,实际 %s", savedUsing.Status)
+ }
+ if err := db.First(&savedBound, boundToken.ID).Error; err != nil {
+ t.Fatalf("读取绑定超时 Token 失败: %v", err)
+ }
+ if savedBound.Status != model.RockStatusRedeemed || savedBound.RedeemedAt == nil {
+ t.Fatalf("绑定超时 Token 应同步为 redeemed,实际 %+v", savedBound)
+ }
+}
+
+// 自动刷新与一键刷新同规则:开关开启后也要覆盖使用超时 Token。
+func TestRefreshStaleTokens_IncludesOverdueWhenEnabled(t *testing.T) {
+ db := newRockTestDB(t)
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(map[string]interface{}{
+ "token": map[string]interface{}{
+ "status": "bound",
+ "bound_account": "自动刷新角色",
+ "bound_account_id": "20001",
+ },
+ })
+ }))
+ defer server.Close()
+
+ svc := NewRockService(db, NewRockClient(server.URL))
+ setRockTestBearer(t, svc, "bearer-test")
+ enabled := true
+ if _, err := svc.UpdateConfig(context.Background(), UpdateRockConfigReq{AutoRefreshMismatchEnabled: &enabled}); err != nil {
+ t.Fatalf("开启自动刷新失败: %v", err)
+ }
+
+ old := time.Now().AddDate(0, 0, -10)
+ token := model.RockToken{
+ UpstreamTokenID: "auto-stale-using",
+ RedeemCode: "CODE-AUTO-OLD",
+ Status: model.RockStatusUsing,
+ UsingAt: &old,
+ }
+ if err := db.Create(&token).Error; err != nil {
+ t.Fatalf("创建 Token 失败: %v", err)
+ }
+ if err := db.Model(&model.RockToken{}).Where("id = ?", token.ID).
+ UpdateColumn("updated_at", time.Now().Add(-time.Hour)).Error; err != nil {
+ t.Fatalf("调整 updated_at 失败: %v", err)
+ }
+
+ svc.RefreshStaleTokens(context.Background())
+
+ var saved model.RockToken
+ if err := db.First(&saved, token.ID).Error; err != nil {
+ t.Fatalf("读取 Token 失败: %v", err)
+ }
+ if saved.Status != model.RockStatusBound {
+ t.Fatalf("自动刷新也应处理使用超时 Token,实际 %s", saved.Status)
+ }
+}
diff --git a/backend/internal/service/rock_stale_scan.go b/backend/internal/service/rock_stale_scan.go
new file mode 100644
index 0000000..8109c6b
--- /dev/null
+++ b/backend/internal/service/rock_stale_scan.go
@@ -0,0 +1,128 @@
+package service
+
+import (
+ "context"
+ "errors"
+ "log"
+ "sync/atomic"
+ "time"
+
+ "affiliate_dash/internal/model"
+)
+
+const (
+ // 每次巡检/一键刷新最多处理 20 条,避免手动请求超时。
+ rockStaleScanLimit = 20
+ // 同一 Token 的处理冷却:10 分钟内不重复调用上游。
+ rockStaleScanCooldown = 10 * time.Minute
+ // 逐条刷新间隔,避免触发上游限流。
+ rockStaleScanGap = 1500 * time.Millisecond
+)
+
+// errRockStaleScanBusy 表示已有巡检/一键刷新在跑,本次直接跳过。
+var errRockStaleScanBusy = errors.New("刷新任务正在执行")
+
+var rockStaleScanRunning atomic.Bool
+
+// RockStaleRefreshResult 是异常 Token 刷新(巡检/一键)的汇总结果。
+type RockStaleRefreshResult struct {
+ Scanned int `json:"scanned"` // 本次扫描条数
+ Fixed int `json:"fixed"` // 已同步为其它状态(视为修复)的条数
+ Failed int `json:"failed"` // 调用上游失败的条数
+ Remaining int `json:"remaining"` // 刷新后状态仍未变化的条数
+}
+
+// RefreshStaleTokens 定时巡检入口:受「自动刷新」开关控制,关闭时直接跳过。
+//
+// 与手动「一键刷新」使用同一套扫描规则:绑定错误 + 使用超时 + 绑定超时。
+func (s *RockService) RefreshStaleTokens(ctx context.Context) {
+ cfg, err := s.GetConfig(ctx)
+ if err != nil {
+ log.Printf("[Rock] 异常 Token 巡检读取配置失败: %v", err)
+ return
+ }
+ if !cfg.AutoRefreshMismatchEnabled {
+ return
+ }
+ result, err := s.refreshStaleTokens(ctx)
+ if err != nil {
+ if errors.Is(err, errRockStaleScanBusy) {
+ return
+ }
+ log.Printf("[Rock] 异常 Token 巡检失败: %v", err)
+ return
+ }
+ if result.Scanned == 0 {
+ return
+ }
+ log.Printf("[Rock] 异常 Token 巡检完成: 扫描=%d 修复=%d 仍异常=%d 失败=%d",
+ result.Scanned, result.Fixed, result.Remaining, result.Failed)
+}
+
+// RefreshAllStaleTokens 手动「一键刷新」:绑定错误 + 使用超时 + 绑定超时,
+// 忽略自动开关,同步执行并返回结果。
+func (s *RockService) RefreshAllStaleTokens(ctx context.Context) (*RockStaleRefreshResult, error) {
+ return s.refreshStaleTokens(ctx)
+}
+
+// refreshStaleTokens 扫描异常 Token,逐条刷新上游状态。
+//
+// 选取条件:绑定错误,或使用中/已绑定超过阈值(与列表筛选同口径);
+// 且 updated_at 早于 10 分钟前(同一 Token 冷却),按最久未处理优先,单次最多 20 条。
+// 刷新失败或状态未变化时,RefreshTokenBinding 会写 updated_at,从而进入下一轮冷却。
+func (s *RockService) refreshStaleTokens(ctx context.Context) (*RockStaleRefreshResult, error) {
+ if !rockStaleScanRunning.CompareAndSwap(false, true) {
+ return nil, errRockStaleScanBusy
+ }
+ defer rockStaleScanRunning.Store(false)
+
+ cfg, err := s.GetConfig(ctx)
+ if err != nil {
+ return nil, err
+ }
+ warnDays, _, boundWarnDays := rockOverdueThresholds(cfg)
+
+ now := time.Now()
+ cutoff := now.Add(-rockStaleScanCooldown)
+ query := s.db.WithContext(ctx).Model(&model.RockToken{}).
+ Where("updated_at <= ?", cutoff).
+ Where(
+ "(status = ? OR (status = ? AND using_at IS NOT NULL AND using_at <= ?) OR (status = ? AND bound_at IS NOT NULL AND bound_at <= ?))",
+ model.RockStatusMismatch,
+ model.RockStatusUsing, rockOverdueCutoff(now, warnDays),
+ model.RockStatusBound, rockOverdueCutoff(now, boundWarnDays),
+ )
+
+ var tokens []model.RockToken
+ if err := query.Order("updated_at ASC").Limit(rockStaleScanLimit).Find(&tokens).Error; err != nil {
+ return nil, err
+ }
+
+ result := &RockStaleRefreshResult{Scanned: len(tokens)}
+ for i, token := range tokens {
+ if i > 0 {
+ select {
+ case <-ctx.Done():
+ return result, ctx.Err()
+ case <-time.After(rockStaleScanGap):
+ }
+ }
+
+ res, err := s.RefreshTokenBinding(ctx, token.ID)
+ if err != nil {
+ result.Failed++
+ // 失败也推进 updated_at,保证同一 Token 至少 10 分钟后才会再次尝试。
+ _ = s.db.WithContext(ctx).Model(&model.RockToken{}).
+ Where("id = ?", token.ID).
+ Update("updated_at", time.Now()).Error
+ log.Printf("[Rock] 刷新异常 Token %d 失败: %v", token.ID, err)
+ continue
+ }
+ if res.Status == token.Status {
+ result.Remaining++
+ continue
+ }
+ result.Fixed++
+ }
+ return result, nil
+}
diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts
index 36ed211..32cd879 100644
--- a/frontend/src/api/index.ts
+++ b/frontend/src/api/index.ts
@@ -785,7 +785,7 @@ export interface RockTokenRestoreResult {
skipped_items: RockTokenRestoreSkipItem[];
}
-export interface RockMismatchRefreshResult {
+export interface RockStaleRefreshResult {
scanned: number;
fixed: number;
failed: number;
@@ -867,10 +867,10 @@ export const rockApi = {
page_size: number;
},
),
- refreshMismatchTokens: () =>
+ refreshStaleTokens: () =>
request
- .post("/platform/rock/token-batches/refresh-mismatch")
- .then((r) => r.data.data as RockMismatchRefreshResult),
+ .post("/platform/rock/token-batches/refresh-stale")
+ .then((r) => r.data.data as RockStaleRefreshResult),
previewTokenDelete: (params?: { status?: string; search?: string }) =>
request
.get("/platform/rock/token-batches/preview", { params })
diff --git a/frontend/src/pages/PlatformRock.tsx b/frontend/src/pages/PlatformRock.tsx
index ec70dfc..5b8563f 100644
--- a/frontend/src/pages/PlatformRock.tsx
+++ b/frontend/src/pages/PlatformRock.tsx
@@ -38,7 +38,7 @@ export default function PlatformRock() {
const [togglingAutoPurchase, setTogglingAutoPurchase] = useState(false)
const [autoRefreshMismatchEnabled, setAutoRefreshMismatchEnabled] = useState(false)
const [togglingAutoRefresh, setTogglingAutoRefresh] = useState(false)
- const [refreshingMismatch, setRefreshingMismatch] = useState(false)
+ const [refreshingStale, setRefreshingStale] = useState(false)
const [purchasing, setPurchasing] = useState(false)
const [purchaseModalOpen, setPurchaseModalOpen] = useState(false)
const [purchaseCount, setPurchaseCount] = useState(50)
@@ -177,13 +177,13 @@ export default function PlatformRock() {
}
}
- // 一键刷新全部绑定错误的 Token:同步执行,单次最多 20 条
- const handleRefreshMismatch = async () => {
- setRefreshingMismatch(true)
+ // 一键刷新异常 Token:绑定错误 + 使用超时 + 绑定超时,同步执行,单次最多 20 条
+ const handleRefreshStale = async () => {
+ setRefreshingStale(true)
try {
- const res = await rockApi.refreshMismatchTokens()
+ const res = await rockApi.refreshStaleTokens()
if (res.scanned === 0) {
- message.info('暂无可刷新的绑定错误 Token(10 分钟内已刷新过的会自动跳过)')
+ message.info('暂无可刷新的异常 Token(10 分钟内已刷新过的会自动跳过)')
} else if (res.failed > 0 || res.remaining > 0) {
message.warning(res.message)
} else {
@@ -192,13 +192,13 @@ export default function PlatformRock() {
loadData()
loadTokens()
} catch (e) {
- message.error('刷新绑定错误失败: ' + (e instanceof Error ? e.message : '未知错误'))
+ message.error('一键刷新失败: ' + (e instanceof Error ? e.message : '未知错误'))
} finally {
- setRefreshingMismatch(false)
+ setRefreshingStale(false)
}
}
- // 自动刷新绑定错误开关:切换即保存,后端每 5 分钟巡检一次
+ // 自动刷新开关:与「一键刷新」同规则(绑定错误 + 使用超时 + 绑定超时),切换即保存
const handleToggleAutoRefreshMismatch = async (checked: boolean) => {
const previous = autoRefreshMismatchEnabled
setAutoRefreshMismatchEnabled(checked)
@@ -208,7 +208,7 @@ export default function PlatformRock() {
setConfig(updated)
setAutoRefreshMismatchEnabled(updated.auto_refresh_mismatch_enabled)
message.success(
- checked ? '自动刷新已开启:每 5 分钟最多刷新 20 条绑定错误 Token' : '自动刷新已关闭',
+ checked ? '自动刷新已开启:每 5 分钟按同样规则刷新最多 20 条异常 Token' : '自动刷新已关闭',
)
} catch (e) {
setAutoRefreshMismatchEnabled(previous)
@@ -476,12 +476,12 @@ export default function PlatformRock() {
totalTokens={totalTokens}
loadingTokens={loadingTokens}
autoRefreshMismatchEnabled={autoRefreshMismatchEnabled}
- refreshingMismatch={refreshingMismatch}
+ refreshingStale={refreshingStale}
togglingAutoRefresh={togglingAutoRefresh}
onSearchKeywordChange={setSearchKeyword}
onFilterStatusChange={handleFilterStatusChange}
onRefresh={loadTokens}
- onRefreshMismatch={handleRefreshMismatch}
+ onRefreshStale={handleRefreshStale}
onToggleAutoRefreshMismatch={handleToggleAutoRefreshMismatch}
/>
diff --git a/frontend/src/pages/platformRock/RockTokenFilterBar.tsx b/frontend/src/pages/platformRock/RockTokenFilterBar.tsx
index bb2904a..728a5cb 100644
--- a/frontend/src/pages/platformRock/RockTokenFilterBar.tsx
+++ b/frontend/src/pages/platformRock/RockTokenFilterBar.tsx
@@ -1,5 +1,5 @@
import { Badge, Button, Card, Input, Radio, Space, Switch, Tooltip, Typography } from 'antd'
-import { ReloadOutlined, SearchOutlined, SyncOutlined } from '@ant-design/icons'
+import { ReloadOutlined, QuestionCircleOutlined, SearchOutlined, SyncOutlined } from '@ant-design/icons'
import type { RockStats } from '../../api'
const { Text } = Typography
@@ -11,12 +11,12 @@ export interface RockTokenFilterBarProps {
totalTokens: number
loadingTokens: boolean
autoRefreshMismatchEnabled: boolean
- refreshingMismatch: boolean
+ refreshingStale: boolean
togglingAutoRefresh: boolean
onSearchKeywordChange: (value: string) => void
onFilterStatusChange: (value: string) => void
onRefresh: () => void
- onRefreshMismatch: () => void
+ onRefreshStale: () => void
onToggleAutoRefreshMismatch: (checked: boolean) => void
}
@@ -27,12 +27,12 @@ export function RockTokenFilterBar({
totalTokens,
loadingTokens,
autoRefreshMismatchEnabled,
- refreshingMismatch,
+ refreshingStale,
togglingAutoRefresh,
onSearchKeywordChange,
onFilterStatusChange,
onRefresh,
- onRefreshMismatch,
+ onRefreshStale,
onToggleAutoRefreshMismatch,
}: RockTokenFilterBarProps) {
return (
@@ -109,15 +109,24 @@ export function RockTokenFilterBar({
} onClick={onRefresh} loading={loadingTokens}>
刷新
-
- }
- onClick={onRefreshMismatch}
- loading={refreshingMismatch}
- >
- 一键刷新全部绑定错误
+
+ } onClick={onRefreshStale} loading={refreshingStale}>
+ 一键刷新
-
+
+ 同时刷新三类异常 Token:绑定错误、使用超时、绑定超时。
+
+ 单次最多 20 条,逐条间隔 1.5 秒,同一 Token 10 分钟内不重复刷新。
+
+ 「启用自动刷新」每 5 分钟按同样规则自动执行。
+
+ }
+ >
+
+
+