import { useCallback, useEffect, useRef, useState } from 'react' import { logsApi, type DailyVolumePoint } from '@/api/logs' export interface DailyVolumeData { points: DailyVolumePoint[] /** 历史覆盖的最早日期(用于提示"可查看范围") */ earliest: string } /** * 每日日志量(平台留存历史,不走 Loki 实时统计)。 * * 为什么要落库: * Loki retention 默认 24 小时,「近 7 天」直接查 Loki 只有 1 天有数据。 * 平台后台每 30 分钟把「昨天 / 今天」的量写进自己的库, * 所以这里读到的是真历史,能支撑 30 天 / 90 天 / 1 年的趋势。 * * 成本:一次 HTTP 读 JSON(毫秒级),不碰 Loki。 */ export function useDailyVolume({ sourceId, days, enabled = true }: { sourceId: number days: number enabled?: boolean }) { const [data, setData] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState('') const [refreshTick, setRefreshTick] = useState(0) const abortRef = useRef(null) const versionRef = useRef(0) const refresh = useCallback(() => setRefreshTick(tick => tick + 1), []) useEffect(() => { if (!sourceId || !enabled) return abortRef.current?.abort() const controller = new AbortController() abortRef.current = controller const version = ++versionRef.current void (async () => { setLoading(true) setError('') try { const points = await logsApi.dailyVolume(sourceId, days, { signal: controller.signal }) if (version !== versionRef.current) return setData({ points: points ?? [], earliest: points?.[0]?.date ?? '', }) } catch (err) { if ((err as Error).name === 'AbortError') return if (version === versionRef.current) setError(err instanceof Error ? err.message : 'daily volume failed') } finally { if (version === versionRef.current) setLoading(false) } })() return () => { controller.abort() } }, [sourceId, days, refreshTick, enabled]) return { data, loading, error, refresh } }