feat: 新增获取指定日期各模型Token统计接口
- 在 AiAccountService 中新增 TokenStatisticsInput DTO 与 POST /account/token-statistics 接口(GetTokenStatisticsAsync),用于按模型统计指定日期的 token 使用量、调用次数并计算成本,返回文本摘要。 - 注入 MessageAggregateRoot 仓储(_messageRepository),使用 SqlSugar 聚合查询(Sum/Count),按 modelId 与日期范围过滤,并只统计 role == "system" 的记录。 - 成本计算逻辑:根据输入的模型 1 亿 token 成本与实际 token 数计算每 1 亿 token 成本;同时输出调用次数与 token(单位万)。 - 接口权限与入参校验:仅允许 CurrentUser.UserName 为 "Guo" 或 "cc" 访问;必须提供 ModelCosts 配置。 - 添加的引用:SqlSugar、System.Globalization、System.Text、Yi.Framework.AiHub.Domain.Entities.Chat。
This commit is contained in:
@@ -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<AiUserExtraInfoEntity> _userRepository;
|
||||
private ISqlSugarRepository<AiRechargeAggregateRoot> _rechargeRepository;
|
||||
private ISqlSugarRepository<PremiumPackageAggregateRoot> _premiumPackageRepository;
|
||||
|
||||
private ISqlSugarRepository<MessageAggregateRoot> _messageRepository;
|
||||
public AiAccountService(
|
||||
IAccountService accountService,
|
||||
ISqlSugarRepository<AiUserExtraInfoEntity> userRepository,
|
||||
ISqlSugarRepository<AiRechargeAggregateRoot> rechargeRepository,
|
||||
ISqlSugarRepository<PremiumPackageAggregateRoot> premiumPackageRepository)
|
||||
ISqlSugarRepository<PremiumPackageAggregateRoot> premiumPackageRepository, ISqlSugarRepository<MessageAggregateRoot> messageRepository)
|
||||
{
|
||||
_accountService = accountService;
|
||||
_userRepository = userRepository;
|
||||
_rechargeRepository = rechargeRepository;
|
||||
_premiumPackageRepository = premiumPackageRepository;
|
||||
_messageRepository = messageRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -148,4 +153,90 @@ public class AiAccountService : ApplicationService
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class TokenStatisticsInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 指定日期(当天零点)
|
||||
/// </summary>
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型Id -> 1亿Token成本(RMB)
|
||||
/// </summary>
|
||||
public Dictionary<string, decimal> ModelCosts { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定日期各模型Token统计
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
[HttpPost("account/token-statistics")]
|
||||
public async Task<string> 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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user