import React, { useEffect, useState } from "react";
|
import {
|
List,
|
SearchInput,
|
TopToolbar,
|
SelectColumnsButton,
|
EditButton,
|
FilterButton,
|
TextInput,
|
DateInput,
|
SelectInput,
|
useListContext,
|
Pagination,
|
} from 'react-admin';
|
import { Box, Chip, Grid, Typography } from '@mui/material';
|
import EmptyData from "@/page/components/EmptyData";
|
import { AiConsoleLayout, AiConsolePanel, aiCardSx } from "@/page/components/AiConsoleLayout";
|
import MyExportButton from '@/page/components/MyExportButton';
|
import { DEFAULT_PAGE_SIZE } from '@/config/setting';
|
import request from "@/utils/request";
|
|
const filters = [
|
<SearchInput source="condition" alwaysOn />,
|
<DateInput label='common.time.after' source="timeStart" alwaysOn />,
|
<DateInput label='common.time.before' source="timeEnd" alwaysOn />,
|
<TextInput source="modelCode" label="模型编码" />,
|
<TextInput source="routeCode" label="路由编码" />,
|
<SelectInput source="result" label="结果" choices={[
|
{ id: '1', name: '成功' },
|
{ id: '0', name: '失败' },
|
]} />,
|
];
|
|
const resultColor = (result) => Number(result) === 1 ? 'success' : 'error';
|
|
const CallLogBoard = () => {
|
const { data, isLoading } = useListContext();
|
const records = data || [];
|
const [stats, setStats] = useState({});
|
|
useEffect(() => {
|
let mounted = true;
|
const fetchStats = async () => {
|
try {
|
const { data: res } = await request.get('/ai/call-log/stats');
|
if (mounted && res?.code === 200) {
|
setStats(res.data || {});
|
}
|
} catch (error) {
|
}
|
};
|
fetchStats();
|
return () => {
|
mounted = false;
|
};
|
}, []);
|
|
if (!isLoading && !records.length) {
|
return <EmptyData />;
|
}
|
|
return (
|
<AiConsoleLayout
|
title="AI调用日志"
|
subtitle="按当前系统后台风格展示模型调用观测,突出成功率、最近 24 小时调用量和平均耗时,便于快速排查路由与上游模型问题。"
|
stats={[
|
{ label: '调用总数', value: stats.total || 0 },
|
{ label: '成功率', value: `${Number(stats.successRate || 0).toFixed(1)}%` },
|
{ label: '平均耗时', value: `${stats.avgSpendTime || 0} ms` },
|
{ label: '近24小时', value: stats.last24hCount || 0 },
|
]}
|
>
|
<AiConsolePanel
|
title="调用明细"
|
subtitle={`已观测模型 ${stats.modelCount || 0} 个,路由 ${stats.routeCount || 0} 条。`}
|
minHeight={420}
|
>
|
<Grid container spacing={2}>
|
{records.map((record) => (
|
<Grid item xs={12} md={6} xl={4} key={record.id}>
|
<Box sx={aiCardSx(record.result === 1)}>
|
<Box display="flex" justifyContent="space-between" gap={1}>
|
<Box>
|
<Typography variant="subtitle1" sx={{ fontWeight: 700, color: '#284059' }}>
|
{record.modelCode || '未记录模型'}
|
</Typography>
|
<Typography variant="caption" sx={{ color: '#8093a8' }}>
|
{record.routeCode || '未记录路由'} · 第 {record.attemptNo || 1} 次
|
</Typography>
|
</Box>
|
<Chip size="small" color={resultColor(record.result)} label={record.result$ || '未知'} />
|
</Box>
|
<Box sx={{ mt: 1.25, display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 1.5 }}>
|
<Box>
|
<Typography variant="caption" sx={{ color: '#70839a' }}>耗时</Typography>
|
<Typography variant="body2" sx={{ color: '#31465d' }}>{record.spendTime || 0} ms</Typography>
|
</Box>
|
<Box>
|
<Typography variant="caption" sx={{ color: '#70839a' }}>请求时间</Typography>
|
<Typography variant="body2" sx={{ color: '#31465d' }}>{record.requestTime || '-'}</Typography>
|
</Box>
|
</Box>
|
<Box sx={{ mt: 1.25 }}>
|
<Typography variant="caption" sx={{ color: '#70839a' }}>错误信息</Typography>
|
<Typography variant="body2" sx={{ mt: 0.5, minHeight: 72, color: '#31465d' }}>
|
{record.err || '无'}
|
</Typography>
|
</Box>
|
<Box sx={{ mt: 1.5 }}>
|
<EditButton record={record} sx={{ px: 1.25, py: 0.5, minWidth: 64 }} />
|
</Box>
|
</Box>
|
</Grid>
|
))}
|
</Grid>
|
<Box sx={{ mt: 2 }}>
|
<Pagination rowsPerPageOptions={[DEFAULT_PAGE_SIZE, 25, 50]} />
|
</Box>
|
</AiConsolePanel>
|
</AiConsoleLayout>
|
);
|
};
|
|
const AiCallLogList = () => (
|
<List
|
sx={{ width: '100%', flexGrow: 1 }}
|
title={"menu.aiCallLog"}
|
filters={filters}
|
sort={{ field: "createTime", order: "desc" }}
|
actions={(
|
<TopToolbar>
|
<FilterButton />
|
<SelectColumnsButton preferenceKey='aiCallLog' />
|
<MyExportButton />
|
</TopToolbar>
|
)}
|
perPage={DEFAULT_PAGE_SIZE}
|
pagination={false}
|
>
|
<CallLogBoard />
|
</List>
|
);
|
|
export default AiCallLogList;
|