mirror of
https://github.com/linux-do/new-api.git
synced 2025-09-17 16:06:38 +08:00
feat: 完善数据看板选择时间区间
This commit is contained in:
parent
ce05e7dd86
commit
1c2bba8979
@ -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,
|
||||||
|
@ -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("aDatas).Error
|
err = DB.Table("quota_data").Where("username = ?", username).Find("aDatas).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("aDatas).Error
|
err = DB.Table("quota_data").Where("user_id = ? and created_at >= ? and created_at <= ?", userId, startTime, endTime).Find("aDatas).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("aDatas).Error
|
||||||
return quotaDatas, err
|
return quotaDatas, err
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
{
|
{
|
||||||
|
@ -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}
|
||||||
|
@ -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,74 +139,73 @@ const Detail = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
await loadQuotaData();
|
await loadQuotaData(modelDataChart, modelDataPieChart);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateChart = (data) => {
|
const initChart = async () => {
|
||||||
if (isAdminUser) {
|
let lineChart = modelDataChart
|
||||||
// 将所有用户的数据累加
|
if (!modelDataChart) {
|
||||||
let pieData = [];
|
lineChart = new VChart(spec_line, {dom: 'model_data'});
|
||||||
let lineData = [];
|
setModelDataChart(lineChart);
|
||||||
for (let i = 0; i < data.length; i++) {
|
await lineChart.renderAsync();
|
||||||
const item = data[i];
|
|
||||||
const {count, id, model_name, quota, user_id, username} = item;
|
|
||||||
// 合并model_name
|
|
||||||
let pieItem = pieData.find(item => item.model_name === model_name);
|
|
||||||
if (pieItem) {
|
|
||||||
pieItem.count += count;
|
|
||||||
} else {
|
|
||||||
pieData.push({
|
|
||||||
"type": model_name,
|
|
||||||
"value": count
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// 合并created_at和model_name 为 lineData, created_at 数据类型是小时的时间戳
|
|
||||||
// 转换日期格式
|
|
||||||
let createTime = timestamp2string1(item.created_at);
|
|
||||||
let lineItem = lineData.find(item => item.Time === item.createTime && item.Model === model_name);
|
|
||||||
if (lineItem) {
|
|
||||||
lineItem.Usage += getQuotaWithUnit(quota);
|
|
||||||
} else {
|
|
||||||
lineData.push({
|
|
||||||
"Time": createTime,
|
|
||||||
"Model": model_name,
|
|
||||||
"Usage": getQuotaWithUnit(quota)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// sort by count
|
|
||||||
pieData.sort((a, b) => b.value - a.value);
|
|
||||||
spec_line.data[0].values = lineData;
|
|
||||||
spec_pie.data[0].values = pieData;
|
|
||||||
// console.log('spec_line', spec_line);
|
|
||||||
console.log('spec_pie', spec_pie);
|
|
||||||
// modelDataChart.renderAsync();
|
|
||||||
modelDataPieChart.updateSpec(spec_pie);
|
|
||||||
modelDataChart.updateSpec(spec_line);
|
|
||||||
}
|
}
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
const updateChart = (lineChart, pieChart, data) => {
|
||||||
refresh();
|
if (isAdminUser) {
|
||||||
}, []);
|
// 将所有用户合并
|
||||||
|
}
|
||||||
|
let pieData = [];
|
||||||
|
let lineData = [];
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
const item = data[i];
|
||||||
|
const {count, id, model_name, quota, user_id, username} = item;
|
||||||
|
// 合并model_name
|
||||||
|
let pieItem = pieData.find(item => item.type === model_name);
|
||||||
|
if (pieItem) {
|
||||||
|
pieItem.count += count;
|
||||||
|
} else {
|
||||||
|
pieData.push({
|
||||||
|
"type": model_name,
|
||||||
|
"value": count
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 合并created_at和model_name 为 lineData, created_at 数据类型是小时的时间戳
|
||||||
|
// 转换日期格式
|
||||||
|
let createTime = timestamp2string1(item.created_at);
|
||||||
|
let lineItem = lineData.find(item => item.Time === item.createTime && item.Model === model_name);
|
||||||
|
if (lineItem) {
|
||||||
|
lineItem.Usage += getQuotaWithUnit(quota);
|
||||||
|
} else {
|
||||||
|
lineData.push({
|
||||||
|
"Time": createTime,
|
||||||
|
"Model": model_name,
|
||||||
|
"Usage": getQuotaWithUnit(quota)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// sort by count
|
||||||
|
pieData.sort((a, b) => b.value - a.value);
|
||||||
|
pieChart.updateData('id0', pieData);
|
||||||
|
lineChart.updateData('barData', lineData);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
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}}>
|
||||||
|
Loading…
Reference in New Issue
Block a user