Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
fe148d17
authored
Jan 13, 2024
by
CaIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
perf: 数据看板支持选择时间粒度
parent
d82930f7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
132 additions
and
65 deletions
+132
-65
common/constants.go
+2
-1
controller/misc.go
+21
-20
model/option.go
+3
-0
web/src/App.js
+1
-0
web/src/components/OperationSetting.js
+41
-21
web/src/helpers/utils.js
+17
-10
web/src/pages/Detail/index.js
+47
-13
No files found.
common/constants.go
View file @
fe148d17
...
...
@@ -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
...
...
controller/misc.go
View file @
fe148d17
...
...
@@ -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
...
...
model/option.go
View file @
fe148d17
...
...
@@ -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"
:
...
...
web/src/App.js
View file @
fe148d17
...
...
@@ -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
{
...
...
web/src/components/OperationSetting.js
View file @
fe148d17
...
...
@@ -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
.
Checkbox
checked
=
{
inputs
.
DataExportEnabled
===
'true'
}
label
=
'启用数据看板(实验性)'
name
=
'DataExportEnabled'
onChange
=
{
handleInputChange
}
/
>
<
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
>
...
...
web/src/helpers/utils.js
View file @
fe148d17
...
...
@@ -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
)
{
...
...
web/src/pages/Detail/index.js
View file @
fe148d17
...
...
@@ -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) => {
<
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
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment