feat: 完善数据看板选择时间区间

This commit is contained in:
CaIon 2024-01-07 19:47:35 +08:00
parent ce05e7dd86
commit 1c2bba8979
5 changed files with 106 additions and 78 deletions

View File

@ -4,10 +4,33 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
"one-api/model" "one-api/model"
"strconv"
) )
func GetAllQuotaDates(c *gin.Context) { func GetAllQuotaDates(c *gin.Context) {
dates, err := model.GetAllQuotaDates() startTimestamp, _ := strconv.ParseInt(c.Query("start_timestamp"), 10, 64)
endTimestamp, _ := strconv.ParseInt(c.Query("end_timestamp"), 10, 64)
dates, err := model.GetAllQuotaDates(startTimestamp, endTimestamp)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
"data": dates,
})
return
}
func GetUserQuotaDates(c *gin.Context) {
userId := c.GetInt("id")
startTimestamp, _ := strconv.ParseInt(c.Query("start_timestamp"), 10, 64)
endTimestamp, _ := strconv.ParseInt(c.Query("end_timestamp"), 10, 64)
dates, err := model.GetQuotaDataByUserId(userId, startTimestamp, endTimestamp)
if err != nil { if err != nil {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,

View File

@ -74,16 +74,23 @@ func SaveQuotaDataCache() {
CacheQuotaData = make(map[string]*QuotaData) CacheQuotaData = make(map[string]*QuotaData)
} }
func GetQuotaDataByUsername(username string) (quotaData []*QuotaData, err error) { func GetQuotaDataByUsername(username string, startTime int64, endTime int64) (quotaData []*QuotaData, err error) {
var quotaDatas []*QuotaData var quotaDatas []*QuotaData
// 从quota_data表中查询数据 // 从quota_data表中查询数据
err = DB.Table("quota_data").Where("username = ?", username).Find(&quotaDatas).Error err = DB.Table("quota_data").Where("username = ?", username).Find(&quotaDatas).Error
return quotaDatas, err return quotaDatas, err
} }
func GetAllQuotaDates() (quotaData []*QuotaData, err error) { func GetQuotaDataByUserId(userId int, startTime int64, endTime int64) (quotaData []*QuotaData, err error) {
var quotaDatas []*QuotaData var quotaDatas []*QuotaData
// 从quota_data表中查询数据 // 从quota_data表中查询数据
err = DB.Table("quota_data").Find(&quotaDatas).Error err = DB.Table("quota_data").Where("user_id = ? and created_at >= ? and created_at <= ?", userId, startTime, endTime).Find(&quotaDatas).Error
return quotaDatas, err
}
func GetAllQuotaDates(startTime int64, endTime int64) (quotaData []*QuotaData, err error) {
var quotaDatas []*QuotaData
// 从quota_data表中查询数据
err = DB.Table("quota_data").Where("created_at >= ? and created_at <= ?", startTime, endTime).Find(&quotaDatas).Error
return quotaDatas, err return quotaDatas, err
} }

View File

@ -116,6 +116,7 @@ func SetApiRouter(router *gin.Engine) {
dataRoute := apiRouter.Group("/data") dataRoute := apiRouter.Group("/data")
dataRoute.GET("/", middleware.AdminAuth(), controller.GetAllQuotaDates) dataRoute.GET("/", middleware.AdminAuth(), controller.GetAllQuotaDates)
dataRoute.GET("/self", middleware.UserAuth(), controller.GetUserQuotaDates)
logRoute.Use(middleware.CORS()) logRoute.Use(middleware.CORS())
{ {

View File

@ -422,7 +422,6 @@ const LogsTable = () => {
value={end_timestamp} type='dateTime' value={end_timestamp} type='dateTime'
name='end_timestamp' name='end_timestamp'
onChange={value => handleInputChange(value, 'end_timestamp')}/> onChange={value => handleInputChange(value, 'end_timestamp')}/>
{/*<Form.Button fluid label='操作' width={2} onClick={refresh}>查询</Form.Button>*/}
{ {
isAdminUser && <> isAdminUser && <>
<Form.Input field="channel" label='渠道 ID' style={{width: 176}} value={channel} <Form.Input field="channel" label='渠道 ID' style={{width: 176}} value={channel}

View File

@ -1,9 +1,8 @@
import React, {useEffect, useState} from 'react'; import React, {useEffect, useRef, useState} from 'react';
import {Button, Col, Form, Layout, Row} from "@douyinfe/semi-ui"; import {Button, Col, Form, Layout, Row} from "@douyinfe/semi-ui";
import VChart from '@visactor/vchart'; import VChart from '@visactor/vchart';
import {useEffectOnce} from "usehooks-ts"; import {useEffectOnce} from "usehooks-ts";
import {API, isAdmin, showError, timestamp2string, timestamp2string1} from "../../helpers"; import {API, isAdmin, showError, timestamp2string, timestamp2string1} from "../../helpers";
import {ITEMS_PER_PAGE} from "../../constants";
import {getQuotaWithUnit} from "../../helpers/render"; import {getQuotaWithUnit} from "../../helpers/render";
const Detail = (props) => { const Detail = (props) => {
@ -19,8 +18,9 @@ const Detail = (props) => {
}); });
const {username, token_name, model_name, start_timestamp, end_timestamp, channel} = inputs; const {username, token_name, model_name, start_timestamp, end_timestamp, channel} = inputs;
const isAdminUser = isAdmin(); const isAdminUser = isAdmin();
let modelDataChart = null; const initialized = useRef(false)
let modelDataPieChart = null; const [modelDataChart, setModelDataChart] = useState(null);
const [modelDataPieChart, setModelDataPieChart] = useState(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [quotaData, setQuotaData] = useState([]); const [quotaData, setQuotaData] = useState([]);
const [quotaDataPie, setQuotaDataPie] = useState([]); const [quotaDataPie, setQuotaDataPie] = useState([]);
@ -116,22 +116,22 @@ const Detail = (props) => {
} }
}; };
const loadQuotaData = async () => { const loadQuotaData = async (lineChart, pieChart) => {
setLoading(true); setLoading(true);
let url = ''; let url = '';
let localStartTimestamp = Date.parse(start_timestamp) / 1000; let localStartTimestamp = Date.parse(start_timestamp) / 1000;
let localEndTimestamp = Date.parse(end_timestamp) / 1000; let localEndTimestamp = Date.parse(end_timestamp) / 1000;
if (isAdminUser) { if (isAdminUser) {
url = `/api/data`; url = `/api/data/?username=${username}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
} else { } else {
url = `/api/data/self`; url = `/api/data/self/?start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
} }
const res = await API.get(url); const res = await API.get(url);
const {success, message, data} = res.data; const {success, message, data} = res.data;
if (success) { if (success) {
setQuotaData(data); setQuotaData(data);
updateChart(data); updateChart(lineChart, pieChart, data);
} else { } else {
showError(message); showError(message);
} }
@ -139,19 +139,37 @@ const Detail = (props) => {
}; };
const refresh = async () => { const refresh = async () => {
await loadQuotaData(); await loadQuotaData(modelDataChart, modelDataPieChart);
}; };
const updateChart = (data) => { const initChart = async () => {
let lineChart = modelDataChart
if (!modelDataChart) {
lineChart = new VChart(spec_line, {dom: 'model_data'});
setModelDataChart(lineChart);
await lineChart.renderAsync();
}
let pieChart = modelDataPieChart
if (!modelDataPieChart) {
pieChart = new VChart(spec_pie, {dom: 'model_pie'});
setModelDataPieChart(pieChart);
await pieChart.renderAsync();
}
console.log('init vchart');
await loadQuotaData(lineChart, pieChart)
}
const updateChart = (lineChart, pieChart, data) => {
if (isAdminUser) { if (isAdminUser) {
// 将所有用户的数据累加 // 将所有用户合并
}
let pieData = []; let pieData = [];
let lineData = []; let lineData = [];
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
const item = data[i]; const item = data[i];
const {count, id, model_name, quota, user_id, username} = item; const {count, id, model_name, quota, user_id, username} = item;
// 合并model_name // 合并model_name
let pieItem = pieData.find(item => item.model_name === model_name); let pieItem = pieData.find(item => item.type === model_name);
if (pieItem) { if (pieItem) {
pieItem.count += count; pieItem.count += count;
} else { } else {
@ -177,36 +195,17 @@ const Detail = (props) => {
} }
// sort by count // sort by count
pieData.sort((a, b) => b.value - a.value); pieData.sort((a, b) => b.value - a.value);
spec_line.data[0].values = lineData; pieChart.updateData('id0', pieData);
spec_pie.data[0].values = pieData; lineChart.updateData('barData', lineData);
// console.log('spec_line', spec_line);
console.log('spec_pie', spec_pie);
// modelDataChart.renderAsync();
modelDataPieChart.updateSpec(spec_pie);
modelDataChart.updateSpec(spec_line);
}
}
useEffect(() => { }
refresh();
}, []);
useEffectOnce(() => { useEffectOnce(() => {
// 创建 vchart 实例 if (!initialized.current) {
if (!modelDataChart) { initialized.current = true;
modelDataChart = new VChart(spec_line, {dom: 'model_data'}); initChart();
// 绘制
modelDataChart.renderAsync();
} }
});
if (!modelDataPieChart) {
modelDataPieChart = new VChart(spec_pie, {dom: 'model_pie'});
// 绘制
modelDataPieChart.renderAsync();
}
console.log('render vchart');
})
return ( return (
<> <>
@ -227,7 +226,6 @@ const Detail = (props) => {
value={end_timestamp} type='dateTime' value={end_timestamp} type='dateTime'
name='end_timestamp' name='end_timestamp'
onChange={value => handleInputChange(value, 'end_timestamp')}/> onChange={value => handleInputChange(value, 'end_timestamp')}/>
{/*<Form.Button fluid label='操作' width={2} onClick={refresh}>查询</Form.Button>*/}
{/*{*/} {/*{*/}
{/* isAdminUser && <>*/} {/* isAdminUser && <>*/}
{/* <Form.Input field="username" label='用户名称' style={{width: 176}} value={username}*/} {/* <Form.Input field="username" label='用户名称' style={{width: 176}} value={username}*/}
@ -235,10 +233,10 @@ const Detail = (props) => {
{/* onChange={value => handleInputChange(value, 'username')}/>*/} {/* onChange={value => handleInputChange(value, 'username')}/>*/}
{/* </>*/} {/* </>*/}
{/*}*/} {/*}*/}
{/*<Form.Section>*/} <Form.Section>
{/* <Button label='查询' type="primary" htmlType="submit" className="btn-margin-right"*/} <Button label='查询' type="primary" htmlType="submit" className="btn-margin-right"
{/* >查询</Button>*/} onClick={refresh}>查询</Button>
{/*</Form.Section>*/} </Form.Section>
</> </>
</Form> </Form>
<div style={{height: 500}}> <div style={{height: 500}}>