fix(analysis): 每日量不再懒加载;按应用统计期间显示 loading(不再假装"没有数据")
CI / Backend (go vet + test) (push) Canceled after 0s
CI / Frontend (lint + build + test) (push) Canceled after 0s

用户的三个现象,对应三个独立缺陷:

=== 1. 每日日志量"一直没数据" ===
接口只读平台库,实测 **10ms**,却仍被 inView 懒加载门控。
ref 虽然挂在 DOM 上,但滚动判定在多层布局里不可靠 → enabled 一直 false
→ 请求根本不发 → 页面显示"暂无历史数据"。
改为:只要有数据源就加载(enabled = Boolean(sourceId))。
这个接口 10ms,没有任何理由懒加载。

=== 2. 排行/流图"打开没有数据,等很久也没 loading" ===
按应用统计要 10~15 秒(读日志内容,物理下限)。
但 loading 是**整页共用**的:索引类指标 100ms 就回来、
重查询还在跑时 loading 就已经变 false,
卡片于是渲染成"没有应用维度的数据"——用户以为查完了、没数据。
改为:
  - 新增 appsLoading,专门标记"按应用统计"这段重查询
  - 卡片在 appsLoading 期间显示 spinner + "正在按应用统计…"
  - loading 与 appsLoading 都在 finally 清理(成功/失败/取消都清)

=== 3. data 为 null 时整页空白 ===
原来 `) : !data || !sourceId ? null : (` 直接 return null,
首屏或重查询异常时是**整页空白**,和"没数据"无法区分。
改为显示 spinner + "正在按应用统计(需读取日志内容,通常 10~30 秒)"。

=== 关于"用 Go 定时任务统计每日总量"===
这个机制**早就存在**(services/daily_volume.go):
  - 启动 20s 后回补 + 每 30 分钟一次 ticker
  - 用 index/stats 取整天的条数与字节,**不读日志内容**,通常秒级
  - 快照写入平台库,保留 400 天
实测当前库内已有 4 天数据(10ms 返回):
  2026-10-27   32.5 GB
  2026-10-28   23.9 GB
  2026-10-29  133.6 GB
  2026-10-30  239.3 GB
问题只在"前端没去请求",不在采集侧。

实测(df3b477e6740):每日量接口 10ms 返回 4 天,Pod 1/1 Running。
This commit is contained in:
opsx committed 2026-10-30 15:49:52 +00:00
1 parent 419a81c2af
commit a14bdb5e07
2 files changed
+24 -6

No files matched your search

@@ -75,6 +75,11 @@ export function useAnalysisMetrics({ sourceId, start, end, namespace, rankingEna
const [appLabel, setAppLabel] = useState('app')
const [data, setData] = useState<AnalysisData | null>(null)
const [loading, setLoading] = useState(false)
// appsLoading 单独跟踪"按应用统计"这段重查询(10~30 秒)。
// 它与 loading 分开的原因:上面索引类指标 100ms 就回来了,
// 如果共用一个 loading,页面会在"索引已好、app 还没好"的空档里
// 显示成"没有应用维度的数据"——用户等了很久以为没数据,其实还在算。
const [appsLoading, setAppsLoading] = useState(false)
const [error, setError] = useState('')
const [refreshTick, setRefreshTick] = useState(0)
const abortRef = useRef<AbortController | null>(null)
@@ -192,6 +197,7 @@ export function useAnalysisMetrics({ sourceId, start, end, namespace, rankingEna
setLoading(false)
return
}
setAppsLoading(true)
// 这两条并行而不是串行:实测总量 22s、error 8.6s,串行 31s,并行 22s。
// 并发上限是 4,两条并行不会撞护栏(之前串行是为了避免 5 条并发的场景)。
@@ -269,12 +275,15 @@ export function useAnalysisMetrics({ sourceId, start, end, namespace, rankingEna
if ((err as Error).name === 'AbortError') return
setError(err instanceof Error ? err.message : 'metrics failed')
} finally {
if (version === versionRef.current) setLoading(false)
if (version === versionRef.current) {
setLoading(false)
setAppsLoading(false)
}
}
})()
return () => { controller.abort() }
}, [sourceId, start, end, namespace, appLabel, refreshTick, rankingEnabled])
return { data, loading, error, appLabel, refresh }
return { data, loading, appsLoading, error, appLabel, refresh }
}
+13 -4
View File
@@ -144,7 +144,7 @@ const AnalysisPage = memo(function AnalysisPage() {
// 都依赖 by-app 数据,而它原来被底部排行卡片的可见性门控 ——
// 用户不滚到底就永远拿不到数据,表现为"三条都没有应用维度的数据"。
// 现在进页面就发,页面用两阶段渲染:索引类指标先出,app 维度到货后补。
const { data, loading, error, appLabel, refresh } = useAnalysisMetrics({
const { data, loading, appsLoading, error, appLabel, refresh } = useAnalysisMetrics({
sourceId: effectiveSourceId,
start,
end,
@@ -170,7 +170,9 @@ const AnalysisPage = memo(function AnalysisPage() {
const daily = useDailyVolume({
sourceId: effectiveSourceId,
days: dailyDays,
enabled: dailyView.inView,
// 不再按可见性懒加载:这个接口只读平台库,实测 9ms。
// 之前门控 + ref 观察器导致"滚到底也不加载",看起来就是"每日日志量一直没数据"。
enabled: Boolean(effectiveSourceId),
})
// 非 admin:菜单不可见,直访时给明确提示(后端 AdminAuth 403 兜底)
@@ -248,7 +250,14 @@ const AnalysisPage = memo(function AnalysisPage() {
<ErrorState message={error} onRetry={refresh} />
) : loading && !data ? (
<LoadingSpinner className="h-64" />
) : !data || !sourceId ? null : (
) : !data || !sourceId ? (
/* data 为 null 时原来直接 return null → 整页空白,用户以为"没数据"。
这里明确显示"正在统计"而不是什么都不画。 */
<div className="flex h-64 flex-col items-center justify-center gap-2 text-sm text-muted-foreground">
<LoadingSpinner />
<p>{t('table.computing')}</p>
</div>
) : (
<>
{/* 不再显示"告警行数"卡片:为了把它从 3 条重查询降到 2 条,
已放弃全局 warn 统计。显示一个恒为 0 的数字比不显示更误导。 */}
@@ -298,7 +307,7 @@ const AnalysisPage = memo(function AnalysisPage() {
{/* 副标题说明这块不需要读日志内容,避免和上面的排行混淆 */}
</CardHeader>
<CardContent>
{loading && !data.apps.length ? (
{(appsLoading || loading) && !data.apps.length ? (
// 这块必须按 app 分组统计(读日志内容),实测大集群 15 分钟约 20~30 秒。
// 明确告诉用户在算什么、要等多久,比空表格或整页转圈好。
<div className="flex h-40 flex-col items-center justify-center gap-2 text-sm text-muted-foreground">