From b8c79ac61c3ce2bc4055ea02dd73317b76ff3ccd Mon Sep 17 00:00:00 2001 From: chenchun Date: Fri, 23 Jan 2026 14:50:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E8=BF=9124=E5=B0=8F?= =?UTF-8?q?=E6=97=B6=E6=AF=8F=E5=B0=8F=E6=97=B6=E4=B8=8E=E4=BB=8A=E6=97=A5?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E4=BD=BF=E7=94=A8=E9=87=8F=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=8F=8A=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UsageStatistics/HourlyTokenUsageDto.cs | 22 ++++ .../UsageStatistics/ModelTodayUsageDto.cs | 22 ++++ .../UsageStatistics/ModelTokenBreakdownDto.cs | 17 +++ .../IServices/IUsageStatisticsService.cs | 12 ++ .../Services/UsageStatisticsService.cs | 104 +++++++++++++++++- 5 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/HourlyTokenUsageDto.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/ModelTodayUsageDto.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/ModelTokenBreakdownDto.cs diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/HourlyTokenUsageDto.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/HourlyTokenUsageDto.cs new file mode 100644 index 00000000..ef86c78e --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/HourlyTokenUsageDto.cs @@ -0,0 +1,22 @@ +namespace Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics; + +/// +/// 每小时Token使用量统计DTO(柱状图) +/// +public class HourlyTokenUsageDto +{ + /// + /// 小时时间点 + /// + public DateTime Hour { get; set; } + + /// + /// 该小时总Token消耗量 + /// + public long TotalTokens { get; set; } + + /// + /// 各模型Token消耗明细 + /// + public List ModelBreakdown { get; set; } = new(); +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/ModelTodayUsageDto.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/ModelTodayUsageDto.cs new file mode 100644 index 00000000..afc163e5 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/ModelTodayUsageDto.cs @@ -0,0 +1,22 @@ +namespace Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics; + +/// +/// 模型今日使用量统计DTO(卡片列表) +/// +public class ModelTodayUsageDto +{ + /// + /// 模型ID + /// + public string ModelId { get; set; } + + /// + /// 今日使用次数 + /// + public int UsageCount { get; set; } + + /// + /// 今日消耗总Token数 + /// + public long TotalTokens { get; set; } +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/ModelTokenBreakdownDto.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/ModelTokenBreakdownDto.cs new file mode 100644 index 00000000..8c1e831e --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/ModelTokenBreakdownDto.cs @@ -0,0 +1,17 @@ +namespace Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics; + +/// +/// 模型Token堆叠数据DTO(用于柱状图) +/// +public class ModelTokenBreakdownDto +{ + /// + /// 模型ID + /// + public string ModelId { get; set; } + + /// + /// Token消耗量 + /// + public long Tokens { get; set; } +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/IUsageStatisticsService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/IUsageStatisticsService.cs index 99be00a6..31a2ef87 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/IUsageStatisticsService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/IUsageStatisticsService.cs @@ -24,4 +24,16 @@ public interface IUsageStatisticsService /// /// 尊享服务Token用量统计 Task GetPremiumTokenUsageAsync(); + + /// + /// 获取当前用户近24小时每小时Token消耗统计(柱状图) + /// + /// 每小时Token使用量列表,包含各模型堆叠数据 + Task> GetLast24HoursTokenUsageAsync(UsageStatisticsGetInput input); + + /// + /// 获取当前用户今日各模型使用量统计(卡片列表) + /// + /// 模型今日使用量列表,包含使用次数和总Token + Task> GetTodayModelUsageAsync(UsageStatisticsGetInput input); } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/UsageStatisticsService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/UsageStatisticsService.cs index 6f988d41..d3d38bae 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/UsageStatisticsService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/UsageStatisticsService.cs @@ -57,7 +57,7 @@ public class UsageStatisticsService : ApplicationService, IUsageStatisticsServic // 从Message表统计近7天的token消耗 var dailyUsage = await _messageRepository._DbQueryable .Where(x => x.UserId == userId) - .Where(x => x.Role == "assistant" || x.Role == "system") + .Where(x => x.Role == "system") .Where(x => x.CreationTime >= startDate && x.CreationTime < endDate.AddDays(1)) .WhereIF(input.TokenId.HasValue,x => x.TokenId == input.TokenId) .GroupBy(x => x.CreationTime.Date) @@ -228,4 +228,106 @@ public class UsageStatisticsService : ApplicationService, IUsageStatisticsServic return result; } + + /// + /// 获取当前用户近24小时每小时Token消耗统计(柱状图) + /// + /// 每小时Token使用量列表,包含各模型堆叠数据 + public async Task> GetLast24HoursTokenUsageAsync([FromQuery]UsageStatisticsGetInput input) + { + var userId = CurrentUser.GetId(); + var now = DateTime.Now; + var startTime = now.AddHours(-23); // 滚动24小时,从23小时前到现在 + var startHour = new DateTime(startTime.Year, startTime.Month, startTime.Day, startTime.Hour, 0, 0); + + // 从Message表查询近24小时的数据,只选择需要的字段 + var messages = await _messageRepository._DbQueryable + .Where(x => x.UserId == userId) + .Where(x => x.Role == "system") + .Where(x => x.CreationTime >= startHour) + .WhereIF(input.TokenId.HasValue, x => x.TokenId == input.TokenId) + .Select(x => new + { + x.CreationTime, + x.ModelId, + x.TokenUsage.TotalTokenCount + }) + .ToListAsync(); + + // 在内存中按小时和模型分组统计 + var hourlyGrouped = messages + .GroupBy(x => new + { + Hour = new DateTime(x.CreationTime.Year, x.CreationTime.Month, x.CreationTime.Day, x.CreationTime.Hour, 0, 0), + x.ModelId + }) + .Select(g => new + { + g.Key.Hour, + g.Key.ModelId, + Tokens = g.Sum(x => x.TotalTokenCount) + }) + .ToList(); + + // 生成完整的24小时数据 + var result = new List(); + for (int i = 0; i < 24; i++) + { + var hour = startHour.AddHours(i); + var hourData = hourlyGrouped.Where(x => x.Hour == hour).ToList(); + + var modelBreakdown = hourData.Select(x => new ModelTokenBreakdownDto + { + ModelId = x.ModelId, + Tokens = x.Tokens + }).ToList(); + + result.Add(new HourlyTokenUsageDto + { + Hour = hour, + TotalTokens = modelBreakdown.Sum(x => x.Tokens), + ModelBreakdown = modelBreakdown + }); + } + + return result; + } + + /// + /// 获取当前用户今日各模型使用量统计(卡片列表) + /// + /// 模型今日使用量列表,包含使用次数和总Token + public async Task> GetTodayModelUsageAsync([FromQuery]UsageStatisticsGetInput input) + { + var userId = CurrentUser.GetId(); + var todayStart = DateTime.Today; // 今天凌晨0点 + var tomorrowStart = todayStart.AddDays(1); + + // 从Message表查询今天的数据,只选择需要的字段 + var messages = await _messageRepository._DbQueryable + .Where(x => x.UserId == userId) + .Where(x => x.Role == "system") + .Where(x => x.CreationTime >= todayStart && x.CreationTime < tomorrowStart) + .WhereIF(input.TokenId.HasValue, x => x.TokenId == input.TokenId) + .Select(x => new + { + x.ModelId, + x.TokenUsage.TotalTokenCount + }) + .ToListAsync(); + + // 在内存中按模型分组统计 + var modelStats = messages + .GroupBy(x => x.ModelId) + .Select(g => new ModelTodayUsageDto + { + ModelId = g.Key, + UsageCount = g.Count(), + TotalTokens = g.Sum(x => x.TotalTokenCount) + }) + .OrderByDescending(x => x.TotalTokens) + .ToList(); + + return modelStats; + } } \ No newline at end of file