using Mapster; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using SqlSugar; using System.Globalization; using Volo.Abp.Application.Services; using Yi.Framework.AiHub.Application.Contracts.Dtos.SystemStatistics; 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.Model; using Yi.Framework.SqlSugarCore.Abstractions; namespace Yi.Framework.AiHub.Application.Services; /// /// 系统使用量统计服务实现 /// [Authorize(Roles = "admin")] public class SystemUsageStatisticsService : ApplicationService, ISystemUsageStatisticsService { private readonly ISqlSugarRepository _premiumPackageRepository; private readonly ISqlSugarRepository _rechargeRepository; private readonly ISqlSugarRepository _messageRepository; private readonly ISqlSugarRepository _modelRepository; public SystemUsageStatisticsService( ISqlSugarRepository premiumPackageRepository, ISqlSugarRepository rechargeRepository, ISqlSugarRepository messageRepository, ISqlSugarRepository modelRepository) { _premiumPackageRepository = premiumPackageRepository; _rechargeRepository = rechargeRepository; _messageRepository = messageRepository; _modelRepository = modelRepository; } /// /// 获取利润统计数据 /// [HttpPost("system-statistics/profit")] public async Task GetProfitStatisticsAsync(ProfitStatisticsInput input) { // 1. 获取尊享包总消耗和剩余库存 var premiumPackages = await _premiumPackageRepository._DbQueryable.ToListAsync(); long totalUsedTokens = premiumPackages.Sum(p => p.UsedTokens); long totalRemainingTokens = premiumPackages.Sum(p => p.RemainingTokens); // 2. 计算1亿Token成本 decimal costPerHundredMillion = totalUsedTokens > 0 ? input.CurrentCost / (totalUsedTokens / 100000000m) : 0; // 3. 计算总成本(剩余+已使用的总成本) long totalTokens = totalUsedTokens + totalRemainingTokens; decimal totalCost = totalTokens > 0 ? (totalTokens / 100000000m) * costPerHundredMillion : 0; // 4. 获取总收益(RechargeType=PremiumPackage的充值金额总和) decimal totalRevenue = await _rechargeRepository._DbQueryable .Where(x => x.RechargeType == Domain.Shared.Enums.RechargeTypeEnum.PremiumPackage) .SumAsync(x => x.RechargeAmount); // 5. 计算利润率 decimal profitRate = totalCost > 0 ? (totalRevenue / totalCost - 1) * 100 : 0; // 6. 按200售价计算成本 decimal costAt200Price = totalRevenue > 0 ? (totalCost / totalRevenue) * 200 : 0; // 7. 格式化日期 var today = DateTime.Now; string dayOfWeek = today.ToString("dddd", new CultureInfo("zh-CN")); string weekDay = dayOfWeek switch { "星期一" => "周1", "星期二" => "周2", "星期三" => "周3", "星期四" => "周4", "星期五" => "周5", "星期六" => "周6", "星期日" => "周日", _ => dayOfWeek }; return new ProfitStatisticsOutput { Date = $"{today:M月d日} {weekDay}", TotalUsedTokens = totalUsedTokens, TotalUsedTokensInHundredMillion = totalUsedTokens / 100000000m, TotalRemainingTokens = totalRemainingTokens, TotalRemainingTokensInHundredMillion = totalRemainingTokens / 100000000m, CurrentCost = input.CurrentCost, CostPerHundredMillion = costPerHundredMillion, TotalCost = totalCost, TotalRevenue = totalRevenue, ProfitRate = profitRate, CostAt200Price = costAt200Price }; } /// /// 获取指定日期各模型Token统计 /// [HttpPost("system-statistics/token")] public async Task GetTokenStatisticsAsync(TokenStatisticsInput input) { var day = input.Date.Date; var nextDay = day.AddDays(1); // 1. 获取所有尊享模型(包含被禁用的),按ModelId去重 var premiumModels = await _modelRepository._DbQueryable .Where(x => x.IsPremium) .ToListAsync(); if (premiumModels.Count == 0) { return new TokenStatisticsOutput { Date = FormatDate(day), ModelStatistics = new List() }; } // 按ModelId去重,保留第一个模型的名称 var distinctModels = premiumModels .GroupBy(x => x.ModelId) .Select(g => g.First()) .ToList(); var modelIds = distinctModels.Select(x => x.ModelId).ToList(); // 2. 查询指定日期内各模型的Token使用统计 var modelStats = await _messageRepository._DbQueryable .Where(x => modelIds.Contains(x.ModelId)) .Where(x => x.CreationTime >= day && x.CreationTime < nextDay) .Where(x => x.Role == "system") .GroupBy(x => x.ModelId) .Select(x => new { ModelId = x.ModelId, Tokens = SqlFunc.AggregateSum(x.TokenUsage.TotalTokenCount), Count = SqlFunc.AggregateCount(x.Id) }) .ToListAsync(); var modelStatDict = modelStats.ToDictionary(x => x.ModelId, x => x); // 3. 构建结果列表,使用去重后的模型列表 var result = new List(); foreach (var model in distinctModels) { modelStatDict.TryGetValue(model.ModelId, out var stat); long tokens = stat?.Tokens ?? 0; long count = stat?.Count ?? 0; // 这里成本设为0,因为需要前端传入或者从配置中获取 decimal cost = 0; decimal costPerHundredMillion = tokens > 0 && cost > 0 ? cost / (tokens / 100000000m) : 0; result.Add(new ModelTokenStatisticsDto { ModelId = model.ModelId, ModelName = model.Name, Tokens = tokens, TokensInWan = tokens / 10000m, Count = count, Cost = cost, CostPerHundredMillion = costPerHundredMillion }); } return new TokenStatisticsOutput { Date = FormatDate(day), ModelStatistics = result }; } private string FormatDate(DateTime date) { string dayOfWeek = date.ToString("dddd", new CultureInfo("zh-CN")); string weekDay = dayOfWeek switch { "星期一" => "周1", "星期二" => "周2", "星期三" => "周3", "星期四" => "周4", "星期五" => "周5", "星期六" => "周6", "星期日" => "周日", _ => dayOfWeek }; return $"{date:M月d日} {weekDay}"; } }