From af76de4ff7c4e2b86d59cff7b8e091a148e181da Mon Sep 17 00:00:00 2001 From: WangDL Date: Fri, 22 May 2026 22:31:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20events=20queue=20page=20+=20restructure?= =?UTF-8?q?=20under=20=E7=B3=BB=E7=BB=9F=E8=BF=90=E7=BB=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 4 +++ src/config/menu.tsx | 5 ++- src/layouts/AdminLayout.tsx | 1 + src/pages/Events.tsx | 71 +++++++++++++++++++++++++++++++++++++ src/services/events-api.ts | 10 ++++++ 5 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 src/pages/Events.tsx create mode 100644 src/services/events-api.ts diff --git a/src/App.tsx b/src/App.tsx index 0d9dd08..c0af787 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -99,6 +99,10 @@ function App() { }> } /> + }>} + /> , requiredRole: 'ADMIN' }, { path: '/billing', name: 'API 用量', icon: , requiredRole: 'SUPER_ADMIN' }, { path: '/git', name: '代码仓库', icon: }, - { path: '/servers', name: '服务器运维', icon: , requiredRole: 'SUPER_ADMIN' }, + { path: '/ops', name: '系统运维', icon: , requiredRole: 'SUPER_ADMIN', children: [ + { path: '/servers', name: '服务器' }, + { path: '/events', name: '事件队列' }, + ]}, { path: '/settings', name: '系统配置', icon: , requiredRole: 'ADMIN' }, ] diff --git a/src/layouts/AdminLayout.tsx b/src/layouts/AdminLayout.tsx index 4bd1280..ab0c3b5 100644 --- a/src/layouts/AdminLayout.tsx +++ b/src/layouts/AdminLayout.tsx @@ -24,6 +24,7 @@ const breadcrumbMap: Record = { '/billing': 'API 用量', '/git': '代码仓库', '/servers': '服务器运维', + '/events': '事件队列', '/audit': '审计日志', } diff --git a/src/pages/Events.tsx b/src/pages/Events.tsx new file mode 100644 index 0000000..1a92f34 --- /dev/null +++ b/src/pages/Events.tsx @@ -0,0 +1,71 @@ +import { useState } from 'react' +import { useQuery, useQueryClient } from '@tanstack/react-query' +import { Table, Tag, Button, Typography, App, Space, Badge } from 'antd' +import { ReloadOutlined, RetweetOutlined, CloudServerOutlined } from '@ant-design/icons' +import { getQueueOverview, getFailedJobs, retryJob } from '@/services/events-api' + +const { Title, Text } = Typography + +function EventsPage() { + const { message } = App.useApp() + const qc = useQueryClient() + const [selectedQueue, setSelectedQueue] = useState(null) + + const { data: overview } = useQuery({ queryKey: ['events', 'overview'], queryFn: getQueueOverview, staleTime: 10_000 }) + const { data: failed } = useQuery({ + queryKey: ['events', 'failed', selectedQueue], + queryFn: () => selectedQueue ? getFailedJobs(selectedQueue) : null, + enabled: !!selectedQueue, + }) + + const handleRetry = async (queue: string, jobId: string) => { + await retryJob(queue, jobId) + message.success('已重试') + qc.invalidateQueries({ queryKey: ['events'] }) + } + + const overviewColumns = [ + { title: '队列', dataIndex: 'name', width: 160 }, + { title: '总计', dataIndex: 'total', width: 60, align: 'center' as const }, + { title: '等待', dataIndex: 'waiting', width: 60, align: 'center' as const, render: (v: number) => }, + { title: '进行中', dataIndex: 'active', width: 70, align: 'center' as const, render: (v: number) => }, + { title: '完成', dataIndex: 'completed', width: 60, align: 'center' as const, render: (v: number) => {v} }, + { title: '失败', dataIndex: 'failed', width: 60, align: 'center' as const, render: (v: number, r: any) => ( + v > 0 ? : 0 + )}, + { title: '延迟', dataIndex: 'delayed', width: 60, align: 'center' as const }, + ] + + const failedColumns = [ + { title: 'Job ID', dataIndex: 'id', width: 160, ellipsis: true }, + { title: '名称', dataIndex: 'name', width: 150 }, + { title: '重试', dataIndex: 'attemptsMade', width: 60, align: 'center' as const }, + { title: '失败原因', dataIndex: 'failedReason', ellipsis: true }, + { title: '操作', width: 80, render: (_: any, r: any) => ( + + )}, + ] + + return ( +
+
+ <CloudServerOutlined /> 事件队列 + +
+ + + + {selectedQueue && ( + <> +
+ 失败任务 · {selectedQueue} + +
+
+ + )} + + ) +} + +export default EventsPage diff --git a/src/services/events-api.ts b/src/services/events-api.ts new file mode 100644 index 0000000..43e2152 --- /dev/null +++ b/src/services/events-api.ts @@ -0,0 +1,10 @@ +import { api } from './http-client' + +export interface QueueInfo { name: string; waiting: number; active: number; completed: number; failed: number; delayed: number; total: number } +export interface FailedJob { id: string; name: string; timestamp: number; attemptsMade: number; failedReason?: string } +export interface JobDetail extends FailedJob { state: string; data: any; stacktrace?: string[] } + +export function getQueueOverview(): Promise<{ queues: QueueInfo[] }> { return api.get('/admin-api/events') } +export function getFailedJobs(queue: string): Promise<{ jobs: FailedJob[] }> { return api.get(`/admin-api/events/${queue}/failed`) } +export function getJobDetail(queue: string, jobId: string): Promise { return api.get(`/admin-api/events/${queue}/jobs/${jobId}`) } +export function retryJob(queue: string, jobId: string): Promise { return api.post(`/admin-api/events/${queue}/jobs/${jobId}/retry`) }