diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/DailyTokenUsageDto.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/DailyTokenUsageDto.cs new file mode 100644 index 00000000..a892025c --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/DailyTokenUsageDto.cs @@ -0,0 +1,17 @@ +namespace Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics; + +/// +/// 每日Token使用量统计DTO +/// +public class DailyTokenUsageDto +{ + /// + /// 日期 + /// + public DateTime Date { get; set; } + + /// + /// Token消耗量 + /// + public long Tokens { get; set; } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/ModelTokenUsageDto.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/ModelTokenUsageDto.cs new file mode 100644 index 00000000..7da82205 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/UsageStatistics/ModelTokenUsageDto.cs @@ -0,0 +1,22 @@ +namespace Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics; + +/// +/// 模型Token使用量统计DTO +/// +public class ModelTokenUsageDto +{ + /// + /// 模型ID + /// + public string Model { get; set; } + + /// + /// 总消耗量 + /// + public long Tokens { get; set; } + + /// + /// 占比(百分比) + /// + public decimal Percentage { get; set; } +} \ No newline at end of file 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 new file mode 100644 index 00000000..625b1cd2 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/IUsageStatisticsService.cs @@ -0,0 +1,21 @@ +using Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics; + +namespace Yi.Framework.AiHub.Application.Contracts.IServices; + +/// +/// 使用量统计服务接口 +/// +public interface IUsageStatisticsService +{ + /// + /// 获取当前用户近7天的Token消耗统计 + /// + /// 每日Token使用量列表 + Task> GetLast7DaysTokenUsageAsync(); + + /// + /// 获取当前用户各个模型的Token消耗量及占比 + /// + /// 模型Token使用量列表 + Task> GetModelTokenUsageAsync(); +} \ 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 new file mode 100644 index 00000000..fcb9cbd1 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/UsageStatisticsService.cs @@ -0,0 +1,105 @@ +using Microsoft.AspNetCore.Authorization; +using SqlSugar; +using Volo.Abp.Application.Services; +using Volo.Abp.Users; +using Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics; +using Yi.Framework.AiHub.Application.Contracts.IServices; +using Yi.Framework.AiHub.Domain.Entities; +using Yi.Framework.AiHub.Domain.Entities.Chat; +using Yi.Framework.SqlSugarCore.Abstractions; + +namespace Yi.Framework.AiHub.Application.Services; + +/// +/// 使用量统计服务 +/// +[Authorize] +public class UsageStatisticsService : ApplicationService, IUsageStatisticsService +{ + private readonly ISqlSugarRepository _messageRepository; + private readonly ISqlSugarRepository _usageStatisticsRepository; + + public UsageStatisticsService( + ISqlSugarRepository messageRepository, + ISqlSugarRepository usageStatisticsRepository) + { + _messageRepository = messageRepository; + _usageStatisticsRepository = usageStatisticsRepository; + } + + /// + /// 获取当前用户近7天的Token消耗统计 + /// + /// 每日Token使用量列表 + public async Task> GetLast7DaysTokenUsageAsync() + { + var userId = CurrentUser.GetId(); + var endDate = DateTime.Today; + var startDate = endDate.AddDays(-6); // 近7天 + + // 从Message表统计近7天的token消耗 + var dailyUsage = await _messageRepository._DbQueryable + .Where(x => x.UserId == userId) + .Where(x => x.CreationTime >= startDate && x.CreationTime < endDate.AddDays(1)) + .GroupBy(x => x.CreationTime.Date) + .Select(g => new + { + Date = g.CreationTime.Date, + Tokens = SqlFunc.AggregateCount(g.TokenUsage.TotalTokenCount) + }) + .ToListAsync(); + + // 生成完整的7天数据,包括没有使用记录的日期 + var result = new List(); + for (int i = 0; i < 7; i++) + { + var date = startDate.AddDays(i); + var usage = dailyUsage.FirstOrDefault(x => x.Date == date); + + result.Add(new DailyTokenUsageDto + { + Date = date, + Tokens = usage?.Tokens ?? 0 + }); + } + + return result.OrderBy(x => x.Date).ToList(); + } + + /// + /// 获取当前用户各个模型的Token消耗量及占比 + /// + /// 模型Token使用量列表 + public async Task> GetModelTokenUsageAsync() + { + var userId = CurrentUser.GetId(); + + // 从UsageStatistics表获取各模型的token消耗统计 + var modelUsages = await _usageStatisticsRepository._DbQueryable + .Where(x => x.UserId == userId) + .Select(x => new + { + x.ModelId, + x.TotalTokenCount + }) + .ToListAsync(); + + if (!modelUsages.Any()) + { + return new List(); + } + + // 计算总token数 + var totalTokens = modelUsages.Sum(x => x.TotalTokenCount); + + // 计算各模型占比 + var result = modelUsages.Select(x => new ModelTokenUsageDto + { + Model = x.ModelId, + Tokens = x.TotalTokenCount, + Percentage = totalTokens > 0 ? Math.Round((decimal)x.TotalTokenCount / totalTokens * 100, 2) : 0 + }).OrderByDescending(x => x.Tokens).ToList(); + + return result; + } +} \ No newline at end of file