- 修复RedeemView订阅刷新失败导致流程中断的问题 将订阅刷新隔离到独立try/catch,失败时仅显示警告 - 修复DataTable resize事件监听器泄漏问题 确保添加和移除使用同一个回调引用 - 修复订阅状态缓存导致强制刷新失效的问题 force=true时绕过activePromise缓存,clear()清空缓存 - 修复图表主题切换后颜色不更新的问题 添加图表ref并在主题切换时调用update()方法
141 lines
3.3 KiB
TypeScript
141 lines
3.3 KiB
TypeScript
/**
|
|
* Subscription Store
|
|
* Global state management for user subscriptions with caching and deduplication
|
|
*/
|
|
|
|
import { defineStore } from 'pinia'
|
|
import { ref, computed } from 'vue'
|
|
import subscriptionsAPI from '@/api/subscriptions'
|
|
import type { UserSubscription } from '@/types'
|
|
|
|
// Cache TTL: 60 seconds
|
|
const CACHE_TTL_MS = 60_000
|
|
|
|
// Request generation counter to invalidate stale in-flight responses
|
|
let requestGeneration = 0
|
|
|
|
export const useSubscriptionStore = defineStore('subscriptions', () => {
|
|
// State
|
|
const activeSubscriptions = ref<UserSubscription[]>([])
|
|
const loading = ref(false)
|
|
const loaded = ref(false)
|
|
const lastFetchedAt = ref<number | null>(null)
|
|
|
|
// In-flight request deduplication
|
|
let activePromise: Promise<UserSubscription[]> | null = null
|
|
|
|
// Auto-refresh interval
|
|
let pollerInterval: ReturnType<typeof setInterval> | null = null
|
|
|
|
// Computed
|
|
const hasActiveSubscriptions = computed(() => activeSubscriptions.value.length > 0)
|
|
|
|
/**
|
|
* Fetch active subscriptions with caching and deduplication
|
|
* @param force - Force refresh even if cache is valid
|
|
*/
|
|
async function fetchActiveSubscriptions(force = false): Promise<UserSubscription[]> {
|
|
const now = Date.now()
|
|
|
|
// Return cached data if valid
|
|
if (
|
|
!force &&
|
|
loaded.value &&
|
|
lastFetchedAt.value &&
|
|
now - lastFetchedAt.value < CACHE_TTL_MS
|
|
) {
|
|
return activeSubscriptions.value
|
|
}
|
|
|
|
// Return in-flight request if exists (deduplication)
|
|
if (activePromise && !force) {
|
|
return activePromise
|
|
}
|
|
|
|
const currentGeneration = ++requestGeneration
|
|
|
|
// Start new request
|
|
loading.value = true
|
|
const requestPromise = subscriptionsAPI
|
|
.getActiveSubscriptions()
|
|
.then((data) => {
|
|
if (currentGeneration === requestGeneration) {
|
|
activeSubscriptions.value = data
|
|
loaded.value = true
|
|
lastFetchedAt.value = Date.now()
|
|
}
|
|
return data
|
|
})
|
|
.catch((error) => {
|
|
console.error('Failed to fetch active subscriptions:', error)
|
|
throw error
|
|
})
|
|
.finally(() => {
|
|
if (activePromise === requestPromise) {
|
|
loading.value = false
|
|
activePromise = null
|
|
}
|
|
})
|
|
|
|
activePromise = requestPromise
|
|
|
|
return activePromise
|
|
}
|
|
|
|
/**
|
|
* Start auto-refresh polling
|
|
*/
|
|
function startPolling() {
|
|
if (pollerInterval) return
|
|
|
|
pollerInterval = setInterval(() => {
|
|
fetchActiveSubscriptions(true).catch((error) => {
|
|
console.error('Subscription polling failed:', error)
|
|
})
|
|
}, 5 * 60 * 1000)
|
|
}
|
|
|
|
/**
|
|
* Stop auto-refresh polling
|
|
*/
|
|
function stopPolling() {
|
|
if (pollerInterval) {
|
|
clearInterval(pollerInterval)
|
|
pollerInterval = null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear all subscription data and stop polling
|
|
*/
|
|
function clear() {
|
|
requestGeneration++
|
|
activePromise = null
|
|
activeSubscriptions.value = []
|
|
loaded.value = false
|
|
lastFetchedAt.value = null
|
|
stopPolling()
|
|
}
|
|
|
|
/**
|
|
* Invalidate cache (force next fetch to reload)
|
|
*/
|
|
function invalidateCache() {
|
|
lastFetchedAt.value = null
|
|
}
|
|
|
|
return {
|
|
// State
|
|
activeSubscriptions,
|
|
loading,
|
|
hasActiveSubscriptions,
|
|
|
|
// Actions
|
|
fetchActiveSubscriptions,
|
|
startPolling,
|
|
stopPolling,
|
|
clear,
|
|
invalidateCache
|
|
}
|
|
})
|