From c5a9b9a15faadfeb2a5294ee06b93540694c507e Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Wed, 9 Jul 2025 21:52:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E9=9D=9E=E6=B5=81?= =?UTF-8?q?=E5=BC=8F=E4=BC=A0=E8=BE=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DbConnOptions.cs | 5 + .../Repositories/SqlSugarRepository.cs | 32 ++-- .../Dtos/OpenAi/ChatCompletionsOutput.cs | 115 +++++++++++++ ...utDto.cs => SendMessageStreamOutputDto.cs} | 2 +- .../Services/OpenApiService.cs | 6 +- .../AiChat/Impl/AzureRestChatService.cs | 151 +++++++++++++++--- .../Managers/AiGateWayManager.cs | 92 +++++++++-- .../YiAbpSqlSugarCoreModule.cs | 2 +- Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs | 8 - 9 files changed, 365 insertions(+), 48 deletions(-) create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/OpenAi/ChatCompletionsOutput.cs rename Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/{SendMessageOutputDto.cs => SendMessageStreamOutputDto.cs} (98%) diff --git a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore.Abstractions/DbConnOptions.cs b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore.Abstractions/DbConnOptions.cs index 6f48f14d..647ca0e8 100644 --- a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore.Abstractions/DbConnOptions.cs +++ b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore.Abstractions/DbConnOptions.cs @@ -58,5 +58,10 @@ namespace Yi.Framework.SqlSugarCore.Abstractions /// 是否启用SaaS多租户 /// public bool EnabledSaasMultiTenancy { get; set; } = false; + + /// + /// 并发乐观锁异常,否则不处理 + /// + public bool EnabledConcurrencyException { get; set; } = true; } } \ No newline at end of file diff --git a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Repositories/SqlSugarRepository.cs b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Repositories/SqlSugarRepository.cs index 12239daf..1cc1ea61 100644 --- a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Repositories/SqlSugarRepository.cs +++ b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Repositories/SqlSugarRepository.cs @@ -2,11 +2,13 @@ using System.Linq.Expressions; using System.Text; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Nito.AsyncEx; using SqlSugar; using Volo.Abp; using Volo.Abp.Auditing; using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; using Volo.Abp.Linq; @@ -24,6 +26,9 @@ namespace Yi.Framework.SqlSugarCore.Repositories private readonly ISugarDbContextProvider _dbContextProvider; + public IAbpLazyServiceProvider LazyServiceProvider { get; set; } + protected DbConnOptions? Options => LazyServiceProvider?.LazyGetService>().Value; + /// /// 异步查询执行器 /// @@ -407,17 +412,26 @@ namespace Yi.Framework.SqlSugarCore.Repositories { if (typeof(TEntity).IsAssignableTo()) //带版本号乐观锁更新 { - try + if (Options is not null && Options.EnabledConcurrencyException) + { + try + { + int num = await (await GetDbSimpleClientAsync()) + .Context.Updateable(updateObj).ExecuteCommandWithOptLockAsync(true); + return num > 0; + } + catch (VersionExceptions ex) + { + throw new AbpDbConcurrencyException( + $"{ex.Message}[更新失败:ConcurrencyStamp不是最新版本],entityInfo:{updateObj}", ex); + } + } + else { int num = await (await GetDbSimpleClientAsync()) - .Context.Updateable(updateObj).ExecuteCommandWithOptLockAsync(true); + .Context.Updateable(updateObj).ExecuteCommandAsync(); return num > 0; } - catch (VersionExceptions ex) - { - throw new AbpDbConcurrencyException( - $"{ex.Message}[更新失败:ConcurrencyStamp不是最新版本],entityInfo:{updateObj}", ex); - } } return await (await GetDbSimpleClientAsync()).UpdateAsync(updateObj); @@ -441,8 +455,8 @@ namespace Yi.Framework.SqlSugarCore.Repositories public class SqlSugarRepository : SqlSugarRepository, ISqlSugarRepository, IRepository where TEntity : class, IEntity, new() { - public SqlSugarRepository(ISugarDbContextProvider sugarDbContextProvider) : base( - sugarDbContextProvider) + public SqlSugarRepository(ISugarDbContextProvider dbContextProvider) : base( + dbContextProvider) { } diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/OpenAi/ChatCompletionsOutput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/OpenAi/ChatCompletionsOutput.cs new file mode 100644 index 00000000..808bbc91 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/OpenAi/ChatCompletionsOutput.cs @@ -0,0 +1,115 @@ +using Newtonsoft.Json; + +namespace Yi.Framework.AiHub.Application.Contracts.Dtos.OpenAi; + +public class ChatCompletionsOutput +{ + [JsonProperty("id")] + public string Id { get; set; } + [JsonProperty("object")] + public string Object { get; set; } + [JsonProperty("created_at")] + public long CreatedAt { get; set; } + [JsonProperty("status")] + public string Status { get; set; } + [JsonProperty("error")] + public object Error { get; set; } + [JsonProperty("incomplete_details")] + public object IncompleteDetails { get; set; } + [JsonProperty("instructions")] + public object Instructions { get; set; } + [JsonProperty("max_output_tokens")] + public object MaxOutputTokens { get; set; } + [JsonProperty("model")] + public string Model { get; set; } + [JsonProperty("output")] + public List Output { get; set; } + [JsonProperty("parallel_tool_calls")] + public bool ParallelToolCalls { get; set; } + [JsonProperty("previous_response_id")] + public object PreviousResponseId { get; set; } + [JsonProperty("reasoning")] + public Reasoning Reasoning { get; set; } + [JsonProperty("store")] + public bool Store { get; set; } + [JsonProperty("temperature")] + public double Temperature { get; set; } + [JsonProperty("text")] + public Text Text { get; set; } + [JsonProperty("tool_choice")] + public string ToolChoice { get; set; } + [JsonProperty("tools")] + public List Tools { get; set; } + [JsonProperty("top_p")] + public double TopP { get; set; } + [JsonProperty("truncation")] + public string Truncation { get; set; } + [JsonProperty("usage")] + public Usage Usage { get; set; } + [JsonProperty("user")] + public object User { get; set; } + [JsonProperty("metadata")] + public Dictionary Metadata { get; set; } +} +public class Output +{ + [JsonProperty("type")] + public string Type { get; set; } + [JsonProperty("id")] + public string Id { get; set; } + [JsonProperty("status")] + public string Status { get; set; } + [JsonProperty("role")] + public string Role { get; set; } + [JsonProperty("content")] + public List Content { get; set; } +} +public class Content +{ + [JsonProperty("type")] + public string Type { get; set; } + [JsonProperty("text")] + public string Text { get; set; } + [JsonProperty("annotations")] + public List Annotations { get; set; } +} +public class Reasoning +{ + [JsonProperty("effort")] + public object Effort { get; set; } + [JsonProperty("summary")] + public object Summary { get; set; } +} +public class Text +{ + [JsonProperty("format")] + public Format Format { get; set; } +} +public class Format +{ + [JsonProperty("type")] + public string Type { get; set; } +} +public class Usage +{ + [JsonProperty("input_tokens")] + public int InputTokens { get; set; } + [JsonProperty("input_tokens_details")] + public InputTokensDetails InputTokensDetails { get; set; } + [JsonProperty("output_tokens")] + public int OutputTokens { get; set; } + [JsonProperty("output_tokens_details")] + public OutputTokensDetails OutputTokensDetails { get; set; } + [JsonProperty("total_tokens")] + public int TotalTokens { get; set; } +} +public class InputTokensDetails +{ + [JsonProperty("cached_tokens")] + public int CachedTokens { get; set; } +} +public class OutputTokensDetails +{ + [JsonProperty("reasoning_tokens")] + public int ReasoningTokens { get; set; } +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/SendMessageOutputDto.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/SendMessageStreamOutputDto.cs similarity index 98% rename from Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/SendMessageOutputDto.cs rename to Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/SendMessageStreamOutputDto.cs index 7b73c9ee..371a57e8 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/SendMessageOutputDto.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/SendMessageStreamOutputDto.cs @@ -1,6 +1,6 @@ namespace Yi.Framework.AiHub.Application.Contracts.Dtos; -public class SendMessageOutputDto +public class SendMessageStreamOutputDto { /// /// 唯一标识符 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 3f0c443f..d7e64fdf 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 @@ -55,6 +55,7 @@ public class OpenApiService : ApplicationService } } + //是否使用流式传输 if (input.Stream) { //ai网关代理httpcontext @@ -64,8 +65,9 @@ public class OpenApiService : ApplicationService } else { - await _aiGateWayManager.CompleteChatForStatisticsAsync(_httpContextAccessor.HttpContext,input.Model, history, userId, null, - cancellationToken); + await _aiGateWayManager.CompleteChatForStatisticsAsync(_httpContextAccessor.HttpContext, input.Model, + history, userId, null, + cancellationToken); } } diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiChat/Impl/AzureRestChatService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiChat/Impl/AzureRestChatService.cs index efe13e75..4eb274e2 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiChat/Impl/AzureRestChatService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiChat/Impl/AzureRestChatService.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System.Net.Http.Json; +using System.Runtime.CompilerServices; using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -62,7 +63,8 @@ public class AzureRestChatService : IChatService if (!response.IsSuccessStatusCode) { - throw new UserFriendlyException($"当前模型不可用:{aiModelDescribe.ModelId},状态码:{response.StatusCode},原因:{response.ReasonPhrase}"); + throw new UserFriendlyException( + $"当前模型不可用:{aiModelDescribe.ModelId},状态码:{response.StatusCode},原因:{response.ReasonPhrase}"); } // 确认响应成功 // response.EnsureSuccessStatusCode(); @@ -76,11 +78,11 @@ public class AzureRestChatService : IChatService var result = new CompleteChatResponse(); try { - var jsonObj = MapToJObject(line); + var jsonObj = MapToStreamJObject(line); if (jsonObj is not null) { - var content = GetContent(jsonObj); - var tokenUsage = GetTokenUsage(jsonObj); + var content = GetStreamContent(jsonObj); + var tokenUsage = GetStreamTokenUsage(jsonObj); result = new CompleteChatResponse { TokenUsage = tokenUsage, @@ -98,28 +100,85 @@ public class AzureRestChatService : IChatService } } - public Task CompleteChatAsync(AiModelDescribe aiModelDescribe, List messages, CancellationToken cancellationToken) + public async Task CompleteChatAsync(AiModelDescribe aiModelDescribe, + List messages, CancellationToken cancellationToken) { - throw new NotImplementedException("暂未实现"); + // 设置API URL + var apiUrl = $"{aiModelDescribe.Endpoint}"; + + // 准备请求内容 + var requestBody = new + { + messages = messages.Select(x => new + { + role = x.GetRoleAsString(), + content = x.Content.FirstOrDefault()?.Text + }).ToList(), + stream = false, + // max_tokens = 2048, + // temperature = 0.8, + // top_p = 0.1, + // presence_penalty = 0, + // frequency_penalty = 0, + model = aiModelDescribe.ModelId + }; + + // 序列化请求内容为JSON + string jsonBody = JsonConvert.SerializeObject(requestBody); + + using var httpClient = new HttpClient() + { + //10分钟超时 + Timeout = TimeSpan.FromSeconds(600) + }; + // 设置请求头 + httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {aiModelDescribe.ApiKey}"); + // 其他头信息如Content-Type在StringContent中设置 + + // 构造 POST 请求 + var request = new HttpRequestMessage(HttpMethod.Post, apiUrl); + + // 设置请求内容(示例) + request.Content = new StringContent(jsonBody, Encoding.UTF8, "application/json"); + + // 发送POST请求 + HttpResponseMessage response = + await httpClient.SendAsync(request, cancellationToken); + + if (!response.IsSuccessStatusCode) + { + throw new UserFriendlyException( + $"当前模型不可用:{aiModelDescribe.ModelId},状态码:{response.StatusCode},原因:{response.ReasonPhrase}"); + } + // 确认响应成功 + // response.EnsureSuccessStatusCode(); + + // 读取响应内容 + var responseStr = await response.Content.ReadAsStringAsync(cancellationToken); + var jObject = MapToJObject(responseStr); + + var content = GetContent(jObject); + var usage = GetTokenUsage(jObject); + var result = new CompleteChatResponse + { + TokenUsage = usage, + IsFinish = true, + Content = content + }; + + return result; } - private JObject? MapToJObject(string line) + private JObject? MapToJObject(string body) { - if (line == "data: [DONE]"||string.IsNullOrWhiteSpace(line) ) - { + if (string.IsNullOrWhiteSpace(body)) return null; - } - - if (string.IsNullOrWhiteSpace(line)) - return null; - string prefix = "data: "; - line = line.Substring(prefix.Length); - return JObject.Parse(line); + return JObject.Parse(body); } private string? GetContent(JObject? jsonObj) { - var contentToken = jsonObj.SelectToken("choices[0].delta.content"); + var contentToken = jsonObj.SelectToken("choices[0].message.content"); if (contentToken != null && contentToken.Type != JTokenType.Null) { return contentToken.ToString(); @@ -157,4 +216,60 @@ public class AzureRestChatService : IChatService return null; } + + + private JObject? MapToStreamJObject(string line) + { + if (line == "data: [DONE]" || string.IsNullOrWhiteSpace(line)) + { + return null; + } + + if (string.IsNullOrWhiteSpace(line)) + return null; + string prefix = "data: "; + line = line.Substring(prefix.Length); + return JObject.Parse(line); + } + + private string? GetStreamContent(JObject? jsonObj) + { + var contentToken = jsonObj.SelectToken("choices[0].delta.content"); + if (contentToken != null && contentToken.Type != JTokenType.Null) + { + return contentToken.ToString(); + } + + return null; + } + + private TokenUsage? GetStreamTokenUsage(JObject? jsonObj) + { + var usage = jsonObj.SelectToken("usage"); + if (usage is not null && usage.Type != JTokenType.Null) + { + var result = new TokenUsage(); + var completionTokens = usage["completion_tokens"]; + if (completionTokens is not null && completionTokens.Type != JTokenType.Null) + { + result.OutputTokenCount = completionTokens.ToObject(); + } + + var promptTokens = usage["prompt_tokens"]; + if (promptTokens is not null && promptTokens.Type != JTokenType.Null) + { + result.InputTokenCount = promptTokens.ToObject(); + } + + var totalTokens = usage["total_tokens"]; + if (totalTokens is not null && totalTokens.Type != JTokenType.Null) + { + result.TotalTokenCount = totalTokens.ToObject(); + } + + return result; + } + + return null; + } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs index b3f9041b..a21acd86 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs @@ -9,11 +9,13 @@ using Newtonsoft.Json.Serialization; using OpenAI.Chat; using Volo.Abp.Domain.Services; using Yi.Framework.AiHub.Application.Contracts.Dtos; +using Yi.Framework.AiHub.Application.Contracts.Dtos.OpenAi; using Yi.Framework.AiHub.Domain.AiChat; using Yi.Framework.AiHub.Domain.Entities; using Yi.Framework.AiHub.Domain.Entities.Model; using Yi.Framework.AiHub.Domain.Shared.Dtos; using Yi.Framework.SqlSugarCore.Abstractions; +using Usage = Yi.Framework.AiHub.Application.Contracts.Dtos.Usage; namespace Yi.Framework.AiHub.Domain.Managers; @@ -107,7 +109,7 @@ public class AiGateWayManager : DomainService await using var writer = new StreamWriter(response.Body, Encoding.UTF8, leaveOpen: true); var modelDescribe = await GetModelAsync(modelId); var chatService = LazyServiceProvider.GetRequiredKeyedService(modelDescribe.HandlerName); - var output = await chatService.CompleteChatAsync(modelDescribe, messages, cancellationToken); + var data = await chatService.CompleteChatAsync(modelDescribe, messages, cancellationToken); if (userId is not null) { await _aiMessageManager.CreateUserMessageAsync(userId.Value, sessionId, @@ -115,22 +117,23 @@ public class AiGateWayManager : DomainService { Content = messages.LastOrDefault().Content.FirstOrDefault()?.Text ?? string.Empty, ModelId = modelId, - TokenUsage = output.TokenUsage, + TokenUsage = data.TokenUsage, }); await _aiMessageManager.CreateSystemMessageAsync(userId.Value, sessionId, new MessageInputDto { - Content = output.Content, + Content = data.Content, ModelId = modelId, - TokenUsage = output.TokenUsage + TokenUsage = data.TokenUsage }); - await _usageStatisticsManager.SetUsageAsync(userId.Value, modelId, output.TokenUsage.InputTokenCount, - output.TokenUsage.OutputTokenCount); + await _usageStatisticsManager.SetUsageAsync(userId.Value, modelId, data.TokenUsage.InputTokenCount, + data.TokenUsage.OutputTokenCount); } - var body = JsonConvert.SerializeObject(output, new JsonSerializerSettings + var result = MapToChatCompletions(modelId, data.Content); + var body = JsonConvert.SerializeObject(result, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }); @@ -261,9 +264,9 @@ public class AiGateWayManager : DomainService } - private SendMessageOutputDto MapToMessage(string modelId, string content) + private SendMessageStreamOutputDto MapToMessage(string modelId, string content) { - var output = new SendMessageOutputDto + var output = new SendMessageStreamOutputDto { Id = "chatcmpl-BotYP3BlN5T4g9YPnW0fBSBvKzXdd", Object = "chat.completion.chunk", @@ -338,4 +341,75 @@ public class AiGateWayManager : DomainService return output; } + + private ChatCompletionsOutput MapToChatCompletions(string modelId, string content) + { + return new ChatCompletionsOutput + { + Id = "resp_67ccd2bed1ec8190b14f964abc0542670bb6a6b452d3795b", + Object = "response", + CreatedAt = 1741476542, + Status = "completed", + Error = null, + IncompleteDetails = null, + Instructions = null, + MaxOutputTokens = null, + Model = modelId, + Output = new List() + { + new Output + { + Type = "message", + Id = "msg_67ccd2bf17f0819081ff3bb2cf6508e60bb6a6b452d3795b", + Status = "completed", + Role = "assistant", + Content = new List + { + new Content + { + Type = "output_text", + Text = content, + Annotations = new List() + } + } + } + }, + ParallelToolCalls = true, + PreviousResponseId = null, + Reasoning = new Reasoning + { + Effort = null, + Summary = null + }, + Store = true, + Temperature = 0, + Text = new Text + { + Format = new Format + { + Type = "text" + } + }, + ToolChoice = "auto", + Tools = new List(), + TopP = 1.0, + Truncation = "disabled", + Usage = new Application.Contracts.Dtos.OpenAi.Usage + { + InputTokens = 0, + InputTokensDetails = new InputTokensDetails + { + CachedTokens = 0 + }, + OutputTokens = 0, + OutputTokensDetails = new OutputTokensDetails + { + ReasoningTokens = 0 + }, + TotalTokens = 0 + }, + User = null, + Metadata = null + }; + } } \ No newline at end of file diff --git a/Yi.Abp.Net8/src/Yi.Abp.SqlSugarCore/YiAbpSqlSugarCoreModule.cs b/Yi.Abp.Net8/src/Yi.Abp.SqlSugarCore/YiAbpSqlSugarCoreModule.cs index d61b3fa1..e8590e08 100644 --- a/Yi.Abp.Net8/src/Yi.Abp.SqlSugarCore/YiAbpSqlSugarCoreModule.cs +++ b/Yi.Abp.Net8/src/Yi.Abp.SqlSugarCore/YiAbpSqlSugarCoreModule.cs @@ -35,7 +35,7 @@ namespace Yi.Abp.SqlsugarCore { public override void ConfigureServices(ServiceConfigurationContext context) { - context.Services.AddYiDbContext(); + context.Services.AddYiDbContext(options => { options.EnabledConcurrencyException = false; }); //默认不开放,可根据项目需要是否Db直接对外开放 //context.Services.AddTransient(x => x.GetRequiredService().SqlSugarClient); } diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs index 1826894f..d87d54db 100644 --- a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs +++ b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs @@ -9,10 +9,7 @@ using Hangfire.Redis.StackExchange; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.StaticFiles; -using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using StackExchange.Redis; @@ -23,13 +20,10 @@ using Volo.Abp.AspNetCore.MultiTenancy; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc.AntiForgery; using Volo.Abp.AspNetCore.Serilog; -using Volo.Abp.AspNetCore.VirtualFileSystem; using Volo.Abp.Auditing; using Volo.Abp.Autofac; -using Volo.Abp.BackgroundJobs.Hangfire; using Volo.Abp.BackgroundWorkers; using Volo.Abp.Caching; -using Volo.Abp.Domain.Repositories; using Volo.Abp.MultiTenancy; using Volo.Abp.Swashbuckle; using Yi.Abp.Application; @@ -44,7 +38,6 @@ using Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection; using Yi.Framework.BackgroundWorkers.Hangfire; using Yi.Framework.Bbs.Application; using Yi.Framework.Bbs.Application.Extensions; -using Yi.Framework.Bbs.Domain.Entities.Forum; using Yi.Framework.ChatHub.Application; using Yi.Framework.CodeGen.Application; using Yi.Framework.Core.Json; @@ -53,7 +46,6 @@ using Yi.Framework.Rbac.Application; using Yi.Framework.Rbac.Domain.Authorization; using Yi.Framework.Rbac.Domain.Shared.Consts; using Yi.Framework.Rbac.Domain.Shared.Options; -using Yi.Framework.SqlSugarCore.Abstractions; using Yi.Framework.Stock.Application; using Yi.Framework.TenantManagement.Application;