CodeReview/frontend/src/components/debug/DatabaseTest.tsx

221 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Alert, AlertDescription } from "@/components/ui/alert";
import {
Database,
CheckCircle,
AlertTriangle,
Loader2,
RefreshCw
} from "lucide-react";
import { api } from "@/shared/config/database";
import { toast } from "sonner";
interface TestResult {
name: string;
status: 'success' | 'error' | 'pending';
message: string;
duration?: number;
}
export default function DatabaseTest() {
const [testing, setTesting] = useState(false);
const [results, setResults] = useState<TestResult[]>([]);
const runTests = async () => {
setTesting(true);
setResults([]);
const tests: Array<{ name: string; test: () => Promise<any> }> = [
{
name: "数据库连接测试",
test: async () => {
const start = Date.now();
await api.getProjectStats();
return { duration: Date.now() - start };
}
},
{
name: "项目数据查询",
test: async () => {
const start = Date.now();
const projects = await api.getProjects();
return {
duration: Date.now() - start,
count: projects.length
};
}
},
{
name: "审计任务查询",
test: async () => {
const start = Date.now();
const tasks = await api.getAuditTasks();
return {
duration: Date.now() - start,
count: tasks.length
};
}
},
{
name: "用户配置查询",
test: async () => {
const start = Date.now();
const count = await api.getProfilesCount();
return {
duration: Date.now() - start,
count
};
}
}
];
for (const { name, test } of tests) {
try {
// 添加pending状态
setResults(prev => [...prev, { name, status: 'pending', message: '测试中...' }]);
const result = await test();
// 更新为成功状态
setResults(prev => prev.map(r =>
r.name === name
? {
name,
status: 'success',
message: `测试通过 (${result.duration}ms)${result.count !== undefined ? ` - 数据量: ${result.count}` : ''}`,
duration: result.duration
}
: r
));
} catch (error: any) {
// 更新为错误状态
setResults(prev => prev.map(r =>
r.name === name
? {
name,
status: 'error',
message: `测试失败: ${error.message || '未知错误'}`
}
: r
));
}
// 添加延迟避免过快执行
await new Promise(resolve => setTimeout(resolve, 500));
}
setTesting(false);
const successCount = results.filter(r => r.status === 'success').length;
const totalCount = tests.length;
if (successCount === totalCount) {
toast.success("所有数据库测试通过!");
} else {
toast.error(`${totalCount - successCount} 个测试失败`);
}
};
const getStatusIcon = (status: TestResult['status']) => {
switch (status) {
case 'success':
return <CheckCircle className="w-4 h-4 text-green-600" />;
case 'error':
return <AlertTriangle className="w-4 h-4 text-red-600" />;
case 'pending':
return <Loader2 className="w-4 h-4 text-primary animate-spin" />;
default:
return null;
}
};
const getStatusBadge = (status: TestResult['status']) => {
switch (status) {
case 'success':
return <Badge className="bg-green-100 text-green-800"></Badge>;
case 'error':
return <Badge className="bg-red-100 text-red-800"></Badge>;
case 'pending':
return <Badge className="bg-red-50 text-red-800"></Badge>;
default:
return null;
}
};
return (
<Card className="w-full max-w-2xl mx-auto">
<CardHeader>
<CardTitle className="flex items-center">
<Database className="w-5 h-5 mr-2" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<p className="text-sm text-muted-foreground">
</p>
<Button
onClick={runTests}
disabled={testing}
size="sm"
>
{testing ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
...
</>
) : (
<>
<RefreshCw className="w-4 h-4 mr-2" />
</>
)}
</Button>
</div>
{results.length > 0 && (
<div className="space-y-3">
{results.map((result, index) => (
<div
key={index}
className="flex items-center justify-between p-3 border rounded-lg"
>
<div className="flex items-center space-x-3">
{getStatusIcon(result.status)}
<div>
<p className="font-medium text-sm">{result.name}</p>
<p className="text-xs text-muted-foreground">{result.message}</p>
</div>
</div>
{getStatusBadge(result.status)}
</div>
))}
</div>
)}
{results.length > 0 && !testing && (
<Alert>
<AlertTriangle className="h-4 w-4" />
<AlertDescription>
: {results.filter(r => r.status === 'success').length} /
: {results.length}
</AlertDescription>
</Alert>
)}
{results.length === 0 && !testing && (
<Alert>
<Database className="h-4 w-4" />
<AlertDescription>
"开始测试"
</AlertDescription>
</Alert>
)}
</CardContent>
</Card>
);
}