From 00306aa1423c5395f0d13a45887b291b393e2fbe Mon Sep 17 00:00:00 2001
From: CaIon <1808837298@qq.com>
Date: Sat, 13 Jan 2024 00:33:52 +0800
Subject: [PATCH] =?UTF-8?q?perf:=20=E6=95=B0=E6=8D=AE=E7=9C=8B=E6=9D=BF?=
=?UTF-8?q?=E6=94=AF=E6=8C=81=E9=80=89=E6=8B=A9=E6=97=B6=E9=97=B4=E7=B2=92?=
=?UTF-8?q?=E5=BA=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
common/constants.go | 3 +-
controller/misc.go | 41 ++++++++---------
model/option.go | 3 ++
web/src/App.js | 1 +
web/src/components/OperationSetting.js | 62 +++++++++++++++++---------
web/src/helpers/utils.js | 27 ++++++-----
web/src/pages/Detail/index.js | 60 +++++++++++++++++++------
7 files changed, 132 insertions(+), 65 deletions(-)
diff --git a/common/constants.go b/common/constants.go
index 8369d8a..212649a 100644
--- a/common/constants.go
+++ b/common/constants.go
@@ -26,7 +26,8 @@ var DisplayInCurrencyEnabled = true
var DisplayTokenStatEnabled = true
var DrawingEnabled = true
var DataExportEnabled = true
-var DataExportInterval = 5 // unit: minute
+var DataExportInterval = 5 // unit: minute
+var DataExportDefaultTime = "hour" // unit: minute
// Any options with "Secret", "Token" in its key won't be return by GetOptions
diff --git a/controller/misc.go b/controller/misc.go
index 80cb795..e8dc843 100644
--- a/controller/misc.go
+++ b/controller/misc.go
@@ -16,26 +16,27 @@ func GetStatus(c *gin.Context) {
"success": true,
"message": "",
"data": gin.H{
- "start_time": common.StartTime,
- "email_verification": common.EmailVerificationEnabled,
- "github_oauth": common.GitHubOAuthEnabled,
- "github_client_id": common.GitHubClientId,
- "system_name": common.SystemName,
- "logo": common.Logo,
- "footer_html": common.Footer,
- "wechat_qrcode": common.WeChatAccountQRCodeImageURL,
- "wechat_login": common.WeChatAuthEnabled,
- "server_address": common.ServerAddress,
- "price": common.Price,
- "turnstile_check": common.TurnstileCheckEnabled,
- "turnstile_site_key": common.TurnstileSiteKey,
- "top_up_link": common.TopUpLink,
- "chat_link": common.ChatLink,
- "quota_per_unit": common.QuotaPerUnit,
- "display_in_currency": common.DisplayInCurrencyEnabled,
- "enable_batch_update": common.BatchUpdateEnabled,
- "enable_drawing": common.DrawingEnabled,
- "enable_data_export": common.DataExportEnabled,
+ "start_time": common.StartTime,
+ "email_verification": common.EmailVerificationEnabled,
+ "github_oauth": common.GitHubOAuthEnabled,
+ "github_client_id": common.GitHubClientId,
+ "system_name": common.SystemName,
+ "logo": common.Logo,
+ "footer_html": common.Footer,
+ "wechat_qrcode": common.WeChatAccountQRCodeImageURL,
+ "wechat_login": common.WeChatAuthEnabled,
+ "server_address": common.ServerAddress,
+ "price": common.Price,
+ "turnstile_check": common.TurnstileCheckEnabled,
+ "turnstile_site_key": common.TurnstileSiteKey,
+ "top_up_link": common.TopUpLink,
+ "chat_link": common.ChatLink,
+ "quota_per_unit": common.QuotaPerUnit,
+ "display_in_currency": common.DisplayInCurrencyEnabled,
+ "enable_batch_update": common.BatchUpdateEnabled,
+ "enable_drawing": common.DrawingEnabled,
+ "enable_data_export": common.DataExportEnabled,
+ "data_export_default_time": common.DataExportDefaultTime,
},
})
return
diff --git a/model/option.go b/model/option.go
index e32cd93..5a1b453 100644
--- a/model/option.go
+++ b/model/option.go
@@ -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":
diff --git a/web/src/App.js b/web/src/App.js
index 40e2175..02db609 100644
--- a/web/src/App.js
+++ b/web/src/App.js
@@ -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 {
diff --git a/web/src/components/OperationSetting.js b/web/src/components/OperationSetting.js
index ef82bbc..88f2d92 100644
--- a/web/src/components/OperationSetting.js
+++ b/web/src/components/OperationSetting.js
@@ -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}
/>
-
-
-
+
+ {
+ setHistoryTimestamp(value);
+ }}/>
+
+ {
+ deleteHistoryLogs().then();
+ }}>清理历史日志
+
+
+
+
{
value={inputs.DataExportInterval}
placeholder='数据看板更新间隔(分钟,设置过短会影响数据库性能)'
/>
+
-
- {
- setHistoryTimestamp(value);
- }}/>
-
- {
- deleteHistoryLogs().then();
- }}>清理历史日志
-
diff --git a/web/src/helpers/utils.js b/web/src/helpers/utils.js
index e881f37..3e1fdb2 100644
--- a/web/src/helpers/utils.js
+++ b/web/src/helpers/utils.js
@@ -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) {
diff --git a/web/src/pages/Detail/index.js b/web/src/pages/Detail/index.js
index fbaf7ca..04f7b98 100644
--- a/web/src/pages/Detail/index.js
+++ b/web/src/pages/Detail/index.js
@@ -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: {
@@ -104,7 +109,7 @@ const Detail = (props) => {
{
id: 'id0',
values: [
- { type: 'null', value: '0' },
+ {type: 'null', value: '0'},
]
}
],
@@ -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);
@@ -190,7 +205,7 @@ const Detail = (props) => {
await loadQuotaData(modelDataChart, modelDataPieChart);
};
- const initChart = async () => {
+ const initChart = async () => {
let lineChart = modelDataChart
if (!modelDataChart) {
lineChart = new VChart(spec_line, {dom: 'model_data'});
@@ -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) => {
数据看板
- {
value={end_timestamp} type='dateTime'
name='end_timestamp'
onChange={value => handleInputChange(value, 'end_timestamp')}/>
+ handleInputChange(value, 'data_export_default_time')}>
+
{
isAdminUser && <>