Explorar el Código

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

CaIon hace 2 años
padre
commit
1c2bba8979
Se han modificado 5 ficheros con 105 adiciones y 77 borrados
  1. 24 1
      controller/usedata.go
  2. 10 3
      model/usedata.go
  3. 1 0
      router/api-router.go
  4. 0 1
      web/src/components/LogsTable.js
  5. 70 72
      web/src/pages/Detail/index.js

+ 24 - 1
controller/usedata.go

@@ -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,

+ 10 - 3
model/usedata.go

@@ -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
 }
 }

+ 1 - 0
router/api-router.go

@@ -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())
 		{
 		{

+ 0 - 1
web/src/components/LogsTable.js

@@ -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}

+ 70 - 72
web/src/pages/Detail/index.js

@@ -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;
-    let modelDataPieChart = null;
+    const initialized = useRef(false)
+    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) => {
-        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.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);
+    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)
     }
     }
 
 
-    useEffect(() => {
-        refresh();
-    }, []);
-
-    useEffectOnce(() => {
-        // 创建 vchart 实例
-        if (!modelDataChart) {
-            modelDataChart = new VChart(spec_line, {dom: 'model_data'});
-            // 绘制
-            modelDataChart.renderAsync();
+    const updateChart = (lineChart, pieChart, data) => {
+        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)
+                });
+            }
 
 
-        if (!modelDataPieChart) {
-            modelDataPieChart = new VChart(spec_pie, {dom: 'model_pie'});
-            // 绘制
-            modelDataPieChart.renderAsync();
         }
         }
+        // sort by count
+        pieData.sort((a, b) => b.value - a.value);
+        pieChart.updateData('id0', pieData);
+        lineChart.updateData('barData', lineData);
 
 
-        console.log('render vchart');
-    })
+    }
+
+    useEffectOnce(() => {
+        if (!initialized.current) {
+            initialized.current = true;
+            initChart();
+        }
+    });
 
 
     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>*/}
-                            {/*    <Button label='查询' type="primary" htmlType="submit" className="btn-margin-right"*/}
-                            {/*            >查询</Button>*/}
-                            {/*</Form.Section>*/}
+                            <Form.Section>
+                                <Button label='查询' type="primary" htmlType="submit" className="btn-margin-right"
+                                        onClick={refresh}>查询</Button>
+                            </Form.Section>
                         </>
                         </>
                     </Form>
                     </Form>
                     <div style={{height: 500}}>
                     <div style={{height: 500}}>