diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/AiAccountService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/AiAccountService.cs index 3d4daf5c..83833ebc 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/AiAccountService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/AiAccountService.cs @@ -1,10 +1,14 @@ using Mapster; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using SqlSugar; +using System.Globalization; +using System.Text; using Volo.Abp.Application.Services; using Volo.Abp.Users; using Yi.Framework.AiHub.Application.Contracts.Dtos; using Yi.Framework.AiHub.Domain.Entities; +using Yi.Framework.AiHub.Domain.Entities.Chat; using Yi.Framework.Rbac.Application.Contracts.IServices; using Yi.Framework.Rbac.Domain.Shared.Dtos; using Yi.Framework.SqlSugarCore.Abstractions; @@ -18,17 +22,18 @@ public class AiAccountService : ApplicationService private ISqlSugarRepository _userRepository; private ISqlSugarRepository _rechargeRepository; private ISqlSugarRepository _premiumPackageRepository; - + private ISqlSugarRepository _messageRepository; public AiAccountService( IAccountService accountService, ISqlSugarRepository userRepository, ISqlSugarRepository rechargeRepository, - ISqlSugarRepository premiumPackageRepository) + ISqlSugarRepository premiumPackageRepository, ISqlSugarRepository messageRepository) { _accountService = accountService; _userRepository = userRepository; _rechargeRepository = rechargeRepository; _premiumPackageRepository = premiumPackageRepository; + _messageRepository = messageRepository; } /// @@ -148,4 +153,90 @@ public class AiAccountService : ApplicationService return result; } -} \ No newline at end of file + + public class TokenStatisticsInput + { + /// + /// 指定日期(当天零点) + /// + public DateTime Date { get; set; } + + /// + /// 模型Id -> 1亿Token成本(RMB) + /// + public Dictionary ModelCosts { get; set; } = new(); + } + + /// + /// 获取指定日期各模型Token统计 + /// + [Authorize] + [HttpPost("account/token-statistics")] + public async Task GetTokenStatisticsAsync([FromBody] TokenStatisticsInput input) + { + if (CurrentUser.UserName != "Guo" && CurrentUser.UserName != "cc") + { + throw new UserFriendlyException("您暂无权限访问"); + } + + if (input.ModelCosts is null || input.ModelCosts.Count == 0) + { + throw new UserFriendlyException("请提供模型成本配置"); + } + + var day = input.Date.Date; + var nextDay = day.AddDays(1); + var modelIds = input.ModelCosts.Keys.ToList(); + + 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); + + string weekDay = day.ToString("dddd", new CultureInfo("zh-CN")) switch + { + "星期一" => "周1", + "星期二" => "周2", + "星期三" => "周3", + "星期四" => "周4", + "星期五" => "周5", + "星期六" => "周6", + "星期日" => "周日", + _ => day.ToString("dddd", new CultureInfo("zh-CN")) + }; + + var sb = new StringBuilder(); + sb.AppendLine($"{day:M月d日} {weekDay}"); + + foreach (var kvp in input.ModelCosts) + { + var modelId = kvp.Key; + var cost = kvp.Value; + + modelStatDict.TryGetValue(modelId, out var stat); + long tokens = stat?.Tokens ?? 0; + long count = stat?.Count ?? 0; + + decimal costPerHundredMillion = tokens > 0 + ? cost / (tokens / 100000000m) + : 0; + + decimal tokensInWan = tokens / 10000m; + + sb.AppendLine(); + sb.AppendLine($"{modelId} 成本:【{cost:F2}RMB】 次数:【{count}次】 token:【{tokensInWan:F0}w】 1亿token成本:【{costPerHundredMillion:F2}RMB】"); + } + + return sb.ToString().TrimEnd(); + } +}