using Mapster; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using SqlSugar; using Volo.Abp.Application.Dtos; 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.AiHub.Domain.Entities.OpenApi; using Yi.Framework.AiHub.Domain.Extensions; using Yi.Framework.AiHub.Domain.Shared.Consts; using Yi.Framework.Ddd.Application.Contracts; 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; private readonly ISqlSugarRepository _premiumPackageRepository; private readonly ISqlSugarRepository _tokenRepository; public UsageStatisticsService( ISqlSugarRepository messageRepository, ISqlSugarRepository usageStatisticsRepository, ISqlSugarRepository premiumPackageRepository, ISqlSugarRepository tokenRepository) { _messageRepository = messageRepository; _usageStatisticsRepository = usageStatisticsRepository; _premiumPackageRepository = premiumPackageRepository; _tokenRepository = tokenRepository; } /// /// 获取当前用户近7天的Token消耗统计 /// /// 每日Token使用量列表 public async Task> GetLast7DaysTokenUsageAsync([FromQuery]UsageStatisticsGetInput input) { 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.Role == "assistant" || 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) .Select(g => new { Date = g.CreationTime.Date, Tokens = SqlFunc.AggregateSum(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([FromQuery]UsageStatisticsGetInput input) { var userId = CurrentUser.GetId(); // 从UsageStatistics表获取各模型的token消耗统计(按ModelId聚合,因为同一模型可能有多个TokenId的记录) var modelUsages = await _usageStatisticsRepository._DbQueryable .Where(x => x.UserId == userId) .WhereIF(input.TokenId.HasValue,x => x.TokenId == input.TokenId) .GroupBy(x => x.ModelId) .Select(x => new { ModelId = x.ModelId, TotalTokenCount = SqlFunc.AggregateSum(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; } /// /// 获取当前用户尊享服务Token用量统计 /// /// 尊享服务Token用量统计 public async Task GetPremiumTokenUsageAsync() { var userId = CurrentUser.GetId(); // 获取尊享包Token信息 var premiumPackages = await _premiumPackageRepository._DbQueryable .Where(x => x.UserId == userId && x.IsActive) .ToListAsync(); var result = new PremiumTokenUsageDto(); if (premiumPackages.Any()) { // 过滤掉已过期、禁用的包,不过滤用量负数的包 var validPackages = premiumPackages .Where(p => p.IsAvailable(false)) .ToList(); result.PremiumTotalTokens = validPackages.Sum(x => x.TotalTokens); result.PremiumUsedTokens = validPackages.Sum(x => x.UsedTokens); result.PremiumRemainingTokens = validPackages.Sum(x => x.RemainingTokens); } return result; } /// /// 获取当前用户尊享服务token用量统计列表 /// /// [HttpGet("usage-statistics/premium-token-usage/list")] public async Task> GetPremiumTokenUsageListAsync( PremiumTokenUsageGetListInput input) { var userId = CurrentUser.GetId(); RefAsync total = 0; // 获取尊享包Token信息 var entities = await _premiumPackageRepository._DbQueryable .Where(x => x.UserId == userId) .WhereIF(input.IsFree == false, x => x.PurchaseAmount > 0) .WhereIF(input.IsFree == true, x => x.PurchaseAmount == 0) .WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime) .OrderByDescending(x => x.CreationTime) .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); return new PagedResultDto(total, entities.Adapt>()); } /// /// 获取当前用户尊享包不同Token用量占比(饼图) /// /// 各Token的尊享模型用量及占比 [HttpGet("usage-statistics/premium-token-usage/by-token")] public async Task> GetPremiumTokenUsageByTokenAsync() { var userId = CurrentUser.GetId(); // 先获取所有尊享模型的ModelId列表 var premiumModelIds = await _aiModelRepository._DbQueryable .Where(x => x.IsPremium) .Select(x => x.ModelId) .ToListAsync(); // 从UsageStatistics表获取尊享模型的token消耗统计(按TokenId聚合) var tokenUsages = await _usageStatisticsRepository._DbQueryable .Where(x => x.UserId == userId && premiumModelIds.Contains(x.ModelId)) .GroupBy(x => x.TokenId) .Select(x => new { TokenId = x.TokenId, TotalTokenCount = SqlFunc.AggregateSum(x.TotalTokenCount) }) .ToListAsync(); if (!tokenUsages.Any()) { return new List(); } // 获取用户的所有Token信息用于名称映射 var tokenIds = tokenUsages.Select(x => x.TokenId).ToList(); var tokens = await _tokenRepository._DbQueryable .Where(x => x.UserId == userId && tokenIds.Contains(x.Id)) .Select(x => new { x.Id, x.Name }) .ToListAsync(); var tokenNameDict = tokens.ToDictionary(x => x.Id, x => x.Name); // 计算总token数 var totalTokens = tokenUsages.Sum(x => x.TotalTokenCount); // 计算各Token占比 var result = tokenUsages.Select(x => new TokenPremiumUsageDto { TokenId = x.TokenId, TokenName = x.TokenId == Guid.Empty ? "默认" : (tokenNameDict.TryGetValue(x.TokenId, out var name) ? name : "其他"), Tokens = x.TotalTokenCount, Percentage = totalTokens > 0 ? Math.Round((decimal)x.TotalTokenCount / totalTokens * 100, 2) : 0 }).OrderByDescending(x => x.Tokens).ToList(); return result; } }