import React, { useEffect, useState } from 'react';
import {
API,
copy,
isAdmin,
showError,
showSuccess,
timestamp2string,
} from '../helpers';
import {
Avatar,
Button,
Form,
Layout,
Modal,
Select,
Space,
Spin,
Table,
Tag,
} from '@douyinfe/semi-ui';
import { ITEMS_PER_PAGE } from '../constants';
import { renderNumber, renderQuota, stringToColor } from '../helpers/render';
import Paragraph from '@douyinfe/semi-ui/lib/es/typography/paragraph';
const { Header } = Layout;
function renderTimestamp(timestamp) {
return <>{timestamp2string(timestamp)}>;
}
const MODE_OPTIONS = [
{ key: 'all', text: '全部用户', value: 'all' },
{ key: 'self', text: '当前用户', value: 'self' },
];
const colors = [
'amber',
'blue',
'cyan',
'green',
'grey',
'indigo',
'light-blue',
'lime',
'orange',
'pink',
'purple',
'red',
'teal',
'violet',
'yellow',
];
function renderType(type) {
switch (type) {
case 1:
return (
{' '}
充值{' '}
);
case 2:
return (
{' '}
消费{' '}
);
case 3:
return (
{' '}
管理{' '}
);
case 4:
return (
{' '}
系统{' '}
);
default:
return (
{' '}
未知{' '}
);
}
}
function renderIsStream(bool) {
if (bool) {
return (
流
);
} else {
return (
非流
);
}
}
function renderUseTime(type) {
const time = parseInt(type);
if (time < 101) {
return (
{' '}
{time} s{' '}
);
} else if (time < 300) {
return (
{' '}
{time} s{' '}
);
} else {
return (
{' '}
{time} s{' '}
);
}
}
const LogsTable = () => {
const columns = [
{
title: '时间',
dataIndex: 'timestamp2string',
},
{
title: '渠道',
dataIndex: 'channel',
className: isAdmin() ? 'tableShow' : 'tableHiddle',
render: (text, record, index) => {
return isAdminUser ? (
record.type === 0 || record.type === 2 ? (
{
{' '}
{text}{' '}
}
) : (
<>>
)
) : (
<>>
);
},
},
{
title: '用户',
dataIndex: 'username',
className: isAdmin() ? 'tableShow' : 'tableHiddle',
render: (text, record, index) => {
return isAdminUser ? (
showUserInfo(record.user_id)}
>
{typeof text === 'string' && text.slice(0, 1)}
{text}
) : (
<>>
);
},
},
{
title: '令牌',
dataIndex: 'token_name',
render: (text, record, index) => {
return record.type === 0 || record.type === 2 ? (
{
copyText(text);
}}
>
{' '}
{text}{' '}
) : (
<>>
);
},
},
{
title: '类型',
dataIndex: 'type',
render: (text, record, index) => {
return {renderType(text)}
;
},
},
{
title: '模型',
dataIndex: 'model_name',
render: (text, record, index) => {
return record.type === 0 || record.type === 2 ? (
{
copyText(text);
}}
>
{' '}
{text}{' '}
) : (
<>>
);
},
},
{
title: '用时',
dataIndex: 'use_time',
render: (text, record, index) => {
return (
{renderUseTime(text)}
{renderIsStream(record.is_stream)}
);
},
},
{
title: '提示',
dataIndex: 'prompt_tokens',
render: (text, record, index) => {
return record.type === 0 || record.type === 2 ? (
{ {text} }
) : (
<>>
);
},
},
{
title: '补全',
dataIndex: 'completion_tokens',
render: (text, record, index) => {
return parseInt(text) > 0 &&
(record.type === 0 || record.type === 2) ? (
{ {text} }
) : (
<>>
);
},
},
{
title: '花费',
dataIndex: 'quota',
render: (text, record, index) => {
return record.type === 0 || record.type === 2 ? (
{renderQuota(text, 6)}
) : (
<>>
);
},
},
{
title: '详情',
dataIndex: 'content',
render: (text, record, index) => {
return (
{text}
);
},
},
];
const [logs, setLogs] = useState([]);
const [showStat, setShowStat] = useState(false);
const [loading, setLoading] = useState(false);
const [loadingStat, setLoadingStat] = useState(false);
const [activePage, setActivePage] = useState(1);
const [logCount, setLogCount] = useState(0);
const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE);
const [searchKeyword, setSearchKeyword] = useState('');
const [searching, setSearching] = useState(false);
const [logType, setLogType] = useState(0);
const isAdminUser = isAdmin();
let now = new Date();
// 初始化start_timestamp为前一天
const [inputs, setInputs] = useState({
username: '',
token_name: '',
model_name: '',
start_timestamp: timestamp2string(now.getTime() / 1000 - 86400),
end_timestamp: timestamp2string(now.getTime() / 1000 + 3600),
channel: '',
});
const {
username,
token_name,
model_name,
start_timestamp,
end_timestamp,
channel,
} = inputs;
const [stat, setStat] = useState({
quota: 0,
token: 0,
});
const handleInputChange = (value, name) => {
setInputs((inputs) => ({ ...inputs, [name]: value }));
};
const getLogSelfStat = async () => {
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
let res = await API.get(
`/api/log/self/stat?type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`,
);
const { success, message, data } = res.data;
if (success) {
setStat(data);
} else {
showError(message);
}
};
const getLogStat = async () => {
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
let res = await API.get(
`/api/log/stat?type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`,
);
const { success, message, data } = res.data;
if (success) {
setStat(data);
} else {
showError(message);
}
};
const handleEyeClick = async () => {
setLoadingStat(true);
if (isAdminUser) {
await getLogStat();
} else {
await getLogSelfStat();
}
setShowStat(true);
setLoadingStat(false);
};
const showUserInfo = async (userId) => {
if (!isAdminUser) {
return;
}
const res = await API.get(`/api/user/${userId}`);
const { success, message, data } = res.data;
if (success) {
Modal.info({
title: '用户信息',
content: (
用户名: {data.username}
余额: {renderQuota(data.quota)}
已用额度:{renderQuota(data.used_quota)}
请求次数:{renderNumber(data.request_count)}
),
centered: true,
});
} else {
showError(message);
}
};
const setLogsFormat = (logs, total) => {
for (let i = 0; i < logs.length; i++) {
logs[i].timestamp2string = timestamp2string(logs[i].created_at);
logs[i].key = '' + logs[i].id;
}
// data.key = '' + data.id
setLogs(logs);
setLogCount(total);
// console.log(logCount);
};
const loadLogs = async (startIdx, pageSize, logType = 0) => {
setLoading(true);
let url = '';
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
if (isAdminUser) {
url = `/api/log/?p=${startIdx}&page_size=${pageSize}&type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`;
} else {
url = `/api/log/self/?p=${startIdx}&page_size=${pageSize}&type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
}
const res = await API.get(url);
const { success, message, total, data } = res.data;
if (success) {
if (startIdx === 0) {
setLogsFormat(data, total);
} else {
let newLogs = [...logs];
newLogs.splice(startIdx * pageSize, data.length, ...data);
setLogsFormat(newLogs, total);
}
} else {
showError(message);
}
setLoading(false);
};
const pageData = logs.slice(
(activePage - 1) * pageSize,
activePage * pageSize,
);
const handlePageChange = (page) => {
setActivePage(page);
if (page === Math.ceil(logs.length / pageSize) + 1) {
// In this case we have to load more data and then append them.
loadLogs(page - 1, pageSize, logType).then((r) => {});
}
};
const handlePageSizeChange = async (size) => {
localStorage.setItem('page-size', size + '');
setPageSize(size);
setActivePage(1);
loadLogs(0, size)
.then()
.catch((reason) => {
showError(reason);
});
};
const refresh = async (localLogType) => {
// setLoading(true);
setActivePage(1);
await loadLogs(0, pageSize, localLogType);
};
const copyText = async (text) => {
if (await copy(text)) {
showSuccess('已复制:' + text);
} else {
// setSearchKeyword(text);
Modal.error({ title: '无法复制到剪贴板,请手动复制', content: text });
}
};
useEffect(() => {
// console.log('default effect')
const localPageSize =
parseInt(localStorage.getItem('page-size')) || ITEMS_PER_PAGE;
setPageSize(localPageSize);
loadLogs(0, localPageSize)
.then()
.catch((reason) => {
showError(reason);
});
}, []);
const searchLogs = async () => {
if (searchKeyword === '') {
// if keyword is blank, load files instead.
await loadLogs(0, pageSize);
setActivePage(1);
return;
}
setSearching(true);
const res = await API.get(`/api/log/self/search?keyword=${searchKeyword}`);
const { success, message, data } = res.data;
if (success) {
setLogs(data);
setActivePage(1);
} else {
showError(message);
}
setSearching(false);
};
return (
<>
使用明细(总消耗额度:
{showStat ? renderQuota(stat.quota) : '点击查看'}
)
handleInputChange(value, 'token_name')}
/>
handleInputChange(value, 'model_name')}
/>
handleInputChange(value, 'start_timestamp')}
/>
handleInputChange(value, 'end_timestamp')}
/>
{isAdminUser && (
<>
handleInputChange(value, 'channel')}
/>
handleInputChange(value, 'username')}
/>
>
)}
>
{
handlePageSizeChange(size).then();
},
onPageChange: handlePageChange,
}}
/>
>
);
};
export default LogsTable;