jettask 0.2.6__py3-none-any.whl → 0.2.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. jettask/core/cli.py +152 -0
  2. jettask/pg_consumer/sql/add_execution_time_field.sql +29 -0
  3. jettask/pg_consumer/sql/create_new_tables.sql +137 -0
  4. jettask/pg_consumer/sql/create_tables_v3.sql +175 -0
  5. jettask/pg_consumer/sql/migrate_to_new_structure.sql +179 -0
  6. jettask/pg_consumer/sql/modify_time_fields.sql +69 -0
  7. jettask/webui/frontend/package.json +30 -0
  8. jettask/webui/frontend/src/App.css +109 -0
  9. jettask/webui/frontend/src/App.jsx +66 -0
  10. jettask/webui/frontend/src/components/NamespaceSelector.jsx +166 -0
  11. jettask/webui/frontend/src/components/QueueBacklogChart.jsx +298 -0
  12. jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +638 -0
  13. jettask/webui/frontend/src/components/QueueDetailsTable.css +65 -0
  14. jettask/webui/frontend/src/components/QueueDetailsTable.jsx +487 -0
  15. jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +465 -0
  16. jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +423 -0
  17. jettask/webui/frontend/src/components/TaskFilter.jsx +425 -0
  18. jettask/webui/frontend/src/components/TimeRangeSelector.css +21 -0
  19. jettask/webui/frontend/src/components/TimeRangeSelector.jsx +160 -0
  20. jettask/webui/frontend/src/components/charts/QueueChart.jsx +111 -0
  21. jettask/webui/frontend/src/components/charts/QueueTrendChart.jsx +115 -0
  22. jettask/webui/frontend/src/components/charts/WorkerChart.jsx +40 -0
  23. jettask/webui/frontend/src/components/common/StatsCard.jsx +18 -0
  24. jettask/webui/frontend/src/components/layout/AppLayout.css +95 -0
  25. jettask/webui/frontend/src/components/layout/AppLayout.jsx +49 -0
  26. jettask/webui/frontend/src/components/layout/Header.css +106 -0
  27. jettask/webui/frontend/src/components/layout/Header.jsx +106 -0
  28. jettask/webui/frontend/src/components/layout/SideMenu.css +137 -0
  29. jettask/webui/frontend/src/components/layout/SideMenu.jsx +209 -0
  30. jettask/webui/frontend/src/components/layout/TabsNav.css +244 -0
  31. jettask/webui/frontend/src/components/layout/TabsNav.jsx +206 -0
  32. jettask/webui/frontend/src/components/layout/UserInfo.css +197 -0
  33. jettask/webui/frontend/src/components/layout/UserInfo.jsx +197 -0
  34. jettask/webui/frontend/src/contexts/LoadingContext.jsx +27 -0
  35. jettask/webui/frontend/src/contexts/NamespaceContext.jsx +72 -0
  36. jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +245 -0
  37. jettask/webui/frontend/src/index.css +114 -0
  38. jettask/webui/frontend/src/main.jsx +20 -0
  39. jettask/webui/frontend/src/pages/Alerts.jsx +684 -0
  40. jettask/webui/frontend/src/pages/Dashboard/index.css +35 -0
  41. jettask/webui/frontend/src/pages/Dashboard/index.jsx +281 -0
  42. jettask/webui/frontend/src/pages/Dashboard.jsx +1330 -0
  43. jettask/webui/frontend/src/pages/QueueDetail.jsx +1117 -0
  44. jettask/webui/frontend/src/pages/QueueMonitor.jsx +527 -0
  45. jettask/webui/frontend/src/pages/Queues.jsx +12 -0
  46. jettask/webui/frontend/src/pages/ScheduledTasks.jsx +809 -0
  47. jettask/webui/frontend/src/pages/Settings.jsx +800 -0
  48. jettask/webui/frontend/src/pages/Workers.jsx +12 -0
  49. jettask/webui/frontend/src/services/api.js +114 -0
  50. jettask/webui/frontend/src/services/queueTrend.js +152 -0
  51. jettask/webui/frontend/src/utils/suppressWarnings.js +22 -0
  52. jettask/webui/frontend/src/utils/userPreferences.js +154 -0
  53. jettask/webui/frontend/vite.config.js +26 -0
  54. {jettask-0.2.6.dist-info → jettask-0.2.8.dist-info}/METADATA +70 -2
  55. {jettask-0.2.6.dist-info → jettask-0.2.8.dist-info}/RECORD +59 -14
  56. jettask/webui/static/dist/assets/index-7129cfe1.css +0 -1
  57. jettask/webui/static/dist/assets/index-8d1935cc.js +0 -774
  58. jettask/webui/static/dist/index.html +0 -15
  59. jettask/webui/static/index.html +0 -1734
  60. jettask/webui/static/queue.html +0 -981
  61. jettask/webui/static/queues.html +0 -549
  62. jettask/webui/static/workers.html +0 -734
  63. {jettask-0.2.6.dist-info → jettask-0.2.8.dist-info}/WHEEL +0 -0
  64. {jettask-0.2.6.dist-info → jettask-0.2.8.dist-info}/entry_points.txt +0 -0
  65. {jettask-0.2.6.dist-info → jettask-0.2.8.dist-info}/licenses/LICENSE +0 -0
  66. {jettask-0.2.6.dist-info → jettask-0.2.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1330 @@
1
+ import { useState, useEffect, useCallback } from 'react';
2
+ import { Card, Row, Col, Statistic, Space, Select, Button, Tag, Spin, Empty, Tooltip, Table, Progress, Alert } from 'antd';
3
+ import {
4
+ ReloadOutlined,
5
+ InfoCircleOutlined,
6
+ RiseOutlined,
7
+ FallOutlined,
8
+ ArrowUpOutlined,
9
+ ArrowDownOutlined,
10
+ ClockCircleOutlined,
11
+ CheckCircleOutlined,
12
+ SyncOutlined,
13
+ ExclamationCircleOutlined,
14
+ DashboardOutlined,
15
+ DatabaseOutlined,
16
+ CloudServerOutlined,
17
+ ThunderboltOutlined,
18
+ RocketOutlined,
19
+ TeamOutlined,
20
+ WarningOutlined,
21
+ PieChartOutlined
22
+ } from '@ant-design/icons';
23
+ import { Line, Area, Column, Pie, Gauge } from '@ant-design/plots';
24
+ import { useNamespace } from '../contexts/NamespaceContext';
25
+ import dayjs from 'dayjs';
26
+ import axios from 'axios';
27
+ import QueueBacklogTrend from '../components/QueueBacklogTrend';
28
+
29
+ const { Option } = Select;
30
+
31
+ // 时间范围选项
32
+ const TIME_RANGES = {
33
+ '15m': '15分钟',
34
+ '30m': '30分钟',
35
+ '1h': '1小时',
36
+ '3h': '3小时',
37
+ '6h': '6小时',
38
+ '12h': '12小时',
39
+ '24h': '24小时',
40
+ '7d': '7天'
41
+ };
42
+
43
+ function Dashboard() {
44
+ const { currentNamespace } = useNamespace();
45
+
46
+ // 添加调试输出,直接显示在页面上
47
+ console.log('🔧 Dashboard组件渲染,currentNamespace:', currentNamespace);
48
+ const [loading, setLoading] = useState(false);
49
+ const [timeRange, setTimeRange] = useState('1h');
50
+ const [autoRefresh, setAutoRefresh] = useState(true);
51
+ const [refreshInterval] = useState(30000); // 30秒自动刷新
52
+ const [granularity, setGranularity] = useState('');
53
+ const [hasAnimated, setHasAnimated] = useState(false); // 跟踪是否已经播放过首次动画
54
+
55
+ // 队列筛选相关状态
56
+ const [selectedQueues, setSelectedQueues] = useState([]); // 选中的队列列表,空数组表示全部队列
57
+ const [availableQueues, setAvailableQueues] = useState([]); // 可用的队列列表
58
+
59
+ // 核心统计数据
60
+ const [coreStats, setCoreStats] = useState({
61
+ totalQueues: 0,
62
+ totalTasks: 0,
63
+ runningTasks: 0,
64
+ completedTasks: 0,
65
+ failedTasks: 0,
66
+ pendingTasks: 0,
67
+ successRate: 0,
68
+ throughput: 0,
69
+ avgProcessingTime: 0,
70
+ });
71
+
72
+ // 系统健康状态(仅保留必要字段)
73
+ const [systemHealth, setSystemHealth] = useState({
74
+ redis: 'healthy',
75
+ postgresql: 'healthy',
76
+ });
77
+
78
+ // Redis监控数据
79
+ const [redisMonitor, setRedisMonitor] = useState({
80
+ status: 'healthy',
81
+ memory: {
82
+ used_memory: 0,
83
+ used_memory_human: '0B',
84
+ usage_percentage: null,
85
+ maxmemory: null,
86
+ maxmemory_human: '0B',
87
+ mem_fragmentation_ratio: 1.0
88
+ },
89
+ clients: {
90
+ connected_clients: 0,
91
+ blocked_clients: 0
92
+ },
93
+ stats: {
94
+ instantaneous_ops_per_sec: 0,
95
+ hit_rate: 0,
96
+ keyspace_hits: 0,
97
+ keyspace_misses: 0
98
+ },
99
+ keyspace: {
100
+ total_keys: 0
101
+ },
102
+ server: {
103
+ redis_version: 'unknown',
104
+ uptime_in_seconds: 0
105
+ }
106
+ });
107
+ const [redisLoading, setRedisLoading] = useState(false);
108
+
109
+ // 任务并发数趋势(小图表)
110
+ const [concurrencyData, setConcurrencyData] = useState([]);
111
+
112
+ // 任务执行趋势(大图表)
113
+ const [taskTrendData, setTaskTrendData] = useState([]);
114
+
115
+ // 任务处理时间趋势
116
+ const [processingTimeData, setProcessingTimeData] = useState([]);
117
+ const [processingTimeLoading, setProcessingTimeLoading] = useState(false);
118
+
119
+ // 排队任务数趋势
120
+ const [queuedTasksData, setQueuedTasksData] = useState([]);
121
+
122
+ // 创建任务延时趋势
123
+ const [createLatencyData, setCreateLatencyData] = useState([]);
124
+
125
+
126
+ // 队列积压Top10
127
+ const [topBacklogQueues, setTopBacklogQueues] = useState([]);
128
+
129
+ // 错误率Top10
130
+ const [topErrorQueues, setTopErrorQueues] = useState([]);
131
+
132
+ // 任务分布数据
133
+ const [taskDistribution, setTaskDistribution] = useState([]);
134
+
135
+ // fetchOverviewData 已移动到统一的 fetchData 函数中
136
+
137
+ // 获取任务处理时间数据 - 已被 fetchOverviewData 统一替代
138
+ // const fetchProcessingTimeData = useCallback(async () => {
139
+ // // 此函数已被 fetchOverviewData 替代,保留用于兼容性
140
+ // }, [currentNamespace, timeRange]);
141
+
142
+ // 生成模拟的处理时间数据(作为备用)
143
+ const generateMockProcessingTimeData = useCallback(() => {
144
+ const now = dayjs();
145
+ const points = 20;
146
+ const processingTime = [];
147
+
148
+ for (let i = points - 1; i >= 0; i--) {
149
+ const time = now.subtract(i * 2, 'minute');
150
+ const timeStr = time.format('HH:mm');
151
+ const baseTime = Math.random() * 1000 + 500;
152
+
153
+ processingTime.push({
154
+ time: timeStr,
155
+ value: baseTime * 1.8,
156
+ metric: 'P90处理时间',
157
+ });
158
+ processingTime.push({
159
+ time: timeStr,
160
+ value: baseTime,
161
+ metric: 'P50处理时间',
162
+ });
163
+ processingTime.push({
164
+ time: timeStr,
165
+ value: baseTime * 1.2,
166
+ metric: '平均处理时间',
167
+ });
168
+ }
169
+ setProcessingTimeData(processingTime);
170
+ }, []);
171
+ // fetchRedisMonitor 已移动到统一的 fetchData 函数中
172
+
173
+ // fetchSystemOverview 已移动到统一的 fetchData 函数中
174
+
175
+ // fetchQueuedTasksData 已移动到统一的 fetchData 函数中
176
+
177
+ // 获取任务趋势数据(大图表)- 已被 fetchOverviewData 统一替代
178
+ // const fetchTaskTrendData = useCallback(async () => {
179
+ // // 此函数已被 fetchOverviewData 替代,保留用于兼容性
180
+ // }, [currentNamespace, timeRange]);
181
+
182
+ // 获取任务并发数数据(小图表)- 已被 fetchOverviewData 统一替代
183
+ // const fetchConcurrencyData = useCallback(async () => {
184
+ // // 此函数已被 fetchOverviewData 替代,保留用于兼容性
185
+ // }, [currentNamespace, timeRange]);
186
+
187
+ // 获取任务状态分布数据 - 已移动到fetchSystemOverview中,避免重复请求
188
+
189
+ // 生成模拟的任务分布数据(作为备用)
190
+ const generateMockTaskDistribution = useCallback(() => {
191
+ const mockData = [
192
+ { type: '队列1 (成功)', value: 45, queue: 'queue1', status: 'success' },
193
+ { type: '队列1 (失败)', value: 5, queue: 'queue1', status: 'error' },
194
+ { type: '队列2 (成功)', value: 30, queue: 'queue2', status: 'success' },
195
+ { type: '队列3 (成功)', value: 20, queue: 'queue3', status: 'success' }
196
+ ];
197
+ setTaskDistribution(mockData);
198
+ }, []);
199
+
200
+ // 生成模拟的任务趋势数据(作为备用)
201
+ const generateMockTaskTrendData = useCallback(() => {
202
+ const now = dayjs();
203
+ const points = 50; // 数据点数
204
+ const taskTrend = [];
205
+ for (let i = points - 1; i >= 0; i--) {
206
+ const time = now.subtract(i * 2, 'minute');
207
+ const timeStr = time.format('HH:mm');
208
+ taskTrend.push({
209
+ time: timeStr,
210
+ value: Math.floor(Math.random() * 100) + 50,
211
+ type: '创建',
212
+ });
213
+ taskTrend.push({
214
+ time: timeStr,
215
+ value: Math.floor(Math.random() * 90) + 45,
216
+ type: '完成',
217
+ });
218
+ taskTrend.push({
219
+ time: timeStr,
220
+ value: Math.floor(Math.random() * 10) + 2,
221
+ type: '失败',
222
+ });
223
+ }
224
+ setTaskTrendData(taskTrend);
225
+ }, []);
226
+
227
+ // 生成模拟的并发数据(作为备用)
228
+ const generateMockConcurrencyData = useCallback(() => {
229
+ const now = dayjs();
230
+ const points = 20;
231
+ const concurrency = [];
232
+ for (let i = points - 1; i >= 0; i--) {
233
+ const time = now.subtract(i * 2, 'minute');
234
+ concurrency.push({
235
+ time: time.format('HH:mm'),
236
+ value: Math.floor(Math.random() * 5) + 2,
237
+ type: '并发数',
238
+ });
239
+ }
240
+ setConcurrencyData(concurrency);
241
+ }, []);
242
+
243
+ // 获取任务创建延时数据 - 已被 fetchOverviewData 统一替代
244
+ // const fetchCreateLatencyData = useCallback(async () => {
245
+ // // 此函数已被 fetchOverviewData 替代,保留用于兼容性
246
+ // }, [currentNamespace, timeRange]);
247
+
248
+
249
+
250
+ // 生成模拟的排队任务数数据(作为备用)
251
+ const generateMockQueuedTasksData = useCallback(() => {
252
+ const now = dayjs();
253
+ const points = 20;
254
+ const queued = [];
255
+ for (let i = points - 1; i >= 0; i--) {
256
+ const time = now.subtract(i * 2, 'minute');
257
+ const timeStr = time.format('HH:mm');
258
+ queued.push({
259
+ time: timeStr,
260
+ value: Math.floor(Math.random() * 10) + 5,
261
+ type: '排队任务数',
262
+ });
263
+ }
264
+ setQueuedTasksData(queued);
265
+ }, []);
266
+
267
+ // 生成模拟的创建延时数据(作为备用)
268
+ const generateMockCreateLatencyData = useCallback(() => {
269
+ const now = dayjs();
270
+ const points = 20;
271
+ const createLatency = [];
272
+ for (let i = points - 1; i >= 0; i--) {
273
+ const time = now.subtract(i * 2, 'minute');
274
+ const timeStr = time.format('HH:mm');
275
+ createLatency.push({
276
+ time: timeStr,
277
+ value: Math.floor(Math.random() * 100) + 50,
278
+ type: '创建延时',
279
+ });
280
+ }
281
+ setCreateLatencyData(createLatency);
282
+ }, []);
283
+
284
+ // 生成模拟系统状态数据
285
+ const generateMockData = useCallback(() => {
286
+ // 任务趋势数据现在通过fetchTaskTrendData获取,不在这里生成
287
+ // 任务处理时间数据现在通过fetchProcessingTimeData获取,不在这里生成
288
+ // 排队任务数和创建延时数据现在通过对应的API获取,不在这里生成
289
+
290
+ // 系统健康状态(模拟PostgreSQL状态)
291
+ setSystemHealth(prev => ({
292
+ ...prev,
293
+ postgresql: 'healthy'
294
+ }));
295
+ }, []);
296
+
297
+ // 获取所有数据 - 统一在一个函数中避免重复请求
298
+ const fetchData = useCallback(async () => {
299
+ setLoading(true);
300
+ try {
301
+ const namespace = currentNamespace || 'default';
302
+
303
+ // 构建队列筛选参数(空数组表示全部队列)
304
+ const queueParams = selectedQueues.length > 0
305
+ ? `&queues=${selectedQueues.join(',')}`
306
+ : '';
307
+
308
+ console.log('🔄 开始获取Dashboard所有数据...');
309
+ console.log('📊 当前命名空间:', currentNamespace);
310
+ console.log('📊 使用的命名空间:', namespace);
311
+ console.log('📊 选中的队列:', selectedQueues);
312
+ console.log('📊 队列筛选参数:', queueParams);
313
+
314
+ // 并行获取所有数据,避免重复请求
315
+ const [
316
+ dashboardStatsResponse,
317
+ queuesResponse,
318
+ backlogResponse,
319
+ errorResponse,
320
+ overviewResponse,
321
+ queueBacklogResponse,
322
+ redisResponse
323
+ ] = await Promise.all([
324
+ axios.get(`/api/data/dashboard-stats/${namespace}?time_range=${timeRange}${queueParams}`),
325
+ axios.get(`/api/queues/${namespace}`),
326
+ axios.get(`/api/data/top-backlog-queues/${namespace}?limit=10&time_range=${timeRange}${queueParams}`),
327
+ axios.get(`/api/data/top-error-queues/${namespace}?limit=10&time_range=${timeRange}${queueParams}`),
328
+ axios.post(`/api/data/dashboard-overview-stats/${namespace}`, { time_range: timeRange, queues: selectedQueues.length > 0 ? selectedQueues : undefined }),
329
+ axios.get(`/api/data/queue-backlog-trend/${namespace}?time_range=${timeRange}${queueParams}`),
330
+ axios.get(`/api/redis/monitor/${namespace}`)
331
+ ]);
332
+
333
+ console.log('✅ 所有API请求完成,开始处理数据...');
334
+
335
+ // 处理队列列表数据(用于筛选器)
336
+ console.log('📊 队列响应数据:', queuesResponse.data);
337
+ if (queuesResponse.data) {
338
+ // 检查返回数据的格式
339
+ if (queuesResponse.data.success && Array.isArray(queuesResponse.data.data)) {
340
+ // 格式: {success: true, data: ['queue1', 'queue2', ...]}
341
+ const queueNames = queuesResponse.data.data;
342
+ console.log('📊 可用队列列表 (format 1):', queueNames);
343
+ setAvailableQueues(queueNames);
344
+ } else if (Array.isArray(queuesResponse.data)) {
345
+ // 格式: [{queue_name: 'queue1'}, {queue_name: 'queue2'}, ...]
346
+ const queueNames = queuesResponse.data.map(queue => queue.queue_name || queue);
347
+ console.log('📊 可用队列列表 (format 2):', queueNames);
348
+ setAvailableQueues(queueNames);
349
+ } else {
350
+ console.warn('📊 队列数据格式异常:', queuesResponse.data);
351
+ }
352
+ }
353
+
354
+ // 处理仪表板统计数据
355
+ if (dashboardStatsResponse.data.success) {
356
+ const stats = dashboardStatsResponse.data.data;
357
+ setCoreStats({
358
+ totalQueues: stats.total_queues,
359
+ totalTasks: stats.total_tasks,
360
+ runningTasks: stats.running_tasks,
361
+ completedTasks: stats.completed_tasks,
362
+ failedTasks: stats.failed_tasks,
363
+ pendingTasks: stats.pending_tasks,
364
+ successRate: stats.success_rate,
365
+ throughput: stats.throughput,
366
+ avgProcessingTime: stats.avg_processing_time,
367
+ });
368
+ }
369
+
370
+ // 处理任务分布数据(现在从dashboardStatsResponse中获取,只按队列分组)
371
+ if (dashboardStatsResponse.data.success && dashboardStatsResponse.data.data.task_distribution) {
372
+ const distributionData = dashboardStatsResponse.data.data.task_distribution;
373
+ const pieData = distributionData.map(item => ({
374
+ type: item.type, // 直接使用队列名
375
+ value: item.value
376
+ })).filter(item => item.value > 0);
377
+
378
+ if (pieData.length === 0) {
379
+ pieData.push({ type: '暂无数据', value: 1 });
380
+ }
381
+ setTaskDistribution(pieData);
382
+ }
383
+
384
+ // 处理排行榜数据
385
+ if (backlogResponse.data.success) {
386
+ setTopBacklogQueues(backlogResponse.data.data || []);
387
+ }
388
+ if (errorResponse.data.success) {
389
+ setTopErrorQueues(errorResponse.data.data || []);
390
+ }
391
+
392
+ // 处理概览统计数据(任务趋势、并发等)
393
+ if (overviewResponse.data) {
394
+ const data = overviewResponse.data;
395
+ const taskTrendData = data.task_trend || [];
396
+ const concurrencyData = data.concurrency || [];
397
+ const processingTimeData = data.processing_time || [];
398
+ const creationLatencyData = data.creation_latency || [];
399
+
400
+ if (taskTrendData.length > 0) {
401
+ setTaskTrendData(taskTrendData);
402
+ }
403
+ if (concurrencyData.length > 0) {
404
+ setConcurrencyData(concurrencyData);
405
+ }
406
+ if (processingTimeData.length > 0) {
407
+ setProcessingTimeData(processingTimeData);
408
+ }
409
+ if (creationLatencyData.length > 0) {
410
+ setCreateLatencyData(creationLatencyData);
411
+ }
412
+ if (data.granularity) {
413
+ setGranularity(data.granularity);
414
+ }
415
+ }
416
+
417
+ // 处理队列积压趋势数据
418
+ if (queueBacklogResponse.data && queueBacklogResponse.data.data) {
419
+ setQueuedTasksData(queueBacklogResponse.data.data);
420
+ }
421
+
422
+ // 处理Redis监控数据
423
+ if (redisResponse.data.success) {
424
+ const redisData = redisResponse.data.data;
425
+ setRedisMonitor(redisData);
426
+ setSystemHealth(prev => ({
427
+ ...prev,
428
+ redis: redisData.status
429
+ }));
430
+ }
431
+
432
+ // 设置PostgreSQL状态(模拟)
433
+ setSystemHealth(prev => ({
434
+ ...prev,
435
+ postgresql: 'healthy'
436
+ }));
437
+
438
+ console.log('🎉 Dashboard数据获取和处理完成');
439
+
440
+ } catch (error) {
441
+ console.error('❌ 获取Dashboard数据失败:', error);
442
+
443
+ // 显示服务端返回的错误消息
444
+ const errorMessage = error.response?.data?.detail || error.message || '获取概览数据失败';
445
+ message.error(errorMessage);
446
+
447
+ // 设置错误状态
448
+ setSystemHealth(prev => ({
449
+ ...prev,
450
+ redis: 'error'
451
+ }));
452
+ } finally {
453
+ setLoading(false);
454
+ // 首次加载完成后,标记已经播放过动画
455
+ if (!hasAnimated) {
456
+ setTimeout(() => {
457
+ setHasAnimated(true);
458
+ // 移除强制重新渲染,避免图表闪烁
459
+ }, 1500); // 等待动画播放完成后再标记
460
+ } else {
461
+ // 非首次加载时,更新key强制图表重新渲染(但无动画)
462
+ setAnimationKey(prev => prev + 1);
463
+ }
464
+ }
465
+ }, [currentNamespace, timeRange, selectedQueues, hasAnimated]);
466
+
467
+ // 监听关键状态变化,统一触发数据获取
468
+ useEffect(() => {
469
+ console.log('🔄 关键状态变化触发数据获取');
470
+ console.log('📊 currentNamespace:', currentNamespace);
471
+ console.log('📊 timeRange:', timeRange);
472
+ console.log('📊 selectedQueues:', selectedQueues);
473
+
474
+ if (currentNamespace) {
475
+ fetchData();
476
+ }
477
+ }, [currentNamespace, timeRange, selectedQueues, fetchData]);
478
+
479
+ // 自动刷新逻辑(不包含初始化调用,避免与第一个useEffect重复)
480
+ useEffect(() => {
481
+ if (autoRefresh) {
482
+ const timer = setInterval(fetchData, refreshInterval);
483
+ return () => clearInterval(timer);
484
+ }
485
+ }, [autoRefresh, refreshInterval, fetchData]);
486
+
487
+ // 创建一个动画key来强制图表重新渲染
488
+ const [animationKey, setAnimationKey] = useState(0);
489
+
490
+ // 根据是否应该播放动画返回配置
491
+ const getAnimateConfig = (enterConfig) => {
492
+ // 只在首次渲染且未播放过动画时启用动画
493
+ if (hasAnimated) {
494
+ return false; // 已经播放过动画,后续不再播放
495
+ }
496
+ return {
497
+ enter: enterConfig,
498
+ update: {
499
+ type: 'morphing',
500
+ duration: 500,
501
+ easing: 'ease-in-out'
502
+ }
503
+ };
504
+ };
505
+
506
+ // 图表通用配置
507
+ const commonChartConfig = {
508
+ // 动画配置(仅首次加载时生效)
509
+ animate: getAnimateConfig({
510
+ type: 'fadeIn',
511
+ duration: 800,
512
+ delay: 100,
513
+ easing: 'ease-out'
514
+ }),
515
+ smooth: true,
516
+ xField: (d) => new Date(d.time),
517
+ height: 200,
518
+ padding: 'auto',
519
+ xAxis: {
520
+ label: {
521
+ autoRotate: false,
522
+ autoHide: true,
523
+ },
524
+ },
525
+ yAxis: {
526
+ label: {
527
+ formatter: (v) => {
528
+ if (v >= 1000) return `${(v / 1000).toFixed(1)}k`;
529
+ return v;
530
+ },
531
+ },
532
+ },
533
+ connectNulls: {
534
+ connect: true,
535
+ connectStroke: '#aaa',
536
+ },
537
+ axis: {
538
+ x: {
539
+ labelFormatter: (text) => {
540
+ const date = dayjs(text);
541
+ switch (granularity) {
542
+ case 'second':
543
+ return date.format('HH:mm:ss');
544
+ case 'minute':
545
+ return date.format('HH:mm');
546
+ case 'hour':
547
+ return date.format('MM-DD HH:mm');
548
+ case 'day':
549
+ return date.format('YYYY-MM-DD');
550
+ default:
551
+ return date.format('MM-DD HH:mm');
552
+ }
553
+ },
554
+ labelAutoRotate: true,
555
+ },
556
+ },
557
+ legend: {
558
+ position: 'top',
559
+ itemName: {
560
+ style: {
561
+ fontSize: 12,
562
+ },
563
+ },
564
+ },
565
+ style: {
566
+ lineWidth: 2,
567
+ },
568
+ colorField: 'metric', // 使用metric字段区分不同的线
569
+ yField: 'value',
570
+ };
571
+
572
+ // 任务并发数配置(小图表)
573
+ const miniConcurrencyConfig = {
574
+ ...commonChartConfig,
575
+ data: concurrencyData,
576
+ yField: 'value',
577
+ smooth: true,
578
+ color: '#5B8FF9',
579
+ // 面积图特定的动画效果(仅首次加载时生效)
580
+ animate: getAnimateConfig({
581
+ type: 'growInY',
582
+ duration: 1000,
583
+ delay: 200,
584
+ easing: 'ease-out'
585
+ }),
586
+ areaStyle: {
587
+ fill: 'l(270) 0:#ffffff 0.5:#7ec8f7 1:#5B8FF9',
588
+ },
589
+ axis: {
590
+ x: {
591
+ labelFormatter: (text) => {
592
+ const date = dayjs(text);
593
+ switch (granularity) {
594
+ case 'second':
595
+ return date.format('HH:mm:ss');
596
+ case 'minute':
597
+ return date.format('HH:mm');
598
+ case 'hour':
599
+ return date.format('MM-DD HH:mm');
600
+ case 'day':
601
+ return date.format('YYYY-MM-DD');
602
+ default:
603
+ return date.format('MM-DD HH:mm');
604
+ }
605
+ },
606
+ labelAutoRotate: true,
607
+ },
608
+ },
609
+ };
610
+
611
+ // 任务执行趋势配置(大图表)
612
+ const taskTrendConfig = {
613
+ ...commonChartConfig,
614
+ data: taskTrendData,
615
+ yField: 'value',
616
+ colorField: 'metric', // 使用metric字段区分不同的线
617
+ // 线图特定的动画效果(仅首次加载时生效)
618
+ animate: getAnimateConfig({
619
+ type: 'pathIn',
620
+ duration: 1200,
621
+ delay: 300,
622
+ easing: 'ease-out'
623
+ }),
624
+ // 配置颜色映射
625
+ scale: {
626
+ color: {
627
+ domain: ['入队速率', '完成速率', '失败数'],
628
+ range: ['#1890ff', '#52c41a', '#ff4d4f'], // 蓝色(入队)、绿色(完成)、红色(失败)
629
+ },
630
+ y: { nice: true },
631
+ },
632
+ };
633
+
634
+ // 任务处理时间配置
635
+ const processingTimeConfig = {
636
+ ...commonChartConfig,
637
+ data: processingTimeData,
638
+ // 处理时间图表动画效果(仅首次加载时生效)
639
+ animate: getAnimateConfig({
640
+ type: 'waveIn',
641
+ duration: 1000,
642
+ delay: 400,
643
+ easing: 'ease-out'
644
+ }),
645
+ // 配置颜色映射
646
+ scale: {
647
+ color: {
648
+ domain: ['P90处理时间', 'P50处理时间', '平均处理时间'],
649
+ range: ['#1890ff', '#52c41a', '#ff4d4f'], // 蓝色、绿色、红色
650
+ },
651
+ y: { nice: true },
652
+ },
653
+ };
654
+
655
+ // 排队任务数配置
656
+ const queuedTasksConfig = {
657
+ ...commonChartConfig,
658
+ data: queuedTasksData,
659
+ yField: 'value',
660
+ smooth: true,
661
+ colorField: 'metric', // 使用metric字段区分不同的线
662
+ // 排队任务数动画效果(仅首次加载时生效)
663
+ animate: getAnimateConfig({
664
+ type: 'scaleInY',
665
+ duration: 900,
666
+ delay: 500,
667
+ easing: 'ease-out'
668
+ }),
669
+ };
670
+
671
+ // 创建任务延时配置
672
+ const createLatencyConfig = {
673
+ ...commonChartConfig,
674
+ data: createLatencyData,
675
+ yField: 'value',
676
+ // 延时图表动画效果(仅首次加载时生效)
677
+ animate: getAnimateConfig({
678
+ type: 'zoomIn',
679
+ duration: 800,
680
+ delay: 600,
681
+ easing: 'ease-out'
682
+ }),
683
+ };
684
+
685
+
686
+ // 任务分布饼图配置(按照官方示例简化)
687
+ const pieConfig = {
688
+ data: taskDistribution,
689
+ angleField: 'value',
690
+ colorField: 'type',
691
+ height: 210,
692
+ // 饼图专属动画效果(仅首次加载时生效)
693
+ animate: getAnimateConfig({
694
+ type: 'waveIn',
695
+ duration: 1200,
696
+ delay: 100,
697
+ easing: 'ease-out'
698
+ }),
699
+ label: {
700
+ text: 'value',
701
+ style: {
702
+ fontWeight: 'bold',
703
+ },
704
+ },
705
+ tooltip: {
706
+ title: 'type', // 标题
707
+ items: ['value'], // 数据项
708
+ }
709
+ };
710
+
711
+ return (
712
+ <div style={{ padding: '16px', backgroundColor: '#f0f2f5', minHeight: '100vh' }}>
713
+ {/* 页面标题和操作栏 */}
714
+ <div style={{ marginBottom: 16, backgroundColor: 'white', padding: '16px', borderRadius: '4px' }}>
715
+ <Row justify="space-between" align="middle">
716
+ <Col>
717
+ <Space>
718
+ <DashboardOutlined style={{ fontSize: 20 }} />
719
+ <span style={{ fontSize: 18, fontWeight: 500 }}>系统概览</span>
720
+ </Space>
721
+ </Col>
722
+ <Col>
723
+ <Space>
724
+ <Select
725
+ mode="multiple"
726
+ placeholder={`全部队列 (${availableQueues.length}个可用)`}
727
+ value={selectedQueues}
728
+ onChange={setSelectedQueues}
729
+ style={{ minWidth: 200 }}
730
+ maxTagCount={1}
731
+ maxTagPlaceholder={(omittedValues) => `+${omittedValues.length}个`}
732
+ allowClear
733
+ showSearch
734
+ optionFilterProp="children"
735
+ dropdownRender={menu => (
736
+ <div>
737
+ {availableQueues.length === 0 ? (
738
+ <div style={{ padding: '8px 12px', color: '#999' }}>
739
+ 暂无队列数据
740
+ </div>
741
+ ) : menu}
742
+ </div>
743
+ )}
744
+ >
745
+ {availableQueues.map(queue => (
746
+ <Option key={queue} value={queue}>{queue}</Option>
747
+ ))}
748
+ </Select>
749
+ <Select value={timeRange} onChange={setTimeRange} style={{ width: 100 }}>
750
+ {Object.entries(TIME_RANGES).map(([key, label]) => (
751
+ <Option key={key} value={key}>{label}</Option>
752
+ ))}
753
+ </Select>
754
+ <Button
755
+ icon={<ReloadOutlined />}
756
+ onClick={fetchData}
757
+ loading={loading}
758
+ >
759
+ 刷新
760
+ </Button>
761
+ </Space>
762
+ </Col>
763
+ </Row>
764
+ </div>
765
+
766
+ {/* 第一行:核心指标卡片 */}
767
+ <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
768
+ <Col xs={24} sm={12} md={6}>
769
+ <Card size="small" style={{ height: 100 }}>
770
+ <Row style={{ height: '100%' }}>
771
+ <Col span={12}>
772
+ <Statistic
773
+ title="队列总数"
774
+ value={coreStats.totalQueues}
775
+ prefix={<DatabaseOutlined />}
776
+ valueStyle={{ color: '#1890ff' }}
777
+ />
778
+ </Col>
779
+ <Col span={12}>
780
+ <div style={{ padding: '8px 0', fontSize: 12, height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
781
+ <div style={{ textAlign: 'center', color: '#666' }}>
782
+ 系统队列总数
783
+ </div>
784
+ </div>
785
+ </Col>
786
+ </Row>
787
+ </Card>
788
+ </Col>
789
+ <Col xs={24} sm={12} md={6}>
790
+ <Card size="small" style={{ height: 100 }}>
791
+ <Row style={{ height: '100%' }}>
792
+ <Col span={12}>
793
+ <Statistic
794
+ title="任务总数"
795
+ value={coreStats.totalTasks}
796
+ prefix={<RocketOutlined />}
797
+ valueStyle={{ color: '#52c41a' }}
798
+ />
799
+ </Col>
800
+ <Col span={12}>
801
+ <div style={{ padding: '8px 0', fontSize: 12, height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
802
+ <div style={{ marginBottom: 4 }}>
803
+ 已完成: <span style={{ color: '#52c41a', fontWeight: 'bold' }}>{coreStats.completedTasks}</span>
804
+ </div>
805
+ <div style={{ marginBottom: 4 }}>
806
+ 处理中: <span style={{ color: '#1890ff', fontWeight: 'bold' }}>{coreStats.runningTasks}</span>
807
+ </div>
808
+ <div>
809
+ 等待中: <span style={{ color: '#faad14', fontWeight: 'bold' }}>{coreStats.pendingTasks}</span>
810
+ </div>
811
+ </div>
812
+ </Col>
813
+ </Row>
814
+ </Card>
815
+ </Col>
816
+ <Col xs={24} sm={12} md={6}>
817
+ <Card size="small" style={{ height: 100 }}>
818
+ <Row style={{ height: '100%' }}>
819
+ <Col span={12}>
820
+ <Statistic
821
+ title="成功率"
822
+ value={coreStats.successRate}
823
+ suffix="%"
824
+ prefix={<CheckCircleOutlined />}
825
+ valueStyle={{ color: coreStats.successRate >= 95 ? '#52c41a' : '#faad14' }}
826
+ />
827
+ </Col>
828
+ <Col span={12}>
829
+ <div style={{ padding: '8px 0', fontSize: 12, height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
830
+ <div style={{ marginBottom: 4 }}>
831
+ 失败: <span style={{ color: '#ff4d4f', fontWeight: 'bold' }}>{coreStats.failedTasks}</span>
832
+ </div>
833
+ <div>
834
+ 错误率: <span style={{ color: '#ff4d4f', fontWeight: 'bold' }}>
835
+ {coreStats.totalTasks > 0 ? ((coreStats.failedTasks / coreStats.totalTasks) * 100).toFixed(1) : 0}%
836
+ </span>
837
+ </div>
838
+ </div>
839
+ </Col>
840
+ </Row>
841
+ </Card>
842
+ </Col>
843
+ <Col xs={24} sm={12} md={6}>
844
+ <Card size="small" style={{ height: 100 }}>
845
+ <Row style={{ height: '100%' }}>
846
+ <Col span={12}>
847
+ <Statistic
848
+ title={
849
+ <Space>
850
+ <span>吞吐量</span>
851
+ <Tooltip title="每分钟处理的任务数,基于最近几分钟内完成的任务计算">
852
+ <InfoCircleOutlined style={{ fontSize: 12, color: '#999' }} />
853
+ </Tooltip>
854
+ </Space>
855
+ }
856
+ value={coreStats.throughput}
857
+ suffix="/min"
858
+ prefix={<ThunderboltOutlined />}
859
+ valueStyle={{ color: '#722ed1' }}
860
+ precision={1}
861
+ />
862
+ </Col>
863
+ <Col span={12}>
864
+ <div style={{ padding: '8px 0', fontSize: 12, height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
865
+ <div style={{ marginBottom: 4 }}>
866
+ 平均处理时间:
867
+ </div>
868
+ <div style={{ color: '#722ed1', fontWeight: 'bold', fontSize: 14 }}>
869
+ {coreStats.avgProcessingTime ? `${coreStats.avgProcessingTime}ms` : 'N/A'}
870
+ </div>
871
+ </div>
872
+ </Col>
873
+ </Row>
874
+ </Card>
875
+ </Col>
876
+ </Row>
877
+
878
+ {/* 第二行:图表展示 */}
879
+ <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
880
+ <Col xs={24} md={12}>
881
+ <Card
882
+ size="small"
883
+ title={
884
+ <Space>
885
+ <span>任务处理趋势</span>
886
+ <Tooltip title="任务处理趋势">
887
+ <InfoCircleOutlined style={{ fontSize: 12, color: '#999' }} />
888
+ </Tooltip>
889
+ </Space>
890
+ }
891
+ >
892
+ <Line
893
+ key={`task-trend-${taskTrendData.length}-${animationKey}`}
894
+ {...taskTrendConfig}
895
+ />
896
+ </Card>
897
+ </Col>
898
+ <Col xs={24} md={12}>
899
+ <Card
900
+ size="small"
901
+ title={
902
+ <Space>
903
+ <PieChartOutlined style={{ color: '#1890ff' }} />
904
+ <span>任务数量分布</span>
905
+ </Space>
906
+ }
907
+ >
908
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '205px' }}>
909
+ <Pie
910
+ key={`task-distribution-${taskDistribution.length}-${animationKey}`}
911
+ {...pieConfig}
912
+ />
913
+ </div>
914
+ </Card>
915
+ </Col>
916
+ </Row>
917
+
918
+ {/* 第三行:并发监控 */}
919
+ <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
920
+ <Col xs={24} md={12}>
921
+ <Card
922
+ size="small"
923
+ title={
924
+ <Space>
925
+ <span>任务并发数量</span>
926
+ <Tooltip title="任务并发数量">
927
+ <InfoCircleOutlined style={{ fontSize: 12, color: '#999' }} />
928
+ </Tooltip>
929
+ </Space>
930
+ }
931
+ >
932
+ <Area {...miniConcurrencyConfig} height={200} />
933
+ </Card>
934
+ </Col>
935
+
936
+ <Col xs={24} md={12}>
937
+ <Card
938
+ size="small"
939
+ title={
940
+ <Space>
941
+ <span>任务处理时间</span>
942
+ <Tooltip title="任务从开始到完成的时间">
943
+ <InfoCircleOutlined style={{ fontSize: 12, color: '#999' }} />
944
+ </Tooltip>
945
+ {processingTimeLoading && <SyncOutlined spin />}
946
+ </Space>
947
+ }
948
+ extra={
949
+ <Button
950
+ size="small"
951
+ icon={<ReloadOutlined />}
952
+ onClick={fetchData}
953
+ loading={processingTimeLoading}
954
+ >
955
+ 刷新
956
+ </Button>
957
+ }
958
+ bodyStyle={{ padding: '12px' }}
959
+ >
960
+ {processingTimeLoading ? (
961
+ <div style={{
962
+ height: '200px',
963
+ display: 'flex',
964
+ alignItems: 'center',
965
+ justifyContent: 'center'
966
+ }}>
967
+ <SyncOutlined spin style={{ fontSize: '24px', color: '#1890ff' }} />
968
+ </div>
969
+ ) : (
970
+ <Line
971
+ key={`processing-time-${processingTimeData.length}-${animationKey}`}
972
+ {...processingTimeConfig}
973
+ />
974
+ )}
975
+ </Card>
976
+ </Col>
977
+
978
+ </Row>
979
+
980
+ {/* 第五行:性能指标图表 */}
981
+ <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
982
+
983
+ <Col xs={24} md={12}>
984
+ <Card
985
+ size="small"
986
+ title={
987
+ <Space>
988
+ <span>排队任务数(个)</span>
989
+ <Tooltip title="等待执行的任务数量">
990
+ <InfoCircleOutlined style={{ fontSize: 12, color: '#999' }} />
991
+ </Tooltip>
992
+ </Space>
993
+ }
994
+ bodyStyle={{ padding: '12px' }}
995
+ >
996
+ <Line
997
+ key={`queued-tasks-${queuedTasksData.length}-${animationKey}`}
998
+ {...queuedTasksConfig}
999
+ />
1000
+ </Card>
1001
+ </Col>
1002
+
1003
+ <Col xs={24} md={12}>
1004
+ <Card
1005
+ size="small"
1006
+ title={
1007
+ <Space>
1008
+ <span>任务执行延时</span>
1009
+ <Tooltip title="任务创建到开始执行的延时">
1010
+ <InfoCircleOutlined style={{ fontSize: 12, color: '#999' }} />
1011
+ </Tooltip>
1012
+ </Space>
1013
+ }
1014
+ bodyStyle={{ padding: '12px' }}
1015
+ >
1016
+ <Line
1017
+ key={`create-latency-${createLatencyData.length}`}
1018
+ {...createLatencyConfig}
1019
+ />
1020
+ </Card>
1021
+ </Col>
1022
+ </Row>
1023
+
1024
+ {/* 第七行:队列监控表格 */}
1025
+ <Row gutter={[16, 16]}>
1026
+ <Col xs={24} lg={12}>
1027
+ <Card
1028
+ size="small"
1029
+ title="队列积压 Top 10"
1030
+ extra={
1031
+ <Tag color="orange">
1032
+ 需要关注
1033
+ </Tag>
1034
+ }
1035
+ >
1036
+ <div style={{ height: 350, overflow: 'auto' }}>
1037
+ {topBacklogQueues.length > 0 ? (
1038
+ <Table
1039
+ dataSource={topBacklogQueues}
1040
+ columns={[
1041
+ {
1042
+ title: '队列名称',
1043
+ dataIndex: 'queue',
1044
+ key: 'queue',
1045
+ ellipsis: true,
1046
+ },
1047
+ {
1048
+ title: '积压数量',
1049
+ dataIndex: 'backlog',
1050
+ key: 'backlog',
1051
+ render: (val) => (
1052
+ <span style={{ fontWeight: 'bold' }}>
1053
+ {val.toLocaleString()}
1054
+ </span>
1055
+ ),
1056
+ },
1057
+ {
1058
+ title: '状态',
1059
+ dataIndex: 'status',
1060
+ key: 'status',
1061
+ render: (status) => {
1062
+ const colorMap = {
1063
+ normal: 'green',
1064
+ warning: 'orange',
1065
+ critical: 'red',
1066
+ };
1067
+ const textMap = {
1068
+ normal: '正常',
1069
+ warning: '警告',
1070
+ critical: '严重',
1071
+ };
1072
+ return <Tag color={colorMap[status]}>{textMap[status]}</Tag>;
1073
+ },
1074
+ },
1075
+ ]}
1076
+ pagination={false}
1077
+ size="small"
1078
+ />
1079
+ ) : (
1080
+ <Empty description="暂无积压队列" />
1081
+ )}
1082
+ </div>
1083
+ </Card>
1084
+ </Col>
1085
+ <Col xs={24} lg={12}>
1086
+ <Card
1087
+ size="small"
1088
+ title="错误率 Top 10"
1089
+ extra={
1090
+ <Tag color="red">
1091
+ 需要处理
1092
+ </Tag>
1093
+ }
1094
+ >
1095
+ <div style={{ height: 350, overflow: 'auto' }}>
1096
+ {topErrorQueues.length > 0 ? (
1097
+ <Table
1098
+ dataSource={topErrorQueues}
1099
+ columns={[
1100
+ {
1101
+ title: '队列名称',
1102
+ dataIndex: 'queue',
1103
+ key: 'queue',
1104
+ ellipsis: true,
1105
+ },
1106
+ {
1107
+ title: '错误率',
1108
+ dataIndex: 'errorRate',
1109
+ key: 'errorRate',
1110
+ render: (val) => (
1111
+ <Progress
1112
+ percent={parseFloat(val)}
1113
+ size="small"
1114
+ strokeColor={val > 10 ? '#ff4d4f' : '#faad14'}
1115
+ />
1116
+ ),
1117
+ },
1118
+ {
1119
+ title: '失败/总数',
1120
+ key: 'ratio',
1121
+ render: (_, record) => (
1122
+ <span>{record.failed}/{record.total}</span>
1123
+ ),
1124
+ },
1125
+ ]}
1126
+ pagination={false}
1127
+ size="small"
1128
+ />
1129
+ ) : (
1130
+ <Empty description="暂无错误" />
1131
+ )}
1132
+ </div>
1133
+ </Card>
1134
+ </Col>
1135
+ </Row>
1136
+ {/* 第四行:Redis监控详情 */}
1137
+ <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
1138
+ <Col span={24}>
1139
+ <Card
1140
+ size="small"
1141
+ title={
1142
+ <Space>
1143
+ <DatabaseOutlined />
1144
+ <span>Redis监控</span>
1145
+ {redisLoading && <SyncOutlined spin />}
1146
+ </Space>
1147
+ }
1148
+ extra={
1149
+ <Button
1150
+ size="small"
1151
+ icon={<ReloadOutlined />}
1152
+ onClick={fetchData}
1153
+ loading={redisLoading}
1154
+ >
1155
+ 刷新
1156
+ </Button>
1157
+ }
1158
+ >
1159
+ <Row gutter={[16, 16]}>
1160
+ {/* Redis基本信息 */}
1161
+ <Col xs={24} md={6}>
1162
+ <Card size="small" bodyStyle={{ padding: '12px' }}>
1163
+ <div style={{ textAlign: 'center' }}>
1164
+ <div style={{ marginBottom: 8, color: '#666', fontSize: 12 }}>版本信息</div>
1165
+ <div style={{ fontSize: 14, fontWeight: 'bold' }}>{redisMonitor.server.redis_version}</div>
1166
+ <div style={{ fontSize: 12, color: '#999', marginTop: 4 }}>
1167
+ 运行时长: {Math.floor(redisMonitor.server.uptime_in_seconds / 3600)}小时
1168
+ </div>
1169
+ </div>
1170
+ </Card>
1171
+ </Col>
1172
+
1173
+ {/* 内存使用情况 */}
1174
+ <Col xs={24} md={6}>
1175
+ <Card size="small" bodyStyle={{ padding: '12px' }}>
1176
+ <div style={{ textAlign: 'center' }}>
1177
+ <div style={{ marginBottom: 8, color: '#666', fontSize: 12 }}>内存使用</div>
1178
+ <Progress
1179
+ type="dashboard"
1180
+ percent={redisMonitor.memory.usage_percentage || 0}
1181
+ size={60}
1182
+ strokeColor={redisMonitor.memory.usage_percentage > 80 ? '#ff4d4f' : '#52c41a'}
1183
+ format={(percent) => `${percent || 0}%`}
1184
+ />
1185
+ <div style={{ fontSize: 12, color: '#999', marginTop: 4 }}>
1186
+ {redisMonitor.memory.used_memory_human} / {redisMonitor.memory.total_memory_human || redisMonitor.memory.maxmemory_human || '∞'}
1187
+ </div>
1188
+ </div>
1189
+ </Card>
1190
+ </Col>
1191
+
1192
+ {/* 连接数统计 */}
1193
+ <Col xs={24} md={6}>
1194
+ <Card size="small" bodyStyle={{ padding: '12px' }}>
1195
+ <div>
1196
+ <div style={{ marginBottom: 8, color: '#666', fontSize: 12 }}>连接数</div>
1197
+ <Row gutter={8}>
1198
+ <Col span={12}>
1199
+ <div style={{ textAlign: 'center' }}>
1200
+ <div style={{ fontSize: 16, fontWeight: 'bold', color: '#1890ff' }}>
1201
+ {redisMonitor.clients.connected_clients}
1202
+ </div>
1203
+ <div style={{ fontSize: 11, color: '#999' }}>连接数</div>
1204
+ </div>
1205
+ </Col>
1206
+ <Col span={12}>
1207
+ <div style={{ textAlign: 'center' }}>
1208
+ <div style={{ fontSize: 16, fontWeight: 'bold', color: '#faad14' }}>
1209
+ {redisMonitor.clients.blocked_clients}
1210
+ </div>
1211
+ <div style={{ fontSize: 11, color: '#999' }}>阻塞数</div>
1212
+ </div>
1213
+ </Col>
1214
+ </Row>
1215
+ </div>
1216
+ </Card>
1217
+ </Col>
1218
+
1219
+ {/* 操作统计 */}
1220
+ <Col xs={24} md={6}>
1221
+ <Card size="small" bodyStyle={{ padding: '12px' }}>
1222
+ <div>
1223
+ <div style={{ marginBottom: 8, color: '#666', fontSize: 12 }}>操作统计</div>
1224
+ <Row gutter={8}>
1225
+ <Col span={24}>
1226
+ <div style={{ textAlign: 'center', marginBottom: 8 }}>
1227
+ <div style={{ fontSize: 16, fontWeight: 'bold', color: '#52c41a' }}>
1228
+ {redisMonitor.stats.instantaneous_ops_per_sec}
1229
+ </div>
1230
+ <div style={{ fontSize: 11, color: '#999' }}>QPS</div>
1231
+ </div>
1232
+ </Col>
1233
+ <Col span={12}>
1234
+ <div style={{ textAlign: 'center' }}>
1235
+ <div style={{ fontSize: 14, fontWeight: 'bold', color: '#722ed1' }}>
1236
+ {redisMonitor.stats.hit_rate}%
1237
+ </div>
1238
+ <div style={{ fontSize: 11, color: '#999' }}>命中率</div>
1239
+ </div>
1240
+ </Col>
1241
+ <Col span={12}>
1242
+ <div style={{ textAlign: 'center' }}>
1243
+ <div style={{ fontSize: 14, fontWeight: 'bold', color: '#eb2f96' }}>
1244
+ {redisMonitor.keyspace.total_keys}
1245
+ </div>
1246
+ <div style={{ fontSize: 11, color: '#999' }}>Key数</div>
1247
+ </div>
1248
+ </Col>
1249
+ </Row>
1250
+ </div>
1251
+ </Card>
1252
+ </Col>
1253
+ </Row>
1254
+
1255
+ {/* Redis状态详情 */}
1256
+ <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
1257
+ <Col xs={24} lg={8}>
1258
+ <div style={{ padding: '8px 0', borderTop: '1px solid #f0f0f0' }}>
1259
+ <div style={{ marginBottom: 8, fontSize: 13, fontWeight: 'bold', color: '#666' }}>内存详情</div>
1260
+ <Space direction="vertical" size={4} style={{ width: '100%' }}>
1261
+ <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12 }}>
1262
+ <span>已使用:</span>
1263
+ <span>{redisMonitor.memory.used_memory_human}</span>
1264
+ </div>
1265
+ <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12 }}>
1266
+ <span>碎片率:</span>
1267
+ <span>{redisMonitor.memory.mem_fragmentation_ratio}</span>
1268
+ </div>
1269
+ </Space>
1270
+ </div>
1271
+ </Col>
1272
+ <Col xs={24} lg={8}>
1273
+ <div style={{ padding: '8px 0', borderTop: '1px solid #f0f0f0' }}>
1274
+ <div style={{ marginBottom: 8, fontSize: 13, fontWeight: 'bold', color: '#666' }}>命中统计</div>
1275
+ <Space direction="vertical" size={4} style={{ width: '100%' }}>
1276
+ <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12 }}>
1277
+ <span>命中:</span>
1278
+ <span style={{ color: '#52c41a' }}>{redisMonitor.stats.keyspace_hits?.toLocaleString() || 0}</span>
1279
+ </div>
1280
+ <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12 }}>
1281
+ <span>未命中:</span>
1282
+ <span style={{ color: '#ff4d4f' }}>{redisMonitor.stats.keyspace_misses?.toLocaleString() || 0}</span>
1283
+ </div>
1284
+ </Space>
1285
+ </div>
1286
+ </Col>
1287
+ <Col xs={24} lg={8}>
1288
+ <div style={{ padding: '8px 0', borderTop: '1px solid #f0f0f0' }}>
1289
+ <div style={{ marginBottom: 8, fontSize: 13, fontWeight: 'bold', color: '#666' }}>状态信息</div>
1290
+ <Space direction="vertical" size={4} style={{ width: '100%' }}>
1291
+ <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12 }}>
1292
+ <span>状态:</span>
1293
+ <Tag color={redisMonitor.status === 'healthy' ? 'green' : 'red'} size="small">
1294
+ {redisMonitor.status === 'healthy' ? '健康' : '异常'}
1295
+ </Tag>
1296
+ </div>
1297
+ <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12 }}>
1298
+ <span>命名空间:</span>
1299
+ <span>{currentNamespace || 'default'}</span>
1300
+ </div>
1301
+ </Space>
1302
+ </div>
1303
+ </Col>
1304
+ </Row>
1305
+ </Card>
1306
+ </Col>
1307
+ </Row>
1308
+
1309
+ {/* 第六行:队列积压监控 */}
1310
+ {/* <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
1311
+ <Col span={24}>
1312
+ <QueueBacklogTrend
1313
+ showTitle={true}
1314
+ defaultTimeRange={timeRange}
1315
+ autoRefresh={autoRefresh}
1316
+ refreshInterval={refreshInterval}
1317
+ showStatistics={true}
1318
+ chartType="line"
1319
+ height={300}
1320
+ selectedQueues={null}
1321
+ />
1322
+ </Col>
1323
+ </Row> */}
1324
+
1325
+
1326
+ </div>
1327
+ );
1328
+ }
1329
+
1330
+ export default Dashboard;