diff --git a/app/app/(admin)/admin/page.tsx b/app/app/(admin)/admin/page.tsx index e653a425b..29c830745 100644 --- a/app/app/(admin)/admin/page.tsx +++ b/app/app/(admin)/admin/page.tsx @@ -1,6 +1,6 @@ // "use client"; import { Grid, Col, Card, Text, AreaChart, Metric } from "@tremor/react"; -import UsageAnalysis from "./usage-analysis"; +// import UsageAnalysis from "./usage-analysis"; import UsageByModel from "./usage-by-model"; export default function AdminPage() { @@ -8,7 +8,8 @@ export default function AdminPage() { <> - + {/**/} + {/**/} diff --git a/app/app/(admin)/admin/usage-analysis.tsx b/app/app/(admin)/admin/usage-analysis.tsx index 345d49652..2349f74de 100644 --- a/app/app/(admin)/admin/usage-analysis.tsx +++ b/app/app/(admin)/admin/usage-analysis.tsx @@ -54,105 +54,7 @@ export default async function UsageAnalysis() { // @ts-ignore const log_data = HandleLogData(todayLog); - // console.log("======", log_data); - // const data = [ - // { - // name: "Twitter", - // value: 456, - // href: "https://twitter.com/tremorlabs", - // icon: function TwitterIcon() { - // return ( - // - // - // - // - // ); - // }, - // }, - // { - // name: "Google", - // value: 351, - // href: "https://google.com", - // icon: function GoogleIcon() { - // return ( - // - // - // - // - // ); - // }, - // }, - // { - // name: "GitHub", - // value: 271, - // href: "https://github.com/tremorlabs/tremor", - // icon: function GitHubIcon() { - // return ( - // - // - // - // - // ); - // }, - // }, - // { - // name: "Reddit", - // value: 191, - // href: "https://reddit.com", - // icon: function RedditIcon() { - // return ( - // - // - // - // - // ); - // }, - // }, - // { - // name: "Youtube", - // value: 91, - // href: "https://www.youtube.com/@tremorlabs3079", - // icon: function YouTubeIcon() { - // return ( - // - // - // - // - // ); - // }, - // }, - // ]; // @ts-ignore return ( diff --git a/app/app/(admin)/admin/usage-by-model-chart.tsx b/app/app/(admin)/admin/usage-by-model-chart.tsx index b327d2dbb..ba41dc53f 100644 --- a/app/app/(admin)/admin/usage-by-model-chart.tsx +++ b/app/app/(admin)/admin/usage-by-model-chart.tsx @@ -7,17 +7,11 @@ export default function UsageByModelChart({ }: { option: echarts.EChartsOption; }) { - // const EchartsComponent: React.FC = ({ data: any }) => { - // const [option, setOption] = useState({}); - console.log("======", option); - useEffect(() => { var chartDom = document.getElementById("usage-by-model-chart"); - // if (chartDom) echarts.dispose(chartDom); var myChart = echarts.init(chartDom); - // console.log('=======[option]', option) option && myChart.setOption(option); - }, []); // 空数组作为第二个参数,表示仅在组件挂载和卸载时执行 + }, [option]); // 空数组作为第二个参数,表示仅在组件挂载和卸载时执行 return (
); } - -// export default EchartsComponent; diff --git a/app/app/(admin)/admin/usage-by-model.tsx b/app/app/(admin)/admin/usage-by-model.tsx index ea432fec5..fbc17e74c 100644 --- a/app/app/(admin)/admin/usage-by-model.tsx +++ b/app/app/(admin)/admin/usage-by-model.tsx @@ -1,45 +1,202 @@ import * as echarts from "echarts"; import { EChartsOption } from "echarts"; import dynamic from "next/dynamic"; +import prisma from "@/lib/prisma"; +import { estimateTokenLength } from "@/app/utils/token"; const UsageByModelChart = dynamic(() => import("./usage-by-model-chart"), { ssr: false, }); -export default function UsageByModel() { +function HandleLogData(todayLog: [{ userName: string; logEntry: string }]) { + const data1 = todayLog.map((log) => { + return { + name: log.userName ?? "unknown", + value: estimateTokenLength(log.logEntry ?? ""), + }; + }); + + type Accumulator = { + [key: string]: number; + }; + const data2 = data1.reduce((acc, item) => { + if (acc[item.name]) { + acc[item.name] += item.value; + } else { + acc[item.name] = item.value; + } + return acc; + }, {}); + const data3 = Object.entries(data2) + .map(([name, value]) => ({ + name, + value, + })) + .sort((a, b) => { + return a.value - b.value; + }); + + const data4 = data3.reduce( + (acc, cur) => { + // @ts-ignore + acc.name.push(cur.name); + // @ts-ignore + acc.value.push(cur.value); + return acc; + }, + { name: [], value: [] }, + ); + return data4; +} + +export default async function UsageByModel() { + // 今天日期的开始和结束 + const startDate = new Date(); + startDate.setHours(0, 0, 0, 0); + + const endDate = new Date(); + endDate.setHours(23, 59, 59, 999); + const todayLog = await prisma.logEntry.findMany({ + where: { + createdAt: { + gte: startDate, // gte 表示 '大于等于' + lte: endDate, // lte 表示 '小于等于' + }, + }, + include: { + user: true, + }, + }); + + // @ts-ignore + const log_data = HandleLogData(todayLog); + + // console.log('[log_data]====---==', log_data) + const usageByModelOption: EChartsOption = { - title: { - text: "Referer of a Website", - subtext: "Fake Data", - left: "center", - }, tooltip: { - trigger: "item", + trigger: "axis", + axisPointer: { + type: "shadow", + }, }, - legend: { - orient: "vertical", - left: "left", + title: { + show: true, + text: "token使用分析", }, + legend: {}, + grid: { + left: "3%", + right: "4%", + bottom: "3%", + containLabel: true, + }, + xAxis: [ + { + type: "value", + }, + ], + yAxis: [ + { + type: "category", + data: log_data.name, + }, + ], series: [ { - name: "Access From", - type: "pie", - radius: "50%", - data: [ - { value: 1048, name: "Search Engine" }, - { value: 735, name: "Direct" }, - { value: 580, name: "Email" }, - { value: 484, name: "Union Ads" }, - { value: 300, name: "Video Ads" }, - ], + name: "总量", + type: "bar", emphasis: { - itemStyle: { - shadowBlur: 10, - shadowOffsetX: 0, - shadowColor: "rgba(0, 0, 0, 0.5)", - }, + focus: "series", }, + label: { + show: true, + position: "right", + }, + colorBy: "data", + // progress: { + // show: true + // }, + data: log_data.value, }, + // { + // name: 'Email', + // type: 'bar', + // stack: 'Ad', + // emphasis: { + // focus: 'series' + // }, + // data: [120, 132, 101, 134, 90, 230, 210] + // }, + // { + // name: 'Union Ads', + // type: 'bar', + // stack: 'Ad', + // emphasis: { + // focus: 'series' + // }, + // data: [220, 182, 191, 234, 290, 330, 310] + // }, + // { + // name: 'Video Ads', + // type: 'bar', + // stack: 'Ad', + // emphasis: { + // focus: 'series' + // }, + // data: [150, 232, 201, 154, 190, 330, 410] + // }, + // { + // name: 'Search Engine', + // type: 'bar', + // data: [862, 1018, 964, 1026, 1679, 1600, 1570], + // emphasis: { + // focus: 'series' + // }, + // // markLine: { + // // lineStyle: { + // // type: 'dashed' + // // }, + // // data: [[{ type: 'min' }, { type: 'max' }]] + // // } + // }, + // { + // name: 'Baidu', + // type: 'bar', + // barWidth: 5, + // stack: 'Search Engine', + // emphasis: { + // focus: 'series' + // }, + // data: [620, 732, 701, 734, 1090, 1130, 1120] + // }, + // { + // name: 'Google', + // type: 'bar', + // stack: 'Search Engine', + // emphasis: { + // focus: 'series' + // }, + // data: [120, 132, 101, 134, 290, 230, 220] + // }, + // { + // name: 'Bing', + // type: 'bar', + // stack: 'Search Engine', + // emphasis: { + // focus: 'series' + // }, + // data: [60, 72, 71, 74, 190, 130, 110] + // }, + // { + // name: 'Others', + // type: 'bar', + // stack: 'Search Engine', + // emphasis: { + // focus: 'series' + // }, + // data: [62, 82, 91, 84, 109, 110, 120] + // } ], }; return (