mirror of
https://github.com/linux-do/new-api.git
synced 2025-09-17 16:06:38 +08:00
perf: 数据看板支持选择时间粒度
This commit is contained in:
parent
d30b9321b2
commit
00306aa142
@ -27,6 +27,7 @@ var DisplayTokenStatEnabled = true
|
||||
var DrawingEnabled = true
|
||||
var DataExportEnabled = true
|
||||
var DataExportInterval = 5 // unit: minute
|
||||
var DataExportDefaultTime = "hour" // unit: minute
|
||||
|
||||
// Any options with "Secret", "Token" in its key won't be return by GetOptions
|
||||
|
||||
|
@ -36,6 +36,7 @@ func GetStatus(c *gin.Context) {
|
||||
"enable_batch_update": common.BatchUpdateEnabled,
|
||||
"enable_drawing": common.DrawingEnabled,
|
||||
"enable_data_export": common.DataExportEnabled,
|
||||
"data_export_default_time": common.DataExportDefaultTime,
|
||||
},
|
||||
})
|
||||
return
|
||||
|
@ -79,6 +79,7 @@ func InitOptionMap() {
|
||||
common.OptionMap["QuotaPerUnit"] = strconv.FormatFloat(common.QuotaPerUnit, 'f', -1, 64)
|
||||
common.OptionMap["RetryTimes"] = strconv.Itoa(common.RetryTimes)
|
||||
common.OptionMap["DataExportInterval"] = strconv.Itoa(common.DataExportInterval)
|
||||
common.OptionMap["DataExportDefaultTime"] = common.DataExportDefaultTime
|
||||
|
||||
common.OptionMapRWMutex.Unlock()
|
||||
loadOptionsFromDatabase()
|
||||
@ -228,6 +229,8 @@ func updateOptionMap(key string, value string) (err error) {
|
||||
common.RetryTimes, _ = strconv.Atoi(value)
|
||||
case "DataExportInterval":
|
||||
common.DataExportInterval, _ = strconv.Atoi(value)
|
||||
case "DataExportDefaultTime":
|
||||
common.DataExportDefaultTime = value
|
||||
case "ModelRatio":
|
||||
err = common.UpdateModelRatioByJSONString(value)
|
||||
case "GroupRatio":
|
||||
|
@ -51,6 +51,7 @@ function App() {
|
||||
localStorage.setItem('display_in_currency', data.display_in_currency);
|
||||
localStorage.setItem('enable_drawing', data.enable_drawing);
|
||||
localStorage.setItem('enable_data_export', data.enable_data_export);
|
||||
localStorage.setItem('data_export_default_time', data.data_export_default_time);
|
||||
if (data.chat_link) {
|
||||
localStorage.setItem('chat_link', data.chat_link);
|
||||
} else {
|
||||
|
@ -23,13 +23,19 @@ const OperationSetting = () => {
|
||||
DisplayTokenStatEnabled: '',
|
||||
DrawingEnabled: '',
|
||||
DataExportEnabled: '',
|
||||
DataExportDefaultTime: 'hour',
|
||||
DataExportInterval: 5,
|
||||
RetryTimes: 0
|
||||
});
|
||||
const [originInputs, setOriginInputs] = useState({});
|
||||
let [loading, setLoading] = useState(false);
|
||||
let [historyTimestamp, setHistoryTimestamp] = useState(timestamp2string(now.getTime() / 1000 - 30 * 24 * 3600)); // a month ago
|
||||
|
||||
// 精确时间选项(小时,天,周)
|
||||
const timeOptions = [
|
||||
{key: 'hour', text: '小时', value: 'hour'},
|
||||
{key: 'day', text: '天', value: 'day'},
|
||||
{key: 'week', text: '周', value: 'week'}
|
||||
];
|
||||
const getOptions = async () => {
|
||||
const res = await API.get('/api/option/');
|
||||
const {success, message, data} = res.data;
|
||||
@ -71,7 +77,10 @@ const OperationSetting = () => {
|
||||
};
|
||||
|
||||
const handleInputChange = async (e, {name, value}) => {
|
||||
if (name.endsWith('Enabled') || name === 'DataExportInterval') {
|
||||
if (name.endsWith('Enabled') || name === 'DataExportInterval' || name === 'DataExportDefaultTime') {
|
||||
if (name === 'DataExportDefaultTime') {
|
||||
localStorage.setItem('data_export_default_time', value);
|
||||
}
|
||||
await updateOption(name, value);
|
||||
} else {
|
||||
setInputs((inputs) => ({...inputs, [name]: value}));
|
||||
@ -234,15 +243,28 @@ const OperationSetting = () => {
|
||||
name='LogConsumeEnabled'
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
|
||||
</Form.Group>
|
||||
<Form.Group inline>
|
||||
<Form.Group widths={4}>
|
||||
<Form.Input label='目标时间' value={historyTimestamp} type='datetime-local'
|
||||
name='history_timestamp'
|
||||
onChange={(e, {name, value}) => {
|
||||
setHistoryTimestamp(value);
|
||||
}}/>
|
||||
</Form.Group>
|
||||
<Form.Button onClick={() => {
|
||||
deleteHistoryLogs().then();
|
||||
}}>清理历史日志</Form.Button>
|
||||
<Divider/>
|
||||
<Header as='h3'>
|
||||
数据看板
|
||||
</Header>
|
||||
<Form.Checkbox
|
||||
checked={inputs.DataExportEnabled === 'true'}
|
||||
label='启用数据看板(实验性)'
|
||||
name='DataExportEnabled'
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<Form.Group>
|
||||
<Form.Input
|
||||
label='数据看板更新间隔(分钟,设置过短会影响数据库性能)'
|
||||
name='DataExportInterval'
|
||||
@ -254,19 +276,17 @@ const OperationSetting = () => {
|
||||
value={inputs.DataExportInterval}
|
||||
placeholder='数据看板更新间隔(分钟,设置过短会影响数据库性能)'
|
||||
/>
|
||||
<Form.Select
|
||||
label='数据看板默认时间粒度(仅修改展示粒度,统计精确到小时)'
|
||||
options={timeOptions}
|
||||
name='DataExportDefaultTime'
|
||||
onChange={handleInputChange}
|
||||
autoComplete='new-password'
|
||||
value={inputs.DataExportDefaultTime}
|
||||
placeholder='数据看板默认时间粒度'
|
||||
/>
|
||||
</Form.Group>
|
||||
<Divider/>
|
||||
<Form.Group widths={4}>
|
||||
<Form.Input label='目标时间' value={historyTimestamp} type='datetime-local'
|
||||
name='history_timestamp'
|
||||
onChange={(e, {name, value}) => {
|
||||
setHistoryTimestamp(value);
|
||||
}}/>
|
||||
</Form.Group>
|
||||
<Form.Button onClick={() => {
|
||||
deleteHistoryLogs().then();
|
||||
}}>清理历史日志</Form.Button>
|
||||
<Divider/>
|
||||
<Header as='h3'>
|
||||
监控设置
|
||||
</Header>
|
||||
|
@ -171,7 +171,7 @@ export function timestamp2string(timestamp) {
|
||||
);
|
||||
}
|
||||
|
||||
export function timestamp2string1(timestamp) {
|
||||
export function timestamp2string1(timestamp, dataExportDefaultTime = 'hour') {
|
||||
let date = new Date(timestamp * 1000);
|
||||
// let year = date.getFullYear().toString();
|
||||
let month = (date.getMonth() + 1).toString();
|
||||
@ -186,15 +186,22 @@ export function timestamp2string1(timestamp) {
|
||||
if (hour.length === 1) {
|
||||
hour = '0' + hour;
|
||||
}
|
||||
return (
|
||||
// year +
|
||||
// '-' +
|
||||
month +
|
||||
'-' +
|
||||
day +
|
||||
' ' +
|
||||
hour + ":00"
|
||||
);
|
||||
let str = month + '-' + day
|
||||
if (dataExportDefaultTime === 'hour') {
|
||||
str += ' ' + hour + ":00"
|
||||
} else if (dataExportDefaultTime === 'week') {
|
||||
let nextWeek = new Date(timestamp * 1000 + 6 * 24 * 60 * 60 * 1000);
|
||||
let nextMonth = (nextWeek.getMonth() + 1).toString();
|
||||
let nextDay = nextWeek.getDate().toString();
|
||||
if (nextMonth.length === 1) {
|
||||
nextMonth = '0' + nextMonth;
|
||||
}
|
||||
if (nextDay.length === 1) {
|
||||
nextDay = '0' + nextDay;
|
||||
}
|
||||
str += ' - ' + nextMonth + '-' + nextDay
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
export function downloadTextAsFile(text, filename) {
|
||||
|
@ -12,17 +12,18 @@ import {
|
||||
} from "../../helpers/render";
|
||||
|
||||
const Detail = (props) => {
|
||||
|
||||
const formRef = useRef();
|
||||
let now = new Date();
|
||||
const [inputs, setInputs] = useState({
|
||||
username: '',
|
||||
token_name: '',
|
||||
model_name: '',
|
||||
start_timestamp: timestamp2string(now.getTime() / 1000 - 86400),
|
||||
start_timestamp: localStorage.getItem('data_export_default_time') === 'hour' ? timestamp2string(now.getTime() / 1000 - 86400) : (localStorage.getItem('data_export_default_time') === 'week' ? timestamp2string(now.getTime() / 1000 - 86400 * 30) : timestamp2string(now.getTime() / 1000 - 86400 * 7)),
|
||||
end_timestamp: timestamp2string(now.getTime() / 1000 + 3600),
|
||||
channel: ''
|
||||
channel: '',
|
||||
data_export_default_time: ''
|
||||
});
|
||||
const {username, token_name, model_name, start_timestamp, end_timestamp, channel} = inputs;
|
||||
const {username, model_name, start_timestamp, end_timestamp, channel} = inputs;
|
||||
const isAdminUser = isAdmin();
|
||||
const initialized = useRef(false)
|
||||
const [modelDataChart, setModelDataChart] = useState(null);
|
||||
@ -31,8 +32,13 @@ const Detail = (props) => {
|
||||
const [quotaData, setQuotaData] = useState([]);
|
||||
const [consumeQuota, setConsumeQuota] = useState(0);
|
||||
const [times, setTimes] = useState(0);
|
||||
const [dataExportDefaultTime, setDataExportDefaultTime] = useState(localStorage.getItem('data_export_default_time') || 'hour');
|
||||
|
||||
const handleInputChange = (value, name) => {
|
||||
if (name === 'data_export_default_time') {
|
||||
setDataExportDefaultTime(value);
|
||||
return
|
||||
}
|
||||
setInputs((inputs) => ({...inputs, [name]: value}));
|
||||
};
|
||||
|
||||
@ -41,8 +47,7 @@ const Detail = (props) => {
|
||||
data: [
|
||||
{
|
||||
id: 'barData',
|
||||
values: [
|
||||
]
|
||||
values: []
|
||||
}
|
||||
],
|
||||
xField: 'Time',
|
||||
@ -54,7 +59,7 @@ const Detail = (props) => {
|
||||
},
|
||||
title: {
|
||||
visible: true,
|
||||
text: '模型消耗分布(小时)',
|
||||
text: '模型消耗分布',
|
||||
subtext: '0'
|
||||
},
|
||||
bar: {
|
||||
@ -163,9 +168,9 @@ const Detail = (props) => {
|
||||
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
||||
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
||||
if (isAdminUser) {
|
||||
url = `/api/data/?username=${username}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
|
||||
url = `/api/data/?username=${username}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&default_time=${dataExportDefaultTime}`;
|
||||
} else {
|
||||
url = `/api/data/self/?start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
|
||||
url = `/api/data/self/?start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&default_time=${dataExportDefaultTime}`;
|
||||
}
|
||||
const res = await API.get(url);
|
||||
const {success, message, data} = res.data;
|
||||
@ -179,6 +184,16 @@ const Detail = (props) => {
|
||||
'created_at': now.getTime() / 1000
|
||||
})
|
||||
}
|
||||
// 根据dataExportDefaultTime重制时间粒度
|
||||
let timeGranularity = 3600;
|
||||
if (dataExportDefaultTime === 'day') {
|
||||
timeGranularity = 86400;
|
||||
} else if (dataExportDefaultTime === 'week') {
|
||||
timeGranularity = 604800;
|
||||
}
|
||||
data.forEach(item => {
|
||||
item['created_at'] = Math.floor(item['created_at'] / timeGranularity) * timeGranularity;
|
||||
});
|
||||
updateChart(lineChart, pieChart, data);
|
||||
} else {
|
||||
showError(message);
|
||||
@ -231,7 +246,7 @@ const Detail = (props) => {
|
||||
}
|
||||
// 合并created_at和model_name 为 lineData, created_at 数据类型是小时的时间戳
|
||||
// 转换日期格式
|
||||
let createTime = timestamp2string1(item.created_at);
|
||||
let createTime = timestamp2string1(item.created_at, dataExportDefaultTime);
|
||||
let lineItem = lineData.find(it => it.Time === createTime && it.Model === item.model_name);
|
||||
if (lineItem) {
|
||||
lineItem.Usage += parseFloat(getQuotaWithUnit(item.quota));
|
||||
@ -263,6 +278,13 @@ const Detail = (props) => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// setDataExportDefaultTime(localStorage.getItem('data_export_default_time'));
|
||||
// if (dataExportDefaultTime === 'day') {
|
||||
// // 设置开始时间为7天前
|
||||
// let st = timestamp2string(now.getTime() / 1000 - 86400 * 7)
|
||||
// inputs.start_timestamp = st;
|
||||
// formRef.current.formApi.setValue('start_timestamp', st);
|
||||
// }
|
||||
if (!initialized.current) {
|
||||
initialized.current = true;
|
||||
initChart();
|
||||
@ -276,7 +298,7 @@ const Detail = (props) => {
|
||||
<h3>数据看板</h3>
|
||||
</Layout.Header>
|
||||
<Layout.Content>
|
||||
<Form layout='horizontal' style={{marginTop: 10}}>
|
||||
<Form ref={formRef} layout='horizontal' style={{marginTop: 10}}>
|
||||
<>
|
||||
<Form.DatePicker field="start_timestamp" label='起始时间' style={{width: 272}}
|
||||
initValue={start_timestamp}
|
||||
@ -288,6 +310,18 @@ const Detail = (props) => {
|
||||
value={end_timestamp} type='dateTime'
|
||||
name='end_timestamp'
|
||||
onChange={value => handleInputChange(value, 'end_timestamp')}/>
|
||||
<Form.Select field="data_export_default_time" label='时间粒度' style={{width: 176}}
|
||||
initValue={dataExportDefaultTime}
|
||||
placeholder={'时间粒度'} name='data_export_default_time'
|
||||
optionList={
|
||||
[
|
||||
{label: '小时', value: 'hour'},
|
||||
{label: '天', value: 'day'},
|
||||
{label: '周', value: 'week'}
|
||||
]
|
||||
}
|
||||
onChange={value => handleInputChange(value, 'data_export_default_time')}>
|
||||
</Form.Select>
|
||||
{
|
||||
isAdminUser && <>
|
||||
<Form.Input field="username" label='用户名称' style={{width: 176}} value={username}
|
||||
|
Loading…
Reference in New Issue
Block a user