feat: 完成ai-hub第一期功能

This commit is contained in:
chenchun
2025-06-25 17:12:09 +08:00
parent 4f71d874bd
commit 695aaedfba
18 changed files with 360 additions and 103 deletions

View File

@@ -5,7 +5,7 @@ public class ModelGetListOutput
/// <summary> /// <summary>
/// 模型ID /// 模型ID
/// </summary> /// </summary>
public long Id { get; set; } public Guid Id { get; set; }
/// <summary> /// <summary>
/// 模型分类 /// 模型分类
@@ -20,7 +20,7 @@ public class ModelGetListOutput
/// <summary> /// <summary>
/// 模型描述 /// 模型描述
/// </summary> /// </summary>
public string ModelDescribe { get; set; } public string? ModelDescribe { get; set; }
/// <summary> /// <summary>
/// 模型价格 /// 模型价格
@@ -55,5 +55,5 @@ public class ModelGetListOutput
/// <summary> /// <summary>
/// 备注信息 /// 备注信息
/// </summary> /// </summary>
public string Remark { get; set; } public string? Remark { get; set; }
} }

View File

@@ -1,17 +0,0 @@
namespace Yi.Framework.AiHub.Application.Contracts.Options;
public class AiGateWayOptions
{
public AiChatOptionDic Chats { get; set; }
}
public class AiChatOptionDic : Dictionary<string, AiChatModelOptions>
{
}
public class AiChatModelOptions
{
public List<string> ModelIds { get; set; }
public string Endpoint { get; set; }
public string ApiKey { get; set; }
}

View File

@@ -5,17 +5,17 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Identity.Client;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using OpenAI.Chat; using OpenAI.Chat;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using Volo.Abp.Users; using Volo.Abp.Users;
using Yi.Framework.AiHub.Application.Contracts.Dtos; using Yi.Framework.AiHub.Application.Contracts.Dtos;
using Yi.Framework.AiHub.Application.Contracts.Options; using Yi.Framework.AiHub.Domain.Entities;
using Yi.Framework.AiHub.Domain.Managers; using Yi.Framework.AiHub.Domain.Managers;
using Yi.Framework.Rbac.Application.Contracts.IServices; using Yi.Framework.Rbac.Application.Contracts.IServices;
using Yi.Framework.Rbac.Domain.Shared.Dtos; using Yi.Framework.Rbac.Domain.Shared.Dtos;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.AiHub.Application.Services; namespace Yi.Framework.AiHub.Application.Services;
@@ -24,16 +24,17 @@ namespace Yi.Framework.AiHub.Application.Services;
/// </summary> /// </summary>
public class AiChatService : ApplicationService public class AiChatService : ApplicationService
{ {
private readonly AiGateWayOptions _options;
private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHttpContextAccessor _httpContextAccessor;
private readonly AiMessageManager _aiMessageManager; private readonly AiMessageManager _aiMessageManager;
private readonly ISqlSugarRepository<AiModelEntity> _aiModelRepository;
private readonly AiBlacklistManager _aiBlacklistManager;
public AiChatService(IOptions<AiGateWayOptions> options, IHttpContextAccessor httpContextAccessor, public AiChatService(IHttpContextAccessor httpContextAccessor,
AiMessageManager aiMessageManager) AiMessageManager aiMessageManager, AiBlacklistManager aiBlacklistManager)
{ {
_options = options.Value;
this._httpContextAccessor = httpContextAccessor; this._httpContextAccessor = httpContextAccessor;
_aiMessageManager = aiMessageManager; _aiMessageManager = aiMessageManager;
_aiBlacklistManager = aiBlacklistManager;
} }
@@ -56,18 +57,20 @@ public class AiChatService : ApplicationService
/// <returns></returns> /// <returns></returns>
public async Task<List<ModelGetListOutput>> GetModelAsync() public async Task<List<ModelGetListOutput>> GetModelAsync()
{ {
var output = _options.Chats.SelectMany(x => x.Value.ModelIds) var output = await _aiModelRepository._DbQueryable.Select(x => new ModelGetListOutput
.Select(x => new ModelGetListOutput() {
{ Id = x.Id,
Id = 001, Category = "chat",
Category = "chat", ModelName = x.Name,
ModelName = x, ModelDescribe = x.Description,
ModelDescribe = "这是一个直连模型", ModelPrice = 0,
ModelPrice = 4, ModelType = "1",
ModelType = "1", ModelShow = "0",
ModelShow = "0", SystemPrompt = null,
Remark = "直连模型" ApiHost = null,
}).ToList(); ApiKey = null,
Remark = x.Description
}).ToListAsync();
return output; return output;
} }
@@ -79,6 +82,25 @@ public class AiChatService : ApplicationService
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
public async Task PostSendAsync(SendMessageInput input, CancellationToken cancellationToken) public async Task PostSendAsync(SendMessageInput input, CancellationToken cancellationToken)
{ {
//除了免费模型,其他的模型都要校验
if (input.Model != "DeepSeek-R1-0528")
{
//有token需要黑名单校验
if (CurrentUser.IsAuthenticated)
{
await _aiBlacklistManager.VerifiyAiBlacklist(CurrentUser.GetId());
if (!CurrentUser.Roles.Contains("YiXinAi-Vip"))
{
throw new UserFriendlyException("该模型需要VIP用户才能使用请购买VIP后重新登录重试");
}
}
else
{
throw new UserFriendlyException("未登录用户只能使用未加速的DeepSeek-R1请登录后重试");
}
}
//前面都是校验,后面才是真正的调用
var httpContext = this._httpContextAccessor.HttpContext; var httpContext = this._httpContextAccessor.HttpContext;
var response = httpContext.Response; var response = httpContext.Response;
// 设置响应头,声明是 SSE 流 // 设置响应头,声明是 SSE 流
@@ -116,7 +138,7 @@ public class AiChatService : ApplicationService
// 启动一个后台任务来消费队列 // 启动一个后台任务来消费队列
var outputTask = Task.Run(async () => var outputTask = Task.Run(async () =>
{ {
while (!(isComplete&&messageQueue.IsEmpty)) while (!(isComplete && messageQueue.IsEmpty))
{ {
if (messageQueue.TryDequeue(out var message)) if (messageQueue.TryDequeue(out var message))
{ {
@@ -153,16 +175,15 @@ public class AiChatService : ApplicationService
await outputTask; await outputTask;
if (CurrentUser.IsAuthenticated && input.SessionId.HasValue) if (CurrentUser.IsAuthenticated && input.SessionId.HasValue)
{ {
// 等待接入token await _aiMessageManager.CreateMessageAsync(CurrentUser.GetId(), input.SessionId.Value, new MessageInputDto
// await _aiMessageManager.CreateMessageAsync(CurrentUser.GetId(), input.SessionId.Value, new MessageInputDto {
// { Content = input.Messages.LastOrDefault().Content,
// Content = null, Role = input.Messages.LastOrDefault().Role,
// Role = null, DeductCost = 0,
// DeductCost = 0, TotalTokens = 0,
// TotalTokens = 0, ModelId = input.Model,
// ModelId = null, Remark = null
// Remark = null });
// });
} }
} }

View File

@@ -0,0 +1,49 @@
namespace Yi.Framework.AiHub.Domain.Shared.Dtos;
public class AiModelDescribe
{
/// <summary>
/// 应用id
/// </summary>
public Guid AppId { get; set; }
/// <summary>
/// 应用名称
/// </summary>
public string AppName { get; set; }
/// <summary>
/// 应用终结点
/// </summary>
public string Endpoint { get; set; }
/// <summary>
/// 应用key
/// </summary>
public string ApiKey { get; set; }
/// <summary>
/// 排序
/// </summary>
public int OrderNum { get; set; }
/// <summary>
/// 处理名
/// </summary>
public string HandlerName { get; set; }
/// <summary>
/// 模型id
/// </summary>
public string ModelId { get; set; }
/// <summary>
/// 模型名称
/// </summary>
public string ModelName { get; set; }
/// <summary>
/// 模型描述
/// </summary>
public string? Description { get; set; }
}

View File

@@ -7,7 +7,6 @@
<ItemGroup> <ItemGroup>
<Folder Include="Consts\" /> <Folder Include="Consts\" />
<Folder Include="Dtos\" />
<Folder Include="Enums\" /> <Folder Include="Enums\" />
<Folder Include="Etos\" /> <Folder Include="Etos\" />
</ItemGroup> </ItemGroup>

View File

@@ -1,8 +1,10 @@
using OpenAI.Chat; using OpenAI.Chat;
using Yi.Framework.AiHub.Domain.Shared.Dtos;
namespace Yi.Framework.AiHub.Domain.AiChat; namespace Yi.Framework.AiHub.Domain.AiChat;
public interface IChatService public interface IChatService
{ {
public IAsyncEnumerable<string> CompleteChatAsync(string modelId, List<ChatMessage> messages,CancellationToken cancellationToken); public IAsyncEnumerable<string> CompleteChatAsync(AiModelDescribe aiModelDescribe, List<ChatMessage> messages,
CancellationToken cancellationToken);
} }

View File

@@ -1,28 +1,24 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Azure; using Azure;
using Azure.AI.OpenAI; using Azure.AI.OpenAI;
using Microsoft.Extensions.Options;
using OpenAI.Chat; using OpenAI.Chat;
using Yi.Framework.AiHub.Application.Contracts.Options; using Yi.Framework.AiHub.Domain.Shared.Dtos;
namespace Yi.Framework.AiHub.Domain.AiChat.Impl; namespace Yi.Framework.AiHub.Domain.AiChat.Impl;
public class AzureChatService : IChatService public class AzureChatService : IChatService
{ {
private readonly AiChatModelOptions _options; public AzureChatService()
public AzureChatService(IOptions<AiGateWayOptions> options)
{ {
this._options = options.Value.Chats[nameof(AzureChatService)];
} }
public async IAsyncEnumerable<string> CompleteChatAsync(string modelId, List<ChatMessage> messages, public async IAsyncEnumerable<string> CompleteChatAsync(AiModelDescribe aiModelDescribe, List<ChatMessage> messages,
[EnumeratorCancellation] CancellationToken cancellationToken) [EnumeratorCancellation] CancellationToken cancellationToken)
{ {
var endpoint = new Uri(_options.Endpoint); var endpoint = new Uri(aiModelDescribe.Endpoint);
var deploymentName = modelId; var deploymentName = aiModelDescribe.ModelId;
var apiKey = _options.ApiKey; var apiKey = aiModelDescribe.ApiKey;
AzureOpenAIClient azureClient = new( AzureOpenAIClient azureClient = new(
endpoint, endpoint,

View File

@@ -1,28 +1,24 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OpenAI.Chat; using OpenAI.Chat;
using Yi.Framework.AiHub.Application.Contracts.Options;
using Yi.Framework.AiHub.Domain.Extensions; using Yi.Framework.AiHub.Domain.Extensions;
using Yi.Framework.AiHub.Domain.Shared.Dtos;
namespace Yi.Framework.AiHub.Domain.AiChat.Impl; namespace Yi.Framework.AiHub.Domain.AiChat.Impl;
public class AzureRestChatService : IChatService public class AzureRestChatService : IChatService
{ {
private readonly AiChatModelOptions _options; public AzureRestChatService()
public AzureRestChatService(IOptions<AiGateWayOptions> options)
{ {
this._options = options.Value.Chats[nameof(AzureRestChatService)];
} }
public async IAsyncEnumerable<string> CompleteChatAsync(string modelId, List<ChatMessage> messages, public async IAsyncEnumerable<string> CompleteChatAsync(AiModelDescribe aiModelDescribe, List<ChatMessage> messages,
[EnumeratorCancellation] CancellationToken cancellationToken) [EnumeratorCancellation] CancellationToken cancellationToken)
{ {
// 设置API URL // 设置API URL
var apiUrl = $"{_options.Endpoint}models/chat/completions"; var apiUrl = $"{aiModelDescribe.Endpoint}models/chat/completions";
var ss = messages.Select(x => new var ss = messages.Select(x => new
@@ -45,7 +41,7 @@ public class AzureRestChatService : IChatService
top_p = 0.1, top_p = 0.1,
presence_penalty = 0, presence_penalty = 0,
frequency_penalty = 0, frequency_penalty = 0,
model = modelId model = aiModelDescribe.ModelId
}; };
// 序列化请求内容为JSON // 序列化请求内容为JSON
@@ -53,24 +49,25 @@ public class AzureRestChatService : IChatService
using var httpClient = new HttpClient(); using var httpClient = new HttpClient();
// 设置请求头 // 设置请求头
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_options.ApiKey}"); httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {aiModelDescribe.ApiKey}");
// 其他头信息如Content-Type在StringContent中设置 // 其他头信息如Content-Type在StringContent中设置
// 构造 POST 请求 // 构造 POST 请求
var request = new HttpRequestMessage(HttpMethod.Post, apiUrl); var request = new HttpRequestMessage(HttpMethod.Post, apiUrl);
// 设置请求内容(示例) // 设置请求内容(示例)
request.Content =new StringContent(jsonBody, Encoding.UTF8, "application/json"); request.Content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
// 发送POST请求 // 发送POST请求
HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); HttpResponseMessage response =
await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
// 确认响应成功 // 确认响应成功
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
// 读取响应内容 // 读取响应内容
var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken); var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken);
// 从流中读取数据并输出到控制台 // 从流中读取数据并输出到控制台
using var streamReader = new System.IO.StreamReader(responseStream); using var streamReader = new StreamReader(responseStream);
string line; string line;
while ((line = await streamReader.ReadLineAsync(cancellationToken)) != null) while ((line = await streamReader.ReadLineAsync(cancellationToken)) != null)
{ {
@@ -117,7 +114,5 @@ public class AzureRestChatService : IChatService
// 解析失败 // 解析失败
return null; return null;
} }
return null;
} }
} }

View File

@@ -0,0 +1,26 @@
using SqlSugar;
using Volo.Abp.Domain.Entities.Auditing;
namespace Yi.Framework.AiHub.Domain.Entities;
/// <summary>
/// ai黑名单
/// </summary>
[SugarTable("Ai_Blacklist")]
public class AiBlacklistAggregateRoot : FullAuditedAggregateRoot<Guid>
{
/// <summary>
/// 用户
/// </summary>
public Guid UserId { get; set; }
/// <summary>
/// 有效开始时间
/// </summary>
public DateTime StartTime { get; set; }
/// <summary>
/// 有效结束时间
/// </summary>
public DateTime EndTime { get; set; }
}

View File

@@ -0,0 +1,38 @@
using SqlSugar;
using Volo.Abp.Domain.Entities.Auditing;
using Yi.Framework.Core.Data;
namespace Yi.Framework.AiHub.Domain.Entities;
/// <summary>
/// ai应用
/// </summary>
[SugarTable("Ai_App")]
public class AiAppAggregateRoot : FullAuditedAggregateRoot<Guid>, IOrderNum
{
/// <summary>
/// 应用名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 应用终结点
/// </summary>
public string Endpoint { get; set; }
/// <summary>
/// 应用key
/// </summary>
public string ApiKey { get; set; }
/// <summary>
/// 排序
/// </summary>
public int OrderNum { get; set; }
/// <summary>
/// ai模型
/// </summary>
[Navigate(NavigateType.OneToMany, nameof(AiModelEntity.AiAppId))]
public List<AiModelEntity> AiModels { get; set; }
}

View File

@@ -0,0 +1,48 @@
using SqlSugar;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Auditing;
using Yi.Framework.Core.Data;
namespace Yi.Framework.AiHub.Domain.Entities;
/// <summary>
/// ai模型定义
/// </summary>
[SugarTable("Ai_Model")]
public class AiModelEntity : Entity<Guid>, IOrderNum,ISoftDelete
{
/// <summary>
/// 处理名
/// </summary>
public string HandlerName { get; set; }
/// <summary>
/// 模型id
/// </summary>
public string ModelId { get; set; }
/// <summary>
/// 模型名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 模型描述
/// </summary>
public string? Description { get; set; }
/// <summary>
/// 排序
/// </summary>
public int OrderNum { get; set; }
/// <summary>
/// 软删除
/// </summary>
public bool IsDeleted { get; set; }
/// <summary>
/// ai应用id
/// </summary>
public Guid AiAppId { get; set; }
}

View File

@@ -0,0 +1,31 @@
using SqlSugar;
using Volo.Abp.Domain.Entities.Auditing;
namespace Yi.Framework.AiHub.Domain.Entities;
/// <summary>
/// 用量统计
/// </summary>
[SugarTable("Ai_UsageStatistics")]
public class UsageStatisticsAggregateRoot : FullAuditedAggregateRoot<Guid>
{
/// <summary>
/// 用户id
/// </summary>
public Guid UserId { get; set; }
/// <summary>
/// 哪个模型
/// </summary>
public string ModelId { get; set; }
/// <summary>
/// 总token使用
/// </summary>
public decimal TotalTokens { get; set; }
/// <summary>
/// 对话次数
/// </summary>
public int Number { get; set; }
}

View File

@@ -0,0 +1,31 @@
using Volo.Abp.Domain.Services;
using Yi.Framework.AiHub.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.AiHub.Domain.Managers;
public class AiBlacklistManager : DomainService
{
private readonly ISqlSugarRepository<AiBlacklistAggregateRoot> _aiBlacklistRepository;
public AiBlacklistManager(ISqlSugarRepository<AiBlacklistAggregateRoot> aiBlacklistRepository)
{
_aiBlacklistRepository = aiBlacklistRepository;
}
/// <summary>
/// 校验黑名单
/// </summary>
/// <param name="userId"></param>
/// <exception cref="UserFriendlyException"></exception>
public async Task VerifiyAiBlacklist(Guid userId)
{
var now = DateTime.Now;
if (await _aiBlacklistRepository._DbQueryable
.Where(x => now >= x.StartTime && now <= x.EndTime)
.AnyAsync(x => x.UserId == userId))
{
throw new UserFriendlyException("当前用户已被加入黑名单,请联系管理员处理");
}
}
}

View File

@@ -1,35 +1,70 @@
using Azure; using System.Runtime.CompilerServices;
using Azure.AI.OpenAI;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenAI.Chat; using OpenAI.Chat;
using Volo.Abp.Domain.Services; using Volo.Abp.Domain.Services;
using Yi.Framework.AiHub.Application.Contracts.Options;
using Yi.Framework.AiHub.Domain.AiChat; using Yi.Framework.AiHub.Domain.AiChat;
using Yi.Framework.AiHub.Domain.Entities;
using Yi.Framework.AiHub.Domain.Shared.Dtos;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.AiHub.Domain.Managers; namespace Yi.Framework.AiHub.Domain.Managers;
public class AiGateWayManager : DomainService public class AiGateWayManager : DomainService
{ {
private readonly AiGateWayOptions _options; private readonly ISqlSugarRepository<AiAppAggregateRoot> _aiAppRepository;
public AiGateWayManager(IOptions<AiGateWayOptions> options) public AiGateWayManager(ISqlSugarRepository<AiAppAggregateRoot> aiAppRepository)
{ {
this._options = options.Value; _aiAppRepository = aiAppRepository;
} }
public IAsyncEnumerable<string> CompleteChatAsync(string modelId, List<ChatMessage> messages, /// <summary>
CancellationToken cancellationToken) /// 获取模型
/// </summary>
/// <param name="modelId"></param>
/// <returns></returns>
private async Task<AiModelDescribe> GetModelAsync(string modelId)
{ {
foreach (var chat in _options.Chats) var allApp = await _aiAppRepository._DbQueryable.Includes(x => x.AiModels).ToListAsync();
foreach (var app in allApp)
{ {
if (chat.Value.ModelIds.Contains(modelId)) var model = app.AiModels.FirstOrDefault(x => x.ModelId == modelId);
if (model is not null)
{ {
var chatService = LazyServiceProvider.GetRequiredKeyedService<IChatService>(chat.Key); return new AiModelDescribe
return chatService.CompleteChatAsync(modelId, messages, cancellationToken); {
AppId = app.Id,
AppName = app.Name,
Endpoint = app.Endpoint,
ApiKey = app.ApiKey,
OrderNum = model.OrderNum,
HandlerName = model.HandlerName,
ModelId = model.ModelId,
ModelName = model.Name,
Description = model.Description
};
} }
} }
throw new UserFriendlyException($"当前暂不支持该模型-【{modelId}】"); throw new UserFriendlyException($"{modelId}模型当前版本不支持");
}
/// <summary>
/// 聊天完成
/// </summary>
/// <param name="modelId"></param>
/// <param name="messages"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async IAsyncEnumerable<string> CompleteChatAsync(string modelId, List<ChatMessage> messages,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
var modelDescribe = await GetModelAsync(modelId);
var chatService = LazyServiceProvider.GetRequiredKeyedService<IChatService>(modelDescribe.HandlerName);
await foreach (var result in chatService.CompleteChatAsync(modelDescribe, messages, cancellationToken))
{
yield return result;
}
} }
} }

View File

@@ -1,11 +1,5 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using OpenAI.Chat;
using Volo.Abp.Caching;
using Volo.Abp.Domain; using Volo.Abp.Domain;
using Yi.Framework.AiHub.Application.Contracts.Options;
using Yi.Framework.AiHub.Domain.AiChat;
using Yi.Framework.AiHub.Domain.AiChat.Impl;
using Yi.Framework.AiHub.Domain.Managers;
using Yi.Framework.AiHub.Domain.Shared; using Yi.Framework.AiHub.Domain.Shared;
using Yi.Framework.Mapster; using Yi.Framework.Mapster;
@@ -23,11 +17,10 @@ namespace Yi.Framework.AiHub.Domain
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
var services = context.Services; var services = context.Services;
Configure<AiGateWayOptions>(configuration.GetSection("AiGateWay")); // Configure<AiGateWayOptions>(configuration.GetSection("AiGateWay"));
//
// services.AddKeyedTransient<IChatService, AzureChatService>(nameof(AzureChatService));
services.AddKeyedTransient<IChatService, AzureChatService>(nameof(AzureChatService)); // services.AddKeyedTransient<IChatService, AzureRestChatService>(nameof(AzureRestChatService));
services.AddKeyedTransient<IChatService, AzureRestChatService>(nameof(AzureRestChatService));
} }
public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context) public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context)

View File

@@ -56,12 +56,22 @@
<Content Update="wwwroot\stock\**"> <Content Update="wwwroot\stock\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Remove="logs\**" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="ip2region.db"> <None Update="ip2region.db">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Remove="logs\**" />
</ItemGroup>
<ItemGroup>
<Compile Remove="logs\**" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="logs\**" />
</ItemGroup> </ItemGroup>
</Project> </Project>