feat: 兼容claude格式
This commit is contained in:
@@ -0,0 +1,42 @@
|
|||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.SystemStatistics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型Token统计DTO
|
||||||
|
/// </summary>
|
||||||
|
public class ModelTokenStatisticsDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 模型ID
|
||||||
|
/// </summary>
|
||||||
|
public string ModelId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型名称
|
||||||
|
/// </summary>
|
||||||
|
public string ModelName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token消耗量
|
||||||
|
/// </summary>
|
||||||
|
public long Tokens { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token消耗量(万)
|
||||||
|
/// </summary>
|
||||||
|
public decimal TokensInWan { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 使用次数
|
||||||
|
/// </summary>
|
||||||
|
public long Count { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 成本(RMB)
|
||||||
|
/// </summary>
|
||||||
|
public decimal Cost { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 1亿Token成本(RMB)
|
||||||
|
/// </summary>
|
||||||
|
public decimal CostPerHundredMillion { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.SystemStatistics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 利润统计输入
|
||||||
|
/// </summary>
|
||||||
|
public class ProfitStatisticsInput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 当前成本(RMB)
|
||||||
|
/// </summary>
|
||||||
|
public decimal CurrentCost { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.SystemStatistics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 利润统计输出
|
||||||
|
/// </summary>
|
||||||
|
public class ProfitStatisticsOutput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 日期
|
||||||
|
/// </summary>
|
||||||
|
public string Date { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 尊享包已消耗Token数(单位:个)
|
||||||
|
/// </summary>
|
||||||
|
public long TotalUsedTokens { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 尊享包已消耗Token数(单位:亿)
|
||||||
|
/// </summary>
|
||||||
|
public decimal TotalUsedTokensInHundredMillion { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 尊享包剩余库存Token数(单位:个)
|
||||||
|
/// </summary>
|
||||||
|
public long TotalRemainingTokens { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 尊享包剩余库存Token数(单位:亿)
|
||||||
|
/// </summary>
|
||||||
|
public decimal TotalRemainingTokensInHundredMillion { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前成本(RMB)
|
||||||
|
/// </summary>
|
||||||
|
public decimal CurrentCost { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 1亿Token成本(RMB)
|
||||||
|
/// </summary>
|
||||||
|
public decimal CostPerHundredMillion { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 总成本(RMB)
|
||||||
|
/// </summary>
|
||||||
|
public decimal TotalCost { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 总收益(RMB)
|
||||||
|
/// </summary>
|
||||||
|
public decimal TotalRevenue { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 利润率(%)
|
||||||
|
/// </summary>
|
||||||
|
public decimal ProfitRate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按200售价计算的成本(RMB)
|
||||||
|
/// </summary>
|
||||||
|
public decimal CostAt200Price { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.SystemStatistics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token统计输入
|
||||||
|
/// </summary>
|
||||||
|
public class TokenStatisticsInput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 指定日期(当天零点)
|
||||||
|
/// </summary>
|
||||||
|
public DateTime Date { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.SystemStatistics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token统计输出
|
||||||
|
/// </summary>
|
||||||
|
public class TokenStatisticsOutput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 日期
|
||||||
|
/// </summary>
|
||||||
|
public string Date { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型统计列表
|
||||||
|
/// </summary>
|
||||||
|
public List<ModelTokenStatisticsDto> ModelStatistics { get; set; } = new();
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Yi.Framework.AiHub.Application.Contracts.Dtos.SystemStatistics;
|
||||||
|
|
||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 系统使用量统计服务接口
|
||||||
|
/// </summary>
|
||||||
|
public interface ISystemUsageStatisticsService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取利润统计数据
|
||||||
|
/// </summary>
|
||||||
|
Task<ProfitStatisticsOutput> GetProfitStatisticsAsync(ProfitStatisticsInput input);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取指定日期各模型Token统计
|
||||||
|
/// </summary>
|
||||||
|
Task<TokenStatisticsOutput> GetTokenStatisticsAsync(TokenStatisticsInput input);
|
||||||
|
}
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 系统使用量统计服务实现
|
||||||
|
/// </summary>
|
||||||
|
[Authorize(Roles = "admin")]
|
||||||
|
public class SystemUsageStatisticsService : ApplicationService, ISystemUsageStatisticsService
|
||||||
|
{
|
||||||
|
private readonly ISqlSugarRepository<PremiumPackageAggregateRoot> _premiumPackageRepository;
|
||||||
|
private readonly ISqlSugarRepository<AiRechargeAggregateRoot> _rechargeRepository;
|
||||||
|
private readonly ISqlSugarRepository<MessageAggregateRoot> _messageRepository;
|
||||||
|
private readonly ISqlSugarRepository<AiModelEntity, Guid> _modelRepository;
|
||||||
|
|
||||||
|
public SystemUsageStatisticsService(
|
||||||
|
ISqlSugarRepository<PremiumPackageAggregateRoot> premiumPackageRepository,
|
||||||
|
ISqlSugarRepository<AiRechargeAggregateRoot> rechargeRepository,
|
||||||
|
ISqlSugarRepository<MessageAggregateRoot> messageRepository,
|
||||||
|
ISqlSugarRepository<AiModelEntity, Guid> modelRepository)
|
||||||
|
{
|
||||||
|
_premiumPackageRepository = premiumPackageRepository;
|
||||||
|
_rechargeRepository = rechargeRepository;
|
||||||
|
_messageRepository = messageRepository;
|
||||||
|
_modelRepository = modelRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取利润统计数据
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("system-statistics/profit")]
|
||||||
|
public async Task<ProfitStatisticsOutput> 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取指定日期各模型Token统计
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("system-statistics/token")]
|
||||||
|
public async Task<TokenStatisticsOutput> 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<ModelTokenStatisticsDto>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按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<ModelTokenStatisticsDto>();
|
||||||
|
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}";
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Yi.Ai.Vue3/src/api/systemStatistics/index.ts
Normal file
17
Yi.Ai.Vue3/src/api/systemStatistics/index.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { post } from '@/utils/request';
|
||||||
|
import type {
|
||||||
|
ProfitStatisticsInput,
|
||||||
|
ProfitStatisticsOutput,
|
||||||
|
TokenStatisticsInput,
|
||||||
|
TokenStatisticsOutput,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
// 获取利润统计数据
|
||||||
|
export function getProfitStatistics(data: ProfitStatisticsInput) {
|
||||||
|
return post<ProfitStatisticsOutput>('/system-statistics/profit', data).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取指定日期各模型Token统计
|
||||||
|
export function getTokenStatistics(data: TokenStatisticsInput) {
|
||||||
|
return post<TokenStatisticsOutput>('/system-statistics/token', data).json();
|
||||||
|
}
|
||||||
41
Yi.Ai.Vue3/src/api/systemStatistics/types.ts
Normal file
41
Yi.Ai.Vue3/src/api/systemStatistics/types.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// 利润统计输入
|
||||||
|
export interface ProfitStatisticsInput {
|
||||||
|
currentCost: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 利润统计输出
|
||||||
|
export interface ProfitStatisticsOutput {
|
||||||
|
date: string;
|
||||||
|
totalUsedTokens: number;
|
||||||
|
totalUsedTokensInHundredMillion: number;
|
||||||
|
totalRemainingTokens: number;
|
||||||
|
totalRemainingTokensInHundredMillion: number;
|
||||||
|
currentCost: number;
|
||||||
|
costPerHundredMillion: number;
|
||||||
|
totalCost: number;
|
||||||
|
totalRevenue: number;
|
||||||
|
profitRate: number;
|
||||||
|
costAt200Price: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token统计输入
|
||||||
|
export interface TokenStatisticsInput {
|
||||||
|
date: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模型Token统计DTO
|
||||||
|
export interface ModelTokenStatisticsDto {
|
||||||
|
modelId: string;
|
||||||
|
modelName: string;
|
||||||
|
tokens: number;
|
||||||
|
tokensInWan: number;
|
||||||
|
count: number;
|
||||||
|
cost: number;
|
||||||
|
costPerHundredMillion: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token统计输出
|
||||||
|
export interface TokenStatisticsOutput {
|
||||||
|
date: string;
|
||||||
|
modelStatistics: ModelTokenStatisticsDto[];
|
||||||
|
}
|
||||||
219
Yi.Ai.Vue3/src/pages/console/system-statistics/index.vue
Normal file
219
Yi.Ai.Vue3/src/pages/console/system-statistics/index.vue
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { Refresh } from '@element-plus/icons-vue';
|
||||||
|
import { getProfitStatistics, getTokenStatistics } from '@/api/systemStatistics';
|
||||||
|
import type { ProfitStatisticsOutput, TokenStatisticsOutput } from '@/api/systemStatistics/types';
|
||||||
|
|
||||||
|
// ==================== 利润统计 ====================
|
||||||
|
const profitLoading = ref(false);
|
||||||
|
const profitData = ref<ProfitStatisticsOutput | null>(null);
|
||||||
|
const currentCost = ref<number>(0);
|
||||||
|
|
||||||
|
// 获取利润统计
|
||||||
|
async function fetchProfitStatistics() {
|
||||||
|
if (!currentCost.value || currentCost.value <= 0) {
|
||||||
|
ElMessage.warning('请输入当前成本');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
profitLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await getProfitStatistics({
|
||||||
|
currentCost: currentCost.value,
|
||||||
|
});
|
||||||
|
profitData.value = res.data;
|
||||||
|
}
|
||||||
|
catch (error: any) {
|
||||||
|
ElMessage.error(error.message || '获取利润统计失败');
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
profitLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== Token统计 ====================
|
||||||
|
const tokenLoading = ref(false);
|
||||||
|
const tokenData = ref<TokenStatisticsOutput | null>(null);
|
||||||
|
const selectedDate = ref<string>(new Date().toISOString().split('T')[0]);
|
||||||
|
|
||||||
|
// 获取Token统计
|
||||||
|
async function fetchTokenStatistics() {
|
||||||
|
if (!selectedDate.value) {
|
||||||
|
ElMessage.warning('请选择日期');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await getTokenStatistics({
|
||||||
|
date: selectedDate.value,
|
||||||
|
});
|
||||||
|
tokenData.value = res.data;
|
||||||
|
}
|
||||||
|
catch (error: any) {
|
||||||
|
ElMessage.error(error.message || '获取Token统计失败');
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
tokenLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
// 页面加载时不自动获取数据,等待用户输入参数后手动获取
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="system-statistics">
|
||||||
|
<div class="statistics-container">
|
||||||
|
<!-- 利润统计卡片 -->
|
||||||
|
<el-card class="statistics-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<h3>利润统计</h3>
|
||||||
|
<el-button type="primary" size="small" :icon="Refresh" :loading="profitLoading" @click="fetchProfitStatistics">
|
||||||
|
查询
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="input-section">
|
||||||
|
<el-form :inline="true">
|
||||||
|
<el-form-item label="当前成本(RMB)">
|
||||||
|
<el-input-number
|
||||||
|
v-model="currentCost"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
placeholder="请输入当前成本"
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="profitData" v-loading="profitLoading" class="data-display">
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="日期">{{ profitData.date }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="当前成本">{{ profitData.currentCost.toFixed(2) }} RMB</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="尊享包已消耗">
|
||||||
|
{{ profitData.totalUsedTokensInHundredMillion.toFixed(2) }}亿 ({{ profitData.totalUsedTokens }})
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="尊享包剩余库存">
|
||||||
|
{{ profitData.totalRemainingTokensInHundredMillion.toFixed(2) }}亿 ({{ profitData.totalRemainingTokens }})
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="1亿Token成本">
|
||||||
|
{{ profitData.costPerHundredMillion.toFixed(2) }} RMB
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="总成本">
|
||||||
|
{{ profitData.totalCost.toFixed(2) }} RMB
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="总收益">
|
||||||
|
{{ profitData.totalRevenue.toFixed(2) }} RMB
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="利润率">
|
||||||
|
<el-tag :type="profitData.profitRate > 0 ? 'success' : 'danger'">
|
||||||
|
{{ profitData.profitRate.toFixed(1) }}%
|
||||||
|
</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="按200售价计算成本" :span="2">
|
||||||
|
{{ profitData.costAt200Price.toFixed(2) }} RMB
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- Token统计卡片 -->
|
||||||
|
<el-card class="statistics-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<h3>Token统计</h3>
|
||||||
|
<el-button type="primary" size="small" :icon="Refresh" :loading="tokenLoading" @click="fetchTokenStatistics">
|
||||||
|
查询
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="input-section">
|
||||||
|
<el-form :inline="true">
|
||||||
|
<el-form-item label="选择日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="selectedDate"
|
||||||
|
type="date"
|
||||||
|
placeholder="选择日期"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="tokenData" v-loading="tokenLoading" class="data-display">
|
||||||
|
<div class="date-info">
|
||||||
|
<h4>{{ tokenData.date }}</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="tokenData.modelStatistics" border stripe>
|
||||||
|
<el-table-column prop="modelName" label="模型名称" min-width="150" />
|
||||||
|
<el-table-column prop="modelId" label="模型ID" min-width="200" show-overflow-tooltip />
|
||||||
|
<el-table-column label="Token消耗" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.tokensInWan.toFixed(0) }}w
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="count" label="使用次数" width="100" />
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.system-statistics {
|
||||||
|
padding: 20px;
|
||||||
|
height: 100vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.statistics-container {
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statistics-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-section {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-display {
|
||||||
|
.date-info {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
1
Yi.Ai.Vue3/types/import_meta.d.ts
vendored
1
Yi.Ai.Vue3/types/import_meta.d.ts
vendored
@@ -7,6 +7,7 @@ interface ImportMetaEnv {
|
|||||||
readonly VITE_WEB_BASE_API: string;
|
readonly VITE_WEB_BASE_API: string;
|
||||||
readonly VITE_API_URL: string;
|
readonly VITE_API_URL: string;
|
||||||
readonly VITE_FILE_UPLOAD_API: string;
|
readonly VITE_FILE_UPLOAD_API: string;
|
||||||
|
readonly VITE_BUILD_COMPRESS: string;
|
||||||
readonly VITE_SSO_SEVER_URL: string;
|
readonly VITE_SSO_SEVER_URL: string;
|
||||||
readonly VITE_APP_VERSION: string;
|
readonly VITE_APP_VERSION: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user