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 && <>