easyai-ai-gateway/apps/web/src/pages/admin/AuditLogsPanel.tsx

94 lines
3.6 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 { History, ShieldCheck } from 'lucide-react';
import type { GatewayAuditLog } from '@easyai-ai-gateway/contracts';
import { Badge, Card, CardContent, CardHeader, CardTitle, Table, TableCell, TableHead, TableRow } from '../../components/ui';
export function AuditLogsPanel(props: { auditLogs: GatewayAuditLog[]; message?: string }) {
return (
<div className="pageStack">
<Card>
<CardHeader>
<div className="identityHeaderTitle">
<div className="iconBox"><ShieldCheck size={17} /></div>
<div>
<CardTitle></CardTitle>
<p className="mutedText">便</p>
</div>
<Badge variant="secondary">{props.auditLogs.length}</Badge>
</div>
</CardHeader>
{props.message && <CardContent><p className="formMessage">{props.message}</p></CardContent>}
</Card>
{props.auditLogs.length ? (
<Table className="identityDataTable auditLogTable">
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
{props.auditLogs.map((item) => (
<TableRow key={item.id}>
<TableCell>
<span className="identityTableName">
<strong>{actionLabel(item.action)}</strong>
<small>{item.category}</small>
</span>
</TableCell>
<TableCell>{item.actorUsername || item.actorUserId || '系统'}</TableCell>
<TableCell>
<span className="identityTableName">
<strong>{targetLabel(item.targetType)}</strong>
<small>{shortId(item.targetId)}</small>
</span>
</TableCell>
<TableCell>{auditSummary(item)}</TableCell>
<TableCell>{formatDateTime(item.createdAt)}</TableCell>
</TableRow>
))}
</Table>
) : (
<Card>
<CardContent className="emptyState">
<History size={18} />
<strong></strong>
<span></span>
</CardContent>
</Card>
)}
</div>
);
}
function actionLabel(action: string) {
if (action === 'wallet.balance.set') return '余额调整';
return action;
}
function targetLabel(targetType: string) {
if (targetType === 'gateway_user') return '用户';
return targetType;
}
function auditSummary(item: GatewayAuditLog) {
const metadata = item.metadata ?? {};
const direction = typeof metadata.direction === 'string' ? metadata.direction : '';
const amount = typeof metadata.amount === 'number' ? metadata.amount : undefined;
const currency = typeof metadata.currency === 'string' ? metadata.currency : 'resource';
const reason = typeof metadata.reason === 'string' ? metadata.reason : '';
const prefix = amount === undefined ? '' : `${direction === 'debit' ? '-' : '+'}${formatBalance(amount)} ${currency}`;
return [prefix, reason].filter(Boolean).join(' · ') || '已记录';
}
function formatDateTime(value: string) {
return value ? new Date(value).toLocaleString() : '-';
}
function formatBalance(value: number) {
return new Intl.NumberFormat('zh-CN', { maximumFractionDigits: 6 }).format(value);
}
function shortId(value: string) {
return value.length > 12 ? `${value.slice(0, 8)}...${value.slice(-4)}` : value;
}