diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Token/TokenGetListOutputDto.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Token/TokenGetListOutputDto.cs index 6358985b..bc694245 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Token/TokenGetListOutputDto.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Token/TokenGetListOutputDto.cs @@ -40,6 +40,11 @@ public class TokenGetListOutputDto /// public bool IsDisabled { get; set; } + /// + /// 是否启用请求日志记录 + /// + public bool IsEnableLog { get; set; } + /// /// 创建时间 /// diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/TokenService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/TokenService.cs index 204fda55..26aec796 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/TokenService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/TokenService.cs @@ -83,6 +83,7 @@ public class TokenService : ApplicationService PremiumQuotaLimit = t.PremiumQuotaLimit, PremiumUsedQuota = usedQuota, IsDisabled = t.IsDisabled, + IsEnableLog = t.IsEnableLog, CreationTime = t.CreationTime }; }).ToList(); @@ -158,6 +159,7 @@ public class TokenService : ApplicationService PremiumQuotaLimit = token.PremiumQuotaLimit, PremiumUsedQuota = 0, IsDisabled = token.IsDisabled, + IsEnableLog = token.IsEnableLog, CreationTime = token.CreationTime }; } diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/OpenApiService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/OpenApiService.cs index 5e4b0cf9..92a06773 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/OpenApiService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/OpenApiService.cs @@ -1,12 +1,15 @@ using System.Text.Json; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Volo.Abp.Application.Services; +using Volo.Abp.Uow; using Volo.Abp.Users; using Yi.Framework.AiHub.Domain.Entities; using Yi.Framework.AiHub.Domain.Entities.Chat; using Yi.Framework.AiHub.Domain.Entities.Model; +using Yi.Framework.AiHub.Domain.Entities.OpenApi; using Yi.Framework.AiHub.Domain.Extensions; using Yi.Framework.AiHub.Domain.Managers; using Yi.Framework.AiHub.Domain.Shared.Consts; @@ -33,10 +36,12 @@ public class OpenApiService : ApplicationService private readonly PremiumPackageManager _premiumPackageManager; private readonly ISqlSugarRepository _imageStoreRepository; private readonly ISqlSugarRepository _aiModelRepository; + private readonly IServiceScopeFactory _serviceScopeFactory; public OpenApiService(IHttpContextAccessor httpContextAccessor, ILogger logger, TokenManager tokenManager, AiGateWayManager aiGateWayManager, ModelManager modelManager, AiBlacklistManager aiBlacklistManager, - IAccountService accountService, PremiumPackageManager premiumPackageManager, ISqlSugarRepository imageStoreRepository, ISqlSugarRepository aiModelRepository) + IAccountService accountService, PremiumPackageManager premiumPackageManager, ISqlSugarRepository imageStoreRepository, ISqlSugarRepository aiModelRepository, + IServiceScopeFactory serviceScopeFactory) { _httpContextAccessor = httpContextAccessor; _logger = logger; @@ -48,6 +53,7 @@ public class OpenApiService : ApplicationService _premiumPackageManager = premiumPackageManager; _imageStoreRepository = imageStoreRepository; _aiModelRepository = aiModelRepository; + _serviceScopeFactory = serviceScopeFactory; } /// @@ -91,6 +97,12 @@ public class OpenApiService : ApplicationService null, tokenId, CancellationToken.None); } + + // 记录请求日志 + if (tokenValidation.IsEnableLog) + { + FireAndForgetMessageLog(JsonSerializer.Serialize(input), tokenValidation.Token, tokenValidation.TokenName, input.Model, ModelApiTypeEnum.Completions); + } } @@ -206,6 +218,12 @@ public class OpenApiService : ApplicationService null, tokenId, CancellationToken.None); } + + // 记录请求日志 + if (tokenValidation.IsEnableLog) + { + FireAndForgetMessageLog(JsonSerializer.Serialize(input), tokenValidation.Token, tokenValidation.TokenName, input.Model, ModelApiTypeEnum.Messages); + } } @@ -258,6 +276,12 @@ public class OpenApiService : ApplicationService null, tokenId, CancellationToken.None); } + + // 记录请求日志 + if (tokenValidation.IsEnableLog) + { + FireAndForgetMessageLog(JsonSerializer.Serialize(input), tokenValidation.Token, tokenValidation.TokenName, input.Model, ModelApiTypeEnum.Responses); + } } @@ -318,6 +342,12 @@ public class OpenApiService : ApplicationService null, tokenId, CancellationToken.None); } + + // 记录请求日志 + if (tokenValidation.IsEnableLog) + { + FireAndForgetMessageLog(input.GetRawText(), tokenValidation.Token, tokenValidation.TokenName, modelId, ModelApiTypeEnum.GenerateContent); + } } #region 私有 @@ -357,5 +387,25 @@ public class OpenApiService : ApplicationService } } + private void FireAndForgetMessageLog(string requestBody, string apiKey, string apiKeyName, string modelId, ModelApiTypeEnum apiType) + { + _ = Task.Run(async () => + { + try + { + using var scope = _serviceScopeFactory.CreateScope(); + var uowManager = scope.ServiceProvider.GetRequiredService(); + var manager = scope.ServiceProvider.GetRequiredService(); + using var uow = uowManager.Begin(requiresNew: true); + await manager.CreateAsync(requestBody, apiKey, apiKeyName, modelId, apiType); + await uow.CompleteAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex, "记录消息日志失败, 请求体长度: {RequestBodyLength}", requestBody?.Length ?? 0); + } + }); + } + #endregion } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/OpenApi/MessageLogAggregateRoot.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/OpenApi/MessageLogAggregateRoot.cs new file mode 100644 index 00000000..038e6316 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/OpenApi/MessageLogAggregateRoot.cs @@ -0,0 +1,49 @@ +using SqlSugar; +using Volo.Abp.Domain.Entities; +using Yi.Framework.AiHub.Domain.Shared.Enums; + +namespace Yi.Framework.AiHub.Domain.Entities.OpenApi; + +[SugarTable("Ai_Message_Log")] +public class MessageLogAggregateRoot : Entity +{ + /// + /// 请求内容(httpbody) + /// + [SugarColumn(ColumnDataType = "text")] + public string? RequestBody { get; set; } + + /// + /// 请求apikey + /// + [SugarColumn(Length = 255)] + public string ApiKey { get; set; } + + /// + /// 请求apikey名称 + /// + [SugarColumn(Length = 255)] + public string ApiKeyName { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreationTime { get; set; } + + /// + /// 模型id + /// + [SugarColumn(Length = 64)] + public string ModelId { get; set; } + + /// + /// api类型 + /// + public ModelApiTypeEnum ApiType { get; set; } + + /// + /// api类型名称 + /// + [SugarColumn(Length = 16)] + public string ApiTypeName { get; set; } +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/OpenApi/TokenAggregateRoot.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/OpenApi/TokenAggregateRoot.cs index d5217ca9..6fbe3656 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/OpenApi/TokenAggregateRoot.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/OpenApi/TokenAggregateRoot.cs @@ -51,6 +51,11 @@ public class TokenAggregateRoot : FullAuditedAggregateRoot /// public bool IsDisabled { get; set; } + /// + /// 是否启用请求日志记录(仅数据库手动修改) + /// + public bool IsEnableLog { get; set; } + /// /// 检查Token是否可用 /// diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/MessageLogManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/MessageLogManager.cs new file mode 100644 index 00000000..4737be73 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/MessageLogManager.cs @@ -0,0 +1,34 @@ +using Volo.Abp.Domain.Services; +using Yi.Framework.AiHub.Domain.Entities.OpenApi; +using Yi.Framework.AiHub.Domain.Shared.Enums; +using Yi.Framework.SqlSugarCore.Abstractions; + +namespace Yi.Framework.AiHub.Domain.Managers; + +public class MessageLogManager : DomainService +{ + private readonly ISqlSugarRepository _repository; + + public MessageLogManager(ISqlSugarRepository repository) + { + _repository = repository; + } + + /// + /// 创建消息日志 + /// + public async Task CreateAsync(string requestBody, string apiKey, string apiKeyName, string modelId, ModelApiTypeEnum apiType) + { + var entity = new MessageLogAggregateRoot + { + RequestBody = requestBody, + ApiKey = apiKey, + ApiKeyName = apiKeyName, + ModelId = modelId, + ApiType = apiType, + ApiTypeName = apiType.ToString(), + CreationTime = DateTime.Now + }; + await _repository.InsertAsync(entity); + } +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/TokenManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/TokenManager.cs index 9dc1fad9..0145f671 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/TokenManager.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/TokenManager.cs @@ -27,6 +27,16 @@ public class TokenValidationResult /// token /// public string Token { get; set; } + + /// + /// Token名称 + /// + public string TokenName { get; set; } + + /// + /// 是否启用请求日志记录 + /// + public bool IsEnableLog { get; set; } } public class TokenManager : DomainService @@ -117,7 +127,9 @@ public class TokenManager : DomainService { UserId = entity.UserId, TokenId = entity.Id, - Token = entity.Token + Token = entity.Token, + TokenName = entity.Name, + IsEnableLog = entity.IsEnableLog }; } diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs index afaa6969..e9f4e792 100644 --- a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs +++ b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs @@ -361,7 +361,7 @@ namespace Yi.Abp.Web var app = context.GetApplicationBuilder(); app.UseRouting(); - // app.ApplicationServices.GetRequiredService().SqlSugarClient.CodeFirst.InitTables(); + //app.ApplicationServices.GetRequiredService().SqlSugarClient.CodeFirst.InitTables(); // app.ApplicationServices.GetRequiredService().SqlSugarClient.CodeFirst.InitTables(); // app.ApplicationServices.GetRequiredService().SqlSugarClient.CodeFirst.InitTables();