Просмотр исходного кода

fix: validate number input in renderQuotaNumberWithDigit and improve data handling in Detail page

- Added input validation to ensure that the `num` parameter in `renderQuotaNumberWithDigit` is a valid number, returning 0 for invalid inputs.
- Updated the `Detail` component to use `datum['rawQuota']` instead of `datum['Usage']` for rendering quota values, ensuring more accurate data representation.
- Enhanced data aggregation logic to handle cases where quota values may be missing or invalid, improving overall data integrity in charts and tables.
- Removed unnecessary time granularity calculations and streamlined the data processing for better performance.
CalciumIon 1 год назад
Родитель
Сommit
bfba4866a5
3 измененных файлов с 87 добавлено и 61 удалено
  1. 3 0
      web/src/helpers/render.js
  2. 3 0
      web/src/helpers/utils.js
  3. 81 61
      web/src/pages/Detail/index.js

+ 3 - 0
web/src/helpers/render.js

@@ -59,6 +59,9 @@ export function renderNumber(num) {
 }
 }
 
 
 export function renderQuotaNumberWithDigit(num, digits = 2) {
 export function renderQuotaNumberWithDigit(num, digits = 2) {
+  if (typeof num !== 'number' || isNaN(num)) {
+    return 0;
+  }
   let displayInCurrency = localStorage.getItem('display_in_currency');
   let displayInCurrency = localStorage.getItem('display_in_currency');
   num = num.toFixed(digits);
   num = num.toFixed(digits);
   if (displayInCurrency) {
   if (displayInCurrency) {

+ 3 - 0
web/src/helpers/utils.js

@@ -180,6 +180,9 @@ export function timestamp2string1(timestamp, dataExportDefaultTime = 'hour') {
   let month = (date.getMonth() + 1).toString();
   let month = (date.getMonth() + 1).toString();
   let day = date.getDate().toString();
   let day = date.getDate().toString();
   let hour = date.getHours().toString();
   let hour = date.getHours().toString();
+  if (day === '24') {
+    console.log("timestamp", timestamp);
+  }
   if (month.length === 1) {
   if (month.length === 1) {
     month = '0' + month;
     month = '0' + month;
   }
   }

+ 81 - 61
web/src/pages/Detail/index.js

@@ -143,8 +143,7 @@ const Detail = (props) => {
         content: [
         content: [
           {
           {
             key: (datum) => datum['Model'],
             key: (datum) => datum['Model'],
-            value: (datum) =>
-              renderQuotaNumberWithDigit(parseFloat(datum['Usage']), 4),
+            value: (datum) => renderQuotaNumberWithDigit(datum['rawQuota'] || 0, 4),
           },
           },
         ],
         ],
       },
       },
@@ -152,22 +151,28 @@ const Detail = (props) => {
         content: [
         content: [
           {
           {
             key: (datum) => datum['Model'],
             key: (datum) => datum['Model'],
-            value: (datum) => datum['Usage'],
+            value: (datum) => datum['rawQuota'] || 0,
           },
           },
         ],
         ],
         updateContent: (array) => {
         updateContent: (array) => {
           array.sort((a, b) => b.value - a.value);
           array.sort((a, b) => b.value - a.value);
           let sum = 0;
           let sum = 0;
           for (let i = 0; i < array.length; i++) {
           for (let i = 0; i < array.length; i++) {
-            sum += parseFloat(array[i].value);
-            array[i].value = renderQuotaNumberWithDigit(
-              parseFloat(array[i].value),
-              4,
-            );
+            if (array[i].key == "其他") {
+              continue;
+            }
+            let value = parseFloat(array[i].value);
+            if (isNaN(value)) {
+              value = 0;
+            }
+            if (array[i].datum && array[i].datum.TimeSum) {
+              sum = array[i].datum.TimeSum;
+            }
+            array[i].value = renderQuota(value, 4);
           }
           }
           array.unshift({
           array.unshift({
             key: t('总计'),
             key: t('总计'),
-            value: renderQuotaNumberWithDigit(sum, 4),
+            value: renderQuota(sum, 4),
           });
           });
           return array;
           return array;
         },
         },
@@ -212,19 +217,8 @@ const Detail = (props) => {
             created_at: now.getTime() / 1000,
             created_at: now.getTime() / 1000,
           });
           });
         }
         }
-        // 根据dataExportDefaultTime重制时间粒度
-        let timeGranularity = 3600;
-        if (dataExportDefaultTime === 'day') {
-          timeGranularity = 86400;
-        } else if (dataExportDefaultTime === 'week') {
-          timeGranularity = 604800;
-        }
         // sort created_at
         // sort created_at
         data.sort((a, b) => a.created_at - b.created_at);
         data.sort((a, b) => a.created_at - b.created_at);
-        data.forEach((item) => {
-          item['created_at'] =
-            Math.floor(item['created_at'] / timeGranularity) * timeGranularity;
-        });
         updateChartData(data);
         updateChartData(data);
       } else {
       } else {
         showError(message);
         showError(message);
@@ -250,14 +244,14 @@ const Detail = (props) => {
     let uniqueModels = new Set();
     let uniqueModels = new Set();
     let totalTokens = 0;
     let totalTokens = 0;
 
 
-    // 收集所有唯一的模型名称和时间点
-    let uniqueTimes = new Set();
+    // 收集所有唯一的模型名称
     data.forEach(item => {
     data.forEach(item => {
       uniqueModels.add(item.model_name);
       uniqueModels.add(item.model_name);
-      uniqueTimes.add(timestamp2string1(item.created_at, dataExportDefaultTime));
       totalTokens += item.token_used;
       totalTokens += item.token_used;
+      totalQuota += item.quota;
+      totalTimes += item.count;
     });
     });
-    
+
     // 处理颜色映射
     // 处理颜色映射
     const newModelColors = {};
     const newModelColors = {};
     Array.from(uniqueModels).forEach((modelName) => {
     Array.from(uniqueModels).forEach((modelName) => {
@@ -267,56 +261,82 @@ const Detail = (props) => {
     });
     });
     setModelColors(newModelColors);
     setModelColors(newModelColors);
 
 
-    // 处理饼图数据
-    for (let item of data) {
-      totalQuota += item.quota;
-      totalTimes += item.count;
-      
-      let pieItem = newPieData.find((it) => it.type === item.model_name);
-      if (pieItem) {
-        pieItem.value += item.count;
-      } else {
-        newPieData.push({
-          type: item.model_name,
-          value: item.count,
+    // 按时间和模型聚合数据
+    let aggregatedData = new Map();
+    data.forEach(item => {
+      const timeKey = timestamp2string1(item.created_at, dataExportDefaultTime);
+      const modelKey = item.model_name;
+      const key = `${timeKey}-${modelKey}`;
+
+      if (!aggregatedData.has(key)) {
+        aggregatedData.set(key, {
+          time: timeKey,
+          model: modelKey,
+          quota: 0,
+          count: 0
         });
         });
       }
       }
+      
+      const existing = aggregatedData.get(key);
+      existing.quota += item.quota;
+      existing.count += item.count;
+    });
+
+    // 处理饼图数据
+    let modelTotals = new Map();
+    for (let [_, value] of aggregatedData) {
+      if (!modelTotals.has(value.model)) {
+        modelTotals.set(value.model, 0);
+      }
+      modelTotals.set(value.model, modelTotals.get(value.model) + value.count);
     }
     }
 
 
-    // 处理柱状图数据
-    let timePoints = Array.from(uniqueTimes);
+    newPieData = Array.from(modelTotals).map(([model, count]) => ({
+      type: model,
+      value: count
+    }));
+
+    // 生成时间点序列
+    let timePoints = Array.from(new Set([...aggregatedData.values()].map(d => d.time)));
     if (timePoints.length < 7) {
     if (timePoints.length < 7) {
-      // 根据时间粒度生成合适的时间点
-      const generateTimePoints = () => {
-        let lastTime = Math.max(...data.map(item => item.created_at));
-        let points = [];
-        let interval = dataExportDefaultTime === 'hour' ? 3600 
+      const lastTime = Math.max(...data.map(item => item.created_at));
+      const interval = dataExportDefaultTime === 'hour' ? 3600 
                       : dataExportDefaultTime === 'day' ? 86400 
                       : dataExportDefaultTime === 'day' ? 86400 
                       : 604800;
                       : 604800;
-
-        for (let i = 0; i < 7; i++) {
-          points.push(timestamp2string1(lastTime - (i * interval), dataExportDefaultTime));
-        }
-        return points.reverse();
-      };
-
-      timePoints = generateTimePoints();
+      
+      timePoints = Array.from({length: 7}, (_, i) => 
+        timestamp2string1(lastTime - (6-i) * interval, dataExportDefaultTime)
+      );
     }
     }
 
 
-    // 为每个时间点和模型生成数据
+    // 生成柱状图数据
     timePoints.forEach(time => {
     timePoints.forEach(time => {
-      Array.from(uniqueModels).forEach(model => {
-        let existingData = data.find(item => 
-          timestamp2string1(item.created_at, dataExportDefaultTime) === time && 
-          item.model_name === model
-        );
-
-        newLineData.push({
+      // 为每个时间点收集所有模型的数据
+      let timeData = Array.from(uniqueModels).map(model => {
+        const key = `${time}-${model}`;
+        const aggregated = aggregatedData.get(key);
+        return {
           Time: time,
           Time: time,
           Model: model,
           Model: model,
-          Usage: existingData ? parseFloat(getQuotaWithUnit(existingData.quota)) : 0
-        });
+          rawQuota: aggregated?.quota || 0,
+          Usage: aggregated?.quota ? getQuotaWithUnit(aggregated.quota, 4) : 0
+        };
       });
       });
+      
+      // 计算该时间点的总计
+      const timeSum = timeData.reduce((sum, item) => sum + item.rawQuota, 0);
+      
+      // 按照 rawQuota 从大到小排序
+      timeData.sort((a, b) => b.rawQuota - a.rawQuota);
+      
+      // 为每个数据点添加该时间的总计
+      timeData = timeData.map(item => ({
+        ...item,
+        TimeSum: timeSum
+      }));
+      
+      // 将排序后的数据添加到 newLineData
+      newLineData.push(...timeData);
     });
     });
 
 
     // 排序
     // 排序