From d824ecc2e9fd3f8e06cf0c0db92d1d841e0aee72 Mon Sep 17 00:00:00 2001 From: colin Date: Thu, 15 Jan 2026 08:52:38 +0800 Subject: [PATCH 01/88] feat(ai): Add localized error codes --- .../LINGYUN/Abp/AI/AbpAICoreModule.cs | 16 +++++++++++++++- .../LINGYUN/Abp/AI/AbpAIErrorCodes.cs | 9 +++++++++ .../Abp/AI/Localization/Resources/en.json | 6 ++++++ .../Abp/AI/Localization/Resources/zh-Hans.json | 6 ++++++ 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAIErrorCodes.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/en.json create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/zh-Hans.json diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs index 8718805c2..8bdcf3754 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs @@ -5,7 +5,9 @@ using System; using System.Collections.Generic; using Volo.Abp.AI; using Volo.Abp.Localization; +using Volo.Abp.Localization.ExceptionHandling; using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; namespace LINGYUN.Abp.AI; @@ -21,15 +23,27 @@ public class AbpAICoreModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + Configure(options => { - options.Resources.Add(); + options.Resources + .Add() + .AddVirtualJson("/LINGYUN/Abp/AI/Localization/Resources"); }); Configure(options => { options.ChatClientProviders.Add(); }); + + Configure(options => + { + options.MapCodeNamespace(AbpAIErrorCodes.Namespace, typeof(AbpAIResource)); + }); } private static void AutoAddDefinitionProviders(IServiceCollection services) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAIErrorCodes.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAIErrorCodes.cs new file mode 100644 index 000000000..bb261dea1 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAIErrorCodes.cs @@ -0,0 +1,9 @@ +namespace LINGYUN.Abp.AI; +public static class AbpAIErrorCodes +{ + public const string Namespace = "Abp.AI"; + /// + /// 工作区不可用: {Workspace}! + /// + public const string WorkspaceIsNotEnabled = Namespace + ":110001"; +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/en.json b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/en.json new file mode 100644 index 000000000..399e2a7c9 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/en.json @@ -0,0 +1,6 @@ +{ + "culture": "en", + "texts": { + "Abp.AI:110001": "Workspace is not enabled: {Workspace}!" + } +} \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/zh-Hans.json b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/zh-Hans.json new file mode 100644 index 000000000..3fc69d30d --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/zh-Hans.json @@ -0,0 +1,6 @@ +{ + "culture": "zh-Hans", + "texts": { + "Abp.AI:110001": "工作区不可用: {Workspace}!" + } +} \ No newline at end of file From 4f603429f44d2552021fcb18f2258654468237c0 Mon Sep 17 00:00:00 2001 From: colin Date: Thu, 15 Jan 2026 08:53:48 +0800 Subject: [PATCH 02/88] feat(ai): check state of use workspace --- .../Abp/AI/Agent/ChatClientAgentFactory.cs | 50 ++++++++++++++----- .../LINGYUN.Abp.AI.Core.csproj | 5 ++ .../LINGYUN/Abp/AI/ChatClientFactory.cs | 20 +++++++- .../LINGYUN/Abp/AI/KernelFactory.cs | 19 ++++++- 4 files changed, 79 insertions(+), 15 deletions(-) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs index 62dd55dca..5f233f4e2 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs @@ -5,24 +5,29 @@ using Microsoft.Extensions.Localization; using System.Collections.Concurrent; using System.Threading.Tasks; using Volo.Abp.AI; +using Volo.Abp.Authorization; using Volo.Abp.DependencyInjection; +using Volo.Abp.SimpleStateChecking; namespace LINGYUN.Abp.AI.Agent; public class ChatClientAgentFactory : IChatClientAgentFactory, ISingletonDependency { private readonly static ConcurrentDictionary _chatClientAgentCache = new(); - private readonly IChatClientFactory _chatClientFactory; - private readonly IStringLocalizerFactory _stringLocalizerFactory; - private readonly IWorkspaceDefinitionManager _workspaceDefinitionManager; + protected IChatClientFactory ChatClientFactory { get; } + protected IStringLocalizerFactory StringLocalizerFactory { get; } + protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; } + protected ISimpleStateCheckerManager StateCheckerManager { get; } public ChatClientAgentFactory( IChatClientFactory chatClientFactory, IStringLocalizerFactory stringLocalizerFactory, - IWorkspaceDefinitionManager workspaceDefinitionManager) + IWorkspaceDefinitionManager workspaceDefinitionManager, + ISimpleStateCheckerManager stateCheckerManager) { - _chatClientFactory = chatClientFactory; - _stringLocalizerFactory = stringLocalizerFactory; - _workspaceDefinitionManager = workspaceDefinitionManager; + ChatClientFactory = chatClientFactory; + StringLocalizerFactory = stringLocalizerFactory; + WorkspaceDefinitionManager = workspaceDefinitionManager; + StateCheckerManager = stateCheckerManager; } public async virtual Task CreateAsync() @@ -33,14 +38,19 @@ public class ChatClientAgentFactory : IChatClientAgentFactory, ISingletonDepende return chatClientAgent; } - var chatClient = await _chatClientFactory.CreateAsync(); + var chatClient = await ChatClientFactory.CreateAsync(); - var workspaceDefine = await _workspaceDefinitionManager.GetOrNullAsync(workspace); + var workspaceDefine = await WorkspaceDefinitionManager.GetOrNullAsync(workspace); + + if (workspaceDefine != null) + { + await CheckWorkspaceStateAsync(workspaceDefine); + } string? description = null; if (workspaceDefine?.Description != null) { - description = workspaceDefine.Description.Localize(_stringLocalizerFactory); + description = workspaceDefine.Description.Localize(StringLocalizerFactory); } chatClientAgent = chatClient.CreateAIAgent( @@ -59,13 +69,16 @@ public class ChatClientAgentFactory : IChatClientAgentFactory, ISingletonDepende { return chatClientAgent; } - var workspaceDefine = await _workspaceDefinitionManager.GetAsync(workspace); - var chatClient = await _chatClientFactory.CreateAsync(workspace); + var workspaceDefine = await WorkspaceDefinitionManager.GetAsync(workspace); + + await CheckWorkspaceStateAsync(workspaceDefine); + + var chatClient = await ChatClientFactory.CreateAsync(workspace); string? description = null; if (workspaceDefine.Description != null) { - description = workspaceDefine.Description.Localize(_stringLocalizerFactory); + description = workspaceDefine.Description.Localize(StringLocalizerFactory); } chatClientAgent = chatClient.CreateAIAgent( @@ -77,4 +90,15 @@ public class ChatClientAgentFactory : IChatClientAgentFactory, ISingletonDepende return chatClientAgent; } + + protected async virtual Task CheckWorkspaceStateAsync(WorkspaceDefinition workspace) + { + if (!await StateCheckerManager.IsEnabledAsync(workspace)) + { + throw new AbpAuthorizationException( + $"Workspace is not enabled: {workspace.Name}!", + AbpAIErrorCodes.WorkspaceIsNotEnabled) + .WithData("Workspace", workspace.Name); + } + } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj index 1c67a9757..57d089b1c 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj @@ -14,6 +14,11 @@ + + + + + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs index bbdfa9c90..c9dd2e74c 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs @@ -5,21 +5,26 @@ using System.Collections.Concurrent; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.AI; +using Volo.Abp.Authorization; using Volo.Abp.DependencyInjection; +using Volo.Abp.SimpleStateChecking; namespace LINGYUN.Abp.AI; public class ChatClientFactory : IChatClientFactory, ISingletonDependency { private readonly static ConcurrentDictionary _chatClientCache = new(); + protected ISimpleStateCheckerManager StateCheckerManager { get; } protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; } protected IChatClientProviderManager ChatClientProviderManager { get; } protected IServiceProvider ServiceProvider { get; } public ChatClientFactory( + ISimpleStateCheckerManager stateCheckerManager, IWorkspaceDefinitionManager workspaceDefinitionManager, IChatClientProviderManager chatClientProviderManager, IServiceProvider serviceProvider) { + StateCheckerManager = stateCheckerManager; WorkspaceDefinitionManager = workspaceDefinitionManager; ChatClientProviderManager = chatClientProviderManager; ServiceProvider = serviceProvider; @@ -58,6 +63,8 @@ public class ChatClientFactory : IChatClientFactory, ISingletonDependency var workspaceDefine = await WorkspaceDefinitionManager.GetAsync(workspace); + await CheckWorkspaceStateAsync(workspaceDefine); + chatClient = await CreateChatClientAsync(workspaceDefine); _chatClientCache.TryAdd(workspace, chatClient); @@ -77,6 +84,17 @@ public class ChatClientFactory : IChatClientFactory, ISingletonDependency return await provider.CreateAsync(workspace); } - throw new AbpException($"The ChatClient provider implementation named {workspace.Provider} was not found"); + throw new AbpException($"The ChatClient provider implementation named {workspace.Provider} was not found!"); + } + + protected async virtual Task CheckWorkspaceStateAsync(WorkspaceDefinition workspace) + { + if (!await StateCheckerManager.IsEnabledAsync(workspace)) + { + throw new AbpAuthorizationException( + $"Workspace is not enabled: {workspace.Name}!", + AbpAIErrorCodes.WorkspaceIsNotEnabled) + .WithData("Workspace", workspace.Name); + } } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelFactory.cs index 5709d9127..a7067651a 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelFactory.cs @@ -5,21 +5,26 @@ using System.Collections.Concurrent; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.AI; +using Volo.Abp.Authorization; using Volo.Abp.DependencyInjection; +using Volo.Abp.SimpleStateChecking; namespace LINGYUN.Abp.AI; public class KernelFactory : IKernelFactory, ISingletonDependency { private readonly static ConcurrentDictionary _kernelCache = new(); + protected ISimpleStateCheckerManager StateCheckerManager { get; } protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; } protected IKernelProviderManager KernelProviderManager { get; } protected IServiceProvider ServiceProvider { get; } public KernelFactory( + ISimpleStateCheckerManager stateCheckerManager, IWorkspaceDefinitionManager workspaceDefinitionManager, IKernelProviderManager kernelProviderManager, IServiceProvider serviceProvider) { + StateCheckerManager = stateCheckerManager; WorkspaceDefinitionManager = workspaceDefinitionManager; KernelProviderManager = kernelProviderManager; ServiceProvider = serviceProvider; @@ -58,6 +63,8 @@ public class KernelFactory : IKernelFactory, ISingletonDependency var workspaceDefine = await WorkspaceDefinitionManager.GetAsync(workspace); + await CheckWorkspaceStateAsync(workspaceDefine); + kernel = await CreateKernelAsync(workspaceDefine); _kernelCache.TryAdd(workspace, kernel); @@ -77,6 +84,16 @@ public class KernelFactory : IKernelFactory, ISingletonDependency return await provider.CreateAsync(workspace); } - throw new AbpException($"The Kernel provider implementation named {workspace.Provider} was not found"); + throw new AbpException($"The Kernel provider implementation named {workspace.Provider} was not found!"); + } + protected async virtual Task CheckWorkspaceStateAsync(WorkspaceDefinition workspace) + { + if (!await StateCheckerManager.IsEnabledAsync(workspace)) + { + throw new AbpAuthorizationException( + $"Workspace is not enabled: {workspace.Name}!", + AbpAIErrorCodes.WorkspaceIsNotEnabled) + .WithData("Workspace", workspace.Name); + } } } From 8bee13911a1846fc7474555e86d1253012008b37 Mon Sep 17 00:00:00 2001 From: colin Date: Thu, 15 Jan 2026 16:29:59 +0800 Subject: [PATCH 03/88] feat(ai): Add definition of workspace properties --- .../Abp/AI/Agent/ChatClientAgentFactory.cs | 60 ++++++++++--- .../Abp/AI/Agent/IChatClientAgentFactory.cs | 5 +- .../LINGYUN/Abp/AI/Agent/WorkspaceAIAgent.cs | 59 +++++++++++++ .../LINGYUN/Abp/AI/ChatClientFactory.cs | 10 +-- .../LINGYUN/Abp/AI/ChatClientProvider.cs | 18 ++++ .../LINGYUN/Abp/AI/IChatClientFactory.cs | 5 +- .../LINGYUN/Abp/AI/IChatClientProvider.cs | 3 +- .../LINGYUN/Abp/AI/IWorkspaceChatClient.cs | 8 ++ .../Abp/AI/OpenAIChatClientProvider.cs | 24 ++++-- .../LINGYUN/Abp/AI/WorkspaceChatClient.cs | 69 +++++++++++++++ .../Abp/AI/Workspaces/WorkspaceDefinition.cs | 86 ++++++++++++++++++- 11 files changed, 313 insertions(+), 34 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/WorkspaceAIAgent.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProvider.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IWorkspaceChatClient.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/WorkspaceChatClient.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs index 5f233f4e2..106e40768 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs @@ -2,6 +2,8 @@ using Microsoft.Agents.AI; using Microsoft.Extensions.AI; using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Logging; +using System; using System.Collections.Concurrent; using System.Threading.Tasks; using Volo.Abp.AI; @@ -12,25 +14,27 @@ using Volo.Abp.SimpleStateChecking; namespace LINGYUN.Abp.AI.Agent; public class ChatClientAgentFactory : IChatClientAgentFactory, ISingletonDependency { - private readonly static ConcurrentDictionary _chatClientAgentCache = new(); - + private readonly static ConcurrentDictionary _chatClientAgentCache = new(); + protected IServiceProvider ServiceProvider { get; } protected IChatClientFactory ChatClientFactory { get; } protected IStringLocalizerFactory StringLocalizerFactory { get; } protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; } protected ISimpleStateCheckerManager StateCheckerManager { get; } public ChatClientAgentFactory( + IServiceProvider serviceProvider, IChatClientFactory chatClientFactory, IStringLocalizerFactory stringLocalizerFactory, IWorkspaceDefinitionManager workspaceDefinitionManager, ISimpleStateCheckerManager stateCheckerManager) { + ServiceProvider = serviceProvider; ChatClientFactory = chatClientFactory; StringLocalizerFactory = stringLocalizerFactory; WorkspaceDefinitionManager = workspaceDefinitionManager; StateCheckerManager = stateCheckerManager; } - public async virtual Task CreateAsync() + public async virtual Task CreateAsync() { var workspace = WorkspaceNameAttribute.GetWorkspaceName(); if (_chatClientAgentCache.TryGetValue(workspace, out var chatClientAgent)) @@ -53,17 +57,33 @@ public class ChatClientAgentFactory : IChatClientAgentFactory, ISingletonDepende description = workspaceDefine.Description.Localize(StringLocalizerFactory); } - chatClientAgent = chatClient.CreateAIAgent( - instructions: workspaceDefine?.SystemPrompt, - name: workspaceDefine?.Name, - description: description); + var clientAgentOptions = new ChatClientAgentOptions + { + ChatOptions = new ChatOptions + { + Instructions = workspaceDefine?.Instructions, + Temperature = workspaceDefine?.Temperature, + MaxOutputTokens = workspaceDefine?.MaxOutputTokens, + PresencePenalty = workspaceDefine?.PresencePenalty, + FrequencyPenalty = workspaceDefine?.FrequencyPenalty, + }, + Name = workspaceDefine?.Name, + Description = description + }; + + chatClientAgent = new WorkspaceAIAgent( + new AIAgentBuilder(chatClient.CreateAIAgent(clientAgentOptions)) + .UseLogging() + .UseOpenTelemetry() + .Build(ServiceProvider), + workspaceDefine); _chatClientAgentCache.TryAdd(workspace, chatClientAgent); return chatClientAgent; } - public async virtual Task CreateAsync(string workspace) + public async virtual Task CreateAsync(string workspace) { if (_chatClientAgentCache.TryGetValue(workspace, out var chatClientAgent)) { @@ -81,10 +101,26 @@ public class ChatClientAgentFactory : IChatClientAgentFactory, ISingletonDepende description = workspaceDefine.Description.Localize(StringLocalizerFactory); } - chatClientAgent = chatClient.CreateAIAgent( - instructions: workspaceDefine.SystemPrompt, - name: workspaceDefine.Name, - description: description); + var clientAgentOptions = new ChatClientAgentOptions + { + ChatOptions = new ChatOptions + { + Instructions = workspaceDefine.Instructions, + Temperature = workspaceDefine.Temperature, + MaxOutputTokens = workspaceDefine.MaxOutputTokens, + PresencePenalty = workspaceDefine.PresencePenalty, + FrequencyPenalty = workspaceDefine.FrequencyPenalty, + }, + Name = workspaceDefine.Name, + Description = description + }; + + chatClientAgent = new WorkspaceAIAgent( + new AIAgentBuilder(chatClient.CreateAIAgent(clientAgentOptions)) + .UseLogging() + .UseOpenTelemetry() + .Build(ServiceProvider), + workspaceDefine); _chatClientAgentCache.TryAdd(workspace, chatClientAgent); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IChatClientAgentFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IChatClientAgentFactory.cs index 0d15511fd..cf7f938ef 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IChatClientAgentFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IChatClientAgentFactory.cs @@ -1,13 +1,12 @@ using JetBrains.Annotations; -using Microsoft.Agents.AI; using System.Threading.Tasks; namespace LINGYUN.Abp.AI.Agent; public interface IChatClientAgentFactory { [NotNull] - Task CreateAsync(); + Task CreateAsync(); [NotNull] - Task CreateAsync(string workspace); + Task CreateAsync(string workspace); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/WorkspaceAIAgent.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/WorkspaceAIAgent.cs new file mode 100644 index 000000000..41539ec09 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/WorkspaceAIAgent.cs @@ -0,0 +1,59 @@ +using LINGYUN.Abp.AI.Workspaces; +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.AI.Agent; +public class WorkspaceAIAgent : AIAgent +{ + protected AIAgent InnerAIAgent { get; } + public WorkspaceDefinition? Workspace { get; } + + public WorkspaceAIAgent( + AIAgent innerAIAgent, + WorkspaceDefinition? workspace = null) + { + InnerAIAgent = innerAIAgent; + Workspace = workspace; + } + + public override AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) + { + return InnerAIAgent.DeserializeThread(serializedThread, jsonSerializerOptions); + } + + public override AgentThread GetNewThread() + { + return InnerAIAgent.GetNewThread(); + } + + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + { + return InnerAIAgent.RunAsync(GetChatMessages(messages), thread, options, cancellationToken); + } + + protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + { + return InnerAIAgent.RunStreamingAsync(GetChatMessages(messages), thread, options, cancellationToken); + } + + protected virtual IEnumerable GetChatMessages(IEnumerable messages) + { + if (Workspace?.SystemPrompt?.IsNullOrWhiteSpace() == false && + !messages.Any(msg => msg.Role == ChatRole.System)) + { + // 加入系统提示词 + messages = new List + { + new ChatMessage(ChatRole.System, Workspace.SystemPrompt) + }.Union(messages); + } + + return messages; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs index c9dd2e74c..04f59d3e3 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs @@ -12,7 +12,7 @@ using Volo.Abp.SimpleStateChecking; namespace LINGYUN.Abp.AI; public class ChatClientFactory : IChatClientFactory, ISingletonDependency { - private readonly static ConcurrentDictionary _chatClientCache = new(); + private readonly static ConcurrentDictionary _chatClientCache = new(); protected ISimpleStateCheckerManager StateCheckerManager { get; } protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; } protected IChatClientProviderManager ChatClientProviderManager { get; } @@ -30,7 +30,7 @@ public class ChatClientFactory : IChatClientFactory, ISingletonDependency ServiceProvider = serviceProvider; } - public async virtual Task CreateAsync() + public async virtual Task CreateAsync() { var workspace = WorkspaceNameAttribute.GetWorkspaceName(); if (_chatClientCache.TryGetValue(workspace, out var chatClient)) @@ -44,7 +44,7 @@ public class ChatClientFactory : IChatClientFactory, ISingletonDependency chatClientAccessor is IChatClientAccessor accessor && accessor.ChatClient != null) { - chatClient = accessor.ChatClient; + chatClient = new WorkspaceChatClient(accessor.ChatClient); _chatClientCache.TryAdd(workspace, chatClient); } else @@ -54,7 +54,7 @@ public class ChatClientFactory : IChatClientFactory, ISingletonDependency return chatClient; } - public async virtual Task CreateAsync(string workspace) + public async virtual Task CreateAsync(string workspace) { if (_chatClientCache.TryGetValue(workspace, out var chatClient)) { @@ -72,7 +72,7 @@ public class ChatClientFactory : IChatClientFactory, ISingletonDependency return chatClient; } - protected async virtual Task CreateChatClientAsync(WorkspaceDefinition workspace) + protected async virtual Task CreateChatClientAsync(WorkspaceDefinition workspace) { foreach (var provider in ChatClientProviderManager.Providers) { diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProvider.cs new file mode 100644 index 000000000..34f536698 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProvider.cs @@ -0,0 +1,18 @@ +using LINGYUN.Abp.AI.Workspaces; +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.AI; +public abstract class ChatClientProvider : IChatClientProvider, ITransientDependency +{ + public abstract string Name { get; } + + protected IServiceProvider ServiceProvider { get; } + protected ChatClientProvider(IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + } + + public abstract Task CreateAsync(WorkspaceDefinition workspace); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientFactory.cs index 3ab6e99bb..437197ebb 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientFactory.cs @@ -1,13 +1,12 @@ using JetBrains.Annotations; -using Microsoft.Extensions.AI; using System.Threading.Tasks; namespace LINGYUN.Abp.AI; public interface IChatClientFactory { [NotNull] - Task CreateAsync(); + Task CreateAsync(); [NotNull] - Task CreateAsync(string workspace); + Task CreateAsync(string workspace); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs index bdf5aeeb7..ebde49eec 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs @@ -1,5 +1,4 @@ using LINGYUN.Abp.AI.Workspaces; -using Microsoft.Extensions.AI; using System.Threading.Tasks; namespace LINGYUN.Abp.AI; @@ -7,5 +6,5 @@ public interface IChatClientProvider { string Name { get; } - Task CreateAsync(WorkspaceDefinition workspace); + Task CreateAsync(WorkspaceDefinition workspace); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IWorkspaceChatClient.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IWorkspaceChatClient.cs new file mode 100644 index 000000000..04592ec53 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IWorkspaceChatClient.cs @@ -0,0 +1,8 @@ +using LINGYUN.Abp.AI.Workspaces; +using Microsoft.Extensions.AI; + +namespace LINGYUN.Abp.AI; +public interface IWorkspaceChatClient : IChatClient +{ + WorkspaceDefinition? Workspace { get; } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs index f8d145334..6574c47a4 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs @@ -5,17 +5,20 @@ using System; using System.ClientModel; using System.Threading.Tasks; using Volo.Abp; -using Volo.Abp.DependencyInjection; namespace LINGYUN.Abp.AI; -public class OpenAIChatClientProvider : IChatClientProvider, ITransientDependency +public class OpenAIChatClientProvider : ChatClientProvider { private const string DefaultEndpoint = "https://api.openai.com/v1"; public const string ProviderName = "OpenAI"; - public virtual string Name => ProviderName; + public override string Name => ProviderName; + public OpenAIChatClientProvider(IServiceProvider serviceProvider) + : base(serviceProvider) + { + } - public virtual Task CreateAsync(WorkspaceDefinition workspace) + public override Task CreateAsync(WorkspaceDefinition workspace) { Check.NotNull(workspace, nameof(workspace)); Check.NotNullOrWhiteSpace(workspace.ApiKey, nameof(WorkspaceDefinition.ApiKey)); @@ -29,8 +32,17 @@ public class OpenAIChatClientProvider : IChatClientProvider, ITransientDependenc var chatClient = openAIClient .GetChatClient(workspace.ModelName) - .AsIChatClient(); + .AsIChatClient() + .AsBuilder() + .UseLogging() + .UseOpenTelemetry() + .UseFunctionInvocation() + .UseDistributedCache() + .UseChatReducer() + .Build(ServiceProvider); + + IWorkspaceChatClient workspaceChatClient = new WorkspaceChatClient(chatClient, workspace); - return Task.FromResult(chatClient); + return Task.FromResult(workspaceChatClient); } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/WorkspaceChatClient.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/WorkspaceChatClient.cs new file mode 100644 index 000000000..cc0563249 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/WorkspaceChatClient.cs @@ -0,0 +1,69 @@ +using LINGYUN.Abp.AI.Tools; +using LINGYUN.Abp.AI.Workspaces; +using Microsoft.Extensions.AI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.AI; +public class WorkspaceChatClient : IWorkspaceChatClient +{ + public IChatClient Client { get; } + public WorkspaceDefinition? Workspace { get; } + public WorkspaceChatClient( + IChatClient chatClient, + WorkspaceDefinition? workspace = null) + { + Workspace = workspace; + Client = chatClient; + } + + public virtual Task GetResponseAsync(IEnumerable messages, ChatOptions? options = null, CancellationToken cancellationToken = default) + { + return Client.GetResponseAsync(GetChatMessages(messages), GetChatOptions(options), cancellationToken); + } + + public virtual IAsyncEnumerable GetStreamingResponseAsync(IEnumerable messages, ChatOptions? options = null, CancellationToken cancellationToken = default) + { + return Client.GetStreamingResponseAsync(GetChatMessages(messages), GetChatOptions(options), cancellationToken); + } + + public virtual object? GetService(Type serviceType, object? serviceKey = null) + { + return Client.GetService(serviceType, serviceKey); + } + + public virtual void Dispose() + { + Client.Dispose(); + } + + protected virtual ChatOptions GetChatOptions(ChatOptions? options) + { + options ??= new ChatOptions(); + options.Instructions = Workspace?.Instructions; + options.Temperature ??= Workspace?.Temperature; + options.MaxOutputTokens ??= Workspace?.MaxOutputTokens; + options.FrequencyPenalty ??= Workspace?.FrequencyPenalty; + options.PresencePenalty ??= Workspace?.PresencePenalty; + + return options; + } + + protected virtual IEnumerable GetChatMessages(IEnumerable messages) + { + if (Workspace?.SystemPrompt?.IsNullOrWhiteSpace() == false && + !messages.Any(msg => msg.Role == ChatRole.System)) + { + // 加入系统提示词 + messages = new List + { + new ChatMessage(ChatRole.System, Workspace.SystemPrompt) + }.Union(messages); + } + + return messages; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs index 56f37e127..2f3288616 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs @@ -41,16 +41,84 @@ public class WorkspaceDefinition : IHasSimpleStateCheckers /// /// API 身份验证密钥 /// - public string? ApiKey { get; set; } + public string? ApiKey { get; private set; } /// /// 自定义端点 URL /// - public string? ApiBaseUrl { get; set; } + public string? ApiBaseUrl { get; private set; } /// /// 系统提示词 /// public string? SystemPrompt { get; set; } /// + /// 附加系统提示词 + /// + public string? Instructions { get; set; } + /// + /// 聊天回复时所依据的温度值, 为空时由模型提供者决定默认值 + /// + /// + /// 范围在 0 到 2 之间, 数值越高(比如 0.8)会使输出更加随机,而数值越低(比如 0.2)则会使输出更加集中且更具确定性 + /// + public float? Temperature { + get => _temperature; + set { + if (value.HasValue) + { + _temperature = Check.Range(value.Value, nameof(value), 0, 2); + } + else + { + _temperature = value; + } + } + } + private float? _temperature; + /// + /// 限制一次请求中模型生成 completion 的最大 token 数 + /// + public int? MaxOutputTokens { get; set; } + /// + /// 介于 -2.0 和 2.0 之间的数字 + /// + /// + /// 如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性 + /// + public float? FrequencyPenalty { + get => _frequencyPenalty; + set { + if (value.HasValue) + { + _frequencyPenalty = Check.Range(value.Value, nameof(value), -2, 2); + } + else + { + _frequencyPenalty = value; + } + } + } + private float? _frequencyPenalty; + /// + /// 介于 -2.0 和 2.0 之间的数字 + /// + /// + /// 如果该值为正,那么新 token 会根据其是否已在已有文本中出现受到相应的惩罚,从而增加模型谈论新主题的可能性 + /// + public float? PresencePenalty { + get => _presencePenalty; + set { + if (value.HasValue) + { + _presencePenalty = Check.Range(value.Value, nameof(value), -2, 2); + } + else + { + _presencePenalty = value; + } + } + } + private float? _presencePenalty; + /// /// 启用/禁用工作区 /// public bool IsEnabled { get; set; } @@ -65,7 +133,13 @@ public class WorkspaceDefinition : IHasSimpleStateCheckers string provider, string modelName, ILocalizableString displayName, - ILocalizableString? description = null) + ILocalizableString? description = null, + string? systemPrompt = null, + string? instructions = null, + float? temperature = null, + int? maxOutputTokens = null, + float? frequencyPenalty = null, + float? presencePenalty = null) { Name = name; Provider = provider; @@ -73,6 +147,12 @@ public class WorkspaceDefinition : IHasSimpleStateCheckers _displayName = displayName; _displayName = displayName; Description = description; + SystemPrompt = systemPrompt; + Instructions = instructions; + Temperature = temperature; + MaxOutputTokens = maxOutputTokens; + FrequencyPenalty = frequencyPenalty; + PresencePenalty = presencePenalty; IsEnabled = true; Properties = new Dictionary(); From 1d35fc6628f0b346592c7e14a2934d5f5ddc3874 Mon Sep 17 00:00:00 2001 From: colin Date: Thu, 15 Jan 2026 20:25:58 +0800 Subject: [PATCH 04/88] feat(ai): Add simple conversation service --- .../LINGYUN/Abp/AI/Agent/AgentFactory.cs | 110 ++++++++++++++ .../LINGYUN/Abp/AI/Agent/AgentService.cs | 91 ++++++++++++ .../Abp/AI/Agent/ChatClientAgentFactory.cs | 140 ------------------ .../LINGYUN/Abp/AI/Agent/IAgentFactory.cs | 13 ++ .../LINGYUN/Abp/AI/Agent/IAgentService.cs | 8 + .../Abp/AI/Agent/IChatClientAgentFactory.cs | 12 -- .../LINGYUN/Abp/AI/Agent/WorkspaceAIAgent.cs | 38 ++--- .../LINGYUN/Abp/AI/AbpAICoreModule.cs | 1 + .../LINGYUN/Abp/AI/ChatClientFactory.cs | 35 +---- .../LINGYUN/Abp/AI/ChatClientProvider.cs | 3 +- .../LINGYUN/Abp/AI/IChatClientFactory.cs | 5 +- .../LINGYUN/Abp/AI/IChatClientProvider.cs | 3 +- .../LINGYUN/Abp/AI/IWorkspaceChatClient.cs | 8 - .../LINGYUN/Abp/AI/KernelFactory.cs | 28 +--- .../LINGYUN/Abp/AI/KernelProvider.cs | 19 +++ .../Abp/AI/Messages/IUserMessageStore.cs | 11 ++ .../AI/Messages/InMemoryUserMessageStore.cs | 40 +++++ .../LINGYUN/Abp/AI/Models/ChatMessageInfo.cs | 8 + .../LINGYUN/Abp/AI/Models/MediaMessage.cs | 13 ++ .../LINGYUN/Abp/AI/Models/TokenUsageInfo.cs | 17 +++ .../LINGYUN/Abp/AI/Models/UserMessage.cs | 37 +++++ .../Abp/AI/OpenAIChatClientProvider.cs | 6 +- .../LINGYUN/Abp/AI/OpenAIKernelProvider.cs | 39 +++++ .../LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs | 9 ++ .../Abp/AI/Tokens/InMemoryTokenUsageStore.cs | 50 +++++++ .../LINGYUN/Abp/AI/Tools/GlobalFunctions.cs | 11 ++ .../LINGYUN/Abp/AI/WorkspaceChatClient.cs | 69 --------- 27 files changed, 517 insertions(+), 307 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentFactory.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentService.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IChatClientAgentFactory.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IWorkspaceChatClient.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelProvider.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessageInfo.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/MediaMessage.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIKernelProvider.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tools/GlobalFunctions.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/WorkspaceChatClient.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs new file mode 100644 index 000000000..3739fdfd3 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs @@ -0,0 +1,110 @@ +using LINGYUN.Abp.AI.Workspaces; +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.Localization; +using System; +using System.Threading.Tasks; +using Volo.Abp.AI; +using Volo.Abp.Authorization; +using Volo.Abp.DependencyInjection; +using Volo.Abp.SimpleStateChecking; + +namespace LINGYUN.Abp.AI.Agent; +public class AgentFactory : IAgentFactory, IScopedDependency +{ + protected IServiceProvider ServiceProvider { get; } + protected IChatClientFactory ChatClientFactory { get; } + protected IStringLocalizerFactory StringLocalizerFactory { get; } + protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; } + protected ISimpleStateCheckerManager StateCheckerManager { get; } + public AgentFactory( + IServiceProvider serviceProvider, + IChatClientFactory chatClientFactory, + IStringLocalizerFactory stringLocalizerFactory, + IWorkspaceDefinitionManager workspaceDefinitionManager, + ISimpleStateCheckerManager stateCheckerManager) + { + ServiceProvider = serviceProvider; + ChatClientFactory = chatClientFactory; + StringLocalizerFactory = stringLocalizerFactory; + WorkspaceDefinitionManager = workspaceDefinitionManager; + StateCheckerManager = stateCheckerManager; + } + + public async virtual Task CreateAsync() + { + var workspace = WorkspaceNameAttribute.GetWorkspaceName(); + + var chatClient = await ChatClientFactory.CreateAsync(); + + var workspaceDefine = await WorkspaceDefinitionManager.GetOrNullAsync(workspace); + + if (workspaceDefine != null) + { + await CheckWorkspaceStateAsync(workspaceDefine); + } + + return await CreateAgentAsync(chatClient, workspaceDefine); + } + + public async virtual Task CreateAsync(string workspace) + { + var workspaceDefine = await WorkspaceDefinitionManager.GetAsync(workspace); + + await CheckWorkspaceStateAsync(workspaceDefine); + + var chatClient = await ChatClientFactory.CreateAsync(workspace); + + return await CreateAgentAsync(chatClient, workspaceDefine); + } + + protected async virtual Task CreateAgentAsync(IChatClient chatClient, WorkspaceDefinition? workspace) + { + string? description = null; + if (workspace?.Description != null) + { + description = workspace.Description.Localize(StringLocalizerFactory); + } + + var tools = await GetAgentToolsAsync(workspace); + + var clientAgentOptions = new ChatClientAgentOptions + { + ChatOptions = new ChatOptions + { + Instructions = workspace?.Instructions, + Temperature = workspace?.Temperature, + MaxOutputTokens = workspace?.MaxOutputTokens, + PresencePenalty = workspace?.PresencePenalty, + FrequencyPenalty = workspace?.FrequencyPenalty, + Tools = tools, + }, + Name = workspace?.Name, + Description = description + }; + + var aiAgent = chatClient.CreateAIAgent(clientAgentOptions) + .AsBuilder() + .UseLogging() + .UseOpenTelemetry() + .Build(ServiceProvider); + + return new WorkspaceAIAgent(aiAgent, workspace); + } + + protected virtual Task GetAgentToolsAsync(WorkspaceDefinition? workspace) + { + return Task.FromResult([]); + } + + protected async virtual Task CheckWorkspaceStateAsync(WorkspaceDefinition workspace) + { + if (!await StateCheckerManager.IsEnabledAsync(workspace)) + { + throw new AbpAuthorizationException( + $"Workspace is not enabled: {workspace.Name}!", + AbpAIErrorCodes.WorkspaceIsNotEnabled) + .WithData("Workspace", workspace.Name); + } + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs new file mode 100644 index 000000000..ed34c4200 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs @@ -0,0 +1,91 @@ +using LINGYUN.Abp.AI.Messages; +using LINGYUN.Abp.AI.Models; +using LINGYUN.Abp.AI.Tokens; +using Microsoft.Extensions.AI; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.AI.Agent; +public class AgentService : IAgentService, IScopedDependency +{ + private readonly IAgentFactory _agentFactory; + private readonly ITokenUsageStore _tokenUsageStore; + private readonly IUserMessageStore _userMessageStore; + public AgentService( + IAgentFactory agentFactory, + ITokenUsageStore tokenUsageStore, + IUserMessageStore userMessageStore) + { + _agentFactory = agentFactory; + _tokenUsageStore = tokenUsageStore; + _userMessageStore = userMessageStore; + } + + public async virtual IAsyncEnumerable SendMessageAsync(UserMessage message) + { + var messages = await BuildChatMessages(message); + + var agent = await _agentFactory.CreateAsync(message.Workspace); + + var agentRunRes = agent.RunStreamingAsync(messages); + + var agentMessageBuilder = new StringBuilder(); + + await foreach (var item in agentRunRes) + { + agentMessageBuilder.Append(item); + yield return item.Text; + + await StoreTokenUsageInfo(message, item.RawRepresentation); + } + + await StoreChatMessage(message, agentMessageBuilder.ToString()); + } + + protected virtual async Task> BuildChatMessages(UserMessage message) + { + var messages = new List(); + + var historyMessages = await _userMessageStore.GetHistoryMessagesAsync(message.ChatId); + + foreach (var chatMessage in historyMessages) + { + messages.Add(new ChatMessage(ChatRole.System, chatMessage.Content)); + } + + messages.Add(new ChatMessage(ChatRole.User, message.Content)); + + return messages; + } + + protected async virtual Task StoreChatMessage(UserMessage message, string agentMessage) + { + message.WithReply(agentMessage); + + await _userMessageStore.SaveMessageAsync(message); + } + + protected async virtual Task StoreTokenUsageInfo(UserMessage message, object? rawRepresentation) + { + if (rawRepresentation is ChatResponseUpdate update) + { + var tokenUsageInfos = update.Contents + .OfType() + .Where(usage => usage.Details != null) + .Select(usage => new TokenUsageInfo(message.Workspace) + { + TotalTokenCount = usage.Details.TotalTokenCount, + CachedInputTokenCount = usage.Details.CachedInputTokenCount, + InputTokenCount = usage.Details.InputTokenCount, + AdditionalCounts = usage.Details.AdditionalCounts, + OutputTokenCount = usage.Details.OutputTokenCount, + ReasoningTokenCount = usage.Details.ReasoningTokenCount, + }); + + await _tokenUsageStore.SaveTokenUsagesAsync(tokenUsageInfos); + } + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs deleted file mode 100644 index 106e40768..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/ChatClientAgentFactory.cs +++ /dev/null @@ -1,140 +0,0 @@ -using LINGYUN.Abp.AI.Workspaces; -using Microsoft.Agents.AI; -using Microsoft.Extensions.AI; -using Microsoft.Extensions.Localization; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Concurrent; -using System.Threading.Tasks; -using Volo.Abp.AI; -using Volo.Abp.Authorization; -using Volo.Abp.DependencyInjection; -using Volo.Abp.SimpleStateChecking; - -namespace LINGYUN.Abp.AI.Agent; -public class ChatClientAgentFactory : IChatClientAgentFactory, ISingletonDependency -{ - private readonly static ConcurrentDictionary _chatClientAgentCache = new(); - protected IServiceProvider ServiceProvider { get; } - protected IChatClientFactory ChatClientFactory { get; } - protected IStringLocalizerFactory StringLocalizerFactory { get; } - protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; } - protected ISimpleStateCheckerManager StateCheckerManager { get; } - public ChatClientAgentFactory( - IServiceProvider serviceProvider, - IChatClientFactory chatClientFactory, - IStringLocalizerFactory stringLocalizerFactory, - IWorkspaceDefinitionManager workspaceDefinitionManager, - ISimpleStateCheckerManager stateCheckerManager) - { - ServiceProvider = serviceProvider; - ChatClientFactory = chatClientFactory; - StringLocalizerFactory = stringLocalizerFactory; - WorkspaceDefinitionManager = workspaceDefinitionManager; - StateCheckerManager = stateCheckerManager; - } - - public async virtual Task CreateAsync() - { - var workspace = WorkspaceNameAttribute.GetWorkspaceName(); - if (_chatClientAgentCache.TryGetValue(workspace, out var chatClientAgent)) - { - return chatClientAgent; - } - - var chatClient = await ChatClientFactory.CreateAsync(); - - var workspaceDefine = await WorkspaceDefinitionManager.GetOrNullAsync(workspace); - - if (workspaceDefine != null) - { - await CheckWorkspaceStateAsync(workspaceDefine); - } - - string? description = null; - if (workspaceDefine?.Description != null) - { - description = workspaceDefine.Description.Localize(StringLocalizerFactory); - } - - var clientAgentOptions = new ChatClientAgentOptions - { - ChatOptions = new ChatOptions - { - Instructions = workspaceDefine?.Instructions, - Temperature = workspaceDefine?.Temperature, - MaxOutputTokens = workspaceDefine?.MaxOutputTokens, - PresencePenalty = workspaceDefine?.PresencePenalty, - FrequencyPenalty = workspaceDefine?.FrequencyPenalty, - }, - Name = workspaceDefine?.Name, - Description = description - }; - - chatClientAgent = new WorkspaceAIAgent( - new AIAgentBuilder(chatClient.CreateAIAgent(clientAgentOptions)) - .UseLogging() - .UseOpenTelemetry() - .Build(ServiceProvider), - workspaceDefine); - - _chatClientAgentCache.TryAdd(workspace, chatClientAgent); - - return chatClientAgent; - } - - public async virtual Task CreateAsync(string workspace) - { - if (_chatClientAgentCache.TryGetValue(workspace, out var chatClientAgent)) - { - return chatClientAgent; - } - var workspaceDefine = await WorkspaceDefinitionManager.GetAsync(workspace); - - await CheckWorkspaceStateAsync(workspaceDefine); - - var chatClient = await ChatClientFactory.CreateAsync(workspace); - - string? description = null; - if (workspaceDefine.Description != null) - { - description = workspaceDefine.Description.Localize(StringLocalizerFactory); - } - - var clientAgentOptions = new ChatClientAgentOptions - { - ChatOptions = new ChatOptions - { - Instructions = workspaceDefine.Instructions, - Temperature = workspaceDefine.Temperature, - MaxOutputTokens = workspaceDefine.MaxOutputTokens, - PresencePenalty = workspaceDefine.PresencePenalty, - FrequencyPenalty = workspaceDefine.FrequencyPenalty, - }, - Name = workspaceDefine.Name, - Description = description - }; - - chatClientAgent = new WorkspaceAIAgent( - new AIAgentBuilder(chatClient.CreateAIAgent(clientAgentOptions)) - .UseLogging() - .UseOpenTelemetry() - .Build(ServiceProvider), - workspaceDefine); - - _chatClientAgentCache.TryAdd(workspace, chatClientAgent); - - return chatClientAgent; - } - - protected async virtual Task CheckWorkspaceStateAsync(WorkspaceDefinition workspace) - { - if (!await StateCheckerManager.IsEnabledAsync(workspace)) - { - throw new AbpAuthorizationException( - $"Workspace is not enabled: {workspace.Name}!", - AbpAIErrorCodes.WorkspaceIsNotEnabled) - .WithData("Workspace", workspace.Name); - } - } -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentFactory.cs new file mode 100644 index 000000000..b969e8f67 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentFactory.cs @@ -0,0 +1,13 @@ +using JetBrains.Annotations; +using Microsoft.Agents.AI; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.AI.Agent; +public interface IAgentFactory +{ + [NotNull] + Task CreateAsync(); + + [NotNull] + Task CreateAsync(string workspace); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentService.cs new file mode 100644 index 000000000..24ad9e19f --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentService.cs @@ -0,0 +1,8 @@ +using LINGYUN.Abp.AI.Models; +using System.Collections.Generic; + +namespace LINGYUN.Abp.AI.Agent; +public interface IAgentService +{ + IAsyncEnumerable SendMessageAsync(UserMessage message); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IChatClientAgentFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IChatClientAgentFactory.cs deleted file mode 100644 index cf7f938ef..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IChatClientAgentFactory.cs +++ /dev/null @@ -1,12 +0,0 @@ -using JetBrains.Annotations; -using System.Threading.Tasks; - -namespace LINGYUN.Abp.AI.Agent; -public interface IChatClientAgentFactory -{ - [NotNull] - Task CreateAsync(); - - [NotNull] - Task CreateAsync(string workspace); -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/WorkspaceAIAgent.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/WorkspaceAIAgent.cs index 41539ec09..03998df61 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/WorkspaceAIAgent.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/WorkspaceAIAgent.cs @@ -11,49 +11,49 @@ using System.Threading.Tasks; namespace LINGYUN.Abp.AI.Agent; public class WorkspaceAIAgent : AIAgent { - protected AIAgent InnerAIAgent { get; } - public WorkspaceDefinition? Workspace { get; } - - public WorkspaceAIAgent( - AIAgent innerAIAgent, - WorkspaceDefinition? workspace = null) + protected AIAgent InnerAgent { get; } + protected WorkspaceDefinition? Workspace { get; } + public WorkspaceAIAgent(AIAgent innerAgent, WorkspaceDefinition? workspace) { - InnerAIAgent = innerAIAgent; + InnerAgent = innerAgent; Workspace = workspace; } public override AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) { - return InnerAIAgent.DeserializeThread(serializedThread, jsonSerializerOptions); + return InnerAgent.DeserializeThread(serializedThread, jsonSerializerOptions); } public override AgentThread GetNewThread() { - return InnerAIAgent.GetNewThread(); + return InnerAgent.GetNewThread(); } protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return InnerAIAgent.RunAsync(GetChatMessages(messages), thread, options, cancellationToken); + return InnerAgent.RunAsync(GetChatMessages(messages), thread, options, cancellationToken); } protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return InnerAIAgent.RunStreamingAsync(GetChatMessages(messages), thread, options, cancellationToken); + return InnerAgent.RunStreamingAsync(GetChatMessages(messages), thread, options, cancellationToken); } protected virtual IEnumerable GetChatMessages(IEnumerable messages) { - if (Workspace?.SystemPrompt?.IsNullOrWhiteSpace() == false && - !messages.Any(msg => msg.Role == ChatRole.System)) + var unionMessages = new List(); + + if (Workspace != null) { - // 加入系统提示词 - messages = new List + if (!Workspace.SystemPrompt.IsNullOrWhiteSpace()) { - new ChatMessage(ChatRole.System, Workspace.SystemPrompt) - }.Union(messages); + unionMessages.Add(new ChatMessage(ChatRole.System, Workspace.SystemPrompt)); + } + if (!Workspace.Instructions.IsNullOrWhiteSpace()) + { + unionMessages.Add(new ChatMessage(ChatRole.System, Workspace.Instructions)); + } } - - return messages; + return unionMessages.Union(messages); } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs index 8bdcf3754..3f81bd409 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs @@ -38,6 +38,7 @@ public class AbpAICoreModule : AbpModule Configure(options => { options.ChatClientProviders.Add(); + options.KernelProviders.Add(); }); Configure(options => diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs index 04f59d3e3..3f5280dee 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs @@ -1,7 +1,6 @@ using LINGYUN.Abp.AI.Workspaces; using Microsoft.Extensions.AI; using System; -using System.Collections.Concurrent; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.AI; @@ -10,9 +9,8 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.SimpleStateChecking; namespace LINGYUN.Abp.AI; -public class ChatClientFactory : IChatClientFactory, ISingletonDependency +public class ChatClientFactory : IChatClientFactory, IScopedDependency { - private readonly static ConcurrentDictionary _chatClientCache = new(); protected ISimpleStateCheckerManager StateCheckerManager { get; } protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; } protected IChatClientProviderManager ChatClientProviderManager { get; } @@ -30,13 +28,9 @@ public class ChatClientFactory : IChatClientFactory, ISingletonDependency ServiceProvider = serviceProvider; } - public async virtual Task CreateAsync() + public async virtual Task CreateAsync() { var workspace = WorkspaceNameAttribute.GetWorkspaceName(); - if (_chatClientCache.TryGetValue(workspace, out var chatClient)) - { - return chatClient; - } var chatClientAccessorType = typeof(IChatClientAccessor<>).MakeGenericType(typeof(TWorkspace)); var chatClientAccessor = ServiceProvider.GetService(chatClientAccessorType); @@ -44,35 +38,22 @@ public class ChatClientFactory : IChatClientFactory, ISingletonDependency chatClientAccessor is IChatClientAccessor accessor && accessor.ChatClient != null) { - chatClient = new WorkspaceChatClient(accessor.ChatClient); - _chatClientCache.TryAdd(workspace, chatClient); + return accessor.ChatClient; } - else - { - chatClient = await CreateAsync(workspace); - } - return chatClient; + + return await CreateAsync(workspace); } - public async virtual Task CreateAsync(string workspace) + public async virtual Task CreateAsync(string workspace) { - if (_chatClientCache.TryGetValue(workspace, out var chatClient)) - { - return chatClient; - } - var workspaceDefine = await WorkspaceDefinitionManager.GetAsync(workspace); await CheckWorkspaceStateAsync(workspaceDefine); - chatClient = await CreateChatClientAsync(workspaceDefine); - - _chatClientCache.TryAdd(workspace, chatClient); - - return chatClient; + return await CreateChatClientAsync(workspaceDefine); } - protected async virtual Task CreateChatClientAsync(WorkspaceDefinition workspace) + protected async virtual Task CreateChatClientAsync(WorkspaceDefinition workspace) { foreach (var provider in ChatClientProviderManager.Providers) { diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProvider.cs index 34f536698..c15b7875f 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProvider.cs @@ -1,4 +1,5 @@ using LINGYUN.Abp.AI.Workspaces; +using Microsoft.Extensions.AI; using System; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; @@ -14,5 +15,5 @@ public abstract class ChatClientProvider : IChatClientProvider, ITransientDepend ServiceProvider = serviceProvider; } - public abstract Task CreateAsync(WorkspaceDefinition workspace); + public abstract Task CreateAsync(WorkspaceDefinition workspace); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientFactory.cs index 437197ebb..3ab6e99bb 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientFactory.cs @@ -1,12 +1,13 @@ using JetBrains.Annotations; +using Microsoft.Extensions.AI; using System.Threading.Tasks; namespace LINGYUN.Abp.AI; public interface IChatClientFactory { [NotNull] - Task CreateAsync(); + Task CreateAsync(); [NotNull] - Task CreateAsync(string workspace); + Task CreateAsync(string workspace); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs index ebde49eec..bdf5aeeb7 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs @@ -1,4 +1,5 @@ using LINGYUN.Abp.AI.Workspaces; +using Microsoft.Extensions.AI; using System.Threading.Tasks; namespace LINGYUN.Abp.AI; @@ -6,5 +7,5 @@ public interface IChatClientProvider { string Name { get; } - Task CreateAsync(WorkspaceDefinition workspace); + Task CreateAsync(WorkspaceDefinition workspace); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IWorkspaceChatClient.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IWorkspaceChatClient.cs deleted file mode 100644 index 04592ec53..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IWorkspaceChatClient.cs +++ /dev/null @@ -1,8 +0,0 @@ -using LINGYUN.Abp.AI.Workspaces; -using Microsoft.Extensions.AI; - -namespace LINGYUN.Abp.AI; -public interface IWorkspaceChatClient : IChatClient -{ - WorkspaceDefinition? Workspace { get; } -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelFactory.cs index a7067651a..5778e7921 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelFactory.cs @@ -1,7 +1,6 @@ using LINGYUN.Abp.AI.Workspaces; using Microsoft.SemanticKernel; using System; -using System.Collections.Concurrent; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.AI; @@ -10,9 +9,8 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.SimpleStateChecking; namespace LINGYUN.Abp.AI; -public class KernelFactory : IKernelFactory, ISingletonDependency +public class KernelFactory : IKernelFactory, IScopedDependency { - private readonly static ConcurrentDictionary _kernelCache = new(); protected ISimpleStateCheckerManager StateCheckerManager { get; } protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; } protected IKernelProviderManager KernelProviderManager { get; } @@ -33,10 +31,6 @@ public class KernelFactory : IKernelFactory, ISingletonDependency public async virtual Task CreateAsync() { var workspace = WorkspaceNameAttribute.GetWorkspaceName(); - if (_kernelCache.TryGetValue(workspace, out var kernel)) - { - return kernel; - } var kernelAccessorType = typeof(IKernelAccessor<>).MakeGenericType(typeof(TWorkspace)); var kernelAccessor = ServiceProvider.GetService(kernelAccessorType); @@ -44,32 +38,18 @@ public class KernelFactory : IKernelFactory, ISingletonDependency kernelAccessor is IKernelAccessor accessor && accessor.Kernel != null) { - kernel = accessor.Kernel; - _kernelCache.TryAdd(workspace, kernel); + return accessor.Kernel; } - else - { - kernel = await CreateAsync(workspace); - } - return kernel; + return await CreateAsync(workspace); } public async virtual Task CreateAsync(string workspace) { - if (_kernelCache.TryGetValue(workspace, out var kernel)) - { - return kernel; - } - var workspaceDefine = await WorkspaceDefinitionManager.GetAsync(workspace); await CheckWorkspaceStateAsync(workspaceDefine); - kernel = await CreateKernelAsync(workspaceDefine); - - _kernelCache.TryAdd(workspace, kernel); - - return kernel; + return await CreateKernelAsync(workspaceDefine); } protected async virtual Task CreateKernelAsync(WorkspaceDefinition workspace) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelProvider.cs new file mode 100644 index 000000000..957f2a43c --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelProvider.cs @@ -0,0 +1,19 @@ +using LINGYUN.Abp.AI.Workspaces; +using Microsoft.SemanticKernel; +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.AI; +public abstract class KernelProvider : IKernelProvider, ITransientDependency +{ + public abstract string Name { get; } + + protected IServiceProvider ServiceProvider { get; } + protected KernelProvider(IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + } + + public abstract Task CreateAsync(WorkspaceDefinition workspace); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs new file mode 100644 index 000000000..f9d8baf54 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs @@ -0,0 +1,11 @@ +using LINGYUN.Abp.AI.Models; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.AI.Messages; +public interface IUserMessageStore +{ + Task SaveMessageAsync(UserMessage message); + + Task> GetHistoryMessagesAsync(string chatId); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs new file mode 100644 index 000000000..2492a51ae --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs @@ -0,0 +1,40 @@ +using LINGYUN.Abp.AI.Models; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.AI.Messages; + +[Dependency(ServiceLifetime.Singleton, TryRegister = true)] +public class InMemoryUserMessageStore : IUserMessageStore +{ + private static readonly ConcurrentDictionary> _userMessageCache = new ConcurrentDictionary>(); + + public Task> GetHistoryMessagesAsync(string chatId) + { + if (_userMessageCache.TryGetValue(chatId, out var messages)) + { + return Task.FromResult(messages.Take(5)); + } + + return Task.FromResult>(Array.Empty()); + } + + public Task SaveMessageAsync(UserMessage message) + { + if (_userMessageCache.ContainsKey(message.ChatId)) + { + _userMessageCache[message.ChatId].Add(message); + } + else + { + _userMessageCache[message.ChatId] = new List() { message }; + } + + return Task.FromResult(message.Id); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessageInfo.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessageInfo.cs new file mode 100644 index 000000000..279c9ba9b --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessageInfo.cs @@ -0,0 +1,8 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace LINGYUN.Abp.AI.Models; +internal class ChatMessageInfo +{ +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/MediaMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/MediaMessage.cs new file mode 100644 index 000000000..f851d303f --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/MediaMessage.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Content; + +namespace LINGYUN.Abp.AI.Models; +public class MediaMessage +{ + public string Id { get; } + public IRemoteStreamContent Content { get; } + public MediaMessage(string id, IRemoteStreamContent content) + { + Id = id; + Content = content; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs new file mode 100644 index 000000000..cdac08043 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace LINGYUN.Abp.AI.Models; +public class TokenUsageInfo +{ + public string Workspace { get; } + public long? InputTokenCount { get; set; } + public long? OutputTokenCount { get; set; } + public long? TotalTokenCount { get; set; } + public long? CachedInputTokenCount { get; set; } + public long? ReasoningTokenCount { get; set; } + public IDictionary? AdditionalCounts { get; set; } + public TokenUsageInfo(string workspace) + { + Workspace = workspace; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs new file mode 100644 index 000000000..f902b0c67 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs @@ -0,0 +1,37 @@ +using System.Linq; + +namespace LINGYUN.Abp.AI.Models; +public class UserMessage +{ + public string Id { get; } + public string ChatId { get; } + public string Content { get; } + public string Workspace { get; } + public string ReplyMessage { get; private set; } + public MediaMessage[]? Medias { get; private set; } + public UserMessage( + string workspace, + string id, + string chatId, + string content) + { + Workspace = workspace; + Id = id; + ChatId = chatId; + Content = content; + } + + public UserMessage WithMedia(MediaMessage media) + { + Medias ??= []; + Medias = Medias.Union([media]).ToArray(); + + return this; + } + + public UserMessage WithReply(string replyMessage) + { + ReplyMessage = replyMessage; + return this; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs index 6574c47a4..d13ac08a1 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs @@ -18,7 +18,7 @@ public class OpenAIChatClientProvider : ChatClientProvider { } - public override Task CreateAsync(WorkspaceDefinition workspace) + public override Task CreateAsync(WorkspaceDefinition workspace) { Check.NotNull(workspace, nameof(workspace)); Check.NotNullOrWhiteSpace(workspace.ApiKey, nameof(WorkspaceDefinition.ApiKey)); @@ -41,8 +41,6 @@ public class OpenAIChatClientProvider : ChatClientProvider .UseChatReducer() .Build(ServiceProvider); - IWorkspaceChatClient workspaceChatClient = new WorkspaceChatClient(chatClient, workspace); - - return Task.FromResult(workspaceChatClient); + return Task.FromResult(chatClient); } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIKernelProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIKernelProvider.cs new file mode 100644 index 000000000..2f1bb8f61 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIKernelProvider.cs @@ -0,0 +1,39 @@ +using LINGYUN.Abp.AI.Workspaces; +using Microsoft.SemanticKernel; +using OpenAI; +using System; +using System.ClientModel; +using System.Threading.Tasks; +using Volo.Abp; + +namespace LINGYUN.Abp.AI; +public class OpenAIKernelProvider : KernelProvider +{ + private const string DefaultEndpoint = "https://api.openai.com/v1"; + + public const string ProviderName = "OpenAI"; + public override string Name => ProviderName; + public OpenAIKernelProvider(IServiceProvider serviceProvider) + : base(serviceProvider) + { + } + + public override Task CreateAsync(WorkspaceDefinition workspace) + { + Check.NotNull(workspace, nameof(workspace)); + Check.NotNullOrWhiteSpace(workspace.ApiKey, nameof(WorkspaceDefinition.ApiKey)); + + var openAIClient = new OpenAIClient( + new ApiKeyCredential(workspace.ApiKey), + new OpenAIClientOptions + { + Endpoint = new Uri(workspace.ApiBaseUrl ?? DefaultEndpoint), + }); + + var kernel = Kernel.CreateBuilder() + .AddOpenAIChatClient(workspace.ModelName, openAIClient) + .Build(); + + return Task.FromResult(kernel); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs new file mode 100644 index 000000000..32b4d0718 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs @@ -0,0 +1,9 @@ +using LINGYUN.Abp.AI.Models; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.AI.Tokens; +public interface ITokenUsageStore +{ + Task SaveTokenUsagesAsync(IEnumerable usageInfos); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs new file mode 100644 index 000000000..621f39c06 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs @@ -0,0 +1,50 @@ +using LINGYUN.Abp.AI.Models; +using Microsoft.Extensions.DependencyInjection; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.AI.Tokens; + +[Dependency(ServiceLifetime.Singleton, TryRegister = true)] +public class InMemoryTokenUsageStore : ITokenUsageStore +{ + private static readonly ConcurrentDictionary _tokenUsageCache = new ConcurrentDictionary(); + + public Task SaveTokenUsagesAsync(IEnumerable usageInfos) + { + foreach (var usageInfo in usageInfos.GroupBy(x => x.Workspace)) + { + var tokenUsageInfo = new TokenUsageInfo(usageInfo.Key) + { + InputTokenCount = usageInfo.Sum(x => x.InputTokenCount), + TotalTokenCount = usageInfo.Sum(x => x.TotalTokenCount), + OutputTokenCount = usageInfo.Sum(x => x.OutputTokenCount), + ReasoningTokenCount = usageInfo.Sum(x => x.ReasoningTokenCount), + CachedInputTokenCount = usageInfo.Sum(x => x.CachedInputTokenCount), + }; + tokenUsageInfo.AdditionalCounts ??= new Dictionary(); + foreach (var item in usageInfo) + { + if (item.AdditionalCounts == null) + { + continue; + } + tokenUsageInfo.AdditionalCounts.AddIfNotContains(item.AdditionalCounts); + } + + if (!_tokenUsageCache.ContainsKey(usageInfo.Key)) + { + _tokenUsageCache.TryAdd(usageInfo.Key, tokenUsageInfo); + } + else + { + _tokenUsageCache[usageInfo.Key] = tokenUsageInfo; + } + } + + return Task.CompletedTask; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tools/GlobalFunctions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tools/GlobalFunctions.cs new file mode 100644 index 000000000..cc52e1fd7 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tools/GlobalFunctions.cs @@ -0,0 +1,11 @@ +using Microsoft.Extensions.AI; +using System; + +namespace LINGYUN.Abp.AI.Tools; +public static class GlobalFunctions +{ + public static AITool Now => AIFunctionFactory.Create( + () => DateTime.Now, + nameof(Now), + "Get now time"); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/WorkspaceChatClient.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/WorkspaceChatClient.cs deleted file mode 100644 index cc0563249..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/WorkspaceChatClient.cs +++ /dev/null @@ -1,69 +0,0 @@ -using LINGYUN.Abp.AI.Tools; -using LINGYUN.Abp.AI.Workspaces; -using Microsoft.Extensions.AI; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace LINGYUN.Abp.AI; -public class WorkspaceChatClient : IWorkspaceChatClient -{ - public IChatClient Client { get; } - public WorkspaceDefinition? Workspace { get; } - public WorkspaceChatClient( - IChatClient chatClient, - WorkspaceDefinition? workspace = null) - { - Workspace = workspace; - Client = chatClient; - } - - public virtual Task GetResponseAsync(IEnumerable messages, ChatOptions? options = null, CancellationToken cancellationToken = default) - { - return Client.GetResponseAsync(GetChatMessages(messages), GetChatOptions(options), cancellationToken); - } - - public virtual IAsyncEnumerable GetStreamingResponseAsync(IEnumerable messages, ChatOptions? options = null, CancellationToken cancellationToken = default) - { - return Client.GetStreamingResponseAsync(GetChatMessages(messages), GetChatOptions(options), cancellationToken); - } - - public virtual object? GetService(Type serviceType, object? serviceKey = null) - { - return Client.GetService(serviceType, serviceKey); - } - - public virtual void Dispose() - { - Client.Dispose(); - } - - protected virtual ChatOptions GetChatOptions(ChatOptions? options) - { - options ??= new ChatOptions(); - options.Instructions = Workspace?.Instructions; - options.Temperature ??= Workspace?.Temperature; - options.MaxOutputTokens ??= Workspace?.MaxOutputTokens; - options.FrequencyPenalty ??= Workspace?.FrequencyPenalty; - options.PresencePenalty ??= Workspace?.PresencePenalty; - - return options; - } - - protected virtual IEnumerable GetChatMessages(IEnumerable messages) - { - if (Workspace?.SystemPrompt?.IsNullOrWhiteSpace() == false && - !messages.Any(msg => msg.Role == ChatRole.System)) - { - // 加入系统提示词 - messages = new List - { - new ChatMessage(ChatRole.System, Workspace.SystemPrompt) - }.Union(messages); - } - - return messages; - } -} From f6726548cdc6766c070aa1f546a3e9198fef767a Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 16 Jan 2026 08:39:33 +0800 Subject: [PATCH 05/88] feat: The ConversationId is not mandatory. --- .../LINGYUN/Abp/AI/Agent/AgentFactory.cs | 2 +- .../LINGYUN/Abp/AI/Agent/AgentService.cs | 12 +++-- .../Abp/AI/Messages/IUserMessageStore.cs | 2 +- .../AI/Messages/InMemoryUserMessageStore.cs | 18 ++++--- .../LINGYUN/Abp/AI/Models/UserMessage.cs | 47 ++++++++++++++++--- .../Abp/AI/OpenAIChatClientProvider.cs | 1 - 6 files changed, 63 insertions(+), 19 deletions(-) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs index 3739fdfd3..95b93584f 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs @@ -80,7 +80,7 @@ public class AgentFactory : IAgentFactory, IScopedDependency Tools = tools, }, Name = workspace?.Name, - Description = description + Description = description, }; var aiAgent = chatClient.CreateAIAgent(clientAgentOptions) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs index ed34c4200..899689c88 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs @@ -2,6 +2,7 @@ using LINGYUN.Abp.AI.Models; using LINGYUN.Abp.AI.Tokens; using Microsoft.Extensions.AI; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -49,11 +50,14 @@ public class AgentService : IAgentService, IScopedDependency { var messages = new List(); - var historyMessages = await _userMessageStore.GetHistoryMessagesAsync(message.ChatId); - - foreach (var chatMessage in historyMessages) + if (!message.ConversationId.IsNullOrWhiteSpace()) { - messages.Add(new ChatMessage(ChatRole.System, chatMessage.Content)); + var historyMessages = await _userMessageStore.GetHistoryMessagesAsync(message.ConversationId); + + foreach (var chatMessage in historyMessages) + { + messages.Add(new ChatMessage(ChatRole.System, chatMessage.Content)); + } } messages.Add(new ChatMessage(ChatRole.User, message.Content)); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs index f9d8baf54..b56406d5f 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs @@ -7,5 +7,5 @@ public interface IUserMessageStore { Task SaveMessageAsync(UserMessage message); - Task> GetHistoryMessagesAsync(string chatId); + Task> GetHistoryMessagesAsync(string conversationId); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs index 2492a51ae..a12cf1de4 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs @@ -14,9 +14,9 @@ public class InMemoryUserMessageStore : IUserMessageStore { private static readonly ConcurrentDictionary> _userMessageCache = new ConcurrentDictionary>(); - public Task> GetHistoryMessagesAsync(string chatId) + public Task> GetHistoryMessagesAsync(string conversationId) { - if (_userMessageCache.TryGetValue(chatId, out var messages)) + if (_userMessageCache.TryGetValue(conversationId, out var messages)) { return Task.FromResult(messages.Take(5)); } @@ -26,15 +26,21 @@ public class InMemoryUserMessageStore : IUserMessageStore public Task SaveMessageAsync(UserMessage message) { - if (_userMessageCache.ContainsKey(message.ChatId)) + var messageId = message.Id; + if (messageId.IsNullOrWhiteSpace()) { - _userMessageCache[message.ChatId].Add(message); + messageId = Guid.NewGuid().ToString(); + message.WithMessageId(messageId); + } + if (_userMessageCache.ContainsKey(messageId)) + { + _userMessageCache[messageId].Add(message); } else { - _userMessageCache[message.ChatId] = new List() { message }; + _userMessageCache[messageId] = new List() { message }; } - return Task.FromResult(message.Id); + return Task.FromResult(messageId); } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs index f902b0c67..1c6a37557 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs @@ -3,24 +3,59 @@ namespace LINGYUN.Abp.AI.Models; public class UserMessage { - public string Id { get; } - public string ChatId { get; } + /// + /// 消息内容 + /// public string Content { get; } + /// + /// 工作区 + /// public string Workspace { get; } + /// + /// 消息Id + /// + /// + /// 在持久化设施处更新 + /// + public string? Id { get; private set; } + /// + /// 对话Id + /// + /// + /// 用于从客户端存储中持久化和检索聊天历史的唯一标识符,如果未指定则与AI对话时无上下文关联 + /// + public string? ConversationId { get; private set; } + /// + /// AI回复消息 + /// public string ReplyMessage { get; private set; } + /// + /// 媒体附件 + /// + /// + /// 暂未实现 + /// public MediaMessage[]? Medias { get; private set; } public UserMessage( string workspace, - string id, - string chatId, string content) { Workspace = workspace; - Id = id; - ChatId = chatId; Content = content; } + public UserMessage WithMessageId(string id) + { + Id = id; + return this; + } + + public UserMessage WithConversationId(string conversationId) + { + ConversationId = conversationId; + return this; + } + public UserMessage WithMedia(MediaMessage media) { Medias ??= []; diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs index d13ac08a1..3501993b1 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs @@ -38,7 +38,6 @@ public class OpenAIChatClientProvider : ChatClientProvider .UseOpenTelemetry() .UseFunctionInvocation() .UseDistributedCache() - .UseChatReducer() .Build(ServiceProvider); return Task.FromResult(chatClient); From 389ad566fbefeabfec01ef860498f07726cd7c2e Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 16 Jan 2026 09:12:31 +0800 Subject: [PATCH 06/88] feat: Reconstruct the general user message --- .../LINGYUN/Abp/AI/Agent/AgentService.cs | 5 +-- .../LINGYUN/Abp/AI/AbpAICoreModule.cs | 6 ++- .../AI/{ => Internal}/ChatClientFactory.cs | 2 +- .../ChatClientProviderManager.cs | 2 +- .../AI/Internal/DeepSeekChatClientProvider.cs | 15 ++++++++ .../Abp/AI/Internal/DeepSeekKernelProvider.cs | 13 +++++++ .../Abp/AI/{ => Internal}/KernelFactory.cs | 2 +- .../{ => Internal}/KernelProviderManager.cs | 2 +- .../OpenAIChatClientProvider.cs | 6 +-- .../AI/{ => Internal}/OpenAIKernelProvider.cs | 4 +- .../LINGYUN/Abp/AI/Models/TokenUsageInfo.cs | 5 +-- .../LINGYUN/Abp/AI/Models/UserMessage.cs | 37 +++++-------------- .../LINGYUN/Abp/AI/Models/UserTextMessage.cs | 20 ++++++++++ .../Abp/AI/Tokens/InMemoryTokenUsageStore.cs | 9 ----- 14 files changed, 74 insertions(+), 54 deletions(-) rename aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/{ => Internal}/ChatClientFactory.cs (98%) rename aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/{ => Internal}/ChatClientProviderManager.cs (97%) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/DeepSeekChatClientProvider.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/DeepSeekKernelProvider.cs rename aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/{ => Internal}/KernelFactory.cs (98%) rename aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/{ => Internal}/KernelProviderManager.cs (97%) rename aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/{ => Internal}/OpenAIChatClientProvider.cs (91%) rename aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/{ => Internal}/OpenAIKernelProvider.cs (89%) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserTextMessage.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs index 899689c88..17ea6b7a9 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs @@ -56,11 +56,11 @@ public class AgentService : IAgentService, IScopedDependency foreach (var chatMessage in historyMessages) { - messages.Add(new ChatMessage(ChatRole.System, chatMessage.Content)); + messages.Add(new ChatMessage(ChatRole.System, chatMessage.GetMessagePrompt())); } } - messages.Add(new ChatMessage(ChatRole.User, message.Content)); + messages.Add(new ChatMessage(ChatRole.User, message.GetMessagePrompt())); return messages; } @@ -84,7 +84,6 @@ public class AgentService : IAgentService, IScopedDependency TotalTokenCount = usage.Details.TotalTokenCount, CachedInputTokenCount = usage.Details.CachedInputTokenCount, InputTokenCount = usage.Details.InputTokenCount, - AdditionalCounts = usage.Details.AdditionalCounts, OutputTokenCount = usage.Details.OutputTokenCount, ReasoningTokenCount = usage.Details.ReasoningTokenCount, }); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs index 3f81bd409..2ab1185a5 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs @@ -1,4 +1,5 @@ -using LINGYUN.Abp.AI.Localization; +using LINGYUN.Abp.AI.Internal; +using LINGYUN.Abp.AI.Localization; using LINGYUN.Abp.AI.Workspaces; using Microsoft.Extensions.DependencyInjection; using System; @@ -38,7 +39,10 @@ public class AbpAICoreModule : AbpModule Configure(options => { options.ChatClientProviders.Add(); + options.ChatClientProviders.Add(); + options.KernelProviders.Add(); + options.KernelProviders.Add(); }); Configure(options => diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/ChatClientFactory.cs similarity index 98% rename from aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/ChatClientFactory.cs index 3f5280dee..a369c05e0 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/ChatClientFactory.cs @@ -8,7 +8,7 @@ using Volo.Abp.Authorization; using Volo.Abp.DependencyInjection; using Volo.Abp.SimpleStateChecking; -namespace LINGYUN.Abp.AI; +namespace LINGYUN.Abp.AI.Internal; public class ChatClientFactory : IChatClientFactory, IScopedDependency { protected ISimpleStateCheckerManager StateCheckerManager { get; } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProviderManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/ChatClientProviderManager.cs similarity index 97% rename from aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProviderManager.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/ChatClientProviderManager.cs index 8d3cc80ec..09495b231 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProviderManager.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/ChatClientProviderManager.cs @@ -7,7 +7,7 @@ using System.Linq; using Volo.Abp; using Volo.Abp.DependencyInjection; -namespace LINGYUN.Abp.AI; +namespace LINGYUN.Abp.AI.Internal; public class ChatClientProviderManager : IChatClientProviderManager, ISingletonDependency { public List Providers => _lazyProviders.Value; diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/DeepSeekChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/DeepSeekChatClientProvider.cs new file mode 100644 index 000000000..bc0863f33 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/DeepSeekChatClientProvider.cs @@ -0,0 +1,15 @@ +using System; + +namespace LINGYUN.Abp.AI.Internal; +public class DeepSeekChatClientProvider : OpenAIChatClientProvider +{ + protected override string DefaultEndpoint => "https://api.deepseek.com/v1"; + + public new const string ProviderName = "DeepSeek"; + public override string Name => ProviderName; + public DeepSeekChatClientProvider( + IServiceProvider serviceProvider) + : base(serviceProvider) + { + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/DeepSeekKernelProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/DeepSeekKernelProvider.cs new file mode 100644 index 000000000..c27cb0e41 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/DeepSeekKernelProvider.cs @@ -0,0 +1,13 @@ +using System; + +namespace LINGYUN.Abp.AI.Internal; +public class DeepSeekKernelProvider : OpenAIKernelProvider +{ + protected override string DefaultEndpoint => "https://api.deepseek.com/v1"; + + public new const string ProviderName = "DeepSeek"; + public override string Name => ProviderName; + public DeepSeekKernelProvider(IServiceProvider serviceProvider) : base(serviceProvider) + { + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/KernelFactory.cs similarity index 98% rename from aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelFactory.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/KernelFactory.cs index 5778e7921..23c6aeda7 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/KernelFactory.cs @@ -8,7 +8,7 @@ using Volo.Abp.Authorization; using Volo.Abp.DependencyInjection; using Volo.Abp.SimpleStateChecking; -namespace LINGYUN.Abp.AI; +namespace LINGYUN.Abp.AI.Internal; public class KernelFactory : IKernelFactory, IScopedDependency { protected ISimpleStateCheckerManager StateCheckerManager { get; } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelProviderManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/KernelProviderManager.cs similarity index 97% rename from aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelProviderManager.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/KernelProviderManager.cs index f2beb0025..ab4bd6221 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/KernelProviderManager.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/KernelProviderManager.cs @@ -6,7 +6,7 @@ using System.Linq; using Volo.Abp; using Volo.Abp.DependencyInjection; -namespace LINGYUN.Abp.AI; +namespace LINGYUN.Abp.AI.Internal; public class KernelProviderManager : IKernelProviderManager, ISingletonDependency { public List Providers => _lazyProviders.Value; diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIChatClientProvider.cs similarity index 91% rename from aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIChatClientProvider.cs index 3501993b1..6f2ab1234 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIChatClientProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIChatClientProvider.cs @@ -6,12 +6,12 @@ using System.ClientModel; using System.Threading.Tasks; using Volo.Abp; -namespace LINGYUN.Abp.AI; +namespace LINGYUN.Abp.AI.Internal; public class OpenAIChatClientProvider : ChatClientProvider { - private const string DefaultEndpoint = "https://api.openai.com/v1"; - public const string ProviderName = "OpenAI"; + protected virtual string DefaultEndpoint => "https://api.openai.com/v1"; + public const string ProviderName = "OpenAI"; public override string Name => ProviderName; public OpenAIChatClientProvider(IServiceProvider serviceProvider) : base(serviceProvider) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIKernelProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIKernelProvider.cs similarity index 89% rename from aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIKernelProvider.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIKernelProvider.cs index 2f1bb8f61..2eb16859e 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/OpenAIKernelProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIKernelProvider.cs @@ -6,10 +6,10 @@ using System.ClientModel; using System.Threading.Tasks; using Volo.Abp; -namespace LINGYUN.Abp.AI; +namespace LINGYUN.Abp.AI.Internal; public class OpenAIKernelProvider : KernelProvider { - private const string DefaultEndpoint = "https://api.openai.com/v1"; + protected virtual string DefaultEndpoint { get; set; } = "https://api.openai.com/v1"; public const string ProviderName = "OpenAI"; public override string Name => ProviderName; diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs index cdac08043..61c8e0457 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace LINGYUN.Abp.AI.Models; +namespace LINGYUN.Abp.AI.Models; public class TokenUsageInfo { public string Workspace { get; } @@ -9,7 +7,6 @@ public class TokenUsageInfo public long? TotalTokenCount { get; set; } public long? CachedInputTokenCount { get; set; } public long? ReasoningTokenCount { get; set; } - public IDictionary? AdditionalCounts { get; set; } public TokenUsageInfo(string workspace) { Workspace = workspace; diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs index 1c6a37557..7681ae260 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs @@ -1,12 +1,6 @@ -using System.Linq; - -namespace LINGYUN.Abp.AI.Models; -public class UserMessage +namespace LINGYUN.Abp.AI.Models; +public abstract class UserMessage { - /// - /// 消息内容 - /// - public string Content { get; } /// /// 工作区 /// @@ -29,44 +23,31 @@ public class UserMessage /// AI回复消息 /// public string ReplyMessage { get; private set; } - /// - /// 媒体附件 - /// - /// - /// 暂未实现 - /// - public MediaMessage[]? Medias { get; private set; } - public UserMessage( - string workspace, - string content) + protected UserMessage(string workspace) { Workspace = workspace; - Content = content; } - public UserMessage WithMessageId(string id) + public virtual UserMessage WithMessageId(string id) { Id = id; return this; } - public UserMessage WithConversationId(string conversationId) + public virtual UserMessage WithConversationId(string conversationId) { ConversationId = conversationId; return this; } - public UserMessage WithMedia(MediaMessage media) + public virtual UserMessage WithReply(string replyMessage) { - Medias ??= []; - Medias = Medias.Union([media]).ToArray(); - + ReplyMessage = replyMessage; return this; } - public UserMessage WithReply(string replyMessage) + public virtual string GetMessagePrompt() { - ReplyMessage = replyMessage; - return this; + return string.Empty; } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserTextMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserTextMessage.cs new file mode 100644 index 000000000..55cb12ae6 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserTextMessage.cs @@ -0,0 +1,20 @@ +namespace LINGYUN.Abp.AI.Models; +public class UserTextMessage : UserMessage +{ + /// + /// 消息内容 + /// + public string Content { get; } + public UserTextMessage( + string workspace, + string content) + : base(workspace) + { + Content = content; + } + + public override string GetMessagePrompt() + { + return Content; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs index 621f39c06..9c7862de8 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs @@ -25,15 +25,6 @@ public class InMemoryTokenUsageStore : ITokenUsageStore ReasoningTokenCount = usageInfo.Sum(x => x.ReasoningTokenCount), CachedInputTokenCount = usageInfo.Sum(x => x.CachedInputTokenCount), }; - tokenUsageInfo.AdditionalCounts ??= new Dictionary(); - foreach (var item in usageInfo) - { - if (item.AdditionalCounts == null) - { - continue; - } - tokenUsageInfo.AdditionalCounts.AddIfNotContains(item.AdditionalCounts); - } if (!_tokenUsageCache.ContainsKey(usageInfo.Key)) { From 81e7bdfe8094ed73bd12473a07d1c96d9bfd701f Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 16 Jan 2026 10:51:06 +0800 Subject: [PATCH 07/88] feat: Add AIManagement Projects --- .../FodyWeavers.xml | 3 ++ ....AIManagement.Application.Contracts.csproj | 25 ++++++++++++++++ .../AbpAIManagementDomainSharedModule.cs | 8 +++++ .../FodyWeavers.xml | 3 ++ ...INGYUN.Abp.AIManagement.Application.csproj | 26 +++++++++++++++++ .../AbpAIManagementDomainSharedModule.cs | 8 +++++ .../FodyWeavers.xml | 3 ++ ...GYUN.Abp.AIManagement.Domain.Shared.csproj | 21 ++++++++++++++ .../AbpAIManagementDomainSharedModule.cs | 10 +++++++ .../Workspaces/WorkspaceConsts.cs | 14 +++++++++ .../FodyWeavers.xml | 3 ++ .../LINGYUN.Abp.AIManagement.Domain.csproj | 29 +++++++++++++++++++ .../Abp/AIManagement/AIManagementOptions.cs | 10 +++++++ .../AbpAIManagementDomainModule.cs | 17 +++++++++++ .../FodyWeavers.xml | 3 ++ ...bp.AIManagement.EntityFrameworkCore.csproj | 25 ++++++++++++++++ .../AbpAIManagementDomainSharedModule.cs | 8 +++++ .../FodyWeavers.xml | 3 ++ ...YUN.Abp.AIManagement.HttpApi.Client.csproj | 25 ++++++++++++++++ .../AbpAIManagementDomainSharedModule.cs | 8 +++++ .../FodyWeavers.xml | 3 ++ .../LINGYUN.Abp.AIManagement.HttpApi.csproj | 25 ++++++++++++++++ .../AbpAIManagementDomainSharedModule.cs | 8 +++++ 23 files changed, 288 insertions(+) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/FodyWeavers.xml create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN.Abp.AIManagement.Application.Contracts.csproj create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/FodyWeavers.xml create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN.Abp.AIManagement.Application.csproj create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/FodyWeavers.xml create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN.Abp.AIManagement.Domain.Shared.csproj create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceConsts.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/FodyWeavers.xml create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AIManagementOptions.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainModule.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/FodyWeavers.xml create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN.Abp.AIManagement.EntityFrameworkCore.csproj create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/FodyWeavers.xml create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/LINGYUN.Abp.AIManagement.HttpApi.Client.csproj create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/FodyWeavers.xml create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN.Abp.AIManagement.HttpApi.csproj create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/FodyWeavers.xml b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN.Abp.AIManagement.Application.Contracts.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN.Abp.AIManagement.Application.Contracts.csproj new file mode 100644 index 000000000..c756d035b --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN.Abp.AIManagement.Application.Contracts.csproj @@ -0,0 +1,25 @@ + + + + + + + netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0 + LINGYUN.Abp.AIManagement.Domain + LINGYUN.Abp.AIManagement.Domain + false + false + false + enable + + + + + + + + + + + + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs new file mode 100644 index 000000000..751b9bfaa --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.AIManagement; + +public class AbpAIManagementDomainSharedModule : AbpModule +{ + +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/FodyWeavers.xml b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN.Abp.AIManagement.Application.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN.Abp.AIManagement.Application.csproj new file mode 100644 index 000000000..5706b07d9 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN.Abp.AIManagement.Application.csproj @@ -0,0 +1,26 @@ + + + + + + + net10.0 + LINGYUN.Abp.AIManagement.Application + LINGYUN.Abp.AIManagement.Application + false + false + false + enable + + + + + + + + + + + + + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs new file mode 100644 index 000000000..751b9bfaa --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.AIManagement; + +public class AbpAIManagementDomainSharedModule : AbpModule +{ + +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/FodyWeavers.xml b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN.Abp.AIManagement.Domain.Shared.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN.Abp.AIManagement.Domain.Shared.csproj new file mode 100644 index 000000000..148c75c26 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN.Abp.AIManagement.Domain.Shared.csproj @@ -0,0 +1,21 @@ + + + + + + + netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0 + LINGYUN.Abp.AIManagement.Domain.Shared + LINGYUN.Abp.AIManagement.Domain.Shared + false + false + false + enable + + + + + + + + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs new file mode 100644 index 000000000..5515864d6 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Domain; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.AIManagement; + +[DependsOn(typeof(AbpDddDomainSharedModule))] +public class AbpAIManagementDomainSharedModule : AbpModule +{ + +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceConsts.cs new file mode 100644 index 000000000..c1dc19dc7 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceConsts.cs @@ -0,0 +1,14 @@ +namespace LINGYUN.Abp.AIManagement.Workspaces; +public static class WorkspaceConsts +{ + public static int MaxNameLength { get; set; } = 64; + public static int MaxProviderLength { get; set; } = 20; + public static int MaxModelNameLength { get; set; } = 64; + public static int MaxDisplayNameLength { get; set; } = 128; + public static int MaxDescriptionLength { get; set; } = 128; + public static int MaxApiKeyLength { get; set; } = 64; + public static int MaxApiBaseUrlLength { get; set; } = 128; + public static int MaxSystemPromptLength { get; set; } = 512; + public static int MaxInstructionsLength { get; set; } = 512; + public static int MaxStateCheckersLength { get; set; } = 256; +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/FodyWeavers.xml b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj new file mode 100644 index 000000000..3a58fe260 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj @@ -0,0 +1,29 @@ + + + + + + + net10.0 + LINGYUN.Abp.AIManagement.Domain + LINGYUN.Abp.AIManagement.Domain + false + false + false + enable + + + + + + + + + + + + + + + + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AIManagementOptions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AIManagementOptions.cs new file mode 100644 index 000000000..493fcb14a --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AIManagementOptions.cs @@ -0,0 +1,10 @@ +namespace LINGYUN.Abp.AIManagement; +public class AIManagementOptions +{ + public bool IsDynamicWorkspaceStoreEnabled { get; set; } + public bool SaveStaticWorkspacesToDatabase { get; set; } + public AIManagementOptions() + { + + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainModule.cs new file mode 100644 index 000000000..8bed08b41 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainModule.cs @@ -0,0 +1,17 @@ +using LINGYUN.Abp.AI; +using Volo.Abp.Caching; +using Volo.Abp.Domain; +using Volo.Abp.Mapperly; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.AIManagement; + +[DependsOn( + typeof(AbpAIManagementDomainSharedModule), + typeof(AbpAICoreModule), + typeof(AbpCachingModule), + typeof(AbpMapperlyModule), + typeof(AbpDddDomainModule))] +public class AbpAIManagementDomainModule : AbpModule +{ +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/FodyWeavers.xml b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN.Abp.AIManagement.EntityFrameworkCore.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN.Abp.AIManagement.EntityFrameworkCore.csproj new file mode 100644 index 000000000..32e7bafeb --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN.Abp.AIManagement.EntityFrameworkCore.csproj @@ -0,0 +1,25 @@ + + + + + + + net10.0 + LINGYUN.Abp.AIManagement.EntityFrameworkCore + LINGYUN.Abp.AIManagement.EntityFrameworkCore + false + false + false + enable + + + + + + + + + + + + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs new file mode 100644 index 000000000..751b9bfaa --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.AIManagement; + +public class AbpAIManagementDomainSharedModule : AbpModule +{ + +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/FodyWeavers.xml b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/LINGYUN.Abp.AIManagement.HttpApi.Client.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/LINGYUN.Abp.AIManagement.HttpApi.Client.csproj new file mode 100644 index 000000000..dc145f2f1 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/LINGYUN.Abp.AIManagement.HttpApi.Client.csproj @@ -0,0 +1,25 @@ + + + + + + + netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0 + LINGYUN.Abp.AIManagement.HttpApi.Client + LINGYUN.Abp.AIManagement.HttpApi.Client + false + false + false + enable + + + + + + + + + + + + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs new file mode 100644 index 000000000..751b9bfaa --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.AIManagement; + +public class AbpAIManagementDomainSharedModule : AbpModule +{ + +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/FodyWeavers.xml b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN.Abp.AIManagement.HttpApi.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN.Abp.AIManagement.HttpApi.csproj new file mode 100644 index 000000000..00f6afa11 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN.Abp.AIManagement.HttpApi.csproj @@ -0,0 +1,25 @@ + + + + + + + net10.0 + LINGYUN.Abp.AIManagement.HttpApi + LINGYUN.Abp.AIManagement.HttpApi + false + false + false + enable + + + + + + + + + + + + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs new file mode 100644 index 000000000..751b9bfaa --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.AIManagement; + +public class AbpAIManagementDomainSharedModule : AbpModule +{ + +} From 6bf0c7b9600dc35806a0db12aa9576582a2c1b0c Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 16 Jan 2026 10:56:46 +0800 Subject: [PATCH 08/88] feat: Add AIManagementDbContext --- .../AbpAIManagementDbProperties.cs | 11 ++++++++++ .../AbpAIManagementDomainSharedModule.cs | 8 ------- .../AIManagementDbContext.cs | 21 +++++++++++++++++++ ...nagementDbContextModelBuilderExtensions.cs | 21 +++++++++++++++++++ ...bpAIManagementEntityFrameworkCoreModule.cs | 19 +++++++++++++++++ .../IAIManagementDbContext.cs | 10 +++++++++ 6 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDbProperties.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDbProperties.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDbProperties.cs new file mode 100644 index 000000000..852fbd74a --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDbProperties.cs @@ -0,0 +1,11 @@ +using Volo.Abp.Data; + +namespace LINGYUN.Abp.AIManagement; +public static class AbpAIManagementDbProperties +{ + public static string DbTablePrefix { get; set; } = AbpCommonDbProperties.DbTablePrefix; + + public static string? DbSchema { get; set; } = AbpCommonDbProperties.DbSchema; + + public const string ConnectionStringName = "AbpAIManagement"; +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs deleted file mode 100644 index 751b9bfaa..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Volo.Abp.Modularity; - -namespace LINGYUN.Abp.AIManagement; - -public class AbpAIManagementDomainSharedModule : AbpModule -{ - -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs new file mode 100644 index 000000000..d1bfd18d6 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; + +[ConnectionStringName(AbpAIManagementDbProperties.ConnectionStringName)] +public class AIManagementDbContext : AbpDbContext, IAIManagementDbContext +{ + public AIManagementDbContext( + DbContextOptions options) : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.ConfigureAIManagement(); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs new file mode 100644 index 000000000..2bf2e574a --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs @@ -0,0 +1,21 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Volo.Abp; +using Volo.Abp.EntityFrameworkCore.Modeling; + +namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; +public static class AIManagementDbContextModelBuilderExtensions +{ + public static void ConfigureAIManagement( + [NotNull] this ModelBuilder builder) + { + Check.NotNull(builder, nameof(builder)); + + if (builder.IsTenantOnlyDatabase()) + { + return; + } + + builder.TryConfigureObjectExtensions(); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs new file mode 100644 index 000000000..adbf7146d --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; + +[DependsOn( + typeof(AbpAIManagementDomainModule), + typeof(AbpEntityFrameworkCoreModule))] +public class AbpAIManagementEntityFrameworkCoreModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAbpDbContext(options => + { + options.AddDefaultRepositories(); + }); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs new file mode 100644 index 000000000..16b860ed9 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; + +[ConnectionStringName(AbpAIManagementDbProperties.ConnectionStringName)] +public interface IAIManagementDbContext : IEfCoreDbContext +{ + +} From c1bf459685b184b750f3b083646f8525cc82e915 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 16 Jan 2026 10:57:32 +0800 Subject: [PATCH 09/88] feat: Implement dynamic workspace --- .../AbpAIManagementDomainModule.cs | 42 +++ .../DynamicWorkspaceDefinitionStore.cs | 161 +++++++++++ ...icWorkspaceDefinitionStoreInMemoryCache.cs | 95 +++++++ ...icWorkspaceDefinitionStoreInMemoryCache.cs | 22 ++ .../Workspaces/IStaticWorkspaceSaver.cs | 7 + .../IWorkspaceDefinitionSerializer.cs | 11 + .../Workspaces/IWorkspaceRepository.cs | 12 + .../Workspaces/StaticWorkspaceSaver.cs | 242 +++++++++++++++++ .../Abp/AIManagement/Workspaces/Workspace.cs | 255 ++++++++++++++++++ .../WorkspaceDefinitionSerializer.cs | 72 +++++ .../Workspaces/WorkspaceDynamicInitializer.cs | 149 ++++++++++ 11 files changed, 1068 insertions(+) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStore.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreInMemoryCache.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IDynamicWorkspaceDefinitionStoreInMemoryCache.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IStaticWorkspaceSaver.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionSerializer.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceRepository.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/StaticWorkspaceSaver.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/Workspace.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDynamicInitializer.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainModule.cs index 8bed08b41..8d139de9b 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainModule.cs @@ -1,8 +1,16 @@ using LINGYUN.Abp.AI; +using LINGYUN.Abp.AIManagement.Workspaces; +using Microsoft.Extensions.DependencyInjection; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp; using Volo.Abp.Caching; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; using Volo.Abp.Domain; using Volo.Abp.Mapperly; using Volo.Abp.Modularity; +using Volo.Abp.Threading; namespace LINGYUN.Abp.AIManagement; @@ -14,4 +22,38 @@ namespace LINGYUN.Abp.AIManagement; typeof(AbpDddDomainModule))] public class AbpAIManagementDomainModule : AbpModule { + + private readonly CancellationTokenSource _cancellationTokenSource = new(); + + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddMapperlyObjectMapper(); + + if (context.Services.IsDataMigrationEnvironment()) + { + Configure(options => + { + options.SaveStaticWorkspacesToDatabase = false; + options.IsDynamicWorkspaceStoreEnabled = false; + }); + } + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context)); + } + + public async override Task OnApplicationInitializationAsync(ApplicationInitializationContext context) + { + var rootServiceProvider = context.ServiceProvider.GetRequiredService(); + var initializer = rootServiceProvider.GetRequiredService(); + await initializer.InitializeAsync(true, _cancellationTokenSource.Token); + } + + public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context) + { + _cancellationTokenSource.Cancel(); + return Task.CompletedTask; + } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStore.cs new file mode 100644 index 000000000..6c7225b8f --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStore.cs @@ -0,0 +1,161 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.AI.Workspaces; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.DistributedLocking; +using Volo.Abp.Threading; + +namespace LINGYUN.Abp.AIManagement.Workspaces; + +[Dependency(ReplaceServices = true)] +public class DynamicWorkspaceDefinitionStore : IDynamicWorkspaceDefinitionStore, ITransientDependency +{ + protected IWorkspaceRepository WorkspaceRepository { get; } + protected IWorkspaceDefinitionSerializer WorkspaceDefinitionSerializer { get; } + protected IDynamicWorkspaceDefinitionStoreInMemoryCache StoreCache { get; } + protected IDistributedCache DistributedCache { get; } + protected IAbpDistributedLock DistributedLock { get; } + public AIManagementOptions AIManagementOptions { get; } + protected AbpDistributedCacheOptions CacheOptions { get; } + + public DynamicWorkspaceDefinitionStore( + IWorkspaceRepository workspaceRepository, + IWorkspaceDefinitionSerializer workspaceDefinitionSerializer, + IDynamicWorkspaceDefinitionStoreInMemoryCache storeCache, + IDistributedCache distributedCache, + IOptions cacheOptions, + IOptions aiManagementOptions, + IAbpDistributedLock distributedLock) + { + WorkspaceRepository = workspaceRepository; + WorkspaceDefinitionSerializer = workspaceDefinitionSerializer; + StoreCache = storeCache; + DistributedCache = distributedCache; + DistributedLock = distributedLock; + AIManagementOptions = aiManagementOptions.Value; + CacheOptions = cacheOptions.Value; + } + + public async virtual Task> GetAllAsync() + { + if (!AIManagementOptions.IsDynamicWorkspaceStoreEnabled) + { + return Array.Empty(); + } + + using (await StoreCache.SyncSemaphore.LockAsync()) + { + await EnsureCacheIsUptoDateAsync(); + return StoreCache.GetWorkspaces(); + } + } + + public async virtual Task GetAsync([NotNull] string name) + { + Check.NotNull(name, nameof(name)); + + return await GetOrNullAsync(name) ?? throw new AbpException("Undefined workspace: " + name); + } + + public async virtual Task GetOrNullAsync([NotNull] string name) + { + Check.NotNull(name, nameof(name)); + + if (!AIManagementOptions.IsDynamicWorkspaceStoreEnabled) + { + return null; + } + + using (await StoreCache.SyncSemaphore.LockAsync()) + { + await EnsureCacheIsUptoDateAsync(); + return StoreCache.GetWorkspaceOrNull(name); + } + } + + protected virtual async Task EnsureCacheIsUptoDateAsync() + { + if (StoreCache.LastCheckTime.HasValue && + DateTime.Now.Subtract(StoreCache.LastCheckTime.Value).TotalSeconds < 30) + { + return; + } + + var stampInDistributedCache = await GetOrSetStampInDistributedCache(); + + if (stampInDistributedCache == StoreCache.CacheStamp) + { + StoreCache.LastCheckTime = DateTime.Now; + return; + } + + await UpdateInMemoryStoreCache(); + + StoreCache.CacheStamp = stampInDistributedCache; + StoreCache.LastCheckTime = DateTime.Now; + } + + protected virtual async Task UpdateInMemoryStoreCache() + { + var workspaces = await WorkspaceRepository.GetListAsync(); + + await StoreCache.FillAsync(workspaces); + } + + protected virtual async Task GetOrSetStampInDistributedCache() + { + var cacheKey = GetCommonStampCacheKey(); + + var stampInDistributedCache = await DistributedCache.GetStringAsync(cacheKey); + if (stampInDistributedCache != null) + { + return stampInDistributedCache; + } + + await using (var commonLockHandle = await DistributedLock + .TryAcquireAsync(GetCommonDistributedLockKey(), TimeSpan.FromMinutes(2))) + { + if (commonLockHandle == null) + { + throw new AbpException( + "Could not acquire distributed lock for workspace definition common stamp check!" + ); + } + + stampInDistributedCache = await DistributedCache.GetStringAsync(cacheKey); + if (stampInDistributedCache != null) + { + return stampInDistributedCache; + } + + stampInDistributedCache = Guid.NewGuid().ToString(); + + await DistributedCache.SetStringAsync( + cacheKey, + stampInDistributedCache, + new DistributedCacheEntryOptions + { + SlidingExpiration = TimeSpan.FromDays(30) + } + ); + } + + return stampInDistributedCache; + } + + protected virtual string GetCommonStampCacheKey() + { + return $"{CacheOptions.KeyPrefix}_AbpInMemoryWorkspaceCacheStamp"; + } + + protected virtual string GetCommonDistributedLockKey() + { + return $"{CacheOptions.KeyPrefix}_Common_AbpWorkspaceUpdateLock"; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreInMemoryCache.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreInMemoryCache.cs new file mode 100644 index 000000000..43314dfed --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreInMemoryCache.cs @@ -0,0 +1,95 @@ +using LINGYUN.Abp.AI.Workspaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Localization; +using Volo.Abp.SimpleStateChecking; + +namespace LINGYUN.Abp.AIManagement.Workspaces; +public class DynamicWorkspaceDefinitionStoreInMemoryCache : IDynamicWorkspaceDefinitionStoreInMemoryCache, ISingletonDependency +{ + public string CacheStamp { get; set; } + protected IDictionary WorkspaceDefinitions { get; } + protected ISimpleStateCheckerSerializer StateCheckerSerializer { get; } + protected ILocalizableStringSerializer LocalizableStringSerializer { get; } + + public SemaphoreSlim SyncSemaphore { get; } = new(1, 1); + + public DateTime? LastCheckTime { get; set; } + + public DynamicWorkspaceDefinitionStoreInMemoryCache( + ISimpleStateCheckerSerializer stateCheckerSerializer, + ILocalizableStringSerializer localizableStringSerializer) + { + StateCheckerSerializer = stateCheckerSerializer; + LocalizableStringSerializer = localizableStringSerializer; + + WorkspaceDefinitions = new Dictionary(); + } + + public Task FillAsync(List workspaces) + { + WorkspaceDefinitions.Clear(); + + foreach (var workspace in workspaces) + { + var workspaceDef = new WorkspaceDefinition( + workspace.Name, + workspace.Provider, + workspace.ModelName, + LocalizableStringSerializer.Deserialize(workspace.DisplayName), + !workspace.Description.IsNullOrWhiteSpace() ? LocalizableStringSerializer.Deserialize(workspace.Description) : null, + workspace.SystemPrompt, + workspace.Instructions, + workspace.Temperature, + workspace.MaxOutputTokens, + workspace.FrequencyPenalty, + workspace.PresencePenalty); + + if (!workspace.ApiKey.IsNullOrWhiteSpace()) + { + workspaceDef.WithApiKey(workspace.ApiKey); + } + if (!workspace.ApiBaseUrl.IsNullOrWhiteSpace()) + { + workspaceDef.WithApiBaseUrl(workspace.ApiBaseUrl); + } + workspaceDef.IsEnabled = workspace.IsEnabled; + + if (!workspace.StateCheckers.IsNullOrWhiteSpace()) + { + var checkers = StateCheckerSerializer + .DeserializeArray( + workspace.StateCheckers, + workspaceDef + ); + workspaceDef.StateCheckers.AddRange(checkers); + } + + foreach (var property in workspace.ExtraProperties) + { + if (property.Value != null) + { + workspaceDef.WithProperty(property.Key, property.Value); + } + } + + WorkspaceDefinitions[workspace.Name] = workspaceDef; + } + + return Task.CompletedTask; + } + + public WorkspaceDefinition? GetWorkspaceOrNull(string name) + { + return WorkspaceDefinitions.GetOrDefault(name); + } + + public IReadOnlyList GetWorkspaces() + { + return WorkspaceDefinitions.Values.ToList(); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IDynamicWorkspaceDefinitionStoreInMemoryCache.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IDynamicWorkspaceDefinitionStoreInMemoryCache.cs new file mode 100644 index 000000000..c2d2a4b12 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IDynamicWorkspaceDefinitionStoreInMemoryCache.cs @@ -0,0 +1,22 @@ +using LINGYUN.Abp.AI.Workspaces; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.AIManagement.Workspaces; + +public interface IDynamicWorkspaceDefinitionStoreInMemoryCache +{ + string CacheStamp { get; set; } + + SemaphoreSlim SyncSemaphore { get; } + + DateTime? LastCheckTime { get; set; } + + Task FillAsync(List permissions); + + WorkspaceDefinition? GetWorkspaceOrNull(string name); + + IReadOnlyList GetWorkspaces(); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IStaticWorkspaceSaver.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IStaticWorkspaceSaver.cs new file mode 100644 index 000000000..32ba0a028 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IStaticWorkspaceSaver.cs @@ -0,0 +1,7 @@ +using System.Threading.Tasks; + +namespace LINGYUN.Abp.AIManagement.Workspaces; +public interface IStaticWorkspaceSaver +{ + Task SaveAsync(); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionSerializer.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionSerializer.cs new file mode 100644 index 000000000..fd1106fcd --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionSerializer.cs @@ -0,0 +1,11 @@ +using LINGYUN.Abp.AI.Workspaces; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.AIManagement.Workspaces; +public interface IWorkspaceDefinitionSerializer +{ + Task SerializeAsync(IEnumerable definitions); + + Task SerializeAsync(WorkspaceDefinition definition); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceRepository.cs new file mode 100644 index 000000000..5405b0b0a --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceRepository.cs @@ -0,0 +1,12 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace LINGYUN.Abp.AIManagement.Workspaces; +public interface IWorkspaceRepository : IBasicRepository +{ + Task FindByNameAsync( + string name, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/StaticWorkspaceSaver.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/StaticWorkspaceSaver.cs new file mode 100644 index 000000000..df306125c --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/StaticWorkspaceSaver.cs @@ -0,0 +1,242 @@ +using LINGYUN.Abp.AI; +using LINGYUN.Abp.AI.Workspaces; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.DistributedLocking; +using Volo.Abp.Guids; +using Volo.Abp.Json.SystemTextJson.Modifiers; +using Volo.Abp.Threading; +using Volo.Abp.Uow; + +namespace LINGYUN.Abp.AIManagement.Workspaces; +public class StaticWorkspaceSaver : IStaticWorkspaceSaver, ITransientDependency +{ + protected IStaticWorkspaceDefinitionStore StaticStore { get; } + protected IWorkspaceRepository WorkspaceRepository { get; } + protected IWorkspaceDefinitionSerializer WorkspaceSerializer { get; } + protected IDistributedCache Cache { get; } + protected IApplicationInfoAccessor ApplicationInfoAccessor { get; } + protected IAbpDistributedLock DistributedLock { get; } + protected AbpAICoreOptions AIOptions { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + protected AbpDistributedCacheOptions CacheOptions { get; } + protected IUnitOfWorkManager UnitOfWorkManager { get; } + protected IGuidGenerator GuidGenerator { get; } + + public StaticWorkspaceSaver( + IStaticWorkspaceDefinitionStore staticStore, + IWorkspaceRepository workspaceRepository, + IWorkspaceDefinitionSerializer workspaceSerializer, + IDistributedCache cache, + IOptions cacheOptions, + IApplicationInfoAccessor applicationInfoAccessor, + IAbpDistributedLock distributedLock, + IOptions settingOptions, + ICancellationTokenProvider cancellationTokenProvider, + IUnitOfWorkManager unitOfWorkManager, + IGuidGenerator guidGenerator) + { + StaticStore = staticStore; + WorkspaceRepository = workspaceRepository; + WorkspaceSerializer = workspaceSerializer; + Cache = cache; + ApplicationInfoAccessor = applicationInfoAccessor; + DistributedLock = distributedLock; + CancellationTokenProvider = cancellationTokenProvider; + AIOptions = settingOptions.Value; + CacheOptions = cacheOptions.Value; + UnitOfWorkManager = unitOfWorkManager; + GuidGenerator = guidGenerator; + } + + [UnitOfWork] + public async Task SaveAsync() + { + await using var applicationLockHandle = await DistributedLock.TryAcquireAsync( + GetApplicationDistributedLockKey() + ); + + if (applicationLockHandle == null) + { + return; + } + + var cacheKey = GetApplicationHashCacheKey(); + var cachedHash = await Cache.GetStringAsync(cacheKey, CancellationTokenProvider.Token); + + var workspaces = await WorkspaceSerializer.SerializeAsync(await StaticStore.GetAllAsync()); + var currentHash = CalculateHash(workspaces, AIOptions.DeletedWorkspaces); + + if (cachedHash == currentHash) + { + return; + } + + await using (var commonLockHandle = await DistributedLock.TryAcquireAsync( + GetCommonDistributedLockKey(), + TimeSpan.FromMinutes(5))) + { + if (commonLockHandle == null) + { + /* It will re-try */ + throw new AbpException("Could not acquire distributed lock for saving static Workspaces!"); + } + + using (var unitOfWork = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: true)) + { + try + { + var hasChangesInWorkspaces = await UpdateChangedWorkspacesAsync(workspaces); + + if (hasChangesInWorkspaces) + { + await Cache.SetStringAsync( + GetCommonStampCacheKey(), + Guid.NewGuid().ToString(), + new DistributedCacheEntryOptions + { + SlidingExpiration = TimeSpan.FromDays(30) + }, + CancellationTokenProvider.Token + ); + } + } + catch + { + try + { + await unitOfWork.RollbackAsync(); + } + catch + { + /* ignored */ + } + + throw; + } + + await unitOfWork.CompleteAsync(); + } + } + + await Cache.SetStringAsync( + cacheKey, + currentHash, + new DistributedCacheEntryOptions + { + SlidingExpiration = TimeSpan.FromDays(30) + }, + CancellationTokenProvider.Token + ); + } + + private async Task UpdateChangedWorkspacesAsync(Workspace[] workspaces) + { + var newRecords = new List(); + var changedRecords = new List(); + + var workspaceRecordsInDatabase = (await WorkspaceRepository.GetListAsync()).ToDictionary(x => x.Name); + + foreach (var record in workspaces) + { + var workspaceRecordInDatabase = workspaceRecordsInDatabase.GetOrDefault(record.Name); + if (workspaceRecordInDatabase == null) + { + /* New group */ + newRecords.Add(record); + continue; + } + + if (record.HasSameData(workspaceRecordInDatabase)) + { + /* Not changed */ + continue; + } + + /* Changed */ + workspaceRecordInDatabase.Patch(record); + changedRecords.Add(workspaceRecordInDatabase); + } + + /* Deleted */ + var deletedRecords = new List(); + + if (AIOptions.DeletedWorkspaces.Any()) + { + deletedRecords.AddRange(workspaceRecordsInDatabase.Values.Where(x => AIOptions.DeletedWorkspaces.Contains(x.Name))); + } + + if (newRecords.Any()) + { + await WorkspaceRepository.InsertManyAsync(newRecords); + } + + if (changedRecords.Any()) + { + await WorkspaceRepository.UpdateManyAsync(changedRecords); + } + + if (deletedRecords.Any()) + { + await WorkspaceRepository.DeleteManyAsync(deletedRecords); + } + + return newRecords.Any() || changedRecords.Any() || deletedRecords.Any(); + } + + private string GetApplicationDistributedLockKey() + { + return $"{CacheOptions.KeyPrefix}_{ApplicationInfoAccessor.ApplicationName}_AbpWorkspaceUpdateLock"; + } + + private string GetCommonDistributedLockKey() + { + return $"{CacheOptions.KeyPrefix}_Common_AbpWorkspaceUpdateLock"; + } + + private string GetApplicationHashCacheKey() + { + return $"{CacheOptions.KeyPrefix}_{ApplicationInfoAccessor.ApplicationName}_AbpWorkspacesHash"; + } + + private string GetCommonStampCacheKey() + { + return $"{CacheOptions.KeyPrefix}_AbpInMemoryWorkspaceCacheStamp"; + } + + private string CalculateHash(Workspace[] workspaces, IEnumerable deletedWorkspaces) + { + var jsonSerializerOptions = new JsonSerializerOptions + { + TypeInfoResolver = new DefaultJsonTypeInfoResolver + { + Modifiers = + { + new AbpIgnorePropertiesModifiers().CreateModifyAction(x => x.Id), + } + } + }; + + var stringBuilder = new StringBuilder(); + + stringBuilder.Append("Workspaces:"); + stringBuilder.AppendLine(JsonSerializer.Serialize(workspaces, jsonSerializerOptions)); + + stringBuilder.Append("DeletedWorkspace:"); + stringBuilder.Append(deletedWorkspaces.JoinAsString(",")); + + return stringBuilder + .ToString() + .ToMd5(); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/Workspace.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/Workspace.cs new file mode 100644 index 000000000..bb09b2f0f --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/Workspace.cs @@ -0,0 +1,255 @@ +using System; +using Volo.Abp; +using Volo.Abp.Data; +using Volo.Abp.Domain.Entities.Auditing; + +namespace LINGYUN.Abp.AIManagement.Workspaces; +public class Workspace : AuditedAggregateRoot +{ + public string Name { get; private set; } + + public string Provider { get; private set; } + + public string ModelName { get; private set; } + + public string DisplayName { get; private set; } + + public string? Description { get; private set; } + + public string? ApiKey { get; private set; } + + public string? ApiBaseUrl { get; private set; } + + public string? SystemPrompt { get; set; } + + public string? Instructions { get; set; } + + public float? Temperature { get; set; } + + public int? MaxOutputTokens { get; set; } + + public float? FrequencyPenalty { get; set; } + + public float? PresencePenalty { get; set; } + + public bool IsEnabled { get; set; } + + public string? StateCheckers { get; set; } + + protected Workspace() + { + ExtraProperties = new ExtraPropertyDictionary(); + this.SetDefaultsForExtraProperties(); + } + + public Workspace( + Guid id, + string name, + string provider, + string modelName, + string displayName, + string? description = null, + string? apiKey = null, + string? apiBaseUrl = null, + string? systemPrompt = null, + string? instructions = null, + float? temperature = null, + int? maxOutputTokens = null, + float? frequencyPenalty = null, + float? presencePenalty = null, + string? stateCheckers = null) + : base(id) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name), WorkspaceConsts.MaxNameLength); + Provider = Check.NotNullOrWhiteSpace(provider, nameof(provider), WorkspaceConsts.MaxProviderLength); + ModelName = Check.NotNullOrWhiteSpace(modelName, nameof(modelName), WorkspaceConsts.MaxModelNameLength); + DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName), WorkspaceConsts.MaxDisplayNameLength); + Description = Check.Length(description, nameof(description), WorkspaceConsts.MaxDescriptionLength); + ApiKey = Check.Length(apiKey, nameof(apiKey), WorkspaceConsts.MaxApiKeyLength); + ApiBaseUrl = Check.Length(apiBaseUrl, nameof(apiBaseUrl), WorkspaceConsts.MaxApiBaseUrlLength); + SystemPrompt = Check.Length(systemPrompt, nameof(systemPrompt), WorkspaceConsts.MaxSystemPromptLength); + Instructions = Check.Length(instructions, nameof(instructions), WorkspaceConsts.MaxInstructionsLength); + StateCheckers = Check.Length(stateCheckers, nameof(stateCheckers), WorkspaceConsts.MaxStateCheckersLength); + Temperature = temperature; + MaxOutputTokens = maxOutputTokens; + FrequencyPenalty = frequencyPenalty; + PresencePenalty = presencePenalty; + + IsEnabled = true; + ExtraProperties = new ExtraPropertyDictionary(); + this.SetDefaultsForExtraProperties(); + } + + public bool HasSameData(Workspace otherWorkspace) + { + if (Name != otherWorkspace.Name) + { + return false; + } + + if (Provider != otherWorkspace.Provider) + { + return false; + } + + if (ModelName != otherWorkspace.ModelName) + { + return false; + } + + if (DisplayName != otherWorkspace.DisplayName) + { + return false; + } + + if (Description != otherWorkspace.Description) + { + return false; + } + + if (ApiKey != otherWorkspace.ApiKey) + { + return false; + } + + if (ApiBaseUrl != otherWorkspace.ApiBaseUrl) + { + return false; + } + + if (SystemPrompt != otherWorkspace.SystemPrompt) + { + return false; + } + + if (Instructions != otherWorkspace.Instructions) + { + return false; + } + + if (IsEnabled != otherWorkspace.IsEnabled) + { + return false; + } + + if (Temperature != otherWorkspace.Temperature) + { + return false; + } + + if (MaxOutputTokens != otherWorkspace.MaxOutputTokens) + { + return false; + } + + if (FrequencyPenalty != otherWorkspace.FrequencyPenalty) + { + return false; + } + + if (PresencePenalty != otherWorkspace.PresencePenalty) + { + return false; + } + + if (StateCheckers != otherWorkspace.StateCheckers) + { + return false; + } + + if (!this.HasSameExtraProperties(otherWorkspace)) + { + return false; + } + + return true; + } + + public void Patch(Workspace otherWorkspace) + { + if (Name != otherWorkspace.Name) + { + Name = otherWorkspace.Name; + } + + if (Provider != otherWorkspace.Provider) + { + Provider = otherWorkspace.Provider; + } + + if (ModelName != otherWorkspace.ModelName) + { + ModelName = otherWorkspace.ModelName; + } + + if (DisplayName != otherWorkspace.DisplayName) + { + DisplayName = otherWorkspace.DisplayName; + } + + if (Description != otherWorkspace.Description) + { + Description = otherWorkspace.Description; + } + + if (ApiKey != otherWorkspace.ApiKey) + { + ApiKey = otherWorkspace.ApiKey; + } + + if (ApiBaseUrl != otherWorkspace.ApiBaseUrl) + { + ApiBaseUrl = otherWorkspace.ApiBaseUrl; + } + + if (SystemPrompt != otherWorkspace.SystemPrompt) + { + SystemPrompt = otherWorkspace.SystemPrompt; + } + + if (Instructions != otherWorkspace.Instructions) + { + Instructions = otherWorkspace.Instructions; + } + + if (IsEnabled != otherWorkspace.IsEnabled) + { + IsEnabled = otherWorkspace.IsEnabled; + } + + if (Temperature != otherWorkspace.Temperature) + { + Temperature = otherWorkspace.Temperature; + } + + if (MaxOutputTokens != otherWorkspace.MaxOutputTokens) + { + MaxOutputTokens = otherWorkspace.MaxOutputTokens; + } + + if (FrequencyPenalty != otherWorkspace.FrequencyPenalty) + { + FrequencyPenalty = otherWorkspace.FrequencyPenalty; + } + + if (PresencePenalty != otherWorkspace.PresencePenalty) + { + PresencePenalty = otherWorkspace.PresencePenalty; + } + + if (StateCheckers != otherWorkspace.StateCheckers) + { + StateCheckers = otherWorkspace.StateCheckers; + } + + if (!this.HasSameExtraProperties(otherWorkspace)) + { + ExtraProperties.Clear(); + + foreach (var property in otherWorkspace.ExtraProperties) + { + ExtraProperties.Add(property.Key, property.Value); + } + } + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs new file mode 100644 index 000000000..351ffa6ca --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs @@ -0,0 +1,72 @@ +using LINGYUN.Abp.AI.Workspaces; +using System.Collections.Generic; +using System.Globalization; +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; +using Volo.Abp.Localization; +using Volo.Abp.SimpleStateChecking; + +namespace LINGYUN.Abp.AIManagement.Workspaces; +public class WorkspaceDefinitionSerializer : IWorkspaceDefinitionSerializer, ITransientDependency +{ + protected IGuidGenerator GuidGenerator { get; } + protected ISimpleStateCheckerSerializer StateCheckerSerializer { get; } + protected ILocalizableStringSerializer LocalizableStringSerializer { get; } + + public WorkspaceDefinitionSerializer( + IGuidGenerator guidGenerator, + ISimpleStateCheckerSerializer stateCheckerSerializer, + ILocalizableStringSerializer localizableStringSerializer) + { + GuidGenerator = guidGenerator; + StateCheckerSerializer = stateCheckerSerializer; + LocalizableStringSerializer = localizableStringSerializer; + } + + public async virtual Task SerializeAsync(IEnumerable definitions) + { + var records = new List(); + foreach (var workspaceDef in definitions) + { + records.Add(await SerializeAsync(workspaceDef)); + } + + return records.ToArray(); + } + + public virtual Task SerializeAsync(WorkspaceDefinition definition) + { + using (CultureHelper.Use(CultureInfo.InvariantCulture)) + { + var workspace = new Workspace( + GuidGenerator.Create(), + definition.Name, + definition.Provider, + definition.ModelName, + LocalizableStringSerializer.Serialize(definition.DisplayName)!, + definition.Description != null ? LocalizableStringSerializer.Serialize(definition.Description) : null, + definition.ApiKey, + definition.ApiBaseUrl, + definition.SystemPrompt, + definition.Instructions, + definition.Temperature, + definition.MaxOutputTokens, + definition.FrequencyPenalty, + definition.PresencePenalty, + SerializeStateCheckers(definition.StateCheckers)); + + foreach (var property in definition.Properties) + { + workspace.SetProperty(property.Key, property.Value); + } + + return Task.FromResult(workspace); + } + } + protected virtual string? SerializeStateCheckers(List> stateCheckers) + { + return StateCheckerSerializer.Serialize(stateCheckers); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDynamicInitializer.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDynamicInitializer.cs new file mode 100644 index 000000000..591535580 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDynamicInitializer.cs @@ -0,0 +1,149 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.AI.Workspaces; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Polly; +using System; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; + +namespace LINGYUN.Abp.AIManagement.Workspaces; +public class WorkspaceDynamicInitializer : ITransientDependency +{ + public ILogger Logger { get; set; } + + protected IServiceProvider ServiceProvider { get; } + protected IOptions Options { get; } + [CanBeNull] + protected IHostApplicationLifetime ApplicationLifetime { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + protected IDynamicWorkspaceDefinitionStore DynamicWorkspaceDefinitionStore { get; } + protected IStaticWorkspaceSaver StaticWorkspaceSaver { get; } + + public WorkspaceDynamicInitializer( + IServiceProvider serviceProvider, + IOptions options, + ICancellationTokenProvider cancellationTokenProvider, + IDynamicWorkspaceDefinitionStore dynamicWorkspaceDefinitionStore, + IStaticWorkspaceSaver staticWorkspaceSaver) + { + Logger = NullLogger.Instance; + + ServiceProvider = serviceProvider; + Options = options; + CancellationTokenProvider = cancellationTokenProvider; + DynamicWorkspaceDefinitionStore = dynamicWorkspaceDefinitionStore; + StaticWorkspaceSaver = staticWorkspaceSaver; + ApplicationLifetime = ServiceProvider.GetRequiredService(); + } + + public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default) + { + var options = Options.Value; + + if (!options.SaveStaticWorkspacesToDatabase && !options.IsDynamicWorkspaceStoreEnabled) + { + return Task.CompletedTask; + } + + if (runInBackground) + { + Task.Run(async () => + { + if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null) + { + cancellationToken = ApplicationLifetime.ApplicationStopping; + } + await ExecuteInitializationAsync(options, cancellationToken); + }, cancellationToken); + + return Task.CompletedTask; + } + + return ExecuteInitializationAsync(options, cancellationToken); + } + + protected virtual async Task ExecuteInitializationAsync(AIManagementOptions options, CancellationToken cancellationToken) + { + try + { + using (CancellationTokenProvider.Use(cancellationToken)) + { + if (CancellationTokenProvider.Token.IsCancellationRequested) + { + return; + } + + await SaveStaticWorkspacesToDatabaseAsync(options, cancellationToken); + + if (CancellationTokenProvider.Token.IsCancellationRequested) + { + return; + } + + await PreCacheDynamicWorkspacesAsync(options); + } + } + catch + { + // No need to log here since inner calls log + } + } + + protected virtual async Task SaveStaticWorkspacesToDatabaseAsync( + AIManagementOptions options, + CancellationToken cancellationToken) + { + if (!options.SaveStaticWorkspacesToDatabase) + { + return; + } + + await Policy + .Handle(ex => ex is not OperationCanceledException) + .WaitAndRetryAsync( + 8, + retryAttempt => TimeSpan.FromSeconds( + Volo.Abp.RandomHelper.GetRandom( + (int)Math.Pow(2, retryAttempt) * 8, + (int)Math.Pow(2, retryAttempt) * 12) + ) + ) + .ExecuteAsync(async _ => + { + try + { + await StaticWorkspaceSaver.SaveAsync(); + } + catch (Exception ex) + { + Logger.LogException(ex); + throw; // Polly will catch it + } + }, cancellationToken); + } + + protected virtual async Task PreCacheDynamicWorkspacesAsync(AIManagementOptions options) + { + if (!options.IsDynamicWorkspaceStoreEnabled) + { + return; + } + + try + { + // Pre-cache Workspaces, so first request doesn't wait + await DynamicWorkspaceDefinitionStore.GetAllAsync(); + } + catch (Exception ex) + { + Logger.LogException(ex); + throw; // It will be cached in Initialize() + } + } +} From d5fe69de58b2b012a237aa1b2c50e406815cc45d Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 16 Jan 2026 11:01:10 +0800 Subject: [PATCH 10/88] feat: Add EF Workspace Model Config --- .../AIManagementDbContext.cs | 4 +++- ...nagementDbContextModelBuilderExtensions.cs | 23 +++++++++++++++++++ .../IAIManagementDbContext.cs | 6 +++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs index d1bfd18d6..823e7cc71 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using LINGYUN.Abp.AIManagement.Workspaces; +using Microsoft.EntityFrameworkCore; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; @@ -7,6 +8,7 @@ namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; [ConnectionStringName(AbpAIManagementDbProperties.ConnectionStringName)] public class AIManagementDbContext : AbpDbContext, IAIManagementDbContext { + public DbSet Workspaces { get; set; } public AIManagementDbContext( DbContextOptions options) : base(options) { diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs index 2bf2e574a..8512e2199 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs @@ -1,4 +1,5 @@ using JetBrains.Annotations; +using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.EntityFrameworkCore; using Volo.Abp; using Volo.Abp.EntityFrameworkCore.Modeling; @@ -16,6 +17,28 @@ public static class AIManagementDbContextModelBuilderExtensions return; } + builder.Entity(b => + { + b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "Workspaces", AbpAIManagementDbProperties.DbSchema); + + b.ConfigureByConvention(); + + b.Property(x => x.Name).HasMaxLength(WorkspaceConsts.MaxNameLength).IsRequired(); + b.Property(x => x.Provider).HasMaxLength(WorkspaceConsts.MaxProviderLength).IsRequired(); + b.Property(x => x.ModelName).HasMaxLength(WorkspaceConsts.MaxModelNameLength).IsRequired(); + b.Property(x => x.DisplayName).HasMaxLength(WorkspaceConsts.MaxDisplayNameLength).IsRequired(); + b.Property(x => x.Description).HasMaxLength(WorkspaceConsts.MaxDescriptionLength); + b.Property(x => x.ApiKey).HasMaxLength(WorkspaceConsts.MaxApiKeyLength); + b.Property(x => x.ApiBaseUrl).HasMaxLength(WorkspaceConsts.MaxApiKeyLength); + b.Property(x => x.SystemPrompt).HasMaxLength(WorkspaceConsts.MaxSystemPromptLength); + b.Property(x => x.Instructions).HasMaxLength(WorkspaceConsts.MaxInstructionsLength); + b.Property(x => x.StateCheckers).HasMaxLength(WorkspaceConsts.MaxStateCheckersLength); + + b.HasIndex(x => new { x.Name }).IsUnique(); + + b.ApplyObjectExtensionMappings(); + }); + builder.TryConfigureObjectExtensions(); } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs index 16b860ed9..234148a27 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs @@ -1,4 +1,6 @@ -using Volo.Abp.Data; +using LINGYUN.Abp.AIManagement.Workspaces; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; @@ -6,5 +8,5 @@ namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; [ConnectionStringName(AbpAIManagementDbProperties.ConnectionStringName)] public interface IAIManagementDbContext : IEfCoreDbContext { - + DbSet Workspaces { get; } } From 5168b9d3755153b11cf9f94eac48e51b6fedd3cf Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 16 Jan 2026 11:03:34 +0800 Subject: [PATCH 11/88] feat: Add Workspace EFCore Repository Impl --- .../Workspaces/IWorkspaceRepository.cs | 2 +- ...bpAIManagementEntityFrameworkCoreModule.cs | 5 +++- .../EfCoreWorkspaceRepository.cs | 26 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreWorkspaceRepository.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceRepository.cs index 5405b0b0a..db729e5a5 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceRepository.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceRepository.cs @@ -6,7 +6,7 @@ using Volo.Abp.Domain.Repositories; namespace LINGYUN.Abp.AIManagement.Workspaces; public interface IWorkspaceRepository : IBasicRepository { - Task FindByNameAsync( + Task FindByNameAsync( string name, CancellationToken cancellationToken = default); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs index adbf7146d..566db1fc1 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using LINGYUN.Abp.AIManagement.Workspaces; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.Modularity; @@ -14,6 +15,8 @@ public class AbpAIManagementEntityFrameworkCoreModule : AbpModule context.Services.AddAbpDbContext(options => { options.AddDefaultRepositories(); + + options.AddRepository(); }); } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreWorkspaceRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreWorkspaceRepository.cs new file mode 100644 index 000000000..c0fcc2cb9 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreWorkspaceRepository.cs @@ -0,0 +1,26 @@ +using LINGYUN.Abp.AIManagement.Workspaces; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; +public class EfCoreWorkspaceRepository : EfCoreRepository, IWorkspaceRepository +{ + public EfCoreWorkspaceRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public async virtual Task FindByNameAsync(string name, CancellationToken cancellationToken = default) + { + return await (await GetQueryableAsync()) + .Where(x => x.Name == name) + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); ; + } +} From 11dfe9af095495a070164b4825d49524a3e110d7 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 16 Jan 2026 11:06:05 +0800 Subject: [PATCH 12/88] feat: Rename Workspace to WorkspaceDefinitionRecord --- ....cs => WorkspaceDefinitionRecordConsts.cs} | 2 +- .../DynamicWorkspaceDefinitionStore.cs | 8 ++--- ...icWorkspaceDefinitionStoreInMemoryCache.cs | 2 +- ...icWorkspaceDefinitionStoreInMemoryCache.cs | 2 +- ...> IWorkspaceDefinitionRecordRepository.cs} | 4 +-- .../IWorkspaceDefinitionSerializer.cs | 4 +-- .../Workspaces/StaticWorkspaceSaver.cs | 16 +++++----- ...kspace.cs => WorkspaceDefinitionRecord.cs} | 30 +++++++++---------- .../WorkspaceDefinitionSerializer.cs | 8 ++--- .../AIManagementDbContext.cs | 2 +- ...nagementDbContextModelBuilderExtensions.cs | 24 +++++++-------- ...bpAIManagementEntityFrameworkCoreModule.cs | 2 +- ...oreWorkspaceDefinitionRecordRepository.cs} | 6 ++-- .../IAIManagementDbContext.cs | 2 +- 14 files changed, 56 insertions(+), 56 deletions(-) rename aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/{WorkspaceConsts.cs => WorkspaceDefinitionRecordConsts.cs} (92%) rename aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/{IWorkspaceRepository.cs => IWorkspaceDefinitionRecordRepository.cs} (59%) rename aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/{Workspace.cs => WorkspaceDefinitionRecord.cs} (86%) rename aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/{EfCoreWorkspaceRepository.cs => EfCoreWorkspaceDefinitionRecordRepository.cs} (64%) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecordConsts.cs similarity index 92% rename from aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceConsts.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecordConsts.cs index c1dc19dc7..f8a2e6bd1 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceConsts.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecordConsts.cs @@ -1,5 +1,5 @@ namespace LINGYUN.Abp.AIManagement.Workspaces; -public static class WorkspaceConsts +public static class WorkspaceDefinitionRecordConsts { public static int MaxNameLength { get; set; } = 64; public static int MaxProviderLength { get; set; } = 20; diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStore.cs index 6c7225b8f..4550973af 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStore.cs @@ -16,7 +16,7 @@ namespace LINGYUN.Abp.AIManagement.Workspaces; [Dependency(ReplaceServices = true)] public class DynamicWorkspaceDefinitionStore : IDynamicWorkspaceDefinitionStore, ITransientDependency { - protected IWorkspaceRepository WorkspaceRepository { get; } + protected IWorkspaceDefinitionRecordRepository WorkspaceDefinitionRecordRepository { get; } protected IWorkspaceDefinitionSerializer WorkspaceDefinitionSerializer { get; } protected IDynamicWorkspaceDefinitionStoreInMemoryCache StoreCache { get; } protected IDistributedCache DistributedCache { get; } @@ -25,7 +25,7 @@ public class DynamicWorkspaceDefinitionStore : IDynamicWorkspaceDefinitionStore, protected AbpDistributedCacheOptions CacheOptions { get; } public DynamicWorkspaceDefinitionStore( - IWorkspaceRepository workspaceRepository, + IWorkspaceDefinitionRecordRepository workspaceRepository, IWorkspaceDefinitionSerializer workspaceDefinitionSerializer, IDynamicWorkspaceDefinitionStoreInMemoryCache storeCache, IDistributedCache distributedCache, @@ -33,7 +33,7 @@ public class DynamicWorkspaceDefinitionStore : IDynamicWorkspaceDefinitionStore, IOptions aiManagementOptions, IAbpDistributedLock distributedLock) { - WorkspaceRepository = workspaceRepository; + WorkspaceDefinitionRecordRepository = workspaceRepository; WorkspaceDefinitionSerializer = workspaceDefinitionSerializer; StoreCache = storeCache; DistributedCache = distributedCache; @@ -103,7 +103,7 @@ public class DynamicWorkspaceDefinitionStore : IDynamicWorkspaceDefinitionStore, protected virtual async Task UpdateInMemoryStoreCache() { - var workspaces = await WorkspaceRepository.GetListAsync(); + var workspaces = await WorkspaceDefinitionRecordRepository.GetListAsync(); await StoreCache.FillAsync(workspaces); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreInMemoryCache.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreInMemoryCache.cs index 43314dfed..478ededa9 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreInMemoryCache.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreInMemoryCache.cs @@ -30,7 +30,7 @@ public class DynamicWorkspaceDefinitionStoreInMemoryCache : IDynamicWorkspaceDef WorkspaceDefinitions = new Dictionary(); } - public Task FillAsync(List workspaces) + public Task FillAsync(List workspaces) { WorkspaceDefinitions.Clear(); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IDynamicWorkspaceDefinitionStoreInMemoryCache.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IDynamicWorkspaceDefinitionStoreInMemoryCache.cs index c2d2a4b12..ec3c6b82b 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IDynamicWorkspaceDefinitionStoreInMemoryCache.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IDynamicWorkspaceDefinitionStoreInMemoryCache.cs @@ -14,7 +14,7 @@ public interface IDynamicWorkspaceDefinitionStoreInMemoryCache DateTime? LastCheckTime { get; set; } - Task FillAsync(List permissions); + Task FillAsync(List permissions); WorkspaceDefinition? GetWorkspaceOrNull(string name); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionRecordRepository.cs similarity index 59% rename from aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceRepository.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionRecordRepository.cs index db729e5a5..e4b566cee 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceRepository.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionRecordRepository.cs @@ -4,9 +4,9 @@ using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; namespace LINGYUN.Abp.AIManagement.Workspaces; -public interface IWorkspaceRepository : IBasicRepository +public interface IWorkspaceDefinitionRecordRepository : IBasicRepository { - Task FindByNameAsync( + Task FindByNameAsync( string name, CancellationToken cancellationToken = default); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionSerializer.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionSerializer.cs index fd1106fcd..f2e186875 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionSerializer.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionSerializer.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; namespace LINGYUN.Abp.AIManagement.Workspaces; public interface IWorkspaceDefinitionSerializer { - Task SerializeAsync(IEnumerable definitions); + Task SerializeAsync(IEnumerable definitions); - Task SerializeAsync(WorkspaceDefinition definition); + Task SerializeAsync(WorkspaceDefinition definition); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/StaticWorkspaceSaver.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/StaticWorkspaceSaver.cs index df306125c..255d65e41 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/StaticWorkspaceSaver.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/StaticWorkspaceSaver.cs @@ -22,7 +22,7 @@ namespace LINGYUN.Abp.AIManagement.Workspaces; public class StaticWorkspaceSaver : IStaticWorkspaceSaver, ITransientDependency { protected IStaticWorkspaceDefinitionStore StaticStore { get; } - protected IWorkspaceRepository WorkspaceRepository { get; } + protected IWorkspaceDefinitionRecordRepository WorkspaceRepository { get; } protected IWorkspaceDefinitionSerializer WorkspaceSerializer { get; } protected IDistributedCache Cache { get; } protected IApplicationInfoAccessor ApplicationInfoAccessor { get; } @@ -35,7 +35,7 @@ public class StaticWorkspaceSaver : IStaticWorkspaceSaver, ITransientDependency public StaticWorkspaceSaver( IStaticWorkspaceDefinitionStore staticStore, - IWorkspaceRepository workspaceRepository, + IWorkspaceDefinitionRecordRepository workspaceRepository, IWorkspaceDefinitionSerializer workspaceSerializer, IDistributedCache cache, IOptions cacheOptions, @@ -140,10 +140,10 @@ public class StaticWorkspaceSaver : IStaticWorkspaceSaver, ITransientDependency ); } - private async Task UpdateChangedWorkspacesAsync(Workspace[] workspaces) + private async Task UpdateChangedWorkspacesAsync(WorkspaceDefinitionRecord[] workspaces) { - var newRecords = new List(); - var changedRecords = new List(); + var newRecords = new List(); + var changedRecords = new List(); var workspaceRecordsInDatabase = (await WorkspaceRepository.GetListAsync()).ToDictionary(x => x.Name); @@ -169,7 +169,7 @@ public class StaticWorkspaceSaver : IStaticWorkspaceSaver, ITransientDependency } /* Deleted */ - var deletedRecords = new List(); + var deletedRecords = new List(); if (AIOptions.DeletedWorkspaces.Any()) { @@ -214,7 +214,7 @@ public class StaticWorkspaceSaver : IStaticWorkspaceSaver, ITransientDependency return $"{CacheOptions.KeyPrefix}_AbpInMemoryWorkspaceCacheStamp"; } - private string CalculateHash(Workspace[] workspaces, IEnumerable deletedWorkspaces) + private string CalculateHash(WorkspaceDefinitionRecord[] workspaces, IEnumerable deletedWorkspaces) { var jsonSerializerOptions = new JsonSerializerOptions { @@ -222,7 +222,7 @@ public class StaticWorkspaceSaver : IStaticWorkspaceSaver, ITransientDependency { Modifiers = { - new AbpIgnorePropertiesModifiers().CreateModifyAction(x => x.Id), + new AbpIgnorePropertiesModifiers().CreateModifyAction(x => x.Id), } } }; diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/Workspace.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecord.cs similarity index 86% rename from aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/Workspace.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecord.cs index bb09b2f0f..1e75cba0b 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/Workspace.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecord.cs @@ -4,7 +4,7 @@ using Volo.Abp.Data; using Volo.Abp.Domain.Entities.Auditing; namespace LINGYUN.Abp.AIManagement.Workspaces; -public class Workspace : AuditedAggregateRoot +public class WorkspaceDefinitionRecord : AuditedAggregateRoot { public string Name { get; private set; } @@ -36,13 +36,13 @@ public class Workspace : AuditedAggregateRoot public string? StateCheckers { get; set; } - protected Workspace() + protected WorkspaceDefinitionRecord() { ExtraProperties = new ExtraPropertyDictionary(); this.SetDefaultsForExtraProperties(); } - public Workspace( + public WorkspaceDefinitionRecord( Guid id, string name, string provider, @@ -60,16 +60,16 @@ public class Workspace : AuditedAggregateRoot string? stateCheckers = null) : base(id) { - Name = Check.NotNullOrWhiteSpace(name, nameof(name), WorkspaceConsts.MaxNameLength); - Provider = Check.NotNullOrWhiteSpace(provider, nameof(provider), WorkspaceConsts.MaxProviderLength); - ModelName = Check.NotNullOrWhiteSpace(modelName, nameof(modelName), WorkspaceConsts.MaxModelNameLength); - DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName), WorkspaceConsts.MaxDisplayNameLength); - Description = Check.Length(description, nameof(description), WorkspaceConsts.MaxDescriptionLength); - ApiKey = Check.Length(apiKey, nameof(apiKey), WorkspaceConsts.MaxApiKeyLength); - ApiBaseUrl = Check.Length(apiBaseUrl, nameof(apiBaseUrl), WorkspaceConsts.MaxApiBaseUrlLength); - SystemPrompt = Check.Length(systemPrompt, nameof(systemPrompt), WorkspaceConsts.MaxSystemPromptLength); - Instructions = Check.Length(instructions, nameof(instructions), WorkspaceConsts.MaxInstructionsLength); - StateCheckers = Check.Length(stateCheckers, nameof(stateCheckers), WorkspaceConsts.MaxStateCheckersLength); + Name = Check.NotNullOrWhiteSpace(name, nameof(name), WorkspaceDefinitionRecordConsts.MaxNameLength); + Provider = Check.NotNullOrWhiteSpace(provider, nameof(provider), WorkspaceDefinitionRecordConsts.MaxProviderLength); + ModelName = Check.NotNullOrWhiteSpace(modelName, nameof(modelName), WorkspaceDefinitionRecordConsts.MaxModelNameLength); + DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName), WorkspaceDefinitionRecordConsts.MaxDisplayNameLength); + Description = Check.Length(description, nameof(description), WorkspaceDefinitionRecordConsts.MaxDescriptionLength); + ApiKey = Check.Length(apiKey, nameof(apiKey), WorkspaceDefinitionRecordConsts.MaxApiKeyLength); + ApiBaseUrl = Check.Length(apiBaseUrl, nameof(apiBaseUrl), WorkspaceDefinitionRecordConsts.MaxApiBaseUrlLength); + SystemPrompt = Check.Length(systemPrompt, nameof(systemPrompt), WorkspaceDefinitionRecordConsts.MaxSystemPromptLength); + Instructions = Check.Length(instructions, nameof(instructions), WorkspaceDefinitionRecordConsts.MaxInstructionsLength); + StateCheckers = Check.Length(stateCheckers, nameof(stateCheckers), WorkspaceDefinitionRecordConsts.MaxStateCheckersLength); Temperature = temperature; MaxOutputTokens = maxOutputTokens; FrequencyPenalty = frequencyPenalty; @@ -80,7 +80,7 @@ public class Workspace : AuditedAggregateRoot this.SetDefaultsForExtraProperties(); } - public bool HasSameData(Workspace otherWorkspace) + public bool HasSameData(WorkspaceDefinitionRecord otherWorkspace) { if (Name != otherWorkspace.Name) { @@ -165,7 +165,7 @@ public class Workspace : AuditedAggregateRoot return true; } - public void Patch(Workspace otherWorkspace) + public void Patch(WorkspaceDefinitionRecord otherWorkspace) { if (Name != otherWorkspace.Name) { diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs index 351ffa6ca..3c69f73fd 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs @@ -25,9 +25,9 @@ public class WorkspaceDefinitionSerializer : IWorkspaceDefinitionSerializer, ITr LocalizableStringSerializer = localizableStringSerializer; } - public async virtual Task SerializeAsync(IEnumerable definitions) + public async virtual Task SerializeAsync(IEnumerable definitions) { - var records = new List(); + var records = new List(); foreach (var workspaceDef in definitions) { records.Add(await SerializeAsync(workspaceDef)); @@ -36,11 +36,11 @@ public class WorkspaceDefinitionSerializer : IWorkspaceDefinitionSerializer, ITr return records.ToArray(); } - public virtual Task SerializeAsync(WorkspaceDefinition definition) + public virtual Task SerializeAsync(WorkspaceDefinition definition) { using (CultureHelper.Use(CultureInfo.InvariantCulture)) { - var workspace = new Workspace( + var workspace = new WorkspaceDefinitionRecord( GuidGenerator.Create(), definition.Name, definition.Provider, diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs index 823e7cc71..790174fb2 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs @@ -8,7 +8,7 @@ namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; [ConnectionStringName(AbpAIManagementDbProperties.ConnectionStringName)] public class AIManagementDbContext : AbpDbContext, IAIManagementDbContext { - public DbSet Workspaces { get; set; } + public DbSet Workspaces { get; set; } public AIManagementDbContext( DbContextOptions options) : base(options) { diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs index 8512e2199..7496ce829 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs @@ -17,22 +17,22 @@ public static class AIManagementDbContextModelBuilderExtensions return; } - builder.Entity(b => + builder.Entity(b => { - b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "Workspaces", AbpAIManagementDbProperties.DbSchema); + b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "WorkspaceDefinitions", AbpAIManagementDbProperties.DbSchema); b.ConfigureByConvention(); - b.Property(x => x.Name).HasMaxLength(WorkspaceConsts.MaxNameLength).IsRequired(); - b.Property(x => x.Provider).HasMaxLength(WorkspaceConsts.MaxProviderLength).IsRequired(); - b.Property(x => x.ModelName).HasMaxLength(WorkspaceConsts.MaxModelNameLength).IsRequired(); - b.Property(x => x.DisplayName).HasMaxLength(WorkspaceConsts.MaxDisplayNameLength).IsRequired(); - b.Property(x => x.Description).HasMaxLength(WorkspaceConsts.MaxDescriptionLength); - b.Property(x => x.ApiKey).HasMaxLength(WorkspaceConsts.MaxApiKeyLength); - b.Property(x => x.ApiBaseUrl).HasMaxLength(WorkspaceConsts.MaxApiKeyLength); - b.Property(x => x.SystemPrompt).HasMaxLength(WorkspaceConsts.MaxSystemPromptLength); - b.Property(x => x.Instructions).HasMaxLength(WorkspaceConsts.MaxInstructionsLength); - b.Property(x => x.StateCheckers).HasMaxLength(WorkspaceConsts.MaxStateCheckersLength); + b.Property(x => x.Name).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxNameLength).IsRequired(); + b.Property(x => x.Provider).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxProviderLength).IsRequired(); + b.Property(x => x.ModelName).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxModelNameLength).IsRequired(); + b.Property(x => x.DisplayName).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxDisplayNameLength).IsRequired(); + b.Property(x => x.Description).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxDescriptionLength); + b.Property(x => x.ApiKey).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); + b.Property(x => x.ApiBaseUrl).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); + b.Property(x => x.SystemPrompt).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxSystemPromptLength); + b.Property(x => x.Instructions).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxInstructionsLength); + b.Property(x => x.StateCheckers).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxStateCheckersLength); b.HasIndex(x => new { x.Name }).IsUnique(); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs index 566db1fc1..870cad669 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs @@ -16,7 +16,7 @@ public class AbpAIManagementEntityFrameworkCoreModule : AbpModule { options.AddDefaultRepositories(); - options.AddRepository(); + options.AddRepository(); }); } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreWorkspaceRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreWorkspaceDefinitionRecordRepository.cs similarity index 64% rename from aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreWorkspaceRepository.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreWorkspaceDefinitionRecordRepository.cs index c0fcc2cb9..7c0bcd3b5 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreWorkspaceRepository.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreWorkspaceDefinitionRecordRepository.cs @@ -9,15 +9,15 @@ using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; -public class EfCoreWorkspaceRepository : EfCoreRepository, IWorkspaceRepository +public class EfCoreWorkspaceDefinitionRecordRepository : EfCoreRepository, IWorkspaceDefinitionRecordRepository { - public EfCoreWorkspaceRepository( + public EfCoreWorkspaceDefinitionRecordRepository( IDbContextProvider dbContextProvider) : base(dbContextProvider) { } - public async virtual Task FindByNameAsync(string name, CancellationToken cancellationToken = default) + public async virtual Task FindByNameAsync(string name, CancellationToken cancellationToken = default) { return await (await GetQueryableAsync()) .Where(x => x.Name == name) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs index 234148a27..8a6f5fa6f 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs @@ -8,5 +8,5 @@ namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; [ConnectionStringName(AbpAIManagementDbProperties.ConnectionStringName)] public interface IAIManagementDbContext : IEfCoreDbContext { - DbSet Workspaces { get; } + DbSet Workspaces { get; } } From ee374cf5767098b93f76dd3a455e5166afa781b9 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 16 Jan 2026 11:10:49 +0800 Subject: [PATCH 13/88] feat: replace IsTenantOnlyDatabase to IsHostDatabase --- ...nagementDbContextModelBuilderExtensions.cs | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs index 7496ce829..46de47d01 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs @@ -12,33 +12,31 @@ public static class AIManagementDbContextModelBuilderExtensions { Check.NotNull(builder, nameof(builder)); - if (builder.IsTenantOnlyDatabase()) + if (builder.IsHostDatabase()) { - return; + builder.Entity(b => + { + b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "WorkspaceDefinitions", AbpAIManagementDbProperties.DbSchema); + + b.ConfigureByConvention(); + + b.Property(x => x.Name).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxNameLength).IsRequired(); + b.Property(x => x.Provider).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxProviderLength).IsRequired(); + b.Property(x => x.ModelName).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxModelNameLength).IsRequired(); + b.Property(x => x.DisplayName).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxDisplayNameLength).IsRequired(); + b.Property(x => x.Description).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxDescriptionLength); + b.Property(x => x.ApiKey).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); + b.Property(x => x.ApiBaseUrl).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); + b.Property(x => x.SystemPrompt).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxSystemPromptLength); + b.Property(x => x.Instructions).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxInstructionsLength); + b.Property(x => x.StateCheckers).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxStateCheckersLength); + + b.HasIndex(x => new { x.Name }).IsUnique(); + + b.ApplyObjectExtensionMappings(); + }); } - builder.Entity(b => - { - b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "WorkspaceDefinitions", AbpAIManagementDbProperties.DbSchema); - - b.ConfigureByConvention(); - - b.Property(x => x.Name).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxNameLength).IsRequired(); - b.Property(x => x.Provider).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxProviderLength).IsRequired(); - b.Property(x => x.ModelName).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxModelNameLength).IsRequired(); - b.Property(x => x.DisplayName).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxDisplayNameLength).IsRequired(); - b.Property(x => x.Description).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxDescriptionLength); - b.Property(x => x.ApiKey).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); - b.Property(x => x.ApiBaseUrl).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); - b.Property(x => x.SystemPrompt).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxSystemPromptLength); - b.Property(x => x.Instructions).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxInstructionsLength); - b.Property(x => x.StateCheckers).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxStateCheckersLength); - - b.HasIndex(x => new { x.Name }).IsUnique(); - - b.ApplyObjectExtensionMappings(); - }); - builder.TryConfigureObjectExtensions(); } } From 3ae8ce0b674baf0dbd945281b5c07d07da91366e Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 16 Jan 2026 11:33:38 +0800 Subject: [PATCH 14/88] feat: Add AIManagement localization resource --- .../AbpAIManagementDomainSharedModule.cs | 17 ++++++++++++++++- .../Localization/AIManagementResource.cs | 8 ++++++++ .../AIManagement/Localization/Resources/en.json | 5 +++++ .../Localization/Resources/zh-Hans.json | 5 +++++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/AIManagementResource.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs index 5515864d6..5c0168a94 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs @@ -1,10 +1,25 @@ -using Volo.Abp.Domain; +using LINGYUN.Abp.AIManagement.Localization; +using Volo.Abp.Domain; +using Volo.Abp.Localization; using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; namespace LINGYUN.Abp.AIManagement; [DependsOn(typeof(AbpDddDomainSharedModule))] public class AbpAIManagementDomainSharedModule : AbpModule { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + Configure(options => + { + options.Resources.Add() + .AddVirtualJson("/LINGYUN/Abp/AIManagement/Localization/Resources"); + }); + } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/AIManagementResource.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/AIManagementResource.cs new file mode 100644 index 000000000..d31c83f9a --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/AIManagementResource.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Localization; + +namespace LINGYUN.Abp.AIManagement.Localization; + +[LocalizationResourceName("AIManagement")] +public class AIManagementResource +{ +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json new file mode 100644 index 000000000..a9c8dcc3f --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json @@ -0,0 +1,5 @@ +{ + "culture": "en", + "texts": { + } +} \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json new file mode 100644 index 000000000..c5ad81326 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json @@ -0,0 +1,5 @@ +{ + "culture": "zh-Hans", + "texts": { + } +} \ No newline at end of file From 6d51f28f30ffb2b90e6d0afdbca9d15ff0de078e Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 16 Jan 2026 13:03:39 +0800 Subject: [PATCH 15/88] feat: Add UseTextMessageStore Impl --- ...GYUN.Abp.AIManagement.Domain.Shared.csproj | 5 + .../Localization/Resources/en.json | 2 + .../Localization/Resources/zh-Hans.json | 2 + .../Messages/UserMessageRecordConsts.cs | 5 + .../Messages/UserTextMessageRecordConsts.cs | 5 + .../LINGYUN.Abp.AIManagement.Domain.csproj | 1 + .../AbpAIManagementDomainMappers.cs | 15 +++ .../IUserTextMessageRecordRepository.cs | 14 +++ .../Messages/UserMessageRecord.cs | 69 +++++++++++ .../AIManagement/Messages/UserMessageStore.cs | 111 ++++++++++++++++++ .../Messages/UserTextMessageRecord.cs | 27 +++++ .../AIManagementSettingDefinitionProvider.cs | 22 ++++ .../Settings/AIManagementSettingNames.cs | 10 ++ .../AIManagementDbContext.cs | 6 +- ...nagementDbContextModelBuilderExtensions.cs | 16 +++ ...bpAIManagementEntityFrameworkCoreModule.cs | 5 +- .../EfCoreUserTextMessageRecordRepository.cs | 31 +++++ .../IAIManagementDbContext.cs | 6 +- 18 files changed, 347 insertions(+), 5 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserMessageRecordConsts.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecordConsts.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/IUserTextMessageRecordRepository.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageRecord.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageStore.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecord.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingDefinitionProvider.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingNames.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreUserTextMessageRecordRepository.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN.Abp.AIManagement.Domain.Shared.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN.Abp.AIManagement.Domain.Shared.csproj index 148c75c26..9f7983f9b 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN.Abp.AIManagement.Domain.Shared.csproj +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN.Abp.AIManagement.Domain.Shared.csproj @@ -14,6 +14,11 @@ + + + + + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json index a9c8dcc3f..d29ec98c1 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json @@ -1,5 +1,7 @@ { "culture": "en", "texts": { + "DisplayName:MaxLatestHistoryMessagesToKeep": "Carry the recent conversation records", + "Description:MaxLatestHistoryMessagesToKeep": "When a user initiates a conversation with a large model, the upper limit of the user's recent conversation history that is carried along." } } \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json index c5ad81326..6eb63499b 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json @@ -1,5 +1,7 @@ { "culture": "zh-Hans", "texts": { + "DisplayName:MaxLatestHistoryMessagesToKeep": "携带最近对话记录", + "Description:MaxLatestHistoryMessagesToKeep": "用户发起与大模型对话时,携带用户最近对话记录的上限." } } \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserMessageRecordConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserMessageRecordConsts.cs new file mode 100644 index 000000000..e51d5365a --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserMessageRecordConsts.cs @@ -0,0 +1,5 @@ +namespace LINGYUN.Abp.AIManagement.Messages; +public static class UserMessageRecordConsts +{ + public static int MaxConversationIdLength { get; set; } = 64; +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecordConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecordConsts.cs new file mode 100644 index 000000000..184b93ab0 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecordConsts.cs @@ -0,0 +1,5 @@ +namespace LINGYUN.Abp.AIManagement.Messages; +public static class UserTextMessageRecordConsts +{ + public static int MaxContentLength { get; set; } = 1024; +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj index 3a58fe260..2cc7884dc 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj @@ -18,6 +18,7 @@ + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs new file mode 100644 index 000000000..1993a46f5 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs @@ -0,0 +1,15 @@ +using LINGYUN.Abp.AI.Models; +using LINGYUN.Abp.AIManagement.Messages; +using Riok.Mapperly.Abstractions; +using Volo.Abp.Mapperly; +using Volo.Abp.ObjectExtending; + +namespace LINGYUN.Abp.AIManagement; + +[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] +[MapExtraProperties(DefinitionChecks = MappingPropertyDefinitionChecks.None)] +public partial class UserTextMessageRecordToUserTextMessageMapper : MapperBase +{ + public override partial UserTextMessage Map(UserTextMessageRecord source); + public override partial void Map(UserTextMessageRecord source, UserTextMessage destination); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/IUserTextMessageRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/IUserTextMessageRecordRepository.cs new file mode 100644 index 000000000..6ecb5ab8a --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/IUserTextMessageRecordRepository.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace LINGYUN.Abp.AIManagement.Messages; +public interface IUserTextMessageRecordRepository : IBasicRepository +{ + Task> GetHistoryMessagesAsync( + string conversationId, + int maxResultCount = 0, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageRecord.cs new file mode 100644 index 000000000..559a4f189 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageRecord.cs @@ -0,0 +1,69 @@ +using LINGYUN.Abp.AIManagement.Workspaces; +using System; +using Volo.Abp; +using Volo.Abp.Data; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.AIManagement.Messages; +public abstract class UserMessageRecord : AuditedAggregateRoot, IMultiTenant +{ + public Guid? TenantId { get; private set; } + + public string Workspace { get; private set; } + + public string? ConversationId { get; private set; } + + public string? ReplyMessage { get; private set; } + + protected UserMessageRecord() + { + ExtraProperties = new ExtraPropertyDictionary(); + this.SetDefaultsForExtraProperties(); + } + + public UserMessageRecord( + Guid id, + string workspace, + Guid? tenantId = null) + : base(id) + { + TenantId = tenantId; + Workspace = workspace; + } + + public virtual UserMessageRecord WithConversationId(string conversationId) + { + ConversationId = Check.NotNullOrWhiteSpace(conversationId, nameof(conversationId), UserMessageRecordConsts.MaxConversationIdLength); + return this; + } + + public virtual UserMessageRecord WithReply(string replyMessage) + { + ReplyMessage = replyMessage; + return this; + } + + public void Patch(UserMessageRecord otherMessage) + { + if (ConversationId != otherMessage.ConversationId) + { + ConversationId = otherMessage.ConversationId; + } + + if (ReplyMessage != otherMessage.ReplyMessage) + { + ReplyMessage = otherMessage.ReplyMessage; + } + + if (!this.HasSameExtraProperties(otherMessage)) + { + ExtraProperties.Clear(); + + foreach (var property in otherMessage.ExtraProperties) + { + ExtraProperties.Add(property.Key, property.Value); + } + } + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageStore.cs new file mode 100644 index 000000000..04dd720a3 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageStore.cs @@ -0,0 +1,111 @@ +using LINGYUN.Abp.AI.Messages; +using LINGYUN.Abp.AI.Models; +using LINGYUN.Abp.AIManagement.Settings; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; +using Volo.Abp.MultiTenancy; +using Volo.Abp.ObjectMapping; +using Volo.Abp.Settings; + +namespace LINGYUN.Abp.AIManagement.Messages; + +[Dependency(ReplaceServices = true)] +public class UserMessageStore : IUserMessageStore, ITransientDependency +{ + private readonly ICurrentTenant _currentTenant; + private readonly IGuidGenerator _guidGenerator; + private readonly ISettingProvider _settingProvider; + private readonly IObjectMapper _objectMapper; + private readonly IUserTextMessageRecordRepository _messageRecordRepository; + + public UserMessageStore( + ICurrentTenant currentTenant, + IGuidGenerator guidGenerator, + ISettingProvider settingProvider, + IObjectMapper objectMapper, + IUserTextMessageRecordRepository messageRecordRepository) + { + _currentTenant = currentTenant; + _guidGenerator = guidGenerator; + _settingProvider = settingProvider; + _objectMapper = objectMapper; + _messageRecordRepository = messageRecordRepository; + } + + public async virtual Task> GetHistoryMessagesAsync(string conversationId) + { + var maxLatestHistoryMessagesToKeep = await _settingProvider.GetAsync(AIManagementSettingNames.UserMessage.MaxLatestHistoryMessagesToKeep, 0); + if (maxLatestHistoryMessagesToKeep < 1) + { + return Array.Empty(); + } + + var userTextMessages = await _messageRecordRepository.GetHistoryMessagesAsync(conversationId, maxLatestHistoryMessagesToKeep); + + return _objectMapper.Map, IEnumerable>(userTextMessages); + } + + public async virtual Task SaveMessageAsync(UserMessage message) + { + var messageId = message.Id; + if (messageId.IsNullOrWhiteSpace()) + { + messageId = _guidGenerator.Create().ToString(); + message.WithMessageId(messageId); + } + + await StoreMessageAsync(Guid.Parse(messageId), message); + + return messageId; + } + + protected async virtual Task StoreMessageAsync(Guid messageId, UserMessage message) + { + switch (message) + { + case UserTextMessage textMessage: + await StoreUserTextMessageAsync(messageId, textMessage); + break; + } + } + + protected async virtual Task StoreUserTextMessageAsync(Guid messageId, UserTextMessage textMessage) + { + var textMessageRecord = await _messageRecordRepository.FindAsync(messageId); + if (textMessageRecord == null) + { + textMessageRecord = new UserTextMessageRecord( + messageId, + textMessage.Workspace, + textMessage.Content, + _currentTenant.Id); + + UpdateUserMessageRecord(textMessageRecord, textMessage); + + await _messageRecordRepository.InsertAsync(textMessageRecord); + } + else + { + textMessageRecord.WithContent(textMessage.Content); + + UpdateUserMessageRecord(textMessageRecord, textMessage); + + await _messageRecordRepository.UpdateAsync(textMessageRecord); + } + } + + private static void UpdateUserMessageRecord(UserMessageRecord messageRecord, UserMessage message) + { + if (!message.ConversationId.IsNullOrWhiteSpace()) + { + messageRecord.WithConversationId(message.ConversationId); + } + if (!message.ReplyMessage.IsNullOrWhiteSpace()) + { + messageRecord.WithConversationId(message.ReplyMessage); + } + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecord.cs new file mode 100644 index 000000000..874790b9b --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecord.cs @@ -0,0 +1,27 @@ +using System; +using Volo.Abp; + +namespace LINGYUN.Abp.AIManagement.Messages; +public class UserTextMessageRecord : UserMessageRecord +{ + public string Content { get; private set; } + + public UserTextMessageRecord() + { + } + + public UserTextMessageRecord( + Guid id, + string workspace, + string content, + Guid? tenantId = null) : base(id, workspace, tenantId) + { + WithContent(content); + } + + public virtual UserTextMessageRecord WithContent(string content) + { + Content = Check.NotNullOrWhiteSpace(content, nameof(content), UserTextMessageRecordConsts.MaxContentLength); + return this; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingDefinitionProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingDefinitionProvider.cs new file mode 100644 index 000000000..c862256fc --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingDefinitionProvider.cs @@ -0,0 +1,22 @@ +using LINGYUN.Abp.AIManagement.Localization; +using Volo.Abp.Localization; +using Volo.Abp.Settings; + +namespace LINGYUN.Abp.AIManagement.Settings; +public class AIManagementSettingDefinitionProvider : SettingDefinitionProvider +{ + public override void Define(ISettingDefinitionContext context) + { + context.Add( + new SettingDefinition( + AIManagementSettingNames.UserMessage.MaxLatestHistoryMessagesToKeep, + defaultValue: "5", + displayName: L("DisplayName:MaxLatestHistoryMessagesToKeep"), + description: L("Description:MaxLatestHistoryMessagesToKeep"))); + } + + private static ILocalizableString L(string name) + { + return LocalizableString.Create(name); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingNames.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingNames.cs new file mode 100644 index 000000000..3cdb8355d --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingNames.cs @@ -0,0 +1,10 @@ +namespace LINGYUN.Abp.AIManagement.Settings; +public static class AIManagementSettingNames +{ + public const string Prefix = "Abp.AIManagement"; + + public static class UserMessage + { + public const string MaxLatestHistoryMessagesToKeep = Prefix + ".MaxLatestHistoryMessagesToKeep"; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs index 790174fb2..865365326 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs @@ -1,4 +1,5 @@ -using LINGYUN.Abp.AIManagement.Workspaces; +using LINGYUN.Abp.AIManagement.Messages; +using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.EntityFrameworkCore; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; @@ -8,7 +9,8 @@ namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; [ConnectionStringName(AbpAIManagementDbProperties.ConnectionStringName)] public class AIManagementDbContext : AbpDbContext, IAIManagementDbContext { - public DbSet Workspaces { get; set; } + public DbSet WorkspaceDefinitions { get; set; } + public DbSet UserTextMessageRecords { get; set; } public AIManagementDbContext( DbContextOptions options) : base(options) { diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs index 46de47d01..181054e54 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs @@ -1,4 +1,5 @@ using JetBrains.Annotations; +using LINGYUN.Abp.AIManagement.Messages; using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.EntityFrameworkCore; using Volo.Abp; @@ -12,6 +13,21 @@ public static class AIManagementDbContextModelBuilderExtensions { Check.NotNull(builder, nameof(builder)); + builder.Entity(b => + { + b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "UserTextMessages", AbpAIManagementDbProperties.DbSchema); + + b.ConfigureByConvention(); + + b.Property(x => x.Workspace).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxNameLength).IsRequired(); + b.Property(x => x.ConversationId).HasMaxLength(UserMessageRecordConsts.MaxConversationIdLength); + b.Property(x => x.Content).HasMaxLength(UserTextMessageRecordConsts.MaxContentLength).IsRequired(); + + b.HasIndex(x => new { x.TenantId, x.ConversationId }); + + b.ApplyObjectExtensionMappings(); + }); + if (builder.IsHostDatabase()) { builder.Entity(b => diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs index 870cad669..0371fb3fd 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs @@ -1,4 +1,5 @@ -using LINGYUN.Abp.AIManagement.Workspaces; +using LINGYUN.Abp.AIManagement.Messages; +using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.Modularity; @@ -16,6 +17,8 @@ public class AbpAIManagementEntityFrameworkCoreModule : AbpModule { options.AddDefaultRepositories(); + options.AddRepository(); + options.AddRepository(); }); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreUserTextMessageRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreUserTextMessageRecordRepository.cs new file mode 100644 index 000000000..6d1ddc943 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreUserTextMessageRecordRepository.cs @@ -0,0 +1,31 @@ +using LINGYUN.Abp.AIManagement.Messages; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; +public class EfCoreUserTextMessageRecordRepository : EfCoreRepository, IUserTextMessageRecordRepository +{ + public EfCoreUserTextMessageRecordRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public async virtual Task> GetHistoryMessagesAsync( + string conversationId, + int maxResultCount = 0, + CancellationToken cancellationToken = default) + { + return await (await GetQueryableAsync()) + .Where(x => x.ConversationId == conversationId) + .OrderByDescending(x => x.CreationTime) + .Take(maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs index 8a6f5fa6f..d253a4aa6 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs @@ -1,4 +1,5 @@ -using LINGYUN.Abp.AIManagement.Workspaces; +using LINGYUN.Abp.AIManagement.Messages; +using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.EntityFrameworkCore; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; @@ -8,5 +9,6 @@ namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; [ConnectionStringName(AbpAIManagementDbProperties.ConnectionStringName)] public interface IAIManagementDbContext : IEfCoreDbContext { - DbSet Workspaces { get; } + DbSet WorkspaceDefinitions { get; } + DbSet UserTextMessageRecords { get; } } From 3ab9926bfc08514ec57fef4acc1351391f37b447 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 16 Jan 2026 17:35:54 +0800 Subject: [PATCH 16/88] feat: Rename UserMessage to ChatMessage --- .../LINGYUN/Abp/AI/Agent/AgentService.cs | 82 +++++++++++-------- .../LINGYUN/Abp/AI/Agent/IAgentService.cs | 2 +- .../LINGYUN.Abp.AI.Core.csproj | 1 + .../LINGYUN/Abp/AI/Chats/IChatMessageStore.cs | 11 +++ .../Abp/AI/Chats/InMemoryChatMessageStore.cs | 52 ++++++++++++ .../Abp/AI/Messages/IUserMessageStore.cs | 11 --- .../AI/Messages/InMemoryUserMessageStore.cs | 46 ----------- .../LINGYUN/Abp/AI/Models/ChatMessage.cs | 53 ++++++++++++ .../LINGYUN/Abp/AI/Models/TextChatMessage.cs | 25 ++++++ .../LINGYUN/Abp/AI/Models/TokenUsageInfo.cs | 34 +++++++- .../LINGYUN/Abp/AI/Models/UserMessage.cs | 53 ------------ .../LINGYUN/Abp/AI/Models/UserTextMessage.cs | 20 ----- .../LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs | 3 +- .../Abp/AI/Tokens/InMemoryTokenUsageStore.cs | 31 ++----- .../Chats/ChatMessageRecordConsts.cs | 6 ++ .../Chats/TextChatMessageRecordConsts.cs | 5 ++ .../Messages/UserMessageRecordConsts.cs | 5 -- .../Messages/UserTextMessageRecordConsts.cs | 5 -- .../AbpAIManagementDomainMappers.cs | 6 +- .../AIManagement/Chats/ChatMessageRecord.cs | 57 +++++++++++++ .../ChatMessageStore.cs} | 41 +++++----- .../ITextChatMessageRecordRepository.cs} | 6 +- .../Chats/TextChatMessageRecord.cs | 31 +++++++ .../Messages/UserMessageRecord.cs | 69 ---------------- .../Messages/UserTextMessageRecord.cs | 27 ------ .../AIManagementSettingDefinitionProvider.cs | 2 +- .../Settings/AIManagementSettingNames.cs | 2 +- .../AIManagement/Tokens/TokenUsageRecord.cs} | 4 +- ...nagementDbContextModelBuilderExtensions.cs | 56 +++++++++---- ...bpAIManagementEntityFrameworkCoreModule.cs | 4 +- ... EfCoreTextChatMessageRecordRepository.cs} | 9 +- .../ChatRoleValueConverter.cs | 11 +++ 32 files changed, 423 insertions(+), 347 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TextChatMessage.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserTextMessage.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecordConsts.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecordConsts.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserMessageRecordConsts.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecordConsts.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs rename aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/{Messages/UserMessageStore.cs => Chats/ChatMessageStore.cs} (68%) rename aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/{Messages/IUserTextMessageRecordRepository.cs => Chats/ITextChatMessageRecordRepository.cs} (56%) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecord.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageRecord.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecord.cs rename aspnet-core/modules/ai/{LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessageInfo.cs => LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageRecord.cs} (50%) rename aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/{EfCoreUserTextMessageRecordRepository.cs => EfCoreTextChatMessageRecordRepository.cs} (70%) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/ValueConversions/ChatRoleValueConverter.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs index 17ea6b7a9..3d4f11183 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs @@ -1,6 +1,7 @@ -using LINGYUN.Abp.AI.Messages; +using LINGYUN.Abp.AI.Chats; using LINGYUN.Abp.AI.Models; using LINGYUN.Abp.AI.Tokens; +using Microsoft.Agents.AI; using Microsoft.Extensions.AI; using System; using System.Collections.Generic; @@ -8,24 +9,29 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; +using Volo.Abp.Timing; +using AIChatMessage = Microsoft.Extensions.AI.ChatMessage; namespace LINGYUN.Abp.AI.Agent; public class AgentService : IAgentService, IScopedDependency { + private readonly IClock _clock; private readonly IAgentFactory _agentFactory; private readonly ITokenUsageStore _tokenUsageStore; - private readonly IUserMessageStore _userMessageStore; + private readonly IChatMessageStore _chatMessageStore; public AgentService( + IClock clock, IAgentFactory agentFactory, ITokenUsageStore tokenUsageStore, - IUserMessageStore userMessageStore) + IChatMessageStore chatMessageStore) { + _clock = clock; _agentFactory = agentFactory; _tokenUsageStore = tokenUsageStore; - _userMessageStore = userMessageStore; + _chatMessageStore = chatMessageStore; } - public async virtual IAsyncEnumerable SendMessageAsync(UserMessage message) + public async virtual IAsyncEnumerable SendMessageAsync(Models.ChatMessage message) { var messages = await BuildChatMessages(message); @@ -33,62 +39,70 @@ public class AgentService : IAgentService, IScopedDependency var agentRunRes = agent.RunStreamingAsync(messages); + var tokenUsageInfo = new TokenUsageInfo(message.Workspace); var agentMessageBuilder = new StringBuilder(); - await foreach (var item in agentRunRes) + await foreach (var response in agentRunRes) { - agentMessageBuilder.Append(item); - yield return item.Text; - - await StoreTokenUsageInfo(message, item.RawRepresentation); + UpdateTokenUsageInfo(tokenUsageInfo, response); + agentMessageBuilder.Append(response.Text); + yield return response.Text; } - await StoreChatMessage(message, agentMessageBuilder.ToString()); + var messageId = await StoreChatMessage(message, agentMessageBuilder.ToString()); + + tokenUsageInfo.WithConversationId(message.ConversationId); + tokenUsageInfo.WithMessageId(messageId); + + Console.WriteLine(); + Console.WriteLine($"消耗Token: {tokenUsageInfo}"); + + await StoreTokenUsageInfo(tokenUsageInfo); } - protected virtual async Task> BuildChatMessages(UserMessage message) + protected virtual async Task> BuildChatMessages(Models.ChatMessage message) { - var messages = new List(); + var messages = new List(); if (!message.ConversationId.IsNullOrWhiteSpace()) { - var historyMessages = await _userMessageStore.GetHistoryMessagesAsync(message.ConversationId); + var historyMessages = await _chatMessageStore.GetHistoryMessagesAsync(message.ConversationId); foreach (var chatMessage in historyMessages) { - messages.Add(new ChatMessage(ChatRole.System, chatMessage.GetMessagePrompt())); + messages.Add(new AIChatMessage(ChatRole.System, chatMessage.GetMessagePrompt())); } } - messages.Add(new ChatMessage(ChatRole.User, message.GetMessagePrompt())); + messages.Add(new AIChatMessage(ChatRole.User, message.GetMessagePrompt())); return messages; } - protected async virtual Task StoreChatMessage(UserMessage message, string agentMessage) + protected async virtual Task StoreChatMessage(Models.ChatMessage message, string agentMessage) { - message.WithReply(agentMessage); + message.WithReply(agentMessage, _clock.Now); - await _userMessageStore.SaveMessageAsync(message); + return await _chatMessageStore.SaveMessageAsync(message); } - protected async virtual Task StoreTokenUsageInfo(UserMessage message, object? rawRepresentation) + protected async virtual Task StoreTokenUsageInfo(TokenUsageInfo tokenUsageInfo) { - if (rawRepresentation is ChatResponseUpdate update) + await _tokenUsageStore.SaveTokenUsageAsync(tokenUsageInfo); + } + + private static void UpdateTokenUsageInfo(TokenUsageInfo tokenUsageInfo, AgentRunResponseUpdate response) + { + if (response.RawRepresentation != null && + response.RawRepresentation is ChatResponseUpdate update) { - var tokenUsageInfos = update.Contents - .OfType() - .Where(usage => usage.Details != null) - .Select(usage => new TokenUsageInfo(message.Workspace) - { - TotalTokenCount = usage.Details.TotalTokenCount, - CachedInputTokenCount = usage.Details.CachedInputTokenCount, - InputTokenCount = usage.Details.InputTokenCount, - OutputTokenCount = usage.Details.OutputTokenCount, - ReasoningTokenCount = usage.Details.ReasoningTokenCount, - }); - - await _tokenUsageStore.SaveTokenUsagesAsync(tokenUsageInfos); + var usageContents = update.Contents.OfType(); + + tokenUsageInfo.InputTokenCount = usageContents.Max(x => x.Details.InputTokenCount); + tokenUsageInfo.OutputTokenCount = usageContents.Max(x => x.Details.OutputTokenCount); + tokenUsageInfo.TotalTokenCount = usageContents.Max(x => x.Details.TotalTokenCount); + tokenUsageInfo.ReasoningTokenCount = usageContents.Max(x => x.Details.ReasoningTokenCount); + tokenUsageInfo.CachedInputTokenCount = usageContents.Max(x => x.Details.CachedInputTokenCount); } } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentService.cs index 24ad9e19f..109f267d3 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentService.cs @@ -4,5 +4,5 @@ using System.Collections.Generic; namespace LINGYUN.Abp.AI.Agent; public interface IAgentService { - IAsyncEnumerable SendMessageAsync(UserMessage message); + IAsyncEnumerable SendMessageAsync(ChatMessage message); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj index 57d089b1c..4dc562c9c 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj @@ -22,6 +22,7 @@ + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs new file mode 100644 index 000000000..023f79b20 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs @@ -0,0 +1,11 @@ +using LINGYUN.Abp.AI.Models; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.AI.Chats; +public interface IChatMessageStore +{ + Task SaveMessageAsync(ChatMessage message); + + Task> GetHistoryMessagesAsync(string conversationId); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs new file mode 100644 index 000000000..293e57202 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs @@ -0,0 +1,52 @@ +using LINGYUN.Abp.AI.Models; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.AI.Chats; + +[Dependency(ServiceLifetime.Singleton, TryRegister = true)] +public class InMemoryChatMessageStore : IChatMessageStore +{ + private static readonly ConcurrentDictionary> _userMessageCache = new ConcurrentDictionary>(); + + public Task> GetHistoryMessagesAsync(string conversationId) + { + var messages = new List(); + + foreach (var userMessages in _userMessageCache.Values) + { + messages.AddRange(userMessages.Where(x => x.ConversationId == conversationId)); + } + + return Task.FromResult>( + messages + .OrderByDescending(x => x.CreatedAt) + .Take(5) + .OrderBy(x => x.CreatedAt)); + } + + public Task SaveMessageAsync(ChatMessage message) + { + var messageId = message.Id; + if (messageId.IsNullOrWhiteSpace()) + { + messageId = Guid.NewGuid().ToString(); + message.WithMessageId(messageId); + } + if (_userMessageCache.ContainsKey(messageId)) + { + _userMessageCache[messageId].Add(message); + } + else + { + _userMessageCache[messageId] = new List() { message }; + } + + return Task.FromResult(messageId); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs deleted file mode 100644 index b56406d5f..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs +++ /dev/null @@ -1,11 +0,0 @@ -using LINGYUN.Abp.AI.Models; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace LINGYUN.Abp.AI.Messages; -public interface IUserMessageStore -{ - Task SaveMessageAsync(UserMessage message); - - Task> GetHistoryMessagesAsync(string conversationId); -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs deleted file mode 100644 index a12cf1de4..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs +++ /dev/null @@ -1,46 +0,0 @@ -using LINGYUN.Abp.AI.Models; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Volo.Abp.DependencyInjection; - -namespace LINGYUN.Abp.AI.Messages; - -[Dependency(ServiceLifetime.Singleton, TryRegister = true)] -public class InMemoryUserMessageStore : IUserMessageStore -{ - private static readonly ConcurrentDictionary> _userMessageCache = new ConcurrentDictionary>(); - - public Task> GetHistoryMessagesAsync(string conversationId) - { - if (_userMessageCache.TryGetValue(conversationId, out var messages)) - { - return Task.FromResult(messages.Take(5)); - } - - return Task.FromResult>(Array.Empty()); - } - - public Task SaveMessageAsync(UserMessage message) - { - var messageId = message.Id; - if (messageId.IsNullOrWhiteSpace()) - { - messageId = Guid.NewGuid().ToString(); - message.WithMessageId(messageId); - } - if (_userMessageCache.ContainsKey(messageId)) - { - _userMessageCache[messageId].Add(message); - } - else - { - _userMessageCache[messageId] = new List() { message }; - } - - return Task.FromResult(messageId); - } -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs new file mode 100644 index 000000000..e173d053a --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs @@ -0,0 +1,53 @@ +using Microsoft.Extensions.AI; +using System; + +namespace LINGYUN.Abp.AI.Models; +public abstract class ChatMessage +{ + public string Workspace { get; } + + public string? Id { get; private set; } + + public string? ConversationId { get; private set; } + + public string? ReplyMessage { get; private set; } + + public DateTime? ReplyAt { get; private set; } + + public ChatRole Role { get; private set; } + + public DateTime CreatedAt { get; private set; } + protected ChatMessage( + string workspace, + ChatRole? role = null, + DateTime? createdAt = null) + { + Workspace = workspace; + Role = role ?? ChatRole.User; + CreatedAt = createdAt ?? DateTime.Now; + } + + public virtual ChatMessage WithMessageId(string id) + { + Id = id; + return this; + } + + public virtual ChatMessage WithConversationId(string conversationId) + { + ConversationId = conversationId; + return this; + } + + public virtual ChatMessage WithReply(string replyMessage, DateTime replyAt) + { + ReplyMessage = replyMessage; + ReplyAt = replyAt; + return this; + } + + public virtual string GetMessagePrompt() + { + return string.Empty; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TextChatMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TextChatMessage.cs new file mode 100644 index 000000000..20ea67e1c --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TextChatMessage.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.AI; +using System; + +namespace LINGYUN.Abp.AI.Models; +public class TextChatMessage : ChatMessage +{ + /// + /// 消息内容 + /// + public string Content { get; } + public TextChatMessage( + string workspace, + string content, + ChatRole? role = null, + DateTime? createdAt = null) + : base(workspace, role, createdAt) + { + Content = content; + } + + public override string GetMessagePrompt() + { + return Content; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs index 61c8e0457..08238d79b 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs @@ -1,7 +1,11 @@ -namespace LINGYUN.Abp.AI.Models; +using System.Text; + +namespace LINGYUN.Abp.AI.Models; public class TokenUsageInfo { public string Workspace { get; } + public string? MessageId { get; private set; } + public string? ConversationId { get; private set; } public long? InputTokenCount { get; set; } public long? OutputTokenCount { get; set; } public long? TotalTokenCount { get; set; } @@ -11,4 +15,32 @@ public class TokenUsageInfo { Workspace = workspace; } + public virtual TokenUsageInfo WithMessageId(string id) + { + MessageId = id; + return this; + } + + public virtual TokenUsageInfo WithConversationId(string? conversationId) + { + ConversationId = conversationId; + return this; + } + + public override string ToString() + { + var sb = new StringBuilder(); + + sb.AppendLine("---------------------- TokenUsage Begin ----------------------"); + sb.AppendLine($"====== Workspace - {Workspace}"); + sb.AppendLine($"====== MessageId - {MessageId}"); + sb.AppendLine($"====== ConversationId - {ConversationId}"); + sb.AppendLine($"====== InputTokenCount - {InputTokenCount}"); + sb.AppendLine($"====== OutputTokenCount - {OutputTokenCount}"); + sb.AppendLine($"====== TotalTokenCount - {TotalTokenCount}"); + sb.AppendLine($"====== ReasoningTokenCount - {ReasoningTokenCount}"); + sb.AppendLine("---------------------- TokenUsage End ----------------------"); + + return sb.ToString(); + } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs deleted file mode 100644 index 7681ae260..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace LINGYUN.Abp.AI.Models; -public abstract class UserMessage -{ - /// - /// 工作区 - /// - public string Workspace { get; } - /// - /// 消息Id - /// - /// - /// 在持久化设施处更新 - /// - public string? Id { get; private set; } - /// - /// 对话Id - /// - /// - /// 用于从客户端存储中持久化和检索聊天历史的唯一标识符,如果未指定则与AI对话时无上下文关联 - /// - public string? ConversationId { get; private set; } - /// - /// AI回复消息 - /// - public string ReplyMessage { get; private set; } - protected UserMessage(string workspace) - { - Workspace = workspace; - } - - public virtual UserMessage WithMessageId(string id) - { - Id = id; - return this; - } - - public virtual UserMessage WithConversationId(string conversationId) - { - ConversationId = conversationId; - return this; - } - - public virtual UserMessage WithReply(string replyMessage) - { - ReplyMessage = replyMessage; - return this; - } - - public virtual string GetMessagePrompt() - { - return string.Empty; - } -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserTextMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserTextMessage.cs deleted file mode 100644 index 55cb12ae6..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserTextMessage.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace LINGYUN.Abp.AI.Models; -public class UserTextMessage : UserMessage -{ - /// - /// 消息内容 - /// - public string Content { get; } - public UserTextMessage( - string workspace, - string content) - : base(workspace) - { - Content = content; - } - - public override string GetMessagePrompt() - { - return Content; - } -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs index 32b4d0718..dd44f988a 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs @@ -1,9 +1,8 @@ using LINGYUN.Abp.AI.Models; -using System.Collections.Generic; using System.Threading.Tasks; namespace LINGYUN.Abp.AI.Tokens; public interface ITokenUsageStore { - Task SaveTokenUsagesAsync(IEnumerable usageInfos); + Task SaveTokenUsageAsync(TokenUsageInfo tokenUsageInfo); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs index 9c7862de8..9f492b8b2 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs @@ -2,7 +2,6 @@ using Microsoft.Extensions.DependencyInjection; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; @@ -11,31 +10,19 @@ namespace LINGYUN.Abp.AI.Tokens; [Dependency(ServiceLifetime.Singleton, TryRegister = true)] public class InMemoryTokenUsageStore : ITokenUsageStore { - private static readonly ConcurrentDictionary _tokenUsageCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary> _tokenUsageCache = new ConcurrentDictionary>(); - public Task SaveTokenUsagesAsync(IEnumerable usageInfos) + public Task SaveTokenUsageAsync(TokenUsageInfo tokenUsageInfo) { - foreach (var usageInfo in usageInfos.GroupBy(x => x.Workspace)) + if (_tokenUsageCache.TryGetValue(tokenUsageInfo.Workspace, out var tokenUsageInfos)) { - var tokenUsageInfo = new TokenUsageInfo(usageInfo.Key) - { - InputTokenCount = usageInfo.Sum(x => x.InputTokenCount), - TotalTokenCount = usageInfo.Sum(x => x.TotalTokenCount), - OutputTokenCount = usageInfo.Sum(x => x.OutputTokenCount), - ReasoningTokenCount = usageInfo.Sum(x => x.ReasoningTokenCount), - CachedInputTokenCount = usageInfo.Sum(x => x.CachedInputTokenCount), - }; - - if (!_tokenUsageCache.ContainsKey(usageInfo.Key)) - { - _tokenUsageCache.TryAdd(usageInfo.Key, tokenUsageInfo); - } - else - { - _tokenUsageCache[usageInfo.Key] = tokenUsageInfo; - } + tokenUsageInfos.Add(tokenUsageInfo); } - + else + { + _tokenUsageCache.TryAdd(tokenUsageInfo.Workspace, [tokenUsageInfo]); + } + return Task.CompletedTask; } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecordConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecordConsts.cs new file mode 100644 index 000000000..3af24e28f --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecordConsts.cs @@ -0,0 +1,6 @@ +namespace LINGYUN.Abp.AIManagement.Chats; +public static class ChatMessageRecordConsts +{ + public static int MaxConversationIdLength { get; set; } = 64; + public static int MaxChatRoleLength { get; set; } = 20; +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecordConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecordConsts.cs new file mode 100644 index 000000000..a603c0e87 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecordConsts.cs @@ -0,0 +1,5 @@ +namespace LINGYUN.Abp.AIManagement.Chats; +public static class TextChatMessageRecordConsts +{ + public static int MaxContentLength { get; set; } = 1024; +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserMessageRecordConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserMessageRecordConsts.cs deleted file mode 100644 index e51d5365a..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserMessageRecordConsts.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace LINGYUN.Abp.AIManagement.Messages; -public static class UserMessageRecordConsts -{ - public static int MaxConversationIdLength { get; set; } = 64; -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecordConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecordConsts.cs deleted file mode 100644 index 184b93ab0..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecordConsts.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace LINGYUN.Abp.AIManagement.Messages; -public static class UserTextMessageRecordConsts -{ - public static int MaxContentLength { get; set; } = 1024; -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs index 1993a46f5..75b113067 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs @@ -8,8 +8,8 @@ namespace LINGYUN.Abp.AIManagement; [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] [MapExtraProperties(DefinitionChecks = MappingPropertyDefinitionChecks.None)] -public partial class UserTextMessageRecordToUserTextMessageMapper : MapperBase +public partial class UserTextMessageRecordToUserTextMessageMapper : MapperBase { - public override partial UserTextMessage Map(UserTextMessageRecord source); - public override partial void Map(UserTextMessageRecord source, UserTextMessage destination); + public override partial TextChatMessage Map(UserTextMessageRecord source); + public override partial void Map(UserTextMessageRecord source, TextChatMessage destination); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs new file mode 100644 index 000000000..2b08d4c67 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs @@ -0,0 +1,57 @@ +using Microsoft.Extensions.AI; +using System; +using Volo.Abp; +using Volo.Abp.Data; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.AIManagement.Chats; +public abstract class ChatMessageRecord : AuditedAggregateRoot, IMultiTenant +{ + public Guid? TenantId { get; private set; } + + public string Workspace { get; private set; } + + public ChatRole Role { get; private set; } + + public DateTime CreatedAt { get; private set; } + + public string? ConversationId { get; private set; } + + public string? ReplyMessage { get; private set; } + + public DateTime? ReplyAt { get; private set; } + + protected ChatMessageRecord() + { + ExtraProperties = new ExtraPropertyDictionary(); + this.SetDefaultsForExtraProperties(); + } + + public ChatMessageRecord( + Guid id, + string workspace, + ChatRole role, + DateTime createdAt, + Guid? tenantId = null) + : base(id) + { + Workspace = workspace; + Role = role; + CreatedAt = createdAt; + TenantId = tenantId; + } + + public virtual ChatMessageRecord SetConversationId(string conversationId) + { + ConversationId = Check.NotNullOrWhiteSpace(conversationId, nameof(conversationId), ChatMessageRecordConsts.MaxConversationIdLength); + return this; + } + + public virtual ChatMessageRecord SetReply(string replyMessage, DateTime? replyAt) + { + ReplyMessage = replyMessage; + ReplyAt = replyAt; + return this; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs similarity index 68% rename from aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageStore.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs index 04dd720a3..feb7b9f80 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs @@ -1,4 +1,4 @@ -using LINGYUN.Abp.AI.Messages; +using LINGYUN.Abp.AI.Chats; using LINGYUN.Abp.AI.Models; using LINGYUN.Abp.AIManagement.Settings; using System; @@ -10,23 +10,23 @@ using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; using Volo.Abp.Settings; -namespace LINGYUN.Abp.AIManagement.Messages; +namespace LINGYUN.Abp.AIManagement.Chats; [Dependency(ReplaceServices = true)] -public class UserMessageStore : IUserMessageStore, ITransientDependency +public class ChatMessageStore : IChatMessageStore, ITransientDependency { private readonly ICurrentTenant _currentTenant; private readonly IGuidGenerator _guidGenerator; private readonly ISettingProvider _settingProvider; private readonly IObjectMapper _objectMapper; - private readonly IUserTextMessageRecordRepository _messageRecordRepository; + private readonly ITextChatMessageRecordRepository _messageRecordRepository; - public UserMessageStore( + public ChatMessageStore( ICurrentTenant currentTenant, IGuidGenerator guidGenerator, ISettingProvider settingProvider, IObjectMapper objectMapper, - IUserTextMessageRecordRepository messageRecordRepository) + ITextChatMessageRecordRepository messageRecordRepository) { _currentTenant = currentTenant; _guidGenerator = guidGenerator; @@ -35,20 +35,21 @@ public class UserMessageStore : IUserMessageStore, ITransientDependency _messageRecordRepository = messageRecordRepository; } - public async virtual Task> GetHistoryMessagesAsync(string conversationId) + public async virtual Task> GetHistoryMessagesAsync(string conversationId) { - var maxLatestHistoryMessagesToKeep = await _settingProvider.GetAsync(AIManagementSettingNames.UserMessage.MaxLatestHistoryMessagesToKeep, 0); + var maxLatestHistoryMessagesToKeep = await _settingProvider.GetAsync( + AIManagementSettingNames.ChatMessage.MaxLatestHistoryMessagesToKeep, 0); if (maxLatestHistoryMessagesToKeep < 1) { - return Array.Empty(); + return Array.Empty(); } var userTextMessages = await _messageRecordRepository.GetHistoryMessagesAsync(conversationId, maxLatestHistoryMessagesToKeep); - return _objectMapper.Map, IEnumerable>(userTextMessages); + return _objectMapper.Map, IEnumerable>(userTextMessages); } - public async virtual Task SaveMessageAsync(UserMessage message) + public async virtual Task SaveMessageAsync(ChatMessage message) { var messageId = message.Id; if (messageId.IsNullOrWhiteSpace()) @@ -62,25 +63,27 @@ public class UserMessageStore : IUserMessageStore, ITransientDependency return messageId; } - protected async virtual Task StoreMessageAsync(Guid messageId, UserMessage message) + protected async virtual Task StoreMessageAsync(Guid messageId, ChatMessage message) { switch (message) { - case UserTextMessage textMessage: + case TextChatMessage textMessage: await StoreUserTextMessageAsync(messageId, textMessage); break; } } - protected async virtual Task StoreUserTextMessageAsync(Guid messageId, UserTextMessage textMessage) + protected async virtual Task StoreUserTextMessageAsync(Guid messageId, TextChatMessage textMessage) { var textMessageRecord = await _messageRecordRepository.FindAsync(messageId); if (textMessageRecord == null) { - textMessageRecord = new UserTextMessageRecord( + textMessageRecord = new TextChatMessageRecord( messageId, textMessage.Workspace, textMessage.Content, + textMessage.Role, + textMessage.CreatedAt, _currentTenant.Id); UpdateUserMessageRecord(textMessageRecord, textMessage); @@ -89,7 +92,7 @@ public class UserMessageStore : IUserMessageStore, ITransientDependency } else { - textMessageRecord.WithContent(textMessage.Content); + textMessageRecord.SetContent(textMessage.Content); UpdateUserMessageRecord(textMessageRecord, textMessage); @@ -97,15 +100,15 @@ public class UserMessageStore : IUserMessageStore, ITransientDependency } } - private static void UpdateUserMessageRecord(UserMessageRecord messageRecord, UserMessage message) + private static void UpdateUserMessageRecord(ChatMessageRecord messageRecord, ChatMessage message) { if (!message.ConversationId.IsNullOrWhiteSpace()) { - messageRecord.WithConversationId(message.ConversationId); + messageRecord.SetConversationId(message.ConversationId); } if (!message.ReplyMessage.IsNullOrWhiteSpace()) { - messageRecord.WithConversationId(message.ReplyMessage); + messageRecord.SetReply(message.ReplyMessage, message.ReplyAt); } } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/IUserTextMessageRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ITextChatMessageRecordRepository.cs similarity index 56% rename from aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/IUserTextMessageRecordRepository.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ITextChatMessageRecordRepository.cs index 6ecb5ab8a..ad641cb43 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/IUserTextMessageRecordRepository.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ITextChatMessageRecordRepository.cs @@ -4,10 +4,10 @@ using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; -namespace LINGYUN.Abp.AIManagement.Messages; -public interface IUserTextMessageRecordRepository : IBasicRepository +namespace LINGYUN.Abp.AIManagement.Chats; +public interface ITextChatMessageRecordRepository : IBasicRepository { - Task> GetHistoryMessagesAsync( + Task> GetHistoryMessagesAsync( string conversationId, int maxResultCount = 0, CancellationToken cancellationToken = default); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecord.cs new file mode 100644 index 000000000..16efa2653 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecord.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.AI; +using System; +using Volo.Abp; + +namespace LINGYUN.Abp.AIManagement.Chats; +public class TextChatMessageRecord : ChatMessageRecord +{ + public string Content { get; private set; } + + public TextChatMessageRecord() + { + } + + public TextChatMessageRecord( + Guid id, + string workspace, + string content, + ChatRole role, + DateTime createdAt, + Guid? tenantId = null) + : base(id, workspace, role, createdAt, tenantId) + { + SetContent(content); + } + + public virtual TextChatMessageRecord SetContent(string content) + { + Content = Check.NotNullOrWhiteSpace(content, nameof(content), TextChatMessageRecordConsts.MaxContentLength); + return this; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageRecord.cs deleted file mode 100644 index 559a4f189..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageRecord.cs +++ /dev/null @@ -1,69 +0,0 @@ -using LINGYUN.Abp.AIManagement.Workspaces; -using System; -using Volo.Abp; -using Volo.Abp.Data; -using Volo.Abp.Domain.Entities.Auditing; -using Volo.Abp.MultiTenancy; - -namespace LINGYUN.Abp.AIManagement.Messages; -public abstract class UserMessageRecord : AuditedAggregateRoot, IMultiTenant -{ - public Guid? TenantId { get; private set; } - - public string Workspace { get; private set; } - - public string? ConversationId { get; private set; } - - public string? ReplyMessage { get; private set; } - - protected UserMessageRecord() - { - ExtraProperties = new ExtraPropertyDictionary(); - this.SetDefaultsForExtraProperties(); - } - - public UserMessageRecord( - Guid id, - string workspace, - Guid? tenantId = null) - : base(id) - { - TenantId = tenantId; - Workspace = workspace; - } - - public virtual UserMessageRecord WithConversationId(string conversationId) - { - ConversationId = Check.NotNullOrWhiteSpace(conversationId, nameof(conversationId), UserMessageRecordConsts.MaxConversationIdLength); - return this; - } - - public virtual UserMessageRecord WithReply(string replyMessage) - { - ReplyMessage = replyMessage; - return this; - } - - public void Patch(UserMessageRecord otherMessage) - { - if (ConversationId != otherMessage.ConversationId) - { - ConversationId = otherMessage.ConversationId; - } - - if (ReplyMessage != otherMessage.ReplyMessage) - { - ReplyMessage = otherMessage.ReplyMessage; - } - - if (!this.HasSameExtraProperties(otherMessage)) - { - ExtraProperties.Clear(); - - foreach (var property in otherMessage.ExtraProperties) - { - ExtraProperties.Add(property.Key, property.Value); - } - } - } -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecord.cs deleted file mode 100644 index 874790b9b..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecord.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using Volo.Abp; - -namespace LINGYUN.Abp.AIManagement.Messages; -public class UserTextMessageRecord : UserMessageRecord -{ - public string Content { get; private set; } - - public UserTextMessageRecord() - { - } - - public UserTextMessageRecord( - Guid id, - string workspace, - string content, - Guid? tenantId = null) : base(id, workspace, tenantId) - { - WithContent(content); - } - - public virtual UserTextMessageRecord WithContent(string content) - { - Content = Check.NotNullOrWhiteSpace(content, nameof(content), UserTextMessageRecordConsts.MaxContentLength); - return this; - } -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingDefinitionProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingDefinitionProvider.cs index c862256fc..e36886420 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingDefinitionProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingDefinitionProvider.cs @@ -9,7 +9,7 @@ public class AIManagementSettingDefinitionProvider : SettingDefinitionProvider { context.Add( new SettingDefinition( - AIManagementSettingNames.UserMessage.MaxLatestHistoryMessagesToKeep, + AIManagementSettingNames.ChatMessage.MaxLatestHistoryMessagesToKeep, defaultValue: "5", displayName: L("DisplayName:MaxLatestHistoryMessagesToKeep"), description: L("Description:MaxLatestHistoryMessagesToKeep"))); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingNames.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingNames.cs index 3cdb8355d..5e87515f6 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingNames.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingNames.cs @@ -3,7 +3,7 @@ public static class AIManagementSettingNames { public const string Prefix = "Abp.AIManagement"; - public static class UserMessage + public static class ChatMessage { public const string MaxLatestHistoryMessagesToKeep = Prefix + ".MaxLatestHistoryMessagesToKeep"; } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessageInfo.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageRecord.cs similarity index 50% rename from aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessageInfo.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageRecord.cs index 279c9ba9b..fedf9a194 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessageInfo.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageRecord.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace LINGYUN.Abp.AI.Models; -internal class ChatMessageInfo +namespace LINGYUN.Abp.AIManagement.Tokens; +public class TokenUsageRecord { } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs index 181054e54..90a2d3fc9 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs @@ -1,5 +1,6 @@ using JetBrains.Annotations; -using LINGYUN.Abp.AIManagement.Messages; +using LINGYUN.Abp.AIManagement.Chats; +using LINGYUN.Abp.AIManagement.EntityFrameworkCore.ValueConversions; using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.EntityFrameworkCore; using Volo.Abp; @@ -13,15 +14,24 @@ public static class AIManagementDbContextModelBuilderExtensions { Check.NotNull(builder, nameof(builder)); - builder.Entity(b => + builder.Entity(b => { - b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "UserTextMessages", AbpAIManagementDbProperties.DbSchema); + b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "TextChatMessages", AbpAIManagementDbProperties.DbSchema); b.ConfigureByConvention(); - b.Property(x => x.Workspace).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxNameLength).IsRequired(); - b.Property(x => x.ConversationId).HasMaxLength(UserMessageRecordConsts.MaxConversationIdLength); - b.Property(x => x.Content).HasMaxLength(UserTextMessageRecordConsts.MaxContentLength).IsRequired(); + b.Property(x => x.Workspace) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxNameLength) + .IsRequired(); + b.Property(x => x.Role) + .HasMaxLength(ChatMessageRecordConsts.MaxChatRoleLength) + .HasConversion(new ChatRoleValueConverter()) + .IsRequired(); + b.Property(x => x.ConversationId) + .HasMaxLength(ChatMessageRecordConsts.MaxConversationIdLength); + b.Property(x => x.Content) + .HasMaxLength(TextChatMessageRecordConsts.MaxContentLength) + .IsRequired(); b.HasIndex(x => new { x.TenantId, x.ConversationId }); @@ -36,16 +46,30 @@ public static class AIManagementDbContextModelBuilderExtensions b.ConfigureByConvention(); - b.Property(x => x.Name).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxNameLength).IsRequired(); - b.Property(x => x.Provider).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxProviderLength).IsRequired(); - b.Property(x => x.ModelName).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxModelNameLength).IsRequired(); - b.Property(x => x.DisplayName).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxDisplayNameLength).IsRequired(); - b.Property(x => x.Description).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxDescriptionLength); - b.Property(x => x.ApiKey).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); - b.Property(x => x.ApiBaseUrl).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); - b.Property(x => x.SystemPrompt).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxSystemPromptLength); - b.Property(x => x.Instructions).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxInstructionsLength); - b.Property(x => x.StateCheckers).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxStateCheckersLength); + b.Property(x => x.Name) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxNameLength) + .IsRequired(); + b.Property(x => x.Provider) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxProviderLength) + .IsRequired(); + b.Property(x => x.ModelName) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxModelNameLength) + .IsRequired(); + b.Property(x => x.DisplayName) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxDisplayNameLength) + .IsRequired(); + b.Property(x => x.Description) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxDescriptionLength); + b.Property(x => x.ApiKey) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); + b.Property(x => x.ApiBaseUrl) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); + b.Property(x => x.SystemPrompt) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxSystemPromptLength); + b.Property(x => x.Instructions) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxInstructionsLength); + b.Property(x => x.StateCheckers) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxStateCheckersLength); b.HasIndex(x => new { x.Name }).IsUnique(); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs index 0371fb3fd..f372b441f 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs @@ -1,4 +1,4 @@ -using LINGYUN.Abp.AIManagement.Messages; +using LINGYUN.Abp.AIManagement.Chats; using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore; @@ -17,7 +17,7 @@ public class AbpAIManagementEntityFrameworkCoreModule : AbpModule { options.AddDefaultRepositories(); - options.AddRepository(); + options.AddRepository(); options.AddRepository(); }); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreUserTextMessageRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTextChatMessageRecordRepository.cs similarity index 70% rename from aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreUserTextMessageRecordRepository.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTextChatMessageRecordRepository.cs index 6d1ddc943..3377e6304 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreUserTextMessageRecordRepository.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTextChatMessageRecordRepository.cs @@ -1,4 +1,4 @@ -using LINGYUN.Abp.AIManagement.Messages; +using LINGYUN.Abp.AIManagement.Chats; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; @@ -9,15 +9,15 @@ using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; -public class EfCoreUserTextMessageRecordRepository : EfCoreRepository, IUserTextMessageRecordRepository +public class EfCoreTextChatMessageRecordRepository : EfCoreRepository, ITextChatMessageRecordRepository { - public EfCoreUserTextMessageRecordRepository( + public EfCoreTextChatMessageRecordRepository( IDbContextProvider dbContextProvider) : base(dbContextProvider) { } - public async virtual Task> GetHistoryMessagesAsync( + public async virtual Task> GetHistoryMessagesAsync( string conversationId, int maxResultCount = 0, CancellationToken cancellationToken = default) @@ -26,6 +26,7 @@ public class EfCoreUserTextMessageRecordRepository : EfCoreRepository x.ConversationId == conversationId) .OrderByDescending(x => x.CreationTime) .Take(maxResultCount) + .OrderBy(x => x.CreationTime) .ToListAsync(GetCancellationToken(cancellationToken)); } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/ValueConversions/ChatRoleValueConverter.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/ValueConversions/ChatRoleValueConverter.cs new file mode 100644 index 000000000..c4a1faf2c --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/ValueConversions/ChatRoleValueConverter.cs @@ -0,0 +1,11 @@ +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Microsoft.Extensions.AI; + +namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore.ValueConversions; +public class ChatRoleValueConverter(ConverterMappingHints? mappingHints = null) : ValueConverter( + value => value.Value, + value => new ChatRole(value), + mappingHints + ) +{ +} From f97d5171dcfaba456a5d95bec01cc9b33a566ee4 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 16 Jan 2026 17:43:08 +0800 Subject: [PATCH 17/88] feat: Reset ConversationId type to Guid --- .../LINGYUN/Abp/AI/Agent/AgentService.cs | 8 +++++--- .../LINGYUN/Abp/AI/Chats/IChatMessageStore.cs | 3 ++- .../LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs | 2 +- .../LINGYUN/Abp/AI/Models/ChatMessage.cs | 4 ++-- .../LINGYUN/Abp/AI/Models/TokenUsageInfo.cs | 7 ++++--- .../Abp/AIManagement/Chats/ChatMessageRecordConsts.cs | 1 - .../LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs | 7 +++---- .../LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs | 2 +- .../Chats/ITextChatMessageRecordRepository.cs | 2 +- .../AIManagementDbContextModelBuilderExtensions.cs | 2 -- .../EfCoreTextChatMessageRecordRepository.cs | 2 +- 11 files changed, 20 insertions(+), 20 deletions(-) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs index 3d4f11183..6fe8db044 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs @@ -54,8 +54,10 @@ public class AgentService : IAgentService, IScopedDependency tokenUsageInfo.WithConversationId(message.ConversationId); tokenUsageInfo.WithMessageId(messageId); +#if DEBUG Console.WriteLine(); - Console.WriteLine($"消耗Token: {tokenUsageInfo}"); + Console.WriteLine(tokenUsageInfo); +#endif await StoreTokenUsageInfo(tokenUsageInfo); } @@ -64,9 +66,9 @@ public class AgentService : IAgentService, IScopedDependency { var messages = new List(); - if (!message.ConversationId.IsNullOrWhiteSpace()) + if (message.ConversationId.HasValue) { - var historyMessages = await _chatMessageStore.GetHistoryMessagesAsync(message.ConversationId); + var historyMessages = await _chatMessageStore.GetHistoryMessagesAsync(message.ConversationId.Value); foreach (var chatMessage in historyMessages) { diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs index 023f79b20..886a0e953 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs @@ -1,4 +1,5 @@ using LINGYUN.Abp.AI.Models; +using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -7,5 +8,5 @@ public interface IChatMessageStore { Task SaveMessageAsync(ChatMessage message); - Task> GetHistoryMessagesAsync(string conversationId); + Task> GetHistoryMessagesAsync(Guid conversationId); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs index 293e57202..b81ccef2b 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs @@ -14,7 +14,7 @@ public class InMemoryChatMessageStore : IChatMessageStore { private static readonly ConcurrentDictionary> _userMessageCache = new ConcurrentDictionary>(); - public Task> GetHistoryMessagesAsync(string conversationId) + public Task> GetHistoryMessagesAsync(Guid conversationId) { var messages = new List(); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs index e173d053a..46770a3f5 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs @@ -8,7 +8,7 @@ public abstract class ChatMessage public string? Id { get; private set; } - public string? ConversationId { get; private set; } + public Guid? ConversationId { get; private set; } public string? ReplyMessage { get; private set; } @@ -33,7 +33,7 @@ public abstract class ChatMessage return this; } - public virtual ChatMessage WithConversationId(string conversationId) + public virtual ChatMessage WithConversationId(Guid conversationId) { ConversationId = conversationId; return this; diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs index 08238d79b..770454a3a 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs @@ -1,11 +1,12 @@ -using System.Text; +using System; +using System.Text; namespace LINGYUN.Abp.AI.Models; public class TokenUsageInfo { public string Workspace { get; } public string? MessageId { get; private set; } - public string? ConversationId { get; private set; } + public Guid? ConversationId { get; private set; } public long? InputTokenCount { get; set; } public long? OutputTokenCount { get; set; } public long? TotalTokenCount { get; set; } @@ -21,7 +22,7 @@ public class TokenUsageInfo return this; } - public virtual TokenUsageInfo WithConversationId(string? conversationId) + public virtual TokenUsageInfo WithConversationId(Guid? conversationId) { ConversationId = conversationId; return this; diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecordConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecordConsts.cs index 3af24e28f..34283daae 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecordConsts.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecordConsts.cs @@ -1,6 +1,5 @@ namespace LINGYUN.Abp.AIManagement.Chats; public static class ChatMessageRecordConsts { - public static int MaxConversationIdLength { get; set; } = 64; public static int MaxChatRoleLength { get; set; } = 20; } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs index 2b08d4c67..91bd1c0f1 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs @@ -1,6 +1,5 @@ using Microsoft.Extensions.AI; using System; -using Volo.Abp; using Volo.Abp.Data; using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; @@ -16,7 +15,7 @@ public abstract class ChatMessageRecord : AuditedAggregateRoot, IMultiTena public DateTime CreatedAt { get; private set; } - public string? ConversationId { get; private set; } + public Guid? ConversationId { get; private set; } public string? ReplyMessage { get; private set; } @@ -42,9 +41,9 @@ public abstract class ChatMessageRecord : AuditedAggregateRoot, IMultiTena TenantId = tenantId; } - public virtual ChatMessageRecord SetConversationId(string conversationId) + public virtual ChatMessageRecord SetConversationId(Guid conversationId) { - ConversationId = Check.NotNullOrWhiteSpace(conversationId, nameof(conversationId), ChatMessageRecordConsts.MaxConversationIdLength); + ConversationId = conversationId; return this; } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs index feb7b9f80..add01791f 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs @@ -35,7 +35,7 @@ public class ChatMessageStore : IChatMessageStore, ITransientDependency _messageRecordRepository = messageRecordRepository; } - public async virtual Task> GetHistoryMessagesAsync(string conversationId) + public async virtual Task> GetHistoryMessagesAsync(Guid conversationId) { var maxLatestHistoryMessagesToKeep = await _settingProvider.GetAsync( AIManagementSettingNames.ChatMessage.MaxLatestHistoryMessagesToKeep, 0); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ITextChatMessageRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ITextChatMessageRecordRepository.cs index ad641cb43..de20f6e27 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ITextChatMessageRecordRepository.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ITextChatMessageRecordRepository.cs @@ -8,7 +8,7 @@ namespace LINGYUN.Abp.AIManagement.Chats; public interface ITextChatMessageRecordRepository : IBasicRepository { Task> GetHistoryMessagesAsync( - string conversationId, + Guid conversationId, int maxResultCount = 0, CancellationToken cancellationToken = default); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs index 90a2d3fc9..185fd3c86 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs @@ -27,8 +27,6 @@ public static class AIManagementDbContextModelBuilderExtensions .HasMaxLength(ChatMessageRecordConsts.MaxChatRoleLength) .HasConversion(new ChatRoleValueConverter()) .IsRequired(); - b.Property(x => x.ConversationId) - .HasMaxLength(ChatMessageRecordConsts.MaxConversationIdLength); b.Property(x => x.Content) .HasMaxLength(TextChatMessageRecordConsts.MaxContentLength) .IsRequired(); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTextChatMessageRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTextChatMessageRecordRepository.cs index 3377e6304..d5d075bd4 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTextChatMessageRecordRepository.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTextChatMessageRecordRepository.cs @@ -18,7 +18,7 @@ public class EfCoreTextChatMessageRecordRepository : EfCoreRepository> GetHistoryMessagesAsync( - string conversationId, + Guid conversationId, int maxResultCount = 0, CancellationToken cancellationToken = default) { From d64571d18cb358b1eed5fc6fb27f2bf57bcfe75b Mon Sep 17 00:00:00 2001 From: colin Date: Tue, 27 Jan 2026 14:43:53 +0800 Subject: [PATCH 18/88] feat: Add persistent conversation --- .../LINGYUN/Abp/AI/Agent/AgentService.cs | 57 ++++++++++- .../LINGYUN.Abp.AI.Core.csproj | 1 + .../LINGYUN/Abp/AI/AbpAICoreModule.cs | 4 + .../LINGYUN/Abp/AI/AbpAIErrorCodes.cs | 4 + .../LINGYUN/Abp/AI/Chats/IChatMessageStore.cs | 2 +- .../Abp/AI/Chats/IConversationStore.cs | 13 +++ .../Abp/AI/Chats/InMemoryChatMessageStore.cs | 18 ++-- .../Abp/AI/Chats/InMemoryConversationStore.cs | 48 ++++++++++ .../Abp/AI/Localization/Resources/en.json | 4 +- .../AI/Localization/Resources/zh-Hans.json | 4 +- .../LINGYUN/Abp/AI/Models/ChatMessage.cs | 4 +- .../LINGYUN/Abp/AI/Models/Conversation.cs | 28 ++++++ .../LINGYUN/Abp/AI/Models/TokenUsageInfo.cs | 15 +-- .../FodyWeavers.xsd | 30 ++++++ .../FodyWeavers.xsd | 30 ++++++ .../Chats/ConversationRecordConsts.cs | 5 + .../AbpAIManagementDomainMappers.cs | 8 +- .../AIManagement/Chats/ChatMessageRecord.cs | 8 ++ .../AIManagement/Chats/ChatMessageStore.cs | 33 +++++-- .../Chats/ConversationCleanupOptions.cs | 15 +++ .../AIManagement/Chats/ConversationRecord.cs | 34 +++++++ .../AIManagement/Chats/ConversationStore.cs | 95 +++++++++++++++++++ .../Chats/IConversationRecordRepository.cs | 21 ++++ .../AIManagementDbContext.cs | 5 +- ...nagementDbContextModelBuilderExtensions.cs | 10 ++ ...bpAIManagementEntityFrameworkCoreModule.cs | 1 + .../EfCoreConversationRecordRepository.cs | 44 +++++++++ .../IAIManagementDbContext.cs | 5 +- .../FodyWeavers.xsd | 30 ++++++ .../FodyWeavers.xsd | 30 ++++++ 30 files changed, 560 insertions(+), 46 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IConversationStore.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryConversationStore.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/Conversation.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/FodyWeavers.xsd create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/FodyWeavers.xsd create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ConversationRecordConsts.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationCleanupOptions.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationRecord.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationStore.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/IConversationRecordRepository.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreConversationRecordRepository.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/FodyWeavers.xsd create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/FodyWeavers.xsd diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs index 6fe8db044..78c9f0508 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs @@ -1,14 +1,18 @@ using LINGYUN.Abp.AI.Chats; +using LINGYUN.Abp.AI.Localization; using LINGYUN.Abp.AI.Models; using LINGYUN.Abp.AI.Tokens; using Microsoft.Agents.AI; using Microsoft.Extensions.AI; +using Microsoft.Extensions.Localization; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Volo.Abp; using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; using Volo.Abp.Timing; using AIChatMessage = Microsoft.Extensions.AI.ChatMessage; @@ -16,30 +20,43 @@ namespace LINGYUN.Abp.AI.Agent; public class AgentService : IAgentService, IScopedDependency { private readonly IClock _clock; + private readonly IGuidGenerator _guidGenerator; private readonly IAgentFactory _agentFactory; private readonly ITokenUsageStore _tokenUsageStore; private readonly IChatMessageStore _chatMessageStore; + private readonly IConversationStore _conversationStore; + private readonly IStringLocalizer _localizerResource; public AgentService( IClock clock, + IGuidGenerator guidGenerator, IAgentFactory agentFactory, ITokenUsageStore tokenUsageStore, - IChatMessageStore chatMessageStore) + IChatMessageStore chatMessageStore, + IConversationStore conversationStore, + IStringLocalizer localizerResource) { _clock = clock; + _guidGenerator = guidGenerator; _agentFactory = agentFactory; _tokenUsageStore = tokenUsageStore; _chatMessageStore = chatMessageStore; + _conversationStore = conversationStore; + _localizerResource = localizerResource; } public async virtual IAsyncEnumerable SendMessageAsync(Models.ChatMessage message) { + var conversationId = await StoreConversation(message); + + message.WithConversationId(conversationId); + var messages = await BuildChatMessages(message); var agent = await _agentFactory.CreateAsync(message.Workspace); var agentRunRes = agent.RunStreamingAsync(messages); - var tokenUsageInfo = new TokenUsageInfo(message.Workspace); + var tokenUsageInfo = new TokenUsageInfo(message.Workspace, conversationId); var agentMessageBuilder = new StringBuilder(); await foreach (var response in agentRunRes) @@ -51,7 +68,6 @@ public class AgentService : IAgentService, IScopedDependency var messageId = await StoreChatMessage(message, agentMessageBuilder.ToString()); - tokenUsageInfo.WithConversationId(message.ConversationId); tokenUsageInfo.WithMessageId(messageId); #if DEBUG @@ -70,9 +86,10 @@ public class AgentService : IAgentService, IScopedDependency { var historyMessages = await _chatMessageStore.GetHistoryMessagesAsync(message.ConversationId.Value); + // TODO: 应用摘要提示压缩 foreach (var chatMessage in historyMessages) { - messages.Add(new AIChatMessage(ChatRole.System, chatMessage.GetMessagePrompt())); + messages.Add(new AIChatMessage(chatMessage.Role, chatMessage.GetMessagePrompt())); } } @@ -81,13 +98,43 @@ public class AgentService : IAgentService, IScopedDependency return messages; } - protected async virtual Task StoreChatMessage(Models.ChatMessage message, string agentMessage) + protected async virtual Task StoreChatMessage(Models.ChatMessage message, string agentMessage) { message.WithReply(agentMessage, _clock.Now); return await _chatMessageStore.SaveMessageAsync(message); } + protected async virtual Task StoreConversation(Models.ChatMessage message) + { + if (message.ConversationId.HasValue) + { + var conversation = await _conversationStore.FindAsync(message.ConversationId.Value); + if (conversation == null || conversation.ExpiredAt <= _clock.Now) + { + throw new BusinessException( + AbpAIErrorCodes.ConversationHasExpired, + "The conversation has expired. Please create a new one!"); + } + + conversation.UpdateAt = _clock.Now; + await _conversationStore.SaveAsync(conversation); + + return conversation.Id; + } + else + { + var conversation = new Conversation( + _guidGenerator.Create(), + _localizerResource["NewConversation"], + _clock.Now); + + await _conversationStore.SaveAsync(conversation); + + return conversation.Id; + } + } + protected async virtual Task StoreTokenUsageInfo(TokenUsageInfo tokenUsageInfo) { await _tokenUsageStore.SaveTokenUsageAsync(tokenUsageInfo); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj index 4dc562c9c..b6e35c892 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj @@ -22,6 +22,7 @@ + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs index 2ab1185a5..9bd80e7b7 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs @@ -5,15 +5,19 @@ using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using Volo.Abp.AI; +using Volo.Abp.Guids; using Volo.Abp.Localization; using Volo.Abp.Localization.ExceptionHandling; using Volo.Abp.Modularity; +using Volo.Abp.Timing; using Volo.Abp.VirtualFileSystem; namespace LINGYUN.Abp.AI; [DependsOn( typeof(AbpAIModule), + typeof(AbpGuidsModule), + typeof(AbpTimingModule), typeof(AbpLocalizationModule))] public class AbpAICoreModule : AbpModule { diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAIErrorCodes.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAIErrorCodes.cs index bb261dea1..e5538e47e 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAIErrorCodes.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAIErrorCodes.cs @@ -6,4 +6,8 @@ public static class AbpAIErrorCodes /// 工作区不可用: {Workspace}! /// public const string WorkspaceIsNotEnabled = Namespace + ":110001"; + /// + /// 对话已过期, 请重新创建会话! + /// + public const string ConversationHasExpired = Namespace + ":110101"; } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs index 886a0e953..724eb5a43 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace LINGYUN.Abp.AI.Chats; public interface IChatMessageStore { - Task SaveMessageAsync(ChatMessage message); + Task SaveMessageAsync(ChatMessage message); Task> GetHistoryMessagesAsync(Guid conversationId); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IConversationStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IConversationStore.cs new file mode 100644 index 000000000..e13119c9d --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IConversationStore.cs @@ -0,0 +1,13 @@ +using LINGYUN.Abp.AI.Models; +using System; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.AI.Chats; +public interface IConversationStore +{ + Task SaveAsync(Conversation conversation); + + Task FindAsync(Guid conversationId); + + Task CleanupAsync(); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs index b81ccef2b..3519ea30c 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs @@ -12,7 +12,7 @@ namespace LINGYUN.Abp.AI.Chats; [Dependency(ServiceLifetime.Singleton, TryRegister = true)] public class InMemoryChatMessageStore : IChatMessageStore { - private static readonly ConcurrentDictionary> _userMessageCache = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> _userMessageCache = new ConcurrentDictionary>(); public Task> GetHistoryMessagesAsync(Guid conversationId) { @@ -30,23 +30,23 @@ public class InMemoryChatMessageStore : IChatMessageStore .OrderBy(x => x.CreatedAt)); } - public Task SaveMessageAsync(ChatMessage message) + public Task SaveMessageAsync(ChatMessage message) { var messageId = message.Id; - if (messageId.IsNullOrWhiteSpace()) + if (!messageId.HasValue) { - messageId = Guid.NewGuid().ToString(); - message.WithMessageId(messageId); + messageId = Guid.NewGuid(); + message.WithMessageId(messageId.Value); } - if (_userMessageCache.ContainsKey(messageId)) + if (_userMessageCache.ContainsKey(messageId.Value)) { - _userMessageCache[messageId].Add(message); + _userMessageCache[messageId.Value].Add(message); } else { - _userMessageCache[messageId] = new List() { message }; + _userMessageCache[messageId.Value] = new List() { message }; } - return Task.FromResult(messageId); + return Task.FromResult(messageId.Value); } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryConversationStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryConversationStore.cs new file mode 100644 index 000000000..d273e47eb --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryConversationStore.cs @@ -0,0 +1,48 @@ +using LINGYUN.Abp.AI.Models; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.AI.Chats; + +[Dependency(ServiceLifetime.Singleton, TryRegister = true)] +public class InMemoryConversationStore : IConversationStore +{ + private static readonly ConcurrentDictionary _conversationCache = new ConcurrentDictionary(); + public Task SaveAsync(Conversation conversation) + { + if (_conversationCache.ContainsKey(conversation.Id)) + { + conversation.ExpiredAt = DateTime.Now.AddHours(2); + _conversationCache[conversation.Id] = conversation; + } + else + { + _conversationCache.TryAdd(conversation.Id, conversation); + } + + return Task.CompletedTask; + } + + public Task FindAsync(Guid conversationId) + { + _conversationCache.TryGetValue(conversationId, out var conversation); + return Task.FromResult(conversation); + } + + public Task CleanupAsync() + { + // Configure it... + var expiredTime = DateTime.Now.AddHours(-2); + var expiredConversationIds = _conversationCache.Values + .Where(x => x.UpdateAt <= expiredTime) + .Select(x => x.Id); + _conversationCache.RemoveAll(x => expiredConversationIds.Contains(x.Key)); + + return Task.CompletedTask; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/en.json b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/en.json index 399e2a7c9..5e631f34c 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/en.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/en.json @@ -1,6 +1,8 @@ { "culture": "en", "texts": { - "Abp.AI:110001": "Workspace is not enabled: {Workspace}!" + "Abp.AI:110001": "Workspace is not enabled: {Workspace}!", + "Abp.AI:110101": "The conversation has expired. Please create a new one!", + "NewConversation": "New Conversation" } } \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/zh-Hans.json b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/zh-Hans.json index 3fc69d30d..8681eedaf 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/zh-Hans.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/zh-Hans.json @@ -1,6 +1,8 @@ { "culture": "zh-Hans", "texts": { - "Abp.AI:110001": "工作区不可用: {Workspace}!" + "Abp.AI:110001": "工作区不可用: {Workspace}!", + "Abp.AI:110101": "对话已过期, 请重新创建会话!", + "NewConversation": "新对话" } } \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs index 46770a3f5..143a537de 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs @@ -6,7 +6,7 @@ public abstract class ChatMessage { public string Workspace { get; } - public string? Id { get; private set; } + public Guid? Id { get; private set; } public Guid? ConversationId { get; private set; } @@ -27,7 +27,7 @@ public abstract class ChatMessage CreatedAt = createdAt ?? DateTime.Now; } - public virtual ChatMessage WithMessageId(string id) + public virtual ChatMessage WithMessageId(Guid id) { Id = id; return this; diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/Conversation.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/Conversation.cs new file mode 100644 index 000000000..52943a7a1 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/Conversation.cs @@ -0,0 +1,28 @@ +using System; + +namespace LINGYUN.Abp.AI.Models; +public class Conversation +{ + public Guid Id { get; private set; } + public string Name { get; private set; } + public DateTime CreatedAt { get; private set; } + public DateTime? ExpiredAt { get; set; } + public DateTime? UpdateAt { get; set; } + public Conversation( + Guid id, + string name, + DateTime createdAt) + { + Id = id; + Name = name; + CreatedAt = createdAt; + UpdateAt = createdAt; + } + + public Conversation WithName(string name) + { + Name = name; + + return this; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs index 770454a3a..f74b1867e 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs @@ -5,29 +5,24 @@ namespace LINGYUN.Abp.AI.Models; public class TokenUsageInfo { public string Workspace { get; } - public string? MessageId { get; private set; } - public Guid? ConversationId { get; private set; } + public Guid? MessageId { get; private set; } + public Guid ConversationId { get; private set; } public long? InputTokenCount { get; set; } public long? OutputTokenCount { get; set; } public long? TotalTokenCount { get; set; } public long? CachedInputTokenCount { get; set; } public long? ReasoningTokenCount { get; set; } - public TokenUsageInfo(string workspace) + public TokenUsageInfo(string workspace, Guid conversationId) { Workspace = workspace; + ConversationId = conversationId; } - public virtual TokenUsageInfo WithMessageId(string id) + public virtual TokenUsageInfo WithMessageId(Guid id) { MessageId = id; return this; } - public virtual TokenUsageInfo WithConversationId(Guid? conversationId) - { - ConversationId = conversationId; - return this; - } - public override string ToString() { var sb = new StringBuilder(); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/FodyWeavers.xsd b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/FodyWeavers.xsd b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ConversationRecordConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ConversationRecordConsts.cs new file mode 100644 index 000000000..30c3868cb --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ConversationRecordConsts.cs @@ -0,0 +1,5 @@ +namespace LINGYUN.Abp.AIManagement.Chats; +public static class ConversationRecordConsts +{ + public static int MaxNameLength { get; set; } = 50; +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs index 75b113067..93cdb0261 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs @@ -1,5 +1,5 @@ using LINGYUN.Abp.AI.Models; -using LINGYUN.Abp.AIManagement.Messages; +using LINGYUN.Abp.AIManagement.Chats; using Riok.Mapperly.Abstractions; using Volo.Abp.Mapperly; using Volo.Abp.ObjectExtending; @@ -8,8 +8,8 @@ namespace LINGYUN.Abp.AIManagement; [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] [MapExtraProperties(DefinitionChecks = MappingPropertyDefinitionChecks.None)] -public partial class UserTextMessageRecordToUserTextMessageMapper : MapperBase +public partial class TextChatMessageRecordToUserTextMessageMapper : MapperBase { - public override partial TextChatMessage Map(UserTextMessageRecord source); - public override partial void Map(UserTextMessageRecord source, TextChatMessage destination); + public override partial TextChatMessage Map(TextChatMessageRecord source); + public override partial void Map(TextChatMessageRecord source, TextChatMessage destination); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs index 91bd1c0f1..805aaadd3 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs @@ -15,6 +15,8 @@ public abstract class ChatMessageRecord : AuditedAggregateRoot, IMultiTena public DateTime CreatedAt { get; private set; } + public Guid? UserId { get; private set; } + public Guid? ConversationId { get; private set; } public string? ReplyMessage { get; private set; } @@ -41,6 +43,12 @@ public abstract class ChatMessageRecord : AuditedAggregateRoot, IMultiTena TenantId = tenantId; } + public virtual ChatMessageRecord SetUserId(Guid userId) + { + UserId = userId; + return this; + } + public virtual ChatMessageRecord SetConversationId(Guid conversationId) { ConversationId = conversationId; diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs index add01791f..724af7db4 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs @@ -3,6 +3,7 @@ using LINGYUN.Abp.AI.Models; using LINGYUN.Abp.AIManagement.Settings; using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Guids; @@ -46,21 +47,35 @@ public class ChatMessageStore : IChatMessageStore, ITransientDependency var userTextMessages = await _messageRecordRepository.GetHistoryMessagesAsync(conversationId, maxLatestHistoryMessagesToKeep); - return _objectMapper.Map, IEnumerable>(userTextMessages); + return userTextMessages.Select(msg => + { + var chatMessage = new TextChatMessage(msg.Workspace, msg.Content, msg.Role, msg.CreatedAt); + chatMessage.WithMessageId(msg.Id); + if (msg.ConversationId.HasValue) + { + chatMessage.WithConversationId(msg.ConversationId.Value); + } + if (!msg.ReplyMessage.IsNullOrWhiteSpace() && msg.ReplyAt.HasValue) + { + chatMessage.WithReply(msg.ReplyMessage, msg.ReplyAt.Value); + } + + return chatMessage; + }); } - public async virtual Task SaveMessageAsync(ChatMessage message) + public async virtual Task SaveMessageAsync(ChatMessage message) { var messageId = message.Id; - if (messageId.IsNullOrWhiteSpace()) + if (!messageId.HasValue) { - messageId = _guidGenerator.Create().ToString(); - message.WithMessageId(messageId); + messageId = _guidGenerator.Create(); + message.WithMessageId(messageId.Value); } - await StoreMessageAsync(Guid.Parse(messageId), message); + await StoreMessageAsync(messageId.Value, message); - return messageId; + return messageId.Value; } protected async virtual Task StoreMessageAsync(Guid messageId, ChatMessage message) @@ -102,9 +117,9 @@ public class ChatMessageStore : IChatMessageStore, ITransientDependency private static void UpdateUserMessageRecord(ChatMessageRecord messageRecord, ChatMessage message) { - if (!message.ConversationId.IsNullOrWhiteSpace()) + if (message.ConversationId.HasValue) { - messageRecord.SetConversationId(message.ConversationId); + messageRecord.SetConversationId(message.ConversationId.Value); } if (!message.ReplyMessage.IsNullOrWhiteSpace()) { diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationCleanupOptions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationCleanupOptions.cs new file mode 100644 index 000000000..22f6cbe26 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationCleanupOptions.cs @@ -0,0 +1,15 @@ +using System; + +namespace LINGYUN.Abp.AIManagement.Chats; +public class ConversationCleanupOptions +{ + public bool IsCleanupEnabled { get; set; } + public TimeSpan ExpiredTime { get; set; } + public int CleanupPeriod { get; set; } + public ConversationCleanupOptions() + { + IsCleanupEnabled = true; + CleanupPeriod = 3_600_000; + ExpiredTime = TimeSpan.FromHours(2); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationRecord.cs new file mode 100644 index 000000000..cd81fcac6 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationRecord.cs @@ -0,0 +1,34 @@ +using System; +using Volo.Abp; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.AIManagement.Chats; +public class ConversationRecord : AuditedEntity, IMultiTenant +{ + public Guid? TenantId { get; private set; } + + public string Name { get; private set; } + + public DateTime CreatedAt { get; private set; } + + public DateTime ExpiredAt { get; set; } + + public DateTime? UpdateAt { get; set; } + public ConversationRecord( + Guid id, + string name, + DateTime createdAt, + DateTime expiredAt, + Guid? tenantId = null) + : base(id) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name), ConversationRecordConsts.MaxNameLength); + CreatedAt = createdAt; + ExpiredAt = expiredAt; + + UpdateAt = createdAt; + + TenantId = tenantId; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationStore.cs new file mode 100644 index 000000000..3d7d2ed5e --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationStore.cs @@ -0,0 +1,95 @@ +using LINGYUN.Abp.AI.Chats; +using LINGYUN.Abp.AI.Models; +using Microsoft.Extensions.Options; +using System; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Specifications; +using Volo.Abp.Timing; + +namespace LINGYUN.Abp.AIManagement.Chats; + +[Dependency(ReplaceServices = true)] +public class ConversationStore : IConversationStore, ITransientDependency +{ + private readonly IClock _clock; + private readonly ICurrentTenant _currentTenant; + private readonly ConversationCleanupOptions _cleanupOptions; + private readonly IConversationRecordRepository _conversationRecordRepository; + + public ConversationStore( + IClock clock, + ICurrentTenant currentTenant, + IOptions cleanupOptions, + IConversationRecordRepository conversationRecordRepository) + { + _clock = clock; + _currentTenant = currentTenant; + _cleanupOptions = cleanupOptions.Value; + _conversationRecordRepository = conversationRecordRepository; + } + + public async virtual Task CleanupAsync() + { + if (!_cleanupOptions.IsCleanupEnabled) + { + return; + } + + var specification = new ExpressionSpecification( + x => x.ExpiredAt <= _clock.Now); + + var totalCount = await _conversationRecordRepository.GetCountAsync(specification); + var expiredRecords = await _conversationRecordRepository.GetListAsync(specification, maxResultCount: totalCount); + + await _conversationRecordRepository.DeleteManyAsync(expiredRecords); + } + + public async virtual Task FindAsync(Guid conversationId) + { + var conversationRecord = await _conversationRecordRepository.FindAsync(conversationId); + if (conversationRecord == null) + { + return null; + } + + var conversation = new Conversation( + conversationRecord.Id, + conversationRecord.Name, + conversationRecord.CreatedAt) + { + UpdateAt = conversationRecord.UpdateAt, + ExpiredAt = conversationRecord.ExpiredAt, + }; + + + return conversation; + } + + public async virtual Task SaveAsync(Conversation conversation) + { + var conversationRecord = await _conversationRecordRepository.FindAsync(conversation.Id); + if (conversationRecord == null) + { + var expiredTime = conversation.CreatedAt.Add(_cleanupOptions.ExpiredTime); + conversationRecord = new ConversationRecord( + conversation.Id, + conversation.Name, + conversation.CreatedAt, + expiredTime, + _currentTenant.Id); + + await _conversationRecordRepository.InsertAsync(conversationRecord); + } + else + { + var expiredTime = (conversation.UpdateAt ?? _clock.Now).Add(_cleanupOptions.ExpiredTime); + conversationRecord.UpdateAt = conversation.UpdateAt; + conversationRecord.ExpiredAt = expiredTime; + + await _conversationRecordRepository.UpdateAsync(conversationRecord); + } + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/IConversationRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/IConversationRecordRepository.cs new file mode 100644 index 000000000..6ed7e0f1a --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/IConversationRecordRepository.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Specifications; + +namespace LINGYUN.Abp.AIManagement.Chats; +public interface IConversationRecordRepository : IBasicRepository +{ + Task GetCountAsync( + ISpecification specification, + CancellationToken cancellationToken = default); + + Task> GetListAsync( + ISpecification specification, + string? sorting = $"{nameof(ConversationRecord.CreatedAt)} DESC", + int maxResultCount = 10, + int skipCount = 0, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs index 865365326..b43c95e49 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs @@ -1,4 +1,4 @@ -using LINGYUN.Abp.AIManagement.Messages; +using LINGYUN.Abp.AIManagement.Chats; using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.EntityFrameworkCore; using Volo.Abp.Data; @@ -10,7 +10,8 @@ namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; public class AIManagementDbContext : AbpDbContext, IAIManagementDbContext { public DbSet WorkspaceDefinitions { get; set; } - public DbSet UserTextMessageRecords { get; set; } + public DbSet TextChatMessageRecords { get; set; } + public DbSet ConversationRecords { get; set; } public AIManagementDbContext( DbContextOptions options) : base(options) { diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs index 185fd3c86..79612bacf 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs @@ -14,6 +14,16 @@ public static class AIManagementDbContextModelBuilderExtensions { Check.NotNull(builder, nameof(builder)); + builder.Entity(b => + { + b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "Conversations", AbpAIManagementDbProperties.DbSchema); + + b.ConfigureByConvention(); + + b.Property(x => x.Name) + .HasMaxLength(ConversationRecordConsts.MaxNameLength) + .IsRequired(); + }); builder.Entity(b => { b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "TextChatMessages", AbpAIManagementDbProperties.DbSchema); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs index f372b441f..0802cbc47 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs @@ -17,6 +17,7 @@ public class AbpAIManagementEntityFrameworkCoreModule : AbpModule { options.AddDefaultRepositories(); + options.AddRepository(); options.AddRepository(); options.AddRepository(); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreConversationRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreConversationRecordRepository.cs new file mode 100644 index 000000000..d79ea9709 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreConversationRecordRepository.cs @@ -0,0 +1,44 @@ +using LINGYUN.Abp.AIManagement.Chats; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Specifications; + +namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; +public class EfCoreConversationRecordRepository : EfCoreRepository, IConversationRecordRepository +{ + public EfCoreConversationRecordRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public async virtual Task GetCountAsync( + ISpecification specification, + CancellationToken cancellationToken = default) + { + return await (await GetQueryableAsync()) + .Where(specification.ToExpression()) + .CountAsync(GetCancellationToken(cancellationToken)); + } + + public async virtual Task> GetListAsync( + ISpecification specification, + string? sorting = $"{nameof(ConversationRecord.CreatedAt)} DESC", + int maxResultCount = 10, + int skipCount = 0, + CancellationToken cancellationToken = default) + { + return await (await GetQueryableAsync()) + .Where(specification.ToExpression()) + .OrderBy(!sorting.IsNullOrWhiteSpace() ? sorting : $"{nameof(ConversationRecord.CreatedAt)} DESC") + .PageBy(skipCount, maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs index d253a4aa6..33d66185c 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs @@ -1,4 +1,4 @@ -using LINGYUN.Abp.AIManagement.Messages; +using LINGYUN.Abp.AIManagement.Chats; using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.EntityFrameworkCore; using Volo.Abp.Data; @@ -10,5 +10,6 @@ namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; public interface IAIManagementDbContext : IEfCoreDbContext { DbSet WorkspaceDefinitions { get; } - DbSet UserTextMessageRecords { get; } + DbSet TextChatMessageRecords { get; } + DbSet ConversationRecords { get; } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/FodyWeavers.xsd b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi.Client/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/FodyWeavers.xsd b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file From 1aad1f0231e460a42edcbb59cf8cb35f691781b2 Mon Sep 17 00:00:00 2001 From: colin Date: Tue, 27 Jan 2026 14:44:34 +0800 Subject: [PATCH 19/88] feat: Add persistent TokenUsage --- .../Abp/AIManagement/AIManagementOptions.cs | 4 +- .../Tokens/ITokenUsageRecordRepository.cs | 26 +++++++ .../AIManagement/Tokens/TokenUsageRecord.cs | 40 ++++++++++- .../AIManagement/Tokens/TokenUsageStore.cs | 68 +++++++++++++++++++ .../AIManagementDbContext.cs | 2 + ...nagementDbContextModelBuilderExtensions.cs | 9 +++ ...bpAIManagementEntityFrameworkCoreModule.cs | 2 + .../EfCoreTokenUsageRecordRepository.cs | 54 +++++++++++++++ .../IAIManagementDbContext.cs | 2 + 9 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/ITokenUsageRecordRepository.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageStore.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTokenUsageRecordRepository.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AIManagementOptions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AIManagementOptions.cs index 493fcb14a..72ab61ffd 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AIManagementOptions.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AIManagementOptions.cs @@ -1,4 +1,6 @@ -namespace LINGYUN.Abp.AIManagement; +using System; + +namespace LINGYUN.Abp.AIManagement; public class AIManagementOptions { public bool IsDynamicWorkspaceStoreEnabled { get; set; } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/ITokenUsageRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/ITokenUsageRecordRepository.cs new file mode 100644 index 000000000..11724dda4 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/ITokenUsageRecordRepository.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Specifications; + +namespace LINGYUN.Abp.AIManagement.Tokens; +public interface ITokenUsageRecordRepository : IBasicRepository +{ + Task FindByMessageIdAsync( + Guid conversationId, + Guid? messageId, + CancellationToken cancellationToken = default); + + Task GetCountAsync( + ISpecification specification, + CancellationToken cancellationToken = default); + + Task> GetListAsync( + ISpecification specification, + string sorting = $"{nameof(TokenUsageRecord.CreationTime)}", + int maxResultCount = 10, + int skipCount = 0, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageRecord.cs index fedf9a194..136747a39 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageRecord.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageRecord.cs @@ -1,8 +1,42 @@ using System; -using System.Collections.Generic; -using System.Text; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; namespace LINGYUN.Abp.AIManagement.Tokens; -public class TokenUsageRecord +public class TokenUsageRecord : AuditedEntity, IMultiTenant { + public Guid? TenantId { get; private set; } + public Guid? MessageId { get; private set; } + public Guid? ConversationId { get; private set; } + public long? InputTokenCount { get; set; } + public long? OutputTokenCount { get; set; } + public long? TotalTokenCount { get; set; } + public long? CachedInputTokenCount { get; set; } + public long? ReasoningTokenCount { get; set; } + protected TokenUsageRecord() + { + + } + + public TokenUsageRecord( + Guid id, + Guid? messageId = null, + Guid? conversationId = null, + long? inputTokenCount = null, + long? outputTokenCount = null, + long? totalTokenCount = null, + long? cachedInputTokenCount = null, + long? reasoningTokenCount = null, + Guid? tenantId = null) + : base(id) + { + MessageId = messageId; + ConversationId = conversationId; + InputTokenCount = inputTokenCount; + OutputTokenCount = outputTokenCount; + TotalTokenCount = totalTokenCount; + CachedInputTokenCount = cachedInputTokenCount; + ReasoningTokenCount = reasoningTokenCount; + TenantId = tenantId; + } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageStore.cs new file mode 100644 index 000000000..026ba82cd --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageStore.cs @@ -0,0 +1,68 @@ +using LINGYUN.Abp.AI.Models; +using LINGYUN.Abp.AI.Tokens; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.AIManagement.Tokens; + +[Dependency(ReplaceServices = true)] +public class TokenUsageStore : ITokenUsageStore, ITransientDependency +{ + private readonly ICurrentTenant _currentTenant; + private readonly IGuidGenerator _guidGenerator; + private readonly ITokenUsageRecordRepository _tokenUsageRecordRepository; + + public TokenUsageStore( + ICurrentTenant currentTenant, + IGuidGenerator guidGenerator, + ITokenUsageRecordRepository tokenUsageRecordRepository) + { + _currentTenant = currentTenant; + _guidGenerator = guidGenerator; + _tokenUsageRecordRepository = tokenUsageRecordRepository; + } + + public async virtual Task SaveTokenUsageAsync(TokenUsageInfo tokenUsageInfo) + { + var tokenUsageRecord = await _tokenUsageRecordRepository.FindByMessageIdAsync( + tokenUsageInfo.ConversationId, + tokenUsageInfo.MessageId); + + if (tokenUsageRecord == null) + { + tokenUsageRecord = new TokenUsageRecord( + _guidGenerator.Create(), + tokenUsageInfo.MessageId, + tokenUsageInfo.ConversationId, + tokenUsageInfo.InputTokenCount, + tokenUsageInfo.OutputTokenCount, + tokenUsageInfo.TotalTokenCount, + tokenUsageInfo.CachedInputTokenCount, + tokenUsageInfo.ReasoningTokenCount, + _currentTenant.Id); + + await _tokenUsageRecordRepository.InsertAsync(tokenUsageRecord); + } + else + { + tokenUsageRecord.InputTokenCount ??= 0; + tokenUsageRecord.InputTokenCount += tokenUsageInfo.InputTokenCount; + + tokenUsageRecord.OutputTokenCount ??= 0; + tokenUsageRecord.OutputTokenCount += tokenUsageInfo.OutputTokenCount; + + tokenUsageRecord.CachedInputTokenCount ??= 0; + tokenUsageRecord.CachedInputTokenCount += tokenUsageInfo.CachedInputTokenCount; + + tokenUsageRecord.ReasoningTokenCount ??= 0; + tokenUsageRecord.ReasoningTokenCount += tokenUsageInfo.ReasoningTokenCount; + + tokenUsageRecord.TotalTokenCount ??= 0; + tokenUsageRecord.TotalTokenCount += tokenUsageInfo.TotalTokenCount; + + await _tokenUsageRecordRepository.UpdateAsync(tokenUsageRecord); + } + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs index b43c95e49..fb3b6a9f2 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs @@ -1,4 +1,5 @@ using LINGYUN.Abp.AIManagement.Chats; +using LINGYUN.Abp.AIManagement.Tokens; using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.EntityFrameworkCore; using Volo.Abp.Data; @@ -12,6 +13,7 @@ public class AIManagementDbContext : AbpDbContext, IAIMan public DbSet WorkspaceDefinitions { get; set; } public DbSet TextChatMessageRecords { get; set; } public DbSet ConversationRecords { get; set; } + public DbSet TokenUsageRecords { get; set; } public AIManagementDbContext( DbContextOptions options) : base(options) { diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs index 79612bacf..b6e8d2110 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using LINGYUN.Abp.AIManagement.Chats; using LINGYUN.Abp.AIManagement.EntityFrameworkCore.ValueConversions; +using LINGYUN.Abp.AIManagement.Tokens; using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.EntityFrameworkCore; using Volo.Abp; @@ -45,6 +46,14 @@ public static class AIManagementDbContextModelBuilderExtensions b.ApplyObjectExtensionMappings(); }); + builder.Entity(b => + { + b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "TokenUsages", AbpAIManagementDbProperties.DbSchema); + + b.ConfigureByConvention(); + + b.HasIndex(x => new { x.TenantId, x.ConversationId }); + }); if (builder.IsHostDatabase()) { diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs index 0802cbc47..bb4cf3964 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs @@ -1,4 +1,5 @@ using LINGYUN.Abp.AIManagement.Chats; +using LINGYUN.Abp.AIManagement.Tokens; using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore; @@ -19,6 +20,7 @@ public class AbpAIManagementEntityFrameworkCoreModule : AbpModule options.AddRepository(); options.AddRepository(); + options.AddRepository(); options.AddRepository(); }); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTokenUsageRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTokenUsageRecordRepository.cs new file mode 100644 index 000000000..2bb211cc6 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTokenUsageRecordRepository.cs @@ -0,0 +1,54 @@ +using LINGYUN.Abp.AIManagement.Tokens; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Specifications; + +namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; +public class EfCoreTokenUsageRecordRepository : EfCoreRepository, ITokenUsageRecordRepository +{ + public EfCoreTokenUsageRecordRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public async virtual Task FindByMessageIdAsync( + Guid conversationId, + Guid? messageId, + CancellationToken cancellationToken = default) + { + return await (await GetQueryableAsync()) + .Where(x => x.ConversationId == conversationId && x.MessageId == messageId) + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); + } + + public async virtual Task GetCountAsync( + ISpecification specification, + CancellationToken cancellationToken = default) + { + return await (await GetQueryableAsync()) + .Where(specification.ToExpression()) + .CountAsync(GetCancellationToken(cancellationToken)); + } + + public async virtual Task> GetListAsync( + ISpecification specification, + string sorting = $"{nameof(TokenUsageRecord.CreationTime)}", + int maxResultCount = 10, + int skipCount = 0, + CancellationToken cancellationToken = default) + { + return await (await GetQueryableAsync()) + .Where(specification.ToExpression()) + .OrderBy(!sorting.IsNullOrWhiteSpace() ? sorting : $"{nameof(TokenUsageRecord.CreationTime)}") + .PageBy(skipCount, maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs index 33d66185c..edc46235d 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs @@ -1,4 +1,5 @@ using LINGYUN.Abp.AIManagement.Chats; +using LINGYUN.Abp.AIManagement.Tokens; using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.EntityFrameworkCore; using Volo.Abp.Data; @@ -12,4 +13,5 @@ public interface IAIManagementDbContext : IEfCoreDbContext DbSet WorkspaceDefinitions { get; } DbSet TextChatMessageRecords { get; } DbSet ConversationRecords { get; } + DbSet TokenUsageRecords { get; } } From e102e6c8f930d7739563a7fa1da246e972c687a7 Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 28 Jan 2026 11:46:55 +0800 Subject: [PATCH 20/88] feat: Add Workspace management interface implementation --- ....AIManagement.Application.Contracts.csproj | 5 +- .../AIManagementRemoteServiceConsts.cs | 7 + ...pAIManagementApplicationContractsModule.cs | 14 ++ .../AbpAIManagementDomainSharedModule.cs | 8 - .../WorkspaceDefinitionRecordCreateDto.cs | 10 ++ ...kspaceDefinitionRecordCreateOrUpdateDto.cs | 47 ++++++ .../Dtos/WorkspaceDefinitionRecordDto.cs | 39 +++++ .../WorkspaceDefinitionRecordGetListInput.cs | 14 ++ .../WorkspaceDefinitionRecordUpdateDto.cs | 10 ++ .../IWorkspaceDefinitionAppService.cs | 14 ++ .../FodyWeavers.xsd | 30 ++++ .../AIManagementApplicationServiceBase.cs | 12 ++ .../AbpAIManagementApplicationMappers.cs | 15 ++ .../AbpAIManagementApplicationModule.cs | 17 ++ .../AbpAIManagementDomainSharedModule.cs | 8 - .../WorkspaceDefinitionAppService.cs | 152 ++++++++++++++++++ .../AIManagement/AIManagementErrorCodes.cs | 12 ++ .../WorkspaceAlreadyExistsException.cs | 16 ++ .../AbpAIManagementDbProperties.cs | 2 +- .../FodyWeavers.xsd | 30 ++++ ...nagementDbContextModelBuilderExtensions.cs | 2 +- .../AbpAIManagementDomainSharedModule.cs | 8 - .../WorkspaceDefinitionController.cs | 52 ++++++ 23 files changed, 496 insertions(+), 28 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AIManagementRemoteServiceConsts.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationContractsModule.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordCreateDto.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordCreateOrUpdateDto.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordDto.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordGetListInput.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordUpdateDto.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionAppService.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/FodyWeavers.xsd create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AIManagementApplicationServiceBase.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationMappers.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationModule.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AIManagementErrorCodes.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceAlreadyExistsException.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/FodyWeavers.xsd delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionController.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN.Abp.AIManagement.Application.Contracts.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN.Abp.AIManagement.Application.Contracts.csproj index c756d035b..1d2169985 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN.Abp.AIManagement.Application.Contracts.csproj +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN.Abp.AIManagement.Application.Contracts.csproj @@ -5,8 +5,8 @@ netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0 - LINGYUN.Abp.AIManagement.Domain - LINGYUN.Abp.AIManagement.Domain + LINGYUN.Abp.AIManagement.Application.Contracts + LINGYUN.Abp.AIManagement.Application.Contracts false false false @@ -16,6 +16,7 @@ + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AIManagementRemoteServiceConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AIManagementRemoteServiceConsts.cs new file mode 100644 index 000000000..b68f59f17 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AIManagementRemoteServiceConsts.cs @@ -0,0 +1,7 @@ +namespace LINGYUN.Abp.AIManagement; +public static class AIManagementRemoteServiceConsts +{ + public const string RemoteServiceName = "AbpAIManagement"; + + public const string ModuleName = "ai-management"; +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationContractsModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationContractsModule.cs new file mode 100644 index 000000000..289fb8ed2 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationContractsModule.cs @@ -0,0 +1,14 @@ +using Volo.Abp.Application; +using Volo.Abp.Authorization; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.AIManagement; + +[DependsOn( + typeof(AbpAIManagementDomainSharedModule), + typeof(AbpDddApplicationContractsModule), + typeof(AbpAuthorizationAbstractionsModule))] +public class AbpAIManagementApplicationContractsModule : AbpModule +{ + +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs deleted file mode 100644 index 751b9bfaa..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Volo.Abp.Modularity; - -namespace LINGYUN.Abp.AIManagement; - -public class AbpAIManagementDomainSharedModule : AbpModule -{ - -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordCreateDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordCreateDto.cs new file mode 100644 index 000000000..9591c2773 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordCreateDto.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Validation; + +namespace LINGYUN.Abp.AIManagement.Workspaces.Dtos; +public class WorkspaceDefinitionRecordCreateDto : WorkspaceDefinitionRecordCreateOrUpdateDto +{ + [Required] + [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxNameLength))] + public string Name { get; set; } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordCreateOrUpdateDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordCreateOrUpdateDto.cs new file mode 100644 index 000000000..dcb866294 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordCreateOrUpdateDto.cs @@ -0,0 +1,47 @@ +using System.ComponentModel.DataAnnotations; +using Volo.Abp.ObjectExtending; +using Volo.Abp.Validation; + +namespace LINGYUN.Abp.AIManagement.Workspaces.Dtos; +public abstract class WorkspaceDefinitionRecordCreateOrUpdateDto : ExtensibleObject +{ + [Required] + [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxProviderLength))] + public string Provider { get; set; } + + [Required] + [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxModelNameLength))] + public string ModelName { get; set; } + + [Required] + [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxDisplayNameLength))] + public string DisplayName { get; set; } + + [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxDescriptionLength))] + public string? Description { get; set; } + + [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxApiKeyLength))] + public string? ApiKey { get; set; } + + [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxApiBaseUrlLength))] + public string? ApiBaseUrl { get; set; } + + [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxSystemPromptLength))] + public string? SystemPrompt { get; set; } + + [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxInstructionsLength))] + public string? Instructions { get; set; } + + public float? Temperature { get; set; } + + public int? MaxOutputTokens { get; set; } + + public float? FrequencyPenalty { get; set; } + + public float? PresencePenalty { get; set; } + + public bool IsEnabled { get; set; } + + [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxStateCheckersLength))] + public string? StateCheckers { get; set; } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordDto.cs new file mode 100644 index 000000000..f65dd0ee9 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordDto.cs @@ -0,0 +1,39 @@ +using System; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Domain.Entities; + +namespace LINGYUN.Abp.AIManagement.Workspaces.Dtos; + +[Serializable] +public class WorkspaceDefinitionRecordDto : ExtensibleAuditedEntityDto, IHasConcurrencyStamp +{ + public string Name { get; set; } + + public string Provider { get; set; } + + public string ModelName { get; set; } + + public string DisplayName { get; set; } + + public string? Description { get; set; } + + public string? ApiBaseUrl { get; set; } + + public string? SystemPrompt { get; set; } + + public string? Instructions { get; set; } + + public float? Temperature { get; set; } + + public int? MaxOutputTokens { get; set; } + + public float? FrequencyPenalty { get; set; } + + public float? PresencePenalty { get; set; } + + public bool IsEnabled { get; set; } + + public string? StateCheckers { get; set; } + + public string ConcurrencyStamp { get; set; } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordGetListInput.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordGetListInput.cs new file mode 100644 index 000000000..a4f99fb6c --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordGetListInput.cs @@ -0,0 +1,14 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace LINGYUN.Abp.AIManagement.Workspaces.Dtos; + +[Serializable] +public class WorkspaceDefinitionRecordGetListInput : PagedAndSortedResultRequestDto +{ + public string? Filter { get; set; } + + public string? Provider { get; set; } + + public string? ModelName { get; set; } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordUpdateDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordUpdateDto.cs new file mode 100644 index 000000000..848ef7394 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordUpdateDto.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Domain.Entities; + +namespace LINGYUN.Abp.AIManagement.Workspaces.Dtos; +public class WorkspaceDefinitionRecordUpdateDto : WorkspaceDefinitionRecordCreateOrUpdateDto, IHasConcurrencyStamp +{ + [Required] + [StringLength(40)] + public string ConcurrencyStamp { get; set; } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionAppService.cs new file mode 100644 index 000000000..6ebb52c95 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionAppService.cs @@ -0,0 +1,14 @@ +using LINGYUN.Abp.AIManagement.Workspaces.Dtos; +using System; +using Volo.Abp.Application.Services; + +namespace LINGYUN.Abp.AIManagement.Workspaces; +public interface IWorkspaceDefinitionAppService : + ICrudAppService< + WorkspaceDefinitionRecordDto, + Guid, + WorkspaceDefinitionRecordGetListInput, + WorkspaceDefinitionRecordCreateDto, + WorkspaceDefinitionRecordUpdateDto> +{ +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/FodyWeavers.xsd b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AIManagementApplicationServiceBase.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AIManagementApplicationServiceBase.cs new file mode 100644 index 000000000..291b27256 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AIManagementApplicationServiceBase.cs @@ -0,0 +1,12 @@ +using LINGYUN.Abp.AIManagement.Localization; +using Volo.Abp.Application.Services; + +namespace LINGYUN.Abp.AIManagement; +public abstract class AIManagementApplicationServiceBase : ApplicationService +{ + protected AIManagementApplicationServiceBase() + { + LocalizationResource = typeof(AIManagementResource); + ObjectMapperContext = typeof(AbpAIManagementApplicationModule); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationMappers.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationMappers.cs new file mode 100644 index 000000000..adc71e599 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationMappers.cs @@ -0,0 +1,15 @@ +using LINGYUN.Abp.AIManagement.Workspaces; +using LINGYUN.Abp.AIManagement.Workspaces.Dtos; +using Riok.Mapperly.Abstractions; +using Volo.Abp.Mapperly; +using Volo.Abp.ObjectExtending; + +namespace LINGYUN.Abp.AIManagement; + +[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] +[MapExtraProperties(DefinitionChecks = MappingPropertyDefinitionChecks.None)] +public partial class WorkspaceDefinitionRecordToWorkspaceDefinitionRecordDtoMapper : MapperBase +{ + public override partial WorkspaceDefinitionRecordDto Map(WorkspaceDefinitionRecord source); + public override partial void Map(WorkspaceDefinitionRecord source, WorkspaceDefinitionRecordDto destination); +} \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationModule.cs new file mode 100644 index 000000000..4b5413042 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationModule.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Application; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.AIManagement; + +[DependsOn( + typeof(AbpAIManagementApplicationContractsModule), + typeof(AbpAIManagementDomainModule), + typeof(AbpDddApplicationModule))] +public class AbpAIManagementApplicationModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddMapperlyObjectMapper(); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs deleted file mode 100644 index 751b9bfaa..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Volo.Abp.Modularity; - -namespace LINGYUN.Abp.AIManagement; - -public class AbpAIManagementDomainSharedModule : AbpModule -{ - -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs new file mode 100644 index 000000000..e249d025b --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs @@ -0,0 +1,152 @@ +using LINGYUN.Abp.AIManagement.Localization; +using LINGYUN.Abp.AIManagement.Workspaces.Dtos; +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Application.Services; +using Volo.Abp.Data; +using Volo.Abp.Security.Encryption; + +namespace LINGYUN.Abp.AIManagement.Workspaces; +public class WorkspaceDefinitionAppService : + CrudAppService< + WorkspaceDefinitionRecord, + WorkspaceDefinitionRecordDto, + Guid, + WorkspaceDefinitionRecordGetListInput, + WorkspaceDefinitionRecordCreateDto, + WorkspaceDefinitionRecordUpdateDto>, + IWorkspaceDefinitionAppService +{ + protected IStringEncryptionService StringEncryptionService { get; } + protected IWorkspaceDefinitionRecordRepository WorkspaceDefinitionRecordRepository { get; } + public WorkspaceDefinitionAppService( + IStringEncryptionService stringEncryptionService, + IWorkspaceDefinitionRecordRepository repository) : base(repository) + { + StringEncryptionService = stringEncryptionService; + WorkspaceDefinitionRecordRepository = repository; + + LocalizationResource = typeof(AIManagementResource); + ObjectMapperContext = typeof(AbpAIManagementApplicationModule); + } + + protected async override Task> CreateFilteredQueryAsync(WorkspaceDefinitionRecordGetListInput input) + { + var queryable = await base.CreateFilteredQueryAsync(input); + + return queryable + .WhereIf(!input.Provider.IsNullOrWhiteSpace(), x => x.Provider == input.Provider) + .WhereIf(!input.ModelName.IsNullOrWhiteSpace(), x => x.ModelName == input.ModelName) + .WhereIf(!input.Filter.IsNullOrWhiteSpace(), x => x.Provider.Contains(input.Filter!) || + x.ModelName.Contains(input.Filter!) || x.DisplayName.Contains(input.Filter!) || + (!x.Description.IsNullOrWhiteSpace() && x.Description.Contains(input.Filter!)) || + (!x.SystemPrompt.IsNullOrWhiteSpace() && x.SystemPrompt.Contains(input.Filter!)) || + (!x.Instructions.IsNullOrWhiteSpace() && x.Instructions.Contains(input.Filter!))); + } + + protected async override Task MapToEntityAsync(WorkspaceDefinitionRecordCreateDto createInput) + { + if (await WorkspaceDefinitionRecordRepository.FindByNameAsync(createInput.Name) != null) + { + throw new WorkspaceAlreadyExistsException(createInput.Name); + } + + var record = new WorkspaceDefinitionRecord( + GuidGenerator.Create(), + createInput.Name, + createInput.Provider, + createInput.ModelName, + createInput.DisplayName, + createInput.Description, + createInput.SystemPrompt, + createInput.Instructions, + createInput.Temperature, + createInput.MaxOutputTokens, + createInput.FrequencyPenalty, + createInput.PresencePenalty, + createInput.StateCheckers); + + if (!createInput.ApiKey.IsNullOrWhiteSpace()) + { + var encryptApiKey = StringEncryptionService.Encrypt(createInput.ApiKey); + record.SetApiKey(encryptApiKey, createInput.ApiBaseUrl); + } + + return record; + } + + protected override void MapToEntity(WorkspaceDefinitionRecordUpdateDto updateInput, WorkspaceDefinitionRecord entity) + { + if (entity.DisplayName != updateInput.DisplayName) + { + entity.SetDisplayName(updateInput.DisplayName); + } + + if (entity.Description != updateInput.Description) + { + entity.Description = updateInput.Description; + } + + if (entity.Provider != updateInput.Provider || entity.ModelName != updateInput.ModelName) + { + entity.SetModel(updateInput.Provider, updateInput.ModelName); + } + + if (entity.SystemPrompt != updateInput.SystemPrompt) + { + entity.SystemPrompt = updateInput.SystemPrompt; + } + + if (entity.Instructions != updateInput.Instructions) + { + entity.Instructions = updateInput.Instructions; + } + + if (entity.IsEnabled != updateInput.IsEnabled) + { + entity.IsEnabled = updateInput.IsEnabled; + } + + if (entity.Temperature != updateInput.Temperature) + { + entity.Temperature = updateInput.Temperature; + } + + if (entity.MaxOutputTokens != updateInput.MaxOutputTokens) + { + entity.MaxOutputTokens = updateInput.MaxOutputTokens; + } + + if (entity.FrequencyPenalty != updateInput.FrequencyPenalty) + { + entity.FrequencyPenalty = updateInput.FrequencyPenalty; + } + + if (entity.PresencePenalty != updateInput.PresencePenalty) + { + entity.PresencePenalty = updateInput.PresencePenalty; + } + + if (entity.StateCheckers != updateInput.StateCheckers) + { + entity.StateCheckers = updateInput.StateCheckers; + } + + if (!updateInput.ApiKey.IsNullOrWhiteSpace()) + { + var encryptApiKey = StringEncryptionService.Encrypt(updateInput.ApiKey); + entity.SetApiKey(encryptApiKey, updateInput.ApiBaseUrl); + } + + if (!entity.HasSameExtraProperties(updateInput)) + { + entity.ExtraProperties.Clear(); + + foreach (var property in updateInput.ExtraProperties) + { + entity.ExtraProperties.Add(property.Key, property.Value); + } + } + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AIManagementErrorCodes.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AIManagementErrorCodes.cs new file mode 100644 index 000000000..ae295d8db --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AIManagementErrorCodes.cs @@ -0,0 +1,12 @@ +namespace LINGYUN.Abp.AIManagement; +public static class AIManagementErrorCodes +{ + public const string Namespace = "AIManagement"; + + public static class Workspace + { + public const string Prefix = Namespace + ":100"; + + public const string NameAlreadyExists = Prefix + "001"; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceAlreadyExistsException.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceAlreadyExistsException.cs new file mode 100644 index 000000000..5e23fd8b3 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceAlreadyExistsException.cs @@ -0,0 +1,16 @@ +using Volo.Abp; + +namespace LINGYUN.Abp.AIManagement.Workspaces; +public class WorkspaceAlreadyExistsException : BusinessException +{ + public string Workspace { get; } + public WorkspaceAlreadyExistsException(string workspace) + : base( + AIManagementErrorCodes.Workspace.NameAlreadyExists, + $"A Workspace named {workspace} already exists!") + { + Workspace = workspace; + + WithData(nameof(Workspace), workspace); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDbProperties.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDbProperties.cs index 852fbd74a..947baaa18 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDbProperties.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDbProperties.cs @@ -3,7 +3,7 @@ namespace LINGYUN.Abp.AIManagement; public static class AbpAIManagementDbProperties { - public static string DbTablePrefix { get; set; } = AbpCommonDbProperties.DbTablePrefix; + public static string DbTablePrefix { get; set; } = AbpCommonDbProperties.DbTablePrefix + "AI"; public static string? DbSchema { get; set; } = AbpCommonDbProperties.DbSchema; diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/FodyWeavers.xsd b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs index b6e8d2110..f3f5e9f9a 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs @@ -80,7 +80,7 @@ public static class AIManagementDbContextModelBuilderExtensions b.Property(x => x.ApiKey) .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); b.Property(x => x.ApiBaseUrl) - .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiBaseUrlLength); b.Property(x => x.SystemPrompt) .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxSystemPromptLength); b.Property(x => x.Instructions) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs deleted file mode 100644 index 751b9bfaa..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Volo.Abp.Modularity; - -namespace LINGYUN.Abp.AIManagement; - -public class AbpAIManagementDomainSharedModule : AbpModule -{ - -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionController.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionController.cs new file mode 100644 index 000000000..58444f420 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionController.cs @@ -0,0 +1,52 @@ +using LINGYUN.Abp.AIManagement.Workspaces.Dtos; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace LINGYUN.Abp.AIManagement.Workspaces; + +[Controller] +[RemoteService(Name = AIManagementRemoteServiceConsts.RemoteServiceName)] +[Area(AIManagementRemoteServiceConsts.ModuleName)] +[Route($"api/{AIManagementRemoteServiceConsts.ModuleName}/workspaces")] +public class WorkspaceDefinitionController : AbpControllerBase, IWorkspaceDefinitionAppService +{ + private readonly IWorkspaceDefinitionAppService _service; + public WorkspaceDefinitionController(IWorkspaceDefinitionAppService service) + { + _service = service; + } + + [HttpPost] + public virtual Task CreateAsync(WorkspaceDefinitionRecordCreateDto input) + { + return _service.CreateAsync(input); + } + + [HttpDelete("{id}")] + public virtual Task DeleteAsync(Guid id) + { + return _service.DeleteAsync(id); + } + + [HttpGet("{id}")] + public virtual Task GetAsync(Guid id) + { + return _service.GetAsync(id); + } + + [HttpGet] + public virtual Task> GetListAsync(WorkspaceDefinitionRecordGetListInput input) + { + return _service.GetListAsync(input); + } + + [HttpPut("{id}")] + public virtual Task UpdateAsync(Guid id, WorkspaceDefinitionRecordUpdateDto input) + { + return _service.UpdateAsync(id, input); + } +} From c64935f5c4b163dbedd63b8526eeb9752b116cb9 Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 28 Jan 2026 11:48:02 +0800 Subject: [PATCH 21/88] Add Conversation management interface implementation --- .../Chats/Dtos/ConversationCreateDto.cs | 8 ++ .../Chats/Dtos/ConversationDto.cs | 14 ++++ .../Chats/Dtos/ConversationGetListInput.cs | 7 ++ .../Chats/Dtos/ConversationUpdateDto.cs | 10 +++ .../Chats/IConversationAppService.cs | 14 ++++ .../Chats/ConversationAppService.cs | 82 +++++++++++++++++++ .../AIManagement/Chats/ConversationRecord.cs | 5 ++ .../Chats/ConversationController.cs | 52 ++++++++++++ 8 files changed, 192 insertions(+) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationCreateDto.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationDto.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationGetListInput.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationUpdateDto.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/IConversationAppService.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ConversationController.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationCreateDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationCreateDto.cs new file mode 100644 index 000000000..8112305b2 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationCreateDto.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Validation; + +namespace LINGYUN.Abp.AIManagement.Chats.Dtos; +public class ConversationCreateDto +{ + [DynamicStringLength(typeof(ConversationRecordConsts), nameof(ConversationRecordConsts.MaxNameLength))] + public string? Name { get; set; } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationDto.cs new file mode 100644 index 000000000..8f5945c95 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationDto.cs @@ -0,0 +1,14 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace LINGYUN.Abp.AIManagement.Chats.Dtos; +public class ConversationDto : AuditedEntityDto +{ + public string Name { get; set; } + + public DateTime CreatedAt { get; set; } + + public DateTime ExpiredAt { get; set; } + + public DateTime? UpdateAt { get; set; } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationGetListInput.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationGetListInput.cs new file mode 100644 index 000000000..d3b889f48 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationGetListInput.cs @@ -0,0 +1,7 @@ +using Volo.Abp.Application.Dtos; + +namespace LINGYUN.Abp.AIManagement.Chats.Dtos; +public class ConversationGetListInput : PagedAndSortedResultRequestDto +{ + public string? Filter { get; set; } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationUpdateDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationUpdateDto.cs new file mode 100644 index 000000000..4469eba6d --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationUpdateDto.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Validation; + +namespace LINGYUN.Abp.AIManagement.Chats.Dtos; +public class ConversationUpdateDto +{ + [Required] + [DynamicStringLength(typeof(ConversationRecordConsts), nameof(ConversationRecordConsts.MaxNameLength))] + public string Name { get; set; } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/IConversationAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/IConversationAppService.cs new file mode 100644 index 000000000..a59c397cb --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/IConversationAppService.cs @@ -0,0 +1,14 @@ +using LINGYUN.Abp.AIManagement.Chats.Dtos; +using System; +using Volo.Abp.Application.Services; + +namespace LINGYUN.Abp.AIManagement.Chats; +public interface IConversationAppService : + ICrudAppService< + ConversationDto, + Guid, + ConversationGetListInput, + ConversationCreateDto, + ConversationUpdateDto> +{ +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs new file mode 100644 index 000000000..203747602 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs @@ -0,0 +1,82 @@ +using LINGYUN.Abp.AIManagement.Chats.Dtos; +using LINGYUN.Abp.AIManagement.Localization; +using Microsoft.Extensions.Options; +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace LINGYUN.Abp.AIManagement.Chats; +public class ConversationAppService : + CrudAppService< + ConversationRecord, + ConversationDto, + Guid, + ConversationGetListInput, + ConversationCreateDto, + ConversationUpdateDto>, + IConversationAppService +{ + private readonly ConversationCleanupOptions _cleanupOptions; + public ConversationAppService( + IRepository repository, + IOptions cleanupOptions) + : base(repository) + { + _cleanupOptions = cleanupOptions.Value; + + LocalizationResource = typeof(AIManagementResource); + ObjectMapperContext = typeof(AbpAIManagementApplicationModule); + } + + protected async override Task> CreateFilteredQueryAsync(ConversationGetListInput input) + { + var queryable = await base.CreateFilteredQueryAsync(input); + + return queryable + .WhereIf(!input.Filter.IsNullOrWhiteSpace(), x => x.Name.Contains(input.Filter!)); + } + + protected override ConversationRecord MapToEntity(ConversationCreateDto createInput) + { + var createdAt = Clock.Now; + var expiredTime = createdAt.Add(_cleanupOptions.ExpiredTime); + var conversationName = createInput.Name ?? L["NewConversation"]; + return new ConversationRecord( + GuidGenerator.Create(), + conversationName, + createdAt, + expiredTime, + CurrentTenant.Id); + } + + protected override void MapToEntity(ConversationUpdateDto updateInput, ConversationRecord entity) + { + if (!string.Equals(entity.Name, updateInput.Name, StringComparison.InvariantCultureIgnoreCase)) + { + entity.SetName(updateInput.Name); + } + } + + protected override ConversationDto MapToGetOutputDto(ConversationRecord entity) + { + return new ConversationDto + { + Id = entity.Id, + CreatedAt = entity.CreatedAt, + ExpiredAt = entity.ExpiredAt, + CreationTime = entity.CreationTime, + CreatorId = entity.CreatorId, + LastModificationTime = entity.LastModificationTime, + LastModifierId = entity.LastModifierId, + Name = entity.Name, + UpdateAt = entity.UpdateAt, + }; + } + + protected override ConversationDto MapToGetListOutputDto(ConversationRecord entity) + { + return MapToGetOutputDto(entity); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationRecord.cs index cd81fcac6..10192ab8b 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationRecord.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationRecord.cs @@ -31,4 +31,9 @@ public class ConversationRecord : AuditedEntity, IMultiTenant TenantId = tenantId; } + + public void SetName(string name) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name), ConversationRecordConsts.MaxNameLength); + } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ConversationController.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ConversationController.cs new file mode 100644 index 000000000..e90aced0b --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ConversationController.cs @@ -0,0 +1,52 @@ +using LINGYUN.Abp.AIManagement.Chats.Dtos; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace LINGYUN.Abp.AIManagement.Chats; + +[Controller] +[RemoteService(Name = AIManagementRemoteServiceConsts.RemoteServiceName)] +[Area(AIManagementRemoteServiceConsts.ModuleName)] +[Route($"api/{AIManagementRemoteServiceConsts.ModuleName}/chats/conversations")] +public class ConversationController : AbpControllerBase, IConversationAppService +{ + private readonly IConversationAppService _service; + public ConversationController(IConversationAppService service) + { + _service = service; + } + + [HttpPost] + public virtual Task CreateAsync(ConversationCreateDto input) + { + return _service.CreateAsync(input); + } + + [HttpDelete("{id}")] + public virtual Task DeleteAsync(Guid id) + { + return _service.DeleteAsync(id); + } + + [HttpGet("{id}")] + public virtual Task GetAsync(Guid id) + { + return _service.GetAsync(id); + } + + [HttpGet] + public virtual Task> GetListAsync(ConversationGetListInput input) + { + return _service.GetListAsync(input); + } + + [HttpPut("{id}")] + public virtual Task UpdateAsync(Guid id, ConversationUpdateDto input) + { + return _service.UpdateAsync(id, input); + } +} From 024d90472238393a0025f7a59cedde1ab4b59b5e Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 28 Jan 2026 11:48:55 +0800 Subject: [PATCH 22/88] feat: Encrypt the Workspace ApiKey --- ...icWorkspaceDefinitionStoreInMemoryCache.cs | 7 ++- .../IWorkspaceDefinitionRecordRepository.cs | 2 +- .../Workspaces/WorkspaceDefinitionRecord.cs | 23 +++++++--- .../WorkspaceDefinitionSerializer.cs | 13 +++++- .../Workspaces/WorkspaceDynamicInitializer.cs | 45 +++++++------------ 5 files changed, 53 insertions(+), 37 deletions(-) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreInMemoryCache.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreInMemoryCache.cs index 478ededa9..3d7d0d142 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreInMemoryCache.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreInMemoryCache.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Localization; +using Volo.Abp.Security.Encryption; using Volo.Abp.SimpleStateChecking; namespace LINGYUN.Abp.AIManagement.Workspaces; @@ -13,6 +14,7 @@ public class DynamicWorkspaceDefinitionStoreInMemoryCache : IDynamicWorkspaceDef { public string CacheStamp { get; set; } protected IDictionary WorkspaceDefinitions { get; } + protected IStringEncryptionService StringEncryptionService { get; } protected ISimpleStateCheckerSerializer StateCheckerSerializer { get; } protected ILocalizableStringSerializer LocalizableStringSerializer { get; } @@ -21,9 +23,11 @@ public class DynamicWorkspaceDefinitionStoreInMemoryCache : IDynamicWorkspaceDef public DateTime? LastCheckTime { get; set; } public DynamicWorkspaceDefinitionStoreInMemoryCache( + IStringEncryptionService stringEncryptionService, ISimpleStateCheckerSerializer stateCheckerSerializer, ILocalizableStringSerializer localizableStringSerializer) { + StringEncryptionService = stringEncryptionService; StateCheckerSerializer = stateCheckerSerializer; LocalizableStringSerializer = localizableStringSerializer; @@ -51,7 +55,8 @@ public class DynamicWorkspaceDefinitionStoreInMemoryCache : IDynamicWorkspaceDef if (!workspace.ApiKey.IsNullOrWhiteSpace()) { - workspaceDef.WithApiKey(workspace.ApiKey); + var decryptApiKey = StringEncryptionService.Decrypt(workspace.ApiKey); + workspaceDef.WithApiKey(decryptApiKey!); } if (!workspace.ApiBaseUrl.IsNullOrWhiteSpace()) { diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionRecordRepository.cs index e4b566cee..4c893dd04 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionRecordRepository.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionRecordRepository.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; namespace LINGYUN.Abp.AIManagement.Workspaces; -public interface IWorkspaceDefinitionRecordRepository : IBasicRepository +public interface IWorkspaceDefinitionRecordRepository : IRepository { Task FindByNameAsync( string name, diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecord.cs index 1e75cba0b..e93500b17 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecord.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecord.cs @@ -14,7 +14,7 @@ public class WorkspaceDefinitionRecord : AuditedAggregateRoot public string DisplayName { get; private set; } - public string? Description { get; private set; } + public string? Description { get; set; } public string? ApiKey { get; private set; } @@ -49,8 +49,6 @@ public class WorkspaceDefinitionRecord : AuditedAggregateRoot string modelName, string displayName, string? description = null, - string? apiKey = null, - string? apiBaseUrl = null, string? systemPrompt = null, string? instructions = null, float? temperature = null, @@ -65,8 +63,6 @@ public class WorkspaceDefinitionRecord : AuditedAggregateRoot ModelName = Check.NotNullOrWhiteSpace(modelName, nameof(modelName), WorkspaceDefinitionRecordConsts.MaxModelNameLength); DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName), WorkspaceDefinitionRecordConsts.MaxDisplayNameLength); Description = Check.Length(description, nameof(description), WorkspaceDefinitionRecordConsts.MaxDescriptionLength); - ApiKey = Check.Length(apiKey, nameof(apiKey), WorkspaceDefinitionRecordConsts.MaxApiKeyLength); - ApiBaseUrl = Check.Length(apiBaseUrl, nameof(apiBaseUrl), WorkspaceDefinitionRecordConsts.MaxApiBaseUrlLength); SystemPrompt = Check.Length(systemPrompt, nameof(systemPrompt), WorkspaceDefinitionRecordConsts.MaxSystemPromptLength); Instructions = Check.Length(instructions, nameof(instructions), WorkspaceDefinitionRecordConsts.MaxInstructionsLength); StateCheckers = Check.Length(stateCheckers, nameof(stateCheckers), WorkspaceDefinitionRecordConsts.MaxStateCheckersLength); @@ -80,6 +76,23 @@ public class WorkspaceDefinitionRecord : AuditedAggregateRoot this.SetDefaultsForExtraProperties(); } + public void SetDisplayName(string displayName) + { + DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName), WorkspaceDefinitionRecordConsts.MaxDisplayNameLength); + } + + public void SetModel(string provider, string modelName) + { + Provider = Check.NotNullOrWhiteSpace(provider, nameof(provider), WorkspaceDefinitionRecordConsts.MaxProviderLength); + ModelName = Check.NotNullOrWhiteSpace(modelName, nameof(modelName), WorkspaceDefinitionRecordConsts.MaxModelNameLength); + } + + public void SetApiKey(string? apiKey = null, string? apiBaseUrl = null) + { + ApiKey = Check.Length(apiKey, nameof(apiKey), WorkspaceDefinitionRecordConsts.MaxApiKeyLength); + ApiBaseUrl = Check.Length(apiBaseUrl, nameof(apiBaseUrl), WorkspaceDefinitionRecordConsts.MaxApiBaseUrlLength); + } + public bool HasSameData(WorkspaceDefinitionRecord otherWorkspace) { if (Name != otherWorkspace.Name) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs index 3c69f73fd..94096f797 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs @@ -1,4 +1,5 @@ using LINGYUN.Abp.AI.Workspaces; +using System; using System.Collections.Generic; using System.Globalization; using System.Threading.Tasks; @@ -6,21 +7,25 @@ using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Guids; using Volo.Abp.Localization; +using Volo.Abp.Security.Encryption; using Volo.Abp.SimpleStateChecking; namespace LINGYUN.Abp.AIManagement.Workspaces; public class WorkspaceDefinitionSerializer : IWorkspaceDefinitionSerializer, ITransientDependency { protected IGuidGenerator GuidGenerator { get; } + protected IStringEncryptionService StringEncryptionService { get; } protected ISimpleStateCheckerSerializer StateCheckerSerializer { get; } protected ILocalizableStringSerializer LocalizableStringSerializer { get; } public WorkspaceDefinitionSerializer( IGuidGenerator guidGenerator, + IStringEncryptionService stringEncryptionService, ISimpleStateCheckerSerializer stateCheckerSerializer, ILocalizableStringSerializer localizableStringSerializer) { GuidGenerator = guidGenerator; + StringEncryptionService = stringEncryptionService; StateCheckerSerializer = stateCheckerSerializer; LocalizableStringSerializer = localizableStringSerializer; } @@ -47,8 +52,6 @@ public class WorkspaceDefinitionSerializer : IWorkspaceDefinitionSerializer, ITr definition.ModelName, LocalizableStringSerializer.Serialize(definition.DisplayName)!, definition.Description != null ? LocalizableStringSerializer.Serialize(definition.Description) : null, - definition.ApiKey, - definition.ApiBaseUrl, definition.SystemPrompt, definition.Instructions, definition.Temperature, @@ -57,6 +60,12 @@ public class WorkspaceDefinitionSerializer : IWorkspaceDefinitionSerializer, ITr definition.PresencePenalty, SerializeStateCheckers(definition.StateCheckers)); + if (!definition.ApiKey.IsNullOrWhiteSpace()) + { + var encryptApiKey = StringEncryptionService.Encrypt(definition.ApiKey); + workspace.SetApiKey(encryptApiKey, definition.ApiBaseUrl); + } + foreach (var property in definition.Properties) { workspace.SetProperty(property.Key, property.Value); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDynamicInitializer.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDynamicInitializer.cs index 591535580..793814d3a 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDynamicInitializer.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDynamicInitializer.cs @@ -1,5 +1,4 @@ -using JetBrains.Annotations; -using LINGYUN.Abp.AI.Workspaces; +using LINGYUN.Abp.AI.Workspaces; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -18,33 +17,17 @@ public class WorkspaceDynamicInitializer : ITransientDependency public ILogger Logger { get; set; } protected IServiceProvider ServiceProvider { get; } - protected IOptions Options { get; } - [CanBeNull] - protected IHostApplicationLifetime ApplicationLifetime { get; } - protected ICancellationTokenProvider CancellationTokenProvider { get; } - protected IDynamicWorkspaceDefinitionStore DynamicWorkspaceDefinitionStore { get; } - protected IStaticWorkspaceSaver StaticWorkspaceSaver { get; } - - public WorkspaceDynamicInitializer( - IServiceProvider serviceProvider, - IOptions options, - ICancellationTokenProvider cancellationTokenProvider, - IDynamicWorkspaceDefinitionStore dynamicWorkspaceDefinitionStore, - IStaticWorkspaceSaver staticWorkspaceSaver) + + public WorkspaceDynamicInitializer(IServiceProvider serviceProvider) { Logger = NullLogger.Instance; ServiceProvider = serviceProvider; - Options = options; - CancellationTokenProvider = cancellationTokenProvider; - DynamicWorkspaceDefinitionStore = dynamicWorkspaceDefinitionStore; - StaticWorkspaceSaver = staticWorkspaceSaver; - ApplicationLifetime = ServiceProvider.GetRequiredService(); } public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default) { - var options = Options.Value; + var options = ServiceProvider.GetRequiredService>().Value; if (!options.SaveStaticWorkspacesToDatabase && !options.IsDynamicWorkspaceStoreEnabled) { @@ -53,11 +36,12 @@ public class WorkspaceDynamicInitializer : ITransientDependency if (runInBackground) { + var applicationLifetime = ServiceProvider.GetService(); Task.Run(async () => { - if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null) + if (cancellationToken == default && applicationLifetime?.ApplicationStopping != null) { - cancellationToken = ApplicationLifetime.ApplicationStopping; + cancellationToken = applicationLifetime.ApplicationStopping; } await ExecuteInitializationAsync(options, cancellationToken); }, cancellationToken); @@ -72,16 +56,17 @@ public class WorkspaceDynamicInitializer : ITransientDependency { try { - using (CancellationTokenProvider.Use(cancellationToken)) + var cancellationTokenProvider = ServiceProvider.GetRequiredService(); + using (cancellationTokenProvider.Use(cancellationToken)) { - if (CancellationTokenProvider.Token.IsCancellationRequested) + if (cancellationTokenProvider.Token.IsCancellationRequested) { return; } await SaveStaticWorkspacesToDatabaseAsync(options, cancellationToken); - if (CancellationTokenProvider.Token.IsCancellationRequested) + if (cancellationTokenProvider.Token.IsCancellationRequested) { return; } @@ -104,6 +89,8 @@ public class WorkspaceDynamicInitializer : ITransientDependency return; } + var staticWorkspaceSaver = ServiceProvider.GetRequiredService(); + await Policy .Handle(ex => ex is not OperationCanceledException) .WaitAndRetryAsync( @@ -118,7 +105,7 @@ public class WorkspaceDynamicInitializer : ITransientDependency { try { - await StaticWorkspaceSaver.SaveAsync(); + await staticWorkspaceSaver.SaveAsync(); } catch (Exception ex) { @@ -135,10 +122,12 @@ public class WorkspaceDynamicInitializer : ITransientDependency return; } + var dynamicWorkspaceDefinitionStore = ServiceProvider.GetRequiredService(); + try { // Pre-cache Workspaces, so first request doesn't wait - await DynamicWorkspaceDefinitionStore.GetAllAsync(); + await dynamicWorkspaceDefinitionStore.GetAllAsync(); } catch (Exception ex) { From 01e61c29a661712460cbc98fd434970bf96035b3 Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 28 Jan 2026 11:50:21 +0800 Subject: [PATCH 23/88] feat: Add Chat interface implementation --- .../AIManagement/Chats/Dtos/ChatMessageDto.cs | 14 +++++++ .../Chats/Dtos/SendTextChatMessageDto.cs | 19 +++++++++ .../Chats/Dtos/TextChatMessageDto.cs | 8 ++++ .../Abp/AIManagement/Chats/IChatAppService.cs | 9 ++++ .../Abp/AIManagement/Chats/ChatAppService.cs | 28 +++++++++++++ .../FodyWeavers.xsd | 30 +++++++++++++ .../LINGYUN.Abp.AIManagement.Domain.csproj | 2 +- .../AbpAIManagementDomainModule.cs | 14 ++++++- .../AbpAIManagementHttpApiModule.cs | 41 ++++++++++++++++++ .../Abp/AIManagement/Chats/ChatController.cs | 30 +++++++++++++ .../Mvc/SseAsyncEnumerableResult.cs | 42 +++++++++++++++++++ .../Mvc/SseAsyncEnumerableResultFilter.cs | 18 ++++++++ 12 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ChatMessageDto.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/SendTextChatMessageDto.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/TextChatMessageDto.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/IChatAppService.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ChatAppService.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/FodyWeavers.xsd create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/AbpAIManagementHttpApiModule.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ChatController.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResult.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResultFilter.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ChatMessageDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ChatMessageDto.cs new file mode 100644 index 000000000..035fc1464 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ChatMessageDto.cs @@ -0,0 +1,14 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace LINGYUN.Abp.AIManagement.Chats.Dtos; +public abstract class ChatMessageDto : ExtensibleAuditedEntityDto +{ + public string Workspace { get; set; } + + public DateTime CreatedAt { get; set; } + + public Guid? UserId { get; set; } + + public Guid? ConversationId { get; set; } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/SendTextChatMessageDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/SendTextChatMessageDto.cs new file mode 100644 index 000000000..ad0372260 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/SendTextChatMessageDto.cs @@ -0,0 +1,19 @@ +using LINGYUN.Abp.AIManagement.Workspaces; +using System; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Validation; + +namespace LINGYUN.Abp.AIManagement.Chats.Dtos; +public class SendTextChatMessageDto +{ + [Required] + [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxNameLength))] + public string Workspace { get; set; } + + [Required] + public Guid ConversationId { get; set; } + + [Required] + [DynamicStringLength(typeof(TextChatMessageRecordConsts), nameof(TextChatMessageRecordConsts.MaxContentLength))] + public string Content { get; set; } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/TextChatMessageDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/TextChatMessageDto.cs new file mode 100644 index 000000000..1ef328229 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/TextChatMessageDto.cs @@ -0,0 +1,8 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace LINGYUN.Abp.AIManagement.Chats.Dtos; +public class TextChatMessageDto +{ +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/IChatAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/IChatAppService.cs new file mode 100644 index 000000000..dcfaa06ca --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/IChatAppService.cs @@ -0,0 +1,9 @@ +using LINGYUN.Abp.AIManagement.Chats.Dtos; +using System.Collections.Generic; +using Volo.Abp.Application.Services; + +namespace LINGYUN.Abp.AIManagement.Chats; +public interface IChatAppService : IApplicationService +{ + IAsyncEnumerable SendMessageAsync(SendTextChatMessageDto input); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ChatAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ChatAppService.cs new file mode 100644 index 000000000..fe420a66a --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ChatAppService.cs @@ -0,0 +1,28 @@ +using LINGYUN.Abp.AI.Agent; +using LINGYUN.Abp.AI.Models; +using LINGYUN.Abp.AIManagement.Chats.Dtos; +using Microsoft.Extensions.AI; +using System.Collections.Generic; + +namespace LINGYUN.Abp.AIManagement.Chats; +public class ChatAppService : AIManagementApplicationServiceBase, IChatAppService +{ + private readonly IAgentService _agentService; + public ChatAppService(IAgentService agentService) + { + _agentService = agentService; + } + + public IAsyncEnumerable SendMessageAsync(SendTextChatMessageDto input) + { + var chatMessage = new TextChatMessage( + input.Workspace, + input.Content, + ChatRole.User, + Clock.Now); + + chatMessage.WithConversationId(input.ConversationId); + + return _agentService.SendMessageAsync(chatMessage); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/FodyWeavers.xsd b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj index 2cc7884dc..813624bf7 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj @@ -23,7 +23,7 @@ - + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainModule.cs index 8d139de9b..75ba502f1 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainModule.cs @@ -1,4 +1,6 @@ -using LINGYUN.Abp.AI; +using LINGYUN.Abp.AI.Agent; +using LINGYUN.Abp.AI.Localization; +using LINGYUN.Abp.AIManagement.Localization; using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.Extensions.DependencyInjection; using System.Threading; @@ -8,6 +10,7 @@ using Volo.Abp.Caching; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain; +using Volo.Abp.Localization; using Volo.Abp.Mapperly; using Volo.Abp.Modularity; using Volo.Abp.Threading; @@ -16,7 +19,7 @@ namespace LINGYUN.Abp.AIManagement; [DependsOn( typeof(AbpAIManagementDomainSharedModule), - typeof(AbpAICoreModule), + typeof(AbpAIAgentModule), typeof(AbpCachingModule), typeof(AbpMapperlyModule), typeof(AbpDddDomainModule))] @@ -37,6 +40,13 @@ public class AbpAIManagementDomainModule : AbpModule options.IsDynamicWorkspaceStoreEnabled = false; }); } + + Configure(options => + { + options.Resources + .Get() + .AddBaseTypes(typeof(AbpAIResource)); + }); } public override void OnApplicationInitialization(ApplicationInitializationContext context) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/AbpAIManagementHttpApiModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/AbpAIManagementHttpApiModule.cs new file mode 100644 index 000000000..7e119483f --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/AbpAIManagementHttpApiModule.cs @@ -0,0 +1,41 @@ +using LINGYUN.Abp.AIManagement.Localization; +using Localization.Resources.AbpUi; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.Localization; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.AIManagement; + +[DependsOn( + typeof(AbpAIManagementApplicationContractsModule), + typeof(AbpAspNetCoreMvcModule))] +public class AbpAIManagementHttpApiModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + PreConfigure(options => + { + options.AddAssemblyResource(typeof(AIManagementResource), typeof(AbpAIManagementApplicationContractsModule).Assembly); + }); + + PreConfigure(mvcBuilder => + { + mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAIManagementHttpApiModule).Assembly); + }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Resources + .Get() + .AddBaseTypes(typeof(AbpUiResource)); + }); + + context.Services.AddTransient(); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ChatController.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ChatController.cs new file mode 100644 index 000000000..811a29c56 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ChatController.cs @@ -0,0 +1,30 @@ +using LINGYUN.Abp.AIManagement.Chats.Dtos; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using Volo.Abp; +using Volo.Abp.AspNetCore.Mvc; + +namespace LINGYUN.Abp.AIManagement.Chats; + +[Controller] +[RemoteService(Name = AIManagementRemoteServiceConsts.RemoteServiceName)] +[Area(AIManagementRemoteServiceConsts.ModuleName)] +[Route($"api/{AIManagementRemoteServiceConsts.ModuleName}/chats")] +public class ChatController : AbpControllerBase, IChatAppService +{ + private readonly IChatAppService _service; + public ChatController(IChatAppService service) + { + _service = service; + } + + [HttpPost] + [ServiceFilter] + public async virtual IAsyncEnumerable SendMessageAsync(SendTextChatMessageDto input) + { + await foreach (var content in _service.SendMessageAsync(input)) + { + yield return content; + } + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResult.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResult.cs new file mode 100644 index 000000000..f44e8f911 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResult.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Mvc; +public class SseAsyncEnumerableResult : IActionResult +{ + private readonly IAsyncEnumerable _asyncEnumerable; + public SseAsyncEnumerableResult(IAsyncEnumerable asyncEnumerable) + { + _asyncEnumerable = asyncEnumerable; + } + public async Task ExecuteResultAsync(ActionContext context) + { + var response = context.HttpContext.Response; + + response.Headers.Append("Content-Type", "text/event-stream"); + response.Headers.Append("Cache-Control", "no-cache"); + response.Headers.Append("Connection", "keep-alive"); + response.Headers.Append("X-Accel-Buffering", "no"); + + try + { + await foreach (var content in _asyncEnumerable) + { + if (!string.IsNullOrEmpty(content)) + { + await response.WriteAsync($"data: {content}\n\n"); + await response.Body.FlushAsync(); + } + } + + await response.WriteAsync("data: [DONE]\n\n"); + await response.Body.FlushAsync(); + } + catch (OperationCanceledException) + { + // ignore + } + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResultFilter.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResultFilter.cs new file mode 100644 index 000000000..55d20014e --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResultFilter.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Mvc.Filters; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Mvc; +public class SseAsyncEnumerableResultFilter : IAsyncActionFilter +{ + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + var executedContext = await next(); + + if (executedContext.Result is ObjectResult objectResult && + objectResult.Value is IAsyncEnumerable asyncEnumerable) + { + executedContext.Result = new SseAsyncEnumerableResult(asyncEnumerable); + } + } +} From ce7d9a6c120cff94b66b0be508ce05abee3bd37c Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 28 Jan 2026 11:51:21 +0800 Subject: [PATCH 24/88] feat: Introduce AI management for microservices --- .../AIServiceDbMigratorHostedService.cs | 53 +++ .../AIServiceDbMigratorModule.cs | 13 + .../FodyWeavers.xml | 3 + ...p.MicroService.AIService.DbMigrator.csproj | 40 ++ .../Program.cs | 43 ++ .../appsettings.json | 5 + .../AIServiceDataSeeder.cs | 26 ++ .../AIServiceDbMigrationEventHandler.cs | 44 ++ .../AIServiceDbMigrationService.cs | 36 ++ .../AIServiceMigrationsDbContext.cs | 32 ++ .../AIServiceMigrationsDbContextFactory.cs | 28 ++ ...viceMigrationsEntityFrameworkCoreModule.cs | 45 ++ .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 ++ ...rvice.AIService.EntityFrameworkCore.csproj | 35 ++ ...60127083027_Initial_AI_Service.Designer.cs | 303 +++++++++++++ .../20260127083027_Initial_AI_Service.cs | 148 ++++++ ...ServiceMigrationsDbContextModelSnapshot.cs | 300 +++++++++++++ .../AIServiceModule.Configure.cs | 423 ++++++++++++++++++ .../AIServiceModule.cs | 103 +++++ .../FodyWeavers.xml | 3 + .../LINGYUN.Abp.MicroService.AIService.csproj | 71 +++ .../Program.cs | 99 ++++ .../Properties/launchSettings.json | 12 + .../TenantHeaderParamter.cs | 35 ++ .../appsettings.Development.json | 116 +++++ .../appsettings.json | 91 ++++ .../yarp.json | 25 ++ .../AppHost.cs | 12 + .../LINGYUN.Abp.MicroService.AppHost.csproj | 2 + 30 files changed, 2179 insertions(+) create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/AIServiceDbMigratorHostedService.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/AIServiceDbMigratorModule.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/FodyWeavers.xml create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/LINGYUN.Abp.MicroService.AIService.DbMigrator.csproj create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/Program.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/appsettings.json create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDataSeeder.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDbMigrationEventHandler.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDbMigrationService.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContext.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContextFactory.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsEntityFrameworkCoreModule.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/FodyWeavers.xml create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/FodyWeavers.xsd create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore.csproj create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260127083027_Initial_AI_Service.Designer.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260127083027_Initial_AI_Service.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.Configure.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/FodyWeavers.xml create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/Program.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/Properties/launchSettings.json create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/TenantHeaderParamter.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.Development.json create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.json diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/AIServiceDbMigratorHostedService.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/AIServiceDbMigratorHostedService.cs new file mode 100644 index 000000000..2590ba079 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/AIServiceDbMigratorHostedService.cs @@ -0,0 +1,53 @@ +using LINGYUN.Abp.MicroService.AIService.DbMigrator; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Serilog; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Data; + +namespace LINGYUN.Abp.MicroService.AIService; +public class AIServiceDbMigratorHostedService : IHostedService +{ + private readonly IHostApplicationLifetime _hostApplicationLifetime; + private readonly IConfiguration _configuration; + + public AIServiceDbMigratorHostedService( + IHostApplicationLifetime hostApplicationLifetime, + IConfiguration configuration) + { + _hostApplicationLifetime = hostApplicationLifetime; + _configuration = configuration; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + using var application = await AbpApplicationFactory + .CreateAsync(options => + { + options.Services.ReplaceConfiguration(_configuration); + options.UseAutofac(); + options.Services.AddLogging(c => c.AddSerilog()); + options.AddDataMigrationEnvironment(); + }); + + await application.InitializeAsync(); + + await application + .ServiceProvider + .GetRequiredService() + .CheckAndApplyDatabaseMigrationsAsync(); + + await application.ShutdownAsync(); + + _hostApplicationLifetime.StopApplication(); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/AIServiceDbMigratorModule.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/AIServiceDbMigratorModule.cs new file mode 100644 index 000000000..ff9dd1c85 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/AIServiceDbMigratorModule.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.MicroService.AIService.DbMigrator; + +[DependsOn( + typeof(AbpAutofacModule), + typeof(AIServiceMigrationsEntityFrameworkCoreModule) + )] +public class AIServiceDbMigratorModule : AbpModule +{ + +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/FodyWeavers.xml b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/LINGYUN.Abp.MicroService.AIService.DbMigrator.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/LINGYUN.Abp.MicroService.AIService.DbMigrator.csproj new file mode 100644 index 000000000..662765cf3 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/LINGYUN.Abp.MicroService.AIService.DbMigrator.csproj @@ -0,0 +1,40 @@ + + + + + + Exe + net10.0 + enable + false + LINGYUN.Abp.MicroService.AIService + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + Always + + + + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/Program.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/Program.cs new file mode 100644 index 000000000..135bb215b --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/Program.cs @@ -0,0 +1,43 @@ +using LINGYUN.Abp.MicroService.AIService; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Serilog; +using Serilog.Events; +using System; + +var defaultOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"; + +Log.Logger = new LoggerConfiguration() + .MinimumLevel.Information() + .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) + .MinimumLevel.Override("Volo.Abp", LogEventLevel.Warning) +#if DEBUG + .MinimumLevel.Override("LINGYUN.Abp.MicroService.AIService", LogEventLevel.Debug) +#else + .MinimumLevel.Override("LINGYUN.Abp.MicroService.AIService", LogEventLevel.Information) +#endif + .Enrich.FromLogContext() + .WriteTo.Async(x => x.Console(outputTemplate: defaultOutputTemplate)) + .WriteTo.Async(x => x.File("Logs/migrations.txt", outputTemplate: defaultOutputTemplate)) + .CreateLogger(); + +try +{ + var builder = Host.CreateDefaultBuilder(args) + .AddAppSettingsSecretsJson() + .ConfigureLogging((context, logging) => logging.ClearProviders()) + .ConfigureServices((hostContext, services) => + { + services.AddHostedService(); + }); + await builder.RunConsoleAsync(); +} +catch (Exception ex) +{ + Log.Fatal(ex, "Host terminated unexpectedly!"); +} +finally +{ + await Log.CloseAndFlushAsync(); +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/appsettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/appsettings.json new file mode 100644 index 000000000..38212c579 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "Default": "Host=127.0.0.1;Database=abp;Username=postgres;Password=123456" + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDataSeeder.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDataSeeder.cs new file mode 100644 index 000000000..8a088b76d --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDataSeeder.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.MicroService.AIService; +public class AIServiceDataSeeder : ITransientDependency +{ + protected ILogger Logger { get; } + protected ICurrentTenant CurrentTenant { get; } + + public AIServiceDataSeeder( + ICurrentTenant currentTenant) + { + CurrentTenant = currentTenant; + + Logger = NullLogger.Instance; + } + + public virtual Task SeedAsync(DataSeedContext context) + { + return Task.CompletedTask; + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDbMigrationEventHandler.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDbMigrationEventHandler.cs new file mode 100644 index 000000000..b05842e54 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDbMigrationEventHandler.cs @@ -0,0 +1,44 @@ +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DistributedLocking; +using Volo.Abp.EntityFrameworkCore.Migrations; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Uow; + +namespace LINGYUN.Abp.MicroService.AIService; +public class AIServiceDbMigrationEventHandler : EfCoreDatabaseMigrationEventHandlerBase +{ + protected AIServiceDataSeeder DataSeeder { get; } + + public AIServiceDbMigrationEventHandler( + ICurrentTenant currentTenant, + IUnitOfWorkManager unitOfWorkManager, + ITenantStore tenantStore, + IAbpDistributedLock abpDistributedLock, + IDistributedEventBus distributedEventBus, + ILoggerFactory loggerFactory, + AIServiceDataSeeder dataSeeder) + : base( + ConnectionStringNameAttribute.GetConnStringName(), + currentTenant, unitOfWorkManager, tenantStore, abpDistributedLock, distributedEventBus, loggerFactory) + { + DataSeeder = dataSeeder; + } + + protected async override Task AfterTenantCreated(TenantCreatedEto eventData, bool schemaMigrated) + { + // 新租户数据种子 + var context = new DataSeedContext(eventData.Id); + if (eventData.Properties != null) + { + foreach (var property in eventData.Properties) + { + context.WithProperty(property.Key, property.Value); + } + } + + await DataSeeder.SeedAsync(context); + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDbMigrationService.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDbMigrationService.cs new file mode 100644 index 000000000..445f0fd92 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDbMigrationService.cs @@ -0,0 +1,36 @@ +using LINGYUN.Abp.Data.DbMigrator; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.DistributedLocking; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Uow; + +namespace LINGYUN.Abp.MicroService.AIService; +public class AIServiceDbMigrationService : EfCoreRuntimeDbMigratorBase, ITransientDependency +{ + protected AIServiceDataSeeder DataSeeder { get; } + public AIServiceDbMigrationService( + ICurrentTenant currentTenant, + IUnitOfWorkManager unitOfWorkManager, + IServiceProvider serviceProvider, + IAbpDistributedLock abpDistributedLock, + IDistributedEventBus distributedEventBus, + ILoggerFactory loggerFactory, + AIServiceDataSeeder dataSeeder) + : base( + ConnectionStringNameAttribute.GetConnStringName(), + unitOfWorkManager, serviceProvider, currentTenant, abpDistributedLock, distributedEventBus, loggerFactory) + { + DataSeeder = dataSeeder; + } + + protected async override Task SeedAsync() + { + // DbMigrator迁移数据种子 + await DataSeeder.SeedAsync(new DataSeedContext()); + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContext.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContext.cs new file mode 100644 index 000000000..35d068464 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContext.cs @@ -0,0 +1,32 @@ +using LINGYUN.Abp.AIManagement.Chats; +using LINGYUN.Abp.AIManagement.EntityFrameworkCore; +using LINGYUN.Abp.AIManagement.Tokens; +using LINGYUN.Abp.AIManagement.Workspaces; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace LINGYUN.Abp.MicroService.AIService; + +[ConnectionStringName("Default")] +public class AIServiceMigrationsDbContext : + AbpDbContext, + IAIManagementDbContext +{ + public DbSet WorkspaceDefinitions { get; set; } + public DbSet TextChatMessageRecords { get; set; } + public DbSet ConversationRecords { get; set; } + public DbSet TokenUsageRecords { get; set; } + + public AIServiceMigrationsDbContext( + DbContextOptions options) : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.ConfigureAIManagement(); + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContextFactory.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContextFactory.cs new file mode 100644 index 000000000..afd229619 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContextFactory.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; +using System.IO; + +namespace LINGYUN.Abp.MicroService.AIService; +public class AIServiceMigrationsDbContextFactory : IDesignTimeDbContextFactory +{ + public AIServiceMigrationsDbContext CreateDbContext(string[] args) + { + var configuration = BuildConfiguration(); + var connectionString = configuration.GetConnectionString("Default"); + + var builder = new DbContextOptionsBuilder() + .UseNpgsql(connectionString); + + return new AIServiceMigrationsDbContext(builder!.Options); + } + + private static IConfigurationRoot BuildConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../LINGYUN.Abp.MicroService.AIService.DbMigrator/")) + .AddJsonFile("appsettings.json", optional: false); + + return builder.Build(); + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsEntityFrameworkCoreModule.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsEntityFrameworkCoreModule.cs new file mode 100644 index 000000000..1c84f02f9 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsEntityFrameworkCoreModule.cs @@ -0,0 +1,45 @@ +using LINGYUN.Abp.AIManagement.EntityFrameworkCore; +using LINGYUN.Abp.Data.DbMigrator; +using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; +using LINGYUN.Abp.Saas.EntityFrameworkCore; +using LINGYUN.Abp.TextTemplating.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using System; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.PostgreSql; +using Volo.Abp.FeatureManagement.EntityFrameworkCore; +using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement.EntityFrameworkCore; +using Volo.Abp.SettingManagement.EntityFrameworkCore; + +namespace LINGYUN.Abp.MicroService.AIService; + +[DependsOn( + typeof(AbpAIManagementEntityFrameworkCoreModule), + typeof(AbpSaasEntityFrameworkCoreModule), + typeof(AbpSettingManagementEntityFrameworkCoreModule), + typeof(AbpPermissionManagementEntityFrameworkCoreModule), + typeof(AbpFeatureManagementEntityFrameworkCoreModule), + typeof(AbpLocalizationManagementEntityFrameworkCoreModule), + typeof(AbpTextTemplatingEntityFrameworkCoreModule), + typeof(AbpEntityFrameworkCorePostgreSqlModule), + typeof(AbpDataDbMigratorModule) + )] +public class AIServiceMigrationsEntityFrameworkCoreModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + // https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic + AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAbpDbContext(); + + Configure(options => + { + options.UseNpgsql(); + }); + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/FodyWeavers.xml b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/FodyWeavers.xsd b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore.csproj new file mode 100644 index 000000000..c85236273 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore.csproj @@ -0,0 +1,35 @@ + + + + + + + false + net10.0 + enable + LINGYUN.Abp.MicroService.AIService + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260127083027_Initial_AI_Service.Designer.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260127083027_Initial_AI_Service.Designer.cs new file mode 100644 index 000000000..3b253c2ca --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260127083027_Initial_AI_Service.Designer.cs @@ -0,0 +1,303 @@ +// +using System; +using LINGYUN.Abp.MicroService.AIService; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LINGYUN.Abp.MicroService.AIService.Migrations +{ + [DbContext(typeof(AIServiceMigrationsDbContext))] + [Migration("20260127083027_Initial_AI_Service")] + partial class Initial_AI_Service + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.ConversationRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UpdateAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("AbpAIConversations", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.TextChatMessageRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ReplyAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReplyMessage") + .HasColumnType("text"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITextChatMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tokens.TokenUsageRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CachedInputTokenCount") + .HasColumnType("bigint"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("InputTokenCount") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MessageId") + .HasColumnType("uuid"); + + b.Property("OutputTokenCount") + .HasColumnType("bigint"); + + b.Property("ReasoningTokenCount") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TotalTokenCount") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITokenUsages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiBaseUrl") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ApiKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrequencyPenalty") + .HasColumnType("real"); + + b.Property("Instructions") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MaxOutputTokens") + .HasColumnType("integer"); + + b.Property("ModelName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PresencePenalty") + .HasColumnType("real"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SystemPrompt") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Temperature") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpAIWorkspaceDefinitions", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260127083027_Initial_AI_Service.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260127083027_Initial_AI_Service.cs new file mode 100644 index 000000000..ed6c57888 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260127083027_Initial_AI_Service.cs @@ -0,0 +1,148 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LINGYUN.Abp.MicroService.AIService.Migrations +{ + /// + public partial class Initial_AI_Service : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AbpAIConversations", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + TenantId = table.Column(type: "uuid", nullable: true), + Name = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + ExpiredAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdateAt = table.Column(type: "timestamp with time zone", nullable: true), + CreationTime = table.Column(type: "timestamp with time zone", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: true), + LastModificationTime = table.Column(type: "timestamp with time zone", nullable: true), + LastModifierId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAIConversations", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpAITextChatMessages", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Content = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + ExtraProperties = table.Column(type: "text", nullable: false), + ConcurrencyStamp = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + CreationTime = table.Column(type: "timestamp with time zone", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: true), + LastModificationTime = table.Column(type: "timestamp with time zone", nullable: true), + LastModifierId = table.Column(type: "uuid", nullable: true), + TenantId = table.Column(type: "uuid", nullable: true), + Workspace = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + Role = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UserId = table.Column(type: "uuid", nullable: true), + ConversationId = table.Column(type: "uuid", nullable: true), + ReplyMessage = table.Column(type: "text", nullable: true), + ReplyAt = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAITextChatMessages", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpAITokenUsages", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + TenantId = table.Column(type: "uuid", nullable: true), + MessageId = table.Column(type: "uuid", nullable: true), + ConversationId = table.Column(type: "uuid", nullable: true), + InputTokenCount = table.Column(type: "bigint", nullable: true), + OutputTokenCount = table.Column(type: "bigint", nullable: true), + TotalTokenCount = table.Column(type: "bigint", nullable: true), + CachedInputTokenCount = table.Column(type: "bigint", nullable: true), + ReasoningTokenCount = table.Column(type: "bigint", nullable: true), + CreationTime = table.Column(type: "timestamp with time zone", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: true), + LastModificationTime = table.Column(type: "timestamp with time zone", nullable: true), + LastModifierId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAITokenUsages", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpAIWorkspaceDefinitions", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + Provider = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), + ModelName = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + DisplayName = table.Column(type: "character varying(128)", maxLength: 128, nullable: false), + Description = table.Column(type: "character varying(128)", maxLength: 128, nullable: true), + ApiKey = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + ApiBaseUrl = table.Column(type: "character varying(128)", maxLength: 128, nullable: true), + SystemPrompt = table.Column(type: "character varying(512)", maxLength: 512, nullable: true), + Instructions = table.Column(type: "character varying(512)", maxLength: 512, nullable: true), + Temperature = table.Column(type: "real", nullable: true), + MaxOutputTokens = table.Column(type: "integer", nullable: true), + FrequencyPenalty = table.Column(type: "real", nullable: true), + PresencePenalty = table.Column(type: "real", nullable: true), + IsEnabled = table.Column(type: "boolean", nullable: false), + StateCheckers = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + ExtraProperties = table.Column(type: "text", nullable: false), + ConcurrencyStamp = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + CreationTime = table.Column(type: "timestamp with time zone", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: true), + LastModificationTime = table.Column(type: "timestamp with time zone", nullable: true), + LastModifierId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAIWorkspaceDefinitions", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAITextChatMessages_TenantId_ConversationId", + table: "AbpAITextChatMessages", + columns: new[] { "TenantId", "ConversationId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAITokenUsages_TenantId_ConversationId", + table: "AbpAITokenUsages", + columns: new[] { "TenantId", "ConversationId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAIWorkspaceDefinitions_Name", + table: "AbpAIWorkspaceDefinitions", + column: "Name", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AbpAIConversations"); + + migrationBuilder.DropTable( + name: "AbpAITextChatMessages"); + + migrationBuilder.DropTable( + name: "AbpAITokenUsages"); + + migrationBuilder.DropTable( + name: "AbpAIWorkspaceDefinitions"); + } + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs new file mode 100644 index 000000000..095a0771d --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs @@ -0,0 +1,300 @@ +// +using System; +using LINGYUN.Abp.MicroService.AIService; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LINGYUN.Abp.MicroService.AIService.Migrations +{ + [DbContext(typeof(AIServiceMigrationsDbContext))] + partial class AIServiceMigrationsDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.ConversationRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UpdateAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("AbpAIConversations", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.TextChatMessageRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ReplyAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReplyMessage") + .HasColumnType("text"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITextChatMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tokens.TokenUsageRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CachedInputTokenCount") + .HasColumnType("bigint"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("InputTokenCount") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MessageId") + .HasColumnType("uuid"); + + b.Property("OutputTokenCount") + .HasColumnType("bigint"); + + b.Property("ReasoningTokenCount") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TotalTokenCount") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITokenUsages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiBaseUrl") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ApiKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrequencyPenalty") + .HasColumnType("real"); + + b.Property("Instructions") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MaxOutputTokens") + .HasColumnType("integer"); + + b.Property("ModelName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PresencePenalty") + .HasColumnType("real"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SystemPrompt") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Temperature") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpAIWorkspaceDefinitions", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.Configure.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.Configure.cs new file mode 100644 index 000000000..f654a1bc1 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.Configure.cs @@ -0,0 +1,423 @@ +using DotNetCore.CAP; +using LINGYUN.Abp.AIManagement; +using LINGYUN.Abp.AIManagement.Chats; +using LINGYUN.Abp.Localization.CultureMap; +using LINGYUN.Abp.LocalizationManagement; +using LINGYUN.Abp.Serilog.Enrichers.UniqueId; +using LINGYUN.Abp.TextTemplating; +using LINGYUN.Abp.Wrapper; +using Medallion.Threading; +using Medallion.Threading.Redis; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Cors; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.Caching.StackExchangeRedis; +using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; +using StackExchange.Redis; +using System.Text.Encodings.Web; +using System.Text.Unicode; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.AntiForgery; +using Volo.Abp.Auditing; +using Volo.Abp.Caching; +using Volo.Abp.Domain.Entities.Events.Distributed; +using Volo.Abp.FeatureManagement; +using Volo.Abp.GlobalFeatures; +using Volo.Abp.Http.Client; +using Volo.Abp.Identity.Localization; +using Volo.Abp.Json; +using Volo.Abp.Json.SystemTextJson; +using Volo.Abp.Localization; +using Volo.Abp.MultiTenancy; +using Volo.Abp.PermissionManagement; +using Volo.Abp.Security.Claims; +using Volo.Abp.SettingManagement; +using Volo.Abp.Threading; +using Volo.Abp.Timing; +using Volo.Abp.VirtualFileSystem; + +namespace LINGYUN.Abp.MicroService.AIService; + +public partial class AIServiceModule +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + private void PreConfigureFeature() + { + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.Editions().EnableAll(); + }); + } + + private void PreConfigureApp(IConfiguration configuration) + { + PreConfigure(options => + { + // 以开放端口区别,应在0-31之间 + options.SnowflakeIdOptions.WorkerId = 19; + options.SnowflakeIdOptions.WorkerIdBits = 5; + options.SnowflakeIdOptions.DatacenterId = 1; + }); + + if (configuration.GetValue("App:ShowPii")) + { + IdentityModelEventSource.ShowPII = true; + } + } + + private void PreConfigureCAP(IConfiguration configuration) + { + PreConfigure(options => + { + options + .UsePostgreSql(mySqlOptions => + { + configuration.GetSection("CAP:PostgreSql").Bind(mySqlOptions); + }) + .UseRabbitMQ(rabbitMQOptions => + { + configuration.GetSection("CAP:RabbitMQ").Bind(rabbitMQOptions); + }) + .UseDashboard(); + }); + } + + private void ConfigureTextTemplating() + { + Configure(options => + { + options.IsDynamicTemplateDefinitionStoreEnabled = true; + }); + } + + private void ConfigureFeatureManagement() + { + Configure(options => + { + options.IsDynamicFeatureStoreEnabled = true; + }); + } + + private void ConfigureJsonSerializer(IConfiguration configuration) + { + // 统一时间日期格式 + Configure(options => + { + var jsonConfiguration = configuration.GetSection("Json"); + if (jsonConfiguration.Exists()) + { + jsonConfiguration.Bind(options); + } + }); + // 中文序列化的编码问题 + Configure(options => + { + options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); + }); + } + + private void ConfigureAIManagement() + { + Configure(options => + { + options.IsDynamicWorkspaceStoreEnabled = true; + options.SaveStaticWorkspacesToDatabase = true; + }); + } + + private void ConfigurePermissionManagement() + { + Configure(options => + { + options.IsDynamicPermissionStoreEnabled = true; + }); + } + + private void ConfigureSettingManagement() + { + Configure(options => + { + options.IsDynamicSettingStoreEnabled = true; + }); + } + + private void ConfigureTiming(IConfiguration configuration) + { + Configure(options => + { + configuration.GetSection("Clock").Bind(options); + }); + } + + private void ConfigureCaching(IConfiguration configuration) + { + Configure(options => + { + configuration.GetSection("DistributedCache").Bind(options); + }); + + Configure(options => + { + options.AutoEventSelectors.AddNamespace("Volo.Abp.TenantManagement"); + }); + + Configure(options => + { + var redisConfig = ConfigurationOptions.Parse(options.Configuration!); + options.ConfigurationOptions = redisConfig; + options.InstanceName = configuration["Redis:InstanceName"]; + }); + } + + private void ConfigureDistributedLocking(IServiceCollection services, IConfiguration configuration) + { + var distributedLockEnabled = configuration["DistributedLock:IsEnabled"]; + if (distributedLockEnabled.IsNullOrEmpty() || bool.Parse(distributedLockEnabled)) + { + services.AddSingleton(sp => + { + var connectionMultiplexer = sp.GetRequiredService(); + return new RedisDistributedSynchronizationProvider(connectionMultiplexer.GetDatabase()); + }); + } + } + + private void ConfigureMvc(IServiceCollection services, IConfiguration configuration) + { + Configure(options => + { + options.ExposeIntegrationServices = true; + }); + } + + private void ConfigureVirtualFileSystem() + { + Configure(options => + { + options.FileSets.AddEmbedded("LINGYUN.Abp.MicroService.AIService"); + }); + } + + private void ConfigureMultiTenancy(IConfiguration configuration) + { + // 多租户 + Configure(options => + { + options.IsEnabled = true; + }); + + var tenantResolveCfg = configuration.GetSection("App:Domains"); + if (tenantResolveCfg.Exists()) + { + Configure(options => + { + var domains = tenantResolveCfg.Get() ?? []; + foreach (var domain in domains) + { + options.AddDomainTenantResolver(domain); + } + }); + } + } + + private void ConfigureIdentity(IConfiguration configuration) + { + Configure(options => + { + options.IsDynamicClaimsEnabled = true; + options.RemoteRefreshUrl = configuration["App:RefreshClaimsUrl"] + options.RemoteRefreshUrl; + }); + } + + private void ConfigureAuditing(IConfiguration configuration) + { + Configure(options => + { + // 是否启用实体变更记录 + var allEntitiesSelectorIsEnabled = configuration["Auditing:AllEntitiesSelector"]; + if (allEntitiesSelectorIsEnabled.IsNullOrWhiteSpace() || + (bool.TryParse(allEntitiesSelectorIsEnabled, out var enabled) && enabled)) + { + options.EntityHistorySelectors.AddAllEntities(); + } + }); + } + + private void ConfigureSwagger(IServiceCollection services, IConfiguration configuration) + { + // Swagger + services.AddAbpSwaggerGenWithOAuth( + configuration["AuthServer:Authority"]!, + new Dictionary + { + { "AIService", "AI Service API"} + }, + options => + { + options.SwaggerDoc("v1", new OpenApiInfo + { + Title = "AI Service API", Version = "v1", + Contact = new OpenApiContact + { + Name = "colin", + Email = "colin.in@foxmail.com", + Url = new Uri("https://github.com/colinin") + }, + License = new OpenApiLicense + { + Name = "MIT", + Url = new Uri("https://github.com/colinin/abp-next-admin/blob/master/LICENSE") + } + }); + options.DocInclusionPredicate((docName, description) => true); + options.CustomSchemaIds(type => type.FullName); + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", + Name = "Authorization", + In = ParameterLocation.Header, + Scheme = "bearer", + Type = SecuritySchemeType.Http, + BearerFormat = "JWT" + }); + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } + }, + new string[] { } + } + }); + options.OperationFilter(); + }); + } + + private void ConfigureLocalization() + { + // 支持本地化语言类型 + Configure(options => + { + options.Languages.Add(new LanguageInfo("en", "en", "English")); + options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文")); + + options.Resources + .Get() + .AddVirtualJson("/Localization/Resources"); + }); + + Configure(options => + { + var zhHansCultureMapInfo = new CultureMapInfo + { + TargetCulture = "zh-Hans", + SourceCultures = new string[] { "zh", "zh_CN", "zh-CN" } + }; + + options.CulturesMaps.Add(zhHansCultureMapInfo); + options.UiCulturesMaps.Add(zhHansCultureMapInfo); + }); + + Configure(options => + { + options.SaveStaticLocalizationsToDatabase = true; + }); + } + + private void ConfigureCors(IServiceCollection services, IConfiguration configuration) + { + services.AddCors(options => + { + options.AddDefaultPolicy(builder => + { + var corsOrigins = configuration.GetSection("App:CorsOrigins").Get>(); + if (corsOrigins == null || corsOrigins.Count == 0) + { + corsOrigins = configuration["App:CorsOrigins"]? + .Split(",", StringSplitOptions.RemoveEmptyEntries) + .Select(o => o.RemovePostFix("/")) + .ToList() ?? new List(); + } + builder + .WithOrigins(corsOrigins + .Select(o => o.RemovePostFix("/")) + .ToArray() + ) + .WithAbpExposedHeaders() + .WithAbpWrapExposedHeaders() + .SetIsOriginAllowedToAllowWildcardSubdomains() + .AllowAnyHeader() + .AllowAnyMethod() + .AllowCredentials(); + }); + }); + } + + private void ConfigureSecurity(IServiceCollection services, IConfiguration configuration, bool isDevelopment = false) + { + Configure(options => + { + options.TokenCookie.HttpOnly = false; + options.TokenCookie.SameSite = SameSiteMode.Lax; + }); + + services.AddAlwaysAllowAuthorization(); + services.AddAlwaysAllowSession(); + + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddAbpJwtBearer(options => + { + configuration.GetSection("AuthServer").Bind(options); + + var validIssuers = configuration.GetSection("AuthServer:ValidIssuers").Get>(); + if (validIssuers?.Count > 0) + { + options.TokenValidationParameters.ValidIssuers = validIssuers; + options.TokenValidationParameters.IssuerValidator = TokenWildcardIssuerValidator.IssuerValidator; + } + var validAudiences = configuration.GetSection("AuthServer:ValidAudiences").Get>(); + if (validAudiences?.Count > 0) + { + options.TokenValidationParameters.ValidAudiences = validAudiences; + } + }); + + services + .AddDataProtection() + .SetApplicationName("LINGYUN.Abp.Application") + .PersistKeysToStackExchangeRedis(() => + { + var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!); + + return redis.GetDatabase(); + }, + "LINGYUN.Abp.Application:DataProtection:Protection-Keys"); + } + + private void ConfigureWrapper() + { + Configure(options => + { + options.IsEnabled = true; + + options.IgnoreControllers.Add(); + }); + } + + private void PreConfigureWrapper() + { + // 服务间调用不包装 + PreConfigure(options => + { + options.ProxyClientActions.Add( + (_, _, client) => + { + client.DefaultRequestHeaders.TryAddWithoutValidation(AbpHttpWrapConsts.AbpDontWrapResult, "true"); + }); + }); + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs new file mode 100644 index 000000000..e18ca18d8 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs @@ -0,0 +1,103 @@ +using LINGYUN.Abp.AIManagement; +using LINGYUN.Abp.AspNetCore.HttpOverrides; +using LINGYUN.Abp.AspNetCore.Mvc.Localization; +using LINGYUN.Abp.AspNetCore.Mvc.Wrapper; +using LINGYUN.Abp.AuditLogging.Elasticsearch; +using LINGYUN.Abp.Claims.Mapping; +using LINGYUN.Abp.Data.DbMigrator; +using LINGYUN.Abp.Emailing.Platform; +using LINGYUN.Abp.EventBus.CAP; +using LINGYUN.Abp.ExceptionHandling.Emailing; +using LINGYUN.Abp.Identity.Session.AspNetCore; +using LINGYUN.Abp.Localization.CultureMap; +using LINGYUN.Abp.Logging.Serilog.Elasticsearch; +using LINGYUN.Abp.Serilog.Enrichers.Application; +using LINGYUN.Abp.Serilog.Enrichers.UniqueId; +using LINGYUN.Abp.Sms.Platform; +using LINGYUN.Abp.TextTemplating.Scriban; +using Volo.Abp.AspNetCore.Authentication.JwtBearer; +using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy; +using Volo.Abp.AspNetCore.Serilog; +using Volo.Abp.Autofac; +using Volo.Abp.Caching.StackExchangeRedis; +using Volo.Abp.Http.Client; +using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement.Identity; +using Volo.Abp.PermissionManagement.OpenIddict; +using Volo.Abp.Swashbuckle; + +namespace LINGYUN.Abp.MicroService.AIService; + +[DependsOn( + typeof(AbpCAPEventBusModule), + typeof(AbpSerilogEnrichersApplicationModule), + typeof(AbpSerilogEnrichersUniqueIdModule), + typeof(AbpAspNetCoreSerilogModule), + typeof(AbpLoggingSerilogElasticsearchModule), + typeof(AbpAuditLoggingElasticsearchModule), + typeof(AbpAspNetCoreMvcUiMultiTenancyModule), + typeof(AbpAspNetCoreMvcLocalizationModule), + + typeof(AbpPermissionManagementDomainIdentityModule), + typeof(AbpPermissionManagementDomainOpenIddictModule), + + // 重写模板引擎支持外部本地化 + typeof(AbpTextTemplatingScribanModule), + + typeof(AbpIdentitySessionAspNetCoreModule), + + typeof(AbpAIManagementApplicationModule), + typeof(AbpAIManagementHttpApiModule), + typeof(AIServiceMigrationsEntityFrameworkCoreModule), + typeof(AbpDataDbMigratorModule), + typeof(AbpAspNetCoreAuthenticationJwtBearerModule), + typeof(AbpEmailingExceptionHandlingModule), + typeof(AbpHttpClientModule), + typeof(AbpSmsPlatformModule), + typeof(AbpEmailingPlatformModule), + typeof(AbpCachingStackExchangeRedisModule), + typeof(AbpLocalizationCultureMapModule), + typeof(AbpAspNetCoreMvcWrapperModule), + typeof(AbpAspNetCoreHttpOverridesModule), + typeof(AbpClaimsMappingModule), + typeof(AbpSwashbuckleModule), + typeof(AbpAutofacModule) + )] +public partial class AIServiceModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + + PreConfigureWrapper(); + PreConfigureFeature(); + PreConfigureApp(configuration); + PreConfigureCAP(configuration); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + var hostingEnvironment = context.Services.GetHostingEnvironment(); + var configuration = context.Services.GetConfiguration(); + + ConfigureWrapper(); + ConfigureLocalization(); + ConfigureVirtualFileSystem(); + ConfigureTextTemplating(); + ConfigureAIManagement(); + ConfigureSettingManagement(); + ConfigureFeatureManagement(); + ConfigurePermissionManagement(); + ConfigureIdentity(configuration); + ConfigureTiming(configuration); + ConfigureCaching(configuration); + ConfigureAuditing(configuration); + ConfigureMultiTenancy(configuration); + ConfigureJsonSerializer(configuration); + ConfigureMvc(context.Services, configuration); + ConfigureCors(context.Services, configuration); + ConfigureSwagger(context.Services, configuration); + ConfigureDistributedLocking(context.Services, configuration); + ConfigureSecurity(context.Services, configuration, hostingEnvironment.IsDevelopment()); + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/FodyWeavers.xml b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj new file mode 100644 index 000000000..dafa6b426 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj @@ -0,0 +1,71 @@ + + + + net10.0 + enable + enable + LINGYUN.Abp.MicroService.AIService + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/Program.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/Program.cs new file mode 100644 index 000000000..85b652951 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/Program.cs @@ -0,0 +1,99 @@ +using LINGYUN.Abp.Identity.Session.AspNetCore; +using LINGYUN.Abp.MicroService.AIService; +using LINGYUN.Abp.Serilog.Enrichers.Application; +using Serilog; +using Volo.Abp.IO; +using Volo.Abp.Modularity.PlugIns; + +Log.Information("Starting AIService Host..."); + +try +{ + var builder = WebApplication.CreateBuilder(args); + builder.Host.AddAppSettingsSecretsJson() + .UseAutofac() + .ConfigureAppConfiguration((context, config) => + { + if (context.Configuration.GetValue("AgileConfig:IsEnabled", false)) + { + config.AddAgileConfig(new AgileConfig.Client.ConfigClient(context.Configuration)); + } + }) + .UseSerilog((context, provider, config) => + { + config.ReadFrom.Configuration(context.Configuration); + }); + + builder.AddServiceDefaults(); + + await builder.AddApplicationAsync(options => + { + var applicationName = Environment.GetEnvironmentVariable("APPLICATION_NAME") ?? "AIService"; + options.ApplicationName = applicationName; + AbpSerilogEnrichersConsts.ApplicationName = applicationName; + + var pluginFolder = Path.Combine(Directory.GetCurrentDirectory(), "Modules"); + DirectoryHelper.CreateIfNotExists(pluginFolder); + options.PlugInSources.AddFolder(pluginFolder, SearchOption.AllDirectories); + }); + + var app = builder.Build(); + + await app.InitializeApplicationAsync(); + + app.MapDefaultEndpoints(); + + app.UseForwardedHeaders(); + // 本地化 + app.UseMapRequestLocalization(); + // http调用链 + app.UseCorrelationId(); + // 文件系统 + app.MapAbpStaticAssets(); + // 路由 + app.UseRouting(); + // 跨域 + app.UseCors(); + // 认证 + app.UseAuthentication(); + app.UseJwtTokenMiddleware(); + // 多租户 + app.UseMultiTenancy(); + // 会话 + app.UseAbpSession(); + // jwt + app.UseDynamicClaims(); + // 授权 + app.UseAuthorization(); + // Swagger + app.UseSwagger(); + // Swagger可视化界面 + app.UseAbpSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support AI Service API"); + + var configuration = app.Configuration; + options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]); + options.OAuthScopes(configuration["AuthServer:Audience"]); + }); + // 审计日志 + app.UseAuditing(); + app.UseAbpSerilogEnrichers(); + // 路由 + app.UseConfiguredEndpoints(); + + await app.RunAsync(); +} +catch (Exception ex) +{ + if (ex is HostAbortedException) + { + throw; + } + + Log.Fatal(ex, "Host terminated unexpectedly!"); +} +finally +{ + await Log.CloseAndFlushAsync(); +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/Properties/launchSettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/Properties/launchSettings.json new file mode 100644 index 000000000..312571616 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "LINGYUN.Abp.MicroService.AIService": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:49787;http://localhost:49788" + } + } +} \ No newline at end of file diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/TenantHeaderParamter.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/TenantHeaderParamter.cs new file mode 100644 index 000000000..f72407569 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/TenantHeaderParamter.cs @@ -0,0 +1,35 @@ +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using Volo.Abp.AspNetCore.MultiTenancy; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.MicroService.AIService; + +public class TenantHeaderParamter : IOperationFilter +{ + private readonly AbpMultiTenancyOptions _multiTenancyOptions; + private readonly AbpAspNetCoreMultiTenancyOptions _aspNetCoreMultiTenancyOptions; + public TenantHeaderParamter( + IOptions multiTenancyOptions, + IOptions aspNetCoreMultiTenancyOptions) + { + _multiTenancyOptions = multiTenancyOptions.Value; + _aspNetCoreMultiTenancyOptions = aspNetCoreMultiTenancyOptions.Value; + } + + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + if (_multiTenancyOptions.IsEnabled) + { + operation.Parameters = operation.Parameters ?? new List(); + operation.Parameters.Add(new OpenApiParameter + { + Name = _aspNetCoreMultiTenancyOptions.TenantKey, + In = ParameterLocation.Header, + Description = "Tenant Id in http header", + Required = false + }); + } + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.Development.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.Development.json new file mode 100644 index 000000000..c9a9d8327 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.Development.json @@ -0,0 +1,116 @@ +{ + "App": { + "ShowPii": true, + "CorsOrigins": [ "http://localhost:5666", "http://localhost:30000" ], + "RefreshClaimsUrl": "http://localhost:30015" + }, + "Auditing": { + "AllEntitiesSelector": true + }, + "DistributedCache": { + "HideErrors": true, + "KeyPrefix": "LINGYUN.Abp.Application", + "GlobalCacheEntryOptions": { + "SlidingExpiration": "30:00:00", + "AbsoluteExpirationRelativeToNow": "60:00:00" + } + }, + "ConnectionStrings": { + "Default": "Host=127.0.0.1;Database=abp;Username=postgres;Password=123456" + }, + "CAP": { + "EventBus": { + "DefaultGroupName": "AIService", + "Version": "v1", + "FailedRetryInterval": 300, + "FailedRetryCount": 10, + "CollectorCleaningInterval": 3600000 + }, + "PostgreSql": { + "TableNamePrefix": "admin", + "ConnectionString": "Host=127.0.0.1;Database=abp;Username=postgres;Password=123456" + }, + "RabbitMQ": { + "HostName": "localhost", + "Port": 5672, + "UserName": "admin", + "Password": "123456", + "ExchangeName": "LINGYUN.Abp.Application", + "VirtualHost": "/" + } + }, + "DistributedLock": { + "IsEnabled": true, + "Redis": { + "Configuration": "localhost,defaultDatabase=13" + } + }, + "Redis": { + "Configuration": "localhost,defaultDatabase=10", + "InstanceName": "LINGYUN.Abp.Application" + }, + "AuthServer": { + "Authority": "http://localhost:44385/", + "Audience": "admin-service", + "ValidAudiences": [ "lingyun-abp-application" ], + "MapInboundClaims": false, + "RequireHttpsMetadata": false, + "SwaggerClientId": "vue-oauth-client" + }, + "RemoteServices": { + "Platform": { + "BaseUrl": "http://localhost:30025", + "UseCurrentAccessToken": false + } + }, + "Logging": { + "Serilog": { + "Elasticsearch": { + "IndexFormat": "abp.dev.logging-{0:yyyy.MM.dd}" + } + } + }, + "AuditLogging": { + "Elasticsearch": { + "IndexPrefix": "abp.dev.auditing" + } + }, + "Elasticsearch": { + "NodeUris": "http://elasticsearch" + }, + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "System": "Warning", + "Microsoft": "Warning", + "DotNetCore": "Debug" + } + }, + "WriteTo": [ + { + "Name": "Async", + "Args": { + "configure": [ + { + "Name": "Console", + "Args": { + "restrictedToMinimumLevel": "Debug", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "Elasticsearch", + "Args": { + "nodeUris": "http://elasticsearch", + "indexFormat": "abp.dev.logging-{0:yyyy.MM.dd}", + "autoRegisterTemplate": true, + "autoRegisterTemplateVersion": "ESv7" + } + } + ] + } + } + ] + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.json new file mode 100644 index 000000000..b8e4da13c --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.json @@ -0,0 +1,91 @@ +{ + "Clock": { + "Kind": "Local" + }, + "Forwarded": { + "ForwardedHeaders": "XForwardedFor,XForwardedProto" + }, + "StringEncryption": { + "DefaultPassPhrase": "s46c5q55nxpeS8Ra", + "InitVectorBytes": "s83ng0abvd02js84", + "DefaultSalt": "sf&5)s3#" + }, + "Json": { + "InputDateTimeFormats": [ + "yyyy-MM-dd HH:mm:ss", + "yyyy-MM-ddTHH:mm:ss" + ] + }, + "Serilog": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "System": "Warning", + "Microsoft": "Warning", + "DotNetCore": "Information" + } + }, + "Enrich": [ "FromLogContext", "WithProcessId", "WithThreadId", "WithEnvironmentName", "WithMachineName", "WithApplicationName", "WithUniqueId" ], + "WriteTo": [ + { + "Name": "Async", + "Args": { + "configure": [ + { + "Name": "Console", + "Args": { + "restrictedToMinimumLevel": "Debug", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Debug-.log", + "restrictedToMinimumLevel": "Debug", + "rollingInterval": "Day", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Info-.log", + "restrictedToMinimumLevel": "Information", + "rollingInterval": "Day", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Warn-.log", + "restrictedToMinimumLevel": "Warning", + "rollingInterval": "Day", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Error-.log", + "restrictedToMinimumLevel": "Error", + "rollingInterval": "Day", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Fatal-.log", + "restrictedToMinimumLevel": "Fatal", + "rollingInterval": "Day", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + } + ] + } + } + ] + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.ApiGateway/yarp.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.ApiGateway/yarp.json index 61baae0e6..34e4940a0 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.ApiGateway/yarp.json +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.ApiGateway/yarp.json @@ -1,6 +1,21 @@ { "ReverseProxy": { "Routes": { + "ai-management-route": { + "ClusterId": "ai-management-cluster", + "Match": { + "Path": "/api/ai-management/{**everything}" + }, + "Transforms": [ + { + "HeaderPrefix": "X-Forwarded-", + "X-Forwarded": "Append" + }, + { + "ResponseHeadersAllowed": "_AbpWrapResult;_AbpDontWrapResult;_AbpErrorFormat" + } + ] + }, "abp-route": { "ClusterId": "admin-service-cluster", "Match": { @@ -451,6 +466,16 @@ } }, "Clusters": { + "ai-management-cluster": { + "Destinations": { + "destination1": { + "Address": "http://localhost:30070", + "Metadata": { + "SwaggerEndpoint": "http://localhost:30070" + } + } + } + }, "auth-server-cluster": { "Destinations": { "destination1": { diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs index 48a47b899..def8a420f 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs @@ -215,6 +215,18 @@ builder.AddProject("WorkflowS .WaitFor(rabbitmq) .WaitFor(taskService); +// AIService +AddDotNetProject< + Projects.LINGYUN_Abp_MicroService_AIService_DbMigrator, + Projects.LINGYUN_Abp_MicroService_AIService>( + builder: builder, + servicePrefix: "AI", + serviceSuffix: "Service", + migratorSuffix: "Migrator", + port: 30070, + portName: "ai", + waitProject: localizationService); + // ApiGateway var apigateway = builder.AddProject("ApiGateway") .WithHttpEndpoint(port: 30000, name: "gateway") diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/LINGYUN.Abp.MicroService.AppHost.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/LINGYUN.Abp.MicroService.AppHost.csproj index cccaa6637..807b9b7a0 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/LINGYUN.Abp.MicroService.AppHost.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/LINGYUN.Abp.MicroService.AppHost.csproj @@ -17,6 +17,8 @@ + + From 4ffa047d7292700f63f3c93643833562e45cb177 Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 4 Feb 2026 11:55:01 +0800 Subject: [PATCH 25/88] feat: Add kibana components --- aspnet-core/LINGYUN.MicroService.Aspire.slnx | 561 ++++++++++++++++++ .../AppHost.cs | 13 +- ...INGYUN.Abp.MicroService.TaskService.csproj | 1 + .../TaskServiceModule.cs | 2 + 4 files changed, 575 insertions(+), 2 deletions(-) create mode 100644 aspnet-core/LINGYUN.MicroService.Aspire.slnx diff --git a/aspnet-core/LINGYUN.MicroService.Aspire.slnx b/aspnet-core/LINGYUN.MicroService.Aspire.slnx new file mode 100644 index 000000000..670b925f3 --- /dev/null +++ b/aspnet-core/LINGYUN.MicroService.Aspire.slnx @@ -0,0 +1,561 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs index def8a420f..dadaf8d82 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs @@ -10,8 +10,17 @@ var redis = builder.AddRedis("redis") // Elasticsearch var elasticsearch = builder.AddElasticsearch("elasticsearch") .WithContainerName("elasticsearch") + .WithImageTag("8.17.3") .WithDataVolume("elasticsearch-dev") - .WithEnvironment("ES_JAVA_OPTS", "-Xms2g -Xmx2g"); + .WithEnvironment("ES_JAVA_OPTS", "-Xms2g -Xmx2g") + // see: https://www.funkysi1701.com/posts/2025/adding-elasticsearch-with-aspire/ + .WithEnvironment("xpack.security.enabled", "false"); + +// Kibana +builder.AddContainer("kibana", "kibana", "8.17.3") + .WithReference(elasticsearch) + .WithEndpoint(5601, 5601) + .WaitFor(elasticsearch); // Postgres var postgres = builder.AddPostgres("postgres") @@ -229,7 +238,7 @@ AddDotNetProject< // ApiGateway var apigateway = builder.AddProject("ApiGateway") - .WithHttpEndpoint(port: 30000, name: "gateway") + // .WithHttpEndpoint(port: 30000, name: "gateway") .WithExternalHttpEndpoints() .WithReference(redis, "Redis") .WithReference(elasticsearch, "Elasticsearch") diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/LINGYUN.Abp.MicroService.TaskService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/LINGYUN.Abp.MicroService.TaskService.csproj index 2a4b28ffb..ba3e066a1 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/LINGYUN.Abp.MicroService.TaskService.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/LINGYUN.Abp.MicroService.TaskService.csproj @@ -55,6 +55,7 @@ + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/TaskServiceModule.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/TaskServiceModule.cs index 42e80af93..4933dbb3b 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/TaskServiceModule.cs +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/TaskServiceModule.cs @@ -10,6 +10,7 @@ using LINGYUN.Abp.BackgroundTasks.Notifications; using LINGYUN.Abp.BackgroundTasks.Quartz; using LINGYUN.Abp.Claims.Mapping; using LINGYUN.Abp.Data.DbMigrator; +using LINGYUN.Abp.Elasticsearch.Jobs; using LINGYUN.Abp.Emailing.Platform; using LINGYUN.Abp.EventBus.CAP; using LINGYUN.Abp.ExceptionHandling.Emailing; @@ -54,6 +55,7 @@ namespace LINGYUN.Abp.MicroService.TaskService; typeof(AbpHttpClientIdentityModelWebModule), typeof(AbpAspNetCoreMultiTenancyModule), typeof(AbpAspNetCoreMvcLocalizationModule), + typeof(AbpElasticsearchJobsModule), typeof(AbpBackgroundTasksJobsModule), typeof(AbpBackgroundTasksQuartzModule), typeof(AbpBackgroundTasksDistributedLockingModule), From 2022ae429a0ee555d3faf63651ccd607f0c3a913 Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 4 Mar 2026 14:39:39 +0800 Subject: [PATCH 26/88] fix: Fix dynamic initialization --- .../LocalizationDynamicInitializer.cs | 48 ++++++-------- .../StaticLocalizationSaver.cs | 16 +++-- .../NotificationDynamicInitializer.cs | 61 ++++++++--------- .../TextTemplateDynamicInitializer.cs | 63 ++++++++---------- .../WebhookDynamicInitializer.cs | 65 ++++++++----------- 5 files changed, 113 insertions(+), 140 deletions(-) diff --git a/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDynamicInitializer.cs b/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDynamicInitializer.cs index 404d79d8a..9cbb82e1f 100644 --- a/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDynamicInitializer.cs +++ b/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDynamicInitializer.cs @@ -16,64 +16,56 @@ public class LocalizationDynamicInitializer : ITransientDependency public ILogger Logger { get; set; } protected IServiceProvider ServiceProvider { get; } - protected IOptions Options { get; } - protected IHostApplicationLifetime ApplicationLifetime { get; } - protected ICancellationTokenProvider CancellationTokenProvider { get; } - protected IStaticLocalizationSaver StaticLocalizationSaver { get; } - - public LocalizationDynamicInitializer( - IServiceProvider serviceProvider, - IOptions options, - ICancellationTokenProvider cancellationTokenProvider, - IStaticLocalizationSaver staticLocalizationSaver) + + public LocalizationDynamicInitializer(IServiceProvider serviceProvider) { ServiceProvider = serviceProvider; - Options = options; - ApplicationLifetime = ServiceProvider.GetService(); - CancellationTokenProvider = cancellationTokenProvider; - StaticLocalizationSaver = staticLocalizationSaver; Logger = NullLogger.Instance; } public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default) { - if (!Options.Value.SaveStaticLocalizationsToDatabase) + var options = ServiceProvider.GetRequiredService>().Value; + + if (!options.SaveStaticLocalizationsToDatabase) { return Task.CompletedTask; } if (runInBackground) { + var applicationLifetime = ServiceProvider.GetService(); Task.Run(async () => { - if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null) + if (cancellationToken == default && applicationLifetime?.ApplicationStopping != null) { - cancellationToken = ApplicationLifetime.ApplicationStopping; + cancellationToken = applicationLifetime.ApplicationStopping; } - await ExecuteInitializationAsync(cancellationToken); + await ExecuteInitializationAsync(options, cancellationToken); }, cancellationToken); return Task.CompletedTask; } - return ExecuteInitializationAsync(cancellationToken); + return ExecuteInitializationAsync(options, cancellationToken); } - protected virtual async Task ExecuteInitializationAsync(CancellationToken cancellationToken) + protected virtual async Task ExecuteInitializationAsync(AbpLocalizationManagementOptions options, CancellationToken cancellationToken) { try { - using (CancellationTokenProvider.Use(cancellationToken)) + var cancellationTokenProvider = ServiceProvider.GetRequiredService(); + using (cancellationTokenProvider.Use(cancellationToken)) { - if (CancellationTokenProvider.Token.IsCancellationRequested) + if (cancellationTokenProvider.Token.IsCancellationRequested) { return; } - await SaveStaticLocalizationToDatabaseAsync(cancellationToken); + await SaveStaticLocalizationToDatabaseAsync(options, cancellationToken); - if (CancellationTokenProvider.Token.IsCancellationRequested) + if (cancellationTokenProvider.Token.IsCancellationRequested) { return; } @@ -85,13 +77,15 @@ public class LocalizationDynamicInitializer : ITransientDependency } } - protected virtual async Task SaveStaticLocalizationToDatabaseAsync(CancellationToken cancellationToken) + protected virtual async Task SaveStaticLocalizationToDatabaseAsync(AbpLocalizationManagementOptions options, CancellationToken cancellationToken) { - if (!Options.Value.SaveStaticLocalizationsToDatabase) + if (!options.SaveStaticLocalizationsToDatabase) { return; } + var staticLocalizationSaver = ServiceProvider.GetRequiredService(); + await Policy .Handle(e => e is not OperationCanceledException) .WaitAndRetryAsync( @@ -108,7 +102,7 @@ public class LocalizationDynamicInitializer : ITransientDependency { cancellationToken.ThrowIfCancellationRequested(); - await StaticLocalizationSaver.SaveAsync(); + await staticLocalizationSaver.SaveAsync(); } catch (Exception ex) { diff --git a/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/StaticLocalizationSaver.cs b/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/StaticLocalizationSaver.cs index daf6ddecb..22bfee209 100644 --- a/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/StaticLocalizationSaver.cs +++ b/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/StaticLocalizationSaver.cs @@ -40,18 +40,18 @@ public class StaticLocalizationSaver : IStaticLocalizationSaver, ITransientDepen public StaticLocalizationSaver( ILogger logger, - IDistributedCache cache, - IGuidGenerator guidGenerator, + IDistributedCache cache, + IGuidGenerator guidGenerator, IAbpDistributedLock distributedLock, IUnitOfWorkManager unitOfWorkManager, IApplicationInfoAccessor applicationInfoAccessor, ICancellationTokenProvider cancellationTokenProvider, IStringLocalizerFactory stringLocalizerFactory, IOptions cacheOptions, - IOptions localizationOptions, - IOptions localizationManagementOptions, - ILanguageRepository languageRepository, - IResourceRepository resourceRepository, + IOptions localizationOptions, + IOptions localizationManagementOptions, + ILanguageRepository languageRepository, + IResourceRepository resourceRepository, ITextRepository textRepository) { Logger = logger; @@ -79,7 +79,9 @@ public class StaticLocalizationSaver : IStaticLocalizationSaver, ITransientDepen Logger.LogDebug("Waiting to acquire the distributed lock for saving static localizations..."); - await using var applicationLockHandle = await DistributedLock.TryAcquireAsync(GetApplicationDistributedLockKey()); + await using var applicationLockHandle = await DistributedLock.TryAcquireAsync( + GetApplicationDistributedLockKey(), + TimeSpan.FromSeconds(5)); if (applicationLockHandle == null) { return; diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationDynamicInitializer.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationDynamicInitializer.cs index 4d8e970a4..18a77bb9f 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationDynamicInitializer.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationDynamicInitializer.cs @@ -15,71 +15,60 @@ public class NotificationDynamicInitializer : ITransientDependency { public ILogger Logger { get; set; } protected IServiceProvider ServiceProvider { get; } - protected IOptions Options { get; } - protected IHostApplicationLifetime ApplicationLifetime { get; } - protected ICancellationTokenProvider CancellationTokenProvider { get; } - protected IDynamicNotificationDefinitionStore DynamicNotificationDefinitionStore { get; } - protected IStaticNotificationSaver StaticNotificationSaver { get; } - - public NotificationDynamicInitializer( - IServiceProvider serviceProvider, - IOptions options, - ICancellationTokenProvider cancellationTokenProvider, - IDynamicNotificationDefinitionStore dynamicNotificationDefinitionStore, - IStaticNotificationSaver staticNotificationSaver) + + public NotificationDynamicInitializer(IServiceProvider serviceProvider) { ServiceProvider = serviceProvider; - Options = options; - CancellationTokenProvider = cancellationTokenProvider; - DynamicNotificationDefinitionStore = dynamicNotificationDefinitionStore; - StaticNotificationSaver = staticNotificationSaver; - ApplicationLifetime = ServiceProvider.GetService(); Logger = NullLogger.Instance; } public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default) { - if (!Options.Value.SaveStaticNotificationsToDatabase && !Options.Value.IsDynamicNotificationsStoreEnabled) + var options = ServiceProvider.GetRequiredService>().Value; + + if (!options.SaveStaticNotificationsToDatabase && !options.IsDynamicNotificationsStoreEnabled) { return Task.CompletedTask; } if (runInBackground) { + var applicationLifetime = ServiceProvider.GetService(); Task.Run(async () => { - if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null) + if (cancellationToken == default && applicationLifetime?.ApplicationStopping != null) { - cancellationToken = ApplicationLifetime.ApplicationStopping; + cancellationToken = applicationLifetime.ApplicationStopping; } - await ExecuteInitializationAsync(cancellationToken); + await ExecuteInitializationAsync(options, cancellationToken); }, cancellationToken); return Task.CompletedTask; } - return ExecuteInitializationAsync(cancellationToken); + return ExecuteInitializationAsync(options, cancellationToken); } - protected virtual async Task ExecuteInitializationAsync(CancellationToken cancellationToken) + protected virtual async Task ExecuteInitializationAsync(AbpNotificationsManagementOptions options, CancellationToken cancellationToken) { try { - using (CancellationTokenProvider.Use(cancellationToken)) + var cancellationTokenProvider = ServiceProvider.GetRequiredService(); + using (cancellationTokenProvider.Use(cancellationToken)) { - if (CancellationTokenProvider.Token.IsCancellationRequested) + if (cancellationTokenProvider.Token.IsCancellationRequested) { return; } - await SaveStaticNotificationssToDatabaseAsync(cancellationToken); + await SaveStaticNotificationssToDatabaseAsync(options, cancellationToken); - if (CancellationTokenProvider.Token.IsCancellationRequested) + if (cancellationTokenProvider.Token.IsCancellationRequested) { return; } - await PreCacheDynamicNotificationsAsync(cancellationToken); + await PreCacheDynamicNotificationsAsync(options, cancellationToken); } } catch @@ -88,13 +77,15 @@ public class NotificationDynamicInitializer : ITransientDependency } } - protected virtual async Task SaveStaticNotificationssToDatabaseAsync(CancellationToken cancellationToken) + protected virtual async Task SaveStaticNotificationssToDatabaseAsync(AbpNotificationsManagementOptions options, CancellationToken cancellationToken) { - if (!Options.Value.SaveStaticNotificationsToDatabase) + if (!options.SaveStaticNotificationsToDatabase) { return; } + var staticNotificationSaver = ServiceProvider.GetRequiredService(); + await Policy .Handle(e => e is not OperationCanceledException) .WaitAndRetryAsync( @@ -111,7 +102,7 @@ public class NotificationDynamicInitializer : ITransientDependency { cancellationToken.ThrowIfCancellationRequested(); - await StaticNotificationSaver.SaveAsync(); + await staticNotificationSaver.SaveAsync(); } catch (Exception ex) { @@ -122,18 +113,20 @@ public class NotificationDynamicInitializer : ITransientDependency }, cancellationToken); } - protected virtual async Task PreCacheDynamicNotificationsAsync(CancellationToken cancellationToken) + protected virtual async Task PreCacheDynamicNotificationsAsync(AbpNotificationsManagementOptions options, CancellationToken cancellationToken) { - if (!Options.Value.IsDynamicNotificationsStoreEnabled) + if (!options.IsDynamicNotificationsStoreEnabled) { return; } + var dynamicNotificationDefinitionStore = ServiceProvider.GetRequiredService(); + try { cancellationToken.ThrowIfCancellationRequested(); // Pre-cache notifications, so first request doesn't wait - await DynamicNotificationDefinitionStore.GetGroupsAsync(); + await dynamicNotificationDefinitionStore.GetGroupsAsync(); } catch (Exception ex) { diff --git a/aspnet-core/modules/text-templating/LINGYUN.Abp.TextTemplating.Domain/LINGYUN/Abp/TextTemplating/TextTemplateDynamicInitializer.cs b/aspnet-core/modules/text-templating/LINGYUN.Abp.TextTemplating.Domain/LINGYUN/Abp/TextTemplating/TextTemplateDynamicInitializer.cs index d51ef86ba..835dcb328 100644 --- a/aspnet-core/modules/text-templating/LINGYUN.Abp.TextTemplating.Domain/LINGYUN/Abp/TextTemplating/TextTemplateDynamicInitializer.cs +++ b/aspnet-core/modules/text-templating/LINGYUN.Abp.TextTemplating.Domain/LINGYUN/Abp/TextTemplating/TextTemplateDynamicInitializer.cs @@ -16,71 +16,60 @@ public class TextTemplateDynamicInitializer : ITransientDependency public ILogger Logger { get; set; } protected IServiceProvider ServiceProvider { get; } - protected IOptions Options { get; } - protected IHostApplicationLifetime ApplicationLifetime { get; } - protected ICancellationTokenProvider CancellationTokenProvider { get; } - protected ITemplateDefinitionStore TemplateDefinitionStore { get; } - protected IStaticTemplateSaver StaticTemplateSaver { get; } - - public TextTemplateDynamicInitializer( - IServiceProvider serviceProvider, - IOptions options, - ICancellationTokenProvider cancellationTokenProvider, - ITemplateDefinitionStore templateDefinitionStore, - IStaticTemplateSaver staticTemplateSaver) + + public TextTemplateDynamicInitializer(IServiceProvider serviceProvider) { ServiceProvider = serviceProvider; - Options = options; - ApplicationLifetime = ServiceProvider.GetService(); - CancellationTokenProvider = cancellationTokenProvider; - TemplateDefinitionStore = templateDefinitionStore; - StaticTemplateSaver = staticTemplateSaver; Logger = NullLogger.Instance; } public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default) { - if (!Options.Value.SaveStaticTemplateDefinitionToDatabase && !Options.Value.IsDynamicTemplateDefinitionStoreEnabled) + var options = ServiceProvider.GetRequiredService>().Value; + + if (!options.SaveStaticTemplateDefinitionToDatabase && !options.IsDynamicTemplateDefinitionStoreEnabled) { return Task.CompletedTask; } if (runInBackground) { + var applicationLifetime = ServiceProvider.GetService(); Task.Run(async () => { - if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null) + if (cancellationToken == default && applicationLifetime?.ApplicationStopping != null) { - cancellationToken = ApplicationLifetime.ApplicationStopping; + cancellationToken = applicationLifetime.ApplicationStopping; } - await ExecuteInitializationAsync(cancellationToken); + await ExecuteInitializationAsync(options, cancellationToken); }, cancellationToken); return Task.CompletedTask; } - return ExecuteInitializationAsync(cancellationToken); + return ExecuteInitializationAsync(options, cancellationToken); } - protected virtual async Task ExecuteInitializationAsync(CancellationToken cancellationToken) + protected virtual async Task ExecuteInitializationAsync(AbpTextTemplatingCachingOptions options, CancellationToken cancellationToken) { try { - using (CancellationTokenProvider.Use(cancellationToken)) + var cancellationTokenProvider = ServiceProvider.GetRequiredService(); + using (cancellationTokenProvider.Use(cancellationToken)) { - if (CancellationTokenProvider.Token.IsCancellationRequested) + if (cancellationTokenProvider.Token.IsCancellationRequested) { return; } - await SaveStaticTextTemplatesToDatabaseAsync(cancellationToken); + await SaveStaticTextTemplatesToDatabaseAsync(options, cancellationToken); - if (CancellationTokenProvider.Token.IsCancellationRequested) + if (cancellationTokenProvider.Token.IsCancellationRequested) { return; } - await PreCacheDynamicTextTemplatesAsync(cancellationToken); + await PreCacheDynamicTextTemplatesAsync(options, cancellationToken); } } catch @@ -89,13 +78,15 @@ public class TextTemplateDynamicInitializer : ITransientDependency } } - protected virtual async Task SaveStaticTextTemplatesToDatabaseAsync(CancellationToken cancellationToken) + protected virtual async Task SaveStaticTextTemplatesToDatabaseAsync(AbpTextTemplatingCachingOptions options, CancellationToken cancellationToken) { - if (!Options.Value.SaveStaticTemplateDefinitionToDatabase) + if (!options.SaveStaticTemplateDefinitionToDatabase) { return; } + var staticTemplateSaver = ServiceProvider.GetRequiredService(); + await Policy .Handle(e => e is not OperationCanceledException) .WaitAndRetryAsync( @@ -112,8 +103,8 @@ public class TextTemplateDynamicInitializer : ITransientDependency { cancellationToken.ThrowIfCancellationRequested(); - await StaticTemplateSaver.SaveDefinitionTemplateAsync(); - await StaticTemplateSaver.SaveTemplateContentAsync(); + await staticTemplateSaver.SaveDefinitionTemplateAsync(); + await staticTemplateSaver.SaveTemplateContentAsync(); } catch (Exception ex) { @@ -124,19 +115,21 @@ public class TextTemplateDynamicInitializer : ITransientDependency }, cancellationToken); } - protected virtual async Task PreCacheDynamicTextTemplatesAsync(CancellationToken cancellationToken) + protected virtual async Task PreCacheDynamicTextTemplatesAsync(AbpTextTemplatingCachingOptions options, CancellationToken cancellationToken) { - if (!Options.Value.IsDynamicTemplateDefinitionStoreEnabled) + if (!options.IsDynamicTemplateDefinitionStoreEnabled) { return; } + var templateDefinitionStore = ServiceProvider.GetRequiredService(); + try { cancellationToken.ThrowIfCancellationRequested(); // Pre-cache tempte definitions, so first request doesn't wait - await TemplateDefinitionStore.GetAllAsync(); + await templateDefinitionStore.GetAllAsync(); } catch (Exception ex) { diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookDynamicInitializer.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookDynamicInitializer.cs index 644507554..287288bd9 100644 --- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookDynamicInitializer.cs +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookDynamicInitializer.cs @@ -1,5 +1,4 @@ -using JetBrains.Annotations; -using LINGYUN.Abp.Webhooks; +using LINGYUN.Abp.Webhooks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -18,72 +17,60 @@ public class WebhookDynamicInitializer : ITransientDependency public ILogger Logger { get; set; } protected IServiceProvider ServiceProvider { get; } - protected IOptions Options { get; } - [CanBeNull] - protected IHostApplicationLifetime ApplicationLifetime { get; } - protected ICancellationTokenProvider CancellationTokenProvider { get; } - protected IDynamicWebhookDefinitionStore DynamicWebhookDefinitionStore { get; } - protected IStaticWebhookSaver StaticWebhookSaver { get; } - - public WebhookDynamicInitializer( - IServiceProvider serviceProvider, - IOptions options, - ICancellationTokenProvider cancellationTokenProvider, - IDynamicWebhookDefinitionStore dynamicWebhookDefinitionStore, - IStaticWebhookSaver staticWebhookSaver) + + public WebhookDynamicInitializer(IServiceProvider serviceProvider) { ServiceProvider = serviceProvider; - Options = options; - ApplicationLifetime = ServiceProvider.GetService(); - CancellationTokenProvider = cancellationTokenProvider; - DynamicWebhookDefinitionStore = dynamicWebhookDefinitionStore; - StaticWebhookSaver = staticWebhookSaver; Logger = NullLogger.Instance; } public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default) { - if (!Options.Value.SaveStaticWebhooksToDatabase && !Options.Value.IsDynamicWebhookStoreEnabled) + var options = ServiceProvider.GetRequiredService>().Value; + + if (!options.SaveStaticWebhooksToDatabase && !options.IsDynamicWebhookStoreEnabled) { return Task.CompletedTask; } if (runInBackground) { + var applicationLifetime = ServiceProvider.GetService(); Task.Run(async () => { - if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null) + if (cancellationToken == default && applicationLifetime?.ApplicationStopping != null) { - cancellationToken = ApplicationLifetime.ApplicationStopping; + cancellationToken = applicationLifetime.ApplicationStopping; } - await ExecuteInitializationAsync(cancellationToken); + await ExecuteInitializationAsync(options, cancellationToken); }, cancellationToken); return Task.CompletedTask; } - return ExecuteInitializationAsync(cancellationToken); + return ExecuteInitializationAsync(options, cancellationToken); } - protected virtual async Task ExecuteInitializationAsync(CancellationToken cancellationToken) + protected virtual async Task ExecuteInitializationAsync(WebhooksManagementOptions options, CancellationToken cancellationToken) { try { - using (CancellationTokenProvider.Use(cancellationToken)) + var cancellationTokenProvider = ServiceProvider.GetRequiredService(); + using (cancellationTokenProvider.Use(cancellationToken)) { - if (CancellationTokenProvider.Token.IsCancellationRequested) + if (cancellationTokenProvider.Token.IsCancellationRequested) { return; } - await SaveStaticWebhooksToDatabaseAsync(cancellationToken); + await SaveStaticWebhooksToDatabaseAsync(options, cancellationToken); - if (CancellationTokenProvider.Token.IsCancellationRequested) + if (cancellationTokenProvider.Token.IsCancellationRequested) { return; } - await PreCacheDynamicWebhooksAsync(cancellationToken); + await PreCacheDynamicWebhooksAsync(options, cancellationToken); } } catch @@ -92,13 +79,15 @@ public class WebhookDynamicInitializer : ITransientDependency } } - protected virtual async Task SaveStaticWebhooksToDatabaseAsync(CancellationToken cancellationToken) + protected virtual async Task SaveStaticWebhooksToDatabaseAsync(WebhooksManagementOptions options, CancellationToken cancellationToken) { - if (!Options.Value.SaveStaticWebhooksToDatabase) + if (!options.SaveStaticWebhooksToDatabase) { return; } + var staticWebhookSaver = ServiceProvider.GetRequiredService(); + await Policy .Handle(e => e is not OperationCanceledException) .WaitAndRetryAsync( @@ -115,7 +104,7 @@ public class WebhookDynamicInitializer : ITransientDependency { cancellationToken.ThrowIfCancellationRequested(); - await StaticWebhookSaver.SaveAsync(); + await staticWebhookSaver.SaveAsync(); } catch (Exception ex) { @@ -126,19 +115,21 @@ public class WebhookDynamicInitializer : ITransientDependency }, cancellationToken); } - protected virtual async Task PreCacheDynamicWebhooksAsync(CancellationToken cancellationToken) + protected virtual async Task PreCacheDynamicWebhooksAsync(WebhooksManagementOptions options, CancellationToken cancellationToken) { - if (!Options.Value.IsDynamicWebhookStoreEnabled) + if (!options.IsDynamicWebhookStoreEnabled) { return; } + var dynamicWebhookDefinitionStore = ServiceProvider.GetRequiredService(); + try { cancellationToken.ThrowIfCancellationRequested(); // Pre-cache webhoks, so first request doesn't wait - await DynamicWebhookDefinitionStore.GetGroupsAsync(); + await dynamicWebhookDefinitionStore.GetGroupsAsync(); } catch (Exception ex) { From 6b0dc0b93b3b42c38b924c6cd5e13f07cf8a4d9b Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 4 Mar 2026 14:42:19 +0800 Subject: [PATCH 27/88] fix: Add the missing permission dependencies --- .../LINGYUN/Abp/Notifications/NotificationAppService.cs | 1 + .../LINGYUN/Abp/Notifications/NotificationController.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/NotificationAppService.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/NotificationAppService.cs index b2e095b70..efee89a25 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/NotificationAppService.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/NotificationAppService.cs @@ -127,6 +127,7 @@ public class NotificationAppService : AbpNotificationsApplicationServiceBase, IN severity: input.Severity); } + [Authorize(NotificationsPermissions.Notification.Send)] public async virtual Task SendTemplateAsync(NotificationTemplateSendDto input) { var notificationTemplate = new NotificationTemplate( diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.HttpApi/LINGYUN/Abp/Notifications/NotificationController.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.HttpApi/LINGYUN/Abp/Notifications/NotificationController.cs index bf5dd07cc..b0b13c195 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.HttpApi/LINGYUN/Abp/Notifications/NotificationController.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.HttpApi/LINGYUN/Abp/Notifications/NotificationController.cs @@ -33,6 +33,7 @@ public class NotificationController : AbpControllerBase, INotificationAppService [HttpPost] [Route("send/template")] + [Authorize(NotificationsPermissions.Notification.Send)] public virtual Task SendTemplateAsync(NotificationTemplateSendDto input) { return NotificationAppService.SendTemplateAsync(input); From 28411ec8e5c0f7a4898c20661524dbcdcf6d4595 Mon Sep 17 00:00:00 2001 From: colin Date: Thu, 5 Mar 2026 10:23:10 +0800 Subject: [PATCH 28/88] fix: Fix some compilation errors - Add the missing constructor injection --- .../EfCoreSecurityLogWriter.cs | 32 +++++++------------ .../IP/Location/IPLocationAuditingStore.cs | 3 +- .../IP/Location/IPLocationSecurityLogStore.cs | 3 +- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.EntityFrameworkCore/LINGYUN/Abp/AuditLogging/EntityFrameworkCore/EfCoreSecurityLogWriter.cs b/aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.EntityFrameworkCore/LINGYUN/Abp/AuditLogging/EntityFrameworkCore/EfCoreSecurityLogWriter.cs index d9b27a0f5..c617aa49d 100644 --- a/aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.EntityFrameworkCore/LINGYUN/Abp/AuditLogging/EntityFrameworkCore/EfCoreSecurityLogWriter.cs +++ b/aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.EntityFrameworkCore/LINGYUN/Abp/AuditLogging/EntityFrameworkCore/EfCoreSecurityLogWriter.cs @@ -14,44 +14,34 @@ namespace LINGYUN.Abp.AuditLogging.EntityFrameworkCore; public class EfCoreSecurityLogWriter : ISecurityLogWriter, ITransientDependency { protected IIdentitySecurityLogRepository IdentitySecurityLogRepository { get; } - protected IUnitOfWorkManager UnitOfWorkManager { get; } protected IGuidGenerator GuidGenerator { get; } public EfCoreSecurityLogWriter( IIdentitySecurityLogRepository identitySecurityLogRepository, - IUnitOfWorkManager unitOfWorkManager, IGuidGenerator guidGenerator) { IdentitySecurityLogRepository = identitySecurityLogRepository; - UnitOfWorkManager = unitOfWorkManager; GuidGenerator = guidGenerator; } + [UnitOfWork] public async virtual Task BulkWriteAsync(IEnumerable securityLogInfos, CancellationToken cancellationToken = default) { - using (var uow = UnitOfWorkManager.Begin(requiresNew: true)) - { - var securityLogs = securityLogInfos.Select(securityLogInfo => + var securityLogs = securityLogInfos.Select(securityLogInfo => new IdentitySecurityLog(GuidGenerator, securityLogInfo)); - await IdentitySecurityLogRepository.InsertManyAsync( - securityLogs, - false, - cancellationToken); - - await uow.CompleteAsync(); - } + await IdentitySecurityLogRepository.InsertManyAsync( + securityLogs, + false, + cancellationToken); } + [UnitOfWork] public async virtual Task WriteAsync(SecurityLogInfo securityLogInfo, CancellationToken cancellationToken = default) { - using (var uow = UnitOfWorkManager.Begin(requiresNew: true)) - { - await IdentitySecurityLogRepository.InsertAsync( - new IdentitySecurityLog(GuidGenerator, securityLogInfo), - false, - cancellationToken); - await uow.CompleteAsync(); - } + await IdentitySecurityLogRepository.InsertAsync( + new IdentitySecurityLog(GuidGenerator, securityLogInfo), + false, + cancellationToken); } } diff --git a/aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.IP.Location/LINGYUN/Abp/AuditLogging/IP/Location/IPLocationAuditingStore.cs b/aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.IP.Location/LINGYUN/Abp/AuditLogging/IP/Location/IPLocationAuditingStore.cs index 18545ca72..06bbb7861 100644 --- a/aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.IP.Location/LINGYUN/Abp/AuditLogging/IP/Location/IPLocationAuditingStore.cs +++ b/aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.IP.Location/LINGYUN/Abp/AuditLogging/IP/Location/IPLocationAuditingStore.cs @@ -14,9 +14,10 @@ public class IPLocationAuditingStore : AuditingStore IOptionsMonitor options, IIPLocationResolver iPLocationResolver, IOptionsMonitor loggingOptions, + IAuditLogWriter auditLogWriter, IAuditLogQueue auditLogQueue, ILogger logger) - : base(loggingOptions, auditLogQueue, logger) + : base(loggingOptions, auditLogWriter, auditLogQueue, logger) { _options = options.CurrentValue; _iPLocationResolver = iPLocationResolver; diff --git a/aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.IP.Location/LINGYUN/Abp/AuditLogging/IP/Location/IPLocationSecurityLogStore.cs b/aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.IP.Location/LINGYUN/Abp/AuditLogging/IP/Location/IPLocationSecurityLogStore.cs index c961d782f..86e0a2b4d 100644 --- a/aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.IP.Location/LINGYUN/Abp/AuditLogging/IP/Location/IPLocationSecurityLogStore.cs +++ b/aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.IP.Location/LINGYUN/Abp/AuditLogging/IP/Location/IPLocationSecurityLogStore.cs @@ -16,9 +16,10 @@ public class IPLocationSecurityLogStore : SecurityLogStore IIPLocationResolver iPLocationResolver, IOptionsMonitor securityLogOptions, IOptionsMonitor loggingOptions, + ISecurityLogWriter securityLogWriter, ISecurityLogQueue securityLogQueue, ILogger logger) - : base(securityLogOptions, loggingOptions, securityLogQueue, logger) + : base(securityLogOptions, loggingOptions, securityLogWriter, securityLogQueue, logger) { _options = options.CurrentValue; _iPLocationResolver = iPLocationResolver; From 18c84e470e2a1f0dd62c2553591d792d2d121885 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 6 Mar 2026 11:17:20 +0800 Subject: [PATCH 29/88] feat: Optimize IP address location --- .../LY.MicroService.Applications.Single.csproj | 2 +- ...ServiceApplicationsSingleModule.Configure.cs | 17 +++++++++++++++++ .../MicroServiceApplicationsSingleModule.cs | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj b/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj index 1769857de..143616ccc 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj +++ b/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj @@ -7,7 +7,7 @@ enable LY.MicroService.Applications.Single enable - 9.3.6.2 + 10.0.2 diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs index a5efc7952..33c8593c7 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs +++ b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs @@ -482,6 +482,23 @@ public partial class MicroServiceApplicationsSingleModule }); } + private void ConfigureIP2RegionIPLocation() + { + Configure(options => + { + // 仅中国IP不显示国家 + options.UseCountry = (localtion) => + { + return !string.Equals("中国", localtion.Country); + }; + // 仅中国IP显示省份 + options.UseProvince = (localtion) => + { + return string.Equals("中国", localtion.Country); + }; + }); + } + private void ConfigureIdempotent() { Configure(options => diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs index b41c08a84..b9f5187b2 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs +++ b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs @@ -428,6 +428,7 @@ public partial class MicroServiceApplicationsSingleModule : AbpModule ConfigureBackgroundTasks(); ConfigureExceptionHandling(); ConfigureVirtualFileSystem(); + ConfigureIP2RegionIPLocation(); ConfigureEntityDataProtected(); ConfigureUrls(configuration); ConfigureAuditing(configuration); From d3640b85d8e9bdc565d969cf0cbd2b4c95081f04 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 6 Mar 2026 11:25:38 +0800 Subject: [PATCH 30/88] feat: Optimize localization resources - Adjust the localization resource path of OpenIddict Account - Adjust the localization resource path of Elsa Designer --- .../AbpAccountWebOpenIddictModule.cs | 4 ++-- .../Localization/Resources/{ => OpenIddict}/en.json | 0 .../Localization/Resources/{ => OpenIddict}/zh-Hans.json | 0 .../elsa/LINGYUN.Abp.Elsa.Designer/AbpElsaDesignerModule.cs | 4 ++-- .../Localization/Resources/{ => ElsaDesigner}/en.json | 0 .../Localization/Resources/{ => ElsaDesigner}/zh-Hans.json | 0 .../elsa/LINGYUN.Abp.Elsa.Designer/Pages/Elsa/Index.cshtml | 2 +- 7 files changed, 5 insertions(+), 5 deletions(-) rename aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Localization/Resources/{ => OpenIddict}/en.json (100%) rename aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Localization/Resources/{ => OpenIddict}/zh-Hans.json (100%) rename aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/Localization/Resources/{ => ElsaDesigner}/en.json (100%) rename aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/Localization/Resources/{ => ElsaDesigner}/zh-Hans.json (100%) diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/AbpAccountWebOpenIddictModule.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/AbpAccountWebOpenIddictModule.cs index 81a6cb805..66b4779f2 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/AbpAccountWebOpenIddictModule.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/AbpAccountWebOpenIddictModule.cs @@ -34,7 +34,7 @@ public class AbpAccountWebOpenIddictModule : AbpModule { Configure(options => { - options.FileSets.AddEmbedded(); + options.FileSets.AddEmbedded("LINGYUN.Abp.Account.Web.OpenIddict"); }); Configure(options => @@ -52,7 +52,7 @@ public class AbpAccountWebOpenIddictModule : AbpModule { options.Resources .Get() - .AddVirtualJson("/Localization/Resources"); + .AddVirtualJson("/Localization/Resources/OpenIddict"); }); Configure(options => diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Localization/Resources/en.json b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Localization/Resources/OpenIddict/en.json similarity index 100% rename from aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Localization/Resources/en.json rename to aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Localization/Resources/OpenIddict/en.json diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Localization/Resources/zh-Hans.json b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Localization/Resources/OpenIddict/zh-Hans.json similarity index 100% rename from aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Localization/Resources/zh-Hans.json rename to aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Localization/Resources/OpenIddict/zh-Hans.json diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/AbpElsaDesignerModule.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/AbpElsaDesignerModule.cs index e02572cfd..45e0b64aa 100644 --- a/aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/AbpElsaDesignerModule.cs +++ b/aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/AbpElsaDesignerModule.cs @@ -34,14 +34,14 @@ public class AbpElsaDesignerModule : AbpModule { Configure(options => { - options.FileSets.AddEmbedded(); + options.FileSets.AddEmbedded("LINGYUN.Abp.Elsa.Designer"); }); Configure(options => { options.Resources .Get() - .AddVirtualJson("/Localization/Resources"); + .AddVirtualJson("/Localization/Resources/ElsaDesigner"); }); Configure(options => diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/Localization/Resources/en.json b/aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/Localization/Resources/ElsaDesigner/en.json similarity index 100% rename from aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/Localization/Resources/en.json rename to aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/Localization/Resources/ElsaDesigner/en.json diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/Localization/Resources/zh-Hans.json b/aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/Localization/Resources/ElsaDesigner/zh-Hans.json similarity index 100% rename from aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/Localization/Resources/zh-Hans.json rename to aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/Localization/Resources/ElsaDesigner/zh-Hans.json diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/Pages/Elsa/Index.cshtml b/aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/Pages/Elsa/Index.cshtml index 7d1ec89e9..c022de8b6 100644 --- a/aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/Pages/Elsa/Index.cshtml +++ b/aspnet-core/modules/elsa/LINGYUN.Abp.Elsa.Designer/Pages/Elsa/Index.cshtml @@ -46,6 +46,6 @@ // Some components publish DOM events that we can handle directly: elsaStudioRoot.addEventListener('workflow-changed', e => { - console.log('Workflow model changed! New model: ${e.detail}'); + console.log(`Workflow model changed! New model: ${e.detail}`); }) \ No newline at end of file From 6781539137ca6218840d70d30b664542ba7eb30f Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 6 Mar 2026 11:26:51 +0800 Subject: [PATCH 31/88] feat: Optimize abpStore --- .../vben5/packages/@abp/core/src/store/abp.ts | 1 + .../packages/@abp/core/src/utils/date.ts | 74 +++++++++++++++++-- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/apps/vben5/packages/@abp/core/src/store/abp.ts b/apps/vben5/packages/@abp/core/src/store/abp.ts index 567c8ca95..ee5837697 100644 --- a/apps/vben5/packages/@abp/core/src/store/abp.ts +++ b/apps/vben5/packages/@abp/core/src/store/abp.ts @@ -58,6 +58,7 @@ export const useAbpStore = defineStore( function setApplication(val: ApplicationConfigurationDto) { application.value = val; + setTenantId(val.currentTenant?.id); xsrfToken.value = cookies.get('XSRF-TOKEN'); } diff --git a/apps/vben5/packages/@abp/core/src/utils/date.ts b/apps/vben5/packages/@abp/core/src/utils/date.ts index c388d0834..7a13d5bbf 100644 --- a/apps/vben5/packages/@abp/core/src/utils/date.ts +++ b/apps/vben5/packages/@abp/core/src/utils/date.ts @@ -6,6 +6,12 @@ import dayjs from 'dayjs'; const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; const DATE_FORMAT = 'YYYY-MM-DD'; +/** + * @zh_CN 格式化时间 + * @param date 需要格式化的时间 + * @param format 格式化字符串,参考dayJs文档 + * @returns 返回格式化后的时间字符串 + */ export function formatToDateTime( date?: dayjs.ConfigType, format = DATE_TIME_FORMAT, @@ -13,6 +19,12 @@ export function formatToDateTime( return dayjs(date).format(format); } +/** + * @zh_CN 格式化日期 + * @param date 需要格式化的日期 + * @param format 格式化字符串,参考dayJs文档 + * @returns 返回格式化后的日期字符串 + */ export function formatToDate( date?: dayjs.ConfigType, format = DATE_FORMAT, @@ -21,7 +33,7 @@ export function formatToDate( return dayjs(date).format(format); } /** - * 获取指定日期 + * @zh_CN 获取指定日期 * @param days 天数 * @returns 返回指定天数之后的日期 */ @@ -36,7 +48,7 @@ export function getAppointDate(days: number): dayjs.Dayjs { } /** - * 获取本周第一天 + * @zh_CN 获取本周第一天 * @returns 返回本周第一天 */ export function firstDayOfWeek(): dayjs.Dayjs { @@ -51,7 +63,15 @@ export function firstDayOfWeek(): dayjs.Dayjs { } /** - * 获取当月第一天 + * @zh_CN 获取本周最后一天 + * @returns 返回本周最后一天 + */ +export function lastDayOfWeek(): dayjs.Dayjs { + return firstDayOfWeek().add(6, 'day'); +} + +/** + * @zh_CN 获取当月第一天 * @returns 返回当月第一天 */ export function firstDayOfMonth(): dayjs.Dayjs { @@ -60,7 +80,7 @@ export function firstDayOfMonth(): dayjs.Dayjs { } /** - * 获取当月最后一天00:00:00 + * @zh_CN 获取当月最后一天00:00:00 * @returns 返回当月最后一天00:00:00 */ export function lastDayOfMonth(): dayjs.Dayjs { @@ -69,7 +89,7 @@ export function lastDayOfMonth(): dayjs.Dayjs { } /** - * 获取当月最后一天23:59:59 + * @zh_CN 获取当月最后一天23:59:59 * @returns 返回当月最后一天23:59:59 */ export function lastDateOfMonth(): dayjs.Dayjs { @@ -80,7 +100,7 @@ export function lastDateOfMonth(): dayjs.Dayjs { } /** - * 获取上个月第一天 + * @zh_CN 获取上个月第一天 * @returns 返回上个月第一天 */ export function firstDayOfLastMonth(): dayjs.Dayjs { @@ -89,7 +109,7 @@ export function firstDayOfLastMonth(): dayjs.Dayjs { } /** - * 获取上个月最后一天23:59:59 + * @zh_CN 获取上个月最后一天23:59:59 * @returns 返回上个月最后一天23:59:59 */ export function lastDateOfLastMonth(): dayjs.Dayjs { @@ -100,7 +120,7 @@ export function lastDateOfLastMonth(): dayjs.Dayjs { } /** - * 获取本年第一天 + * @zh_CN 获取本年第一天 * @returns 返回本年第一天 */ export function firstDayOfYear(): dayjs.Dayjs { @@ -108,4 +128,42 @@ export function firstDayOfYear(): dayjs.Dayjs { return dayjs(new Date(now.getFullYear(), 0, 1)); } +/** + * @zh_CN 获取本年最后一天 + * @returns 返回本年最后一天 + */ +export function lastDayOfYear(): dayjs.Dayjs { + const now = new Date(); + return dayjs(new Date(now.getFullYear(), 11, 31)); +} + +/** + * @zh_CN 获取最近半年第一天 + * @returns 返回最近半年第一天 + */ +export function firstDayOfLastHalfYear(): dayjs.Dayjs { + const now = new Date(); + return dayjs(new Date(now.getFullYear(), now.getMonth() - 6, 1)); +} + +/** + * @zh_CN 获取本季度第一天 + * @returns 返回本季度第一天 + */ +export function firstDayOfQuarter(): dayjs.Dayjs { + const now = new Date(); + const quarter = Math.floor(now.getMonth() / 3) + 1; + return dayjs(new Date(now.getFullYear(), (quarter - 1) * 3, 1)); +} + +/** + * @zh_CN 获取本季度最后一天 + * @returns 返回本季度最后一天 + */ +export function lastDayOfQuarter(): dayjs.Dayjs { + const now = new Date(); + const quarter = Math.floor(now.getMonth() / 3) + 1; + return dayjs(new Date(now.getFullYear(), quarter * 3, 0)); +} + export const dateUtil = dayjs; From f70330b2a4fbe9885ccfaebc6cdba7bcfae346f3 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 6 Mar 2026 11:28:20 +0800 Subject: [PATCH 32/88] fix: Fix the issue where virtual-file-explorer fails to open files. - Add the `@abp/virtual-file-explorer` package dependency --- .../services/LY.MicroService.Applications.Single/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/package.json b/aspnet-core/services/LY.MicroService.Applications.Single/package.json index 63653b369..c7592d0be 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/package.json +++ b/aspnet-core/services/LY.MicroService.Applications.Single/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "5.0.2", + "@abp/virtual-file-explorer": "10.0.2", "@abp/qrcode": "10.0.2" } } \ No newline at end of file From 1d1e6a80b9db06c9421292233ffc4a3948cb2ba2 Mon Sep 17 00:00:00 2001 From: colin Date: Tue, 10 Mar 2026 16:34:04 +0800 Subject: [PATCH 33/88] feat: TimeZoneSettings Support - Add TimeZoneSettings Interface - Use IanaTimezones for time zone settings --- .../SettingManagement/SettingAppService.cs | 7 +- .../TimeZoneSettingsAppService.cs | 67 +++++++++++++++++++ .../TimeZoneSettingsController.cs | 42 ++++++++++++ 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/TimeZoneSettingsAppService.cs create mode 100644 aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.HttpApi/LINGYUN/Abp/SettingManagement/TimeZoneSettingsController.cs diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs index 7a40bc1ac..9ebac6603 100644 --- a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs +++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs @@ -145,7 +145,12 @@ public class SettingAppService : ApplicationService, ISettingAppService, ISettin providerName) ?.AddOptions(LocalizationOptions.Languages.Select(l => new OptionDto(l.DisplayName, l.CultureName))); // 时区 - var timezones = TimeZoneHelper.GetTimezones(TimezoneProvider.GetWindowsTimezones()); + var timezones = TimeZoneHelper.GetTimezones(TimezoneProvider.GetIanaTimezones()); + timezones.Insert(0, new NameValue + { + Name = L["DefaultTimeZone"], + Value = "Unspecified" + }); var timingSetting = sysSettingGroup.AddSetting(L["DisplayName:System.Timing"], L["Description:System.Timing"]); timingSetting.AddDetail( await SettingDefinitionManager.GetAsync(TimingSettingNames.TimeZone), diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/TimeZoneSettingsAppService.cs b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/TimeZoneSettingsAppService.cs new file mode 100644 index 000000000..c2ed056b5 --- /dev/null +++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/TimeZoneSettingsAppService.cs @@ -0,0 +1,67 @@ +using Microsoft.AspNetCore.Authorization; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.MultiTenancy; +using Volo.Abp.SettingManagement; +using Volo.Abp.Timing; + +namespace LINGYUN.Abp.SettingManagement; + +[Authorize(Volo.Abp.SettingManagement.SettingManagementPermissions.TimeZone)] +public class TimeZoneSettingsAppService : SettingManagementAppServiceBase, ITimeZoneSettingsAppService +{ + protected ISettingManager SettingManager { get; } + protected ITimezoneProvider TimezoneProvider { get; } + + private const string UnspecifiedTimeZone = "Unspecified"; + + public TimeZoneSettingsAppService(ISettingManager settingManager, ITimezoneProvider timezoneProvider) + { + SettingManager = settingManager; + TimezoneProvider = timezoneProvider; + } + + public virtual async Task GetAsync() + { + var timezone = CurrentTenant.GetMultiTenancySide() == MultiTenancySides.Host + ? await SettingManager.GetOrNullGlobalAsync(TimingSettingNames.TimeZone) + : await SettingManager.GetOrNullForCurrentTenantAsync(TimingSettingNames.TimeZone); + + if (timezone.IsNullOrWhiteSpace()) + { + timezone = UnspecifiedTimeZone; + } + + return timezone; + } + + public virtual Task> GetTimezonesAsync() + { + var timezones = TimeZoneHelper.GetTimezones(TimezoneProvider.GetIanaTimezones()); + timezones.Insert(0, new NameValue + { + Name = L["DefaultTimeZone"], + Value = UnspecifiedTimeZone + }); + return Task.FromResult(timezones); + } + + public virtual async Task UpdateAsync(string timezone) + { + if (timezone.Equals(UnspecifiedTimeZone, StringComparison.OrdinalIgnoreCase)) + { + timezone = null; + } + + if (CurrentTenant.GetMultiTenancySide() == MultiTenancySides.Host) + { + await SettingManager.SetGlobalAsync(TimingSettingNames.TimeZone, timezone); + } + else + { + await SettingManager.SetForCurrentTenantAsync(TimingSettingNames.TimeZone, timezone); + } + } +} diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.HttpApi/LINGYUN/Abp/SettingManagement/TimeZoneSettingsController.cs b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.HttpApi/LINGYUN/Abp/SettingManagement/TimeZoneSettingsController.cs new file mode 100644 index 000000000..724563df9 --- /dev/null +++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.HttpApi/LINGYUN/Abp/SettingManagement/TimeZoneSettingsController.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.SettingManagement; + +namespace LINGYUN.Abp.SettingManagement; + +[RemoteService(Name = AbpSettingManagementRemoteServiceConsts.RemoteServiceName)] +[Area(AbpSettingManagementRemoteServiceConsts.ModuleName)] +[Route("api/setting-management/timezone")] +[Authorize(Volo.Abp.SettingManagement.SettingManagementPermissions.TimeZone)] +public class TimeZoneSettingsController : AbpControllerBase, ITimeZoneSettingsAppService +{ + private readonly ITimeZoneSettingsAppService _service; + + public TimeZoneSettingsController(ITimeZoneSettingsAppService service) + { + _service = service; + } + + [HttpGet] + public virtual Task GetAsync() + { + return _service.GetAsync(); + } + + [HttpGet] + [Route("timezones")] + public virtual Task> GetTimezonesAsync() + { + return _service.GetTimezonesAsync(); + } + + [HttpPost] + public virtual Task UpdateAsync(string timezone) + { + return _service.UpdateAsync(timezone); + } +} From 760e43c5da169ddf42e3cd274e2f80f354f66e18 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 13 Mar 2026 11:21:32 +0800 Subject: [PATCH 34/88] feat: Add the workspace field to the conversation. --- ...rkspace-To-Conversation-Record.Designer.cs | 308 ++++++++++++++++++ ...18_Add-Workspace-To-Conversation-Record.cs | 30 ++ ...ServiceMigrationsDbContextModelSnapshot.cs | 5 + .../LINGYUN/Abp/AI/Agent/AgentService.cs | 1 + .../Chats/Dtos/ConversationCreateDto.cs | 9 +- .../Chats/Dtos/ConversationDto.cs | 2 + .../Chats/ConversationAppService.cs | 4 + .../AIManagement/Chats/ConversationRecord.cs | 14 +- .../AIManagement/Chats/ConversationStore.cs | 3 +- ...nagementDbContextModelBuilderExtensions.cs | 3 + 10 files changed, 376 insertions(+), 3 deletions(-) create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313010418_Add-Workspace-To-Conversation-Record.Designer.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313010418_Add-Workspace-To-Conversation-Record.cs diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313010418_Add-Workspace-To-Conversation-Record.Designer.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313010418_Add-Workspace-To-Conversation-Record.Designer.cs new file mode 100644 index 000000000..e6b5bb135 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313010418_Add-Workspace-To-Conversation-Record.Designer.cs @@ -0,0 +1,308 @@ +// +using System; +using LINGYUN.Abp.MicroService.AIService; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LINGYUN.Abp.MicroService.AIService.Migrations +{ + [DbContext(typeof(AIServiceMigrationsDbContext))] + [Migration("20260313010418_Add-Workspace-To-Conversation-Record")] + partial class AddWorkspaceToConversationRecord + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.ConversationRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UpdateAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.ToTable("AbpAIConversations", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.TextChatMessageRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ReplyAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReplyMessage") + .HasColumnType("text"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITextChatMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tokens.TokenUsageRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CachedInputTokenCount") + .HasColumnType("bigint"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("InputTokenCount") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MessageId") + .HasColumnType("uuid"); + + b.Property("OutputTokenCount") + .HasColumnType("bigint"); + + b.Property("ReasoningTokenCount") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TotalTokenCount") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITokenUsages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiBaseUrl") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ApiKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrequencyPenalty") + .HasColumnType("real"); + + b.Property("Instructions") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MaxOutputTokens") + .HasColumnType("integer"); + + b.Property("ModelName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PresencePenalty") + .HasColumnType("real"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SystemPrompt") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Temperature") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpAIWorkspaceDefinitions", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313010418_Add-Workspace-To-Conversation-Record.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313010418_Add-Workspace-To-Conversation-Record.cs new file mode 100644 index 000000000..b080e32e7 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313010418_Add-Workspace-To-Conversation-Record.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LINGYUN.Abp.MicroService.AIService.Migrations +{ + /// + public partial class AddWorkspaceToConversationRecord : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Workspace", + table: "AbpAIConversations", + type: "character varying(64)", + maxLength: 64, + nullable: false, + defaultValue: ""); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Workspace", + table: "AbpAIConversations"); + } + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs index 095a0771d..9cc821579 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs @@ -63,6 +63,11 @@ namespace LINGYUN.Abp.MicroService.AIService.Migrations b.Property("UpdateAt") .HasColumnType("timestamp with time zone"); + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + b.HasKey("Id"); b.ToTable("AbpAIConversations", (string)null); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs index 78c9f0508..046d714ce 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs @@ -127,6 +127,7 @@ public class AgentService : IAgentService, IScopedDependency var conversation = new Conversation( _guidGenerator.Create(), _localizerResource["NewConversation"], + message.Workspace, _clock.Now); await _conversationStore.SaveAsync(conversation); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationCreateDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationCreateDto.cs index 8112305b2..e8ad0a33b 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationCreateDto.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationCreateDto.cs @@ -1,8 +1,15 @@ -using Volo.Abp.Validation; +using LINGYUN.Abp.AIManagement.Workspaces; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Validation; namespace LINGYUN.Abp.AIManagement.Chats.Dtos; public class ConversationCreateDto { [DynamicStringLength(typeof(ConversationRecordConsts), nameof(ConversationRecordConsts.MaxNameLength))] public string? Name { get; set; } + + [Required] + [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxNameLength))] + public string Workspace { get; set; } + } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationDto.cs index 8f5945c95..5a64ac79e 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationDto.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ConversationDto.cs @@ -6,6 +6,8 @@ public class ConversationDto : AuditedEntityDto { public string Name { get; set; } + public string Workspace { get; set; } + public DateTime CreatedAt { get; set; } public DateTime ExpiredAt { get; set; } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs index 203747602..dc1fc00af 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs @@ -46,6 +46,7 @@ public class ConversationAppService : return new ConversationRecord( GuidGenerator.Create(), conversationName, + createInput.Workspace, createdAt, expiredTime, CurrentTenant.Id); @@ -57,6 +58,8 @@ public class ConversationAppService : { entity.SetName(updateInput.Name); } + + entity.ChangeTime(Clock.Now); } protected override ConversationDto MapToGetOutputDto(ConversationRecord entity) @@ -72,6 +75,7 @@ public class ConversationAppService : LastModifierId = entity.LastModifierId, Name = entity.Name, UpdateAt = entity.UpdateAt, + Workspace = entity.Workspace, }; } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationRecord.cs index 10192ab8b..939d6f935 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationRecord.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationRecord.cs @@ -1,4 +1,5 @@ -using System; +using LINGYUN.Abp.AIManagement.Workspaces; +using System; using Volo.Abp; using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; @@ -10,6 +11,8 @@ public class ConversationRecord : AuditedEntity, IMultiTenant public string Name { get; private set; } + public string Workspace { get; private set; } + public DateTime CreatedAt { get; private set; } public DateTime ExpiredAt { get; set; } @@ -18,13 +21,16 @@ public class ConversationRecord : AuditedEntity, IMultiTenant public ConversationRecord( Guid id, string name, + string workspace, DateTime createdAt, DateTime expiredAt, Guid? tenantId = null) : base(id) { Name = Check.NotNullOrWhiteSpace(name, nameof(name), ConversationRecordConsts.MaxNameLength); + Workspace = Check.NotNullOrWhiteSpace(workspace, nameof(workspace), WorkspaceDefinitionRecordConsts.MaxNameLength); CreatedAt = createdAt; + CreationTime = createdAt; ExpiredAt = expiredAt; UpdateAt = createdAt; @@ -36,4 +42,10 @@ public class ConversationRecord : AuditedEntity, IMultiTenant { Name = Check.NotNullOrWhiteSpace(name, nameof(name), ConversationRecordConsts.MaxNameLength); } + + public void ChangeTime(DateTime updateTime) + { + UpdateAt = updateTime; + LastModificationTime = updateTime; + } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationStore.cs index 3d7d2ed5e..7ee2c120a 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationStore.cs @@ -3,7 +3,6 @@ using LINGYUN.Abp.AI.Models; using Microsoft.Extensions.Options; using System; using System.Threading.Tasks; -using Volo.Abp; using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; using Volo.Abp.Specifications; @@ -58,6 +57,7 @@ public class ConversationStore : IConversationStore, ITransientDependency var conversation = new Conversation( conversationRecord.Id, conversationRecord.Name, + conversationRecord.Workspace, conversationRecord.CreatedAt) { UpdateAt = conversationRecord.UpdateAt, @@ -77,6 +77,7 @@ public class ConversationStore : IConversationStore, ITransientDependency conversationRecord = new ConversationRecord( conversation.Id, conversation.Name, + conversation.Workspace, conversation.CreatedAt, expiredTime, _currentTenant.Id); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs index f3f5e9f9a..e62b67b95 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs @@ -24,6 +24,9 @@ public static class AIManagementDbContextModelBuilderExtensions b.Property(x => x.Name) .HasMaxLength(ConversationRecordConsts.MaxNameLength) .IsRequired(); + b.Property(x => x.Workspace) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxNameLength) + .IsRequired(); }); builder.Entity(b => { From d5380c46540aaffcfd1752f6d4af2920502dad07 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 13 Mar 2026 11:24:43 +0800 Subject: [PATCH 35/88] feat: Adjust the output format of SSE --- .../Mvc/SseAsyncEnumerableResult.cs | 33 +++++++++++++++++-- .../Mvc/SseAsyncEnumerableResultFilter.cs | 2 +- .../Microsoft/AspNetCore/Mvc/SseResult.cs | 13 ++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseResult.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResult.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResult.cs index f44e8f911..11eb46992 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResult.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResult.cs @@ -1,7 +1,12 @@ using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using System; using System.Collections.Generic; using System.Threading.Tasks; +using Volo.Abp.AspNetCore.ExceptionHandling; +using Volo.Abp.Http; +using Volo.Abp.Json; namespace Microsoft.AspNetCore.Mvc; public class SseAsyncEnumerableResult : IActionResult @@ -22,21 +27,45 @@ public class SseAsyncEnumerableResult : IActionResult try { + var jsonSerializer = context.HttpContext.RequestServices.GetRequiredService(); + await foreach (var content in _asyncEnumerable) { if (!string.IsNullOrEmpty(content)) { - await response.WriteAsync($"data: {content}\n\n"); + var sseResult = new SseResult(content); + await response.WriteAsync($"data: {jsonSerializer.Serialize(sseResult)}\n\n"); await response.Body.FlushAsync(); } } - await response.WriteAsync("data: [DONE]\n\n"); + await response.WriteAsync($"data: {jsonSerializer.Serialize(new SseResult("FINISHED"))}\n\n"); await response.Body.FlushAsync(); } catch (OperationCanceledException) { // ignore } + catch (Exception ex) + { + var exceptionHandlingOptions = context.HttpContext.RequestServices.GetRequiredService>().Value; + var exceptionToErrorInfoConverter = context.HttpContext.RequestServices.GetRequiredService(); + var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(ex, options => + { + options.SendExceptionsDetailsToClients = exceptionHandlingOptions.SendExceptionsDetailsToClients; + options.SendExceptionDataToClientTypes = exceptionHandlingOptions.SendExceptionDataToClientTypes; + options.SendStackTraceToClients = exceptionHandlingOptions.SendStackTraceToClients; + }); + + response.Headers.RemoveAll(x => x.Key == "Content-Type"); + response.Headers.Append("Content-Type", "application/json"); + response.Headers.Append(AbpHttpConsts.AbpErrorFormat, "true"); + response.StatusCode = (int)context.HttpContext.RequestServices + .GetRequiredService() + .GetStatusCode(context.HttpContext, ex); + + await response.WriteAsJsonAsync(remoteServiceErrorInfo); + await response.Body.FlushAsync(); + } } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResultFilter.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResultFilter.cs index 55d20014e..0c1f2c8e4 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResultFilter.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResultFilter.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; namespace Microsoft.AspNetCore.Mvc; public class SseAsyncEnumerableResultFilter : IAsyncActionFilter { - public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + public async virtual Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var executedContext = await next(); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseResult.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseResult.cs new file mode 100644 index 000000000..7225d6fc7 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseResult.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace Microsoft.AspNetCore.Mvc; + +public class SseResult +{ + [JsonPropertyName("m")] + public string Message { get; } + public SseResult(string message) + { + Message = message; + } +} From 82dd0c3e69fd898ba4723441eb320dae4f60b25c Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 13 Mar 2026 11:26:50 +0800 Subject: [PATCH 36/88] feat: Add the interface for available model providers --- .../LINGYUN/Abp/AI/ChatClientProvider.cs | 5 ++- .../LINGYUN/Abp/AI/IChatClientProvider.cs | 5 ++- .../AI/Internal/DeepSeekChatClientProvider.cs | 11 ++++++- .../AI/Internal/OpenAIChatClientProvider.cs | 15 ++++++++- .../LINGYUN/Abp/AI/Models/ChatModel.cs | 14 ++++++++ .../LINGYUN/Abp/AI/Models/Conversation.cs | 5 ++- .../Workspaces/Dtos/ChatClientProviderDto.cs | 12 +++++++ .../IWorkspaceDefinitionAppService.cs | 3 ++ .../WorkspaceDefinitionAppService.cs | 33 ++++++++++++++++--- .../WorkspaceDefinitionController.cs | 6 ++++ 10 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatModel.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/ChatClientProviderDto.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProvider.cs index c15b7875f..fb03f68fa 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/ChatClientProvider.cs @@ -1,4 +1,5 @@ -using LINGYUN.Abp.AI.Workspaces; +using LINGYUN.Abp.AI.Models; +using LINGYUN.Abp.AI.Workspaces; using Microsoft.Extensions.AI; using System; using System.Threading.Tasks; @@ -15,5 +16,7 @@ public abstract class ChatClientProvider : IChatClientProvider, ITransientDepend ServiceProvider = serviceProvider; } + public abstract ChatModel[] GetModels(); + public abstract Task CreateAsync(WorkspaceDefinition workspace); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs index bdf5aeeb7..a78f761b4 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/IChatClientProvider.cs @@ -1,4 +1,5 @@ -using LINGYUN.Abp.AI.Workspaces; +using LINGYUN.Abp.AI.Models; +using LINGYUN.Abp.AI.Workspaces; using Microsoft.Extensions.AI; using System.Threading.Tasks; @@ -7,5 +8,7 @@ public interface IChatClientProvider { string Name { get; } + ChatModel[] GetModels(); + Task CreateAsync(WorkspaceDefinition workspace); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/DeepSeekChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/DeepSeekChatClientProvider.cs index bc0863f33..d2af9d566 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/DeepSeekChatClientProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/DeepSeekChatClientProvider.cs @@ -1,4 +1,5 @@ -using System; +using LINGYUN.Abp.AI.Models; +using System; namespace LINGYUN.Abp.AI.Internal; public class DeepSeekChatClientProvider : OpenAIChatClientProvider @@ -12,4 +13,12 @@ public class DeepSeekChatClientProvider : OpenAIChatClientProvider : base(serviceProvider) { } + + public override ChatModel[] GetModels() + { + return [ + new ChatModel("deepseek-chat", "DeepSeek-V3", "DeepSeek-Chat是全能高效的“快枪手”,擅长日常对话与通用任务"), + new ChatModel("deepseek-reasoner", "DeepSeek-R1", "DeepSeek-Reasoner是深思熟虑的“解题家”,专攻复杂推理与逻辑难题"), + ]; + } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIChatClientProvider.cs index 6f2ab1234..7e3bd4047 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIChatClientProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIChatClientProvider.cs @@ -1,4 +1,5 @@ -using LINGYUN.Abp.AI.Workspaces; +using LINGYUN.Abp.AI.Models; +using LINGYUN.Abp.AI.Workspaces; using Microsoft.Extensions.AI; using OpenAI; using System; @@ -18,6 +19,18 @@ public class OpenAIChatClientProvider : ChatClientProvider { } + public override ChatModel[] GetModels() + { + return [ + new ChatModel("gpt-4.1", "GPT-4.1", "Smartest non-reasoning model"), + new ChatModel("gpt-5", "GPT-5", "Previous intelligent reasoning model for coding and agentic tasks with configurable reasoning effort"), + new ChatModel("gpt-5-nano", "GPT-5 nano", "Fastest, most cost-efficient version of GPT-5"), + new ChatModel("gpt-5-mini", "GPT-5 mini", "Near-frontier intelligence for cost sensitive, low latency, high volume workloads"), + new ChatModel("gpt-5.4", "GPT-5.4", "Best intelligence at scale for agentic, coding, and professional workflows"), + new ChatModel("gpt-5.4-pro", "GPT-5.4 pro", "Version of GPT-5.4 that produces smarter and more precise responses."), + ]; + } + public override Task CreateAsync(WorkspaceDefinition workspace) { Check.NotNull(workspace, nameof(workspace)); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatModel.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatModel.cs new file mode 100644 index 000000000..baa5a1d04 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatModel.cs @@ -0,0 +1,14 @@ +namespace LINGYUN.Abp.AI.Models; + +public class ChatModel +{ + public string Id { get; } + public string Name { get; } + public string? Description { get; } + public ChatModel(string id, string name, string? description = null) + { + Id = id; + Name = name; + Description = description; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/Conversation.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/Conversation.cs index 52943a7a1..e2752a3e8 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/Conversation.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/Conversation.cs @@ -5,18 +5,21 @@ public class Conversation { public Guid Id { get; private set; } public string Name { get; private set; } + public string Workspace { get; private set; } public DateTime CreatedAt { get; private set; } public DateTime? ExpiredAt { get; set; } public DateTime? UpdateAt { get; set; } public Conversation( Guid id, - string name, + string name, + string workspace, DateTime createdAt) { Id = id; Name = name; CreatedAt = createdAt; UpdateAt = createdAt; + Workspace = workspace; } public Conversation WithName(string name) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/ChatClientProviderDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/ChatClientProviderDto.cs new file mode 100644 index 000000000..bbd2ea7e4 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/ChatClientProviderDto.cs @@ -0,0 +1,12 @@ +namespace LINGYUN.Abp.AIManagement.Workspaces.Dtos; + +public class ChatClientProviderDto +{ + public string Name { get; } + public string[] Models { get; } + public ChatClientProviderDto(string name, string[] models) + { + Name = name; + Models = models; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionAppService.cs index 6ebb52c95..7f827538e 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionAppService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionAppService.cs @@ -1,5 +1,7 @@ using LINGYUN.Abp.AIManagement.Workspaces.Dtos; using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; namespace LINGYUN.Abp.AIManagement.Workspaces; @@ -11,4 +13,5 @@ public interface IWorkspaceDefinitionAppService : WorkspaceDefinitionRecordCreateDto, WorkspaceDefinitionRecordUpdateDto> { + Task> GetAvailableProvidersAsync(); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs index e249d025b..696b331b9 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs @@ -1,8 +1,13 @@ -using LINGYUN.Abp.AIManagement.Localization; +using LINGYUN.Abp.AI; +using LINGYUN.Abp.AIManagement.Localization; using LINGYUN.Abp.AIManagement.Workspaces.Dtos; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using System; +using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; using Volo.Abp.Data; using Volo.Abp.Security.Encryption; @@ -18,12 +23,15 @@ public class WorkspaceDefinitionAppService : WorkspaceDefinitionRecordUpdateDto>, IWorkspaceDefinitionAppService { + protected AbpAICoreOptions AIOptions { get; } protected IStringEncryptionService StringEncryptionService { get; } protected IWorkspaceDefinitionRecordRepository WorkspaceDefinitionRecordRepository { get; } public WorkspaceDefinitionAppService( + IOptions aiOptions, IStringEncryptionService stringEncryptionService, IWorkspaceDefinitionRecordRepository repository) : base(repository) { + AIOptions = aiOptions.Value; StringEncryptionService = stringEncryptionService; WorkspaceDefinitionRecordRepository = repository; @@ -31,6 +39,23 @@ public class WorkspaceDefinitionAppService : ObjectMapperContext = typeof(AbpAIManagementApplicationModule); } + public virtual Task> GetAvailableProvidersAsync() + { + var providers = AIOptions.ChatClientProviders + .Select(LazyServiceProvider.GetRequiredService) + .OfType() + .Select(provider => + { + var models = provider.GetModels(); + + return new ChatClientProviderDto( + provider.Name, + models.Select(model => model.Id).ToArray()); + }); + + return Task.FromResult(new ListResultDto(providers.ToImmutableArray())); + } + protected async override Task> CreateFilteredQueryAsync(WorkspaceDefinitionRecordGetListInput input) { var queryable = await base.CreateFilteredQueryAsync(input); @@ -40,9 +65,9 @@ public class WorkspaceDefinitionAppService : .WhereIf(!input.ModelName.IsNullOrWhiteSpace(), x => x.ModelName == input.ModelName) .WhereIf(!input.Filter.IsNullOrWhiteSpace(), x => x.Provider.Contains(input.Filter!) || x.ModelName.Contains(input.Filter!) || x.DisplayName.Contains(input.Filter!) || - (!x.Description.IsNullOrWhiteSpace() && x.Description.Contains(input.Filter!)) || - (!x.SystemPrompt.IsNullOrWhiteSpace() && x.SystemPrompt.Contains(input.Filter!)) || - (!x.Instructions.IsNullOrWhiteSpace() && x.Instructions.Contains(input.Filter!))); + x.Description!.Contains(input.Filter!) || + x.SystemPrompt!.Contains(input.Filter!) || + x.Instructions!.Contains(input.Filter!)); } protected async override Task MapToEntityAsync(WorkspaceDefinitionRecordCreateDto createInput) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionController.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionController.cs index 58444f420..0e7446fa7 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionController.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionController.cs @@ -20,6 +20,12 @@ public class WorkspaceDefinitionController : AbpControllerBase, IWorkspaceDefini _service = service; } + [HttpGet("available-providers")] + public virtual Task> GetAvailableProvidersAsync() + { + return _service.GetAvailableProvidersAsync(); + } + [HttpPost] public virtual Task CreateAsync(WorkspaceDefinitionRecordCreateDto input) { From 19d8cb26941a1bfbe98f9c5f1f1eee57a22eb9b7 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 13 Mar 2026 11:27:47 +0800 Subject: [PATCH 37/88] fix: Check if the workspace is enabled --- .../LINGYUN/Abp/AI/Agent/AgentFactory.cs | 19 ------------------- .../Abp/AI/Internal/ChatClientFactory.cs | 2 +- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs index 95b93584f..0a89027e3 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs @@ -5,7 +5,6 @@ using Microsoft.Extensions.Localization; using System; using System.Threading.Tasks; using Volo.Abp.AI; -using Volo.Abp.Authorization; using Volo.Abp.DependencyInjection; using Volo.Abp.SimpleStateChecking; @@ -39,11 +38,6 @@ public class AgentFactory : IAgentFactory, IScopedDependency var workspaceDefine = await WorkspaceDefinitionManager.GetOrNullAsync(workspace); - if (workspaceDefine != null) - { - await CheckWorkspaceStateAsync(workspaceDefine); - } - return await CreateAgentAsync(chatClient, workspaceDefine); } @@ -51,8 +45,6 @@ public class AgentFactory : IAgentFactory, IScopedDependency { var workspaceDefine = await WorkspaceDefinitionManager.GetAsync(workspace); - await CheckWorkspaceStateAsync(workspaceDefine); - var chatClient = await ChatClientFactory.CreateAsync(workspace); return await CreateAgentAsync(chatClient, workspaceDefine); @@ -96,15 +88,4 @@ public class AgentFactory : IAgentFactory, IScopedDependency { return Task.FromResult([]); } - - protected async virtual Task CheckWorkspaceStateAsync(WorkspaceDefinition workspace) - { - if (!await StateCheckerManager.IsEnabledAsync(workspace)) - { - throw new AbpAuthorizationException( - $"Workspace is not enabled: {workspace.Name}!", - AbpAIErrorCodes.WorkspaceIsNotEnabled) - .WithData("Workspace", workspace.Name); - } - } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/ChatClientFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/ChatClientFactory.cs index a369c05e0..8df8f777f 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/ChatClientFactory.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/ChatClientFactory.cs @@ -70,7 +70,7 @@ public class ChatClientFactory : IChatClientFactory, IScopedDependency protected async virtual Task CheckWorkspaceStateAsync(WorkspaceDefinition workspace) { - if (!await StateCheckerManager.IsEnabledAsync(workspace)) + if (!workspace.IsEnabled || !await StateCheckerManager.IsEnabledAsync(workspace)) { throw new AbpAuthorizationException( $"Workspace is not enabled: {workspace.Name}!", From 892650ab4f8be112d56e559c4a08c8af141982bb Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 13 Mar 2026 11:29:19 +0800 Subject: [PATCH 38/88] feat: Add a query interface for conversation history messages --- .../AIManagement/Chats/Dtos/ChatMessageDto.cs | 6 +++++ .../Chats/Dtos/SendTextChatMessageDto.cs | 6 ++--- .../Chats/Dtos/TextChatMessageDto.cs | 9 +++---- .../Chats/Dtos/TextChatMessageGetListInput.cs | 11 ++++++++ .../Abp/AIManagement/Chats/IChatAppService.cs | 4 +++ .../AbpAIManagementApplicationMappers.cs | 21 +++++++++++++++- .../Abp/AIManagement/Chats/ChatAppService.cs | 24 ++++++++++++++++-- .../Chats/ITextChatMessageRecordRepository.cs | 12 +++++++++ .../EfCoreTextChatMessageRecordRepository.cs | 25 +++++++++++++++++++ .../Abp/AIManagement/Chats/ChatController.cs | 8 ++++++ 10 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/TextChatMessageGetListInput.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ChatMessageDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ChatMessageDto.cs index 035fc1464..02e161331 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ChatMessageDto.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/ChatMessageDto.cs @@ -8,7 +8,13 @@ public abstract class ChatMessageDto : ExtensibleAuditedEntityDto public DateTime CreatedAt { get; set; } + public string Role { get; set; } + public Guid? UserId { get; set; } public Guid? ConversationId { get; set; } + + public string? ReplyMessage { get; set; } + + public DateTime? ReplyAt { get; set; } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/SendTextChatMessageDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/SendTextChatMessageDto.cs index ad0372260..a36641a54 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/SendTextChatMessageDto.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/SendTextChatMessageDto.cs @@ -7,11 +7,11 @@ namespace LINGYUN.Abp.AIManagement.Chats.Dtos; public class SendTextChatMessageDto { [Required] - [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxNameLength))] - public string Workspace { get; set; } + public Guid ConversationId { get; set; } [Required] - public Guid ConversationId { get; set; } + [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxNameLength))] + public string Workspace { get; set; } [Required] [DynamicStringLength(typeof(TextChatMessageRecordConsts), nameof(TextChatMessageRecordConsts.MaxContentLength))] diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/TextChatMessageDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/TextChatMessageDto.cs index 1ef328229..1709662c2 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/TextChatMessageDto.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/TextChatMessageDto.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace LINGYUN.Abp.AIManagement.Chats.Dtos; -public class TextChatMessageDto +namespace LINGYUN.Abp.AIManagement.Chats.Dtos; +public class TextChatMessageDto : ChatMessageDto { + public string Content { get; set; } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/TextChatMessageGetListInput.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/TextChatMessageGetListInput.cs new file mode 100644 index 000000000..34ed81515 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/Dtos/TextChatMessageGetListInput.cs @@ -0,0 +1,11 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Application.Dtos; + +namespace LINGYUN.Abp.AIManagement.Chats.Dtos; + +public class TextChatMessageGetListInput : PagedAndSortedResultRequestDto +{ + [Required] + public Guid ConversationId { get; set; } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/IChatAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/IChatAppService.cs index dcfaa06ca..c3850a9d8 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/IChatAppService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Chats/IChatAppService.cs @@ -1,9 +1,13 @@ using LINGYUN.Abp.AIManagement.Chats.Dtos; using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; namespace LINGYUN.Abp.AIManagement.Chats; public interface IChatAppService : IApplicationService { IAsyncEnumerable SendMessageAsync(SendTextChatMessageDto input); + + Task> GetListAsync(TextChatMessageGetListInput input); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationMappers.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationMappers.cs index adc71e599..6f6935e13 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationMappers.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationMappers.cs @@ -1,4 +1,6 @@ -using LINGYUN.Abp.AIManagement.Workspaces; +using LINGYUN.Abp.AIManagement.Chats; +using LINGYUN.Abp.AIManagement.Chats.Dtos; +using LINGYUN.Abp.AIManagement.Workspaces; using LINGYUN.Abp.AIManagement.Workspaces.Dtos; using Riok.Mapperly.Abstractions; using Volo.Abp.Mapperly; @@ -12,4 +14,21 @@ public partial class WorkspaceDefinitionRecordToWorkspaceDefinitionRecordDtoMapp { public override partial WorkspaceDefinitionRecordDto Map(WorkspaceDefinitionRecord source); public override partial void Map(WorkspaceDefinitionRecord source, WorkspaceDefinitionRecordDto destination); +} + +[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] +[MapExtraProperties(DefinitionChecks = MappingPropertyDefinitionChecks.None)] +public partial class TextChatMessageRecordToTextChatMessageDtoMapper : MapperBase +{ + [MapPropertyFromSource(nameof(TextChatMessageDto.Role), Use = nameof(ConvertChatRole))] + public override partial TextChatMessageDto Map(TextChatMessageRecord source); + + [MapPropertyFromSource(nameof(TextChatMessageDto.Role), Use = nameof(ConvertChatRole))] + public override partial void Map(TextChatMessageRecord source, TextChatMessageDto destination); + + [UserMapping(Default = false)] + private static string ConvertChatRole(TextChatMessageRecord record) + { + return record.Role.Value; + } } \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ChatAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ChatAppService.cs index fe420a66a..b7863f15a 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ChatAppService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ChatAppService.cs @@ -3,17 +3,24 @@ using LINGYUN.Abp.AI.Models; using LINGYUN.Abp.AIManagement.Chats.Dtos; using Microsoft.Extensions.AI; using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Specifications; namespace LINGYUN.Abp.AIManagement.Chats; public class ChatAppService : AIManagementApplicationServiceBase, IChatAppService { private readonly IAgentService _agentService; - public ChatAppService(IAgentService agentService) + private readonly ITextChatMessageRecordRepository _repository; + public ChatAppService( + IAgentService agentService, + ITextChatMessageRecordRepository repository) { _agentService = agentService; + _repository = repository; } - public IAsyncEnumerable SendMessageAsync(SendTextChatMessageDto input) + public virtual IAsyncEnumerable SendMessageAsync(SendTextChatMessageDto input) { var chatMessage = new TextChatMessage( input.Workspace, @@ -25,4 +32,17 @@ public class ChatAppService : AIManagementApplicationServiceBase, IChatAppServic return _agentService.SendMessageAsync(chatMessage); } + + public async virtual Task> GetListAsync(TextChatMessageGetListInput input) + { + var specification = new ExpressionSpecification( + x => x.ConversationId == input.ConversationId); + + var totalCount = await _repository.GetCountAsync(specification); + var textChatMessages = await _repository.GetListAsync(specification, + input.Sorting, input.MaxResultCount, input.SkipCount); + + return new PagedResultDto(totalCount, + ObjectMapper.Map, List>(textChatMessages)); + } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ITextChatMessageRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ITextChatMessageRecordRepository.cs index de20f6e27..ed074ebba 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ITextChatMessageRecordRepository.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ITextChatMessageRecordRepository.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; +using Volo.Abp.Specifications; namespace LINGYUN.Abp.AIManagement.Chats; public interface ITextChatMessageRecordRepository : IBasicRepository @@ -11,4 +12,15 @@ public interface ITextChatMessageRecordRepository : IBasicRepository GetCountAsync( + ISpecification specification, + CancellationToken cancellationToken = default); + + Task> GetListAsync( + ISpecification specification, + string? sorting = $"{nameof(TextChatMessageRecord.CreatedAt)}", + int maxResultCount = 10, + int skipCount = 0, + CancellationToken cancellationToken = default); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTextChatMessageRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTextChatMessageRecordRepository.cs index d5d075bd4..e2b9c24f1 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTextChatMessageRecordRepository.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTextChatMessageRecordRepository.cs @@ -3,10 +3,12 @@ using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Dynamic.Core; using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Specifications; namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; public class EfCoreTextChatMessageRecordRepository : EfCoreRepository, ITextChatMessageRecordRepository @@ -29,4 +31,27 @@ public class EfCoreTextChatMessageRecordRepository : EfCoreRepository x.CreationTime) .ToListAsync(GetCancellationToken(cancellationToken)); } + + public async virtual Task GetCountAsync( + ISpecification specification, + CancellationToken cancellationToken = default) + { + return await (await GetQueryableAsync()) + .Where(specification.ToExpression()) + .CountAsync(GetCancellationToken(cancellationToken)); + } + + public async virtual Task> GetListAsync( + ISpecification specification, + string? sorting = $"{nameof(TextChatMessageRecord.CreatedAt)}", + int maxResultCount = 10, + int skipCount = 0, + CancellationToken cancellationToken = default) + { + return await (await GetQueryableAsync()) + .Where(specification.ToExpression()) + .OrderBy(!sorting.IsNullOrWhiteSpace() ? sorting : $"{nameof(TextChatMessageRecord.CreatedAt)}") + .PageBy(skipCount, maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ChatController.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ChatController.cs index 811a29c56..2ed4e67bb 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ChatController.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ChatController.cs @@ -1,7 +1,9 @@ using LINGYUN.Abp.AIManagement.Chats.Dtos; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; +using System.Threading.Tasks; using Volo.Abp; +using Volo.Abp.Application.Dtos; using Volo.Abp.AspNetCore.Mvc; namespace LINGYUN.Abp.AIManagement.Chats; @@ -27,4 +29,10 @@ public class ChatController : AbpControllerBase, IChatAppService yield return content; } } + + [HttpGet] + public virtual Task> GetListAsync(TextChatMessageGetListInput input) + { + return _service.GetListAsync(input); + } } From 37202496303f3872d3b363145404b84bd7754519 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 13 Mar 2026 11:30:13 +0800 Subject: [PATCH 39/88] feat: Improve the localizing documentation --- .../AIManagement/AIManagementErrorCodes.cs | 2 +- .../Localization/Resources/en.json | 35 ++++++++++++++++++- .../Localization/Resources/zh-Hans.json | 35 ++++++++++++++++++- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AIManagementErrorCodes.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AIManagementErrorCodes.cs index ae295d8db..253c9467d 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AIManagementErrorCodes.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AIManagementErrorCodes.cs @@ -5,7 +5,7 @@ public static class AIManagementErrorCodes public static class Workspace { - public const string Prefix = Namespace + ":100"; + public const string Prefix = Namespace + ":111"; public const string NameAlreadyExists = Prefix + "001"; } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json index d29ec98c1..dbec885cd 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json @@ -1,7 +1,40 @@ { "culture": "en", "texts": { + "AIManagement:111001": "Workspace {Workspace} already exists!", "DisplayName:MaxLatestHistoryMessagesToKeep": "Carry the recent conversation records", - "Description:MaxLatestHistoryMessagesToKeep": "When a user initiates a conversation with a large model, the upper limit of the user's recent conversation history that is carried along." + "Description:MaxLatestHistoryMessagesToKeep": "When a user initiates a conversation with a large model, the upper limit of the user's recent conversation history that is carried along.", + "Conversations:Delete": "Delete Conversation", + "Conversations:New": "New Conversation", + "Conversations:Edit": "Edit Conversation", + "ConversationsDeleteWarnMessage": "The selected conversation will be deleted. The deleted conversation records cannot be restored.!", + "ConversationsExpiredWarnMessage": "The conversation has expired. Please create a new one.!", + "ChatWelcomeTitle": "Hello, what can I do for you today?", + "ChatWelcomeMessage": "Send your question to start a new AI conversation.", + "ChatSendMessage": "Please click the button or press the Enter key to send the message.", + "Workspaces": "Workspaces", + "Workspaces:New": "New Workspace", + "Workspaces:Edit": "Edit Workspace", + "BasicInfo": "Basic Infomation", + "ModelInfo": "Model", + "DisplayName:Name": "Name", + "DisplayName:Provider": "Provider", + "DisplayName:ModelName": "Model Name", + "DisplayName:ApiBaseUrl": "Api Base Url", + "DisplayName:ApiKey": "Api Key", + "DisplayName:DisplayName": "Display Name", + "DisplayName:Description": "Description", + "DisplayName:SystemPrompt": "System Prompt", + "DisplayName:Instructions": "Instructions", + "DisplayName:Temperature": "Temperature", + "Description:Temperature": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.", + "DisplayName:MaxOutputTokens": "Max Output Tokens", + "Description:MaxOutputTokens": "The maximum number of tokens that can be generated in the chat completion.The total length of input tokens and generated tokens is limited by the model's context length.", + "DisplayName:FrequencyPenalty": "Frequency Penalty", + "Description:FrequencyPenalty": "Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.", + "DisplayName:PresencePenalty": "Presence Penalty", + "Description:PresencePenalty": "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.", + "DisplayName:IsEnabled": "Is Enabled", + "DisplayName:Workspace": "Workspace" } } \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json index 6eb63499b..d749e4ec7 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json @@ -1,7 +1,40 @@ { "culture": "zh-Hans", "texts": { + "AIManagement:111001": "工作区 {Workspace} 已存在!", "DisplayName:MaxLatestHistoryMessagesToKeep": "携带最近对话记录", - "Description:MaxLatestHistoryMessagesToKeep": "用户发起与大模型对话时,携带用户最近对话记录的上限." + "Description:MaxLatestHistoryMessagesToKeep": "用户发起与大模型对话时,携带用户最近对话记录的上限.", + "Conversations:Delete": "删除对话", + "Conversations:New": "新对话", + "Conversations:Edit": "编辑对话", + "ConversationsDeleteWarnMessage": "选择的对话将被删除, 已删除的对话记录无法恢复!", + "ConversationsExpiredWarnMessage": "对话已过期, 请重新创建新对话!", + "ChatWelcomeTitle": "您好,今天有什么可以帮到你?", + "ChatWelcomeMessage": "发送您的问题创建一个新AI对话.", + "ChatSendMessage": "请输入消息点击按钮或回车键发送消息.", + "Workspaces": "工作区", + "Workspaces:New": "新工作区", + "Workspaces:Edit": "编辑工作区", + "BasicInfo": "基本信息", + "ModelInfo": "模型", + "DisplayName:Name": "名称", + "DisplayName:Provider": "模型提供者", + "DisplayName:ModelName": "模型名称", + "DisplayName:ApiBaseUrl": "接口地址", + "DisplayName:ApiKey": "Api Key", + "DisplayName:DisplayName": "显示名称", + "DisplayName:Description": "描述", + "DisplayName:SystemPrompt": "系统提示词", + "DisplayName:Instructions": "补充提示", + "DisplayName:Temperature": "温度", + "Description:Temperature": "采样温度,介于 0 和 2 之间.更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定.", + "DisplayName:MaxOutputTokens": "Token使用数量", + "Description:MaxOutputTokens": "限制一次请求中模型生成 completion 的最大 token 数,输入 token 和输出 token 的总长度受模型的上下文长度的限制.", + "DisplayName:FrequencyPenalty": "频率惩罚", + "Description:FrequencyPenalty": "介于 -2.0 和 2.0 之间的数字.如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性.", + "DisplayName:PresencePenalty": "存在惩罚", + "Description:PresencePenalty": "介于 -2.0 和 2.0 之间的数字.如果该值为正,那么新 token 会根据其是否已在已有文本中出现受到相应的惩罚,从而增加模型谈论新主题的可能性.", + "DisplayName:IsEnabled": "启用", + "DisplayName:Workspace": "工作区" } } \ No newline at end of file From bac6500c9c825660ee1872d1efa493448be22025 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 13 Mar 2026 15:30:08 +0800 Subject: [PATCH 40/88] feat: Add system workspace deletion check --- ...d-IsSystem-To-Workspace-Record.Designer.cs | 311 ++++++++++++++++++ ...034606_Add-IsSystem-To-Workspace-Record.cs | 29 ++ ...ServiceMigrationsDbContextModelSnapshot.cs | 3 + .../Dtos/WorkspaceDefinitionRecordDto.cs | 2 + .../WorkspaceDefinitionAppService.cs | 16 + .../AIManagement/AIManagementErrorCodes.cs | 7 + .../AbpAIManagementDomainSharedModule.cs | 6 + .../Localization/Resources/en.json | 1 + .../Localization/Resources/zh-Hans.json | 1 + .../Workspaces/WorkspaceDefinitionRecord.cs | 2 + .../WorkspaceDefinitionSerializer.cs | 3 + 11 files changed, 381 insertions(+) create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313034606_Add-IsSystem-To-Workspace-Record.Designer.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313034606_Add-IsSystem-To-Workspace-Record.cs diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313034606_Add-IsSystem-To-Workspace-Record.Designer.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313034606_Add-IsSystem-To-Workspace-Record.Designer.cs new file mode 100644 index 000000000..3f1247abe --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313034606_Add-IsSystem-To-Workspace-Record.Designer.cs @@ -0,0 +1,311 @@ +// +using System; +using LINGYUN.Abp.MicroService.AIService; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LINGYUN.Abp.MicroService.AIService.Migrations +{ + [DbContext(typeof(AIServiceMigrationsDbContext))] + [Migration("20260313034606_Add-IsSystem-To-Workspace-Record")] + partial class AddIsSystemToWorkspaceRecord + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.ConversationRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UpdateAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.ToTable("AbpAIConversations", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.TextChatMessageRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ReplyAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReplyMessage") + .HasColumnType("text"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITextChatMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tokens.TokenUsageRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CachedInputTokenCount") + .HasColumnType("bigint"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("InputTokenCount") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MessageId") + .HasColumnType("uuid"); + + b.Property("OutputTokenCount") + .HasColumnType("bigint"); + + b.Property("ReasoningTokenCount") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TotalTokenCount") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITokenUsages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiBaseUrl") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ApiKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrequencyPenalty") + .HasColumnType("real"); + + b.Property("Instructions") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("IsSystem") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MaxOutputTokens") + .HasColumnType("integer"); + + b.Property("ModelName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PresencePenalty") + .HasColumnType("real"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SystemPrompt") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Temperature") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpAIWorkspaceDefinitions", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313034606_Add-IsSystem-To-Workspace-Record.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313034606_Add-IsSystem-To-Workspace-Record.cs new file mode 100644 index 000000000..eb0bc436c --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260313034606_Add-IsSystem-To-Workspace-Record.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LINGYUN.Abp.MicroService.AIService.Migrations +{ + /// + public partial class AddIsSystemToWorkspaceRecord : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsSystem", + table: "AbpAIWorkspaceDefinitions", + type: "boolean", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsSystem", + table: "AbpAIWorkspaceDefinitions"); + } + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs index 9cc821579..734232117 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs @@ -252,6 +252,9 @@ namespace LINGYUN.Abp.MicroService.AIService.Migrations b.Property("IsEnabled") .HasColumnType("boolean"); + b.Property("IsSystem") + .HasColumnType("boolean"); + b.Property("LastModificationTime") .HasColumnType("timestamp with time zone") .HasColumnName("LastModificationTime"); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordDto.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordDto.cs index f65dd0ee9..07cc111c7 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordDto.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordDto.cs @@ -33,6 +33,8 @@ public class WorkspaceDefinitionRecordDto : ExtensibleAuditedEntityDto, IH public bool IsEnabled { get; set; } + public bool IsSystem { get; set; } + public string? StateCheckers { get; set; } public string ConcurrencyStamp { get; set; } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs index 696b331b9..38f7a016a 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; +using Volo.Abp; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; using Volo.Abp.Data; @@ -56,6 +57,21 @@ public class WorkspaceDefinitionAppService : return Task.FromResult(new ListResultDto(providers.ToImmutableArray())); } + protected async override Task DeleteByIdAsync(Guid id) + { + var workspace = await Repository.GetAsync(id); + + if (workspace.IsSystem) + { + throw new BusinessException( + AIManagementErrorCodes.Workspace.SystemWorkspaceNotAllowedToBeDeleted, + $"System workspace {workspace.Name} is not allowed to be deleted!") + .WithData("Workspace", workspace.Name); + } + + await Repository.DeleteAsync(workspace); + } + protected async override Task> CreateFilteredQueryAsync(WorkspaceDefinitionRecordGetListInput input) { var queryable = await base.CreateFilteredQueryAsync(input); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AIManagementErrorCodes.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AIManagementErrorCodes.cs index 253c9467d..bdd94b6cd 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AIManagementErrorCodes.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AIManagementErrorCodes.cs @@ -7,6 +7,13 @@ public static class AIManagementErrorCodes { public const string Prefix = Namespace + ":111"; + /// + /// Workspace {Workspace} already exists! + /// public const string NameAlreadyExists = Prefix + "001"; + /// + /// System workspace {Workspace} is not allowed to be deleted! + /// + public const string SystemWorkspaceNotAllowedToBeDeleted = Prefix + "002"; } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs index 5c0168a94..b6d31ceb0 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AbpAIManagementDomainSharedModule.cs @@ -1,6 +1,7 @@ using LINGYUN.Abp.AIManagement.Localization; using Volo.Abp.Domain; using Volo.Abp.Localization; +using Volo.Abp.Localization.ExceptionHandling; using Volo.Abp.Modularity; using Volo.Abp.VirtualFileSystem; @@ -21,5 +22,10 @@ public class AbpAIManagementDomainSharedModule : AbpModule options.Resources.Add() .AddVirtualJson("/LINGYUN/Abp/AIManagement/Localization/Resources"); }); + + Configure(options => + { + options.MapCodeNamespace(AIManagementErrorCodes.Namespace, typeof(AIManagementResource)); + }); } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json index dbec885cd..ccdec0db2 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json @@ -2,6 +2,7 @@ "culture": "en", "texts": { "AIManagement:111001": "Workspace {Workspace} already exists!", + "AIManagement:111002": "System workspace {Workspace} is not allowed to be deleted!", "DisplayName:MaxLatestHistoryMessagesToKeep": "Carry the recent conversation records", "Description:MaxLatestHistoryMessagesToKeep": "When a user initiates a conversation with a large model, the upper limit of the user's recent conversation history that is carried along.", "Conversations:Delete": "Delete Conversation", diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json index d749e4ec7..dfe1f5e75 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json @@ -2,6 +2,7 @@ "culture": "zh-Hans", "texts": { "AIManagement:111001": "工作区 {Workspace} 已存在!", + "AIManagement:111002": "系统工作区 {Workspace} 不允许删除!", "DisplayName:MaxLatestHistoryMessagesToKeep": "携带最近对话记录", "Description:MaxLatestHistoryMessagesToKeep": "用户发起与大模型对话时,携带用户最近对话记录的上限.", "Conversations:Delete": "删除对话", diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecord.cs index e93500b17..487256815 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecord.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecord.cs @@ -34,6 +34,8 @@ public class WorkspaceDefinitionRecord : AuditedAggregateRoot public bool IsEnabled { get; set; } + public bool IsSystem { get; set; } + public string? StateCheckers { get; set; } protected WorkspaceDefinitionRecord() diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs index 94096f797..ec8ad7403 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs @@ -71,6 +71,9 @@ public class WorkspaceDefinitionSerializer : IWorkspaceDefinitionSerializer, ITr workspace.SetProperty(property.Key, property.Value); } + workspace.IsEnabled = definition.IsEnabled; + workspace.IsSystem = true; + return Task.FromResult(workspace); } } From b924144343a39dd264fb085c1cc20506632efc2d Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 13 Mar 2026 15:40:10 +0800 Subject: [PATCH 41/88] fix: Add SSE error log recording --- .../AspNetCore/Mvc/SseAsyncEnumerableResult.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResult.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResult.cs index 11eb46992..e5707ccc0 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResult.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/Microsoft/AspNetCore/Mvc/SseAsyncEnumerableResult.cs @@ -1,8 +1,10 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; using System.Collections.Generic; +using System.Text; using System.Threading.Tasks; using Volo.Abp.AspNetCore.ExceptionHandling; using Volo.Abp.Http; @@ -49,6 +51,7 @@ public class SseAsyncEnumerableResult : IActionResult catch (Exception ex) { var exceptionHandlingOptions = context.HttpContext.RequestServices.GetRequiredService>().Value; + var exceptionToErrorInfoConverter = context.HttpContext.RequestServices.GetRequiredService(); var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(ex, options => { @@ -57,6 +60,14 @@ public class SseAsyncEnumerableResult : IActionResult options.SendStackTraceToClients = exceptionHandlingOptions.SendStackTraceToClients; }); + if (exceptionHandlingOptions.ShouldLogException(ex)) + { + var logger = context.HttpContext.RequestServices.GetService>(); + var logLevel = ex.GetLogLevel(); + logger?.LogException(ex, logLevel); + } + + response.Headers.RemoveAll(x => x.Key == "Content-Type"); response.Headers.Append("Content-Type", "application/json"); response.Headers.Append(AbpHttpConsts.AbpErrorFormat, "true"); From 08744952413ab460e12ca55c0bb0beadfaf390ef Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 13 Mar 2026 16:05:12 +0800 Subject: [PATCH 42/88] fix: Fix context assistant reply loss --- .../LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs index 046d714ce..63b090f4c 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs @@ -90,6 +90,10 @@ public class AgentService : IAgentService, IScopedDependency foreach (var chatMessage in historyMessages) { messages.Add(new AIChatMessage(chatMessage.Role, chatMessage.GetMessagePrompt())); + if (!chatMessage.ReplyMessage.IsNullOrWhiteSpace()) + { + messages.Add(new AIChatMessage(ChatRole.Assistant, chatMessage.ReplyMessage)); + } } } From 6df7d43636879dc2665351fc10c265bcf2ac5576 Mon Sep 17 00:00:00 2001 From: colin Date: Sat, 14 Mar 2026 09:04:55 +0800 Subject: [PATCH 43/88] feat: Increase dialogue grouping multilingual support. --- .../LINGYUN/Abp/AIManagement/Localization/Resources/en.json | 4 ++++ .../Abp/AIManagement/Localization/Resources/zh-Hans.json | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json index ccdec0db2..b55671f5b 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json @@ -18,6 +18,10 @@ "Workspaces:Edit": "Edit Workspace", "BasicInfo": "Basic Infomation", "ModelInfo": "Model", + "Group:Today": "Today", + "Group:Yesterday": "Yesterday", + "Group:Week": "Week", + "Group:Month": "Month", "DisplayName:Name": "Name", "DisplayName:Provider": "Provider", "DisplayName:ModelName": "Model Name", diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json index dfe1f5e75..021c57d77 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json @@ -18,6 +18,10 @@ "Workspaces:Edit": "编辑工作区", "BasicInfo": "基本信息", "ModelInfo": "模型", + "Group:Today": "今天", + "Group:Yesterday": "昨天", + "Group:Week": "7天内", + "Group:Month": "30天内", "DisplayName:Name": "名称", "DisplayName:Provider": "模型提供者", "DisplayName:ModelName": "模型名称", From 409154366bbc1fe23aecda41d63f5908fc4942ac Mon Sep 17 00:00:00 2001 From: colin Date: Sat, 14 Mar 2026 14:43:09 +0800 Subject: [PATCH 44/88] feat: Adding the API key without proper verification --- .../LINGYUN/Abp/AI/Agent/AgentService.cs | 40 ++++++++++++++++--- ...AsyncEnumerableErrorHandlingExtenssions.cs | 35 ++++++++++++++++ .../LINGYUN/Abp/AI/AbpAIErrorCodes.cs | 4 ++ .../Abp/AI/Localization/Resources/en.json | 1 + .../AI/Localization/Resources/zh-Hans.json | 1 + 5 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/System/Collections/Generic/IAsyncEnumerableErrorHandlingExtenssions.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs index 63b090f4c..20508d83e 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs @@ -2,10 +2,14 @@ using LINGYUN.Abp.AI.Localization; using LINGYUN.Abp.AI.Models; using LINGYUN.Abp.AI.Tokens; +using LINGYUN.Abp.AI.Workspaces; using Microsoft.Agents.AI; using Microsoft.Extensions.AI; using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using System; +using System.ClientModel; using System.Collections.Generic; using System.Linq; using System.Text; @@ -19,6 +23,8 @@ using AIChatMessage = Microsoft.Extensions.AI.ChatMessage; namespace LINGYUN.Abp.AI.Agent; public class AgentService : IAgentService, IScopedDependency { + public ILogger Logger { protected get; set; } + private readonly IClock _clock; private readonly IGuidGenerator _guidGenerator; private readonly IAgentFactory _agentFactory; @@ -42,6 +48,8 @@ public class AgentService : IAgentService, IScopedDependency _chatMessageStore = chatMessageStore; _conversationStore = conversationStore; _localizerResource = localizerResource; + + Logger = NullLogger.Instance; } public async virtual IAsyncEnumerable SendMessageAsync(Models.ChatMessage message) @@ -52,9 +60,29 @@ public class AgentService : IAgentService, IScopedDependency var messages = await BuildChatMessages(message); - var agent = await _agentFactory.CreateAsync(message.Workspace); + AIAgent agent = default!; + + try + { + agent = await _agentFactory.CreateAsync(message.Workspace); + } + catch (ArgumentException ae) + { + if (ae.ParamName == nameof(WorkspaceDefinition.ApiKey)) + { + throw new BusinessException(AbpAIErrorCodes.MissingOrInvalidApiKey, innerException: ae); + } + throw; + } - var agentRunRes = agent.RunStreamingAsync(messages); + var agentRunRes = agent.RunStreamingAsync(messages) + .WithErrorHandling((ex) => + { + if (ex is ClientResultException cre && cre.Status == 401) + { + throw new BusinessException(AbpAIErrorCodes.MissingOrInvalidApiKey, innerException: ex); + } + }); var tokenUsageInfo = new TokenUsageInfo(message.Workspace, conversationId); var agentMessageBuilder = new StringBuilder(); @@ -70,10 +98,10 @@ public class AgentService : IAgentService, IScopedDependency tokenUsageInfo.WithMessageId(messageId); -#if DEBUG - Console.WriteLine(); - Console.WriteLine(tokenUsageInfo); -#endif + if (Logger.IsEnabled(LogLevel.Debug)) + { + Logger.LogDebug("TokenUsageInfo: {TokenUsageInfo}", tokenUsageInfo); + } await StoreTokenUsageInfo(tokenUsageInfo); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/System/Collections/Generic/IAsyncEnumerableErrorHandlingExtenssions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/System/Collections/Generic/IAsyncEnumerableErrorHandlingExtenssions.cs new file mode 100644 index 000000000..145445fd0 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/System/Collections/Generic/IAsyncEnumerableErrorHandlingExtenssions.cs @@ -0,0 +1,35 @@ +using System.Runtime.CompilerServices; +using System.Threading; + +namespace System.Collections.Generic; + +internal static class IAsyncEnumerableErrorHandlingExtenssions +{ + public static async IAsyncEnumerable WithErrorHandling( + this IAsyncEnumerable source, + Action onError, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var enumerator = source.GetAsyncEnumerator(cancellationToken); + await using (enumerator) + { + while (true) + { + try + { + if (!await enumerator.MoveNextAsync()) + { + break; + } + } + catch (Exception ex) + { + onError(ex); + yield break; + } + + yield return enumerator.Current; + } + } + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAIErrorCodes.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAIErrorCodes.cs index e5538e47e..90c7e6084 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAIErrorCodes.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAIErrorCodes.cs @@ -7,6 +7,10 @@ public static class AbpAIErrorCodes /// public const string WorkspaceIsNotEnabled = Namespace + ":110001"; /// + /// ApiKey缺失或无效! + /// + public const string MissingOrInvalidApiKey = Namespace + ":110002"; + /// /// 对话已过期, 请重新创建会话! /// public const string ConversationHasExpired = Namespace + ":110101"; diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/en.json b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/en.json index 5e631f34c..e8f71a734 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/en.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/en.json @@ -2,6 +2,7 @@ "culture": "en", "texts": { "Abp.AI:110001": "Workspace is not enabled: {Workspace}!", + "Abp.AI:110002": "Missing or invalid API key!", "Abp.AI:110101": "The conversation has expired. Please create a new one!", "NewConversation": "New Conversation" } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/zh-Hans.json b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/zh-Hans.json index 8681eedaf..4346e798a 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/zh-Hans.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Localization/Resources/zh-Hans.json @@ -2,6 +2,7 @@ "culture": "zh-Hans", "texts": { "Abp.AI:110001": "工作区不可用: {Workspace}!", + "Abp.AI:110002": "ApiKey缺失或无效!", "Abp.AI:110101": "对话已过期, 请重新创建会话!", "NewConversation": "新对话" } From 855c98282f2998783ea988f615a474a33f629f6a Mon Sep 17 00:00:00 2001 From: colin Date: Sat, 14 Mar 2026 14:44:13 +0800 Subject: [PATCH 45/88] feat: Workspace change synchronization event --- .../Chats/ConversationExpiredHandler.cs | 42 ++++++++++++++ ...orkspaceDefinitionStoreCacheInvalidator.cs | 58 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationExpiredHandler.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreCacheInvalidator.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationExpiredHandler.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationExpiredHandler.cs new file mode 100644 index 000000000..8368ad019 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ConversationExpiredHandler.cs @@ -0,0 +1,42 @@ +using LINGYUN.Abp.AIManagement.Workspaces; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.EventBus; +using Volo.Abp.Specifications; +using Volo.Abp.Timing; + +namespace LINGYUN.Abp.AIManagement.Chats; + +public class ConversationExpiredHandler : + ILocalEventHandler>, + ITransientDependency +{ + private readonly IClock _clock; + private readonly IConversationRecordRepository _conversationRecordRepository; + + public ConversationExpiredHandler( + IClock clock, + IConversationRecordRepository conversationRecordRepository) + { + _clock = clock; + _conversationRecordRepository = conversationRecordRepository; + } + + public async virtual Task HandleEventAsync(EntityDeletedEventData eventData) + { + var specification = new ExpressionSpecification( + x => x.Workspace == eventData.Entity.Name); + + var totalCount = await _conversationRecordRepository.GetCountAsync(specification); + var conversations = await _conversationRecordRepository.GetListAsync(specification, + maxResultCount: totalCount); + + foreach (var conversation in conversations ) + { + conversation.ExpiredAt = _clock.Now; + } + + await _conversationRecordRepository.UpdateManyAsync(conversations); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreCacheInvalidator.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreCacheInvalidator.cs new file mode 100644 index 000000000..69f705602 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreCacheInvalidator.cs @@ -0,0 +1,58 @@ +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Options; +using System; +using System.Threading.Tasks; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.EventBus; +using Volo.Abp.Threading; +using Volo.Abp.Timing; + +namespace LINGYUN.Abp.AIManagement.Workspaces; + +public class DynamicWorkspaceDefinitionStoreCacheInvalidator : + ILocalEventHandler>, + ITransientDependency +{ + private readonly IDynamicWorkspaceDefinitionStoreInMemoryCache _storeCache; + + private readonly IClock _clock; + private readonly IDistributedCache _distributedCache; + private readonly AbpDistributedCacheOptions _cacheOptions; + + public DynamicWorkspaceDefinitionStoreCacheInvalidator( + IClock clock, + IDistributedCache distributedCache, + IDynamicWorkspaceDefinitionStoreInMemoryCache storeCache, + IOptions cacheOptions) + { + _storeCache = storeCache; + _clock = clock; + _distributedCache = distributedCache; + _cacheOptions = cacheOptions.Value; + } + + public async virtual Task HandleEventAsync(EntityChangedEventData eventData) + { + await RemoveStampInDistributedCacheAsync(); + } + + protected async virtual Task RemoveStampInDistributedCacheAsync() + { + using (await _storeCache.SyncSemaphore.LockAsync()) + { + var cacheKey = GetCommonStampCacheKey(); + + await _distributedCache.RemoveAsync(cacheKey); + + _storeCache.CacheStamp = Guid.NewGuid().ToString(); + _storeCache.LastCheckTime = _clock.Now.AddMinutes(-5); + } + } + + protected virtual string GetCommonStampCacheKey() + { + return $"{_cacheOptions.KeyPrefix}_AbpInMemoryWorkspaceCacheStamp"; + } +} From 48931eeae6c5af33a4078e0b1994c4935435aaad Mon Sep 17 00:00:00 2001 From: colin Date: Sat, 14 Mar 2026 14:44:46 +0800 Subject: [PATCH 46/88] feat: Only allow querying of one's own conversation. --- .../LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs index dc1fc00af..2ecabaa41 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs @@ -35,6 +35,7 @@ public class ConversationAppService : var queryable = await base.CreateFilteredQueryAsync(input); return queryable + .Where(x => x.CreatorId == CurrentUser.Id) .WhereIf(!input.Filter.IsNullOrWhiteSpace(), x => x.Name.Contains(input.Filter!)); } From 9b21eda9838e2f7a05c817201fd5f31cd9519228 Mon Sep 17 00:00:00 2001 From: colin Date: Mon, 16 Mar 2026 10:19:39 +0800 Subject: [PATCH 47/88] feat: Increase interface permissions --- ...IManagementPermissionDefinitionProvider.cs | 56 +++++++++++++++++++ .../AIManagementPermissionNames.cs | 38 +++++++++++++ .../Abp/AIManagement/Chats/ChatAppService.cs | 5 ++ .../Chats/ConversationAppService.cs | 7 +++ .../WorkspaceDefinitionAppService.cs | 7 +++ .../Localization/Resources/en.json | 5 ++ .../Localization/Resources/zh-Hans.json | 5 ++ .../Abp/AIManagement/Chats/ChatController.cs | 4 ++ .../Chats/ConversationController.cs | 6 ++ .../WorkspaceDefinitionController.cs | 8 ++- 10 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionDefinitionProvider.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionNames.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionDefinitionProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionDefinitionProvider.cs new file mode 100644 index 000000000..ce5c6f665 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionDefinitionProvider.cs @@ -0,0 +1,56 @@ +using LINGYUN.Abp.AIManagement.Localization; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Localization; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.AIManagement.Permissions; + +public class AIManagementPermissionDefinitionProvider : PermissionDefinitionProvider +{ + public override void Define(IPermissionDefinitionContext context) + { + var group = context.AddGroup(AIManagementPermissionNames.GroupName, L("Permission:AIManagement")); + + var groupDefinition = group.AddPermission( + AIManagementPermissionNames.WorkspaceDefinition.Default, + L("Permission:WorkspaceDefinition"), + MultiTenancySides.Host); + groupDefinition.AddChild( + AIManagementPermissionNames.WorkspaceDefinition.Create, + L("Permission:Create"), + MultiTenancySides.Host); + groupDefinition.AddChild( + AIManagementPermissionNames.WorkspaceDefinition.Update, + L("Permission:Edit"), + MultiTenancySides.Host); + groupDefinition.AddChild( + AIManagementPermissionNames.WorkspaceDefinition.Delete, + L("Permission:Delete"), + MultiTenancySides.Host); + + var conversation = group.AddPermission( + AIManagementPermissionNames.Conversation.Default, + L("Permission:Conversation")); + conversation.AddChild( + AIManagementPermissionNames.Conversation.Create, + L("Permission:Create")); + conversation.AddChild( + AIManagementPermissionNames.Conversation.Update, + L("Permission:Update")); + conversation.AddChild( + AIManagementPermissionNames.Conversation.Delete, + L("Permission:Delete")); + + var chat = group.AddPermission( + AIManagementPermissionNames.Chat.Default, + L("Permission:Chats")); + chat.AddChild( + AIManagementPermissionNames.Chat.SendMessage, + L("Permission:SendMessage")); + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionNames.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionNames.cs new file mode 100644 index 000000000..949d5539d --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionNames.cs @@ -0,0 +1,38 @@ +using Volo.Abp.Reflection; + +namespace LINGYUN.Abp.AIManagement.Permissions; + +public static class AIManagementPermissionNames +{ + public const string GroupName = "AIManagement"; + + public static class WorkspaceDefinition + { + public const string Default = GroupName + ".WorkspaceDefinitions"; + + public const string Create = Default + ".Create"; + public const string Update = Default + ".Update"; + public const string Delete = Default + ".Delete"; + } + + public static class Conversation + { + public const string Default = GroupName + ".Conversations"; + + public const string Create = Default + ".Create"; + public const string Update = Default + ".Update"; + public const string Delete = Default + ".Delete"; + } + + public static class Chat + { + public const string Default = GroupName + ".Chats"; + + public const string SendMessage = Default + ".SendMessage"; + } + + public static string[] GetAll() + { + return ReflectionHelper.GetPublicConstantsRecursively(typeof(AIManagementPermissionNames)); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ChatAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ChatAppService.cs index b7863f15a..af96ece71 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ChatAppService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ChatAppService.cs @@ -1,6 +1,8 @@ using LINGYUN.Abp.AI.Agent; using LINGYUN.Abp.AI.Models; using LINGYUN.Abp.AIManagement.Chats.Dtos; +using LINGYUN.Abp.AIManagement.Permissions; +using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.AI; using System.Collections.Generic; using System.Threading.Tasks; @@ -8,6 +10,8 @@ using Volo.Abp.Application.Dtos; using Volo.Abp.Specifications; namespace LINGYUN.Abp.AIManagement.Chats; + +[Authorize(AIManagementPermissionNames.Chat.Default)] public class ChatAppService : AIManagementApplicationServiceBase, IChatAppService { private readonly IAgentService _agentService; @@ -20,6 +24,7 @@ public class ChatAppService : AIManagementApplicationServiceBase, IChatAppServic _repository = repository; } + [Authorize(AIManagementPermissionNames.Chat.SendMessage)] public virtual IAsyncEnumerable SendMessageAsync(SendTextChatMessageDto input) { var chatMessage = new TextChatMessage( diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs index 2ecabaa41..81bd7ea55 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Chats/ConversationAppService.cs @@ -1,5 +1,6 @@ using LINGYUN.Abp.AIManagement.Chats.Dtos; using LINGYUN.Abp.AIManagement.Localization; +using LINGYUN.Abp.AIManagement.Permissions; using Microsoft.Extensions.Options; using System; using System.Linq; @@ -28,6 +29,12 @@ public class ConversationAppService : LocalizationResource = typeof(AIManagementResource); ObjectMapperContext = typeof(AbpAIManagementApplicationModule); + + CreatePolicyName = AIManagementPermissionNames.Conversation.Create; + UpdatePolicyName = AIManagementPermissionNames.Conversation.Update; + DeletePolicyName = AIManagementPermissionNames.Conversation.Delete; + GetListPolicyName = AIManagementPermissionNames.Conversation.Default; + GetPolicyName = AIManagementPermissionNames.Conversation.Default; } protected async override Task> CreateFilteredQueryAsync(ConversationGetListInput input) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs index 38f7a016a..661fc697c 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs @@ -1,5 +1,6 @@ using LINGYUN.Abp.AI; using LINGYUN.Abp.AIManagement.Localization; +using LINGYUN.Abp.AIManagement.Permissions; using LINGYUN.Abp.AIManagement.Workspaces.Dtos; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -38,6 +39,12 @@ public class WorkspaceDefinitionAppService : LocalizationResource = typeof(AIManagementResource); ObjectMapperContext = typeof(AbpAIManagementApplicationModule); + + CreatePolicyName = AIManagementPermissionNames.WorkspaceDefinition.Create; + UpdatePolicyName = AIManagementPermissionNames.WorkspaceDefinition.Update; + DeletePolicyName = AIManagementPermissionNames.WorkspaceDefinition.Delete; + GetListPolicyName = AIManagementPermissionNames.WorkspaceDefinition.Default; + GetPolicyName = AIManagementPermissionNames.WorkspaceDefinition.Default; } public virtual Task> GetAvailableProvidersAsync() diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json index b55671f5b..b3dfbe714 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json @@ -1,6 +1,11 @@ { "culture": "en", "texts": { + "Permission:AIManagement": "Artificia Intelligence", + "Permission:WorkspaceDefinition": "Workspaces", + "Permission:Conversation": "Conversations", + "Permission:Chat": "Chats", + "Permission:SendMessage": "Send Message", "AIManagement:111001": "Workspace {Workspace} already exists!", "AIManagement:111002": "System workspace {Workspace} is not allowed to be deleted!", "DisplayName:MaxLatestHistoryMessagesToKeep": "Carry the recent conversation records", diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json index 021c57d77..dfdbc8f8a 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json @@ -1,6 +1,11 @@ { "culture": "zh-Hans", "texts": { + "Permission:AIManagement": "人工智能", + "Permission:WorkspaceDefinition": "工作区管理", + "Permission:Conversation": "对话管理", + "Permission:Chat": "聊天管理", + "Permission:SendMessage": "发送消息", "AIManagement:111001": "工作区 {Workspace} 已存在!", "AIManagement:111002": "系统工作区 {Workspace} 不允许删除!", "DisplayName:MaxLatestHistoryMessagesToKeep": "携带最近对话记录", diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ChatController.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ChatController.cs index 2ed4e67bb..a562fdeb1 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ChatController.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ChatController.cs @@ -1,4 +1,6 @@ using LINGYUN.Abp.AIManagement.Chats.Dtos; +using LINGYUN.Abp.AIManagement.Permissions; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using System.Threading.Tasks; @@ -12,6 +14,7 @@ namespace LINGYUN.Abp.AIManagement.Chats; [RemoteService(Name = AIManagementRemoteServiceConsts.RemoteServiceName)] [Area(AIManagementRemoteServiceConsts.ModuleName)] [Route($"api/{AIManagementRemoteServiceConsts.ModuleName}/chats")] +[Authorize(AIManagementPermissionNames.Chat.Default)] public class ChatController : AbpControllerBase, IChatAppService { private readonly IChatAppService _service; @@ -22,6 +25,7 @@ public class ChatController : AbpControllerBase, IChatAppService [HttpPost] [ServiceFilter] + [Authorize(AIManagementPermissionNames.Chat.SendMessage)] public async virtual IAsyncEnumerable SendMessageAsync(SendTextChatMessageDto input) { await foreach (var content in _service.SendMessageAsync(input)) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ConversationController.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ConversationController.cs index e90aced0b..4822f5847 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ConversationController.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Chats/ConversationController.cs @@ -1,4 +1,6 @@ using LINGYUN.Abp.AIManagement.Chats.Dtos; +using LINGYUN.Abp.AIManagement.Permissions; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System; using System.Threading.Tasks; @@ -12,6 +14,7 @@ namespace LINGYUN.Abp.AIManagement.Chats; [RemoteService(Name = AIManagementRemoteServiceConsts.RemoteServiceName)] [Area(AIManagementRemoteServiceConsts.ModuleName)] [Route($"api/{AIManagementRemoteServiceConsts.ModuleName}/chats/conversations")] +[Authorize(AIManagementPermissionNames.Conversation.Default)] public class ConversationController : AbpControllerBase, IConversationAppService { private readonly IConversationAppService _service; @@ -21,12 +24,14 @@ public class ConversationController : AbpControllerBase, IConversationAppService } [HttpPost] + [Authorize(AIManagementPermissionNames.Conversation.Create)] public virtual Task CreateAsync(ConversationCreateDto input) { return _service.CreateAsync(input); } [HttpDelete("{id}")] + [Authorize(AIManagementPermissionNames.Conversation.Delete)] public virtual Task DeleteAsync(Guid id) { return _service.DeleteAsync(id); @@ -45,6 +50,7 @@ public class ConversationController : AbpControllerBase, IConversationAppService } [HttpPut("{id}")] + [Authorize(AIManagementPermissionNames.Conversation.Update)] public virtual Task UpdateAsync(Guid id, ConversationUpdateDto input) { return _service.UpdateAsync(id, input); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionController.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionController.cs index 0e7446fa7..0a61a7bc2 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionController.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionController.cs @@ -1,4 +1,6 @@ -using LINGYUN.Abp.AIManagement.Workspaces.Dtos; +using LINGYUN.Abp.AIManagement.Permissions; +using LINGYUN.Abp.AIManagement.Workspaces.Dtos; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System; using System.Threading.Tasks; @@ -12,6 +14,7 @@ namespace LINGYUN.Abp.AIManagement.Workspaces; [RemoteService(Name = AIManagementRemoteServiceConsts.RemoteServiceName)] [Area(AIManagementRemoteServiceConsts.ModuleName)] [Route($"api/{AIManagementRemoteServiceConsts.ModuleName}/workspaces")] +[Authorize(AIManagementPermissionNames.WorkspaceDefinition.Default)] public class WorkspaceDefinitionController : AbpControllerBase, IWorkspaceDefinitionAppService { private readonly IWorkspaceDefinitionAppService _service; @@ -27,12 +30,14 @@ public class WorkspaceDefinitionController : AbpControllerBase, IWorkspaceDefini } [HttpPost] + [Authorize(AIManagementPermissionNames.WorkspaceDefinition.Create)] public virtual Task CreateAsync(WorkspaceDefinitionRecordCreateDto input) { return _service.CreateAsync(input); } [HttpDelete("{id}")] + [Authorize(AIManagementPermissionNames.WorkspaceDefinition.Delete)] public virtual Task DeleteAsync(Guid id) { return _service.DeleteAsync(id); @@ -51,6 +56,7 @@ public class WorkspaceDefinitionController : AbpControllerBase, IWorkspaceDefini } [HttpPut("{id}")] + [Authorize(AIManagementPermissionNames.WorkspaceDefinition.Update)] public virtual Task UpdateAsync(Guid id, WorkspaceDefinitionRecordUpdateDto input) { return _service.UpdateAsync(id, input); From 4fba631ffb198a772cb040947443a6d6cddc8067 Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 18 Mar 2026 11:56:33 +0800 Subject: [PATCH 48/88] fix: Fix the mapping of the oss object --- .../OssManagementApplicationMappers.cs | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/OssManagementApplicationMappers.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/OssManagementApplicationMappers.cs index 9ef13678e..c77419b1d 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/OssManagementApplicationMappers.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/OssManagementApplicationMappers.cs @@ -1,4 +1,8 @@ using Riok.Mapperly.Abstractions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.ConstrainedExecution; using Volo.Abp.Mapperly; namespace LINGYUN.Abp.OssManagement; @@ -10,21 +14,18 @@ public partial class OssContainerToOssContainerDtoMapper : MapperBase { - [MapperIgnoreTarget(nameof(OssObjectDto.Path))] - [MapperIgnoreSource(nameof(OssObject.Prefix))] + [MapperIgnoreSource(nameof(OssObject.Content))] + [MapperIgnoreSource(nameof(OssObject.FullName))] + [MapProperty(nameof(OssObject.Prefix), nameof(OssObjectDto.Path))] public override partial OssObjectDto Map(OssObject source); - [MapperIgnoreTarget(nameof(OssObjectDto.Path))] - [MapperIgnoreSource(nameof(OssObject.Prefix))] + [MapperIgnoreSource(nameof(OssObject.Content))] + [MapperIgnoreSource(nameof(OssObject.FullName))] + [MapProperty(nameof(OssObject.Prefix), nameof(OssObjectDto.Path))] public override partial void Map(OssObject source, OssObjectDto destination); - - public override void AfterMap(OssObject source, OssObjectDto destination) - { - destination.Path = source.Prefix; - } } [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] @@ -37,8 +38,34 @@ public partial class GetOssContainersResponseToOssContainersResultDtoMapper : Ma [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] public partial class GetOssObjectsResponseToOssObjectsResultDtoMapper : MapperBase { + private readonly OssObjectToOssObjectDtoMapper _ossObjectMapper; + + public GetOssObjectsResponseToOssObjectsResultDtoMapper(OssObjectToOssObjectDtoMapper ossObjectMapper) + { + _ossObjectMapper = ossObjectMapper; + } + + [MapperIgnoreTarget(nameof(GetOssObjectsResponse.Objects))] public override partial OssObjectsResultDto Map(GetOssObjectsResponse source); + + [MapperIgnoreTarget(nameof(GetOssObjectsResponse.Objects))] public override partial void Map(GetOssObjectsResponse source, OssObjectsResultDto destination); + + public override void AfterMap(GetOssObjectsResponse source, OssObjectsResultDto destination) + { + if (source.Objects != null) + { + destination.Objects ??= new List(); + destination.Objects.Clear(); + + foreach (var ossObject in source.Objects) + { + var ossObjectDto = _ossObjectMapper.Map(ossObject); + _ossObjectMapper.AfterMap(ossObject, ossObjectDto); + destination.Objects.Add(ossObjectDto); + } + } + } } [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] From e869f4d435f91366dbe9ea57ca301fd5b4670a8e Mon Sep 17 00:00:00 2001 From: colin Date: Thu, 19 Mar 2026 11:14:49 +0800 Subject: [PATCH 49/88] feat: Add my time zone settings --- .../ITimeZoneSettingsAppService.cs | 6 ++++ .../IUserTimeZoneSettingsAppService.cs | 9 ++++++ .../TimeZoneSettingsAppService.cs | 32 ++++++++++++++++--- .../TimeZoneSettingsController.cs | 19 +++++++++-- 4 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/ITimeZoneSettingsAppService.cs create mode 100644 aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/IUserTimeZoneSettingsAppService.cs diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/ITimeZoneSettingsAppService.cs b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/ITimeZoneSettingsAppService.cs new file mode 100644 index 000000000..246e6774e --- /dev/null +++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/ITimeZoneSettingsAppService.cs @@ -0,0 +1,6 @@ +using IVoloTimeZoneSettingsAppService = Volo.Abp.SettingManagement.ITimeZoneSettingsAppService; + +namespace LINGYUN.Abp.SettingManagement; +public interface ITimeZoneSettingsAppService : IVoloTimeZoneSettingsAppService, IUserTimeZoneSettingsAppService +{ +} diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/IUserTimeZoneSettingsAppService.cs b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/IUserTimeZoneSettingsAppService.cs new file mode 100644 index 000000000..0890937c0 --- /dev/null +++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/IUserTimeZoneSettingsAppService.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace LINGYUN.Abp.SettingManagement; +public interface IUserTimeZoneSettingsAppService +{ + Task GetMyTimezoneAsync(); + + Task SetMyTimezoneAsync(string timezone); +} diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/TimeZoneSettingsAppService.cs b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/TimeZoneSettingsAppService.cs index c2ed056b5..b33d06646 100644 --- a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/TimeZoneSettingsAppService.cs +++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/TimeZoneSettingsAppService.cs @@ -3,19 +3,25 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp; +using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; using Volo.Abp.SettingManagement; using Volo.Abp.Timing; +using IVoloTimeZoneSettingsAppService = Volo.Abp.SettingManagement.ITimeZoneSettingsAppService; namespace LINGYUN.Abp.SettingManagement; -[Authorize(Volo.Abp.SettingManagement.SettingManagementPermissions.TimeZone)] +[Authorize] +[ExposeServices( + typeof(ITimeZoneSettingsAppService), + typeof(IUserTimeZoneSettingsAppService), + typeof(IVoloTimeZoneSettingsAppService))] public class TimeZoneSettingsAppService : SettingManagementAppServiceBase, ITimeZoneSettingsAppService { protected ISettingManager SettingManager { get; } protected ITimezoneProvider TimezoneProvider { get; } - private const string UnspecifiedTimeZone = "Unspecified"; + internal const string UnspecifiedTimeZone = "Unspecified"; public TimeZoneSettingsAppService(ISettingManager settingManager, ITimezoneProvider timezoneProvider) { @@ -23,7 +29,24 @@ public class TimeZoneSettingsAppService : SettingManagementAppServiceBase, ITime TimezoneProvider = timezoneProvider; } - public virtual async Task GetAsync() + public async virtual Task GetMyTimezoneAsync() + { + return await SettingManager.GetOrNullForCurrentUserAsync(TimingSettingNames.TimeZone) + ?? UnspecifiedTimeZone; + } + + public async virtual Task SetMyTimezoneAsync(string timezone) + { + if (timezone.Equals(UnspecifiedTimeZone, StringComparison.OrdinalIgnoreCase)) + { + timezone = null; + } + + await SettingManager.SetForCurrentUserAsync(TimingSettingNames.TimeZone, timezone); + } + + [Authorize(Volo.Abp.SettingManagement.SettingManagementPermissions.TimeZone)] + public async virtual Task GetAsync() { var timezone = CurrentTenant.GetMultiTenancySide() == MultiTenancySides.Host ? await SettingManager.GetOrNullGlobalAsync(TimingSettingNames.TimeZone) @@ -48,7 +71,8 @@ public class TimeZoneSettingsAppService : SettingManagementAppServiceBase, ITime return Task.FromResult(timezones); } - public virtual async Task UpdateAsync(string timezone) + [Authorize(Volo.Abp.SettingManagement.SettingManagementPermissions.TimeZone)] + public async virtual Task UpdateAsync(string timezone) { if (timezone.Equals(UnspecifiedTimeZone, StringComparison.OrdinalIgnoreCase)) { diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.HttpApi/LINGYUN/Abp/SettingManagement/TimeZoneSettingsController.cs b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.HttpApi/LINGYUN/Abp/SettingManagement/TimeZoneSettingsController.cs index 724563df9..db261f6b6 100644 --- a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.HttpApi/LINGYUN/Abp/SettingManagement/TimeZoneSettingsController.cs +++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.HttpApi/LINGYUN/Abp/SettingManagement/TimeZoneSettingsController.cs @@ -4,14 +4,13 @@ using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.AspNetCore.Mvc; -using Volo.Abp.SettingManagement; namespace LINGYUN.Abp.SettingManagement; [RemoteService(Name = AbpSettingManagementRemoteServiceConsts.RemoteServiceName)] [Area(AbpSettingManagementRemoteServiceConsts.ModuleName)] [Route("api/setting-management/timezone")] -[Authorize(Volo.Abp.SettingManagement.SettingManagementPermissions.TimeZone)] +[Authorize] public class TimeZoneSettingsController : AbpControllerBase, ITimeZoneSettingsAppService { private readonly ITimeZoneSettingsAppService _service; @@ -22,11 +21,19 @@ public class TimeZoneSettingsController : AbpControllerBase, ITimeZoneSettingsAp } [HttpGet] + [Authorize(Volo.Abp.SettingManagement.SettingManagementPermissions.TimeZone)] public virtual Task GetAsync() { return _service.GetAsync(); } + [HttpGet] + [Route("my-timezone")] + public virtual Task GetMyTimezoneAsync() + { + return _service.GetMyTimezoneAsync(); + } + [HttpGet] [Route("timezones")] public virtual Task> GetTimezonesAsync() @@ -35,6 +42,14 @@ public class TimeZoneSettingsController : AbpControllerBase, ITimeZoneSettingsAp } [HttpPost] + [Route("my-timezone")] + public virtual Task SetMyTimezoneAsync(string timezone) + { + return _service.SetMyTimezoneAsync(timezone); + } + + [HttpPost] + [Authorize(Volo.Abp.SettingManagement.SettingManagementPermissions.TimeZone)] public virtual Task UpdateAsync(string timezone) { return _service.UpdateAsync(timezone); From f49060bdff467674737477d3738bea55f3c89f01 Mon Sep 17 00:00:00 2001 From: colin Date: Thu, 19 Mar 2026 11:16:08 +0800 Subject: [PATCH 50/88] feat: Modify the visibility of private constants --- .../LINGYUN/Abp/SettingManagement/TimeZoneSettingsAppService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/TimeZoneSettingsAppService.cs b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/TimeZoneSettingsAppService.cs index b33d06646..e2ce4dbc4 100644 --- a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/TimeZoneSettingsAppService.cs +++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/TimeZoneSettingsAppService.cs @@ -21,7 +21,7 @@ public class TimeZoneSettingsAppService : SettingManagementAppServiceBase, ITime protected ISettingManager SettingManager { get; } protected ITimezoneProvider TimezoneProvider { get; } - internal const string UnspecifiedTimeZone = "Unspecified"; + private const string UnspecifiedTimeZone = "Unspecified"; public TimeZoneSettingsAppService(ISettingManager settingManager, ITimezoneProvider timezoneProvider) { From 57073b8122116fd0d3a8cfa08507d980d0a8076a Mon Sep 17 00:00:00 2001 From: colin Date: Tue, 24 Mar 2026 14:28:28 +0800 Subject: [PATCH 51/88] feat: Add notification sending records --- Directory.Packages.props | 1 + .../LINGYUN.MicroService.SingleProject.sln | 68 +- ...viceMigrationsEntityFrameworkCoreModule.cs | 1 + ...1_Add-Notification-Send-Record.Designer.cs | 812 +++ ...0320092811_Add-Notification-Send-Record.cs | 49 + ...ServiceMigrationsDbContextModelSnapshot.cs | 55 +- .../BackgroundJobs/NotificationPublishJob.cs | 69 +- .../Distributed/NotificationEventHandler.cs | 59 +- ...31109_Add-AI-Management-Module.Designer.cs | 5696 ++++++++++++++++ ...20260316031109_Add-AI-Management-Module.cs | 150 + ...9_Add-Notification-Send-Record.Designer.cs | 5749 ++++++++++++++++ ...0324062229_Add-Notification-Send-Record.cs | 49 + .../SingleMigrationsDbContextModelSnapshot.cs | 332 + ...53918_Add-AI-Management-Module.Designer.cs | 5701 ++++++++++++++++ ...20260319053918_Add-AI-Management-Module.cs | 150 + ...3_Add-Notification-Send-Record.Designer.cs | 5754 +++++++++++++++++ ...0324062303_Add-Notification-Send-Record.cs | 48 + .../SingleMigrationsDbContextModelSnapshot.cs | 332 + ...ications.Single.EntityFrameworkCore.csproj | 1 + .../SingleMigrationsDbContext.cs | 22 +- ...ngleMigrationsEntityFrameworkCoreModule.cs | 2 + ...eVbenAdmin5NavigationDefinitionProvider.cs | 11 +- .../Dto/NotificationSendRecordDto.cs | 14 + ...NotificationSendRecordGetPagedListInput.cs | 16 + .../INotificationSendRecordAppService.cs | 13 + .../Permissions/NotificationsPermissions.cs | 6 + ...ificationsPermissionsDefinitionProvider.cs | 5 + .../AbpNotificationsApplicationMappers.cs | 26 + .../NotificationSendRecordAppService.cs | 107 + .../NotificationPublishContext.cs | 35 + .../Abp/Notifications/NotificationSendInfo.cs | 49 + .../Notifications/NotificationSendState.cs | 23 + .../Localization/DomainShared/en.json | 17 +- .../Localization/DomainShared/zh-Hans.json | 17 +- .../NotificationSendRecordConsts.cs | 6 + .../INotificationSendRecordRepository.cs | 20 + .../IUserNotificationRepository.cs | 14 + .../Notifications/NotificationSendRecord.cs | 48 + .../NotificationSendRecordInfo.cs | 14 + .../Abp/Notifications/NotificationStore.cs | 33 + .../EmailingNotificationPublishProvider.cs | 15 +- ...pNotificationsEntityFrameworkCoreModule.cs | 2 + .../EfCoreNotificationSendRecordRepository.cs | 99 + .../INotificationsDbContext.cs | 4 +- .../NotificationsDbContext.cs | 4 +- ...cationsDbContextModelCreatingExtensions.cs | 99 +- .../NotificationSendRecordController.cs | 45 + .../PushPlusNotificationPublishProvider.cs | 13 +- .../SignalRNotificationPublishProvider.cs | 23 +- .../Sms/SmsNotificationPublishProvider.cs | 13 +- ...tMiniProgramNotificationPublishProvider.cs | 94 +- .../WeChatWorkNotificationPublishProvider.cs | 30 +- .../WxPusherNotificationPublishProvider.cs | 14 +- .../INotificationPublishProvider.cs | 14 +- .../Abp/Notifications/INotificationStore.cs | 4 + .../NotificationPublishProvider.cs | 27 +- .../Notifications/NullNotificationStore.cs | 7 + .../BackgroundJobs/NotificationPublishJob.cs | 68 +- .../Distributed/NotificationEventHandler.cs | 66 +- ...LY.MicroService.Applications.Single.csproj | 4 +- ...rviceApplicationsSingleModule.Configure.cs | 13 + .../MicroServiceApplicationsSingleModule.cs | 7 + .../BackgroundJobs/NotificationPublishJob.cs | 69 +- .../Distributed/NotificationEventHandler.cs | 59 +- .../appsettings.Development.PostgreSql.json | 25 + 65 files changed, 26191 insertions(+), 201 deletions(-) create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/Migrations/20260320092811_Add-Notification-Send-Record.Designer.cs create mode 100644 aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/Migrations/20260320092811_Add-Notification-Send-Record.cs create mode 100644 aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260316031109_Add-AI-Management-Module.Designer.cs create mode 100644 aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260316031109_Add-AI-Management-Module.cs create mode 100644 aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260324062229_Add-Notification-Send-Record.Designer.cs create mode 100644 aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260324062229_Add-Notification-Send-Record.cs create mode 100644 aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260319053918_Add-AI-Management-Module.Designer.cs create mode 100644 aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260319053918_Add-AI-Management-Module.cs create mode 100644 aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260324062303_Add-Notification-Send-Record.Designer.cs create mode 100644 aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260324062303_Add-Notification-Send-Record.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Dto/NotificationSendRecordDto.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Dto/NotificationSendRecordGetPagedListInput.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/INotificationSendRecordAppService.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/NotificationSendRecordAppService.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationPublishContext.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationSendInfo.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationSendState.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/NotificationSendRecordConsts.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/INotificationSendRecordRepository.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationSendRecord.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationSendRecordInfo.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/EfCoreNotificationSendRecordRepository.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.HttpApi/LINGYUN/Abp/Notifications/NotificationSendRecordController.cs create mode 100644 aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.PostgreSql.json diff --git a/Directory.Packages.props b/Directory.Packages.props index 0c030dad2..ff9567ced 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -255,6 +255,7 @@ + diff --git a/aspnet-core/LINGYUN.MicroService.SingleProject.sln b/aspnet-core/LINGYUN.MicroService.SingleProject.sln index 8ebc11483..e5aecae7e 100644 --- a/aspnet-core/LINGYUN.MicroService.SingleProject.sln +++ b/aspnet-core/LINGYUN.MicroService.SingleProject.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.14.36616.10 d17.14 +VisualStudioVersion = 17.14.36616.10 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{0B58AA48-665A-443F-A6A8-751FB9629DAF}" EndProject @@ -700,6 +700,26 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WeChat.Work.OA" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Quartz.PostgresSqlInstaller", "modules\task-management\LINGYUN.Abp.Quartz.PostgresSqlInstaller\LINGYUN.Abp.Quartz.PostgresSqlInstaller.csproj", "{CD7B8501-57B7-6C0E-472F-4E9B894ED6C2}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ai", "ai", "{E87F876B-EEC8-4A3B-9FF0-15EB38B4A7BC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.AI.Core", "modules\ai\LINGYUN.Abp.AI.Core\LINGYUN.Abp.AI.Core.csproj", "{72083C13-32E8-1122-7761-CE7C0B64AABA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.AI.Agent", "modules\ai\LINGYUN.Abp.AI.Agent\LINGYUN.Abp.AI.Agent.csproj", "{F80E583B-93F3-C1E6-A83D-AC83132DBC5F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.AIManagement.Domain.Shared", "modules\ai\LINGYUN.Abp.AIManagement.Domain.Shared\LINGYUN.Abp.AIManagement.Domain.Shared.csproj", "{D8E28DB1-D8C8-C46C-A93E-CA3A0D90065B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.AIManagement.Domain", "modules\ai\LINGYUN.Abp.AIManagement.Domain\LINGYUN.Abp.AIManagement.Domain.csproj", "{7D372713-5117-82F2-7591-C0AE09BA04CF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.AIManagement.Application.Contracts", "modules\ai\LINGYUN.Abp.AIManagement.Application.Contracts\LINGYUN.Abp.AIManagement.Application.Contracts.csproj", "{479D24D3-D01D-6655-87E8-5A236B3BA371}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.AIManagement.Application", "modules\ai\LINGYUN.Abp.AIManagement.Application\LINGYUN.Abp.AIManagement.Application.csproj", "{62A90887-34B9-1BDE-8851-9CC926AEBBBE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.AIManagement.EntityFrameworkCore", "modules\ai\LINGYUN.Abp.AIManagement.EntityFrameworkCore\LINGYUN.Abp.AIManagement.EntityFrameworkCore.csproj", "{5AFC464A-9B1F-7415-2D2E-320D567ED818}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.AIManagement.HttpApi", "modules\ai\LINGYUN.Abp.AIManagement.HttpApi\LINGYUN.Abp.AIManagement.HttpApi.csproj", "{3B618981-F1CB-9AEC-E90B-4B05B982C1FC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.AIManagement.HttpApi.Client", "modules\ai\LINGYUN.Abp.AIManagement.HttpApi.Client\LINGYUN.Abp.AIManagement.HttpApi.Client.csproj", "{44957812-548A-D14C-6C92-F9902DADDCDA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1866,6 +1886,42 @@ Global {CD7B8501-57B7-6C0E-472F-4E9B894ED6C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {CD7B8501-57B7-6C0E-472F-4E9B894ED6C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {CD7B8501-57B7-6C0E-472F-4E9B894ED6C2}.Release|Any CPU.Build.0 = Release|Any CPU + {72083C13-32E8-1122-7761-CE7C0B64AABA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72083C13-32E8-1122-7761-CE7C0B64AABA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72083C13-32E8-1122-7761-CE7C0B64AABA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72083C13-32E8-1122-7761-CE7C0B64AABA}.Release|Any CPU.Build.0 = Release|Any CPU + {F80E583B-93F3-C1E6-A83D-AC83132DBC5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F80E583B-93F3-C1E6-A83D-AC83132DBC5F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F80E583B-93F3-C1E6-A83D-AC83132DBC5F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F80E583B-93F3-C1E6-A83D-AC83132DBC5F}.Release|Any CPU.Build.0 = Release|Any CPU + {D8E28DB1-D8C8-C46C-A93E-CA3A0D90065B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D8E28DB1-D8C8-C46C-A93E-CA3A0D90065B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D8E28DB1-D8C8-C46C-A93E-CA3A0D90065B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D8E28DB1-D8C8-C46C-A93E-CA3A0D90065B}.Release|Any CPU.Build.0 = Release|Any CPU + {7D372713-5117-82F2-7591-C0AE09BA04CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D372713-5117-82F2-7591-C0AE09BA04CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D372713-5117-82F2-7591-C0AE09BA04CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D372713-5117-82F2-7591-C0AE09BA04CF}.Release|Any CPU.Build.0 = Release|Any CPU + {479D24D3-D01D-6655-87E8-5A236B3BA371}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {479D24D3-D01D-6655-87E8-5A236B3BA371}.Debug|Any CPU.Build.0 = Debug|Any CPU + {479D24D3-D01D-6655-87E8-5A236B3BA371}.Release|Any CPU.ActiveCfg = Release|Any CPU + {479D24D3-D01D-6655-87E8-5A236B3BA371}.Release|Any CPU.Build.0 = Release|Any CPU + {62A90887-34B9-1BDE-8851-9CC926AEBBBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {62A90887-34B9-1BDE-8851-9CC926AEBBBE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {62A90887-34B9-1BDE-8851-9CC926AEBBBE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {62A90887-34B9-1BDE-8851-9CC926AEBBBE}.Release|Any CPU.Build.0 = Release|Any CPU + {5AFC464A-9B1F-7415-2D2E-320D567ED818}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5AFC464A-9B1F-7415-2D2E-320D567ED818}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5AFC464A-9B1F-7415-2D2E-320D567ED818}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5AFC464A-9B1F-7415-2D2E-320D567ED818}.Release|Any CPU.Build.0 = Release|Any CPU + {3B618981-F1CB-9AEC-E90B-4B05B982C1FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B618981-F1CB-9AEC-E90B-4B05B982C1FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B618981-F1CB-9AEC-E90B-4B05B982C1FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B618981-F1CB-9AEC-E90B-4B05B982C1FC}.Release|Any CPU.Build.0 = Release|Any CPU + {44957812-548A-D14C-6C92-F9902DADDCDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44957812-548A-D14C-6C92-F9902DADDCDA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44957812-548A-D14C-6C92-F9902DADDCDA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44957812-548A-D14C-6C92-F9902DADDCDA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2207,6 +2263,16 @@ Global {C996D147-5B81-49D9-949B-F3BC185B2CD4} = {91867618-0D86-4410-91C6-B1166A9ACDF9} {AB207B2A-C8E7-977D-98E6-66527CBB2B7F} = {91867618-0D86-4410-91C6-B1166A9ACDF9} {CD7B8501-57B7-6C0E-472F-4E9B894ED6C2} = {91EE5D5B-B6DF-43F1-BC09-1A982719A34B} + {E87F876B-EEC8-4A3B-9FF0-15EB38B4A7BC} = {0B58AA48-665A-443F-A6A8-751FB9629DAF} + {72083C13-32E8-1122-7761-CE7C0B64AABA} = {E87F876B-EEC8-4A3B-9FF0-15EB38B4A7BC} + {F80E583B-93F3-C1E6-A83D-AC83132DBC5F} = {E87F876B-EEC8-4A3B-9FF0-15EB38B4A7BC} + {D8E28DB1-D8C8-C46C-A93E-CA3A0D90065B} = {E87F876B-EEC8-4A3B-9FF0-15EB38B4A7BC} + {7D372713-5117-82F2-7591-C0AE09BA04CF} = {E87F876B-EEC8-4A3B-9FF0-15EB38B4A7BC} + {479D24D3-D01D-6655-87E8-5A236B3BA371} = {E87F876B-EEC8-4A3B-9FF0-15EB38B4A7BC} + {62A90887-34B9-1BDE-8851-9CC926AEBBBE} = {E87F876B-EEC8-4A3B-9FF0-15EB38B4A7BC} + {5AFC464A-9B1F-7415-2D2E-320D567ED818} = {E87F876B-EEC8-4A3B-9FF0-15EB38B4A7BC} + {3B618981-F1CB-9AEC-E90B-4B05B982C1FC} = {E87F876B-EEC8-4A3B-9FF0-15EB38B4A7BC} + {44957812-548A-D14C-6C92-F9902DADDCDA} = {E87F876B-EEC8-4A3B-9FF0-15EB38B4A7BC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {711A43C0-A2F8-4E5C-9B9F-F2551E4B3FF1} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/MessageServiceMigrationsEntityFrameworkCoreModule.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/MessageServiceMigrationsEntityFrameworkCoreModule.cs index 016447de6..171994888 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/MessageServiceMigrationsEntityFrameworkCoreModule.cs +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/MessageServiceMigrationsEntityFrameworkCoreModule.cs @@ -4,6 +4,7 @@ using LINGYUN.Abp.Notifications.EntityFrameworkCore; using LINGYUN.Abp.Quartz.PostgresSqlInstaller; using LINGYUN.Abp.Saas.EntityFrameworkCore; using LINGYUN.Abp.TextTemplating.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using System; using Volo.Abp.EntityFrameworkCore; diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/Migrations/20260320092811_Add-Notification-Send-Record.Designer.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/Migrations/20260320092811_Add-Notification-Send-Record.Designer.cs new file mode 100644 index 000000000..a17c59781 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/Migrations/20260320092811_Add-Notification-Send-Record.Designer.cs @@ -0,0 +1,812 @@ +// +using System; +using LINGYUN.Abp.MicroService.MessageService; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LINGYUN.Abp.MicroService.MessageService.Migrations +{ + [DbContext(typeof(MessageServiceMigrationsDbContext))] + [Migration("20260320092811_Add-Notification-Send-Record")] + partial class AddNotificationSendRecord + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatCard", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Age") + .HasColumnType("integer"); + + b.Property("AvatarUrl") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Birthday") + .HasColumnType("timestamp with time zone"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LastOnlineTime") + .HasColumnType("timestamp with time zone"); + + b.Property("NickName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Sex") + .HasColumnType("integer"); + + b.Property("Sign") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("State") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserChatCards", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatFriend", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Black") + .HasColumnType("boolean"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DontDisturb") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrientId") + .HasColumnType("uuid"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("RemarkName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SpecialFocus") + .HasColumnType("boolean"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "FrientId"); + + b.ToTable("AppUserChatFriends", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AllowAddFriend") + .HasColumnType("boolean"); + + b.Property("AllowAnonymous") + .HasColumnType("boolean"); + + b.Property("AllowReceiveMessage") + .HasColumnType("boolean"); + + b.Property("AllowSendMessage") + .HasColumnType("boolean"); + + b.Property("RequireAddFriendValition") + .HasColumnType("boolean"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserChatSettings", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("character varying(1048576)"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("MessageId") + .HasColumnType("bigint"); + + b.Property("ReceiveUserId") + .HasColumnType("uuid"); + + b.Property("SendUserName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Source") + .HasColumnType("integer"); + + b.Property("State") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ReceiveUserId"); + + b.ToTable("AppUserMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.ChatGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("AdminUserId") + .HasColumnType("uuid"); + + b.Property("AllowAnonymous") + .HasColumnType("boolean"); + + b.Property("AllowSendMessage") + .HasColumnType("boolean"); + + b.Property("AvatarUrl") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MaxUserCount") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Notice") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Tag") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("AppChatGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.GroupChatBlack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("ShieldUserId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId"); + + b.ToTable("AppGroupChatBlacks", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.GroupMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("character varying(1048576)"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("MessageId") + .HasColumnType("bigint"); + + b.Property("SendUserName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Source") + .HasColumnType("integer"); + + b.Property("State") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId"); + + b.ToTable("AppGroupMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.UserChatGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId", "UserId"); + + b.ToTable("AppUserChatGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.UserGroupCard", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsAdmin") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("NickName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SilenceEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserGroupCards", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ContentType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("NotificationTypeName") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Severity") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "NotificationName"); + + b.ToTable("AppNotifications", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationDefinitionGroupRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowSubscriptionToClients") + .HasColumnType("boolean"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("DisplayName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("AppNotificationDefinitionGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowSubscriptionToClients") + .HasColumnType("boolean"); + + b.Property("ContentType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("DisplayName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("NotificationLifetime") + .HasColumnType("integer"); + + b.Property("NotificationType") + .HasColumnType("integer"); + + b.Property("Providers") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Template") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.HasKey("Id"); + + b.ToTable("AppNotificationDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationSendRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Reason") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SendTime") + .HasColumnType("timestamp with time zone"); + + b.Property("State") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasDefaultValue("/"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "NotificationName") + .HasDatabaseName("IX_Tenant_Send_Notification_Name"); + + b.ToTable("AppNotificationSendRecords", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.UserNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("ReadStatus") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "NotificationId") + .HasDatabaseName("IX_Tenant_User_Notification_Id"); + + b.ToTable("AppUserNotifications", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.UserSubscribe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasDefaultValue("/"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "NotificationName") + .IsUnique() + .HasDatabaseName("IX_Tenant_User_Notification_Name"); + + b.ToTable("AppUserSubscribes", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/Migrations/20260320092811_Add-Notification-Send-Record.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/Migrations/20260320092811_Add-Notification-Send-Record.cs new file mode 100644 index 000000000..f5d88f390 --- /dev/null +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/Migrations/20260320092811_Add-Notification-Send-Record.cs @@ -0,0 +1,49 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace LINGYUN.Abp.MicroService.MessageService.Migrations +{ + /// + public partial class AddNotificationSendRecord : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AppNotificationSendRecords", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + TenantId = table.Column(type: "uuid", nullable: true), + Provider = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + SendTime = table.Column(type: "timestamp with time zone", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + UserName = table.Column(type: "character varying(128)", maxLength: 128, nullable: false, defaultValue: "/"), + NotificationId = table.Column(type: "bigint", nullable: false), + NotificationName = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + State = table.Column(type: "integer", nullable: false), + Reason = table.Column(type: "character varying(255)", maxLength: 255, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AppNotificationSendRecords", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_Tenant_Send_Notification_Name", + table: "AppNotificationSendRecords", + columns: new[] { "TenantId", "NotificationName" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AppNotificationSendRecords"); + } + } +} diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/Migrations/MessageServiceMigrationsDbContextModelSnapshot.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/Migrations/MessageServiceMigrationsDbContextModelSnapshot.cs index d1b513018..2aebeadd1 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/Migrations/MessageServiceMigrationsDbContextModelSnapshot.cs +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/Migrations/MessageServiceMigrationsDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace LINGYUN.Abp.MicroService.MessageService.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) - .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -682,6 +682,59 @@ namespace LINGYUN.Abp.MicroService.MessageService.Migrations b.ToTable("AppNotificationDefinitions", (string)null); }); + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationSendRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Reason") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SendTime") + .HasColumnType("timestamp with time zone"); + + b.Property("State") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasDefaultValue("/"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "NotificationName") + .HasDatabaseName("IX_Tenant_Send_Notification_Name"); + + b.ToTable("AppNotificationSendRecords", (string)null); + }); + modelBuilder.Entity("LINGYUN.Abp.Notifications.UserNotification", b => { b.Property("Id") diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/BackgroundJobs/NotificationPublishJob.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/BackgroundJobs/NotificationPublishJob.cs index d4f1ccba9..c0b26073f 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/BackgroundJobs/NotificationPublishJob.cs +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/BackgroundJobs/NotificationPublishJob.cs @@ -1,25 +1,34 @@ using LINGYUN.Abp.Notifications; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; +using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.BackgroundJobs; using Volo.Abp.DependencyInjection; +using Volo.Abp.Timing; namespace LINGYUN.Abp.MicroService.MessageService.BackgroundJobs; public class NotificationPublishJob : AsyncBackgroundJob, ITransientDependency { + protected IClock Clock { get; } protected AbpNotificationsPublishOptions Options { get; } protected IServiceScopeFactory ServiceScopeFactory { get; } + protected INotificationStore NotificationStore { get; } protected INotificationDataSerializer NotificationDataSerializer { get; } public NotificationPublishJob( + IClock clock, IOptions options, IServiceScopeFactory serviceScopeFactory, + INotificationStore notificationStore, INotificationDataSerializer notificationDataSerializer) { + Clock = clock; Options = options.Value; ServiceScopeFactory = serviceScopeFactory; + NotificationStore = notificationStore; NotificationDataSerializer = notificationDataSerializer; } @@ -33,9 +42,65 @@ public class NotificationPublishJob : AsyncBackgroundJob(); var notification = await store.GetNotificationOrNullAsync(args.TenantId, args.NotificationId); notification.Data = NotificationDataSerializer.Serialize(notification.Data); - - await publishProvider.PublishAsync(notification, args.UserIdentifiers); + + var sendInfo = OnPublishing(publishProvider, notification, args.UserIdentifiers); + + try + { + if (await publishProvider.CanPublishAsync(notification)) + { + var context = new NotificationPublishContext(notification, args.UserIdentifiers); + // 发布 + await publishProvider.PublishAsync(context); + + sendInfo.Sent(context.Exception); + + if (context.Exception == null && !context.Reason.IsNullOrWhiteSpace()) + { + sendInfo.Cancel(context.Reason); + } + + Logger.LogDebug($"Send notification {notification.Name} with provider {publishProvider.Name} was successful"); + } + else + { + sendInfo.Disbaled(); + } + + await OnPublished(sendInfo); + } + catch (Exception ex) + { + Logger.LogWarning($"Send notification error with provider {publishProvider.Name}"); + Logger.LogWarning($"Error message:{ex.Message}"); + + try + { + sendInfo.Sent(ex); + await OnPublished(sendInfo); + } + catch { } + + throw; + } } } } + + protected virtual NotificationSendInfo OnPublishing( + INotificationPublishProvider provider, + NotificationInfo notification, + IEnumerable identifiers) + { + return new NotificationSendInfo( + provider.Name, + Clock.Now, + notification, + identifiers); + } + + protected async Task OnPublished(NotificationSendInfo sendInfo) + { + await NotificationStore.InsertSendStateAsync(sendInfo); + } } diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/Handlers/Distributed/NotificationEventHandler.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/Handlers/Distributed/NotificationEventHandler.cs index d7ba789a6..8ba419705 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/Handlers/Distributed/NotificationEventHandler.cs +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/Handlers/Distributed/NotificationEventHandler.cs @@ -19,6 +19,7 @@ using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; using Volo.Abp.Settings; using Volo.Abp.TextTemplating; +using Volo.Abp.Timing; using Volo.Abp.Uow; namespace LINGYUN.Abp.MicroService.MessageService.Handlers.Distributed @@ -44,6 +45,10 @@ namespace LINGYUN.Abp.MicroService.MessageService.Handlers.Distributed /// protected AbpNotificationsPublishOptions Options { get; } /// + /// Reference to . + /// + protected IClock Clock { get; } + /// /// Reference to . /// protected ISettingProvider SettingProvider { get; } @@ -100,6 +105,7 @@ namespace LINGYUN.Abp.MicroService.MessageService.Handlers.Distributed /// Initializes a new instance of the class. /// public NotificationEventHandler( + IClock clock, ICurrentTenant currentTenant, ISettingProvider settingProvider, ITenantConfigurationCache tenantConfigurationCache, @@ -115,6 +121,7 @@ namespace LINGYUN.Abp.MicroService.MessageService.Handlers.Distributed INotificationSubscriptionManager notificationSubscriptionManager, INotificationPublishProviderManager notificationPublishProviderManager) { + Clock = clock; Options = options.Value; TenantConfigurationCache = tenantConfigurationCache; CurrentTenant = currentTenant; @@ -435,20 +442,40 @@ namespace LINGYUN.Abp.MicroService.MessageService.Handlers.Distributed /// /// 通知发布者 /// 通知信息 - /// 订阅用户列表 + /// 订阅用户列表 /// protected async Task PublishToSubscriberAsync( INotificationPublishProvider provider, NotificationInfo notificationInfo, IEnumerable subscriptionUsers) { + var sendInfo = OnPublishing(provider, notificationInfo, subscriptionUsers); + try { Logger.LogDebug($"Sending notification with provider {provider.Name}"); - // 发布 - await provider.PublishAsync(notificationInfo, subscriptionUsers); - Logger.LogDebug($"Send notification {notificationInfo.Name} with provider {provider.Name} was successful"); + if (await provider.CanPublishAsync(notificationInfo)) + { + var context = new NotificationPublishContext(notificationInfo, subscriptionUsers); + // 发布 + await provider.PublishAsync(context); + + sendInfo.Sent(context.Exception); + + if (context.Exception == null && !context.Reason.IsNullOrWhiteSpace()) + { + sendInfo.Cancel(context.Reason); + } + + Logger.LogDebug($"Send notification {notificationInfo.Name} with provider {provider.Name} was successful"); + } + else + { + sendInfo.Disbaled(); + } + + await OnPublished(sendInfo); } catch (Exception ex) { @@ -457,6 +484,13 @@ namespace LINGYUN.Abp.MicroService.MessageService.Handlers.Distributed Logger.LogDebug($"Failed to send notification {notificationInfo.Name}. Try to push notification to background job"); // 发送失败的消息进入后台队列 await ProcessingFailedToQueueAsync(provider, notificationInfo, subscriptionUsers); + + try + { + sendInfo.Sent(ex); + await OnPublished(sendInfo); + } + catch { } } } /// @@ -489,5 +523,22 @@ namespace LINGYUN.Abp.MicroService.MessageService.Handlers.Distributed Logger.LogWarning("Failed to push to background job, notification will be discarded, error cause: {message}", ex.Message); } } + + protected virtual NotificationSendInfo OnPublishing( + INotificationPublishProvider provider, + NotificationInfo notification, + IEnumerable identifiers) + { + return new NotificationSendInfo( + provider.Name, + Clock.Now, + notification, + identifiers); + } + + protected async Task OnPublished(NotificationSendInfo sendInfo) + { + await NotificationStore.InsertSendStateAsync(sendInfo); + } } } diff --git a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260316031109_Add-AI-Management-Module.Designer.cs b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260316031109_Add-AI-Management-Module.Designer.cs new file mode 100644 index 000000000..1dfa168ec --- /dev/null +++ b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260316031109_Add-AI-Management-Module.Designer.cs @@ -0,0 +1,5696 @@ +// +using System; +using LY.MicroService.Applications.Single.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql.Migrations +{ + [DbContext(typeof(SingleMigrationsDbContext))] + [Migration("20260316031109_Add-AI-Management-Module")] + partial class AddAIManagementModule + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.ConversationRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp without time zone"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UpdateAt") + .HasColumnType("timestamp without time zone"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.ToTable("AbpAIConversations", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.TextChatMessageRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ReplyAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ReplyMessage") + .HasColumnType("text"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITextChatMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tokens.TokenUsageRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CachedInputTokenCount") + .HasColumnType("bigint"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("InputTokenCount") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MessageId") + .HasColumnType("uuid"); + + b.Property("OutputTokenCount") + .HasColumnType("bigint"); + + b.Property("ReasoningTokenCount") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TotalTokenCount") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITokenUsages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiBaseUrl") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ApiKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrequencyPenalty") + .HasColumnType("real"); + + b.Property("Instructions") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("IsSystem") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MaxOutputTokens") + .HasColumnType("integer"); + + b.Property("ModelName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PresencePenalty") + .HasColumnType("real"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SystemPrompt") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Temperature") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpAIWorkspaceDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityEnumInfo", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("PropertyInfoId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasColumnName("Value"); + + b.HasKey("Id"); + + b.HasIndex("PropertyInfoId", "Name"); + + b.ToTable("AbpAuthEntityEnums", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("JavaScriptType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("JavaScriptType"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("TypeFullName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("TypeFullName"); + + b.Property("TypeInfoId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TypeInfoId", "TypeFullName"); + + b.ToTable("AbpAuthEntityProperties", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsAuditEnabled") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("TypeFullName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("TypeFullName"); + + b.HasKey("Id"); + + b.HasIndex("TypeFullName"); + + b.ToTable("AbpAuthEntitites", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.OrganizationUnitEntityRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessedProperties") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("AccessedProperties"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("EntityTypeFullName") + .HasColumnType("text"); + + b.Property("EntityTypeId") + .HasColumnType("uuid"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FilterGroup") + .HasColumnType("text") + .HasColumnName("FilterGroup"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Operation") + .HasColumnType("integer"); + + b.Property("OrgCode") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("OrgCode"); + + b.Property("OrgId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityTypeId"); + + b.ToTable("AbpAuthOrganizationUnitEntityRules", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.RoleEntityRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessedProperties") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("AccessedProperties"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("EntityTypeFullName") + .HasColumnType("text"); + + b.Property("EntityTypeId") + .HasColumnType("uuid"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FilterGroup") + .HasColumnType("text") + .HasColumnName("FilterGroup"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Operation") + .HasColumnType("integer"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("RoleName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("RoleName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityTypeId"); + + b.ToTable("AbpAuthRoleEntityRules", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.SubjectStrategy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Strategy") + .HasColumnType("integer"); + + b.Property("SubjectId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("SubjectId"); + + b.Property("SubjectName") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("SubjectName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpAuthSubjectStrategys", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Authors.Author", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ShortBio") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("Demo_Authors", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.Book", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AuthorId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Price") + .HasColumnType("real"); + + b.Property("PublishDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AuthorId"); + + b.ToTable("Demo_Books", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.BookAuth", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EntityId") + .HasMaxLength(64) + .HasColumnType("uuid") + .HasColumnName("EntityId"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("EntityType"); + + b.Property("OrganizationUnit") + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("OrganizationUnit"); + + b.Property("Role") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("Role"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("EntityId"); + + b.HasIndex("OrganizationUnit"); + + b.HasIndex("Role"); + + b.ToTable("Demo_BooksAuths", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprInfo", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Data") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Data"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Provider"); + + b.Property("RequestId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RequestId"); + + b.ToTable("AbpGdprInfos", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("ReadyTime") + .HasColumnType("timestamp without time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpGdprRequests", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Language", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("CultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("CultureName"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("DisplayName"); + + b.Property("Enable") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("TwoLetterISOLanguageName") + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("TwoLetterISOLanguageName"); + + b.Property("UiCultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("UiCultureName"); + + b.HasKey("Id"); + + b.HasIndex("CultureName"); + + b.ToTable("AbpLocalizationLanguages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Resource", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DefaultCultureName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("DefaultCultureName"); + + b.Property("Description") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Description"); + + b.Property("DisplayName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("DisplayName"); + + b.Property("Enable") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("Name"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("AbpLocalizationResources", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Text", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("CultureName"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("Key"); + + b.Property("ResourceName") + .HasColumnType("text"); + + b.Property("Value") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)") + .HasColumnName("Value"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("AbpLocalizationTexts", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatCard", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Age") + .HasColumnType("integer"); + + b.Property("AvatarUrl") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Birthday") + .HasColumnType("timestamp without time zone"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LastOnlineTime") + .HasColumnType("timestamp without time zone"); + + b.Property("NickName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Sex") + .HasColumnType("integer"); + + b.Property("Sign") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("State") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserChatCards", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatFriend", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Black") + .HasColumnType("boolean"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DontDisturb") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrientId") + .HasColumnType("uuid"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("RemarkName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SpecialFocus") + .HasColumnType("boolean"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "FrientId"); + + b.ToTable("AppUserChatFriends", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AllowAddFriend") + .HasColumnType("boolean"); + + b.Property("AllowAnonymous") + .HasColumnType("boolean"); + + b.Property("AllowReceiveMessage") + .HasColumnType("boolean"); + + b.Property("AllowSendMessage") + .HasColumnType("boolean"); + + b.Property("RequireAddFriendValition") + .HasColumnType("boolean"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserChatSettings", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("character varying(1048576)"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("MessageId") + .HasColumnType("bigint"); + + b.Property("ReceiveUserId") + .HasColumnType("uuid"); + + b.Property("SendUserName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Source") + .HasColumnType("integer"); + + b.Property("State") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ReceiveUserId"); + + b.ToTable("AppUserMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.ChatGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("AdminUserId") + .HasColumnType("uuid"); + + b.Property("AllowAnonymous") + .HasColumnType("boolean"); + + b.Property("AllowSendMessage") + .HasColumnType("boolean"); + + b.Property("AvatarUrl") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MaxUserCount") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Notice") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Tag") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("AppChatGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.GroupChatBlack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("ShieldUserId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId"); + + b.ToTable("AppGroupChatBlacks", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.GroupMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("character varying(1048576)"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("MessageId") + .HasColumnType("bigint"); + + b.Property("SendUserName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Source") + .HasColumnType("integer"); + + b.Property("State") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId"); + + b.ToTable("AppGroupMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.UserChatGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId", "UserId"); + + b.ToTable("AppUserChatGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.UserGroupCard", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsAdmin") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("NickName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SilenceEnd") + .HasColumnType("timestamp without time zone"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserGroupCards", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ContentType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("NotificationTypeName") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Severity") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "NotificationName"); + + b.ToTable("AppNotifications", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationDefinitionGroupRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowSubscriptionToClients") + .HasColumnType("boolean"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("DisplayName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("AppNotificationDefinitionGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowSubscriptionToClients") + .HasColumnType("boolean"); + + b.Property("ContentType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("DisplayName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("NotificationLifetime") + .HasColumnType("integer"); + + b.Property("NotificationType") + .HasColumnType("integer"); + + b.Property("Providers") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Template") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.HasKey("Id"); + + b.ToTable("AppNotificationDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.UserNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("ReadStatus") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "NotificationId") + .HasDatabaseName("IX_Tenant_User_Notification_Id"); + + b.ToTable("AppUserNotifications", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.UserSubscribe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasDefaultValue("/"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "NotificationName") + .IsUnique() + .HasDatabaseName("IX_Tenant_User_Notification_Name"); + + b.ToTable("AppUserSubscribes", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Editions.Edition", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("EntityVersion") + .HasColumnType("integer"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.HasKey("Id"); + + b.HasIndex("DisplayName"); + + b.ToTable("AbpEditions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("DisableTime") + .HasColumnType("timestamp without time zone"); + + b.Property("EditionId") + .HasColumnType("uuid"); + + b.Property("EnableTime") + .HasColumnType("timestamp without time zone"); + + b.Property("EntityVersion") + .HasColumnType("integer"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NormalizedName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("EditionId"); + + b.HasIndex("Name"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpTenants", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.TenantConnectionString", b => + { + b.Property("TenantId") + .HasColumnType("uuid"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobAction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("JobId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("JobId"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("Name"); + + b.Property("Paramters") + .HasColumnType("text") + .HasColumnName("Paramters"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("TK_BackgroundJobActions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobInfo", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Args") + .HasColumnType("text") + .HasColumnName("Args"); + + b.Property("BeginTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Cron") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("Cron"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Description"); + + b.Property("EndTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Group") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("Group"); + + b.Property("Interval") + .HasColumnType("integer"); + + b.Property("IsAbandoned") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("JobType") + .HasColumnType("integer"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LastRunTime") + .HasColumnType("timestamp without time zone"); + + b.Property("LockTimeOut") + .HasColumnType("integer"); + + b.Property("MaxCount") + .HasColumnType("integer"); + + b.Property("MaxTryCount") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("Name"); + + b.Property("NextRunTime") + .HasColumnType("timestamp without time zone"); + + b.Property("NodeName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("NodeName"); + + b.Property("Priority") + .HasColumnType("integer"); + + b.Property("Result") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("Result"); + + b.Property("Source") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TriggerCount") + .HasColumnType("integer"); + + b.Property("TryCount") + .HasColumnType("integer"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("Type"); + + b.HasKey("Id"); + + b.HasIndex("Name", "Group"); + + b.ToTable("TK_BackgroundJobs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Exception") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("Exception"); + + b.Property("JobGroup") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("JobGroup"); + + b.Property("JobId") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("JobId"); + + b.Property("JobName") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("JobName"); + + b.Property("JobType") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("JobType"); + + b.Property("Message") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("Message"); + + b.Property("RunTime") + .HasColumnType("timestamp without time zone"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("JobGroup", "JobName"); + + b.ToTable("TK_BackgroundJobLogs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TextTemplating.TextTemplate", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Content") + .HasMaxLength(1048576) + .HasColumnType("character varying(1048576)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Culture") + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("Culture"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("DisplayName"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("Name"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Tenant_Text_Template_Name"); + + b.ToTable("AbpTextTemplates", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TextTemplating.TextTemplateDefinition", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("DefaultCultureName") + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("DefaultCultureName"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsInlineLocalized") + .HasColumnType("boolean"); + + b.Property("IsLayout") + .HasColumnType("boolean"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("Layout") + .HasMaxLength(60) + .HasColumnType("character varying(60)") + .HasColumnName("Layout"); + + b.Property("LocalizationResourceName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("LocalizationResourceName"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("Name"); + + b.Property("RenderEngine") + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("RenderEngine"); + + b.HasKey("Id"); + + b.ToTable("AbpTextTemplateDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("RequiredFeatures") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("GroupName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpWebhooksWebhooks", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookEventRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("Data") + .HasMaxLength(2147483647) + .HasColumnType("text") + .HasColumnName("Data"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.Property("WebhookName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("WebhookName"); + + b.HasKey("Id"); + + b.ToTable("AbpWebhooksEvents", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookGroupDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpWebhooksWebhookGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookSendRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("RequestHeaders") + .HasMaxLength(2147483647) + .HasColumnType("text") + .HasColumnName("RequestHeaders"); + + b.Property("Response") + .HasMaxLength(2147483647) + .HasColumnType("text") + .HasColumnName("Response"); + + b.Property("ResponseHeaders") + .HasMaxLength(2147483647) + .HasColumnType("text") + .HasColumnName("ResponseHeaders"); + + b.Property("ResponseStatusCode") + .HasColumnType("integer"); + + b.Property("SendExactSameData") + .HasColumnType("boolean"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.Property("WebhookEventId") + .HasColumnType("uuid"); + + b.Property("WebhookSubscriptionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("WebhookEventId"); + + b.ToTable("AbpWebhooksSendAttempts", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("Description"); + + b.Property("Headers") + .HasMaxLength(2147483647) + .HasColumnType("text") + .HasColumnName("Headers"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Secret") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("Secret"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.Property("TimeoutDuration") + .HasColumnType("integer"); + + b.Property("WebhookUri") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("WebhookUri"); + + b.Property("Webhooks") + .HasMaxLength(2147483647) + .HasColumnType("text") + .HasColumnName("Webhooks"); + + b.HasKey("Id"); + + b.ToTable("AbpWebhooksSubscriptions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.Data", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("Description"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("Name"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("AppPlatformDatas", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.DataItem", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowBeNull") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DataId") + .HasColumnType("uuid"); + + b.Property("DefaultValue") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DefaultValue"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("Description"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("Name"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("ValueType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DataId"); + + b.HasIndex("Name"); + + b.ToTable("AppPlatformDataItems", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.Feedback", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Category"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformFeedbacks", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("FeedbackId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Url"); + + b.HasKey("Id"); + + b.HasIndex("FeedbackId"); + + b.ToTable("AppPlatformFeedbackAttachments", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackComment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Capacity") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Capacity"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("FeedbackId") + .HasColumnType("uuid"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("FeedbackId"); + + b.ToTable("AppPlatformFeedbackComments", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Layouts.Layout", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DataId") + .HasColumnType("uuid"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Framework") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Framework"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("Path") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Path"); + + b.Property("Redirect") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Redirect"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformLayouts", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.Menu", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(23) + .HasColumnType("character varying(23)") + .HasColumnName("Code"); + + b.Property("Component") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Component"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Framework") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Framework"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsPublic") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LayoutId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("Path") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Path"); + + b.Property("Redirect") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Redirect"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.RoleMenu", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MenuId") + .HasColumnType("uuid"); + + b.Property("RoleName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("RoleName"); + + b.Property("Startup") + .HasColumnType("boolean"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleName", "MenuId"); + + b.ToTable("AppPlatformRoleMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.UserFavoriteMenu", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AliasName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("AliasName"); + + b.Property("Color") + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("Color"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("Framework") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Framework"); + + b.Property("Icon") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("Icon"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MenuId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Path"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "MenuId"); + + b.ToTable("AppPlatformUserFavoriteMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.UserMenu", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MenuId") + .HasColumnType("uuid"); + + b.Property("Startup") + .HasColumnType("boolean"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "MenuId"); + + b.ToTable("AppPlatformUserMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessage", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BodyTransferEncoding") + .HasColumnType("integer"); + + b.Property("CC") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("CC"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeliveryNotificationOptions") + .HasColumnType("integer"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("From") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("From"); + + b.Property("IsBodyHtml") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Normalize") + .HasColumnType("boolean"); + + b.Property("Priority") + .HasColumnType("integer"); + + b.Property("Provider") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("Provider"); + + b.Property("Reason") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Reason"); + + b.Property("Receiver") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("Receiver"); + + b.Property("SendCount") + .HasColumnType("integer"); + + b.Property("SendTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Sender") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Sender"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Subject") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("Subject"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformEmailMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BlobName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("BlobName"); + + b.Property("MessageId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Name"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("MessageId"); + + b.ToTable("AppPlatformEmailMessageAttachments", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Key"); + + b.Property("MessageId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("Value"); + + b.HasKey("Id"); + + b.HasIndex("MessageId"); + + b.ToTable("AppPlatformEmailMessageHeaders", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.SmsMessage", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Provider") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("Provider"); + + b.Property("Reason") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Reason"); + + b.Property("Receiver") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("Receiver"); + + b.Property("SendCount") + .HasColumnType("integer"); + + b.Property("SendTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Sender") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Sender"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformSmsMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.Package", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Authors") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("Authors"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Description"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("ForceUpdate") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Level") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Name"); + + b.Property("Note") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("Note"); + + b.Property("Version") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("Version"); + + b.HasKey("Id"); + + b.HasIndex("Name", "Version"); + + b.ToTable("AppPlatformPackages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.PackageBlob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Authors") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("Authors"); + + b.Property("ContentType") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("ContentType"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DownloadCount") + .HasColumnType("integer"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("License") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("License"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Name"); + + b.Property("PackageId") + .HasColumnType("uuid"); + + b.Property("SHA256") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("SHA256"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("Summary") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("Summary"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("Url") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("Url"); + + b.HasKey("Id"); + + b.HasIndex("PackageId", "Name"); + + b.ToTable("AppPlatformPackageBlobs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Portal.Enterprise", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Address") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Address"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("EnglishName") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("EnglishName"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LegalMan") + .HasMaxLength(60) + .HasColumnType("character varying(60)") + .HasColumnName("LegalMan"); + + b.Property("Logo") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("Logo"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Name"); + + b.Property("OrganizationCode") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("OrganizationCode"); + + b.Property("RegistrationCode") + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("RegistrationCode"); + + b.Property("RegistrationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("TaxCode") + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("TaxCode"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformEnterprises", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("character varying(96)") + .HasColumnName("ApplicationName"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("BrowserInfo"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("ClientId"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("ClientIpAddress"); + + b.Property("ClientName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("ClientName"); + + b.Property("Comments") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Comments"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("CorrelationId"); + + b.Property("Exceptions") + .HasColumnType("text"); + + b.Property("ExecutionDuration") + .HasColumnType("integer") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("HttpMethod"); + + b.Property("HttpStatusCode") + .HasColumnType("integer") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnType("uuid") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorTenantName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("ImpersonatorTenantName"); + + b.Property("ImpersonatorUserId") + .HasColumnType("uuid") + .HasColumnName("ImpersonatorUserId"); + + b.Property("ImpersonatorUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("ImpersonatorUserName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("TenantName"); + + b.Property("Url") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Url"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AuditLogId") + .HasColumnType("uuid") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnType("integer") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("MethodName"); + + b.Property("Parameters") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("Parameters"); + + b.Property("ServiceName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("ServiceName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogExcelFile", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("FileName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("FileName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpAuditLogExcelFiles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AuditLogId") + .HasColumnType("uuid") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnType("smallint") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("EntityId"); + + b.Property("EntityTenantId") + .HasColumnType("uuid"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("EntityTypeFullName"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("EntityChangeId") + .HasColumnType("uuid"); + + b.Property("NewValue") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("NewValue"); + + b.Property("OriginalValue") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("OriginalValue"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("PropertyName"); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("PropertyTypeFullName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowedProviders") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("DefaultValue") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("IsAvailableToHost") + .HasColumnType("boolean"); + + b.Property("IsVisibleToClients") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ParentName") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ValueType") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.HasKey("Id"); + + b.HasIndex("GroupName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpFeatures", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpFeatureGroups", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpFeatureValues", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Regex") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("RegexDescription") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ValueType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("SourceTenantId") + .HasColumnType("uuid"); + + b.Property("SourceUserId") + .HasColumnType("uuid"); + + b.Property("TargetTenantId") + .HasColumnType("uuid"); + + b.Property("TargetUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SourceUserId", "SourceTenantId", "TargetUserId", "TargetTenantId") + .IsUnique(); + + b.ToTable("AbpLinkUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("EntityVersion") + .HasColumnType("integer"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnType("boolean") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnType("boolean") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnType("boolean") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Action") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Identity") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Action"); + + b.HasIndex("TenantId", "ApplicationName"); + + b.HasIndex("TenantId", "Identity"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSecurityLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySession", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Device") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("DeviceInfo") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IpAddresses") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("LastAccessed") + .HasColumnType("timestamp without time zone"); + + b.Property("SessionId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("SignedIn") + .HasColumnType("timestamp without time zone"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("Device"); + + b.HasIndex("SessionId"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSessions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Email"); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("EmailConfirmed"); + + b.Property("EntityVersion") + .HasColumnType("integer"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("boolean") + .HasColumnName("IsActive"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsExternal") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsExternal"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LastPasswordChangeTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("LockoutEnabled"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("NormalizedEmail"); + + b.Property("NormalizedUserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("NormalizedUserName"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("PasswordHash"); + + b.Property("PhoneNumber") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("PhoneNumber"); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("PhoneNumberConfirmed"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("SecurityStamp"); + + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("boolean"); + + b.Property("Surname") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Surname"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("TwoFactorEnabled"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("NormalizedEmail"); + + b.HasIndex("NormalizedUserName"); + + b.HasIndex("UserName"); + + b.ToTable("AbpUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserDelegation", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("EndTime") + .HasColumnType("timestamp without time zone"); + + b.Property("SourceUserId") + .HasColumnType("uuid"); + + b.Property("StartTime") + .HasColumnType("timestamp without time zone"); + + b.Property("TargetUserId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpUserDelegations", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196) + .HasColumnType("character varying(196)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(95) + .HasColumnType("character varying(95)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("EntityVersion") + .HasColumnType("integer"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientUri") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutUri") + .HasColumnType("text"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("OpenIddictApplications", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Scopes.OpenIddictScope", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("OpenIddictScopes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Tokens.OpenIddictToken", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("AuthorizationId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("MultiTenancySide") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ParentName") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Providers") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("GroupName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpPermissions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpPermissionGrants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGroupDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpPermissionGroups", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpSettings", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.SettingDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("DefaultValue") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsEncrypted") + .HasColumnType("boolean"); + + b.Property("IsInherited") + .HasColumnType("boolean"); + + b.Property("IsVisibleToClients") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Providers") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpSettingDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityEnumInfo", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", "PropertyInfo") + .WithMany("Enums") + .HasForeignKey("PropertyInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PropertyInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", "TypeInfo") + .WithMany("Properties") + .HasForeignKey("TypeInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TypeInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.OrganizationUnitEntityRule", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", "EntityTypeInfo") + .WithMany() + .HasForeignKey("EntityTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EntityTypeInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.RoleEntityRule", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", "EntityTypeInfo") + .WithMany() + .HasForeignKey("EntityTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EntityTypeInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.Book", b => + { + b.HasOne("LINGYUN.Abp.Demo.Authors.Author", null) + .WithMany() + .HasForeignKey("AuthorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.BookAuth", b => + { + b.HasOne("LINGYUN.Abp.Demo.Books.Book", "Entity") + .WithMany() + .HasForeignKey("EntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Entity"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprInfo", b => + { + b.HasOne("LINGYUN.Abp.Gdpr.GdprRequest", null) + .WithMany("Infos") + .HasForeignKey("RequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.HasOne("LINGYUN.Abp.Saas.Editions.Edition", "Edition") + .WithMany() + .HasForeignKey("EditionId"); + + b.Navigation("Edition"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.TenantConnectionString", b => + { + b.HasOne("LINGYUN.Abp.Saas.Tenants.Tenant", null) + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookSendRecord", b => + { + b.HasOne("LINGYUN.Abp.WebhooksManagement.WebhookEventRecord", "WebhookEvent") + .WithOne() + .HasForeignKey("LINGYUN.Abp.WebhooksManagement.WebhookSendRecord", "WebhookEventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebhookEvent"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.DataItem", b => + { + b.HasOne("LINGYUN.Platform.Datas.Data", null) + .WithMany("Items") + .HasForeignKey("DataId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackAttachment", b => + { + b.HasOne("LINGYUN.Platform.Feedbacks.Feedback", null) + .WithMany("Attachments") + .HasForeignKey("FeedbackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackComment", b => + { + b.HasOne("LINGYUN.Platform.Feedbacks.Feedback", null) + .WithMany("Comments") + .HasForeignKey("FeedbackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageAttachment", b => + { + b.HasOne("LINGYUN.Platform.Messages.EmailMessage", null) + .WithMany("Attachments") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageHeader", b => + { + b.HasOne("LINGYUN.Platform.Messages.EmailMessage", null) + .WithMany("Headers") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.PackageBlob", b => + { + b.HasOne("LINGYUN.Platform.Packages.Package", "Package") + .WithMany("Blobs") + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Package"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange", null) + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", b => + { + b.HasOne("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", null) + .WithMany() + .HasForeignKey("ApplicationId"); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Tokens.OpenIddictToken", b => + { + b.HasOne("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", null) + .WithMany() + .HasForeignKey("ApplicationId"); + + b.HasOne("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", null) + .WithMany() + .HasForeignKey("AuthorizationId"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", b => + { + b.Navigation("Enums"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", b => + { + b.Navigation("Properties"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprRequest", b => + { + b.Navigation("Infos"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.Navigation("ConnectionStrings"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.Data", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.Feedback", b => + { + b.Navigation("Attachments"); + + b.Navigation("Comments"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessage", b => + { + b.Navigation("Attachments"); + + b.Navigation("Headers"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.Package", b => + { + b.Navigation("Blobs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Navigation("Actions"); + + b.Navigation("EntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Navigation("PropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Navigation("Claims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("OrganizationUnits"); + + b.Navigation("Roles"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Navigation("Roles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260316031109_Add-AI-Management-Module.cs b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260316031109_Add-AI-Management-Module.cs new file mode 100644 index 000000000..e26f327e9 --- /dev/null +++ b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260316031109_Add-AI-Management-Module.cs @@ -0,0 +1,150 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql.Migrations +{ + /// + public partial class AddAIManagementModule : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AbpAIConversations", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + TenantId = table.Column(type: "uuid", nullable: true), + Name = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + Workspace = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), + ExpiredAt = table.Column(type: "timestamp without time zone", nullable: false), + UpdateAt = table.Column(type: "timestamp without time zone", nullable: true), + CreationTime = table.Column(type: "timestamp without time zone", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: true), + LastModificationTime = table.Column(type: "timestamp without time zone", nullable: true), + LastModifierId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAIConversations", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpAITextChatMessages", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Content = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + ExtraProperties = table.Column(type: "text", nullable: false), + ConcurrencyStamp = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + CreationTime = table.Column(type: "timestamp without time zone", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: true), + LastModificationTime = table.Column(type: "timestamp without time zone", nullable: true), + LastModifierId = table.Column(type: "uuid", nullable: true), + TenantId = table.Column(type: "uuid", nullable: true), + Workspace = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + Role = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), + CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), + UserId = table.Column(type: "uuid", nullable: true), + ConversationId = table.Column(type: "uuid", nullable: true), + ReplyMessage = table.Column(type: "text", nullable: true), + ReplyAt = table.Column(type: "timestamp without time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAITextChatMessages", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpAITokenUsages", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + TenantId = table.Column(type: "uuid", nullable: true), + MessageId = table.Column(type: "uuid", nullable: true), + ConversationId = table.Column(type: "uuid", nullable: true), + InputTokenCount = table.Column(type: "bigint", nullable: true), + OutputTokenCount = table.Column(type: "bigint", nullable: true), + TotalTokenCount = table.Column(type: "bigint", nullable: true), + CachedInputTokenCount = table.Column(type: "bigint", nullable: true), + ReasoningTokenCount = table.Column(type: "bigint", nullable: true), + CreationTime = table.Column(type: "timestamp without time zone", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: true), + LastModificationTime = table.Column(type: "timestamp without time zone", nullable: true), + LastModifierId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAITokenUsages", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpAIWorkspaceDefinitions", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + Provider = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), + ModelName = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + DisplayName = table.Column(type: "character varying(128)", maxLength: 128, nullable: false), + Description = table.Column(type: "character varying(128)", maxLength: 128, nullable: true), + ApiKey = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + ApiBaseUrl = table.Column(type: "character varying(128)", maxLength: 128, nullable: true), + SystemPrompt = table.Column(type: "character varying(512)", maxLength: 512, nullable: true), + Instructions = table.Column(type: "character varying(512)", maxLength: 512, nullable: true), + Temperature = table.Column(type: "real", nullable: true), + MaxOutputTokens = table.Column(type: "integer", nullable: true), + FrequencyPenalty = table.Column(type: "real", nullable: true), + PresencePenalty = table.Column(type: "real", nullable: true), + IsEnabled = table.Column(type: "boolean", nullable: false), + IsSystem = table.Column(type: "boolean", nullable: false), + StateCheckers = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + ExtraProperties = table.Column(type: "text", nullable: false), + ConcurrencyStamp = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + CreationTime = table.Column(type: "timestamp without time zone", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: true), + LastModificationTime = table.Column(type: "timestamp without time zone", nullable: true), + LastModifierId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAIWorkspaceDefinitions", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAITextChatMessages_TenantId_ConversationId", + table: "AbpAITextChatMessages", + columns: new[] { "TenantId", "ConversationId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAITokenUsages_TenantId_ConversationId", + table: "AbpAITokenUsages", + columns: new[] { "TenantId", "ConversationId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAIWorkspaceDefinitions_Name", + table: "AbpAIWorkspaceDefinitions", + column: "Name", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AbpAIConversations"); + + migrationBuilder.DropTable( + name: "AbpAITextChatMessages"); + + migrationBuilder.DropTable( + name: "AbpAITokenUsages"); + + migrationBuilder.DropTable( + name: "AbpAIWorkspaceDefinitions"); + } + } +} diff --git a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260324062229_Add-Notification-Send-Record.Designer.cs b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260324062229_Add-Notification-Send-Record.Designer.cs new file mode 100644 index 000000000..53106ad7b --- /dev/null +++ b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260324062229_Add-Notification-Send-Record.Designer.cs @@ -0,0 +1,5749 @@ +// +using System; +using LY.MicroService.Applications.Single.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql.Migrations +{ + [DbContext(typeof(SingleMigrationsDbContext))] + [Migration("20260324062229_Add-Notification-Send-Record")] + partial class AddNotificationSendRecord + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.ConversationRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp without time zone"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UpdateAt") + .HasColumnType("timestamp without time zone"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.ToTable("AbpAIConversations", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.TextChatMessageRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ReplyAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ReplyMessage") + .HasColumnType("text"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITextChatMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tokens.TokenUsageRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CachedInputTokenCount") + .HasColumnType("bigint"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("InputTokenCount") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MessageId") + .HasColumnType("uuid"); + + b.Property("OutputTokenCount") + .HasColumnType("bigint"); + + b.Property("ReasoningTokenCount") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TotalTokenCount") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITokenUsages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiBaseUrl") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ApiKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrequencyPenalty") + .HasColumnType("real"); + + b.Property("Instructions") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("IsSystem") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MaxOutputTokens") + .HasColumnType("integer"); + + b.Property("ModelName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PresencePenalty") + .HasColumnType("real"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SystemPrompt") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Temperature") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpAIWorkspaceDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityEnumInfo", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("PropertyInfoId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasColumnName("Value"); + + b.HasKey("Id"); + + b.HasIndex("PropertyInfoId", "Name"); + + b.ToTable("AbpAuthEntityEnums", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("JavaScriptType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("JavaScriptType"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("TypeFullName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("TypeFullName"); + + b.Property("TypeInfoId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TypeInfoId", "TypeFullName"); + + b.ToTable("AbpAuthEntityProperties", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsAuditEnabled") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("TypeFullName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("TypeFullName"); + + b.HasKey("Id"); + + b.HasIndex("TypeFullName"); + + b.ToTable("AbpAuthEntitites", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.OrganizationUnitEntityRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessedProperties") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("AccessedProperties"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("EntityTypeFullName") + .HasColumnType("text"); + + b.Property("EntityTypeId") + .HasColumnType("uuid"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FilterGroup") + .HasColumnType("text") + .HasColumnName("FilterGroup"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Operation") + .HasColumnType("integer"); + + b.Property("OrgCode") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("OrgCode"); + + b.Property("OrgId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityTypeId"); + + b.ToTable("AbpAuthOrganizationUnitEntityRules", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.RoleEntityRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessedProperties") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("AccessedProperties"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("EntityTypeFullName") + .HasColumnType("text"); + + b.Property("EntityTypeId") + .HasColumnType("uuid"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FilterGroup") + .HasColumnType("text") + .HasColumnName("FilterGroup"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Operation") + .HasColumnType("integer"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("RoleName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("RoleName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityTypeId"); + + b.ToTable("AbpAuthRoleEntityRules", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.SubjectStrategy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Strategy") + .HasColumnType("integer"); + + b.Property("SubjectId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("SubjectId"); + + b.Property("SubjectName") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("SubjectName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpAuthSubjectStrategys", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Authors.Author", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ShortBio") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("Demo_Authors", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.Book", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AuthorId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Price") + .HasColumnType("real"); + + b.Property("PublishDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AuthorId"); + + b.ToTable("Demo_Books", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.BookAuth", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EntityId") + .HasMaxLength(64) + .HasColumnType("uuid") + .HasColumnName("EntityId"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("EntityType"); + + b.Property("OrganizationUnit") + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("OrganizationUnit"); + + b.Property("Role") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("Role"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("EntityId"); + + b.HasIndex("OrganizationUnit"); + + b.HasIndex("Role"); + + b.ToTable("Demo_BooksAuths", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprInfo", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Data") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Data"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Provider"); + + b.Property("RequestId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RequestId"); + + b.ToTable("AbpGdprInfos", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("ReadyTime") + .HasColumnType("timestamp without time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpGdprRequests", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Language", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("CultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("CultureName"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("DisplayName"); + + b.Property("Enable") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("TwoLetterISOLanguageName") + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("TwoLetterISOLanguageName"); + + b.Property("UiCultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("UiCultureName"); + + b.HasKey("Id"); + + b.HasIndex("CultureName"); + + b.ToTable("AbpLocalizationLanguages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Resource", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DefaultCultureName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("DefaultCultureName"); + + b.Property("Description") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Description"); + + b.Property("DisplayName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("DisplayName"); + + b.Property("Enable") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("Name"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("AbpLocalizationResources", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Text", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("CultureName"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("Key"); + + b.Property("ResourceName") + .HasColumnType("text"); + + b.Property("Value") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)") + .HasColumnName("Value"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("AbpLocalizationTexts", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatCard", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Age") + .HasColumnType("integer"); + + b.Property("AvatarUrl") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Birthday") + .HasColumnType("timestamp without time zone"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LastOnlineTime") + .HasColumnType("timestamp without time zone"); + + b.Property("NickName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Sex") + .HasColumnType("integer"); + + b.Property("Sign") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("State") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserChatCards", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatFriend", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Black") + .HasColumnType("boolean"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DontDisturb") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrientId") + .HasColumnType("uuid"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("RemarkName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SpecialFocus") + .HasColumnType("boolean"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "FrientId"); + + b.ToTable("AppUserChatFriends", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AllowAddFriend") + .HasColumnType("boolean"); + + b.Property("AllowAnonymous") + .HasColumnType("boolean"); + + b.Property("AllowReceiveMessage") + .HasColumnType("boolean"); + + b.Property("AllowSendMessage") + .HasColumnType("boolean"); + + b.Property("RequireAddFriendValition") + .HasColumnType("boolean"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserChatSettings", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("character varying(1048576)"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("MessageId") + .HasColumnType("bigint"); + + b.Property("ReceiveUserId") + .HasColumnType("uuid"); + + b.Property("SendUserName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Source") + .HasColumnType("integer"); + + b.Property("State") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ReceiveUserId"); + + b.ToTable("AppUserMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.ChatGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("AdminUserId") + .HasColumnType("uuid"); + + b.Property("AllowAnonymous") + .HasColumnType("boolean"); + + b.Property("AllowSendMessage") + .HasColumnType("boolean"); + + b.Property("AvatarUrl") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MaxUserCount") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Notice") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Tag") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("AppChatGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.GroupChatBlack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("ShieldUserId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId"); + + b.ToTable("AppGroupChatBlacks", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.GroupMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("character varying(1048576)"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("MessageId") + .HasColumnType("bigint"); + + b.Property("SendUserName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Source") + .HasColumnType("integer"); + + b.Property("State") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId"); + + b.ToTable("AppGroupMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.UserChatGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId", "UserId"); + + b.ToTable("AppUserChatGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.UserGroupCard", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsAdmin") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("NickName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SilenceEnd") + .HasColumnType("timestamp without time zone"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserGroupCards", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ContentType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("NotificationTypeName") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Severity") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "NotificationName"); + + b.ToTable("AppNotifications", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationDefinitionGroupRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowSubscriptionToClients") + .HasColumnType("boolean"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("DisplayName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("AppNotificationDefinitionGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowSubscriptionToClients") + .HasColumnType("boolean"); + + b.Property("ContentType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("DisplayName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("NotificationLifetime") + .HasColumnType("integer"); + + b.Property("NotificationType") + .HasColumnType("integer"); + + b.Property("Providers") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Template") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.HasKey("Id"); + + b.ToTable("AppNotificationDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationSendRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Reason") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SendTime") + .HasColumnType("timestamp without time zone"); + + b.Property("State") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasDefaultValue("/"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "NotificationName") + .HasDatabaseName("IX_Tenant_Send_Notification_Name"); + + b.ToTable("AppNotificationSendRecords", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.UserNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("ReadStatus") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "NotificationId") + .HasDatabaseName("IX_Tenant_User_Notification_Id"); + + b.ToTable("AppUserNotifications", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.UserSubscribe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasDefaultValue("/"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "NotificationName") + .IsUnique() + .HasDatabaseName("IX_Tenant_User_Notification_Name"); + + b.ToTable("AppUserSubscribes", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Editions.Edition", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("EntityVersion") + .HasColumnType("integer"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.HasKey("Id"); + + b.HasIndex("DisplayName"); + + b.ToTable("AbpEditions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("DisableTime") + .HasColumnType("timestamp without time zone"); + + b.Property("EditionId") + .HasColumnType("uuid"); + + b.Property("EnableTime") + .HasColumnType("timestamp without time zone"); + + b.Property("EntityVersion") + .HasColumnType("integer"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NormalizedName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("EditionId"); + + b.HasIndex("Name"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpTenants", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.TenantConnectionString", b => + { + b.Property("TenantId") + .HasColumnType("uuid"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobAction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("JobId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("JobId"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("Name"); + + b.Property("Paramters") + .HasColumnType("text") + .HasColumnName("Paramters"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("TK_BackgroundJobActions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobInfo", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Args") + .HasColumnType("text") + .HasColumnName("Args"); + + b.Property("BeginTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Cron") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("Cron"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Description"); + + b.Property("EndTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Group") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("Group"); + + b.Property("Interval") + .HasColumnType("integer"); + + b.Property("IsAbandoned") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("JobType") + .HasColumnType("integer"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LastRunTime") + .HasColumnType("timestamp without time zone"); + + b.Property("LockTimeOut") + .HasColumnType("integer"); + + b.Property("MaxCount") + .HasColumnType("integer"); + + b.Property("MaxTryCount") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("Name"); + + b.Property("NextRunTime") + .HasColumnType("timestamp without time zone"); + + b.Property("NodeName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("NodeName"); + + b.Property("Priority") + .HasColumnType("integer"); + + b.Property("Result") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("Result"); + + b.Property("Source") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TriggerCount") + .HasColumnType("integer"); + + b.Property("TryCount") + .HasColumnType("integer"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("Type"); + + b.HasKey("Id"); + + b.HasIndex("Name", "Group"); + + b.ToTable("TK_BackgroundJobs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Exception") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("Exception"); + + b.Property("JobGroup") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("JobGroup"); + + b.Property("JobId") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("JobId"); + + b.Property("JobName") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("JobName"); + + b.Property("JobType") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("JobType"); + + b.Property("Message") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("Message"); + + b.Property("RunTime") + .HasColumnType("timestamp without time zone"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("JobGroup", "JobName"); + + b.ToTable("TK_BackgroundJobLogs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TextTemplating.TextTemplate", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Content") + .HasMaxLength(1048576) + .HasColumnType("character varying(1048576)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Culture") + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("Culture"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("DisplayName"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("Name"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Tenant_Text_Template_Name"); + + b.ToTable("AbpTextTemplates", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TextTemplating.TextTemplateDefinition", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("DefaultCultureName") + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("DefaultCultureName"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsInlineLocalized") + .HasColumnType("boolean"); + + b.Property("IsLayout") + .HasColumnType("boolean"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("Layout") + .HasMaxLength(60) + .HasColumnType("character varying(60)") + .HasColumnName("Layout"); + + b.Property("LocalizationResourceName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("LocalizationResourceName"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("Name"); + + b.Property("RenderEngine") + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("RenderEngine"); + + b.HasKey("Id"); + + b.ToTable("AbpTextTemplateDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("RequiredFeatures") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("GroupName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpWebhooksWebhooks", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookEventRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("Data") + .HasMaxLength(2147483647) + .HasColumnType("text") + .HasColumnName("Data"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.Property("WebhookName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("WebhookName"); + + b.HasKey("Id"); + + b.ToTable("AbpWebhooksEvents", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookGroupDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpWebhooksWebhookGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookSendRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("RequestHeaders") + .HasMaxLength(2147483647) + .HasColumnType("text") + .HasColumnName("RequestHeaders"); + + b.Property("Response") + .HasMaxLength(2147483647) + .HasColumnType("text") + .HasColumnName("Response"); + + b.Property("ResponseHeaders") + .HasMaxLength(2147483647) + .HasColumnType("text") + .HasColumnName("ResponseHeaders"); + + b.Property("ResponseStatusCode") + .HasColumnType("integer"); + + b.Property("SendExactSameData") + .HasColumnType("boolean"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.Property("WebhookEventId") + .HasColumnType("uuid"); + + b.Property("WebhookSubscriptionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("WebhookEventId"); + + b.ToTable("AbpWebhooksSendAttempts", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("Description"); + + b.Property("Headers") + .HasMaxLength(2147483647) + .HasColumnType("text") + .HasColumnName("Headers"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Secret") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("Secret"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.Property("TimeoutDuration") + .HasColumnType("integer"); + + b.Property("WebhookUri") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("WebhookUri"); + + b.Property("Webhooks") + .HasMaxLength(2147483647) + .HasColumnType("text") + .HasColumnName("Webhooks"); + + b.HasKey("Id"); + + b.ToTable("AbpWebhooksSubscriptions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.Data", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("Description"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("Name"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("AppPlatformDatas", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.DataItem", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowBeNull") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DataId") + .HasColumnType("uuid"); + + b.Property("DefaultValue") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DefaultValue"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("Description"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("Name"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("ValueType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DataId"); + + b.HasIndex("Name"); + + b.ToTable("AppPlatformDataItems", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.Feedback", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Category"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformFeedbacks", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("FeedbackId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Url"); + + b.HasKey("Id"); + + b.HasIndex("FeedbackId"); + + b.ToTable("AppPlatformFeedbackAttachments", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackComment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Capacity") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Capacity"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("FeedbackId") + .HasColumnType("uuid"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("FeedbackId"); + + b.ToTable("AppPlatformFeedbackComments", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Layouts.Layout", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DataId") + .HasColumnType("uuid"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Framework") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Framework"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("Path") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Path"); + + b.Property("Redirect") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Redirect"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformLayouts", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.Menu", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(23) + .HasColumnType("character varying(23)") + .HasColumnName("Code"); + + b.Property("Component") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Component"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Framework") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Framework"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsPublic") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LayoutId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("Path") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Path"); + + b.Property("Redirect") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Redirect"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.RoleMenu", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MenuId") + .HasColumnType("uuid"); + + b.Property("RoleName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("RoleName"); + + b.Property("Startup") + .HasColumnType("boolean"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleName", "MenuId"); + + b.ToTable("AppPlatformRoleMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.UserFavoriteMenu", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AliasName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("AliasName"); + + b.Property("Color") + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("Color"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("Framework") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Framework"); + + b.Property("Icon") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("Icon"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MenuId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Path"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "MenuId"); + + b.ToTable("AppPlatformUserFavoriteMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.UserMenu", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MenuId") + .HasColumnType("uuid"); + + b.Property("Startup") + .HasColumnType("boolean"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "MenuId"); + + b.ToTable("AppPlatformUserMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessage", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BodyTransferEncoding") + .HasColumnType("integer"); + + b.Property("CC") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("CC"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeliveryNotificationOptions") + .HasColumnType("integer"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("From") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("From"); + + b.Property("IsBodyHtml") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Normalize") + .HasColumnType("boolean"); + + b.Property("Priority") + .HasColumnType("integer"); + + b.Property("Provider") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("Provider"); + + b.Property("Reason") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Reason"); + + b.Property("Receiver") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("Receiver"); + + b.Property("SendCount") + .HasColumnType("integer"); + + b.Property("SendTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Sender") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Sender"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Subject") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("Subject"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformEmailMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BlobName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("BlobName"); + + b.Property("MessageId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Name"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("MessageId"); + + b.ToTable("AppPlatformEmailMessageAttachments", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Key"); + + b.Property("MessageId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("Value"); + + b.HasKey("Id"); + + b.HasIndex("MessageId"); + + b.ToTable("AppPlatformEmailMessageHeaders", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.SmsMessage", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Provider") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("Provider"); + + b.Property("Reason") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Reason"); + + b.Property("Receiver") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("Receiver"); + + b.Property("SendCount") + .HasColumnType("integer"); + + b.Property("SendTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Sender") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Sender"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformSmsMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.Package", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Authors") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("Authors"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Description"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("ForceUpdate") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Level") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Name"); + + b.Property("Note") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("Note"); + + b.Property("Version") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("Version"); + + b.HasKey("Id"); + + b.HasIndex("Name", "Version"); + + b.ToTable("AppPlatformPackages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.PackageBlob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Authors") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("Authors"); + + b.Property("ContentType") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("ContentType"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DownloadCount") + .HasColumnType("integer"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("License") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("License"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Name"); + + b.Property("PackageId") + .HasColumnType("uuid"); + + b.Property("SHA256") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("SHA256"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("Summary") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("Summary"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("Url") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("Url"); + + b.HasKey("Id"); + + b.HasIndex("PackageId", "Name"); + + b.ToTable("AppPlatformPackageBlobs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Portal.Enterprise", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Address") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Address"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("EnglishName") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("EnglishName"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LegalMan") + .HasMaxLength(60) + .HasColumnType("character varying(60)") + .HasColumnName("LegalMan"); + + b.Property("Logo") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("Logo"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Name"); + + b.Property("OrganizationCode") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("OrganizationCode"); + + b.Property("RegistrationCode") + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("RegistrationCode"); + + b.Property("RegistrationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("TaxCode") + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("TaxCode"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformEnterprises", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("character varying(96)") + .HasColumnName("ApplicationName"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("BrowserInfo"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("ClientId"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("ClientIpAddress"); + + b.Property("ClientName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("ClientName"); + + b.Property("Comments") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Comments"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("CorrelationId"); + + b.Property("Exceptions") + .HasColumnType("text"); + + b.Property("ExecutionDuration") + .HasColumnType("integer") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("HttpMethod"); + + b.Property("HttpStatusCode") + .HasColumnType("integer") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnType("uuid") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorTenantName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("ImpersonatorTenantName"); + + b.Property("ImpersonatorUserId") + .HasColumnType("uuid") + .HasColumnName("ImpersonatorUserId"); + + b.Property("ImpersonatorUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("ImpersonatorUserName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("TenantName"); + + b.Property("Url") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Url"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AuditLogId") + .HasColumnType("uuid") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnType("integer") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("MethodName"); + + b.Property("Parameters") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("Parameters"); + + b.Property("ServiceName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("ServiceName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogExcelFile", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("FileName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("FileName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpAuditLogExcelFiles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AuditLogId") + .HasColumnType("uuid") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnType("smallint") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("EntityId"); + + b.Property("EntityTenantId") + .HasColumnType("uuid"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("EntityTypeFullName"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("EntityChangeId") + .HasColumnType("uuid"); + + b.Property("NewValue") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("NewValue"); + + b.Property("OriginalValue") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("OriginalValue"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("PropertyName"); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("PropertyTypeFullName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowedProviders") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("DefaultValue") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("IsAvailableToHost") + .HasColumnType("boolean"); + + b.Property("IsVisibleToClients") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ParentName") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ValueType") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.HasKey("Id"); + + b.HasIndex("GroupName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpFeatures", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpFeatureGroups", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpFeatureValues", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Regex") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("RegexDescription") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ValueType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("SourceTenantId") + .HasColumnType("uuid"); + + b.Property("SourceUserId") + .HasColumnType("uuid"); + + b.Property("TargetTenantId") + .HasColumnType("uuid"); + + b.Property("TargetUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SourceUserId", "SourceTenantId", "TargetUserId", "TargetTenantId") + .IsUnique(); + + b.ToTable("AbpLinkUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("EntityVersion") + .HasColumnType("integer"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnType("boolean") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnType("boolean") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnType("boolean") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Action") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Identity") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Action"); + + b.HasIndex("TenantId", "ApplicationName"); + + b.HasIndex("TenantId", "Identity"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSecurityLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySession", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Device") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("DeviceInfo") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IpAddresses") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("LastAccessed") + .HasColumnType("timestamp without time zone"); + + b.Property("SessionId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("SignedIn") + .HasColumnType("timestamp without time zone"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("Device"); + + b.HasIndex("SessionId"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSessions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Email"); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("EmailConfirmed"); + + b.Property("EntityVersion") + .HasColumnType("integer"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("boolean") + .HasColumnName("IsActive"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsExternal") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsExternal"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LastPasswordChangeTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("LockoutEnabled"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("NormalizedEmail"); + + b.Property("NormalizedUserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("NormalizedUserName"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("PasswordHash"); + + b.Property("PhoneNumber") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("PhoneNumber"); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("PhoneNumberConfirmed"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("SecurityStamp"); + + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("boolean"); + + b.Property("Surname") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Surname"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("TwoFactorEnabled"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("NormalizedEmail"); + + b.HasIndex("NormalizedUserName"); + + b.HasIndex("UserName"); + + b.ToTable("AbpUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserDelegation", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("EndTime") + .HasColumnType("timestamp without time zone"); + + b.Property("SourceUserId") + .HasColumnType("uuid"); + + b.Property("StartTime") + .HasColumnType("timestamp without time zone"); + + b.Property("TargetUserId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpUserDelegations", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196) + .HasColumnType("character varying(196)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(95) + .HasColumnType("character varying(95)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("EntityVersion") + .HasColumnType("integer"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientUri") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutUri") + .HasColumnType("text"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("OpenIddictApplications", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Scopes.OpenIddictScope", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("OpenIddictScopes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Tokens.OpenIddictToken", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("AuthorizationId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("MultiTenancySide") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ParentName") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Providers") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("GroupName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpPermissions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpPermissionGrants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGroupDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpPermissionGroups", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpSettings", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.SettingDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("DefaultValue") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsEncrypted") + .HasColumnType("boolean"); + + b.Property("IsInherited") + .HasColumnType("boolean"); + + b.Property("IsVisibleToClients") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Providers") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpSettingDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityEnumInfo", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", "PropertyInfo") + .WithMany("Enums") + .HasForeignKey("PropertyInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PropertyInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", "TypeInfo") + .WithMany("Properties") + .HasForeignKey("TypeInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TypeInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.OrganizationUnitEntityRule", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", "EntityTypeInfo") + .WithMany() + .HasForeignKey("EntityTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EntityTypeInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.RoleEntityRule", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", "EntityTypeInfo") + .WithMany() + .HasForeignKey("EntityTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EntityTypeInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.Book", b => + { + b.HasOne("LINGYUN.Abp.Demo.Authors.Author", null) + .WithMany() + .HasForeignKey("AuthorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.BookAuth", b => + { + b.HasOne("LINGYUN.Abp.Demo.Books.Book", "Entity") + .WithMany() + .HasForeignKey("EntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Entity"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprInfo", b => + { + b.HasOne("LINGYUN.Abp.Gdpr.GdprRequest", null) + .WithMany("Infos") + .HasForeignKey("RequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.HasOne("LINGYUN.Abp.Saas.Editions.Edition", "Edition") + .WithMany() + .HasForeignKey("EditionId"); + + b.Navigation("Edition"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.TenantConnectionString", b => + { + b.HasOne("LINGYUN.Abp.Saas.Tenants.Tenant", null) + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookSendRecord", b => + { + b.HasOne("LINGYUN.Abp.WebhooksManagement.WebhookEventRecord", "WebhookEvent") + .WithOne() + .HasForeignKey("LINGYUN.Abp.WebhooksManagement.WebhookSendRecord", "WebhookEventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebhookEvent"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.DataItem", b => + { + b.HasOne("LINGYUN.Platform.Datas.Data", null) + .WithMany("Items") + .HasForeignKey("DataId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackAttachment", b => + { + b.HasOne("LINGYUN.Platform.Feedbacks.Feedback", null) + .WithMany("Attachments") + .HasForeignKey("FeedbackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackComment", b => + { + b.HasOne("LINGYUN.Platform.Feedbacks.Feedback", null) + .WithMany("Comments") + .HasForeignKey("FeedbackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageAttachment", b => + { + b.HasOne("LINGYUN.Platform.Messages.EmailMessage", null) + .WithMany("Attachments") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageHeader", b => + { + b.HasOne("LINGYUN.Platform.Messages.EmailMessage", null) + .WithMany("Headers") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.PackageBlob", b => + { + b.HasOne("LINGYUN.Platform.Packages.Package", "Package") + .WithMany("Blobs") + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Package"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange", null) + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", b => + { + b.HasOne("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", null) + .WithMany() + .HasForeignKey("ApplicationId"); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Tokens.OpenIddictToken", b => + { + b.HasOne("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", null) + .WithMany() + .HasForeignKey("ApplicationId"); + + b.HasOne("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", null) + .WithMany() + .HasForeignKey("AuthorizationId"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", b => + { + b.Navigation("Enums"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", b => + { + b.Navigation("Properties"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprRequest", b => + { + b.Navigation("Infos"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.Navigation("ConnectionStrings"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.Data", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.Feedback", b => + { + b.Navigation("Attachments"); + + b.Navigation("Comments"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessage", b => + { + b.Navigation("Attachments"); + + b.Navigation("Headers"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.Package", b => + { + b.Navigation("Blobs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Navigation("Actions"); + + b.Navigation("EntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Navigation("PropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Navigation("Claims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("OrganizationUnits"); + + b.Navigation("Roles"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Navigation("Roles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260324062229_Add-Notification-Send-Record.cs b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260324062229_Add-Notification-Send-Record.cs new file mode 100644 index 000000000..ff588f3a6 --- /dev/null +++ b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/20260324062229_Add-Notification-Send-Record.cs @@ -0,0 +1,49 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql.Migrations +{ + /// + public partial class AddNotificationSendRecord : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AppNotificationSendRecords", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + TenantId = table.Column(type: "uuid", nullable: true), + Provider = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + SendTime = table.Column(type: "timestamp without time zone", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + UserName = table.Column(type: "character varying(128)", maxLength: 128, nullable: false, defaultValue: "/"), + NotificationId = table.Column(type: "bigint", nullable: false), + NotificationName = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + State = table.Column(type: "integer", nullable: false), + Reason = table.Column(type: "character varying(255)", maxLength: 255, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AppNotificationSendRecords", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_Tenant_Send_Notification_Name", + table: "AppNotificationSendRecords", + columns: new[] { "TenantId", "NotificationName" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AppNotificationSendRecords"); + } + } +} diff --git a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/SingleMigrationsDbContextModelSnapshot.cs b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/SingleMigrationsDbContextModelSnapshot.cs index 96fa93373..410292c0a 100644 --- a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/SingleMigrationsDbContextModelSnapshot.cs +++ b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql/Migrations/SingleMigrationsDbContextModelSnapshot.cs @@ -24,6 +24,285 @@ namespace LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql.Mig NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.ConversationRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp without time zone"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UpdateAt") + .HasColumnType("timestamp without time zone"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.ToTable("AbpAIConversations", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.TextChatMessageRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ReplyAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ReplyMessage") + .HasColumnType("text"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITextChatMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tokens.TokenUsageRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CachedInputTokenCount") + .HasColumnType("bigint"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("InputTokenCount") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MessageId") + .HasColumnType("uuid"); + + b.Property("OutputTokenCount") + .HasColumnType("bigint"); + + b.Property("ReasoningTokenCount") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TotalTokenCount") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITokenUsages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiBaseUrl") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ApiKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrequencyPenalty") + .HasColumnType("real"); + + b.Property("Instructions") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("IsSystem") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MaxOutputTokens") + .HasColumnType("integer"); + + b.Property("ModelName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PresencePenalty") + .HasColumnType("real"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SystemPrompt") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Temperature") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpAIWorkspaceDefinitions", (string)null); + }); + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityEnumInfo", b => { b.Property("Id") @@ -1395,6 +1674,59 @@ namespace LY.MicroService.Applications.Single.EntityFrameworkCore.PostgreSql.Mig b.ToTable("AppNotificationDefinitions", (string)null); }); + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationSendRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Reason") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SendTime") + .HasColumnType("timestamp without time zone"); + + b.Property("State") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasDefaultValue("/"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "NotificationName") + .HasDatabaseName("IX_Tenant_Send_Notification_Name"); + + b.ToTable("AppNotificationSendRecords", (string)null); + }); + modelBuilder.Entity("LINGYUN.Abp.Notifications.UserNotification", b => { b.Property("Id") diff --git a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260319053918_Add-AI-Management-Module.Designer.cs b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260319053918_Add-AI-Management-Module.Designer.cs new file mode 100644 index 000000000..8d703130f --- /dev/null +++ b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260319053918_Add-AI-Management-Module.Designer.cs @@ -0,0 +1,5701 @@ +// +using System; +using LY.MicroService.Applications.Single.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer.Migrations +{ + [DbContext(typeof(SingleMigrationsDbContext))] + [Migration("20260319053918_Add-AI-Management-Module")] + partial class AddAIManagementModule + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.ConversationRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExpiredAt") + .HasColumnType("datetime2"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UpdateAt") + .HasColumnType("datetime2"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("Id"); + + b.ToTable("AbpAIConversations", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.TextChatMessageRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("ConversationId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ReplyAt") + .HasColumnType("datetime2"); + + b.Property("ReplyMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITextChatMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tokens.TokenUsageRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CachedInputTokenCount") + .HasColumnType("bigint"); + + b.Property("ConversationId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("InputTokenCount") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("OutputTokenCount") + .HasColumnType("bigint"); + + b.Property("ReasoningTokenCount") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TotalTokenCount") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITokenUsages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ApiBaseUrl") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ApiKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FrequencyPenalty") + .HasColumnType("real"); + + b.Property("Instructions") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("IsSystem") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MaxOutputTokens") + .HasColumnType("int"); + + b.Property("ModelName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("PresencePenalty") + .HasColumnType("real"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("SystemPrompt") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("Temperature") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpAIWorkspaceDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityEnumInfo", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("PropertyInfoId") + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)") + .HasColumnName("Value"); + + b.HasKey("Id"); + + b.HasIndex("PropertyInfoId", "Name"); + + b.ToTable("AbpAuthEntityEnums", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("JavaScriptType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("JavaScriptType"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("TypeFullName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("TypeFullName"); + + b.Property("TypeInfoId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TypeInfoId", "TypeFullName"); + + b.ToTable("AbpAuthEntityProperties", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsAuditEnabled") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("TypeFullName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("TypeFullName"); + + b.HasKey("Id"); + + b.HasIndex("TypeFullName"); + + b.ToTable("AbpAuthEntitites", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.OrganizationUnitEntityRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessedProperties") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("AccessedProperties"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("EntityTypeFullName") + .HasColumnType("nvarchar(max)"); + + b.Property("EntityTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FilterGroup") + .HasColumnType("nvarchar(max)") + .HasColumnName("FilterGroup"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Operation") + .HasColumnType("int"); + + b.Property("OrgCode") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("OrgCode"); + + b.Property("OrgId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityTypeId"); + + b.ToTable("AbpAuthOrganizationUnitEntityRules", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.RoleEntityRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessedProperties") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("AccessedProperties"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("EntityTypeFullName") + .HasColumnType("nvarchar(max)"); + + b.Property("EntityTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FilterGroup") + .HasColumnType("nvarchar(max)") + .HasColumnName("FilterGroup"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Operation") + .HasColumnType("int"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("RoleName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityTypeId"); + + b.ToTable("AbpAuthRoleEntityRules", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.SubjectStrategy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Strategy") + .HasColumnType("int"); + + b.Property("SubjectId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("SubjectId"); + + b.Property("SubjectName") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("SubjectName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpAuthSubjectStrategys", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Authors.Author", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("BirthDate") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ShortBio") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("Demo_Authors", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.Book", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Price") + .HasColumnType("real"); + + b.Property("PublishDate") + .HasColumnType("datetime2"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AuthorId"); + + b.ToTable("Demo_Books", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.BookAuth", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("EntityId") + .HasMaxLength(64) + .HasColumnType("uniqueidentifier") + .HasColumnName("EntityId"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("EntityType"); + + b.Property("OrganizationUnit") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)") + .HasColumnName("OrganizationUnit"); + + b.Property("Role") + .HasMaxLength(32) + .HasColumnType("nvarchar(32)") + .HasColumnName("Role"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("EntityId"); + + b.HasIndex("OrganizationUnit"); + + b.HasIndex("Role"); + + b.ToTable("Demo_BooksAuths", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprInfo", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Data") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("Data"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Provider"); + + b.Property("RequestId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RequestId"); + + b.ToTable("AbpGdprInfos", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprRequest", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("ReadyTime") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpGdprRequests", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Language", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("CultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)") + .HasColumnName("CultureName"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("DisplayName"); + + b.Property("Enable") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("TwoLetterISOLanguageName") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("TwoLetterISOLanguageName"); + + b.Property("UiCultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)") + .HasColumnName("UiCultureName"); + + b.HasKey("Id"); + + b.HasIndex("CultureName"); + + b.ToTable("AbpLocalizationLanguages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Resource", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DefaultCultureName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("DefaultCultureName"); + + b.Property("Description") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Description"); + + b.Property("DisplayName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("DisplayName"); + + b.Property("Enable") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)") + .HasColumnName("Name"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("AbpLocalizationResources", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Text", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)") + .HasColumnName("CultureName"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("Key"); + + b.Property("ResourceName") + .HasColumnType("nvarchar(max)"); + + b.Property("Value") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)") + .HasColumnName("Value"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("AbpLocalizationTexts", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatCard", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Age") + .HasColumnType("int"); + + b.Property("AvatarUrl") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("Birthday") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LastOnlineTime") + .HasColumnType("datetime2"); + + b.Property("NickName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Sex") + .HasColumnType("int"); + + b.Property("Sign") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("State") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserChatCards", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatFriend", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Black") + .HasColumnType("bit"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("DontDisturb") + .HasColumnType("bit"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FrientId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("RemarkName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("SpecialFocus") + .HasColumnType("bit"); + + b.Property("Status") + .HasColumnType("tinyint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "FrientId"); + + b.ToTable("AppUserChatFriends", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AllowAddFriend") + .HasColumnType("bit"); + + b.Property("AllowAnonymous") + .HasColumnType("bit"); + + b.Property("AllowReceiveMessage") + .HasColumnType("bit"); + + b.Property("AllowSendMessage") + .HasColumnType("bit"); + + b.Property("RequireAddFriendValition") + .HasColumnType("bit"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserChatSettings", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("MessageId") + .HasColumnType("bigint"); + + b.Property("ReceiveUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("SendUserName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Source") + .HasColumnType("int"); + + b.Property("State") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ReceiveUserId"); + + b.ToTable("AppUserMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.ChatGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Address") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("AdminUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("AllowAnonymous") + .HasColumnType("bit"); + + b.Property("AllowSendMessage") + .HasColumnType("bit"); + + b.Property("AvatarUrl") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MaxUserCount") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("Notice") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Tag") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("AppChatGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.GroupChatBlack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("ShieldUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId"); + + b.ToTable("AppGroupChatBlacks", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.GroupMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("MessageId") + .HasColumnType("bigint"); + + b.Property("SendUserName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Source") + .HasColumnType("int"); + + b.Property("State") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId"); + + b.ToTable("AppGroupMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.UserChatGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId", "UserId"); + + b.ToTable("AppUserChatGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.UserGroupCard", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsAdmin") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("NickName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("SilenceEnd") + .HasColumnType("datetime2"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserGroupCards", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContentType") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("ExpirationTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("NotificationTypeName") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("Severity") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "NotificationName"); + + b.ToTable("AppNotifications", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationDefinitionGroupRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AllowSubscriptionToClients") + .HasColumnType("bit"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("DisplayName") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("Id"); + + b.ToTable("AppNotificationDefinitionGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AllowSubscriptionToClients") + .HasColumnType("bit"); + + b.Property("ContentType") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("DisplayName") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("NotificationLifetime") + .HasColumnType("int"); + + b.Property("NotificationType") + .HasColumnType("int"); + + b.Property("Providers") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Template") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("AppNotificationDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.UserNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("ReadStatus") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "NotificationId") + .HasDatabaseName("IX_Tenant_User_Notification_Id"); + + b.ToTable("AppUserNotifications", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.UserSubscribe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserName") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasDefaultValue("/"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "NotificationName") + .IsUnique() + .HasDatabaseName("IX_Tenant_User_Notification_Name") + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AppUserSubscribes", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Editions.Edition", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.HasKey("Id"); + + b.HasIndex("DisplayName"); + + b.ToTable("AbpEditions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisableTime") + .HasColumnType("datetime2"); + + b.Property("EditionId") + .HasColumnType("uniqueidentifier"); + + b.Property("EnableTime") + .HasColumnType("datetime2"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("NormalizedName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("Id"); + + b.HasIndex("EditionId"); + + b.HasIndex("Name"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpTenants", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.TenantConnectionString", b => + { + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobAction", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("JobId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("JobId"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("Name"); + + b.Property("Paramters") + .HasColumnType("nvarchar(max)") + .HasColumnName("Paramters"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("TK_BackgroundJobActions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobInfo", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("Args") + .HasColumnType("nvarchar(max)") + .HasColumnName("Args"); + + b.Property("BeginTime") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Cron") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)") + .HasColumnName("Cron"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Description"); + + b.Property("EndTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Group") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("Group"); + + b.Property("Interval") + .HasColumnType("int"); + + b.Property("IsAbandoned") + .HasColumnType("bit"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("JobType") + .HasColumnType("int"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LastRunTime") + .HasColumnType("datetime2"); + + b.Property("LockTimeOut") + .HasColumnType("int"); + + b.Property("MaxCount") + .HasColumnType("int"); + + b.Property("MaxTryCount") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("Name"); + + b.Property("NextRunTime") + .HasColumnType("datetime2"); + + b.Property("NodeName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("NodeName"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("Result") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("Result"); + + b.Property("Source") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TriggerCount") + .HasColumnType("int"); + + b.Property("TryCount") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("Type"); + + b.HasKey("Id"); + + b.HasIndex("Name", "Group"); + + b.ToTable("TK_BackgroundJobs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Exception") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)") + .HasColumnName("Exception"); + + b.Property("JobGroup") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("JobGroup"); + + b.Property("JobId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("JobId"); + + b.Property("JobName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("JobName"); + + b.Property("JobType") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("JobType"); + + b.Property("Message") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("Message"); + + b.Property("RunTime") + .HasColumnType("datetime2"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("JobGroup", "JobName"); + + b.ToTable("TK_BackgroundJobLogs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TextTemplating.TextTemplate", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .HasMaxLength(1048576) + .HasColumnType("nvarchar(max)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Culture") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("Culture"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("DisplayName"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("Name"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Tenant_Text_Template_Name"); + + b.ToTable("AbpTextTemplates", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TextTemplating.TextTemplateDefinition", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("DefaultCultureName") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("DefaultCultureName"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsInlineLocalized") + .HasColumnType("bit"); + + b.Property("IsLayout") + .HasColumnType("bit"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("Layout") + .HasMaxLength(60) + .HasColumnType("nvarchar(60)") + .HasColumnName("Layout"); + + b.Property("LocalizationResourceName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("LocalizationResourceName"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("Name"); + + b.Property("RenderEngine") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("RenderEngine"); + + b.HasKey("Id"); + + b.ToTable("AbpTextTemplateDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("RequiredFeatures") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("GroupName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpWebhooksWebhooks", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookEventRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("Data") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)") + .HasColumnName("Data"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("WebhookName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("WebhookName"); + + b.HasKey("Id"); + + b.ToTable("AbpWebhooksEvents", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookGroupDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpWebhooksWebhookGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookSendRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("RequestHeaders") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)") + .HasColumnName("RequestHeaders"); + + b.Property("Response") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)") + .HasColumnName("Response"); + + b.Property("ResponseHeaders") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)") + .HasColumnName("ResponseHeaders"); + + b.Property("ResponseStatusCode") + .HasColumnType("int"); + + b.Property("SendExactSameData") + .HasColumnType("bit"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("WebhookEventId") + .HasColumnType("uniqueidentifier"); + + b.Property("WebhookSubscriptionId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("WebhookEventId"); + + b.ToTable("AbpWebhooksSendAttempts", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("Description"); + + b.Property("Headers") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)") + .HasColumnName("Headers"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Secret") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("Secret"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("TimeoutDuration") + .HasColumnType("int"); + + b.Property("WebhookUri") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("WebhookUri"); + + b.Property("Webhooks") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)") + .HasColumnName("Webhooks"); + + b.HasKey("Id"); + + b.ToTable("AbpWebhooksSubscriptions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.Data", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("Description"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("Name"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("AppPlatformDatas", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.DataItem", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AllowBeNull") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataId") + .HasColumnType("uniqueidentifier"); + + b.Property("DefaultValue") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DefaultValue"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("Description"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("Name"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("ValueType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DataId"); + + b.HasIndex("Name"); + + b.ToTable("AppPlatformDataItems", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.Feedback", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Category"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformFeedbacks", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("FeedbackId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Url"); + + b.HasKey("Id"); + + b.HasIndex("FeedbackId"); + + b.ToTable("AppPlatformFeedbackAttachments", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackComment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Capacity") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Capacity"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("FeedbackId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("FeedbackId"); + + b.ToTable("AppPlatformFeedbackComments", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Layouts.Layout", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Framework") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Framework"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("Path") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Path"); + + b.Property("Redirect") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Redirect"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformLayouts", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.Menu", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(23) + .HasColumnType("nvarchar(23)") + .HasColumnName("Code"); + + b.Property("Component") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Component"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Framework") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Framework"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsPublic") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LayoutId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("Path") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Path"); + + b.Property("Redirect") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Redirect"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.RoleMenu", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MenuId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("RoleName"); + + b.Property("Startup") + .HasColumnType("bit"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleName", "MenuId"); + + b.ToTable("AppPlatformRoleMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.UserFavoriteMenu", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AliasName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("AliasName"); + + b.Property("Color") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("Color"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("Framework") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Framework"); + + b.Property("Icon") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("Icon"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MenuId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Path"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "MenuId"); + + b.ToTable("AppPlatformUserFavoriteMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.UserMenu", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MenuId") + .HasColumnType("uniqueidentifier"); + + b.Property("Startup") + .HasColumnType("bit"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "MenuId"); + + b.ToTable("AppPlatformUserMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessage", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("BodyTransferEncoding") + .HasColumnType("int"); + + b.Property("CC") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("CC"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeliveryNotificationOptions") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("From") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("From"); + + b.Property("IsBodyHtml") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Normalize") + .HasColumnType("bit"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("Provider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("Provider"); + + b.Property("Reason") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Reason"); + + b.Property("Receiver") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("Receiver"); + + b.Property("SendCount") + .HasColumnType("int"); + + b.Property("SendTime") + .HasColumnType("datetime2"); + + b.Property("Sender") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Sender"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Subject") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("Subject"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformEmailMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BlobName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("BlobName"); + + b.Property("MessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Name"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("MessageId"); + + b.ToTable("AppPlatformEmailMessageAttachments", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Key"); + + b.Property("MessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("Value"); + + b.HasKey("Id"); + + b.HasIndex("MessageId"); + + b.ToTable("AppPlatformEmailMessageHeaders", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.SmsMessage", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Provider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("Provider"); + + b.Property("Reason") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Reason"); + + b.Property("Receiver") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("Receiver"); + + b.Property("SendCount") + .HasColumnType("int"); + + b.Property("SendTime") + .HasColumnType("datetime2"); + + b.Property("Sender") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Sender"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformSmsMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.Package", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Authors") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("Authors"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Description"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("ForceUpdate") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Name"); + + b.Property("Note") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("Note"); + + b.Property("Version") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("Version"); + + b.HasKey("Id"); + + b.HasIndex("Name", "Version"); + + b.ToTable("AppPlatformPackages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.PackageBlob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Authors") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("Authors"); + + b.Property("ContentType") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("ContentType"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DownloadCount") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("License") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("License"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Name"); + + b.Property("PackageId") + .HasColumnType("uniqueidentifier"); + + b.Property("SHA256") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("SHA256"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("Summary") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("Summary"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("Url") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("Url"); + + b.HasKey("Id"); + + b.HasIndex("PackageId", "Name"); + + b.ToTable("AppPlatformPackageBlobs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Portal.Enterprise", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Address") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Address"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("EnglishName") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("EnglishName"); + + b.Property("ExpirationDate") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LegalMan") + .HasMaxLength(60) + .HasColumnType("nvarchar(60)") + .HasColumnName("LegalMan"); + + b.Property("Logo") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("Logo"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Name"); + + b.Property("OrganizationCode") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("OrganizationCode"); + + b.Property("RegistrationCode") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("RegistrationCode"); + + b.Property("RegistrationDate") + .HasColumnType("datetime2"); + + b.Property("TaxCode") + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("TaxCode"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformEnterprises", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)") + .HasColumnName("ApplicationName"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("BrowserInfo"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("ClientId"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("ClientIpAddress"); + + b.Property("ClientName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("ClientName"); + + b.Property("Comments") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Comments"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("CorrelationId"); + + b.Property("Exceptions") + .HasColumnType("nvarchar(max)"); + + b.Property("ExecutionDuration") + .HasColumnType("int") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("HttpMethod"); + + b.Property("HttpStatusCode") + .HasColumnType("int") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorTenantName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("ImpersonatorTenantName"); + + b.Property("ImpersonatorUserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("ImpersonatorUserId"); + + b.Property("ImpersonatorUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("ImpersonatorUserName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("TenantName"); + + b.Property("Url") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Url"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AuditLogId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnType("int") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("datetime2") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("MethodName"); + + b.Property("Parameters") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)") + .HasColumnName("Parameters"); + + b.Property("ServiceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("ServiceName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogExcelFile", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("FileName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("FileName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpAuditLogExcelFiles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AuditLogId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnType("datetime2") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnType("tinyint") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("EntityId"); + + b.Property("EntityTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("EntityTypeFullName"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("EntityChangeId") + .HasColumnType("uniqueidentifier"); + + b.Property("NewValue") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("NewValue"); + + b.Property("OriginalValue") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("OriginalValue"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("PropertyName"); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("PropertyTypeFullName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AllowedProviders") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("DefaultValue") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("IsAvailableToHost") + .HasColumnType("bit"); + + b.Property("IsVisibleToClients") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ParentName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ValueType") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.HasKey("Id"); + + b.HasIndex("GroupName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpFeatures", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpFeatureGroups", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[ProviderName] IS NOT NULL AND [ProviderKey] IS NOT NULL"); + + b.ToTable("AbpFeatureValues", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Regex") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("RegexDescription") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Required") + .HasColumnType("bit"); + + b.Property("ValueType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("SourceTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("SourceUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("TargetTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("TargetUserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SourceUserId", "SourceTenantId", "TargetUserId", "TargetTenantId") + .IsUnique() + .HasFilter("[SourceTenantId] IS NOT NULL AND [TargetTenantId] IS NOT NULL"); + + b.ToTable("AbpLinkUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnType("bit") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnType("bit") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnType("bit") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Action") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CreationTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Identity") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Action"); + + b.HasIndex("TenantId", "ApplicationName"); + + b.HasIndex("TenantId", "Identity"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSecurityLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySession", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Device") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DeviceInfo") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IpAddresses") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.Property("LastAccessed") + .HasColumnType("datetime2"); + + b.Property("SessionId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("SignedIn") + .HasColumnType("datetime2"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Device"); + + b.HasIndex("SessionId"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSessions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Email"); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("EmailConfirmed"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("bit") + .HasColumnName("IsActive"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsExternal") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsExternal"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LastPasswordChangeTime") + .HasColumnType("datetimeoffset"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("LockoutEnabled"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("NormalizedEmail"); + + b.Property("NormalizedUserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("NormalizedUserName"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("PasswordHash"); + + b.Property("PhoneNumber") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("PhoneNumber"); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("PhoneNumberConfirmed"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("SecurityStamp"); + + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + + b.Property("Surname") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Surname"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("TwoFactorEnabled"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("NormalizedEmail"); + + b.HasIndex("NormalizedUserName"); + + b.HasIndex("UserName"); + + b.ToTable("AbpUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserDelegation", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("EndTime") + .HasColumnType("datetime2"); + + b.Property("SourceUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.Property("TargetUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpUserDelegations", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196) + .HasColumnType("nvarchar(196)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(95) + .HasColumnType("nvarchar(95)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ClientSecret") + .HasColumnType("nvarchar(max)"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ClientUri") + .HasColumnType("nvarchar(max)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayNames") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutUri") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("JsonWebKeySet") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasColumnType("nvarchar(max)"); + + b.Property("Permissions") + .HasColumnType("nvarchar(max)"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("RedirectUris") + .HasColumnType("nvarchar(max)"); + + b.Property("Requirements") + .HasColumnType("nvarchar(max)"); + + b.Property("Settings") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("OpenIddictApplications", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("Scopes") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Scopes.OpenIddictScope", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Descriptions") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayNames") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("Resources") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("OpenIddictScopes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Tokens.OpenIddictToken", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationId") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ExpirationDate") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Payload") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("RedemptionDate") + .HasColumnType("datetime2"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("MultiTenancySide") + .HasColumnType("tinyint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ParentName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Providers") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("GroupName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpPermissions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpPermissionGrants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGroupDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpPermissionGroups", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[ProviderName] IS NOT NULL AND [ProviderKey] IS NOT NULL"); + + b.ToTable("AbpSettings", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.SettingDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("DefaultValue") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsEncrypted") + .HasColumnType("bit"); + + b.Property("IsInherited") + .HasColumnType("bit"); + + b.Property("IsVisibleToClients") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Providers") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpSettingDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityEnumInfo", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", "PropertyInfo") + .WithMany("Enums") + .HasForeignKey("PropertyInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PropertyInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", "TypeInfo") + .WithMany("Properties") + .HasForeignKey("TypeInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TypeInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.OrganizationUnitEntityRule", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", "EntityTypeInfo") + .WithMany() + .HasForeignKey("EntityTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EntityTypeInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.RoleEntityRule", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", "EntityTypeInfo") + .WithMany() + .HasForeignKey("EntityTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EntityTypeInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.Book", b => + { + b.HasOne("LINGYUN.Abp.Demo.Authors.Author", null) + .WithMany() + .HasForeignKey("AuthorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.BookAuth", b => + { + b.HasOne("LINGYUN.Abp.Demo.Books.Book", "Entity") + .WithMany() + .HasForeignKey("EntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Entity"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprInfo", b => + { + b.HasOne("LINGYUN.Abp.Gdpr.GdprRequest", null) + .WithMany("Infos") + .HasForeignKey("RequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.HasOne("LINGYUN.Abp.Saas.Editions.Edition", "Edition") + .WithMany() + .HasForeignKey("EditionId"); + + b.Navigation("Edition"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.TenantConnectionString", b => + { + b.HasOne("LINGYUN.Abp.Saas.Tenants.Tenant", null) + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookSendRecord", b => + { + b.HasOne("LINGYUN.Abp.WebhooksManagement.WebhookEventRecord", "WebhookEvent") + .WithOne() + .HasForeignKey("LINGYUN.Abp.WebhooksManagement.WebhookSendRecord", "WebhookEventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebhookEvent"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.DataItem", b => + { + b.HasOne("LINGYUN.Platform.Datas.Data", null) + .WithMany("Items") + .HasForeignKey("DataId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackAttachment", b => + { + b.HasOne("LINGYUN.Platform.Feedbacks.Feedback", null) + .WithMany("Attachments") + .HasForeignKey("FeedbackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackComment", b => + { + b.HasOne("LINGYUN.Platform.Feedbacks.Feedback", null) + .WithMany("Comments") + .HasForeignKey("FeedbackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageAttachment", b => + { + b.HasOne("LINGYUN.Platform.Messages.EmailMessage", null) + .WithMany("Attachments") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageHeader", b => + { + b.HasOne("LINGYUN.Platform.Messages.EmailMessage", null) + .WithMany("Headers") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.PackageBlob", b => + { + b.HasOne("LINGYUN.Platform.Packages.Package", "Package") + .WithMany("Blobs") + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Package"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange", null) + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", b => + { + b.HasOne("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", null) + .WithMany() + .HasForeignKey("ApplicationId"); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Tokens.OpenIddictToken", b => + { + b.HasOne("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", null) + .WithMany() + .HasForeignKey("ApplicationId"); + + b.HasOne("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", null) + .WithMany() + .HasForeignKey("AuthorizationId"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", b => + { + b.Navigation("Enums"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", b => + { + b.Navigation("Properties"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprRequest", b => + { + b.Navigation("Infos"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.Navigation("ConnectionStrings"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.Data", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.Feedback", b => + { + b.Navigation("Attachments"); + + b.Navigation("Comments"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessage", b => + { + b.Navigation("Attachments"); + + b.Navigation("Headers"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.Package", b => + { + b.Navigation("Blobs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Navigation("Actions"); + + b.Navigation("EntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Navigation("PropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Navigation("Claims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("OrganizationUnits"); + + b.Navigation("Roles"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Navigation("Roles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260319053918_Add-AI-Management-Module.cs b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260319053918_Add-AI-Management-Module.cs new file mode 100644 index 000000000..384cdb329 --- /dev/null +++ b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260319053918_Add-AI-Management-Module.cs @@ -0,0 +1,150 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer.Migrations +{ + /// + public partial class AddAIManagementModule : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AbpAIConversations", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Name = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false), + Workspace = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + ExpiredAt = table.Column(type: "datetime2", nullable: false), + UpdateAt = table.Column(type: "datetime2", nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAIConversations", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpAITextChatMessages", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Content = table.Column(type: "nvarchar(1024)", maxLength: 1024, nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Workspace = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + Role = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + UserId = table.Column(type: "uniqueidentifier", nullable: true), + ConversationId = table.Column(type: "uniqueidentifier", nullable: true), + ReplyMessage = table.Column(type: "nvarchar(max)", nullable: true), + ReplyAt = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAITextChatMessages", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpAITokenUsages", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + MessageId = table.Column(type: "uniqueidentifier", nullable: true), + ConversationId = table.Column(type: "uniqueidentifier", nullable: true), + InputTokenCount = table.Column(type: "bigint", nullable: true), + OutputTokenCount = table.Column(type: "bigint", nullable: true), + TotalTokenCount = table.Column(type: "bigint", nullable: true), + CachedInputTokenCount = table.Column(type: "bigint", nullable: true), + ReasoningTokenCount = table.Column(type: "bigint", nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAITokenUsages", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpAIWorkspaceDefinitions", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + Provider = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: false), + ModelName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + DisplayName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Description = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), + ApiKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + ApiBaseUrl = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), + SystemPrompt = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), + Instructions = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), + Temperature = table.Column(type: "real", nullable: true), + MaxOutputTokens = table.Column(type: "int", nullable: true), + FrequencyPenalty = table.Column(type: "real", nullable: true), + PresencePenalty = table.Column(type: "real", nullable: true), + IsEnabled = table.Column(type: "bit", nullable: false), + IsSystem = table.Column(type: "bit", nullable: false), + StateCheckers = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAIWorkspaceDefinitions", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAITextChatMessages_TenantId_ConversationId", + table: "AbpAITextChatMessages", + columns: new[] { "TenantId", "ConversationId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAITokenUsages_TenantId_ConversationId", + table: "AbpAITokenUsages", + columns: new[] { "TenantId", "ConversationId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAIWorkspaceDefinitions_Name", + table: "AbpAIWorkspaceDefinitions", + column: "Name", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AbpAIConversations"); + + migrationBuilder.DropTable( + name: "AbpAITextChatMessages"); + + migrationBuilder.DropTable( + name: "AbpAITokenUsages"); + + migrationBuilder.DropTable( + name: "AbpAIWorkspaceDefinitions"); + } + } +} diff --git a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260324062303_Add-Notification-Send-Record.Designer.cs b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260324062303_Add-Notification-Send-Record.Designer.cs new file mode 100644 index 000000000..93f983995 --- /dev/null +++ b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260324062303_Add-Notification-Send-Record.Designer.cs @@ -0,0 +1,5754 @@ +// +using System; +using LY.MicroService.Applications.Single.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer.Migrations +{ + [DbContext(typeof(SingleMigrationsDbContext))] + [Migration("20260324062303_Add-Notification-Send-Record")] + partial class AddNotificationSendRecord + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.ConversationRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExpiredAt") + .HasColumnType("datetime2"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UpdateAt") + .HasColumnType("datetime2"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("Id"); + + b.ToTable("AbpAIConversations", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.TextChatMessageRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("ConversationId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ReplyAt") + .HasColumnType("datetime2"); + + b.Property("ReplyMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITextChatMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tokens.TokenUsageRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CachedInputTokenCount") + .HasColumnType("bigint"); + + b.Property("ConversationId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("InputTokenCount") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("OutputTokenCount") + .HasColumnType("bigint"); + + b.Property("ReasoningTokenCount") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TotalTokenCount") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITokenUsages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ApiBaseUrl") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ApiKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FrequencyPenalty") + .HasColumnType("real"); + + b.Property("Instructions") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("IsSystem") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MaxOutputTokens") + .HasColumnType("int"); + + b.Property("ModelName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("PresencePenalty") + .HasColumnType("real"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("SystemPrompt") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("Temperature") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpAIWorkspaceDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityEnumInfo", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("PropertyInfoId") + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)") + .HasColumnName("Value"); + + b.HasKey("Id"); + + b.HasIndex("PropertyInfoId", "Name"); + + b.ToTable("AbpAuthEntityEnums", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("JavaScriptType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("JavaScriptType"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("TypeFullName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("TypeFullName"); + + b.Property("TypeInfoId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TypeInfoId", "TypeFullName"); + + b.ToTable("AbpAuthEntityProperties", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsAuditEnabled") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("TypeFullName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("TypeFullName"); + + b.HasKey("Id"); + + b.HasIndex("TypeFullName"); + + b.ToTable("AbpAuthEntitites", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.OrganizationUnitEntityRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessedProperties") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("AccessedProperties"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("EntityTypeFullName") + .HasColumnType("nvarchar(max)"); + + b.Property("EntityTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FilterGroup") + .HasColumnType("nvarchar(max)") + .HasColumnName("FilterGroup"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Operation") + .HasColumnType("int"); + + b.Property("OrgCode") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("OrgCode"); + + b.Property("OrgId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityTypeId"); + + b.ToTable("AbpAuthOrganizationUnitEntityRules", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.RoleEntityRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessedProperties") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("AccessedProperties"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("EntityTypeFullName") + .HasColumnType("nvarchar(max)"); + + b.Property("EntityTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FilterGroup") + .HasColumnType("nvarchar(max)") + .HasColumnName("FilterGroup"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Operation") + .HasColumnType("int"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("RoleName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityTypeId"); + + b.ToTable("AbpAuthRoleEntityRules", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.SubjectStrategy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Strategy") + .HasColumnType("int"); + + b.Property("SubjectId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("SubjectId"); + + b.Property("SubjectName") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("SubjectName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpAuthSubjectStrategys", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Authors.Author", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("BirthDate") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ShortBio") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("Demo_Authors", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.Book", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Price") + .HasColumnType("real"); + + b.Property("PublishDate") + .HasColumnType("datetime2"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AuthorId"); + + b.ToTable("Demo_Books", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.BookAuth", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("EntityId") + .HasMaxLength(64) + .HasColumnType("uniqueidentifier") + .HasColumnName("EntityId"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("EntityType"); + + b.Property("OrganizationUnit") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)") + .HasColumnName("OrganizationUnit"); + + b.Property("Role") + .HasMaxLength(32) + .HasColumnType("nvarchar(32)") + .HasColumnName("Role"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("EntityId"); + + b.HasIndex("OrganizationUnit"); + + b.HasIndex("Role"); + + b.ToTable("Demo_BooksAuths", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprInfo", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Data") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("Data"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Provider"); + + b.Property("RequestId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RequestId"); + + b.ToTable("AbpGdprInfos", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprRequest", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("ReadyTime") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpGdprRequests", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Language", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("CultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)") + .HasColumnName("CultureName"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("DisplayName"); + + b.Property("Enable") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("TwoLetterISOLanguageName") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("TwoLetterISOLanguageName"); + + b.Property("UiCultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)") + .HasColumnName("UiCultureName"); + + b.HasKey("Id"); + + b.HasIndex("CultureName"); + + b.ToTable("AbpLocalizationLanguages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Resource", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DefaultCultureName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("DefaultCultureName"); + + b.Property("Description") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Description"); + + b.Property("DisplayName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("DisplayName"); + + b.Property("Enable") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)") + .HasColumnName("Name"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("AbpLocalizationResources", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.LocalizationManagement.Text", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CultureName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)") + .HasColumnName("CultureName"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("Key"); + + b.Property("ResourceName") + .HasColumnType("nvarchar(max)"); + + b.Property("Value") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)") + .HasColumnName("Value"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("AbpLocalizationTexts", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatCard", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Age") + .HasColumnType("int"); + + b.Property("AvatarUrl") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("Birthday") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LastOnlineTime") + .HasColumnType("datetime2"); + + b.Property("NickName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Sex") + .HasColumnType("int"); + + b.Property("Sign") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("State") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserChatCards", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatFriend", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Black") + .HasColumnType("bit"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("DontDisturb") + .HasColumnType("bit"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FrientId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("RemarkName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("SpecialFocus") + .HasColumnType("bit"); + + b.Property("Status") + .HasColumnType("tinyint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "FrientId"); + + b.ToTable("AppUserChatFriends", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserChatSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AllowAddFriend") + .HasColumnType("bit"); + + b.Property("AllowAnonymous") + .HasColumnType("bit"); + + b.Property("AllowReceiveMessage") + .HasColumnType("bit"); + + b.Property("AllowSendMessage") + .HasColumnType("bit"); + + b.Property("RequireAddFriendValition") + .HasColumnType("bit"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserChatSettings", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Chat.UserMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("MessageId") + .HasColumnType("bigint"); + + b.Property("ReceiveUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("SendUserName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Source") + .HasColumnType("int"); + + b.Property("State") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ReceiveUserId"); + + b.ToTable("AppUserMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.ChatGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Address") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("AdminUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("AllowAnonymous") + .HasColumnType("bit"); + + b.Property("AllowSendMessage") + .HasColumnType("bit"); + + b.Property("AvatarUrl") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MaxUserCount") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("Notice") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Tag") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("AppChatGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.GroupChatBlack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("ShieldUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId"); + + b.ToTable("AppGroupChatBlacks", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.GroupMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("MessageId") + .HasColumnType("bigint"); + + b.Property("SendUserName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Source") + .HasColumnType("int"); + + b.Property("State") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId"); + + b.ToTable("AppGroupMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.UserChatGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupId", "UserId"); + + b.ToTable("AppUserChatGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.MessageService.Groups.UserGroupCard", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsAdmin") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("NickName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("SilenceEnd") + .HasColumnType("datetime2"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AppUserGroupCards", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContentType") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("ExpirationTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("NotificationTypeName") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("Severity") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "NotificationName"); + + b.ToTable("AppNotifications", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationDefinitionGroupRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AllowSubscriptionToClients") + .HasColumnType("bit"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("DisplayName") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("Id"); + + b.ToTable("AppNotificationDefinitionGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AllowSubscriptionToClients") + .HasColumnType("bit"); + + b.Property("ContentType") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("DisplayName") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("NotificationLifetime") + .HasColumnType("int"); + + b.Property("NotificationType") + .HasColumnType("int"); + + b.Property("Providers") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Template") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("AppNotificationDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationSendRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Reason") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("SendTime") + .HasColumnType("datetime2"); + + b.Property("State") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserName") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasDefaultValue("/"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "NotificationName") + .HasDatabaseName("IX_Tenant_Send_Notification_Name"); + + b.ToTable("AppNotificationSendRecords", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.UserNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("ReadStatus") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "NotificationId") + .HasDatabaseName("IX_Tenant_User_Notification_Id"); + + b.ToTable("AppUserNotifications", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Notifications.UserSubscribe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserName") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasDefaultValue("/"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "NotificationName") + .IsUnique() + .HasDatabaseName("IX_Tenant_User_Notification_Name") + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AppUserSubscribes", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Editions.Edition", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.HasKey("Id"); + + b.HasIndex("DisplayName"); + + b.ToTable("AbpEditions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisableTime") + .HasColumnType("datetime2"); + + b.Property("EditionId") + .HasColumnType("uniqueidentifier"); + + b.Property("EnableTime") + .HasColumnType("datetime2"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("NormalizedName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("Id"); + + b.HasIndex("EditionId"); + + b.HasIndex("Name"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpTenants", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.TenantConnectionString", b => + { + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobAction", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("JobId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("JobId"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("Name"); + + b.Property("Paramters") + .HasColumnType("nvarchar(max)") + .HasColumnName("Paramters"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("TK_BackgroundJobActions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobInfo", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("Args") + .HasColumnType("nvarchar(max)") + .HasColumnName("Args"); + + b.Property("BeginTime") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Cron") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)") + .HasColumnName("Cron"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Description"); + + b.Property("EndTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Group") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("Group"); + + b.Property("Interval") + .HasColumnType("int"); + + b.Property("IsAbandoned") + .HasColumnType("bit"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("JobType") + .HasColumnType("int"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LastRunTime") + .HasColumnType("datetime2"); + + b.Property("LockTimeOut") + .HasColumnType("int"); + + b.Property("MaxCount") + .HasColumnType("int"); + + b.Property("MaxTryCount") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("Name"); + + b.Property("NextRunTime") + .HasColumnType("datetime2"); + + b.Property("NodeName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("NodeName"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("Result") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("Result"); + + b.Property("Source") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TriggerCount") + .HasColumnType("int"); + + b.Property("TryCount") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("Type"); + + b.HasKey("Id"); + + b.HasIndex("Name", "Group"); + + b.ToTable("TK_BackgroundJobs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Exception") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)") + .HasColumnName("Exception"); + + b.Property("JobGroup") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("JobGroup"); + + b.Property("JobId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("JobId"); + + b.Property("JobName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("JobName"); + + b.Property("JobType") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("JobType"); + + b.Property("Message") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("Message"); + + b.Property("RunTime") + .HasColumnType("datetime2"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("JobGroup", "JobName"); + + b.ToTable("TK_BackgroundJobLogs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TextTemplating.TextTemplate", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .HasMaxLength(1048576) + .HasColumnType("nvarchar(max)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Culture") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("Culture"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("DisplayName"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("Name"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Tenant_Text_Template_Name"); + + b.ToTable("AbpTextTemplates", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.TextTemplating.TextTemplateDefinition", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("DefaultCultureName") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("DefaultCultureName"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsInlineLocalized") + .HasColumnType("bit"); + + b.Property("IsLayout") + .HasColumnType("bit"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("Layout") + .HasMaxLength(60) + .HasColumnType("nvarchar(60)") + .HasColumnName("Layout"); + + b.Property("LocalizationResourceName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("LocalizationResourceName"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("Name"); + + b.Property("RenderEngine") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("RenderEngine"); + + b.HasKey("Id"); + + b.ToTable("AbpTextTemplateDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("RequiredFeatures") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("GroupName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpWebhooksWebhooks", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookEventRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("Data") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)") + .HasColumnName("Data"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("WebhookName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("WebhookName"); + + b.HasKey("Id"); + + b.ToTable("AbpWebhooksEvents", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookGroupDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpWebhooksWebhookGroups", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookSendRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("RequestHeaders") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)") + .HasColumnName("RequestHeaders"); + + b.Property("Response") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)") + .HasColumnName("Response"); + + b.Property("ResponseHeaders") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)") + .HasColumnName("ResponseHeaders"); + + b.Property("ResponseStatusCode") + .HasColumnType("int"); + + b.Property("SendExactSameData") + .HasColumnType("bit"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("WebhookEventId") + .HasColumnType("uniqueidentifier"); + + b.Property("WebhookSubscriptionId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("WebhookEventId"); + + b.ToTable("AbpWebhooksSendAttempts", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("Description"); + + b.Property("Headers") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)") + .HasColumnName("Headers"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Secret") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("Secret"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("TimeoutDuration") + .HasColumnType("int"); + + b.Property("WebhookUri") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("WebhookUri"); + + b.Property("Webhooks") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)") + .HasColumnName("Webhooks"); + + b.HasKey("Id"); + + b.ToTable("AbpWebhooksSubscriptions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.Data", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("Description"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("Name"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("AppPlatformDatas", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.DataItem", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AllowBeNull") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataId") + .HasColumnType("uniqueidentifier"); + + b.Property("DefaultValue") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DefaultValue"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("Description"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("Name"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("ValueType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DataId"); + + b.HasIndex("Name"); + + b.ToTable("AppPlatformDataItems", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.Feedback", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Category"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformFeedbacks", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("FeedbackId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Url"); + + b.HasKey("Id"); + + b.HasIndex("FeedbackId"); + + b.ToTable("AppPlatformFeedbackAttachments", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackComment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Capacity") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Capacity"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("FeedbackId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("FeedbackId"); + + b.ToTable("AppPlatformFeedbackComments", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Layouts.Layout", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Framework") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Framework"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("Path") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Path"); + + b.Property("Redirect") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Redirect"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformLayouts", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.Menu", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(23) + .HasColumnType("nvarchar(23)") + .HasColumnName("Code"); + + b.Property("Component") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Component"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Framework") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Framework"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsPublic") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LayoutId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("Path") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Path"); + + b.Property("Redirect") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Redirect"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.RoleMenu", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MenuId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("RoleName"); + + b.Property("Startup") + .HasColumnType("bit"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleName", "MenuId"); + + b.ToTable("AppPlatformRoleMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.UserFavoriteMenu", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AliasName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("AliasName"); + + b.Property("Color") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("Color"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("Framework") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Framework"); + + b.Property("Icon") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("Icon"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MenuId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Path"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "MenuId"); + + b.ToTable("AppPlatformUserFavoriteMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Menus.UserMenu", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MenuId") + .HasColumnType("uniqueidentifier"); + + b.Property("Startup") + .HasColumnType("bit"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "MenuId"); + + b.ToTable("AppPlatformUserMenus", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessage", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("BodyTransferEncoding") + .HasColumnType("int"); + + b.Property("CC") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("CC"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeliveryNotificationOptions") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("From") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("From"); + + b.Property("IsBodyHtml") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Normalize") + .HasColumnType("bit"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("Provider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("Provider"); + + b.Property("Reason") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Reason"); + + b.Property("Receiver") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("Receiver"); + + b.Property("SendCount") + .HasColumnType("int"); + + b.Property("SendTime") + .HasColumnType("datetime2"); + + b.Property("Sender") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Sender"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Subject") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("Subject"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformEmailMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BlobName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("BlobName"); + + b.Property("MessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Name"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("MessageId"); + + b.ToTable("AppPlatformEmailMessageAttachments", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Key"); + + b.Property("MessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("Value"); + + b.HasKey("Id"); + + b.HasIndex("MessageId"); + + b.ToTable("AppPlatformEmailMessageHeaders", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.SmsMessage", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("Content"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Provider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("Provider"); + + b.Property("Reason") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Reason"); + + b.Property("Receiver") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("Receiver"); + + b.Property("SendCount") + .HasColumnType("int"); + + b.Property("SendTime") + .HasColumnType("datetime2"); + + b.Property("Sender") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Sender"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformSmsMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.Package", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Authors") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("Authors"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Description"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("ForceUpdate") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Name"); + + b.Property("Note") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("Note"); + + b.Property("Version") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("Version"); + + b.HasKey("Id"); + + b.HasIndex("Name", "Version"); + + b.ToTable("AppPlatformPackages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.PackageBlob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Authors") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("Authors"); + + b.Property("ContentType") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("ContentType"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DownloadCount") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("License") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("License"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Name"); + + b.Property("PackageId") + .HasColumnType("uniqueidentifier"); + + b.Property("SHA256") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("SHA256"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("Summary") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)") + .HasColumnName("Summary"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("Url") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("Url"); + + b.HasKey("Id"); + + b.HasIndex("PackageId", "Name"); + + b.ToTable("AppPlatformPackageBlobs", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Platform.Portal.Enterprise", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Address") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Address"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("EnglishName") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("EnglishName"); + + b.Property("ExpirationDate") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LegalMan") + .HasMaxLength(60) + .HasColumnType("nvarchar(60)") + .HasColumnName("LegalMan"); + + b.Property("Logo") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("Logo"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Name"); + + b.Property("OrganizationCode") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("OrganizationCode"); + + b.Property("RegistrationCode") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)") + .HasColumnName("RegistrationCode"); + + b.Property("RegistrationDate") + .HasColumnType("datetime2"); + + b.Property("TaxCode") + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("TaxCode"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("AppPlatformEnterprises", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)") + .HasColumnName("ApplicationName"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("BrowserInfo"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("ClientId"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("ClientIpAddress"); + + b.Property("ClientName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("ClientName"); + + b.Property("Comments") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Comments"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("CorrelationId"); + + b.Property("Exceptions") + .HasColumnType("nvarchar(max)"); + + b.Property("ExecutionDuration") + .HasColumnType("int") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("HttpMethod"); + + b.Property("HttpStatusCode") + .HasColumnType("int") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorTenantName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("ImpersonatorTenantName"); + + b.Property("ImpersonatorUserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("ImpersonatorUserId"); + + b.Property("ImpersonatorUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("ImpersonatorUserName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("TenantName"); + + b.Property("Url") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Url"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AuditLogId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnType("int") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("datetime2") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("MethodName"); + + b.Property("Parameters") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)") + .HasColumnName("Parameters"); + + b.Property("ServiceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("ServiceName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogExcelFile", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("FileName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("FileName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpAuditLogExcelFiles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AuditLogId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnType("datetime2") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnType("tinyint") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("EntityId"); + + b.Property("EntityTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("EntityTypeFullName"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("EntityChangeId") + .HasColumnType("uniqueidentifier"); + + b.Property("NewValue") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("NewValue"); + + b.Property("OriginalValue") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("OriginalValue"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("PropertyName"); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("PropertyTypeFullName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AllowedProviders") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("DefaultValue") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("IsAvailableToHost") + .HasColumnType("bit"); + + b.Property("IsVisibleToClients") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ParentName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ValueType") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.HasKey("Id"); + + b.HasIndex("GroupName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpFeatures", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpFeatureGroups", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[ProviderName] IS NOT NULL AND [ProviderKey] IS NOT NULL"); + + b.ToTable("AbpFeatureValues", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Regex") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("RegexDescription") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Required") + .HasColumnType("bit"); + + b.Property("ValueType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("SourceTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("SourceUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("TargetTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("TargetUserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SourceUserId", "SourceTenantId", "TargetUserId", "TargetTenantId") + .IsUnique() + .HasFilter("[SourceTenantId] IS NOT NULL AND [TargetTenantId] IS NOT NULL"); + + b.ToTable("AbpLinkUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnType("bit") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnType("bit") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnType("bit") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Action") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CreationTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Identity") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Action"); + + b.HasIndex("TenantId", "ApplicationName"); + + b.HasIndex("TenantId", "Identity"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSecurityLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySession", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Device") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DeviceInfo") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IpAddresses") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.Property("LastAccessed") + .HasColumnType("datetime2"); + + b.Property("SessionId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("SignedIn") + .HasColumnType("datetime2"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Device"); + + b.HasIndex("SessionId"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSessions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Email"); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("EmailConfirmed"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("bit") + .HasColumnName("IsActive"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsExternal") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsExternal"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LastPasswordChangeTime") + .HasColumnType("datetimeoffset"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("LockoutEnabled"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("NormalizedEmail"); + + b.Property("NormalizedUserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("NormalizedUserName"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("PasswordHash"); + + b.Property("PhoneNumber") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("PhoneNumber"); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("PhoneNumberConfirmed"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("SecurityStamp"); + + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + + b.Property("Surname") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Surname"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("TwoFactorEnabled"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("NormalizedEmail"); + + b.HasIndex("NormalizedUserName"); + + b.HasIndex("UserName"); + + b.ToTable("AbpUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserDelegation", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("EndTime") + .HasColumnType("datetime2"); + + b.Property("SourceUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.Property("TargetUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpUserDelegations", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196) + .HasColumnType("nvarchar(196)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(95) + .HasColumnType("nvarchar(95)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ClientSecret") + .HasColumnType("nvarchar(max)"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ClientUri") + .HasColumnType("nvarchar(max)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayNames") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutUri") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("JsonWebKeySet") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasColumnType("nvarchar(max)"); + + b.Property("Permissions") + .HasColumnType("nvarchar(max)"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("RedirectUris") + .HasColumnType("nvarchar(max)"); + + b.Property("Requirements") + .HasColumnType("nvarchar(max)"); + + b.Property("Settings") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("OpenIddictApplications", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("Scopes") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Scopes.OpenIddictScope", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Descriptions") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayNames") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("Resources") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("OpenIddictScopes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Tokens.OpenIddictToken", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationId") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ExpirationDate") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Payload") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("RedemptionDate") + .HasColumnType("datetime2"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("MultiTenancySide") + .HasColumnType("tinyint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ParentName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Providers") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("GroupName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpPermissions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpPermissionGrants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGroupDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpPermissionGroups", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[ProviderName] IS NOT NULL AND [ProviderKey] IS NOT NULL"); + + b.ToTable("AbpSettings", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.SettingDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("DefaultValue") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsEncrypted") + .HasColumnType("bit"); + + b.Property("IsInherited") + .HasColumnType("bit"); + + b.Property("IsVisibleToClients") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Providers") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpSettingDefinitions", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityEnumInfo", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", "PropertyInfo") + .WithMany("Enums") + .HasForeignKey("PropertyInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PropertyInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", "TypeInfo") + .WithMany("Properties") + .HasForeignKey("TypeInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TypeInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.OrganizationUnitEntityRule", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", "EntityTypeInfo") + .WithMany() + .HasForeignKey("EntityTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EntityTypeInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.RoleEntityRule", b => + { + b.HasOne("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", "EntityTypeInfo") + .WithMany() + .HasForeignKey("EntityTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EntityTypeInfo"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.Book", b => + { + b.HasOne("LINGYUN.Abp.Demo.Authors.Author", null) + .WithMany() + .HasForeignKey("AuthorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Abp.Demo.Books.BookAuth", b => + { + b.HasOne("LINGYUN.Abp.Demo.Books.Book", "Entity") + .WithMany() + .HasForeignKey("EntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Entity"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprInfo", b => + { + b.HasOne("LINGYUN.Abp.Gdpr.GdprRequest", null) + .WithMany("Infos") + .HasForeignKey("RequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.HasOne("LINGYUN.Abp.Saas.Editions.Edition", "Edition") + .WithMany() + .HasForeignKey("EditionId"); + + b.Navigation("Edition"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.TenantConnectionString", b => + { + b.HasOne("LINGYUN.Abp.Saas.Tenants.Tenant", null) + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Abp.WebhooksManagement.WebhookSendRecord", b => + { + b.HasOne("LINGYUN.Abp.WebhooksManagement.WebhookEventRecord", "WebhookEvent") + .WithOne() + .HasForeignKey("LINGYUN.Abp.WebhooksManagement.WebhookSendRecord", "WebhookEventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebhookEvent"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.DataItem", b => + { + b.HasOne("LINGYUN.Platform.Datas.Data", null) + .WithMany("Items") + .HasForeignKey("DataId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackAttachment", b => + { + b.HasOne("LINGYUN.Platform.Feedbacks.Feedback", null) + .WithMany("Attachments") + .HasForeignKey("FeedbackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.FeedbackComment", b => + { + b.HasOne("LINGYUN.Platform.Feedbacks.Feedback", null) + .WithMany("Comments") + .HasForeignKey("FeedbackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageAttachment", b => + { + b.HasOne("LINGYUN.Platform.Messages.EmailMessage", null) + .WithMany("Attachments") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessageHeader", b => + { + b.HasOne("LINGYUN.Platform.Messages.EmailMessage", null) + .WithMany("Headers") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.PackageBlob", b => + { + b.HasOne("LINGYUN.Platform.Packages.Package", "Package") + .WithMany("Blobs") + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Package"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange", null) + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", b => + { + b.HasOne("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", null) + .WithMany() + .HasForeignKey("ApplicationId"); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Tokens.OpenIddictToken", b => + { + b.HasOne("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", null) + .WithMany() + .HasForeignKey("ApplicationId"); + + b.HasOne("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", null) + .WithMany() + .HasForeignKey("AuthorizationId"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityPropertyInfo", b => + { + b.Navigation("Enums"); + }); + + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityTypeInfo", b => + { + b.Navigation("Properties"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Gdpr.GdprRequest", b => + { + b.Navigation("Infos"); + }); + + modelBuilder.Entity("LINGYUN.Abp.Saas.Tenants.Tenant", b => + { + b.Navigation("ConnectionStrings"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Datas.Data", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Feedbacks.Feedback", b => + { + b.Navigation("Attachments"); + + b.Navigation("Comments"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Messages.EmailMessage", b => + { + b.Navigation("Attachments"); + + b.Navigation("Headers"); + }); + + modelBuilder.Entity("LINGYUN.Platform.Packages.Package", b => + { + b.Navigation("Blobs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Navigation("Actions"); + + b.Navigation("EntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Navigation("PropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Navigation("Claims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("OrganizationUnits"); + + b.Navigation("Roles"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Navigation("Roles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260324062303_Add-Notification-Send-Record.cs b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260324062303_Add-Notification-Send-Record.cs new file mode 100644 index 000000000..0bd495012 --- /dev/null +++ b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/20260324062303_Add-Notification-Send-Record.cs @@ -0,0 +1,48 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer.Migrations +{ + /// + public partial class AddNotificationSendRecord : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AppNotificationSendRecords", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Provider = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false), + SendTime = table.Column(type: "datetime2", nullable: false), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + UserName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false, defaultValue: "/"), + NotificationId = table.Column(type: "bigint", nullable: false), + NotificationName = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + State = table.Column(type: "int", nullable: false), + Reason = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AppNotificationSendRecords", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_Tenant_Send_Notification_Name", + table: "AppNotificationSendRecords", + columns: new[] { "TenantId", "NotificationName" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AppNotificationSendRecords"); + } + } +} diff --git a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/SingleMigrationsDbContextModelSnapshot.cs b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/SingleMigrationsDbContextModelSnapshot.cs index c88e40eaa..67bef9187 100644 --- a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/SingleMigrationsDbContextModelSnapshot.cs +++ b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer/Migrations/SingleMigrationsDbContextModelSnapshot.cs @@ -24,6 +24,285 @@ namespace LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer.Migr SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.ConversationRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExpiredAt") + .HasColumnType("datetime2"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UpdateAt") + .HasColumnType("datetime2"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("Id"); + + b.ToTable("AbpAIConversations", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.TextChatMessageRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("ConversationId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ReplyAt") + .HasColumnType("datetime2"); + + b.Property("ReplyMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Workspace") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITextChatMessages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tokens.TokenUsageRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CachedInputTokenCount") + .HasColumnType("bigint"); + + b.Property("ConversationId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("InputTokenCount") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("OutputTokenCount") + .HasColumnType("bigint"); + + b.Property("ReasoningTokenCount") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TotalTokenCount") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ConversationId"); + + b.ToTable("AbpAITokenUsages", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ApiBaseUrl") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ApiKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FrequencyPenalty") + .HasColumnType("real"); + + b.Property("Instructions") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("IsSystem") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MaxOutputTokens") + .HasColumnType("int"); + + b.Property("ModelName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("PresencePenalty") + .HasColumnType("real"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("SystemPrompt") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("Temperature") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpAIWorkspaceDefinitions", (string)null); + }); + modelBuilder.Entity("LINGYUN.Abp.DataProtectionManagement.EntityEnumInfo", b => { b.Property("Id") @@ -1395,6 +1674,59 @@ namespace LY.MicroService.Applications.Single.EntityFrameworkCore.SqlServer.Migr b.ToTable("AppNotificationDefinitions", (string)null); }); + modelBuilder.Entity("LINGYUN.Abp.Notifications.NotificationSendRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("NotificationId") + .HasColumnType("bigint"); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Reason") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("SendTime") + .HasColumnType("datetime2"); + + b.Property("State") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserName") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasDefaultValue("/"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "NotificationName") + .HasDatabaseName("IX_Tenant_Send_Notification_Name"); + + b.ToTable("AppNotificationSendRecords", (string)null); + }); + modelBuilder.Entity("LINGYUN.Abp.Notifications.UserNotification", b => { b.Property("Id") diff --git a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/LY.MicroService.Applications.Single.EntityFrameworkCore.csproj b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/LY.MicroService.Applications.Single.EntityFrameworkCore.csproj index 3da59a64e..45cfc057f 100644 --- a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/LY.MicroService.Applications.Single.EntityFrameworkCore.csproj +++ b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/LY.MicroService.Applications.Single.EntityFrameworkCore.csproj @@ -24,6 +24,7 @@ + diff --git a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/SingleMigrationsDbContext.cs b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/SingleMigrationsDbContext.cs index b94ad4f4b..c977ddc20 100644 --- a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/SingleMigrationsDbContext.cs +++ b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/SingleMigrationsDbContext.cs @@ -1,4 +1,8 @@ -using LINGYUN.Abp.DataProtectionManagement; +using LINGYUN.Abp.AIManagement.Chats; +using LINGYUN.Abp.AIManagement.EntityFrameworkCore; +using LINGYUN.Abp.AIManagement.Tokens; +using LINGYUN.Abp.AIManagement.Workspaces; +using LINGYUN.Abp.DataProtectionManagement; using LINGYUN.Abp.DataProtectionManagement.EntityFrameworkCore; using LINGYUN.Abp.Demo.Authors; using LINGYUN.Abp.Demo.Books; @@ -68,6 +72,7 @@ namespace LY.MicroService.Applications.Single.EntityFrameworkCore; [ReplaceDbContext(typeof(IAbpDataProtectionManagementDbContext))] [ReplaceDbContext(typeof(IGdprDbContext))] [ReplaceDbContext(typeof(IDemoDbContext))] +[ReplaceDbContext(typeof(IAIManagementDbContext))] [ConnectionStringName("Default")] public class SingleMigrationsDbContext : @@ -89,7 +94,8 @@ public class SingleMigrationsDbContext : IMessageServiceDbContext, IAbpDataProtectionManagementDbContext, IGdprDbContext, - IDemoDbContext + IDemoDbContext, + IAIManagementDbContext { public SingleMigrationsDbContext(DbContextOptions options) : base(options) @@ -199,6 +205,8 @@ public class SingleMigrationsDbContext : public DbSet UserSubscribes { get; set; } + public DbSet NotificationSendRecords { get; set; } + public DbSet NotificationDefinitionGroupRecords { get; set; } public DbSet NotificationDefinitionRecords { get; set; } @@ -229,6 +237,14 @@ public class SingleMigrationsDbContext : public DbSet Authors { get; set; } + public DbSet WorkspaceDefinitions { get; set; } + + public DbSet TextChatMessageRecords { get; set; } + + public DbSet ConversationRecords { get; set; } + + public DbSet TokenUsageRecords { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); @@ -252,5 +268,7 @@ public class SingleMigrationsDbContext : modelBuilder.ConfigureGdpr(); modelBuilder.ConfigureDemo(); + + modelBuilder.ConfigureAIManagement(); } } diff --git a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/SingleMigrationsEntityFrameworkCoreModule.cs b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/SingleMigrationsEntityFrameworkCoreModule.cs index 03e2015fc..9a6f76890 100644 --- a/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/SingleMigrationsEntityFrameworkCoreModule.cs +++ b/aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/SingleMigrationsEntityFrameworkCoreModule.cs @@ -1,3 +1,4 @@ +using LINGYUN.Abp.AIManagement.EntityFrameworkCore; using LINGYUN.Abp.AuditLogging.EntityFrameworkCore; using LINGYUN.Abp.Data.DbMigrator; using LINGYUN.Abp.Gdpr.EntityFrameworkCore; @@ -29,6 +30,7 @@ namespace LY.MicroService.Applications.Single.EntityFrameworkCore; typeof(AbpFeatureManagementEntityFrameworkCoreModule), typeof(AbpNotificationsEntityFrameworkCoreModule), typeof(AbpMessageServiceEntityFrameworkCoreModule), + typeof(AbpAIManagementEntityFrameworkCoreModule), typeof(PlatformEntityFrameworkCoreModule), typeof(AbpLocalizationManagementEntityFrameworkCoreModule), typeof(AbpIdentityEntityFrameworkCoreModule), diff --git a/aspnet-core/modules/platform/LINGYUN.Abp.UI.Navigation.VueVbenAdmin5/LINGYUN/Abp/UI/Navigation/VueVbenAdmin5/AbpUINavigationVueVbenAdmin5NavigationDefinitionProvider.cs b/aspnet-core/modules/platform/LINGYUN.Abp.UI.Navigation.VueVbenAdmin5/LINGYUN/Abp/UI/Navigation/VueVbenAdmin5/AbpUINavigationVueVbenAdmin5NavigationDefinitionProvider.cs index 3faa0acd8..275dfb7d6 100644 --- a/aspnet-core/modules/platform/LINGYUN.Abp.UI.Navigation.VueVbenAdmin5/LINGYUN/Abp/UI/Navigation/VueVbenAdmin5/AbpUINavigationVueVbenAdmin5NavigationDefinitionProvider.cs +++ b/aspnet-core/modules/platform/LINGYUN.Abp.UI.Navigation.VueVbenAdmin5/LINGYUN/Abp/UI/Navigation/VueVbenAdmin5/AbpUINavigationVueVbenAdmin5NavigationDefinitionProvider.cs @@ -485,7 +485,7 @@ public class AbpUINavigationVueVbenAdmin5NavigationDefinitionProvider : Navigati multiTenancySides: MultiTenancySides.Host) .SetProperty("title", "abp.manage.notifications.groups")); notificationManagement.AddItem(new ApplicationMenu( - name: "NotificationsDefinitions", + name: "Vben5NotificationsDefinitions", displayName: "通知定义", url: "/manage/notifications/definitions", component: "/notifications/definitions/index", @@ -493,6 +493,15 @@ public class AbpUINavigationVueVbenAdmin5NavigationDefinitionProvider : Navigati description: "通知定义", multiTenancySides: MultiTenancySides.Host) .SetProperty("title", "abp.manage.notifications.definitions")); + notificationManagement.AddItem(new ApplicationMenu( + name: "Vben5NotificationsSendRecords", + displayName: "发送记录", + url: "/manage/notifications/send-records", + component: "/notifications/send-records/index", + icon: "material-symbols:history", + description: "发送记录", + multiTenancySides: MultiTenancySides.Host) + .SetProperty("title", "abp.manage.notifications.sendRecords")); manage.AddItem( new ApplicationMenu( diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Dto/NotificationSendRecordDto.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Dto/NotificationSendRecordDto.cs new file mode 100644 index 000000000..2706ef671 --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Dto/NotificationSendRecordDto.cs @@ -0,0 +1,14 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace LINGYUN.Abp.Notifications; +public class NotificationSendRecordDto : EntityDto +{ + public string Provider { get; set; } + public DateTime SendTime { get; set; } + public Guid UserId { get; set; } + public string UserName { get; set; } + public NotificationSendState State { get; set; } + public string Reason { get; set; } + public UserNotificationDto Notification { get; set; } +} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Dto/NotificationSendRecordGetPagedListInput.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Dto/NotificationSendRecordGetPagedListInput.cs new file mode 100644 index 000000000..e1b19dd67 --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Dto/NotificationSendRecordGetPagedListInput.cs @@ -0,0 +1,16 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace LINGYUN.Abp.Notifications; + +#nullable enable +public class NotificationSendRecordGetPagedListInput : PagedAndSortedResultRequestDto +{ + public string? Provider { get; set; } + public DateTime? BeginSendTime { get; set; } + public DateTime? EndSendTime { get; set; } + public Guid? UserId { get; set; } + public string? NotificationName { get; set; } + public NotificationSendState? State { get; set; } +} +#nullable disable diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/INotificationSendRecordAppService.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/INotificationSendRecordAppService.cs new file mode 100644 index 000000000..c49ea2b66 --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/INotificationSendRecordAppService.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace LINGYUN.Abp.Notifications; +public interface INotificationSendRecordAppService : IApplicationService +{ + Task DeleteAsync(long id); + + Task ReSendAsync(long id); + + Task> GetListAsync(NotificationSendRecordGetPagedListInput input); +} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Permissions/NotificationsPermissions.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Permissions/NotificationsPermissions.cs index 778559b7f..13ddbf40c 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Permissions/NotificationsPermissions.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Permissions/NotificationsPermissions.cs @@ -10,6 +10,12 @@ public class NotificationsPermissions public const string Delete = Default + ".Delete"; public const string Send = Default + ".Send"; + public static class SendRecord + { + public const string Default = Notification.Default + ".SendRecord"; + public const string ReSend = Default + ".ReSend"; + public const string Delete = Default + ".Delete"; + } } public static class GroupDefinition diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Permissions/NotificationsPermissionsDefinitionProvider.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Permissions/NotificationsPermissionsDefinitionProvider.cs index 7f6b614a7..56f3d5afa 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Permissions/NotificationsPermissionsDefinitionProvider.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Permissions/NotificationsPermissionsDefinitionProvider.cs @@ -48,6 +48,11 @@ public class NotificationsPermissionsDefinitionProvider : PermissionDefinitionPr var noticeGroup = group.AddPermission(NotificationsPermissions.Notification.Default, L("Permission:Notification")); noticeGroup.AddChild(NotificationsPermissions.Notification.Delete, L("Permission:Delete")); noticeGroup.AddChild(NotificationsPermissions.Notification.Send, L("Permission:Send")); + var notificationSendPermission = noticeGroup.AddChild( + NotificationsPermissions.Notification.SendRecord.Default, + L("Permission:SendRecord")); + notificationSendPermission.AddChild(NotificationsPermissions.Notification.SendRecord.Delete, L("Permission:Delete")); + notificationSendPermission.AddChild(NotificationsPermissions.Notification.SendRecord.ReSend, L("Permission:ReSend")); } private static LocalizableString L(string name) diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/AbpNotificationsApplicationMappers.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/AbpNotificationsApplicationMappers.cs index 440ccc130..0e03eb6a0 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/AbpNotificationsApplicationMappers.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/AbpNotificationsApplicationMappers.cs @@ -33,3 +33,29 @@ public partial class UserNotificationInfoToUserNotificationDtoMapper : MapperBas return new NotificationData(); } } + +[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] +public partial class NotificationSendRecordInfoToNotificationSendRecordDtoMapper : MapperBase +{ + private readonly UserNotificationInfoToUserNotificationDtoMapper _userNotificationInfoMapper; + public NotificationSendRecordInfoToNotificationSendRecordDtoMapper(UserNotificationInfoToUserNotificationDtoMapper userNotificationInfoMapper) + { + _userNotificationInfoMapper = userNotificationInfoMapper; + } + + [MapProperty(nameof(NotificationSendRecordInfo.Id), nameof(NotificationSendRecordDto.Id))] + public override partial NotificationSendRecordDto Map(NotificationSendRecordInfo source); + + [MapProperty(nameof(NotificationSendRecordInfo.Id), nameof(NotificationSendRecordDto.Id))] + public override partial void Map(NotificationSendRecordInfo source, NotificationSendRecordDto destination); + + public override void AfterMap(NotificationSendRecordInfo source, NotificationSendRecordDto destination) + { + if (source.NotificationInfo != null) + { + var userNotificationInfoDto = _userNotificationInfoMapper.Map(source.NotificationInfo); + _userNotificationInfoMapper.AfterMap(source.NotificationInfo, userNotificationInfoDto); + destination.Notification = userNotificationInfoDto; + } + } +} \ No newline at end of file diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/NotificationSendRecordAppService.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/NotificationSendRecordAppService.cs new file mode 100644 index 000000000..706402060 --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/NotificationSendRecordAppService.cs @@ -0,0 +1,107 @@ +using LINGYUN.Abp.Notifications.Permissions; +using Microsoft.AspNetCore.Authorization; +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Specifications; + +namespace LINGYUN.Abp.Notifications; + +[Authorize(NotificationsPermissions.Notification.SendRecord.Default)] +public class NotificationSendRecordAppService : AbpNotificationsApplicationServiceBase, INotificationSendRecordAppService +{ + private readonly INotificationSendRecordRepository _repository; + + protected INotificationStore NotificationStore => LazyServiceProvider.LazyGetRequiredService(); + + protected INotificationSender NotificationSender => LazyServiceProvider.LazyGetRequiredService(); + + protected INotificationDefinitionManager NotificationDefinitionManager => LazyServiceProvider.LazyGetRequiredService(); + + public NotificationSendRecordAppService(INotificationSendRecordRepository repository) + { + _repository = repository; + } + + [Authorize(NotificationsPermissions.Notification.SendRecord.Delete)] + public async virtual Task DeleteAsync(long id) + { + var sendRecord = await _repository.GetAsync(id); + + await _repository.DeleteAsync(sendRecord); + } + + [Authorize(NotificationsPermissions.Notification.SendRecord.ReSend)] + public async virtual Task ReSendAsync(long id) + { + var sendRecord = await _repository.GetAsync(id); + var notificationInfo = await NotificationStore.GetNotificationOrNullAsync(sendRecord.TenantId, sendRecord.NotificationId); + var notificationDefine = await NotificationDefinitionManager.GetOrNullAsync(notificationInfo.Name); + + if (notificationDefine?.Template != null) + { + var template = new NotificationTemplate( + notificationInfo.Name, + data: notificationInfo.Data.ExtraProperties); + + await NotificationSender.SendNofiterAsync( + notificationInfo.Name, + template, + [new UserIdentifier(sendRecord.UserId, sendRecord.UserName)], + sendRecord.TenantId, + notificationInfo.Severity, + [sendRecord.Provider]); + } + else + { + await NotificationSender.SendNofiterAsync( + notificationInfo.Name, + notificationInfo.Data, + [new UserIdentifier(sendRecord.UserId, sendRecord.UserName)], + sendRecord.TenantId, + notificationInfo.Severity, + [sendRecord.Provider]); + } + } + + public async virtual Task> GetListAsync(NotificationSendRecordGetPagedListInput input) + { + Expression> expression = _ => true; + + if (input.State.HasValue) + { + expression = expression.And(x => x.State == input.State); + } + if (!input.Provider.IsNullOrWhiteSpace()) + { + expression = expression.And(x => x.Provider == input.Provider); + } + if (!input.NotificationName.IsNullOrWhiteSpace()) + { + expression = expression.And(x => x.NotificationInfo.Name == input.NotificationName); + } + if (input.UserId.HasValue) + { + expression = expression.And(x => x.UserId == input.UserId); + } + if (input.BeginSendTime.HasValue) + { + expression = expression.And(x => x.SendTime >= input.BeginSendTime); + } + if (input.EndSendTime.HasValue) + { + expression = expression.And(x => x.SendTime <= input.EndSendTime); + } + + var specification = new ExpressionSpecification(expression); + + var totalCount = await _repository.GetCountAsync(specification); + var list = await _repository.GetListAsync(specification, + input.Sorting, input.MaxResultCount, input.SkipCount); + + return new PagedResultDto(totalCount, + ObjectMapper.Map, List>(list)); + } +} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationPublishContext.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationPublishContext.cs new file mode 100644 index 000000000..d8872f63f --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationPublishContext.cs @@ -0,0 +1,35 @@ +using JetBrains.Annotations; +using System; +using System.Collections.Generic; + +namespace LINGYUN.Abp.Notifications; + +#nullable enable +public class NotificationPublishContext +{ + [NotNull] + public NotificationInfo Notification { get; } + + [CanBeNull] + public IEnumerable Users { get; } + + [CanBeNull] + public string? Reason { get; private set; } + + [CanBeNull] + public Exception? Exception { get; private set; } + public NotificationPublishContext( + NotificationInfo notification, + IEnumerable users) + { + Notification = notification; + Users = users; + } + + public void Cancel(string reason, Exception exception = null) + { + Reason = reason; + Exception = exception; + } +} +#nullable disable diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationSendInfo.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationSendInfo.cs new file mode 100644 index 000000000..16dc10c33 --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationSendInfo.cs @@ -0,0 +1,49 @@ +using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using Volo.Abp; + +namespace LINGYUN.Abp.Notifications; +public class NotificationSendInfo +{ + public string Provider { get; } + public DateTime SendTime { get; } + public NotificationInfo NotificationInfo { get; } + public IEnumerable Users { get; } + public NotificationSendState State { get; private set; } + public string Reason { get; private set; } + public NotificationSendInfo( + [NotNull] string provider, + DateTime sendTime, + NotificationInfo notificationInfo, + IEnumerable users) + { + Check.NotNullOrWhiteSpace(provider, nameof(provider)); + Check.NotNull(notificationInfo, nameof(notificationInfo)); + Check.NotNull(users, nameof(users)); + + Provider = provider; + SendTime = sendTime; + NotificationInfo = notificationInfo; + Users = users; + + State = NotificationSendState.None; + } + + public void Cancel(string reason) + { + State = NotificationSendState.None; + Reason = reason; + } + + public void Disbaled() + { + State = NotificationSendState.Disabled; + } + + public void Sent(Exception exception = null) + { + State = exception != null ? NotificationSendState.Failed : NotificationSendState.Sent; + Reason = exception?.Message; + } +} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationSendState.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationSendState.cs new file mode 100644 index 000000000..82f7fddb7 --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationSendState.cs @@ -0,0 +1,23 @@ +namespace LINGYUN.Abp.Notifications; +/// +/// 发送状态 +/// +public enum NotificationSendState +{ + /// + /// 未发送 + /// + None, + /// + /// 提供者禁用 + /// + Disabled, + /// + /// 已发送 + /// + Sent, + /// + /// 发送失败 + /// + Failed +} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/Localization/DomainShared/en.json b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/Localization/DomainShared/en.json index cf3b8bdb5..0bd05c282 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/Localization/DomainShared/en.json +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/Localization/DomainShared/en.json @@ -5,10 +5,12 @@ "Permission:GroupDefinitions": "Group Definitions", "Permission:NotificationDefinitions": "Notification Definitions", "Permission:Notification": "Manage Notification", + "Permission:SendRecord": "Send Records", "Permission:Create": "Create", "Permission:Edit": "Edit", "Permission:Delete": "Delete", "Permission:Send": "Send", + "Permission:ReSend": "Re Send", "Notifications:001404": "The notification template does not exist!", "Notifications:002400": "The static notification group {Name} is not allowed to change!", "Notifications:002403": "Notification that the {Name} group already exists!", @@ -34,6 +36,11 @@ "DisplayName:Template": "Template", "Description:Template": "The notification template is required when sending template notifications.", "DisplayName:IsStatic": "Static", + "DisplayName:SendTime": "Send Time", + "DisplayName:Provider": "Provider", + "DisplayName:NotificationName": "Notification Name", + "DisplayName:SendState": "Send State", + "DisplayName:UserName": "User Name", "Providers:Emailing": "Email", "Providers:SignalR": "SignalR", "Providers:Sms": "Sms", @@ -49,6 +56,14 @@ "TemplateContent": "Template Content", "ItemWillBeDeleteOrRestoreMessage": "If the notification has been changed, it will revert to the default notification. Otherwise, the custom notification is removed.", "Notifications:Send": "Send Notification", - "SendSuccessfully": "Send Successfully" + "SendSuccessfully": "Send Successfully", + "SendRecords": "Send Records", + "Resend": "Re Send", + "NotificationSendState:None": "None", + "NotificationSendState:Disabled": "Disabled", + "NotificationSendState:Sent": "Sent", + "NotificationSendState:Failed": "Failed", + "SelectedSendRecordWillBeDeleteMessage": "The selected sent messages will be deleted!", + "SelectedSendRecordWillBeReSendMessage": "Send the record of the notification for the re-sending of the selection!" } } \ No newline at end of file diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/Localization/DomainShared/zh-Hans.json b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/Localization/DomainShared/zh-Hans.json index b2f50c5f5..0c52f86f5 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/Localization/DomainShared/zh-Hans.json +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/Localization/DomainShared/zh-Hans.json @@ -5,10 +5,12 @@ "Permission:GroupDefinitions": "分组定义", "Permission:NotificationDefinitions": "通知定义", "Permission:Notification": "管理通知", + "Permission:SendRecord": "发送记录", "Permission:Create": "新增", "Permission:Edit": "编辑", "Permission:Delete": "删除", "Permission:Send": "发送通知", + "Permission:ReSend": "重新发送", "Notifications:001404": "通知模板不存在!", "Notifications:002400": "静态通知分组 {Name} 不允许变更!", "Notifications:002403": "通知分组 {Name} 已经存在!", @@ -34,6 +36,11 @@ "DisplayName:Template": "模板", "Description:Template": "发送模板通知时需要提供通知模板.", "DisplayName:IsStatic": "静态", + "DisplayName:SendTime": "发送时间", + "DisplayName:Provider": "提供者", + "DisplayName:NotificationName": "通知名称", + "DisplayName:SendState": "发送状态", + "DisplayName:UserName": "用户名", "Providers:Emailing": "邮件", "Providers:SignalR": "SignalR", "Providers:Sms": "短信", @@ -49,6 +56,14 @@ "TemplateContent": "模板内容", "ItemWillBeDeleteOrRestoreMessage": "如果已改变通知, 将还原到默认通知。否则会删除自定义通知.", "Notifications:Send": "发送通知", - "SendSuccessfully": "发送成功" + "SendSuccessfully": "发送成功", + "SendRecords": "发送记录", + "Resend": "重新发送", + "NotificationSendState:None": "未发送", + "NotificationSendState:Disabled": "提供者禁用", + "NotificationSendState:Sent": "已发送", + "NotificationSendState:Failed": "发送失败", + "SelectedSendRecordWillBeDeleteMessage": "选择的发送记录将被删除!", + "SelectedSendRecordWillBeReSendMessage": "将重新发送选择的通知发送记录!" } } \ No newline at end of file diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/NotificationSendRecordConsts.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/NotificationSendRecordConsts.cs new file mode 100644 index 000000000..d4b47026e --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/NotificationSendRecordConsts.cs @@ -0,0 +1,6 @@ +namespace LINGYUN.Abp.Notifications; +public static class NotificationSendRecordConsts +{ + public static int MaxProviderLength { get; set; } = 50; + public static int MaxReasonLength { get; set; } = 255; +} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/INotificationSendRecordRepository.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/INotificationSendRecordRepository.cs new file mode 100644 index 000000000..c239bdc99 --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/INotificationSendRecordRepository.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Specifications; + +namespace LINGYUN.Abp.Notifications; +public interface INotificationSendRecordRepository : IBasicRepository +{ + Task GetCountAsync( + ISpecification specification, + CancellationToken cancellationToken = default); + + Task> GetListAsync( + ISpecification specification, + string sorting = $"{nameof(NotificationSendRecordInfo.SendTime)} DESC", + int maxResultCount = 10, + int skipCount = 0, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/IUserNotificationRepository.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/IUserNotificationRepository.cs index 02bd12cdc..80a2994f7 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/IUserNotificationRepository.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/IUserNotificationRepository.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; +using Volo.Abp.Specifications; namespace LINGYUN.Abp.Notifications; @@ -47,4 +48,17 @@ public interface IUserNotificationRepository : IBasicRepository GetCountAsync( + Guid userId, + ISpecification specification, + CancellationToken cancellationToken = default); + + Task> GetListAsync( + Guid userId, + ISpecification specification, + string sorting = nameof(Notification.CreationTime), + int skipCount = 0, + int maxResultCount = 10, + CancellationToken cancellationToken = default); } diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationSendRecord.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationSendRecord.cs new file mode 100644 index 000000000..947bbd0c1 --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationSendRecord.cs @@ -0,0 +1,48 @@ +using System; +using Volo.Abp; +using Volo.Abp.Domain.Entities; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.Notifications; +public class NotificationSendRecord : Entity, IMultiTenant +{ + public virtual Guid? TenantId { get; protected set; } + public virtual string Provider { get; protected set; } + public virtual DateTime SendTime { get; protected set; } + public virtual Guid UserId { get; protected set; } + public virtual string UserName { get; protected set; } + public virtual long NotificationId { get; protected set; } + public virtual string NotificationName { get; protected set; } + public virtual NotificationSendState State { get; protected set; } + public virtual string Reason { get; protected set; } + protected NotificationSendRecord() + { + } + + public NotificationSendRecord( + string provider, + DateTime sendTime, + Guid userId, + string userName, + long notificationId, + string notificationName, + NotificationSendState state, + string reason = null, + Guid? tenantId = null) + { + Provider = Check.NotNullOrWhiteSpace(provider, nameof(provider), NotificationSendRecordConsts.MaxProviderLength); + SendTime = sendTime; + UserId = userId; + UserName = Check.Length(userName, nameof(userName), SubscribeConsts.MaxUserNameLength); + NotificationId = notificationId; + NotificationName = Check.NotNullOrWhiteSpace(notificationName, nameof(notificationName), NotificationConsts.MaxNameLength); + State = state; + Reason = reason; + TenantId = tenantId; + + if (!Reason.IsNullOrWhiteSpace() && Reason.Length > NotificationSendRecordConsts.MaxReasonLength) + { + Reason = Reason.Substring(0, NotificationSendRecordConsts.MaxReasonLength); + } + } +} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationSendRecordInfo.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationSendRecordInfo.cs new file mode 100644 index 000000000..44543fdd5 --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationSendRecordInfo.cs @@ -0,0 +1,14 @@ +using System; + +namespace LINGYUN.Abp.Notifications; +public class NotificationSendRecordInfo +{ + public long Id { get; set; } + public string Provider { get; set; } + public DateTime SendTime { get; set; } + public Guid UserId { get; set; } + public string UserName { get; set; } + public NotificationSendState State { get; set; } + public string Reason { get; set; } + public UserNotificationInfo NotificationInfo { get; set; } +} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationStore.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationStore.cs index 4d13f2cda..d4b6b59f2 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationStore.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/NotificationStore.cs @@ -25,6 +25,8 @@ public class NotificationStore : INotificationStore private readonly INotificationRepository _notificationRepository; + private readonly INotificationSendRecordRepository _notificationSendRecordRepository; + private readonly IUserNotificationRepository _userNotificationRepository; private readonly IUserSubscribeRepository _userSubscribeRepository; @@ -40,6 +42,7 @@ public class NotificationStore : INotificationStore INotificationRepository notificationRepository, IUserSubscribeRepository userSubscribeRepository, IUserNotificationRepository userNotificationRepository, + INotificationSendRecordRepository notificationSendRecordRepository, IOptions options, IObjectMapper objectMapper) { @@ -50,10 +53,40 @@ public class NotificationStore : INotificationStore _notificationRepository = notificationRepository; _userSubscribeRepository = userSubscribeRepository; _userNotificationRepository = userNotificationRepository; + _notificationSendRecordRepository = notificationSendRecordRepository; _options = options.Value; } + public async virtual Task InsertSendStateAsync( + NotificationSendInfo notificationSendInfo, + CancellationToken cancellationToken = default) + { + if (!notificationSendInfo.Users.Any()) + { + return; + } + var notificationSendRecords = notificationSendInfo.Users + .Select(user => new NotificationSendRecord( + notificationSendInfo.Provider, + notificationSendInfo.SendTime, + user.UserId, + user.UserName, + notificationSendInfo.NotificationInfo.GetId(), + notificationSendInfo.NotificationInfo.Name, + notificationSendInfo.State, + notificationSendInfo.Reason, + notificationSendInfo.NotificationInfo.TenantId)); + + using (var unitOfWork = _unitOfWorkManager.Begin()) + using (_currentTenant.Change(notificationSendInfo.NotificationInfo.TenantId)) + { + await _notificationSendRecordRepository.InsertManyAsync(notificationSendRecords); + + await unitOfWork.CompleteAsync(); + } + } + public async virtual Task ChangeUserNotificationReadStateAsync( Guid? tenantId, Guid userId, diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Emailing/LINGYUN/Abp/Notifications/Emailing/EmailingNotificationPublishProvider.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Emailing/LINGYUN/Abp/Notifications/Emailing/EmailingNotificationPublishProvider.cs index f726a9c3b..209d58a3f 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Emailing/LINGYUN/Abp/Notifications/Emailing/EmailingNotificationPublishProvider.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Emailing/LINGYUN/Abp/Notifications/Emailing/EmailingNotificationPublishProvider.cs @@ -21,11 +21,10 @@ public class EmailingNotificationPublishProvider : NotificationPublishProvider protected INotificationDataSerializer NotificationDataSerializer => ServiceProvider.LazyGetRequiredService(); protected async override Task PublishAsync( - NotificationInfo notification, - IEnumerable identifiers, + NotificationPublishContext context, CancellationToken cancellationToken = default) { - var userIds = identifiers.Select(x => x.UserId).ToList(); + var userIds = context.Users.Select(x => x.UserId).ToList(); var userList = await UserRepository.GetListByIdListAsync(userIds, cancellationToken: cancellationToken); var emailAddress = userList @@ -47,19 +46,21 @@ public class EmailingNotificationPublishProvider : NotificationPublishProvider if (emailAddress.IsNullOrWhiteSpace()) { - Logger.LogWarning("The subscriber did not confirm the email address and could not send email notifications!"); + var reason = "The subscriber did not confirm the email address and could not send email notifications!"; + Logger.LogWarning(reason); + context.Cancel(reason); return; } - var notificationData = await NotificationDataSerializer.ToStandard(notification.Data); + var notificationData = await NotificationDataSerializer.ToStandard(context.Notification.Data); // markdown进行处理 - if (notification.ContentType == NotificationContentType.Markdown) + if (context.Notification.ContentType == NotificationContentType.Markdown) { notificationData.Message = Markdown.ToHtml(notificationData.Message); } await EmailSender.SendAsync(emailAddress, notificationData.Title, notificationData.Message); - Logger.LogDebug("The notification: {0} with provider: {1} has successfully published!", notification.Name, Name); + Logger.LogDebug("The notification: {0} with provider: {1} has successfully published!", context.Notification.Name, Name); } } diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/AbpNotificationsEntityFrameworkCoreModule.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/AbpNotificationsEntityFrameworkCoreModule.cs index 44b87f57c..d48e540cd 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/AbpNotificationsEntityFrameworkCoreModule.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/AbpNotificationsEntityFrameworkCoreModule.cs @@ -19,6 +19,8 @@ public class AbpNotificationsEntityFrameworkCoreModule : AbpModule options.AddRepository(); options.AddRepository(); + + options.AddRepository(); }); context.Services.AddAbpDbContext(options => diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/EfCoreNotificationSendRecordRepository.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/EfCoreNotificationSendRecordRepository.cs new file mode 100644 index 000000000..0977dc942 --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/EfCoreNotificationSendRecordRepository.cs @@ -0,0 +1,99 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Specifications; + +namespace LINGYUN.Abp.Notifications.EntityFrameworkCore; +public class EfCoreNotificationSendRecordRepository : + EfCoreRepository, + INotificationSendRecordRepository +{ + public EfCoreNotificationSendRecordRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public async virtual Task GetCountAsync( + ISpecification specification, + CancellationToken cancellationToken = default) + { + return await (await GetSendRecordInfoAsync()) + .Where(specification.ToExpression()) + .CountAsync(GetCancellationToken(cancellationToken)); + } + + public async virtual Task> GetListAsync( + ISpecification specification, + string sorting = $"{nameof(NotificationSendRecordInfo.SendTime)} DESC", + int maxResultCount = 10, + int skipCount = 0, + CancellationToken cancellationToken = default) + { + return await (await GetSendRecordInfoAsync()) + .Where(specification.ToExpression()) + .OrderBy(!sorting.IsNullOrWhiteSpace() ? sorting : $"{nameof(NotificationSendRecordInfo.SendTime)} DESC") + .PageBy(skipCount, maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + protected async virtual Task> GetSendRecordInfoAsync() + { + var dbContext = await GetDbContextAsync(); + + return dbContext.Set() + .Join( + dbContext.Set(), + n => n.NotificationId, + un => un.NotificationId, + (n, un) => new + { + NotificationId = n.NotificationId, + NotificationName = n.NotificationName, + NotificationTypeName = n.NotificationTypeName, + TenantId = n.TenantId, + Type = n.Type, + Severity = n.Severity, + ContentType = n.ContentType, + CreationTime = n.CreationTime, + ExtraProperties = n.ExtraProperties, + State = un.ReadStatus, + UserId = un.UserId, + Id = un.Id, + }) + .Join( + dbContext.Set(), + un => new { un.UserId, un.NotificationId }, + nsr => new { nsr.UserId, nsr.NotificationId }, + (un, nsr) => new NotificationSendRecordInfo + { + Id = nsr.Id, + UserId = nsr.UserId, + UserName = nsr.UserName, + Provider = nsr.Provider, + State = nsr.State, + Reason = nsr.Reason, + SendTime = nsr.SendTime, + NotificationInfo = new UserNotificationInfo + { + TenantId = un.TenantId, + Type = un.Type, + Severity = un.Severity, + ContentType = un.ContentType, + CreationTime = un.CreationTime, + ExtraProperties = un.ExtraProperties, + NotificationId = un.NotificationId, + NotificationTypeName = un.NotificationTypeName, + Name = un.NotificationName, + State = un.State, + Id = un.Id, + }, + }); + } +} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/INotificationsDbContext.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/INotificationsDbContext.cs index e21a9893a..6cc0921f9 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/INotificationsDbContext.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/INotificationsDbContext.cs @@ -10,7 +10,5 @@ public interface INotificationsDbContext : IEfCoreDbContext DbSet Notifications { get; } DbSet UserNotifications { get; } DbSet UserSubscribes { get; } - - DbSet NotificationDefinitionGroupRecords { get; } - DbSet NotificationDefinitionRecords { get; } + DbSet NotificationSendRecords { get; } } diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/NotificationsDbContext.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/NotificationsDbContext.cs index bda29b424..013ecbc28 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/NotificationsDbContext.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/NotificationsDbContext.cs @@ -10,9 +10,7 @@ public class NotificationsDbContext : AbpDbContext, INot public DbSet Notifications { get; set; } public DbSet UserNotifications { get; set; } public DbSet UserSubscribes { get; set; } - - public DbSet NotificationDefinitionGroupRecords { get; set; } - public DbSet NotificationDefinitionRecords { get; set; } + public DbSet NotificationSendRecords { get; set; } public NotificationsDbContext(DbContextOptions options) : base(options) diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/NotificationsDbContextModelCreatingExtensions.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/NotificationsDbContextModelCreatingExtensions.cs index 8923a3b0c..c1a350e27 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/NotificationsDbContextModelCreatingExtensions.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/NotificationsDbContextModelCreatingExtensions.cs @@ -1,5 +1,4 @@ using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; using System; using Volo.Abp; using Volo.Abp.EntityFrameworkCore.Modeling; @@ -60,6 +59,25 @@ public static class NotificationsDbContextModelCreatingExtensions .HasDatabaseName("IX_Tenant_User_Notification_Name") .IsUnique(); }); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "NotificationSendRecords", options.Schema); + + b.Property(p => p.Provider).HasMaxLength(NotificationSendRecordConsts.MaxProviderLength).IsRequired(); + b.Property(p => p.NotificationName).HasMaxLength(NotificationConsts.MaxNameLength).IsRequired(); + b.Property(p => p.UserName) + .HasMaxLength(SubscribeConsts.MaxUserNameLength) + .HasDefaultValue("/")// 不是必须的 + .IsRequired(); + + b.Property(p => p.Reason).HasMaxLength(NotificationSendRecordConsts.MaxReasonLength); + + b.ConfigureByConvention(); + + b.HasIndex(p => new { p.TenantId, p.NotificationName }) + .HasDatabaseName("IX_Tenant_Send_Notification_Name"); + }); } public static void ConfigureNotificationsDefinition( @@ -72,44 +90,47 @@ public static class NotificationsDbContextModelCreatingExtensions optionsAction?.Invoke(options); - builder.Entity(b => + if (builder.IsHostDatabase()) { - b.ToTable(options.TablePrefix + "NotificationDefinitionGroups", options.Schema); - b.Property(p => p.Name) - .HasMaxLength(NotificationDefinitionGroupRecordConsts.MaxNameLength) - .IsRequired(); - - b.Property(p => p.DisplayName) - .HasMaxLength(NotificationDefinitionGroupRecordConsts.MaxDisplayNameLength); - b.Property(p => p.Description) - .HasMaxLength(NotificationDefinitionGroupRecordConsts.MaxDescriptionLength); - - b.ConfigureByConvention(); - }); - - builder.Entity(b => - { - b.ToTable(options.TablePrefix + "NotificationDefinitions", options.Schema); - b.Property(p => p.Name) - .HasMaxLength(NotificationDefinitionRecordConsts.MaxNameLength) - .IsRequired(); - b.Property(p => p.GroupName) - .HasMaxLength(NotificationDefinitionGroupRecordConsts.MaxNameLength) - .IsRequired(); - - b.Property(p => p.DisplayName) - .HasMaxLength(NotificationDefinitionRecordConsts.MaxDisplayNameLength); - b.Property(p => p.Description) - .HasMaxLength(NotificationDefinitionRecordConsts.MaxDescriptionLength); - b.Property(p => p.Providers) - .HasMaxLength(NotificationDefinitionRecordConsts.MaxProvidersLength); - b.Property(p => p.Template) - .HasMaxLength(NotificationDefinitionRecordConsts.MaxTemplateLength); - - b.Property(p => p.ContentType) - .HasDefaultValue(NotificationContentType.Text); - - b.ConfigureByConvention(); - }); + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "NotificationDefinitionGroups", options.Schema); + b.Property(p => p.Name) + .HasMaxLength(NotificationDefinitionGroupRecordConsts.MaxNameLength) + .IsRequired(); + + b.Property(p => p.DisplayName) + .HasMaxLength(NotificationDefinitionGroupRecordConsts.MaxDisplayNameLength); + b.Property(p => p.Description) + .HasMaxLength(NotificationDefinitionGroupRecordConsts.MaxDescriptionLength); + + b.ConfigureByConvention(); + }); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "NotificationDefinitions", options.Schema); + b.Property(p => p.Name) + .HasMaxLength(NotificationDefinitionRecordConsts.MaxNameLength) + .IsRequired(); + b.Property(p => p.GroupName) + .HasMaxLength(NotificationDefinitionGroupRecordConsts.MaxNameLength) + .IsRequired(); + + b.Property(p => p.DisplayName) + .HasMaxLength(NotificationDefinitionRecordConsts.MaxDisplayNameLength); + b.Property(p => p.Description) + .HasMaxLength(NotificationDefinitionRecordConsts.MaxDescriptionLength); + b.Property(p => p.Providers) + .HasMaxLength(NotificationDefinitionRecordConsts.MaxProvidersLength); + b.Property(p => p.Template) + .HasMaxLength(NotificationDefinitionRecordConsts.MaxTemplateLength); + + b.Property(p => p.ContentType) + .HasDefaultValue(NotificationContentType.Text); + + b.ConfigureByConvention(); + }); + } } } diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.HttpApi/LINGYUN/Abp/Notifications/NotificationSendRecordController.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.HttpApi/LINGYUN/Abp/Notifications/NotificationSendRecordController.cs new file mode 100644 index 000000000..cffbda869 --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.HttpApi/LINGYUN/Abp/Notifications/NotificationSendRecordController.cs @@ -0,0 +1,45 @@ +using LINGYUN.Abp.Notifications.Permissions; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace LINGYUN.Abp.Notifications; + +[Controller] +[RemoteService(Name = AbpNotificationsRemoteServiceConsts.RemoteServiceName)] +[Area(AbpNotificationsRemoteServiceConsts.ModuleName)] +[Route("api/notifications/send-records")] +[Authorize(NotificationsPermissions.Notification.SendRecord.Default)] +public class NotificationSendRecordController : AbpControllerBase, INotificationSendRecordAppService +{ + private readonly INotificationSendRecordAppService _service; + public NotificationSendRecordController(INotificationSendRecordAppService service) + { + _service = service; + } + + [HttpDelete] + [Route("{id}")] + [Authorize(NotificationsPermissions.Notification.SendRecord.Delete)] + public virtual Task DeleteAsync(long id) + { + return _service.DeleteAsync(id); + } + + [HttpGet] + public virtual Task> GetListAsync(NotificationSendRecordGetPagedListInput input) + { + return _service.GetListAsync(input); + } + + [HttpPost] + [Route("{id}/re-send")] + [Authorize(NotificationsPermissions.Notification.SendRecord.ReSend)] + public virtual Task ReSendAsync(long id) + { + return _service.ReSendAsync(id); + } +} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.PushPlus/LINGYUN/Abp/Notifications/PushPlus/PushPlusNotificationPublishProvider.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.PushPlus/LINGYUN/Abp/Notifications/PushPlus/PushPlusNotificationPublishProvider.cs index 4cc9ddbcd..4b5c417b1 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.PushPlus/LINGYUN/Abp/Notifications/PushPlus/PushPlusNotificationPublishProvider.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.PushPlus/LINGYUN/Abp/Notifications/PushPlus/PushPlusNotificationPublishProvider.cs @@ -35,13 +35,12 @@ public class PushPlusNotificationPublishProvider : NotificationPublishProvider } protected async override Task PublishAsync( - NotificationInfo notification, - IEnumerable identifiers, + NotificationPublishContext context, CancellationToken cancellationToken = default) { var topic = ""; - var notificationDefine = await NotificationDefinitionManager.GetOrNullAsync(notification.Name); + var notificationDefine = await NotificationDefinitionManager.GetOrNullAsync(context.Notification.Name); var topicDefine = notificationDefine?.GetTopicOrNull(); if (!topicDefine.IsNullOrWhiteSpace()) { @@ -51,9 +50,9 @@ public class PushPlusNotificationPublishProvider : NotificationPublishProvider ?? PushPlusChannelType.Email; var template = notificationDefine?.GetTemplateOrDefault(PushPlusMessageTemplate.Text) ?? PushPlusMessageTemplate.Text; - var webhook = notification.Data.GetWebhookOrNull() ?? ""; - var callbackUrl = notification.Data.GetCallbackUrlOrNull() ?? ""; - var notificationData = await NotificationDataSerializer.ToStandard(notification.Data); + var webhook = context.Notification.Data.GetWebhookOrNull() ?? ""; + var callbackUrl = context.Notification.Data.GetCallbackUrlOrNull() ?? ""; + var notificationData = await NotificationDataSerializer.ToStandard(context.Notification.Data); await PushPlusMessageSender.SendWithChannelAsync( notificationData.Title, @@ -65,6 +64,6 @@ public class PushPlusNotificationPublishProvider : NotificationPublishProvider callbackUrl: callbackUrl, cancellationToken: cancellationToken); - Logger.LogDebug("The notification: {0} with provider: {1} has successfully published!", notification.Name, Name); + Logger.LogDebug("The notification: {0} with provider: {1} has successfully published!", context.Notification.Name, Name); } } diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/SignalRNotificationPublishProvider.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/SignalRNotificationPublishProvider.cs index f1672afee..f18fa9df4 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/SignalRNotificationPublishProvider.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/SignalRNotificationPublishProvider.cs @@ -27,40 +27,43 @@ public class SignalRNotificationPublishProvider : NotificationPublishProvider } protected async override Task PublishAsync( - NotificationInfo notification, - IEnumerable identifiers, + NotificationPublishContext context, CancellationToken cancellationToken = default) { - if (identifiers?.Count() == 0) + if (!context.Users.Any()) { - var groupName = notification.TenantId?.ToString() ?? "Global"; + var groupName = context.Notification.TenantId?.ToString() ?? "Global"; try { var singalRGroup = _hubContext.Clients.Group(groupName); // 租户通知群发 Logger.LogDebug($"Found a singalr group, begin senging notifications"); - await singalRGroup.SendAsync(_options.MethodName, notification, cancellationToken); + await singalRGroup.SendAsync(_options.MethodName, context.Notification, cancellationToken); - Logger.LogDebug("The notification: {0} with provider: {1} has successfully published!", notification.Name, Name); + Logger.LogDebug("The notification: {0} with provider: {1} has successfully published!", context.Notification.Name, Name); } catch (Exception ex) { Logger.LogWarning("Could not send notifications to group {0}", groupName); - Logger.LogWarning("Send to user notifications error: {0}", ex.Message); + + context.Cancel(string.Format("Send to user notifications error: {0}", ex.Message), ex); + Logger.LogWarning(context.Reason); } } else { try { - var onlineClients = _hubContext.Clients.Users(identifiers.Select(x => x.UserId.ToString())); + var onlineClients = _hubContext.Clients.Users(context.Users.Select(x => x.UserId.ToString())); Logger.LogDebug($"Found a singalr client, begin senging notifications"); - await onlineClients.SendAsync(_options.MethodName, notification, cancellationToken); + await onlineClients.SendAsync(_options.MethodName, context.Notification, cancellationToken); } catch (Exception ex) { Logger.LogWarning("Could not send notifications to all users"); - Logger.LogWarning("Send to user notifications error: {0}", ex.Message); + + context.Cancel(string.Format("Send to user notifications error: {0}", ex.Message), ex); + Logger.LogWarning(context.Reason); } } } diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Sms/LINGYUN/Abp/Notifications/Sms/SmsNotificationPublishProvider.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Sms/LINGYUN/Abp/Notifications/Sms/SmsNotificationPublishProvider.cs index 57fc4f564..2e15165ad 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Sms/LINGYUN/Abp/Notifications/Sms/SmsNotificationPublishProvider.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Sms/LINGYUN/Abp/Notifications/Sms/SmsNotificationPublishProvider.cs @@ -16,22 +16,23 @@ public class SmsNotificationPublishProvider : NotificationPublishProvider protected IOptions Options => ServiceProvider.LazyGetRequiredService>(); protected override async Task PublishAsync( - NotificationInfo notification, - IEnumerable identifiers, + NotificationPublishContext context, CancellationToken cancellationToken = default) { - if (!identifiers.Any()) + if (!context.Users.Any()) { + context.Cancel("The user who received the text message is empty."); return; } - var sendToPhones = await UserPhoneFinder.FindByUserIdsAsync(identifiers.Select(usr => usr.UserId), cancellationToken); + var sendToPhones = await UserPhoneFinder.FindByUserIdsAsync(context.Users.Select(usr => usr.UserId), cancellationToken); if (!sendToPhones.Any()) { + context.Cancel("The user has not confirmed their mobile phone number, so the message cannot be sent."); return; } - await Sender.SendAsync(notification, sendToPhones.JoinAsString(",")); + await Sender.SendAsync(context.Notification, sendToPhones.JoinAsString(",")); - Logger.LogDebug("The notification: {0} with provider: {1} has successfully published!", notification.Name, Name); + Logger.LogDebug("The notification: {0} with provider: {1} has successfully published!", context.Notification.Name, Name); } } diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WeChat.MiniProgram/LINGYUN/Abp/Notifications/WeChat/MiniProgram/WeChatMiniProgramNotificationPublishProvider.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WeChat.MiniProgram/LINGYUN/Abp/Notifications/WeChat/MiniProgram/WeChatMiniProgramNotificationPublishProvider.cs index 6315b2a56..fc150a76a 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WeChat.MiniProgram/LINGYUN/Abp/Notifications/WeChat/MiniProgram/WeChatMiniProgramNotificationPublishProvider.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WeChat.MiniProgram/LINGYUN/Abp/Notifications/WeChat/MiniProgram/WeChatMiniProgramNotificationPublishProvider.cs @@ -37,7 +37,7 @@ public class WeChatMiniProgramNotificationPublishProvider : NotificationPublishP return true; } - protected async override Task PublishAsync(NotificationInfo notification, IEnumerable identifiers, CancellationToken cancellationToken = default) + protected async override Task PublishAsync(NotificationPublishContext context, CancellationToken cancellationToken = default) { // step1 默认微信openid绑定的就是username, // 如果不是,需要自行处理openid获取逻辑 @@ -46,55 +46,51 @@ public class WeChatMiniProgramNotificationPublishProvider : NotificationPublishP // 微信不支持推送到所有用户 // 在小程序里用户订阅消息后通过 api/subscribes/subscribe 接口订阅对应模板消息 - foreach (var identifier in identifiers) + foreach (var identifier in context.Users) { - await SendWeChatTemplateMessagAsync(notification, identifier, cancellationToken); - } - } - - protected async virtual Task SendWeChatTemplateMessagAsync(NotificationInfo notification, UserIdentifier identifier, CancellationToken cancellationToken = default) - { - var templateId = GetOrDefaultTemplateId(notification.Data); - if (templateId.IsNullOrWhiteSpace()) - { - Logger.LogWarning("Wechat weapp template id be empty, can not send notification!"); - return; - } - - Logger.LogDebug($"Get wechat weapp template id: {templateId}"); - - var redirect = GetOrDefault(notification.Data, "RedirectPage", null); - Logger.LogDebug($"Get wechat weapp redirect page: {redirect ?? "null"}"); - - var weAppState = GetOrDefault(notification.Data, "WeAppState", Options.Value.DefaultState); - Logger.LogDebug($"Get wechat weapp state: {weAppState ?? null}"); - - var weAppLang = GetOrDefault(notification.Data, "WeAppLanguage", Options.Value.DefaultLanguage); - Logger.LogDebug($"Get wechat weapp language: {weAppLang ?? null}"); - - // TODO: 如果微信端发布通知,请组装好 openid 字段在通知数据内容里面 - var openId = GetOrDefault(notification.Data, AbpWeChatClaimTypes.OpenId, ""); - - if (openId.IsNullOrWhiteSpace()) - { - // 发送小程序订阅消息 - await SubscribeMessager - .SendAsync( - identifier.UserId, templateId, redirect, weAppLang, - weAppState, notification.Data.ExtraProperties, cancellationToken); - } - else - { - var weChatWeAppNotificationData = new SubscribeMessage(templateId, redirect, weAppState, weAppLang); - // 写入模板数据 - weChatWeAppNotificationData.WriteData(notification.Data.ExtraProperties); - - Logger.LogDebug($"Sending wechat weapp notification: {notification.Name}"); - - // 发送小程序订阅消息 - await SubscribeMessager.SendAsync(weChatWeAppNotificationData, cancellationToken); - - Logger.LogDebug("The notification: {0} with provider: {1} has successfully published!", notification.Name, Name); + var templateId = GetOrDefaultTemplateId(context.Notification.Data); + if (templateId.IsNullOrWhiteSpace()) + { + context.Cancel("Wechat weapp template id be empty, can not send notification!"); + Logger.LogWarning(context.Reason); + continue; + } + + Logger.LogDebug($"Get wechat weapp template id: {templateId}"); + + var redirect = GetOrDefault(context.Notification.Data, "RedirectPage", null); + Logger.LogDebug($"Get wechat weapp redirect page: {redirect ?? "null"}"); + + var weAppState = GetOrDefault(context.Notification.Data, "WeAppState", Options.Value.DefaultState); + Logger.LogDebug($"Get wechat weapp state: {weAppState ?? null}"); + + var weAppLang = GetOrDefault(context.Notification.Data, "WeAppLanguage", Options.Value.DefaultLanguage); + Logger.LogDebug($"Get wechat weapp language: {weAppLang ?? null}"); + + // TODO: 如果微信端发布通知,请组装好 openid 字段在通知数据内容里面 + var openId = GetOrDefault(context.Notification.Data, AbpWeChatClaimTypes.OpenId, ""); + + if (openId.IsNullOrWhiteSpace()) + { + // 发送小程序订阅消息 + await SubscribeMessager + .SendAsync( + identifier.UserId, templateId, redirect, weAppLang, + weAppState, context.Notification.Data.ExtraProperties, cancellationToken); + } + else + { + var weChatWeAppNotificationData = new SubscribeMessage(templateId, redirect, weAppState, weAppLang); + // 写入模板数据 + weChatWeAppNotificationData.WriteData(context.Notification.Data.ExtraProperties); + + Logger.LogDebug($"Sending wechat weapp notification: {context.Notification.Name}"); + + // 发送小程序订阅消息 + await SubscribeMessager.SendAsync(weChatWeAppNotificationData, cancellationToken); + + Logger.LogDebug("The notification: {0} with provider: {1} has successfully published!", context.Notification.Name, Name); + } } } diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WeChat.Work/LINGYUN/Abp/Notifications/WeChat/Work/WeChatWorkNotificationPublishProvider.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WeChat.Work/LINGYUN/Abp/Notifications/WeChat/Work/WeChatWorkNotificationPublishProvider.cs index ce8a01230..4e4a00de2 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WeChat.Work/LINGYUN/Abp/Notifications/WeChat/Work/WeChatWorkNotificationPublishProvider.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WeChat.Work/LINGYUN/Abp/Notifications/WeChat/Work/WeChatWorkNotificationPublishProvider.cs @@ -41,34 +41,35 @@ public class WeChatWorkNotificationPublishProvider : NotificationPublishProvider } protected async override Task PublishAsync( - NotificationInfo notification, - IEnumerable identifiers, + NotificationPublishContext context, CancellationToken cancellationToken = default) { var sendToAgentIds = new List(); - var notificationDefine = await NotificationDefinitionManager.GetOrNullAsync(notification.Name); + var notificationDefine = await NotificationDefinitionManager.GetOrNullAsync(context.Notification.Name); var agentId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.AgentId); if (agentId.IsNullOrWhiteSpace()) { - Logger.LogWarning("Unable to send work weixin messages because agentId is not set."); + context.Cancel("Unable to send work weixin messages because agentId is not set."); + Logger.LogWarning(context.Reason); return; } - var notificationData = await NotificationDataSerializer.ToStandard(notification.Data); - var toTag = notification.Data.GetTagOrNull() ?? notificationDefine?.GetTagOrNull(); - var toParty = notification.Data.GetPartyOrNull() ?? notificationDefine?.GetPartyOrNull(); - var toUsers = await WeChatWorkInternalUserFinder.FindUserIdentifierListAsync(identifiers.Select(id => id.UserId)); + var notificationData = await NotificationDataSerializer.ToStandard(context.Notification.Data); + var toTag = context.Notification.Data.GetTagOrNull() ?? notificationDefine?.GetTagOrNull(); + var toParty = context.Notification.Data.GetPartyOrNull() ?? notificationDefine?.GetPartyOrNull(); + var toUsers = await WeChatWorkInternalUserFinder.FindUserIdentifierListAsync(context.Users.Select(id => id.UserId)); if (toUsers.IsNullOrEmpty() && toTag.IsNullOrWhiteSpace() && toParty.IsNullOrWhiteSpace()) { // touser、toparty、totag不能同时为空:https://developer.work.weixin.qq.com/document/path/90236 - Logger.LogWarning("Unable to send work weixin messages because The recipient/department/label cannot be empty simultaneously."); + context.Cancel("Unable to send work weixin messages because The recipient/department/label cannot be empty simultaneously."); + Logger.LogWarning(context.Reason); return; } // 发送到个人 await PublishToAgentAsync( + context, agentId, - notification, notificationData.Title, notificationData.Message, notificationData.Description, @@ -79,8 +80,8 @@ public class WeChatWorkNotificationPublishProvider : NotificationPublishProvider } protected async virtual Task PublishToAgentAsync( + NotificationPublishContext context, string agentId, - NotificationInfo notification, string title, string content, string description = "", @@ -91,7 +92,7 @@ public class WeChatWorkNotificationPublishProvider : NotificationPublishProvider { WeChatWorkMessage message = null; - switch (notification.ContentType) + switch (context.Notification.ContentType) { case NotificationContentType.Text: message = new WeChatWorkTextMessage(agentId, new TextMessage(content)); @@ -108,7 +109,8 @@ public class WeChatWorkNotificationPublishProvider : NotificationPublishProvider if (message == null) { - Logger.LogWarning("Unable to send work weixin messages because WeChatWorkMessage is null."); + context.Cancel("Unable to send work weixin messages because WeChatWorkMessage is null."); + Logger.LogWarning(context.Reason); return; } @@ -118,6 +120,6 @@ public class WeChatWorkNotificationPublishProvider : NotificationPublishProvider await WeChatWorkMessageSender.SendAsync(message, cancellationToken); - Logger.LogDebug("The notification: {0} with provider: {1} has successfully published!", notification.Name, Name); + Logger.LogDebug("The notification: {0} with provider: {1} has successfully published!", context.Notification.Name, Name); } } diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WxPusher/LINGYUN/Abp/Notifications/WxPusher/WxPusherNotificationPublishProvider.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WxPusher/LINGYUN/Abp/Notifications/WxPusher/WxPusherNotificationPublishProvider.cs index 13c04ddd4..ffe1d8282 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WxPusher/LINGYUN/Abp/Notifications/WxPusher/WxPusherNotificationPublishProvider.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WxPusher/LINGYUN/Abp/Notifications/WxPusher/WxPusherNotificationPublishProvider.cs @@ -2,7 +2,6 @@ using LINGYUN.Abp.WxPusher.Messages; using LINGYUN.Abp.WxPusher.User; using Microsoft.Extensions.Logging; -using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -36,17 +35,16 @@ public class WxPusherNotificationPublishProvider : NotificationPublishProvider } protected async override Task PublishAsync( - NotificationInfo notification, - IEnumerable identifiers, + NotificationPublishContext context, CancellationToken cancellationToken = default) { - var subscribeUserIds = identifiers.Select(x => x.UserId); + var subscribeUserIds = context.Users.Select(x => x.UserId); var topics = await WxPusherUserStore.GetSubscribeTopicsAsync(subscribeUserIds, cancellationToken); var uids = await WxPusherUserStore.GetBindUidsAsync(subscribeUserIds, cancellationToken); - var notificationDefine = await NotificationDefinitionManager.GetOrNullAsync(notification.Name); - var url = notification.Data.GetUrlOrNull() ?? notificationDefine?.GetUrlOrNull(); + var notificationDefine = await NotificationDefinitionManager.GetOrNullAsync(context.Notification.Name); + var url = context.Notification.Data.GetUrlOrNull() ?? notificationDefine?.GetUrlOrNull(); var topicDefine = notificationDefine?.GetTopics(); if (topicDefine.Any()) { @@ -54,7 +52,7 @@ public class WxPusherNotificationPublishProvider : NotificationPublishProvider } var contentType = notificationDefine?.GetContentTypeOrDefault(MessageContentType.Text) ?? MessageContentType.Text; - var notificationData = await NotificationDataSerializer.ToStandard(notification.Data); + var notificationData = await NotificationDataSerializer.ToStandard(context.Notification.Data); await WxPusherMessageSender.SendAsync( content: notificationData.Message, @@ -65,6 +63,6 @@ public class WxPusherNotificationPublishProvider : NotificationPublishProvider url: url, cancellationToken: cancellationToken); - Logger.LogDebug("The notification: {0} with provider: {1} has successfully published!", notification.Name, Name); + Logger.LogDebug("The notification: {0} with provider: {1} has successfully published!", context.Notification.Name, Name); } } diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationPublishProvider.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationPublishProvider.cs index c738a7440..afc71f55e 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationPublishProvider.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationPublishProvider.cs @@ -13,12 +13,16 @@ public interface INotificationPublishProvider /// string Name { get; } /// + /// 是否可发布通知 + /// + /// + /// + Task CanPublishAsync( + NotificationInfo notification); + /// /// 发布通知 /// - /// 通知信息 - /// 接收用户列表 + /// 通知发送上下文信息 /// - Task PublishAsync( - NotificationInfo notification, - IEnumerable identifiers); + Task PublishAsync(NotificationPublishContext context); } diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationStore.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationStore.cs index 9c68f51a9..68b34c762 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationStore.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationStore.cs @@ -7,6 +7,10 @@ namespace LINGYUN.Abp.Notifications; public interface INotificationStore { + Task InsertSendStateAsync( + NotificationSendInfo notificationSendInfo, + CancellationToken cancellationToken = default); + Task InsertUserSubscriptionAsync( Guid? tenantId, UserIdentifier identifier, diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProvider.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProvider.cs index 2ef369a25..8bf1cc899 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProvider.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProvider.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; @@ -22,24 +21,23 @@ public abstract class NotificationPublishProvider : INotificationPublishProvider public ICancellationTokenProvider CancellationTokenProvider => ServiceProvider.LazyGetService(NullCancellationTokenProvider.Instance); - public async Task PublishAsync( - NotificationInfo notification, - IEnumerable identifiers) + public async virtual Task CanPublishAsync(NotificationInfo notification) + { + return await CanPublishAsync(notification, GetCancellationToken()); + } + + public async Task PublishAsync(NotificationPublishContext context) { - if (await CanPublishAsync(notification)) - { - await PublishAsync( - notification, - identifiers, - GetCancellationToken()); - } + await PublishAsync(context, GetCancellationToken()); } + protected virtual Task CanPublishAsync( - NotificationInfo notification, + NotificationInfo notification, CancellationToken cancellationToken = default) { return Task.FromResult(true); } + protected virtual CancellationToken GetCancellationToken(CancellationToken cancellationToken = default) { return CancellationTokenProvider.FallbackToProvider(cancellationToken); @@ -47,9 +45,8 @@ public abstract class NotificationPublishProvider : INotificationPublishProvider /// /// 重写实现通知发布 /// - /// - /// + /// 通知发送上下文 /// /// - protected abstract Task PublishAsync(NotificationInfo notification, IEnumerable identifiers, CancellationToken cancellationToken = default); + protected abstract Task PublishAsync(NotificationPublishContext context, CancellationToken cancellationToken = default); } diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NullNotificationStore.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NullNotificationStore.cs index e55f068e0..8d893b4bc 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NullNotificationStore.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NullNotificationStore.cs @@ -11,6 +11,13 @@ public class NullNotificationStore : INotificationStore, ISingletonDependency { public readonly static INotificationStore Instance = new NullNotificationStore(); + public Task InsertSendStateAsync( + NotificationSendInfo notificationSendInfo, + CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } + public Task ChangeUserNotificationReadStateAsync( Guid? tenantId, Guid userId, diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/BackgroundJobs/NotificationPublishJob.cs b/aspnet-core/services/LY.MicroService.Applications.Single/BackgroundJobs/NotificationPublishJob.cs index 8e96f80d2..d64b8ca16 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/BackgroundJobs/NotificationPublishJob.cs +++ b/aspnet-core/services/LY.MicroService.Applications.Single/BackgroundJobs/NotificationPublishJob.cs @@ -1,22 +1,28 @@ -using LINGYUN.Abp.Notifications; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options; using Volo.Abp.BackgroundJobs; using Volo.Abp.DependencyInjection; +using Volo.Abp.Timing; namespace LY.MicroService.Applications.Single.BackgroundJobs; public class NotificationPublishJob : AsyncBackgroundJob, ITransientDependency { + protected IClock Clock { get; } protected AbpNotificationsPublishOptions Options { get; } protected IServiceScopeFactory ServiceScopeFactory { get; } + protected INotificationStore NotificationStore { get; } protected INotificationDataSerializer NotificationDataSerializer { get; } public NotificationPublishJob( + IClock clock, IOptions options, IServiceScopeFactory serviceScopeFactory, + INotificationStore notificationStore, INotificationDataSerializer notificationDataSerializer) { + Clock = clock; Options = options.Value; ServiceScopeFactory = serviceScopeFactory; + NotificationStore = notificationStore; NotificationDataSerializer = notificationDataSerializer; } @@ -31,8 +37,64 @@ public class NotificationPublishJob : AsyncBackgroundJob identifiers) + { + return new NotificationSendInfo( + provider.Name, + Clock.Now, + notification, + identifiers); + } + + protected async Task OnPublished(NotificationSendInfo sendInfo) + { + await NotificationStore.InsertSendStateAsync(sendInfo); + } } diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/EventBus/Distributed/NotificationEventHandler.cs b/aspnet-core/services/LY.MicroService.Applications.Single/EventBus/Distributed/NotificationEventHandler.cs index 5cc4b0e37..9e163e427 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/EventBus/Distributed/NotificationEventHandler.cs +++ b/aspnet-core/services/LY.MicroService.Applications.Single/EventBus/Distributed/NotificationEventHandler.cs @@ -13,6 +13,7 @@ using Volo.Abp.Json; using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; using Volo.Abp.TextTemplating; +using Volo.Abp.Timing; using Volo.Abp.Uow; namespace LY.MicroService.Applications.Single.EventBus.Distributed @@ -38,6 +39,10 @@ namespace LY.MicroService.Applications.Single.EventBus.Distributed /// protected AbpNotificationsPublishOptions Options { get; } /// + /// Reference to . + /// + protected IClock Clock { get; } + /// /// Reference to . /// protected ICurrentTenant CurrentTenant { get; } @@ -90,6 +95,7 @@ namespace LY.MicroService.Applications.Single.EventBus.Distributed /// Initializes a new instance of the class. /// public NotificationEventHandler( + IClock clock, ICurrentTenant currentTenant, ITenantConfigurationCache tenantConfigurationCache, IJsonSerializer jsonSerializer, @@ -104,6 +110,7 @@ namespace LY.MicroService.Applications.Single.EventBus.Distributed INotificationSubscriptionManager notificationSubscriptionManager, INotificationPublishProviderManager notificationPublishProviderManager) { + Clock = clock; Options = options.Value; TenantConfigurationCache = tenantConfigurationCache; CurrentTenant = currentTenant; @@ -414,29 +421,40 @@ namespace LY.MicroService.Applications.Single.EventBus.Distributed /// /// 通知发布者 /// 通知信息 - /// 订阅用户列表 + /// 订阅用户列表 /// protected async Task PublishToSubscriberAsync( INotificationPublishProvider provider, NotificationInfo notificationInfo, IEnumerable subscriptionUsers) { + var sendInfo = OnPublishing(provider, notificationInfo, subscriptionUsers); + try { Logger.LogDebug($"Sending notification with provider {provider.Name}"); - // 2024-10-10: 框架层面应该取消通知数据转换,而是交给提供商来实现 - //var notifacationDataMapping = Options.NotificationDataMappings - // .GetMapItemOrDefault(provider.Name, notificationInfo.Name); - //if (notifacationDataMapping != null) - //{ - // notificationInfo.Data = notifacationDataMapping.MappingFunc(notificationInfo.Data); - //} + if (await provider.CanPublishAsync(notificationInfo)) + { + var context = new NotificationPublishContext(notificationInfo, subscriptionUsers); + // 发布 + await provider.PublishAsync(context); + + sendInfo.Sent(context.Exception); - // 发布 - await provider.PublishAsync(notificationInfo, subscriptionUsers); + if (context.Exception == null && !context.Reason.IsNullOrWhiteSpace()) + { + sendInfo.Cancel(context.Reason); + } + + Logger.LogDebug($"Send notification {notificationInfo.Name} with provider {provider.Name} was successful"); + } + else + { + sendInfo.Disbaled(); + } - Logger.LogDebug($"Send notification {notificationInfo.Name} with provider {provider.Name} was successful"); + await OnPublished(sendInfo); } catch (Exception ex) { @@ -445,6 +463,13 @@ namespace LY.MicroService.Applications.Single.EventBus.Distributed Logger.LogDebug($"Failed to send notification {notificationInfo.Name}. Try to push notification to background job"); // 发送失败的消息进入后台队列 await ProcessingFailedToQueueAsync(provider, notificationInfo, subscriptionUsers); + + try + { + sendInfo.Sent(ex); + await OnPublished(sendInfo); + } + catch { } } } /// @@ -472,10 +497,27 @@ namespace LY.MicroService.Applications.Single.EventBus.Distributed subscriptionUsers.ToList(), notificationInfo.TenantId)); } - catch(Exception ex) + catch (Exception ex) { Logger.LogWarning("Failed to push to background job, notification will be discarded, error cause: {message}", ex.Message); } } + + protected virtual NotificationSendInfo OnPublishing( + INotificationPublishProvider provider, + NotificationInfo notification, + IEnumerable identifiers) + { + return new NotificationSendInfo( + provider.Name, + Clock.Now, + notification, + identifiers); + } + + protected async Task OnPublished(NotificationSendInfo sendInfo) + { + await NotificationStore.InsertSendStateAsync(sendInfo); + } } } diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj b/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj index 143616ccc..66122ce6d 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj +++ b/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj @@ -28,7 +28,7 @@ - + @@ -138,6 +138,8 @@ + + diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs index 33c8593c7..99b46017a 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs +++ b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs @@ -1,3 +1,4 @@ +using LINGYUN.Abp.AIManagement; using Microsoft.AspNetCore.SignalR; using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite.Bundling; using VoloAbpExceptionHandlingOptions = Volo.Abp.AspNetCore.ExceptionHandling.AbpExceptionHandlingOptions; @@ -464,6 +465,18 @@ public partial class MicroServiceApplicationsSingleModule } } + private void ConfigureAIManagement(IConfiguration configuration) + { + if (configuration.GetValue("AIManagement:IsDynamicStoreEnabled")) + { + Configure(options => + { + options.IsDynamicWorkspaceStoreEnabled = true; + options.SaveStaticWorkspacesToDatabase = true; + }); + } + } + private void ConfigureDistributedLock(IServiceCollection services, IConfiguration configuration) { var distributedLockEnabled = configuration["DistributedLock:IsEnabled"]; diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs index b9f5187b2..a7602a35b 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs +++ b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs @@ -1,3 +1,4 @@ +using LINGYUN.Abp.AIManagement; using LINGYUN.Abp.SystemInfo; namespace LY.MicroService.Applications.Single; @@ -351,6 +352,11 @@ namespace LY.MicroService.Applications.Single; // 微信模块 设置管理 typeof(AbpWeChatSettingManagementModule), + // AI管理模块 应用服务 + typeof(AbpAIManagementApplicationModule), + // AI管理模块 控制器 + typeof(AbpAIManagementHttpApiModule), + // 数据迁移模块 typeof(AbpDataDbMigratorModule), // IP解析模块 IP2Region集成 @@ -439,6 +445,7 @@ public partial class MicroServiceApplicationsSingleModule : AbpModule ConfigureMultiTenancy(configuration); ConfigureJsonSerializer(configuration); ConfigureTextTemplating(configuration); + ConfigureAIManagement(configuration); ConfigureFeatureManagement(configuration); ConfigureSettingManagement(configuration); ConfigureWebhooksManagement(configuration); diff --git a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/BackgroundJobs/NotificationPublishJob.cs b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/BackgroundJobs/NotificationPublishJob.cs index 3682b05f6..7a8d0c0a8 100644 --- a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/BackgroundJobs/NotificationPublishJob.cs +++ b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/BackgroundJobs/NotificationPublishJob.cs @@ -1,25 +1,34 @@ using LINGYUN.Abp.Notifications; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; +using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.BackgroundJobs; using Volo.Abp.DependencyInjection; +using Volo.Abp.Timing; namespace LY.MicroService.RealtimeMessage.BackgroundJobs; public class NotificationPublishJob : AsyncBackgroundJob, ITransientDependency { + protected IClock Clock { get; } protected AbpNotificationsPublishOptions Options { get; } protected IServiceScopeFactory ServiceScopeFactory { get; } + protected INotificationStore NotificationStore { get; } protected INotificationDataSerializer NotificationDataSerializer { get; } public NotificationPublishJob( + IClock clock, IOptions options, IServiceScopeFactory serviceScopeFactory, + INotificationStore notificationStore, INotificationDataSerializer notificationDataSerializer) { + Clock = clock; Options = options.Value; ServiceScopeFactory = serviceScopeFactory; + NotificationStore = notificationStore; NotificationDataSerializer = notificationDataSerializer; } @@ -33,9 +42,65 @@ public class NotificationPublishJob : AsyncBackgroundJob(); var notification = await store.GetNotificationOrNullAsync(args.TenantId, args.NotificationId); notification.Data = NotificationDataSerializer.Serialize(notification.Data); - - await publishProvider.PublishAsync(notification, args.UserIdentifiers); + + var sendInfo = OnPublishing(publishProvider, notification, args.UserIdentifiers); + + try + { + if (await publishProvider.CanPublishAsync(notification)) + { + var context = new NotificationPublishContext(notification, args.UserIdentifiers); + // 发布 + await publishProvider.PublishAsync(context); + + sendInfo.Sent(context.Exception); + + if (context.Exception == null && !context.Reason.IsNullOrWhiteSpace()) + { + sendInfo.Cancel(context.Reason); + } + + Logger.LogDebug($"Send notification {notification.Name} with provider {publishProvider.Name} was successful"); + } + else + { + sendInfo.Disbaled(); + } + + await OnPublished(sendInfo); + } + catch (Exception ex) + { + Logger.LogWarning($"Send notification error with provider {publishProvider.Name}"); + Logger.LogWarning($"Error message:{ex.Message}"); + + try + { + sendInfo.Sent(ex); + await OnPublished(sendInfo); + } + catch { } + + throw; + } } } } + + protected virtual NotificationSendInfo OnPublishing( + INotificationPublishProvider provider, + NotificationInfo notification, + IEnumerable identifiers) + { + return new NotificationSendInfo( + provider.Name, + Clock.Now, + notification, + identifiers); + } + + protected async Task OnPublished(NotificationSendInfo sendInfo) + { + await NotificationStore.InsertSendStateAsync(sendInfo); + } } diff --git a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/EventBus/Distributed/NotificationEventHandler.cs b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/EventBus/Distributed/NotificationEventHandler.cs index 6497831b3..7805f9ec3 100644 --- a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/EventBus/Distributed/NotificationEventHandler.cs +++ b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/EventBus/Distributed/NotificationEventHandler.cs @@ -19,6 +19,7 @@ using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; using Volo.Abp.Settings; using Volo.Abp.TextTemplating; +using Volo.Abp.Timing; using Volo.Abp.Uow; namespace LY.MicroService.RealtimeMessage.EventBus.Distributed @@ -44,6 +45,10 @@ namespace LY.MicroService.RealtimeMessage.EventBus.Distributed /// protected AbpNotificationsPublishOptions Options { get; } /// + /// Reference to . + /// + protected IClock Clock { get; } + /// /// Reference to . /// protected ISettingProvider SettingProvider { get; } @@ -100,6 +105,7 @@ namespace LY.MicroService.RealtimeMessage.EventBus.Distributed /// Initializes a new instance of the class. /// public NotificationEventHandler( + IClock clock, ICurrentTenant currentTenant, ISettingProvider settingProvider, ITenantConfigurationCache tenantConfigurationCache, @@ -115,6 +121,7 @@ namespace LY.MicroService.RealtimeMessage.EventBus.Distributed INotificationSubscriptionManager notificationSubscriptionManager, INotificationPublishProviderManager notificationPublishProviderManager) { + Clock = clock; Options = options.Value; TenantConfigurationCache = tenantConfigurationCache; CurrentTenant = currentTenant; @@ -435,20 +442,40 @@ namespace LY.MicroService.RealtimeMessage.EventBus.Distributed /// /// 通知发布者 /// 通知信息 - /// 订阅用户列表 + /// 订阅用户列表 /// protected async Task PublishToSubscriberAsync( INotificationPublishProvider provider, NotificationInfo notificationInfo, IEnumerable subscriptionUsers) { + var sendInfo = OnPublishing(provider, notificationInfo, subscriptionUsers); + try { Logger.LogDebug($"Sending notification with provider {provider.Name}"); - // 发布 - await provider.PublishAsync(notificationInfo, subscriptionUsers); - Logger.LogDebug($"Send notification {notificationInfo.Name} with provider {provider.Name} was successful"); + if (await provider.CanPublishAsync(notificationInfo)) + { + var context = new NotificationPublishContext(notificationInfo, subscriptionUsers); + // 发布 + await provider.PublishAsync(context); + + sendInfo.Sent(context.Exception); + + if (context.Exception == null && !context.Reason.IsNullOrWhiteSpace()) + { + sendInfo.Cancel(context.Reason); + } + + Logger.LogDebug($"Send notification {notificationInfo.Name} with provider {provider.Name} was successful"); + } + else + { + sendInfo.Disbaled(); + } + + await OnPublished(sendInfo); } catch (Exception ex) { @@ -457,6 +484,13 @@ namespace LY.MicroService.RealtimeMessage.EventBus.Distributed Logger.LogDebug($"Failed to send notification {notificationInfo.Name}. Try to push notification to background job"); // 发送失败的消息进入后台队列 await ProcessingFailedToQueueAsync(provider, notificationInfo, subscriptionUsers); + + try + { + sendInfo.Sent(ex); + await OnPublished(sendInfo); + } + catch { } } } /// @@ -489,5 +523,22 @@ namespace LY.MicroService.RealtimeMessage.EventBus.Distributed Logger.LogWarning("Failed to push to background job, notification will be discarded, error cause: {message}", ex.Message); } } + + protected virtual NotificationSendInfo OnPublishing( + INotificationPublishProvider provider, + NotificationInfo notification, + IEnumerable identifiers) + { + return new NotificationSendInfo( + provider.Name, + Clock.Now, + notification, + identifiers); + } + + protected async Task OnPublished(NotificationSendInfo sendInfo) + { + await NotificationStore.InsertSendStateAsync(sendInfo); + } } } diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.PostgreSql.json b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.PostgreSql.json new file mode 100644 index 000000000..94deda2ff --- /dev/null +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.Development.PostgreSql.json @@ -0,0 +1,25 @@ +{ + "Quartz": { + "UsePersistentStore": true, + "Properties": { + "quartz.jobStore.dataSource": "tkm", + "quartz.jobStore.type": "Quartz.Impl.AdoJobStore.JobStoreTX,Quartz", + "quartz.jobStore.driverDelegateType": "Quartz.Impl.AdoJobStore.PostgreSQLDelegate,Quartz", + "quartz.dataSource.tkm.connectionString": "Host=127.0.0.1;Database=Platform-V70;Username=postgres;Password=123456;SslMode=Prefer", + "quartz.dataSource.tkm.connectionStringName": "TaskManagement", + "quartz.dataSource.tkm.provider": "Npgsql", + "quartz.jobStore.clustered": "true", + "quartz.serializer.type": "json" + } + }, + "ConnectionStrings": { + "Default": "Host=127.0.0.1;Database=Platform-V70;Username=postgres;Password=123456;SslMode=Prefer", + "AbpSaas": "Host=127.0.0.1;Database=Platform-V70;Username=postgres;Password=123456;SslMode=Prefer", + "AbpTenantManagement": "Host=127.0.0.1;Database=Platform-V70;Username=postgres;Password=123456;SslMode=Prefer", + "AbpSettingManagement": "Host=127.0.0.1;Database=Platform-V70;Username=postgres;Password=123456;SslMode=Prefer", + "AbpPermissionManagement": "Host=127.0.0.1;Database=Platform-V70;Username=postgres;Password=123456;SslMode=Prefer", + "AbpFeatureManagement": "Host=127.0.0.1;Database=Platform-V70;Username=postgres;Password=123456;SslMode=Prefer", + "AbpTextTemplating": "Host=127.0.0.1;Database=Platform-V70;Username=postgres;Password=123456;SslMode=Prefer", + "AbpLocalizationManagement": "Host=127.0.0.1;Database=Platform-V70;Username=postgres;Password=123456;SslMode=Prefer" + } +} From 3b8900c61502a82bea4b23d18e2ff14aebb351be Mon Sep 17 00:00:00 2001 From: colin Date: Tue, 24 Mar 2026 14:30:54 +0800 Subject: [PATCH 52/88] fix: Fix the error code in the notification module --- .../Abp/Notifications/AbpNotificationsDomainSharedModule.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/AbpNotificationsDomainSharedModule.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/AbpNotificationsDomainSharedModule.cs index 1ba0c0cc1..9bd6d6b94 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/AbpNotificationsDomainSharedModule.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Domain.Shared/LINGYUN/Abp/Notifications/AbpNotificationsDomainSharedModule.cs @@ -1,5 +1,6 @@ using LINGYUN.Abp.Notifications.Localization; using Volo.Abp.Localization; +using Volo.Abp.Localization.ExceptionHandling; using Volo.Abp.Modularity; using Volo.Abp.Users; using Volo.Abp.VirtualFileSystem; @@ -24,5 +25,10 @@ public class AbpNotificationsDomainSharedModule : AbpModule .Get() .AddVirtualJson("/LINGYUN/Abp/Notifications/Localization/DomainShared"); }); + + Configure(options => + { + options.MapCodeNamespace(NotificationsErrorCodes.Namespace, typeof(NotificationsResource)); + }); } } From 8fcb105be2c017d96ce4d144dfc1c4bd000bdb39 Mon Sep 17 00:00:00 2001 From: colin Date: Tue, 24 Mar 2026 14:31:27 +0800 Subject: [PATCH 53/88] feat: Add user notification query conditions --- .../Dto/UserNotificationGetByPagedDto.cs | 5 +- .../Notifications/MyNotificationAppService.cs | 38 ++++++--- .../EfCoreUserNotificationRepository.cs | 81 +++++++++++++++++-- 3 files changed, 106 insertions(+), 18 deletions(-) diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Dto/UserNotificationGetByPagedDto.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Dto/UserNotificationGetByPagedDto.cs index 14b9ae89a..e3510ba12 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Dto/UserNotificationGetByPagedDto.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application.Contracts/LINGYUN/Abp/Notifications/Dto/UserNotificationGetByPagedDto.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; using Volo.Abp.Application.Dtos; namespace LINGYUN.Abp.Notifications; @@ -9,4 +10,6 @@ public class UserNotificationGetByPagedDto : PagedAndSortedResultRequestDto [DisplayName("Notifications:State")] public NotificationReadState? ReadState { get; set; } + public DateTime? BeginCreationTime { get; set; } + public DateTime? EndCreationTime { get; set; } } diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/MyNotificationAppService.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/MyNotificationAppService.cs index 4e5956587..12acdbd1a 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/MyNotificationAppService.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Application/LINGYUN/Abp/Notifications/MyNotificationAppService.cs @@ -1,5 +1,8 @@ using Microsoft.AspNetCore.Authorization; +using System; using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; using Volo.Abp.Users; @@ -56,17 +59,34 @@ public class MyNotificationAppService : AbpNotificationsApplicationServiceBase, public async virtual Task> GetListAsync(UserNotificationGetByPagedDto input) { - var totalCount = await UserNotificationRepository - .GetCountAsync( - CurrentUser.GetId(), - input.Filter, - input.ReadState); + Expression> expression = _ => true; + + if (input.ReadState.HasValue) + { + expression = expression.And(x => x.State == input.ReadState); + } + if (input.BeginCreationTime.HasValue) + { + expression = expression.And(x => x.CreationTime >= input.BeginCreationTime); + } + if (input.EndCreationTime.HasValue) + { + expression = expression.And(x => x.CreationTime <= input.EndCreationTime); + } + if (!input.Filter.IsNullOrWhiteSpace()) + { + expression = expression.And(x => + x.Name.Contains(input.Filter) || + x.NotificationTypeName.Contains(input.Filter)); + } + + var specification = new Volo.Abp.Specifications.ExpressionSpecification(expression); + + var userId = CurrentUser.GetId(); + var totalCount = await UserNotificationRepository.GetCountAsync(userId, specification); var notifications = await UserNotificationRepository - .GetListAsync( - CurrentUser.GetId(), - input.Filter, input.Sorting, - input.ReadState, input.SkipCount, input.MaxResultCount); + .GetListAsync(userId, specification, input.Sorting, input.SkipCount, input.MaxResultCount); return new PagedResultDto(totalCount, ObjectMapper.Map, List>(notifications)); diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/EfCoreUserNotificationRepository.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/EfCoreUserNotificationRepository.cs index 077b6956c..651de3ce7 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/EfCoreUserNotificationRepository.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.EntityFrameworkCore/LINGYUN/Abp/Notifications/EntityFrameworkCore/EfCoreUserNotificationRepository.cs @@ -1,13 +1,16 @@ using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Linq.Dynamic.Core; using System.Threading; using System.Threading.Tasks; +using System.Xml; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Specifications; namespace LINGYUN.Abp.Notifications.EntityFrameworkCore; @@ -151,6 +154,35 @@ public class EfCoreUserNotificationRepository : EfCoreRepository GetCountAsync( + Guid userId, + ISpecification specification, + CancellationToken cancellationToken = default) + { + var dbContext = await GetDbContextAsync(); + var notifilerQuery = from un in dbContext.Set() + join n in dbContext.Set() + on un.NotificationId equals n.NotificationId + where un.UserId == userId + select new UserNotificationInfo + { + NotificationId = n.NotificationId, + TenantId = n.TenantId, + Name = n.NotificationName, + ExtraProperties = n.ExtraProperties, + CreationTime = n.CreationTime, + NotificationTypeName = n.NotificationTypeName, + Severity = n.Severity, + State = un.ReadStatus, + Type = n.Type, + ContentType = n.ContentType + }; + + return await notifilerQuery + .Where(specification.ToExpression()) + .CountAsync(GetCancellationToken(cancellationToken)); + } + public async virtual Task> GetListAsync( Guid userId, string filter = "", @@ -165,14 +197,6 @@ public class EfCoreUserNotificationRepository : EfCoreRepository() - // .Where(x => x.UserId == userId) - // .WhereIf(readState.HasValue, x => x.ReadStatus == readState.Value); - - //var notificationQuery = dbContext.Set() - // .WhereIf(!filter.IsNullOrWhiteSpace(), nf => - // nf.NotificationName.Contains(filter) || - // nf.NotificationTypeName.Contains(filter)); var notifilerQuery = from un in dbContext.Set() join n in dbContext.Set() @@ -202,4 +226,45 @@ public class EfCoreUserNotificationRepository : EfCoreRepository> GetListAsync( + Guid userId, + ISpecification specification, + string sorting = nameof(Notification.CreationTime), + int skipCount = 0, + int maxResultCount = 10, + CancellationToken cancellationToken = default) + { + if (sorting.IsNullOrWhiteSpace()) + { + sorting = $"{nameof(Notification.CreationTime)} DESC"; + } + var dbContext = await GetDbContextAsync(); + + var notifilerQuery = from un in dbContext.Set() + join n in dbContext.Set() + on un.NotificationId equals n.NotificationId + where un.UserId == userId + select new UserNotificationInfo + { + NotificationId = n.NotificationId, + TenantId = n.TenantId, + Name = n.NotificationName, + ExtraProperties = n.ExtraProperties, + CreationTime = n.CreationTime, + NotificationTypeName = n.NotificationTypeName, + Severity = n.Severity, + State = un.ReadStatus, + Type = n.Type, + ContentType = n.ContentType, + Id = un.Id, + }; + + return await notifilerQuery + .Where(specification.ToExpression()) + .OrderBy(sorting) + .PageBy(skipCount, maxResultCount) + .AsNoTracking() + .ToListAsync(GetCancellationToken(cancellationToken)); + } } From 1a7cb4aadcff24539f674a7e536a3a8817f29573 Mon Sep 17 00:00:00 2001 From: colin Date: Tue, 24 Mar 2026 14:32:12 +0800 Subject: [PATCH 54/88] fix: Fix multi language caching issue --- .../LocalizationManagement/LocalizationLanguageStoreCache.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageStoreCache.cs b/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageStoreCache.cs index 037511cc2..34471125f 100644 --- a/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageStoreCache.cs +++ b/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageStoreCache.cs @@ -45,6 +45,8 @@ public class LocalizationLanguageStoreCache : ILocalizationLanguageStoreCache, I new LanguageInfo(x.CultureName, x.UiCultureName, x.DisplayName)) .ToList()); + await LanguageCache.SetAsync(LocalizationLanguageCacheItem.CacheKey, cacheItem); + return cacheItem; } } From 97859bc1455136e14e17109144a19e30d94ddbff Mon Sep 17 00:00:00 2001 From: colin Date: Tue, 24 Mar 2026 14:37:32 +0800 Subject: [PATCH 55/88] feat: Add an artificial intelligence menu --- ...eVbenAdmin5NavigationDefinitionProvider.cs | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/aspnet-core/modules/platform/LINGYUN.Abp.UI.Navigation.VueVbenAdmin5/LINGYUN/Abp/UI/Navigation/VueVbenAdmin5/AbpUINavigationVueVbenAdmin5NavigationDefinitionProvider.cs b/aspnet-core/modules/platform/LINGYUN.Abp.UI.Navigation.VueVbenAdmin5/LINGYUN/Abp/UI/Navigation/VueVbenAdmin5/AbpUINavigationVueVbenAdmin5NavigationDefinitionProvider.cs index 275dfb7d6..d849eccf3 100644 --- a/aspnet-core/modules/platform/LINGYUN.Abp.UI.Navigation.VueVbenAdmin5/LINGYUN/Abp/UI/Navigation/VueVbenAdmin5/AbpUINavigationVueVbenAdmin5NavigationDefinitionProvider.cs +++ b/aspnet-core/modules/platform/LINGYUN.Abp.UI.Navigation.VueVbenAdmin5/LINGYUN/Abp/UI/Navigation/VueVbenAdmin5/AbpUINavigationVueVbenAdmin5NavigationDefinitionProvider.cs @@ -1,5 +1,4 @@ -using System.Security.Principal; -using Volo.Abp.Data; +using Volo.Abp.Data; using Volo.Abp.MultiTenancy; namespace LINGYUN.Abp.UI.Navigation.VueVbenAdmin5; @@ -11,6 +10,7 @@ public class AbpUINavigationVueVbenAdmin5NavigationDefinitionProvider : Navigati context.Add(GetDashboard()); context.Add(GetAccount()); context.Add(GetManage()); + context.Add(GetAIManagement()); context.Add(GetSaas()); context.Add(GetPlatform()); context.Add(GetOssManagement()); @@ -529,6 +529,38 @@ public class AbpUINavigationVueVbenAdmin5NavigationDefinitionProvider : Navigati return new NavigationDefinition(manage); } + private static NavigationDefinition GetAIManagement() + { + var aiManagement = new ApplicationMenu( + name: "Artificial Intelligence", + displayName: "人工智能", + url: "/artificia-intelligence", + component: "", + description: "人工智能", + icon: "hugeicons:artificial-intelligence-04") + .SetProperty("title", "abp.ai.title"); + aiManagement.AddItem( + new ApplicationMenu( + name: "Vben5AIWorkspaceDefinitions", + displayName: "工作区管理", + url: "/artificia-intelligence/workspace", + component: "/ai-management/workspaces/index", + icon: "carbon:workspace", + description: "工作区管理") + .SetProperty("title", "abp.ai.workspaces")); + aiManagement.AddItem( + new ApplicationMenu( + name: "Vben5AIConversations", + displayName: "会话管理", + url: "/artificia-intelligence/conversations", + component: "/ai-management/conversations/index", + icon: "arcticons:conversations", + description: "会话管理") + .SetProperty("title", "abp.ai.conversations")); + + return new NavigationDefinition(aiManagement); + } + private static NavigationDefinition GetSaas() { var saas = new ApplicationMenu( From acbdf09428e0e7c1a6dca018d975d2e755e16dbc Mon Sep 17 00:00:00 2001 From: colin Date: Tue, 24 Mar 2026 16:05:27 +0800 Subject: [PATCH 56/88] feat: Standardize the application of time formats - The historical issues of the microservice project will not be activated for the time being. --- .../appsettings.json | 6 ++++-- .../appsettings.json | 6 ++++-- .../appsettings.json | 10 ++++++---- .../appsettings.json | 7 ++++--- .../appsettings.json | 7 ++++--- .../appsettings.json | 7 ++++--- .../appsettings.json | 16 ++++------------ .../appsettings.json | 11 ++++++----- .../appsettings.json | 7 ++++--- .../appsettings.json | 7 ++++--- .../Pages/Account/SelectAccount.cshtml | 2 +- .../Pages/Account/SelectAccount.cshtml.cs | 2 +- .../appsettings.json | 4 +++- 13 files changed, 49 insertions(+), 43 deletions(-) diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.json index b8e4da13c..5b6582f3e 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.json +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.json @@ -1,6 +1,6 @@ { "Clock": { - "Kind": "Local" + "Kind": "Utc" }, "Forwarded": { "ForwardedHeaders": "XForwardedFor,XForwardedProto" @@ -13,7 +13,9 @@ "Json": { "InputDateTimeFormats": [ "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-ddTHH:mm:ss" + "yyyy-MM-ddTHH:mm:ss", + "yyyy-MM-dd HH:mm:ssK", + "yyyy-MM-ddTHH:mm:ssK" ] }, "Serilog": { diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AdminService/appsettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AdminService/appsettings.json index b8e4da13c..5b6582f3e 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AdminService/appsettings.json +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AdminService/appsettings.json @@ -1,6 +1,6 @@ { "Clock": { - "Kind": "Local" + "Kind": "Utc" }, "Forwarded": { "ForwardedHeaders": "XForwardedFor,XForwardedProto" @@ -13,7 +13,9 @@ "Json": { "InputDateTimeFormats": [ "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-ddTHH:mm:ss" + "yyyy-MM-ddTHH:mm:ss", + "yyyy-MM-dd HH:mm:ssK", + "yyyy-MM-ddTHH:mm:ssK" ] }, "Serilog": { diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AuthServer/appsettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AuthServer/appsettings.json index f76197991..b14a350fb 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AuthServer/appsettings.json +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AuthServer/appsettings.json @@ -1,4 +1,7 @@ { + "Clock": { + "Kind": "Utc" + }, "App": { "Branding": { "AppName": "Auth Server" @@ -6,9 +9,6 @@ "SslFile": "openiddict.pfx", "SslPassword": "e1c48393-0c43-11f0-9582-4aecacda42db" }, - "Clock": { - "Kind": "Local" - }, "Forwarded": { "ForwardedHeaders": "XForwardedFor,XForwardedProto" }, @@ -20,7 +20,9 @@ "Json": { "InputDateTimeFormats": [ "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-ddTHH:mm:ss" + "yyyy-MM-ddTHH:mm:ss", + "yyyy-MM-dd HH:mm:ssK", + "yyyy-MM-ddTHH:mm:ssK" ] }, "SkyWalking": { diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.IdentityService/appsettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.IdentityService/appsettings.json index 2c4961697..d6c9ee455 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.IdentityService/appsettings.json +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.IdentityService/appsettings.json @@ -1,6 +1,6 @@ { "Clock": { - "Kind": "Local" + "Kind": "Utc" }, "Forwarded": { "ForwardedHeaders": "XForwardedFor,XForwardedProto" @@ -14,10 +14,11 @@ "DefaultSalt": "sf&5)s3#" }, "Json": { - "OutputDateTimeFormat": "yyyy-MM-dd HH:mm:ss", "InputDateTimeFormats": [ "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-ddTHH:mm:ss" + "yyyy-MM-ddTHH:mm:ss", + "yyyy-MM-dd HH:mm:ssK", + "yyyy-MM-ddTHH:mm:ssK" ] }, "SkyWalking": { diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.LocalizationService/appsettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.LocalizationService/appsettings.json index 3c85151c7..c2136b005 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.LocalizationService/appsettings.json +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.LocalizationService/appsettings.json @@ -1,6 +1,6 @@ { "Clock": { - "Kind": "Local" + "Kind": "Utc" }, "Forwarded": { "ForwardedHeaders": "XForwardedFor,XForwardedProto" @@ -15,10 +15,11 @@ "DefaultSalt": "sf&5)s3#" }, "Json": { - "OutputDateTimeFormat": "yyyy-MM-dd HH:mm:ss", "InputDateTimeFormats": [ "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-ddTHH:mm:ss" + "yyyy-MM-ddTHH:mm:ss", + "yyyy-MM-dd HH:mm:ssK", + "yyyy-MM-ddTHH:mm:ssK" ] }, "SkyWalking": { diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/appsettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/appsettings.json index 3c85151c7..c2136b005 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/appsettings.json +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/appsettings.json @@ -1,6 +1,6 @@ { "Clock": { - "Kind": "Local" + "Kind": "Utc" }, "Forwarded": { "ForwardedHeaders": "XForwardedFor,XForwardedProto" @@ -15,10 +15,11 @@ "DefaultSalt": "sf&5)s3#" }, "Json": { - "OutputDateTimeFormat": "yyyy-MM-dd HH:mm:ss", "InputDateTimeFormats": [ "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-ddTHH:mm:ss" + "yyyy-MM-ddTHH:mm:ss", + "yyyy-MM-dd HH:mm:ssK", + "yyyy-MM-ddTHH:mm:ssK" ] }, "SkyWalking": { diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.PlatformService/appsettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.PlatformService/appsettings.json index 4c9e40bd6..7fb253206 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.PlatformService/appsettings.json +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.PlatformService/appsettings.json @@ -1,15 +1,6 @@ { - "Aspire": { - "StackExchange": { - "Redis": { - "ConfigurationOptions": { - "ConnectRetry": 5 - } - } - } - }, "Clock": { - "Kind": "Local" + "Kind": "Utc" }, "Forwarded": { "ForwardedHeaders": "XForwardedFor,XForwardedProto" @@ -21,10 +12,11 @@ "DefaultSalt": "sf&5)s3#" }, "Json": { - "OutputDateTimeFormat": "yyyy-MM-dd HH:mm:ss", "InputDateTimeFormats": [ "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-ddTHH:mm:ss" + "yyyy-MM-ddTHH:mm:ss", + "yyyy-MM-dd HH:mm:ssK", + "yyyy-MM-ddTHH:mm:ssK" ] }, "SkyWalking": { diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/appsettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/appsettings.json index 06dfdddfc..daf12b49e 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/appsettings.json +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/appsettings.json @@ -1,10 +1,10 @@ { + "Clock": { + "Kind": "Utc" + }, "App": { "CorsOrigins": "http://localhost:3100" }, - "Clock": { - "Kind": "Local" - }, "Forwarded": { "ForwardedHeaders": "XForwardedFor,XForwardedProto" }, @@ -14,10 +14,11 @@ "DefaultSalt": "sf&5)s3#" }, "Json": { - "OutputDateTimeFormat": "yyyy-MM-dd HH:mm:ss", "InputDateTimeFormats": [ "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-ddTHH:mm:ss" + "yyyy-MM-ddTHH:mm:ss", + "yyyy-MM-dd HH:mm:ssK", + "yyyy-MM-ddTHH:mm:ssK" ] }, "SkyWalking": { diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WeChatService/appsettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WeChatService/appsettings.json index f7f8d8154..c0389eaf0 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WeChatService/appsettings.json +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WeChatService/appsettings.json @@ -1,6 +1,6 @@ { "Clock": { - "Kind": "Local" + "Kind": "Utc" }, "Forwarded": { "ForwardedHeaders": "XForwardedFor,XForwardedProto" @@ -11,10 +11,11 @@ "DefaultSalt": "sf&5)s3#" }, "Json": { - "OutputDateTimeFormat": "yyyy-MM-dd HH:mm:ss", "InputDateTimeFormats": [ "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-ddTHH:mm:ss" + "yyyy-MM-ddTHH:mm:ss", + "yyyy-MM-dd HH:mm:ssK", + "yyyy-MM-ddTHH:mm:ssK" ] }, "SkyWalking": { diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/appsettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/appsettings.json index b862514d8..94f6e3d4e 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/appsettings.json +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/appsettings.json @@ -1,6 +1,6 @@ { "Clock": { - "Kind": "Local" + "Kind": "Utc" }, "Forwarded": { "ForwardedHeaders": "XForwardedFor,XForwardedProto" @@ -11,10 +11,11 @@ "DefaultSalt": "sf&5)s3#" }, "Json": { - "OutputDateTimeFormat": "yyyy-MM-dd HH:mm:ss", "InputDateTimeFormats": [ "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-ddTHH:mm:ss" + "yyyy-MM-ddTHH:mm:ss", + "yyyy-MM-dd HH:mm:ssK", + "yyyy-MM-ddTHH:mm:ssK" ] }, "SkyWalking": { diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Pages/Account/SelectAccount.cshtml b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Pages/Account/SelectAccount.cshtml index 9dde8b19b..ae75998fe 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Pages/Account/SelectAccount.cshtml +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Pages/Account/SelectAccount.cshtml @@ -52,7 +52,7 @@ accountName += "/" + account.TenantName; } // TODO: Date format - var lastLoginTime = account.LastLoginTime.HasValue ? account.LastLoginTime.Value.ToString("yyyy-MM:dd HH:mm:ss") : ""; + var lastLoginTime = account.LastLoginTime.HasValue ? account.LastLoginTime.Value.ToString(SelectAccountModel.DefaultDateFormat) : ""; var isCurrent = account.IsCurrentAccount; // TODO: 实现用户头像 var avatar = !string.IsNullOrWhiteSpace(account.UserName) ? account.UserName[0].ToString().ToUpper() : "U"; diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Pages/Account/SelectAccount.cshtml.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Pages/Account/SelectAccount.cshtml.cs index bfe181731..4ec3a00ac 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Pages/Account/SelectAccount.cshtml.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Pages/Account/SelectAccount.cshtml.cs @@ -24,7 +24,7 @@ public class SelectAccountModel : AccountPageModel { private const string LastLoginTimeFieldName = "LastLoginTime"; private const string AllowedTenantsFieldName = "AllowedTenants"; - private const string DefaultDateFormat = "yyyy-MM-dd HH:mm:ss"; + public const string DefaultDateFormat = "yyyy-MM-dd HH:mm:ss"; [BindProperty(SupportsGet = true)] public string RedirectUri { get; set; } diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/appsettings.json b/aspnet-core/services/LY.MicroService.Applications.Single/appsettings.json index 080b0b1d6..85418946b 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/appsettings.json +++ b/aspnet-core/services/LY.MicroService.Applications.Single/appsettings.json @@ -21,7 +21,9 @@ "Json": { "InputDateTimeFormats": [ "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-ddTHH:mm:ss" + "yyyy-MM-ddTHH:mm:ss", + "yyyy-MM-dd HH:mm:ssK", + "yyyy-MM-ddTHH:mm:ssK" ] }, "AllowedHosts": "*", From 8ce30bf7875487791947632c4b7479e92398a020 Mon Sep 17 00:00:00 2001 From: colin Date: Tue, 24 Mar 2026 16:09:21 +0800 Subject: [PATCH 57/88] feat: Adjust the versions of some packages --- Directory.Packages.props | 12 ++++++------ aspnet-core/LINGYUN.MicroService.Aspire.slnx | 17 +++++++++++++++++ .../LINGYUN.Abp.MicroService.AIService.csproj | 2 +- ...LINGYUN.Abp.MicroService.AdminService.csproj | 2 +- .../LINGYUN.Abp.MicroService.ApiGateway.csproj | 2 +- .../LINGYUN.Abp.MicroService.AppHost/AppHost.cs | 2 +- .../LINGYUN.Abp.MicroService.AuthServer.csproj | 2 +- ...GYUN.Abp.MicroService.IdentityService.csproj | 2 +- ....Abp.MicroService.LocalizationService.csproj | 2 +- ...NGYUN.Abp.MicroService.MessageService.csproj | 2 +- .../appsettings.Development.json | 10 +++++----- ...GYUN.Abp.MicroService.PlatformService.csproj | 2 +- .../LINGYUN.Abp.MicroService.TaskService.csproj | 2 +- ...INGYUN.Abp.MicroService.WeChatService.csproj | 2 +- ...NGYUN.Abp.MicroService.WebhookService.csproj | 2 +- ...GYUN.Abp.MicroService.WorkflowService.csproj | 2 +- 16 files changed, 41 insertions(+), 24 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ff9567ced..9964f7847 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@ 10.0.0 - 2.15.2 + 2.16.0 3.3.5 10.0.2 10.0.2 @@ -252,7 +252,7 @@ - + @@ -260,12 +260,12 @@ - + - - + + - + diff --git a/aspnet-core/LINGYUN.MicroService.Aspire.slnx b/aspnet-core/LINGYUN.MicroService.Aspire.slnx index 670b925f3..32dcf1d9f 100644 --- a/aspnet-core/LINGYUN.MicroService.Aspire.slnx +++ b/aspnet-core/LINGYUN.MicroService.Aspire.slnx @@ -536,6 +536,23 @@ + + + + + + + + + + + + + + + + + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj index dafa6b426..828674405 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj @@ -16,7 +16,7 @@ - + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AdminService/LINGYUN.Abp.MicroService.AdminService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AdminService/LINGYUN.Abp.MicroService.AdminService.csproj index 2a22a054b..3ba8b7013 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AdminService/LINGYUN.Abp.MicroService.AdminService.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AdminService/LINGYUN.Abp.MicroService.AdminService.csproj @@ -16,7 +16,7 @@ - + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.ApiGateway/LINGYUN.Abp.MicroService.ApiGateway.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.ApiGateway/LINGYUN.Abp.MicroService.ApiGateway.csproj index f1c6246ba..760812fa9 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.ApiGateway/LINGYUN.Abp.MicroService.ApiGateway.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.ApiGateway/LINGYUN.Abp.MicroService.ApiGateway.csproj @@ -14,7 +14,7 @@ - + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs index dadaf8d82..74b86e32c 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs @@ -238,7 +238,7 @@ AddDotNetProject< // ApiGateway var apigateway = builder.AddProject("ApiGateway") - // .WithHttpEndpoint(port: 30000, name: "gateway") + .WithHttpEndpoint(port: 30000, name: "gateway") .WithExternalHttpEndpoints() .WithReference(redis, "Redis") .WithReference(elasticsearch, "Elasticsearch") diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AuthServer/LINGYUN.Abp.MicroService.AuthServer.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AuthServer/LINGYUN.Abp.MicroService.AuthServer.csproj index f7e001506..a571c5741 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AuthServer/LINGYUN.Abp.MicroService.AuthServer.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AuthServer/LINGYUN.Abp.MicroService.AuthServer.csproj @@ -16,7 +16,7 @@ - + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.IdentityService/LINGYUN.Abp.MicroService.IdentityService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.IdentityService/LINGYUN.Abp.MicroService.IdentityService.csproj index a138d2096..701c75044 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.IdentityService/LINGYUN.Abp.MicroService.IdentityService.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.IdentityService/LINGYUN.Abp.MicroService.IdentityService.csproj @@ -17,7 +17,7 @@ - + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.LocalizationService/LINGYUN.Abp.MicroService.LocalizationService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.LocalizationService/LINGYUN.Abp.MicroService.LocalizationService.csproj index 2ba3bdf49..47841785d 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.LocalizationService/LINGYUN.Abp.MicroService.LocalizationService.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.LocalizationService/LINGYUN.Abp.MicroService.LocalizationService.csproj @@ -16,7 +16,7 @@ - + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/LINGYUN.Abp.MicroService.MessageService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/LINGYUN.Abp.MicroService.MessageService.csproj index 3daaf5c0c..c266d7bb7 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/LINGYUN.Abp.MicroService.MessageService.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/LINGYUN.Abp.MicroService.MessageService.csproj @@ -18,7 +18,7 @@ - + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/appsettings.Development.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/appsettings.Development.json index a3aeaaf5c..829074aa6 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/appsettings.Development.json +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/appsettings.Development.json @@ -115,14 +115,14 @@ }, "Serilog": { "MinimumLevel": { - "Default": "Debug", + "Default": "Information", "Override": { - "Microsoft.EntityFrameworkCore": "Warning", - "DotNetCore.CAP": "Debug", + "Microsoft.EntityFrameworkCore": "Information", + "DotNetCore.CAP": "Information", "System": "Warning", "Microsoft": "Warning", - "Microsoft.AspNetCore.SignalR": "Debug", - "Microsoft.AspNetCore.Http.Connections": "Debug" + "Microsoft.AspNetCore.SignalR": "Information", + "Microsoft.AspNetCore.Http.Connections": "Information" } }, "WriteTo": [ diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.PlatformService/LINGYUN.Abp.MicroService.PlatformService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.PlatformService/LINGYUN.Abp.MicroService.PlatformService.csproj index 4269d0f4f..a0eac7c99 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.PlatformService/LINGYUN.Abp.MicroService.PlatformService.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.PlatformService/LINGYUN.Abp.MicroService.PlatformService.csproj @@ -16,7 +16,7 @@ - + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/LINGYUN.Abp.MicroService.TaskService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/LINGYUN.Abp.MicroService.TaskService.csproj index ba3e066a1..2c7326826 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/LINGYUN.Abp.MicroService.TaskService.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/LINGYUN.Abp.MicroService.TaskService.csproj @@ -17,7 +17,7 @@ - + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WeChatService/LINGYUN.Abp.MicroService.WeChatService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WeChatService/LINGYUN.Abp.MicroService.WeChatService.csproj index 13d464634..85ea2eab7 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WeChatService/LINGYUN.Abp.MicroService.WeChatService.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WeChatService/LINGYUN.Abp.MicroService.WeChatService.csproj @@ -17,7 +17,7 @@ - + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WebhookService/LINGYUN.Abp.MicroService.WebhookService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WebhookService/LINGYUN.Abp.MicroService.WebhookService.csproj index b6bb9c6a8..69bc4d977 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WebhookService/LINGYUN.Abp.MicroService.WebhookService.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WebhookService/LINGYUN.Abp.MicroService.WebhookService.csproj @@ -17,7 +17,7 @@ - + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/LINGYUN.Abp.MicroService.WorkflowService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/LINGYUN.Abp.MicroService.WorkflowService.csproj index 849cb6730..1dd555806 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/LINGYUN.Abp.MicroService.WorkflowService.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/LINGYUN.Abp.MicroService.WorkflowService.csproj @@ -24,7 +24,7 @@ - + From 0fc4f8d0c310682f58194ca4935917269dd41f44 Mon Sep 17 00:00:00 2001 From: colin Date: Tue, 24 Mar 2026 16:35:28 +0800 Subject: [PATCH 58/88] fix: Resolved #1444 --- .../Demo/Authors/EfCoreAuthorRepository.cs | 4 +- .../EfCoreLanguageRepository.cs | 4 +- .../EfCoreResourceRepository.cs | 4 +- .../EfCoreTextRepository.cs | 5 +- .../Platform/Datas/EfCoreDataRepository.cs | 4 +- .../Layouts/EfCoreLayoutRepository.cs | 4 +- .../Platform/Menus/EfCoreMenuRepository.cs | 4 +- .../Menus/EfCoreRoleMenuRepository.cs | 132 +++++++++--------- .../Menus/EfCoreUserFavoriteMenuRepository.cs | 4 +- .../Menus/EfCoreUserMenuRepository.cs | 4 +- .../Packages/EfCorePackageRepository.cs | 4 +- .../EfCoreBackgroundJobActionRepository.cs | 4 +- .../EfCoreBackgroundJobInfoRepository.cs | 4 +- .../EfCoreBackgroundJobLogRepository.cs | 4 +- .../ProjectName/Users/UserRepository.cs | 4 +- 15 files changed, 94 insertions(+), 95 deletions(-) diff --git a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/Authors/EfCoreAuthorRepository.cs b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/Authors/EfCoreAuthorRepository.cs index 4336e8e67..0866258db 100644 --- a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/Authors/EfCoreAuthorRepository.cs +++ b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/Authors/EfCoreAuthorRepository.cs @@ -6,11 +6,11 @@ using Volo.Abp.EntityFrameworkCore; namespace LINGYUN.Abp.Demo.Authors; public class EfCoreAuthorRepository - : EfCoreRepository, + : EfCoreRepository, IAuthorRepository { public EfCoreAuthorRepository( - IDbContextProvider dbContextProvider) + IDbContextProvider dbContextProvider) : base(dbContextProvider) { } diff --git a/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreLanguageRepository.cs b/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreLanguageRepository.cs index e1698ec03..649e2253b 100644 --- a/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreLanguageRepository.cs +++ b/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreLanguageRepository.cs @@ -9,11 +9,11 @@ using Volo.Abp.EntityFrameworkCore; namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; -public class EfCoreLanguageRepository : EfCoreRepository, +public class EfCoreLanguageRepository : EfCoreRepository, ILanguageRepository { public EfCoreLanguageRepository( - IDbContextProvider dbContextProvider) : base(dbContextProvider) + IDbContextProvider dbContextProvider) : base(dbContextProvider) { } diff --git a/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreResourceRepository.cs b/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreResourceRepository.cs index 3291af769..269e308fe 100644 --- a/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreResourceRepository.cs +++ b/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreResourceRepository.cs @@ -9,11 +9,11 @@ using Volo.Abp.Threading; namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; -public class EfCoreResourceRepository : EfCoreRepository, +public class EfCoreResourceRepository : EfCoreRepository, IResourceRepository { public EfCoreResourceRepository( - IDbContextProvider dbContextProvider) : base(dbContextProvider) + IDbContextProvider dbContextProvider) : base(dbContextProvider) { } diff --git a/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreTextRepository.cs b/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreTextRepository.cs index 1834aff8f..af22207b1 100644 --- a/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreTextRepository.cs +++ b/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore/LINGYUN/Abp/LocalizationManagement/EntityFrameworkCore/EfCoreTextRepository.cs @@ -1,5 +1,4 @@ using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Internal; using System; using System.Collections.Generic; using System.Linq; @@ -11,11 +10,11 @@ using Volo.Abp.EntityFrameworkCore; namespace LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; -public class EfCoreTextRepository : EfCoreRepository, +public class EfCoreTextRepository : EfCoreRepository, ITextRepository { public EfCoreTextRepository( - IDbContextProvider dbContextProvider) : base(dbContextProvider) + IDbContextProvider dbContextProvider) : base(dbContextProvider) { } diff --git a/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Datas/EfCoreDataRepository.cs b/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Datas/EfCoreDataRepository.cs index 1b27973c0..cc66e8c9c 100644 --- a/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Datas/EfCoreDataRepository.cs +++ b/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Datas/EfCoreDataRepository.cs @@ -11,10 +11,10 @@ using Volo.Abp.EntityFrameworkCore; namespace LINGYUN.Platform.Datas; -public class EfCoreDataRepository : EfCoreRepository, IDataRepository +public class EfCoreDataRepository : EfCoreRepository, IDataRepository { public EfCoreDataRepository( - IDbContextProvider dbContextProvider) : base(dbContextProvider) + IDbContextProvider dbContextProvider) : base(dbContextProvider) { } diff --git a/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Layouts/EfCoreLayoutRepository.cs b/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Layouts/EfCoreLayoutRepository.cs index dd0cdb1c9..0a2379c2d 100644 --- a/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Layouts/EfCoreLayoutRepository.cs +++ b/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Layouts/EfCoreLayoutRepository.cs @@ -11,9 +11,9 @@ using Volo.Abp.EntityFrameworkCore; namespace LINGYUN.Platform.Layouts; -public class EfCoreLayoutRepository : EfCoreRepository, ILayoutRepository +public class EfCoreLayoutRepository : EfCoreRepository, ILayoutRepository { - public EfCoreLayoutRepository(IDbContextProvider dbContextProvider) + public EfCoreLayoutRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) { } diff --git a/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreMenuRepository.cs b/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreMenuRepository.cs index f88d7ec7f..950cf2bd0 100644 --- a/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreMenuRepository.cs +++ b/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreMenuRepository.cs @@ -11,10 +11,10 @@ using Volo.Abp.EntityFrameworkCore; namespace LINGYUN.Platform.Menus; -public class EfCoreMenuRepository : EfCoreRepository, IMenuRepository +public class EfCoreMenuRepository : EfCoreRepository, IMenuRepository { public EfCoreMenuRepository( - IDbContextProvider dbContextProvider) + IDbContextProvider dbContextProvider) : base(dbContextProvider) { } diff --git a/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreRoleMenuRepository.cs b/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreRoleMenuRepository.cs index 468b56f40..38e5b6fe2 100644 --- a/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreRoleMenuRepository.cs +++ b/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreRoleMenuRepository.cs @@ -1,70 +1,70 @@ -using LINGYUN.Platform.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Dynamic.Core; -using System.Threading; -using System.Threading.Tasks; -using Volo.Abp.Domain.Repositories.EntityFrameworkCore; -using Volo.Abp.EntityFrameworkCore; - -namespace LINGYUN.Platform.Menus; - -public class EfCoreRoleMenuRepository : EfCoreRepository, IRoleMenuRepository -{ - public EfCoreRoleMenuRepository( - IDbContextProvider dbContextProvider) - : base(dbContextProvider) - { - } - - public async virtual Task> GetListByRoleNameAsync( +using LINGYUN.Platform.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace LINGYUN.Platform.Menus; + +public class EfCoreRoleMenuRepository : EfCoreRepository, IRoleMenuRepository +{ + public EfCoreRoleMenuRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public async virtual Task> GetListByRoleNameAsync( string roleName, - string framework = null, - CancellationToken cancellationToken = default) - { - var dbContext = await GetDbContextAsync(); - - var menus = dbContext.Set(); - var roleMenus = dbContext.Set().Where(x => x.RoleName == roleName); - - IQueryable queryable; + string framework = null, + CancellationToken cancellationToken = default) + { + var dbContext = await GetDbContextAsync(); + + var menus = dbContext.Set(); + var roleMenus = dbContext.Set().Where(x => x.RoleName == roleName); + + IQueryable queryable; if (!framework.IsNullOrWhiteSpace()) { - queryable = from menu in menus - join roleMenu in roleMenus - on menu.Id equals roleMenu.MenuId - where menu.Framework == framework + queryable = from menu in menus + join roleMenu in roleMenus + on menu.Id equals roleMenu.MenuId + where menu.Framework == framework select roleMenu; - } + } else { queryable = roleMenus; } - return await queryable.ToListAsync(GetCancellationToken(cancellationToken)); - } - - public async virtual Task RoleHasInMenuAsync( - string roleName, - string menuName, - CancellationToken cancellationToken = default) - { - var menuQuery = (await GetDbContextAsync()).Set().Where(x => x.Name == menuName); - - return await - (from roleMenu in (await GetDbSetAsync()) - join menu in menuQuery - on roleMenu.MenuId equals menu.Id - select roleMenu) - .AnyAsync(x => x.RoleName == roleName, - GetCancellationToken(cancellationToken)); - } - - public async virtual Task FindStartupMenuAsync( + return await queryable.ToListAsync(GetCancellationToken(cancellationToken)); + } + + public async virtual Task RoleHasInMenuAsync( + string roleName, + string menuName, + CancellationToken cancellationToken = default) + { + var menuQuery = (await GetDbContextAsync()).Set().Where(x => x.Name == menuName); + + return await + (from roleMenu in (await GetDbSetAsync()) + join menu in menuQuery + on roleMenu.MenuId equals menu.Id + select roleMenu) + .AnyAsync(x => x.RoleName == roleName, + GetCancellationToken(cancellationToken)); + } + + public async virtual Task FindStartupMenuAsync( IEnumerable roleNames, - string framework = null, + string framework = null, CancellationToken cancellationToken = default) { var dbContext = await GetDbContextAsync(); @@ -72,13 +72,13 @@ public class EfCoreRoleMenuRepository : EfCoreRepository roleNames.Contains(x.RoleName)) .Where(x => x.Startup); - return await - (from roleMenu in roleMenuQuery - join menu in dbContext.Set() - on roleMenu.MenuId equals menu.Id - select menu) - .WhereIf(!framework.IsNullOrWhiteSpace(), x => x.Framework == framework) - .OrderByDescending(x => x.CreationTime) + return await + (from roleMenu in roleMenuQuery + join menu in dbContext.Set() + on roleMenu.MenuId equals menu.Id + select menu) + .WhereIf(!framework.IsNullOrWhiteSpace(), x => x.Framework == framework) + .OrderByDescending(x => x.CreationTime) .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); - } -} + } +} diff --git a/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreUserFavoriteMenuRepository.cs b/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreUserFavoriteMenuRepository.cs index f43af6609..0691d4956 100644 --- a/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreUserFavoriteMenuRepository.cs +++ b/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreUserFavoriteMenuRepository.cs @@ -10,11 +10,11 @@ using Volo.Abp.EntityFrameworkCore; namespace LINGYUN.Platform.Menus; public class EfCoreUserFavoriteMenuRepository : - EfCoreRepository, + EfCoreRepository, IUserFavoriteMenuRepository { public EfCoreUserFavoriteMenuRepository( - IDbContextProvider dbContextProvider) + IDbContextProvider dbContextProvider) : base(dbContextProvider) { } diff --git a/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreUserMenuRepository.cs b/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreUserMenuRepository.cs index fbe61f832..37a3dbd65 100644 --- a/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreUserMenuRepository.cs +++ b/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreUserMenuRepository.cs @@ -10,10 +10,10 @@ using Volo.Abp.EntityFrameworkCore; namespace LINGYUN.Platform.Menus; -public class EfCoreUserMenuRepository : EfCoreRepository, IUserMenuRepository +public class EfCoreUserMenuRepository : EfCoreRepository, IUserMenuRepository { public EfCoreUserMenuRepository( - IDbContextProvider dbContextProvider) + IDbContextProvider dbContextProvider) : base(dbContextProvider) { } diff --git a/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Packages/EfCorePackageRepository.cs b/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Packages/EfCorePackageRepository.cs index 9b7698f69..98c2fe4a7 100644 --- a/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Packages/EfCorePackageRepository.cs +++ b/aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Packages/EfCorePackageRepository.cs @@ -12,11 +12,11 @@ using Volo.Abp.Specifications; namespace LINGYUN.Platform.Packages; public class EfCorePackageRepository : - EfCoreRepository, + EfCoreRepository, IPackageRepository { public EfCorePackageRepository( - IDbContextProvider dbContextProvider) + IDbContextProvider dbContextProvider) : base(dbContextProvider) { } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobActionRepository.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobActionRepository.cs index 5a5967f9f..f49c6cee8 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobActionRepository.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobActionRepository.cs @@ -9,11 +9,11 @@ using Volo.Abp.EntityFrameworkCore; namespace LINGYUN.Abp.TaskManagement.EntityFrameworkCore; public class EfCoreBackgroundJobActionRepository : - EfCoreRepository, + EfCoreRepository, IBackgroundJobActionRepository { public EfCoreBackgroundJobActionRepository( - IDbContextProvider dbContextProvider) + IDbContextProvider dbContextProvider) : base(dbContextProvider) { } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs index 7e1adcdac..0ca182810 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs @@ -14,14 +14,14 @@ using Volo.Abp.Timing; namespace LINGYUN.Abp.TaskManagement.EntityFrameworkCore; public class EfCoreBackgroundJobInfoRepository : - EfCoreRepository, + EfCoreRepository, IBackgroundJobInfoRepository { protected IClock Clock { get; } public EfCoreBackgroundJobInfoRepository( IClock clock, - IDbContextProvider dbContextProvider) + IDbContextProvider dbContextProvider) : base(dbContextProvider) { Clock = clock; diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobLogRepository.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobLogRepository.cs index a22494057..530c22930 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobLogRepository.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobLogRepository.cs @@ -12,11 +12,11 @@ using Volo.Abp.Specifications; namespace LINGYUN.Abp.TaskManagement.EntityFrameworkCore; public class EfCoreBackgroundJobLogRepository : - EfCoreRepository, + EfCoreRepository, IBackgroundJobLogRepository { public EfCoreBackgroundJobLogRepository( - IDbContextProvider dbContextProvider) + IDbContextProvider dbContextProvider) : base(dbContextProvider) { } diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/Users/UserRepository.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/Users/UserRepository.cs index 72d1575bf..d30a28c24 100644 --- a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/Users/UserRepository.cs +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/Users/UserRepository.cs @@ -8,9 +8,9 @@ using Volo.Abp.EntityFrameworkCore; namespace PackageName.CompanyName.ProjectName.Users { - public class UserRepository : EfCoreRepository, IUserRepository + public class UserRepository : EfCoreRepository, IUserRepository { - public UserRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) + public UserRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) { } From 6fbdf5030fb03d841626d1fbfadf75cb4c339853 Mon Sep 17 00:00:00 2001 From: colin Date: Tue, 24 Mar 2026 17:07:03 +0800 Subject: [PATCH 59/88] fix: Add the missing entity references --- .../MessageServiceMigrationsDbContext.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/MessageServiceMigrationsDbContext.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/MessageServiceMigrationsDbContext.cs index c4af21f0e..247e0ede6 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/MessageServiceMigrationsDbContext.cs +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService.EntityFrameworkCore/MessageServiceMigrationsDbContext.cs @@ -44,6 +44,8 @@ public class MessageServiceMigrationsDbContext : public DbSet UserSubscribes { get; set; } + public DbSet NotificationSendRecords { get; set; } + public MessageServiceMigrationsDbContext(DbContextOptions options) : base(options) { From bd577af21dcc2e028f80067ee8aca33df6e63740 Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 25 Mar 2026 15:31:45 +0800 Subject: [PATCH 60/88] feat: Add the missing localized text --- aspnet-core/LINGYUN.MicroService.Aspire.slnx | 2 -- .../LINGYUN/Abp/Account/Localization/Resources/en.json | 5 ++++- .../LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json | 5 ++++- .../Permissions/AIManagementPermissionDefinitionProvider.cs | 2 +- .../LINGYUN/Abp/AIManagement/Localization/Resources/en.json | 5 ++++- .../Abp/AIManagement/Localization/Resources/zh-Hans.json | 5 ++++- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/aspnet-core/LINGYUN.MicroService.Aspire.slnx b/aspnet-core/LINGYUN.MicroService.Aspire.slnx index 32dcf1d9f..ff2345acb 100644 --- a/aspnet-core/LINGYUN.MicroService.Aspire.slnx +++ b/aspnet-core/LINGYUN.MicroService.Aspire.slnx @@ -284,8 +284,6 @@ - - diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/en.json b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/en.json index fa6738c80..daf6584b7 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/en.json +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/en.json @@ -64,6 +64,9 @@ "CancelBind": "Cancel Bind", "BindSuccessfully": "Binding successful!", "CancelBindSuccessfully": "Unbinding successful!", - "CancelBindWarningMessage": "After unbinding, you will not be able to use an external identity. If you log in through this method, your session will be logged out!" + "CancelBindWarningMessage": "After unbinding, you will not be able to use an external identity. If you log in through this method, your session will be logged out!", + "EmailConfirm": "Email Confirm", + "AvatarChanged": "Avatar Changed", + "ClickToValidation": "Click To Validation" } } \ No newline at end of file diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json index c2ac9bd6f..7325b8892 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json @@ -64,6 +64,9 @@ "CancelBind": "解除绑定", "BindSuccessfully": "绑定成功!", "CancelBindSuccessfully": "解除绑定成功!", - "CancelBindWarningMessage": "解除绑定后将无法使用外部身份,如果你通过此方式登录,你的会话将被注销!" + "CancelBindWarningMessage": "解除绑定后将无法使用外部身份,如果你通过此方式登录,你的会话将被注销!", + "EmailConfirm": "确认邮件", + "AvatarChanged": "变更头像", + "ClickToValidation": "点击去验证" } } \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionDefinitionProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionDefinitionProvider.cs index ce5c6f665..9b1c697f4 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionDefinitionProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionDefinitionProvider.cs @@ -21,7 +21,7 @@ public class AIManagementPermissionDefinitionProvider : PermissionDefinitionProv MultiTenancySides.Host); groupDefinition.AddChild( AIManagementPermissionNames.WorkspaceDefinition.Update, - L("Permission:Edit"), + L("Permission:Update"), MultiTenancySides.Host); groupDefinition.AddChild( AIManagementPermissionNames.WorkspaceDefinition.Delete, diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json index b3dfbe714..b60960562 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json @@ -4,8 +4,11 @@ "Permission:AIManagement": "Artificia Intelligence", "Permission:WorkspaceDefinition": "Workspaces", "Permission:Conversation": "Conversations", - "Permission:Chat": "Chats", + "Permission:Chats": "Chats", "Permission:SendMessage": "Send Message", + "Permission:Create": "Create", + "Permission:Update": "Edit", + "Permission:Delete": "Delete", "AIManagement:111001": "Workspace {Workspace} already exists!", "AIManagement:111002": "System workspace {Workspace} is not allowed to be deleted!", "DisplayName:MaxLatestHistoryMessagesToKeep": "Carry the recent conversation records", diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json index dfdbc8f8a..a6825173b 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json @@ -4,8 +4,11 @@ "Permission:AIManagement": "人工智能", "Permission:WorkspaceDefinition": "工作区管理", "Permission:Conversation": "对话管理", - "Permission:Chat": "聊天管理", + "Permission:Chats": "聊天管理", "Permission:SendMessage": "发送消息", + "Permission:Create": "新增", + "Permission:Update": "编辑", + "Permission:Delete": "删除", "AIManagement:111001": "工作区 {Workspace} 已存在!", "AIManagement:111002": "系统工作区 {Workspace} 不允许删除!", "DisplayName:MaxLatestHistoryMessagesToKeep": "携带最近对话记录", From 347830df9d8baeca2807f46131fca7188c8c3785 Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 25 Mar 2026 15:33:17 +0800 Subject: [PATCH 61/88] fix: Fix type mapping --- .../LINGYUN/Abp/Identity/IdentityUserAppService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aspnet-core/modules/identity/LINGYUN.Abp.Identity.Application/LINGYUN/Abp/Identity/IdentityUserAppService.cs b/aspnet-core/modules/identity/LINGYUN.Abp.Identity.Application/LINGYUN/Abp/Identity/IdentityUserAppService.cs index 621b28471..14c958877 100644 --- a/aspnet-core/modules/identity/LINGYUN.Abp.Identity.Application/LINGYUN/Abp/Identity/IdentityUserAppService.cs +++ b/aspnet-core/modules/identity/LINGYUN.Abp.Identity.Application/LINGYUN/Abp/Identity/IdentityUserAppService.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; using System; using System.Collections.Generic; +using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Volo.Abp; @@ -62,7 +63,7 @@ public class IdentityUserAppService : IdentityAppServiceBase, IIdentityUserAppSe { var user = await UserManager.GetByIdAsync(id); - return new ListResultDto(ObjectMapper.Map, List>(user.Claims)); + return new ListResultDto(ObjectMapper.Map, List>(user.Claims.ToList())); } [Authorize(IdentityPermissions.Users.ManageClaims)] From ae888e73cf3e96fa3d14e1bdce7e20f300121b9e Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 25 Mar 2026 15:46:17 +0800 Subject: [PATCH 62/88] chore: Update vben to version 5.6.0 --- apps/vben5/.gitignore | 2 + apps/vben5/.node-version | 2 +- apps/vben5/.npmrc | 2 +- apps/vben5/.prettierignore | 2 + apps/vben5/.stylelintignore | 1 + apps/vben5/.vscode/settings.json | 16 +- apps/vben5/README.ja-JP.md | 6 +- apps/vben5/README.md | 8 +- apps/vben5/README.zh-CN.md | 6 +- apps/vben5/apps/app-antd/.env.development | 10 +- apps/vben5/apps/app-antd/.env.production | 8 +- apps/vben5/apps/app-antd/index.html | 13 - apps/vben5/apps/app-antd/package.json | 3 +- .../app-antd/public/resource/img/logo.png | Bin 0 -> 120444 bytes .../app-antd/src/adapter/component/index.ts | 482 ++++++++- .../app-antd/src/adapter/request/index.ts | 9 +- apps/vben5/apps/app-antd/src/bootstrap.ts | 4 + .../app-antd/src/locales/langs/en-US/abp.json | 11 +- .../app-antd/src/locales/langs/zh-CN/abp.json | 11 +- apps/vben5/apps/app-antd/src/preferences.ts | 3 + apps/vben5/apps/app-antd/src/timezone-init.ts | 28 + .../src/views/_core/authentication/login.vue | 44 +- .../authentication/third-party-login.vue | 6 +- .../src/views/account/my-settings/index.vue | 28 +- .../ai-management/conversations/index.vue | 15 + .../views/ai-management/workspaces/index.vue | 15 + .../notifications/send-records/index.vue | 15 + .../app-antd/src/views/oss/objects/index.vue | 2 +- .../src/views/wechat/settings/index.vue | 15 + apps/vben5/apps/web-antdv-next/.env | 8 + apps/vben5/apps/web-antdv-next/.env.analyze | 7 + .../apps/web-antdv-next/.env.development | 16 + .../vben5/apps/web-antdv-next/.env.production | 19 + apps/vben5/apps/web-antdv-next/index.html | 35 + apps/vben5/apps/web-antdv-next/package.json | 50 + .../apps/web-antdv-next/postcss.config.mjs | 1 + .../apps/web-antdv-next/public/favicon.ico | Bin 0 -> 5430 bytes .../src/adapter/component/index.ts | 603 +++++++++++ .../apps/web-antdv-next/src/adapter/form.ts | 49 + .../web-antdv-next/src/adapter/vxe-table.ts | 70 ++ .../apps/web-antdv-next/src/api/core/auth.ts | 51 + .../apps/web-antdv-next/src/api/core/index.ts | 3 + .../apps/web-antdv-next/src/api/core/menu.ts | 10 + .../apps/web-antdv-next/src/api/core/user.ts | 10 + .../apps/web-antdv-next/src/api/index.ts | 1 + .../apps/web-antdv-next/src/api/request.ts | 113 ++ apps/vben5/apps/web-antdv-next/src/app.vue | 39 + .../apps/web-antdv-next/src/bootstrap.ts | 76 ++ .../apps/web-antdv-next/src/layouts/auth.vue | 25 + .../apps/web-antdv-next/src/layouts/basic.vue | 206 ++++ .../apps/web-antdv-next/src/layouts/index.ts | 6 + .../apps/web-antdv-next/src/locales/README.md | 3 + .../apps/web-antdv-next/src/locales/index.ts | 102 ++ .../src/locales/langs/en-US/demos.json | 14 + .../src/locales/langs/en-US/page.json | 15 + .../src/locales/langs/zh-CN/demos.json | 14 + .../src/locales/langs/zh-CN/page.json | 15 + apps/vben5/apps/web-antdv-next/src/main.ts | 31 + .../apps/web-antdv-next/src/preferences.ts | 13 + .../apps/web-antdv-next/src/router/access.ts | 42 + .../apps/web-antdv-next/src/router/guard.ts | 133 +++ .../apps/web-antdv-next/src/router/index.ts | 37 + .../web-antdv-next/src/router/routes/core.ts | 97 ++ .../web-antdv-next/src/router/routes/index.ts | 37 + .../src/router/routes/modules/dashboard.ts | 38 + .../src/router/routes/modules/demos.ts | 28 + .../src/router/routes/modules/vben.ts | 116 +++ .../apps/web-antdv-next/src/store/auth.ts | 118 +++ .../apps/web-antdv-next/src/store/index.ts | 1 + .../web-antdv-next/src/views/_core/README.md | 3 + .../src/views/_core/about/index.vue | 9 + .../views/_core/authentication/code-login.vue | 69 ++ .../_core/authentication/forget-password.vue | 43 + .../src/views/_core/authentication/login.vue | 98 ++ .../_core/authentication/qrcode-login.vue | 10 + .../views/_core/authentication/register.vue | 96 ++ .../src/views/_core/fallback/coming-soon.vue | 7 + .../src/views/_core/fallback/forbidden.vue | 9 + .../views/_core/fallback/internal-error.vue | 9 + .../src/views/_core/fallback/not-found.vue | 9 + .../src/views/_core/fallback/offline.vue | 9 + .../src/views/_core/profile/base-setting.vue | 65 ++ .../src/views/_core/profile/index.vue | 49 + .../_core/profile/notification-setting.vue | 31 + .../views/_core/profile/password-setting.vue | 63 ++ .../views/_core/profile/security-setting.vue | 43 + .../dashboard/analytics/analytics-trends.vue | 98 ++ .../analytics/analytics-visits-data.vue | 82 ++ .../analytics/analytics-visits-sales.vue | 46 + .../analytics/analytics-visits-source.vue | 65 ++ .../dashboard/analytics/analytics-visits.vue | 55 + .../src/views/dashboard/analytics/index.vue | 90 ++ .../src/views/dashboard/workspace/index.vue | 266 +++++ .../src/views/demos/antd/index.vue | 66 ++ .../apps/web-antdv-next/tailwind.config.mjs | 1 + apps/vben5/apps/web-antdv-next/tsconfig.json | 12 + .../apps/web-antdv-next/tsconfig.node.json | 10 + .../vben5/apps/web-antdv-next/vite.config.mts | 20 + apps/vben5/apps/web-tdesign/.env | 8 + apps/vben5/apps/web-tdesign/.env.analyze | 7 + apps/vben5/apps/web-tdesign/.env.development | 16 + apps/vben5/apps/web-tdesign/.env.production | 19 + apps/vben5/apps/web-tdesign/index.html | 35 + apps/vben5/apps/web-tdesign/package.json | 51 + .../vben5/apps/web-tdesign/postcss.config.mjs | 1 + .../vben5/apps/web-tdesign/public/favicon.ico | Bin 0 -> 5430 bytes .../src/adapter/component/index.ts | 229 ++++ .../apps/web-tdesign/src/adapter/form.ts | 49 + .../apps/web-tdesign/src/adapter/tdesign.ts | 5 + .../apps/web-tdesign/src/adapter/vxe-table.ts | 70 ++ .../apps/web-tdesign/src/api/core/auth.ts | 51 + .../apps/web-tdesign/src/api/core/index.ts | 3 + .../apps/web-tdesign/src/api/core/menu.ts | 10 + .../apps/web-tdesign/src/api/core/user.ts | 10 + apps/vben5/apps/web-tdesign/src/api/index.ts | 1 + .../vben5/apps/web-tdesign/src/api/request.ts | 112 ++ apps/vben5/apps/web-tdesign/src/app.vue | 36 + apps/vben5/apps/web-tdesign/src/bootstrap.ts | 79 ++ .../apps/web-tdesign/src/layouts/auth.vue | 23 + .../apps/web-tdesign/src/layouts/basic.vue | 206 ++++ .../apps/web-tdesign/src/layouts/index.ts | 6 + .../apps/web-tdesign/src/locales/README.md | 3 + .../apps/web-tdesign/src/locales/index.ts | 77 ++ .../src/locales/langs/en-US/demos.json | 13 + .../src/locales/langs/en-US/page.json | 15 + .../src/locales/langs/zh-CN/demos.json | 13 + .../src/locales/langs/zh-CN/page.json | 15 + apps/vben5/apps/web-tdesign/src/main.ts | 31 + .../vben5/apps/web-tdesign/src/preferences.ts | 13 + .../apps/web-tdesign/src/router/access.ts | 41 + .../apps/web-tdesign/src/router/guard.ts | 133 +++ .../apps/web-tdesign/src/router/index.ts | 37 + .../web-tdesign/src/router/routes/core.ts | 97 ++ .../web-tdesign/src/router/routes/index.ts | 37 + .../src/router/routes/modules/dashboard.ts | 38 + .../src/router/routes/modules/demos.ts | 28 + .../src/router/routes/modules/vben.ts | 116 +++ apps/vben5/apps/web-tdesign/src/store/auth.ts | 117 +++ .../vben5/apps/web-tdesign/src/store/index.ts | 1 + .../web-tdesign/src/views/_core/README.md | 3 + .../src/views/_core/about/index.vue | 9 + .../views/_core/authentication/code-login.vue | 69 ++ .../_core/authentication/forget-password.vue | 43 + .../src/views/_core/authentication/login.vue | 99 ++ .../_core/authentication/qrcode-login.vue | 10 + .../views/_core/authentication/register.vue | 96 ++ .../src/views/_core/fallback/coming-soon.vue | 7 + .../src/views/_core/fallback/forbidden.vue | 9 + .../views/_core/fallback/internal-error.vue | 9 + .../src/views/_core/fallback/not-found.vue | 9 + .../src/views/_core/fallback/offline.vue | 9 + .../src/views/_core/profile/base-setting.vue | 65 ++ .../src/views/_core/profile/index.vue | 49 + .../_core/profile/notification-setting.vue | 31 + .../views/_core/profile/password-setting.vue | 63 ++ .../views/_core/profile/security-setting.vue | 43 + .../dashboard/analytics/analytics-trends.vue | 98 ++ .../analytics/analytics-visits-data.vue | 82 ++ .../analytics/analytics-visits-sales.vue | 46 + .../analytics/analytics-visits-source.vue | 65 ++ .../dashboard/analytics/analytics-visits.vue | 55 + .../src/views/dashboard/analytics/index.vue | 90 ++ .../src/views/dashboard/workspace/index.vue | 266 +++++ .../src/views/demos/tdesign/index.vue | 67 ++ .../apps/web-tdesign/tailwind.config.mjs | 1 + apps/vben5/apps/web-tdesign/tsconfig.json | 12 + .../vben5/apps/web-tdesign/tsconfig.node.json | 10 + apps/vben5/apps/web-tdesign/vite.config.mts | 20 + apps/vben5/cspell.json | 25 +- .../.vitepress/components/demo-preview.vue | 6 +- .../.vitepress/components/preview-group.vue | 14 +- .../.vitepress/config/plugins/demo-preview.ts | 2 +- apps/vben5/docs/package.json | 4 +- apps/vben5/docs/src/_env/adapter/vxe-table.ts | 5 +- .../src/components/common-ui/vben-drawer.md | 8 +- .../src/components/common-ui/vben-form.md | 80 +- .../src/components/common-ui/vben-modal.md | 10 + .../demos/vben-drawer/auto-height/drawer.vue | 2 +- .../docs/src/demos/vben-form/rules/index.vue | 1 + .../demos/vben-modal/animation-type/index.vue | 36 + .../demos/vben-modal/auto-height/modal.vue | 2 +- .../vben-vxe-table/custom-cell/index.vue | 2 +- .../src/en/guide/essentials/development.md | 2 + .../docs/src/en/guide/essentials/server.md | 4 +- .../docs/src/en/guide/essentials/settings.md | 6 + .../src/en/guide/introduction/quick-start.md | 1 + .../docs/src/en/guide/introduction/thin.md | 1 + .../docs/src/en/guide/project/standard.md | 3 - apps/vben5/docs/src/friend-links/index.md | 1 - .../docs/src/guide/essentials/development.md | 2 + apps/vben5/docs/src/guide/essentials/route.md | 7 + .../vben5/docs/src/guide/essentials/server.md | 4 +- .../docs/src/guide/essentials/settings.md | 6 + .../src/guide/introduction/quick-start.md | 3 +- .../vben5/docs/src/guide/introduction/thin.md | 5 +- apps/vben5/docs/src/guide/project/standard.md | 3 - .../lint-configs/commitlint-config/index.mjs | 2 +- .../commitlint-config/package.json | 2 +- .../lint-configs/eslint-config/package.json | 7 +- .../eslint-config/src/configs/command.ts | 1 - .../eslint-config/src/configs/ignores.ts | 2 + .../eslint-config/src/configs/index.ts | 2 + .../eslint-config/src/configs/jsonc.ts | 16 + .../eslint-config/src/configs/node.ts | 2 +- .../src/configs/perfectionist.ts | 1 - .../eslint-config/src/configs/pnpm.ts | 41 + .../eslint-config/src/configs/turbo.ts | 1 - .../eslint-config/src/configs/typescript.ts | 7 +- .../eslint-config/src/configs/vue.ts | 1 - .../eslint-config/src/configs/yaml.ts | 87 ++ .../lint-configs/eslint-config/src/index.ts | 4 + .../lint-configs/prettier-config/package.json | 2 +- .../lint-configs/stylelint-config/index.mjs | 1 + .../stylelint-config/package.json | 2 +- apps/vben5/internal/node-utils/package.json | 2 +- .../internal/tailwind-config/package.json | 3 +- .../internal/tailwind-config/src/index.ts | 8 +- .../internal/tailwind-config/tsconfig.json | 3 - apps/vben5/internal/tsconfig/node.json | 1 + apps/vben5/internal/tsconfig/package.json | 2 +- apps/vben5/internal/vite-config/package.json | 2 +- .../vite-config/src/config/application.ts | 2 +- .../internal/vite-config/src/config/index.ts | 4 +- apps/vben5/internal/vite-config/src/index.ts | 1 + apps/vben5/internal/vite-config/src/typing.ts | 10 +- apps/vben5/package.json | 30 +- apps/vben5/packages/@abp/account/package.json | 5 +- .../@abp/account/src/components/MySetting.vue | 22 +- .../components/AuthenticatorSettings.vue | 5 +- .../components/components/BasicSettings.vue | 2 +- .../components/components/BindSettings.vue | 2 +- .../components/components/NoticeSettings.vue | 2 +- .../components/SecuritySettings.vue | 2 +- .../components/components/SessionSettings.vue | 2 +- .../components/components/UserSettings.vue | 9 + .../packages/@abp/account/src/utils/auth.ts | 14 +- .../packages/@abp/ai-management/package.json | 46 + .../@abp/ai-management/src/api/index.ts | 3 + .../@abp/ai-management/src/api/useChatsApi.ts | 77 ++ .../src/api/useConversationsApi.ts | 92 ++ .../src/api/useWorkspaceDefinitionsApi.ts | 117 +++ .../src/components/conversations/index.vue | 618 +++++++++++ .../ai-management/src/components/index.ts | 2 + .../workspaces/WorkspaceDefinitionModal.vue | 307 ++++++ .../workspaces/WorkspaceDefinitionTable.vue | 209 ++++ .../src/constants/permissions.ts | 18 + .../packages/@abp/ai-management/src/index.ts | 3 + .../@abp/ai-management/src/types/chats.ts | 42 + .../ai-management/src/types/conversations.ts | 32 + .../@abp/ai-management/src/types/index.ts | 3 + .../ai-management/src/types/workspaces.ts | 67 ++ .../packages/@abp/ai-management/tsconfig.json | 6 + .../vben5/packages/@abp/auditing/package.json | 4 +- .../components/audit-logs/AuditLogDrawer.vue | 2 + .../components/audit-logs/AuditLogTable.vue | 2 +- .../src/components/loggings/LoggingDrawer.vue | 44 +- .../packages/@abp/components/package.json | 4 +- .../components/src/codemirror/codemirror.css | 34 +- apps/vben5/packages/@abp/core/package.json | 3 +- .../vben5/packages/@abp/core/src/types/dto.ts | 63 +- .../packages/@abp/core/src/utils/date.ts | 35 +- apps/vben5/packages/@abp/core/src/utils/is.ts | 12 +- .../@abp/data-protection/package.json | 4 +- apps/vben5/packages/@abp/demo/package.json | 4 +- .../vben5/packages/@abp/features/package.json | 4 +- .../features/FeatureDefinitionTable.vue | 11 +- .../groups/FeatureGroupDefinitionTable.vue | 2 +- .../@abp/features/src/types/groups.ts | 6 +- apps/vben5/packages/@abp/gdpr/package.json | 4 +- .../vben5/packages/@abp/identity/package.json | 5 +- .../src/components/sessions/SessionTable.vue | 10 +- .../components/sessions/UserSessionTable.vue | 7 +- .../src/components/users/UserModal.vue | 23 +- .../components/users/UserSessionDrawer.vue | 2 +- .../packages/@abp/identity/src/types/users.ts | 15 +- .../packages/@abp/localization/package.json | 4 +- .../languages/LocalizationLanguageTable.vue | 2 +- .../resources/LocalizationResourceTable.vue | 2 +- .../texts/LocalizationTextTable.vue | 2 +- .../packages/@abp/notifications/package.json | 4 +- .../@abp/notifications/src/api/index.ts | 1 + .../src/api/useNotificationSendRecordsApi.ts | 55 + .../NotificationGroupDefinitionTable.vue | 2 +- .../NotificationDefinitionTable.vue | 19 +- .../notifications/src/components/index.ts | 1 + .../my-notifilers/MyNotificationModal.vue | 17 +- .../NotificationSendRecordTable.vue | 349 +++++++ .../notifications/src/types/definitions.ts | 3 +- .../@abp/notifications/src/types/index.ts | 1 + .../notifications/src/types/send-records.ts | 34 + .../packages/@abp/openiddict/package.json | 4 +- .../src/components/scopes/ScopeModal.vue | 50 +- apps/vben5/packages/@abp/oss/package.json | 4 +- .../oss/src/components/objects/FileList.vue | 5 +- .../oss/src/components/objects/FolderTree.vue | 34 +- .../oss/src/components/objects/ObjectPage.vue | 4 +- .../packages/@abp/permissions/package.json | 4 +- .../groups/PermissionGroupDefinitionTable.vue | 2 +- .../permissions/PermissionDefinitionTable.vue | 4 +- .../vben5/packages/@abp/platform/package.json | 4 +- .../DataDictionaryItemDrawer.vue | 2 +- .../data-dictionaries/DataDictionaryTable.vue | 2 +- .../src/components/menus/MenuTable.vue | 2 +- apps/vben5/packages/@abp/request/package.json | 2 +- .../packages/@abp/request/src/hooks/index.ts | 1 + .../@abp/request/src/hooks/useErrorFormat.ts | 8 +- .../@abp/request/src/hooks/useSseRequest.ts | 34 + .../request/src/hooks/useWrapperResult.ts | 15 +- apps/vben5/packages/@abp/request/src/index.ts | 2 + apps/vben5/packages/@abp/saas/package.json | 4 +- .../vben5/packages/@abp/settings/package.json | 4 +- .../packages/@abp/settings/src/api/index.ts | 1 + .../src/api/useTimeZoneSettingsApi.ts | 72 ++ .../definitions/SettingDefinitionTable.vue | 2 +- .../src/components/settings/SettingForm.vue | 6 +- apps/vben5/packages/@abp/signalr/package.json | 2 +- apps/vben5/packages/@abp/tasks/package.json | 4 +- .../@abp/text-templating/package.json | 4 +- .../definitions/TemplateDefinitionTable.vue | 2 +- apps/vben5/packages/@abp/ui/package.json | 4 +- .../packages/@abp/ui/src/adapter/vxe-table.ts | 10 +- .../@abp/ui/src/components/vxe-table/api.ts | 16 +- .../ui/src/components/vxe-table/style.css | 13 + .../@abp/ui/src/components/vxe-table/types.ts | 31 +- .../src/components/vxe-table/use-vxe-grid.ts | 31 +- .../src/components/vxe-table/use-vxe-grid.vue | 138 ++- .../vben5/packages/@abp/ui/src/hooks/index.ts | 1 + .../packages/@abp/ui/src/hooks/useMessage.ts | 5 + apps/vben5/packages/@abp/ui/src/index.ts | 1 + .../vben5/packages/@abp/webhooks/package.json | 4 +- .../groups/WebhookGroupDefinitionTable.vue | 2 +- .../webhooks/WebhookDefinitionTable.vue | 4 +- .../send-attempts/WebhookSendAttemptTable.vue | 4 +- apps/vben5/packages/@abp/wechat/package.json | 4 +- .../wechat/src/components/bind-user/index.vue | 1 - .../packages/@core/base/design/package.json | 2 +- .../@core/base/design/src/css/global.css | 9 +- .../packages/@core/base/design/src/css/ui.css | 24 +- .../base/design/src/design-tokens/default.css | 1 + .../base/design/src/design-tokens/index.ts | 2 - .../packages/@core/base/design/src/index.ts | 2 - .../packages/@core/base/icons/package.json | 2 +- .../packages/@core/base/icons/src/lucide.ts | 3 + .../packages/@core/base/shared/package.json | 9 +- .../@core/base/shared/src/constants/vben.ts | 4 + .../shared/src/utils/__tests__/date.test.ts | 143 +++ .../shared/src/utils/__tests__/dom.test.ts | 8 +- .../src/utils/__tests__/resources.test.ts | 82 ++ .../shared/src/utils/__tests__/stack.test.ts | 107 ++ .../@core/base/shared/src/utils/date.ts | 59 +- .../@core/base/shared/src/utils/dom.ts | 12 + .../@core/base/shared/src/utils/index.ts | 7 +- .../@core/base/shared/src/utils/resources.ts | 21 + .../@core/base/shared/src/utils/stack.ts | 103 ++ .../@core/base/shared/src/utils/tree.ts | 30 +- .../@core/base/shared/src/utils/window.ts | 2 +- .../packages/@core/base/typings/package.json | 2 +- .../packages/@core/base/typings/src/app.d.ts | 10 + .../@core/base/typings/src/helper.d.ts | 34 +- .../@core/base/typings/src/vue-router.d.ts | 4 + .../packages/@core/composables/package.json | 4 +- .../src/__tests__/use-sortable.test.ts | 6 +- .../packages/@core/composables/src/index.ts | 2 +- .../__snapshots__/config.test.ts.snap | 9 + .../packages/@core/preferences/package.json | 2 +- .../packages/@core/preferences/src/config.ts | 9 + .../@core/preferences/src/constants.ts | 38 +- .../packages/@core/preferences/src/index.ts | 34 +- .../@core/preferences/src/preferences.ts | 197 ++-- .../packages/@core/preferences/src/types.ts | 24 + .../preferences/src/update-css-variables.ts | 13 + .../@core/preferences/src/use-preferences.ts | 2 +- .../@core/ui-kit/form-ui/package.json | 2 +- .../form-ui/src/components/form-actions.vue | 86 +- .../@core/ui-kit/form-ui/src/form-api.ts | 139 +-- .../form-ui/src/form-render/dependencies.ts | 24 +- .../form-ui/src/form-render/form-field.vue | 21 +- .../form-ui/src/form-render/form-label.vue | 2 +- .../ui-kit/form-ui/src/form-render/form.vue | 33 +- .../@core/ui-kit/form-ui/src/types.ts | 36 +- .../ui-kit/form-ui/src/use-form-context.ts | 2 +- .../@core/ui-kit/form-ui/src/vben-form.vue | 4 +- .../ui-kit/form-ui/src/vben-use-form.vue | 21 +- .../@core/ui-kit/layout-ui/package.json | 2 +- .../src/components/layout-content.vue | 3 +- .../src/components/layout-footer.vue | 2 +- .../src/components/layout-header.vue | 2 +- .../src/components/layout-sidebar.vue | 131 ++- .../src/components/layout-tabbar.vue | 2 +- .../widgets/sidebar-collapse-button.vue | 2 +- .../widgets/sidebar-fixed-button.vue | 2 +- .../layout-ui/src/hooks/use-sidebar-drag.ts | 157 +++ .../@core/ui-kit/layout-ui/src/vben-layout.ts | 5 + .../ui-kit/layout-ui/src/vben-layout.vue | 62 +- .../@core/ui-kit/menu-ui/package.json | 2 +- .../menu-ui/src/components/menu-badge.vue | 2 +- .../ui-kit/menu-ui/src/components/menu.vue | 58 +- .../components/normal-menu/normal-menu.vue | 14 +- .../menu-ui/src/components/sub-menu.vue | 3 +- .../@core/ui-kit/popup-ui/package.json | 2 +- .../ui-kit/popup-ui/src/alert/AlertBuilder.ts | 18 +- .../@core/ui-kit/popup-ui/src/alert/alert.vue | 4 +- .../ui-kit/popup-ui/src/drawer/drawer.vue | 5 +- .../ui-kit/popup-ui/src/modal/modal-api.ts | 8 +- .../@core/ui-kit/popup-ui/src/modal/modal.ts | 5 + .../@core/ui-kit/popup-ui/src/modal/modal.vue | 52 +- .../popup-ui/src/modal/use-modal-draggable.ts | 41 +- .../ui-kit/popup-ui/src/modal/use-modal.ts | 10 +- .../@core/ui-kit/shadcn-ui/package.json | 4 +- .../src/components/avatar/avatar.vue | 4 +- .../src/components/back-top/back-top.vue | 2 +- .../breadcrumb/breadcrumb-background.vue | 8 +- .../components/breadcrumb/breadcrumb-view.vue | 2 +- .../shadcn-ui/src/components/button/button.ts | 10 +- .../src/components/button/button.vue | 2 +- .../src/components/checkbox/checkbox.vue | 8 +- .../components/context-menu/context-menu.vue | 5 +- .../src/components/context-menu/interface.ts | 4 + .../dropdown-menu/dropdown-menu.vue | 2 +- .../dropdown-menu/dropdown-radio-menu.vue | 4 +- .../components/full-screen/full-screen.vue | 9 +- .../src/components/hover-card/hover-card.vue | 4 +- .../src/components/hover-card/index.ts | 2 +- .../input-password/input-password.vue | 4 +- .../input-password/password-strength.vue | 2 +- .../shadcn-ui/src/components/logo/logo.vue | 27 +- .../src/components/pin-input/input.vue | 6 +- .../src/components/popover/popover.vue | 4 +- .../render-content/render-content.vue | 13 +- .../src/components/scrollbar/scrollbar.vue | 4 +- .../src/components/segmented/segmented.vue | 14 +- .../components/segmented/tabs-indicator.vue | 8 +- .../src/components/spinner/loading.vue | 12 +- .../src/components/spinner/spinner.vue | 4 +- .../src/components/tooltip/help-tooltip.vue | 2 +- .../src/components/tooltip/tooltip.vue | 4 +- .../@core/ui-kit/shadcn-ui/src/index.ts | 2 +- .../shadcn-ui/src/ui/accordion/Accordion.vue | 4 +- .../src/ui/accordion/AccordionContent.vue | 6 +- .../src/ui/accordion/AccordionItem.vue | 4 +- .../src/ui/accordion/AccordionTrigger.vue | 6 +- .../src/ui/alert-dialog/AlertDialog.vue | 4 +- .../src/ui/alert-dialog/AlertDialogAction.vue | 4 +- .../src/ui/alert-dialog/AlertDialogCancel.vue | 4 +- .../ui/alert-dialog/AlertDialogContent.vue | 11 +- .../alert-dialog/AlertDialogDescription.vue | 6 +- .../ui/alert-dialog/AlertDialogOverlay.vue | 2 +- .../src/ui/alert-dialog/AlertDialogTitle.vue | 4 +- .../ui-kit/shadcn-ui/src/ui/avatar/Avatar.vue | 2 +- .../src/ui/avatar/AvatarFallback.vue | 4 +- .../shadcn-ui/src/ui/avatar/AvatarImage.vue | 4 +- .../src/ui/breadcrumb/BreadcrumbItem.vue | 2 +- .../src/ui/breadcrumb/BreadcrumbLink.vue | 6 +- .../src/ui/breadcrumb/BreadcrumbList.vue | 2 +- .../src/ui/breadcrumb/BreadcrumbPage.vue | 2 +- .../ui-kit/shadcn-ui/src/ui/button/Button.vue | 4 +- .../ui-kit/shadcn-ui/src/ui/card/Card.vue | 2 +- .../shadcn-ui/src/ui/card/CardDescription.vue | 2 +- .../shadcn-ui/src/ui/checkbox/Checkbox.vue | 10 +- .../src/ui/context-menu/ContextMenu.vue | 4 +- .../context-menu/ContextMenuCheckboxItem.vue | 6 +- .../ui/context-menu/ContextMenuContent.vue | 9 +- .../src/ui/context-menu/ContextMenuGroup.vue | 4 +- .../src/ui/context-menu/ContextMenuItem.vue | 6 +- .../src/ui/context-menu/ContextMenuLabel.vue | 6 +- .../src/ui/context-menu/ContextMenuPortal.vue | 4 +- .../ui/context-menu/ContextMenuRadioGroup.vue | 4 +- .../ui/context-menu/ContextMenuRadioItem.vue | 6 +- .../ui/context-menu/ContextMenuSeparator.vue | 6 +- .../ui/context-menu/ContextMenuShortcut.vue | 2 +- .../src/ui/context-menu/ContextMenuSub.vue | 4 +- .../ui/context-menu/ContextMenuSubContent.vue | 13 +- .../ui/context-menu/ContextMenuSubTrigger.vue | 6 +- .../ui/context-menu/ContextMenuTrigger.vue | 4 +- .../ui-kit/shadcn-ui/src/ui/dialog/Dialog.vue | 4 +- .../shadcn-ui/src/ui/dialog/DialogClose.vue | 4 +- .../shadcn-ui/src/ui/dialog/DialogContent.vue | 30 +- .../src/ui/dialog/DialogDescription.vue | 6 +- .../shadcn-ui/src/ui/dialog/DialogOverlay.vue | 2 +- .../src/ui/dialog/DialogScrollContent.vue | 10 +- .../shadcn-ui/src/ui/dialog/DialogTitle.vue | 4 +- .../shadcn-ui/src/ui/dialog/DialogTrigger.vue | 4 +- .../src/ui/dropdown-menu/DropdownMenu.vue | 4 +- .../DropdownMenuCheckboxItem.vue | 6 +- .../ui/dropdown-menu/DropdownMenuContent.vue | 6 +- .../ui/dropdown-menu/DropdownMenuGroup.vue | 4 +- .../src/ui/dropdown-menu/DropdownMenuItem.vue | 6 +- .../ui/dropdown-menu/DropdownMenuLabel.vue | 4 +- .../dropdown-menu/DropdownMenuRadioGroup.vue | 4 +- .../dropdown-menu/DropdownMenuRadioItem.vue | 6 +- .../dropdown-menu/DropdownMenuSeparator.vue | 6 +- .../src/ui/dropdown-menu/DropdownMenuSub.vue | 4 +- .../dropdown-menu/DropdownMenuSubContent.vue | 13 +- .../dropdown-menu/DropdownMenuSubTrigger.vue | 6 +- .../ui/dropdown-menu/DropdownMenuTrigger.vue | 4 +- .../shadcn-ui/src/ui/dropdown-menu/index.ts | 2 +- .../shadcn-ui/src/ui/form/FormControl.vue | 2 +- .../shadcn-ui/src/ui/form/FormDescription.vue | 2 +- .../shadcn-ui/src/ui/form/FormLabel.vue | 2 +- .../shadcn-ui/src/ui/form/FormMessage.vue | 2 +- .../shadcn-ui/src/ui/hover-card/HoverCard.vue | 4 +- .../src/ui/hover-card/HoverCardContent.vue | 6 +- .../src/ui/hover-card/HoverCardTrigger.vue | 4 +- .../ui-kit/shadcn-ui/src/ui/input/Input.vue | 13 +- .../ui-kit/shadcn-ui/src/ui/label/Label.vue | 4 +- .../src/ui/number-field/NumberField.vue | 4 +- .../ui/number-field/NumberFieldDecrement.vue | 4 +- .../ui/number-field/NumberFieldIncrement.vue | 4 +- .../src/ui/number-field/NumberFieldInput.vue | 4 +- .../src/ui/pagination/PaginationEllipsis.vue | 4 +- .../src/ui/pagination/PaginationFirst.vue | 4 +- .../src/ui/pagination/PaginationLast.vue | 4 +- .../src/ui/pagination/PaginationNext.vue | 4 +- .../src/ui/pagination/PaginationPrev.vue | 4 +- .../shadcn-ui/src/ui/pagination/index.ts | 2 +- .../shadcn-ui/src/ui/pin-input/PinInput.vue | 4 +- .../src/ui/pin-input/PinInputGroup.vue | 4 +- .../src/ui/pin-input/PinInputInput.vue | 6 +- .../src/ui/pin-input/PinInputSeparator.vue | 4 +- .../shadcn-ui/src/ui/popover/Popover.vue | 4 +- .../src/ui/popover/PopoverContent.vue | 6 +- .../src/ui/popover/PopoverTrigger.vue | 4 +- .../ui-kit/shadcn-ui/src/ui/popover/index.ts | 2 +- .../src/ui/radio-group/RadioGroup.vue | 4 +- .../src/ui/radio-group/RadioGroupItem.vue | 10 +- .../src/ui/resizable/ResizableHandle.vue | 8 +- .../src/ui/resizable/ResizablePanelGroup.vue | 4 +- .../shadcn-ui/src/ui/resizable/index.ts | 2 +- .../src/ui/scroll-area/ScrollArea.vue | 8 +- .../src/ui/scroll-area/ScrollBar.vue | 6 +- .../ui-kit/shadcn-ui/src/ui/select/Select.vue | 4 +- .../shadcn-ui/src/ui/select/SelectContent.vue | 8 +- .../shadcn-ui/src/ui/select/SelectGroup.vue | 4 +- .../shadcn-ui/src/ui/select/SelectItem.vue | 6 +- .../src/ui/select/SelectItemText.vue | 4 +- .../shadcn-ui/src/ui/select/SelectLabel.vue | 4 +- .../src/ui/select/SelectScrollDownButton.vue | 4 +- .../src/ui/select/SelectScrollUpButton.vue | 4 +- .../src/ui/select/SelectSeparator.vue | 6 +- .../shadcn-ui/src/ui/select/SelectTrigger.vue | 6 +- .../shadcn-ui/src/ui/select/SelectValue.vue | 4 +- .../shadcn-ui/src/ui/separator/Separator.vue | 8 +- .../ui-kit/shadcn-ui/src/ui/sheet/Sheet.vue | 4 +- .../shadcn-ui/src/ui/sheet/SheetClose.vue | 4 +- .../shadcn-ui/src/ui/sheet/SheetContent.vue | 8 +- .../src/ui/sheet/SheetDescription.vue | 6 +- .../shadcn-ui/src/ui/sheet/SheetOverlay.vue | 2 +- .../shadcn-ui/src/ui/sheet/SheetTitle.vue | 6 +- .../shadcn-ui/src/ui/sheet/SheetTrigger.vue | 4 +- .../ui-kit/shadcn-ui/src/ui/switch/Switch.vue | 8 +- .../ui-kit/shadcn-ui/src/ui/tabs/Tabs.vue | 4 +- .../shadcn-ui/src/ui/tabs/TabsContent.vue | 6 +- .../ui-kit/shadcn-ui/src/ui/tabs/TabsList.vue | 6 +- .../shadcn-ui/src/ui/tabs/TabsTrigger.vue | 6 +- .../ui-kit/shadcn-ui/src/ui/tabs/index.ts | 2 +- .../shadcn-ui/src/ui/textarea/Textarea.vue | 2 +- .../src/ui/toggle-group/ToggleGroup.vue | 4 +- .../src/ui/toggle-group/ToggleGroupItem.vue | 4 +- .../ui-kit/shadcn-ui/src/ui/toggle/Toggle.vue | 4 +- .../shadcn-ui/src/ui/tooltip/Tooltip.vue | 4 +- .../src/ui/tooltip/TooltipContent.vue | 6 +- .../src/ui/tooltip/TooltipProvider.vue | 4 +- .../src/ui/tooltip/TooltipTrigger.vue | 4 +- .../ui-kit/shadcn-ui/src/ui/tree/index.ts | 4 +- .../ui-kit/shadcn-ui/src/ui/tree/tree.vue | 305 ++++-- .../ui-kit/shadcn-ui/src/ui/tree/types.ts | 24 +- .../@core/ui-kit/tabs-ui/package.json | 2 +- .../src/components/tabs-chrome/tabs.vue | 20 +- .../tabs-ui/src/components/tabs/tabs.vue | 12 +- .../tabs-ui/src/components/widgets/index.ts | 1 + .../src/components/widgets/tool-more.vue | 6 +- .../src/components/widgets/tool-refresh.vue | 18 + .../src/components/widgets/tool-screen.vue | 2 +- .../@core/ui-kit/tabs-ui/src/tabs-view.vue | 13 +- .../tabs-ui/src/use-tabs-view-scroll.ts | 2 +- apps/vben5/packages/constants/package.json | 2 +- .../packages/effects/access/package.json | 2 +- .../packages/effects/common-ui/package.json | 3 +- .../api-component/api-component.vue | 48 +- .../common-ui/src/components/captcha/index.ts | 1 + .../slider-translate-captcha/index.vue | 311 ++++++ .../common-ui/src/components/captcha/types.ts | 39 +- .../src/components/cropper/cropper.vue | 979 ++++++++++++++++++ .../common-ui/src/components/cropper/index.ts | 1 + .../components/icon-picker/icon-picker.vue | 4 +- .../effects/common-ui/src/components/index.ts | 6 +- .../src/components/json-viewer/index.vue | 28 +- .../common-ui/src/components/page/page.vue | 12 +- .../src/components/resize/resize.vue | 10 +- .../common-ui/src/components/tree/index.ts | 1 + .../common-ui/src/components/tree/tree.vue | 25 + .../src/ui/authentication/code-login.vue | 12 +- .../src/ui/authentication/dingding-login.vue | 113 ++ .../ui/authentication/login-expired-modal.vue | 2 + .../common-ui/src/ui/authentication/login.vue | 2 +- .../src/ui/authentication/qrcode-login.vue | 12 +- .../ui/authentication/third-party-login.vue | 52 +- .../ui/dashboard/workbench/workbench-todo.vue | 2 +- .../effects/common-ui/src/ui/index.ts | 1 + .../common-ui/src/ui/profile/base-setting.vue | 58 ++ .../effects/common-ui/src/ui/profile/index.ts | 6 + .../src/ui/profile/notification-setting.vue | 53 + .../src/ui/profile/password-setting.vue | 59 ++ .../common-ui/src/ui/profile/profile.vue | 62 ++ .../src/ui/profile/security-setting.vue | 53 + .../effects/common-ui/src/ui/profile/types.ts | 21 + .../vben5/packages/effects/hooks/package.json | 2 +- .../effects/hooks/src/use-app-config.ts | 40 +- .../effects/hooks/src/use-hover-toggle.ts | 72 +- .../effects/hooks/src/use-pagination.ts | 26 +- .../packages/effects/layouts/package.json | 2 +- .../src/authentication/authentication.vue | 66 +- .../layouts/src/authentication/form.vue | 7 +- .../layouts/src/basic/content/content.vue | 102 +- .../layouts/src/basic/header/header.vue | 18 +- .../effects/layouts/src/basic/layout.vue | 56 +- .../layouts/src/basic/menu/use-navigation.ts | 17 +- .../layouts/src/basic/tabbar/tabbar.vue | 13 +- .../effects/layouts/src/hooks/index.ts | 98 ++ .../effects/layouts/src/route-cached/index.ts | 2 + .../src/route-cached/route-cached-page.vue | 36 + .../src/route-cached/route-cached-view.vue | 98 ++ .../widgets/global-search/search-panel.vue | 2 +- .../effects/layouts/src/widgets/index.ts | 1 + .../layouts/src/widgets/language-toggle.vue | 2 +- .../widgets/lock-screen/lock-screen-modal.vue | 52 +- .../src/widgets/lock-screen/lock-screen.vue | 53 +- .../src/widgets/notification/notification.vue | 56 +- .../layouts/src/widgets/notification/types.ts | 8 + .../preferences/blocks/general/general.vue | 22 +- .../src/widgets/preferences/blocks/index.ts | 1 + .../widgets/preferences/blocks/input-item.vue | 15 +- .../preferences/blocks/layout/sidebar.vue | 4 + .../preferences/blocks/layout/tabbar.vue | 8 + .../preferences/blocks/switch-item.vue | 2 +- .../preferences/blocks/theme/builtin.vue | 13 +- .../preferences/blocks/theme/font-size.vue | 62 ++ .../preferences/blocks/theme/theme.vue | 33 +- .../preferences/preferences-button.vue | 2 +- .../preferences/preferences-drawer.vue | 67 +- .../src/widgets/theme-toggle/theme-button.vue | 9 +- .../layouts/src/widgets/timezone/index.ts | 1 + .../src/widgets/timezone/timezone-button.vue | 87 ++ .../packages/effects/plugins/package.json | 2 +- .../plugins/src/echarts/use-echarts.ts | 87 +- .../plugins/src/vxe-table/use-vxe-grid.ts | 20 + .../plugins/src/vxe-table/use-vxe-grid.vue | 10 +- .../packages/effects/request/package.json | 2 +- .../request-client/modules/downloader.test.ts | 71 ++ .../src/request-client/modules/downloader.ts | 23 +- .../src/request-client/modules/sse.test.ts | 142 +++ .../request/src/request-client/modules/sse.ts | 137 +++ .../src/request-client/request-client.ts | 16 +- .../request/src/request-client/types.ts | 9 + apps/vben5/packages/icons/package.json | 2 +- .../vben5/packages/icons/src/iconify/index.ts | 8 - .../icons/src/svg/icons/antdv-next-logo.svg | 1 + .../packages/icons/src/svg/icons/dingding.svg | 1 + .../packages/icons/src/svg/icons/github.svg | 1 + .../packages/icons/src/svg/icons/google.svg | 1 + .../packages/icons/src/svg/icons/qqchat.svg | 1 + .../icons/src/svg/icons/tdesign-logo.svg | 39 + .../packages/icons/src/svg/icons/wechat.svg | 1 + apps/vben5/packages/icons/src/svg/index.ts | 14 + apps/vben5/packages/icons/src/svg/load.ts | 20 +- apps/vben5/packages/locales/package.json | 2 +- .../src/langs/en-US/authentication.json | 9 + .../locales/src/langs/en-US/preferences.json | 12 + .../locales/src/langs/en-US/profile.json | 4 + .../packages/locales/src/langs/en-US/ui.json | 21 +- .../src/langs/zh-CN/authentication.json | 9 + .../locales/src/langs/zh-CN/preferences.json | 12 + .../locales/src/langs/zh-CN/profile.json | 4 + .../packages/locales/src/langs/zh-CN/ui.json | 21 +- apps/vben5/packages/preferences/package.json | 2 +- apps/vben5/packages/stores/package.json | 2 +- apps/vben5/packages/stores/shim-pinia.d.ts | 2 +- .../packages/stores/src/modules/index.ts | 1 + .../packages/stores/src/modules/tabbar.ts | 125 ++- .../packages/stores/src/modules/timezone.ts | 132 +++ apps/vben5/packages/styles/package.json | 5 +- .../packages/styles/src/antdv-next/index.css | 77 ++ apps/vben5/packages/types/global.d.ts | 28 +- apps/vben5/packages/types/package.json | 2 +- apps/vben5/packages/utils/package.json | 2 +- .../utils/src/helpers/generate-menus.ts | 4 +- .../src/helpers/generate-routes-backend.ts | 26 +- apps/vben5/playground/.env.development | 4 + apps/vben5/playground/package.json | 3 +- .../playground/src/adapter/component/index.ts | 420 +++++++- .../vben5/playground/src/adapter/vxe-table.ts | 5 +- apps/vben5/playground/src/api/core/index.ts | 1 + .../vben5/playground/src/api/core/timezone.ts | 26 + apps/vben5/playground/src/api/request.ts | 14 +- apps/vben5/playground/src/bootstrap.ts | 4 + apps/vben5/playground/src/layouts/basic.vue | 59 +- .../src/locales/langs/en-US/demos.json | 4 +- .../src/locales/langs/en-US/examples.json | 9 + .../src/locales/langs/en-US/page.json | 3 +- .../src/locales/langs/zh-CN/demos.json | 4 +- .../src/locales/langs/zh-CN/examples.json | 9 + .../src/locales/langs/zh-CN/page.json | 3 +- .../src/locales/langs/zh-CN/system.json | 4 +- apps/vben5/playground/src/router/guard.ts | 4 +- .../src/router/routes/modules/dashboard.ts | 1 + .../src/router/routes/modules/demos.ts | 13 +- .../src/router/routes/modules/examples.ts | 36 + .../src/router/routes/modules/vben.ts | 41 +- apps/vben5/playground/src/store/auth.ts | 13 +- apps/vben5/playground/src/timezone-init.ts | 20 + .../src/views/_core/profile/base-setting.vue | 65 ++ .../src/views/_core/profile/index.vue | 49 + .../_core/profile/notification-setting.vue | 31 + .../views/_core/profile/password-setting.vue | 63 ++ .../views/_core/profile/security-setting.vue | 43 + .../analytics/analytics-visits-sales.vue | 2 +- .../src/views/demos/access/button-control.vue | 2 +- .../src/views/demos/access/index.vue | 2 +- .../src/views/demos/features/icons/index.vue | 18 +- .../demos/features/login-expired/index.vue | 2 +- .../src/views/demos/features/tabs/index.vue | 8 +- .../views/demos/features/watermark/index.vue | 4 +- .../captcha/point-selection-captcha.vue | 4 +- .../captcha/slider-translate-captcha.vue | 27 + .../src/views/examples/context-menu/index.vue | 59 ++ .../src/views/examples/count-to/index.vue | 2 +- .../src/views/examples/cropper/index.vue | 144 +++ .../examples/drawer/auto-height-demo.vue | 2 +- .../src/views/examples/form/basic.vue | 60 +- .../src/views/examples/form/merge.vue | 2 +- .../src/views/examples/form/query.vue | 129 +++ .../examples/form/scroll-to-error-test.vue | 180 ++++ .../src/views/examples/layout/col-page.vue | 22 +- .../src/views/examples/loading/index.vue | 4 +- .../views/examples/modal/auto-height-demo.vue | 2 +- .../src/views/examples/tippy/index.vue | 2 +- .../src/views/examples/vxe-table/basic.vue | 1 + .../views/examples/vxe-table/custom-cell.vue | 2 +- .../src/views/examples/vxe-table/remote.vue | 2 +- .../playground/src/views/system/dept/list.vue | 2 +- .../playground/src/views/system/menu/list.vue | 2 +- .../src/views/system/menu/modules/form.vue | 3 +- .../playground/src/views/system/role/list.vue | 4 +- .../src/views/system/role/modules/form.vue | 23 +- apps/vben5/pnpm-workspace.yaml | 270 ++--- apps/vben5/scripts/clean.mjs | 129 ++- apps/vben5/scripts/turbo-run/package.json | 2 +- apps/vben5/scripts/vsh/package.json | 2 +- apps/vben5/vben-admin.code-workspace | 116 ++- apps/vben5/vitest.config.ts | 10 +- apps/vben5/vitest.workspace.ts | 3 - 751 files changed, 18852 insertions(+), 2083 deletions(-) create mode 100644 apps/vben5/apps/app-antd/public/resource/img/logo.png create mode 100644 apps/vben5/apps/app-antd/src/timezone-init.ts create mode 100644 apps/vben5/apps/app-antd/src/views/ai-management/conversations/index.vue create mode 100644 apps/vben5/apps/app-antd/src/views/ai-management/workspaces/index.vue create mode 100644 apps/vben5/apps/app-antd/src/views/notifications/send-records/index.vue create mode 100644 apps/vben5/apps/app-antd/src/views/wechat/settings/index.vue create mode 100644 apps/vben5/apps/web-antdv-next/.env create mode 100644 apps/vben5/apps/web-antdv-next/.env.analyze create mode 100644 apps/vben5/apps/web-antdv-next/.env.development create mode 100644 apps/vben5/apps/web-antdv-next/.env.production create mode 100644 apps/vben5/apps/web-antdv-next/index.html create mode 100644 apps/vben5/apps/web-antdv-next/package.json create mode 100644 apps/vben5/apps/web-antdv-next/postcss.config.mjs create mode 100644 apps/vben5/apps/web-antdv-next/public/favicon.ico create mode 100644 apps/vben5/apps/web-antdv-next/src/adapter/component/index.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/adapter/form.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/adapter/vxe-table.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/api/core/auth.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/api/core/index.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/api/core/menu.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/api/core/user.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/api/index.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/api/request.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/app.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/bootstrap.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/layouts/auth.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/layouts/basic.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/layouts/index.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/locales/README.md create mode 100644 apps/vben5/apps/web-antdv-next/src/locales/index.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/locales/langs/en-US/demos.json create mode 100644 apps/vben5/apps/web-antdv-next/src/locales/langs/en-US/page.json create mode 100644 apps/vben5/apps/web-antdv-next/src/locales/langs/zh-CN/demos.json create mode 100644 apps/vben5/apps/web-antdv-next/src/locales/langs/zh-CN/page.json create mode 100644 apps/vben5/apps/web-antdv-next/src/main.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/preferences.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/router/access.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/router/guard.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/router/index.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/router/routes/core.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/router/routes/index.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/router/routes/modules/dashboard.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/router/routes/modules/demos.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/router/routes/modules/vben.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/store/auth.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/store/index.ts create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/README.md create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/about/index.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/authentication/code-login.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/authentication/forget-password.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/authentication/login.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/authentication/qrcode-login.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/authentication/register.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/fallback/coming-soon.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/fallback/forbidden.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/fallback/internal-error.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/fallback/not-found.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/fallback/offline.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/profile/base-setting.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/profile/index.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/profile/notification-setting.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/profile/password-setting.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/_core/profile/security-setting.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/dashboard/analytics/analytics-trends.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits-data.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits-sales.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits-source.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/dashboard/analytics/index.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/dashboard/workspace/index.vue create mode 100644 apps/vben5/apps/web-antdv-next/src/views/demos/antd/index.vue create mode 100644 apps/vben5/apps/web-antdv-next/tailwind.config.mjs create mode 100644 apps/vben5/apps/web-antdv-next/tsconfig.json create mode 100644 apps/vben5/apps/web-antdv-next/tsconfig.node.json create mode 100644 apps/vben5/apps/web-antdv-next/vite.config.mts create mode 100644 apps/vben5/apps/web-tdesign/.env create mode 100644 apps/vben5/apps/web-tdesign/.env.analyze create mode 100644 apps/vben5/apps/web-tdesign/.env.development create mode 100644 apps/vben5/apps/web-tdesign/.env.production create mode 100644 apps/vben5/apps/web-tdesign/index.html create mode 100644 apps/vben5/apps/web-tdesign/package.json create mode 100644 apps/vben5/apps/web-tdesign/postcss.config.mjs create mode 100644 apps/vben5/apps/web-tdesign/public/favicon.ico create mode 100644 apps/vben5/apps/web-tdesign/src/adapter/component/index.ts create mode 100644 apps/vben5/apps/web-tdesign/src/adapter/form.ts create mode 100644 apps/vben5/apps/web-tdesign/src/adapter/tdesign.ts create mode 100644 apps/vben5/apps/web-tdesign/src/adapter/vxe-table.ts create mode 100644 apps/vben5/apps/web-tdesign/src/api/core/auth.ts create mode 100644 apps/vben5/apps/web-tdesign/src/api/core/index.ts create mode 100644 apps/vben5/apps/web-tdesign/src/api/core/menu.ts create mode 100644 apps/vben5/apps/web-tdesign/src/api/core/user.ts create mode 100644 apps/vben5/apps/web-tdesign/src/api/index.ts create mode 100644 apps/vben5/apps/web-tdesign/src/api/request.ts create mode 100644 apps/vben5/apps/web-tdesign/src/app.vue create mode 100644 apps/vben5/apps/web-tdesign/src/bootstrap.ts create mode 100644 apps/vben5/apps/web-tdesign/src/layouts/auth.vue create mode 100644 apps/vben5/apps/web-tdesign/src/layouts/basic.vue create mode 100644 apps/vben5/apps/web-tdesign/src/layouts/index.ts create mode 100644 apps/vben5/apps/web-tdesign/src/locales/README.md create mode 100644 apps/vben5/apps/web-tdesign/src/locales/index.ts create mode 100644 apps/vben5/apps/web-tdesign/src/locales/langs/en-US/demos.json create mode 100644 apps/vben5/apps/web-tdesign/src/locales/langs/en-US/page.json create mode 100644 apps/vben5/apps/web-tdesign/src/locales/langs/zh-CN/demos.json create mode 100644 apps/vben5/apps/web-tdesign/src/locales/langs/zh-CN/page.json create mode 100644 apps/vben5/apps/web-tdesign/src/main.ts create mode 100644 apps/vben5/apps/web-tdesign/src/preferences.ts create mode 100644 apps/vben5/apps/web-tdesign/src/router/access.ts create mode 100644 apps/vben5/apps/web-tdesign/src/router/guard.ts create mode 100644 apps/vben5/apps/web-tdesign/src/router/index.ts create mode 100644 apps/vben5/apps/web-tdesign/src/router/routes/core.ts create mode 100644 apps/vben5/apps/web-tdesign/src/router/routes/index.ts create mode 100644 apps/vben5/apps/web-tdesign/src/router/routes/modules/dashboard.ts create mode 100644 apps/vben5/apps/web-tdesign/src/router/routes/modules/demos.ts create mode 100644 apps/vben5/apps/web-tdesign/src/router/routes/modules/vben.ts create mode 100644 apps/vben5/apps/web-tdesign/src/store/auth.ts create mode 100644 apps/vben5/apps/web-tdesign/src/store/index.ts create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/README.md create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/about/index.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/authentication/code-login.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/authentication/forget-password.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/authentication/login.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/authentication/qrcode-login.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/authentication/register.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/fallback/coming-soon.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/fallback/forbidden.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/fallback/internal-error.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/fallback/not-found.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/fallback/offline.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/profile/base-setting.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/profile/index.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/profile/notification-setting.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/profile/password-setting.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/_core/profile/security-setting.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/dashboard/analytics/analytics-trends.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/dashboard/analytics/analytics-visits-data.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/dashboard/analytics/analytics-visits-sales.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/dashboard/analytics/analytics-visits-source.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/dashboard/analytics/analytics-visits.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/dashboard/analytics/index.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/dashboard/workspace/index.vue create mode 100644 apps/vben5/apps/web-tdesign/src/views/demos/tdesign/index.vue create mode 100644 apps/vben5/apps/web-tdesign/tailwind.config.mjs create mode 100644 apps/vben5/apps/web-tdesign/tsconfig.json create mode 100644 apps/vben5/apps/web-tdesign/tsconfig.node.json create mode 100644 apps/vben5/apps/web-tdesign/vite.config.mts create mode 100644 apps/vben5/docs/src/demos/vben-modal/animation-type/index.vue create mode 100644 apps/vben5/internal/lint-configs/eslint-config/src/configs/pnpm.ts create mode 100644 apps/vben5/internal/lint-configs/eslint-config/src/configs/yaml.ts create mode 100644 apps/vben5/packages/@abp/account/src/components/components/UserSettings.vue create mode 100644 apps/vben5/packages/@abp/ai-management/package.json create mode 100644 apps/vben5/packages/@abp/ai-management/src/api/index.ts create mode 100644 apps/vben5/packages/@abp/ai-management/src/api/useChatsApi.ts create mode 100644 apps/vben5/packages/@abp/ai-management/src/api/useConversationsApi.ts create mode 100644 apps/vben5/packages/@abp/ai-management/src/api/useWorkspaceDefinitionsApi.ts create mode 100644 apps/vben5/packages/@abp/ai-management/src/components/conversations/index.vue create mode 100644 apps/vben5/packages/@abp/ai-management/src/components/index.ts create mode 100644 apps/vben5/packages/@abp/ai-management/src/components/workspaces/WorkspaceDefinitionModal.vue create mode 100644 apps/vben5/packages/@abp/ai-management/src/components/workspaces/WorkspaceDefinitionTable.vue create mode 100644 apps/vben5/packages/@abp/ai-management/src/constants/permissions.ts create mode 100644 apps/vben5/packages/@abp/ai-management/src/index.ts create mode 100644 apps/vben5/packages/@abp/ai-management/src/types/chats.ts create mode 100644 apps/vben5/packages/@abp/ai-management/src/types/conversations.ts create mode 100644 apps/vben5/packages/@abp/ai-management/src/types/index.ts create mode 100644 apps/vben5/packages/@abp/ai-management/src/types/workspaces.ts create mode 100644 apps/vben5/packages/@abp/ai-management/tsconfig.json create mode 100644 apps/vben5/packages/@abp/notifications/src/api/useNotificationSendRecordsApi.ts create mode 100644 apps/vben5/packages/@abp/notifications/src/components/send-records/NotificationSendRecordTable.vue create mode 100644 apps/vben5/packages/@abp/notifications/src/types/send-records.ts create mode 100644 apps/vben5/packages/@abp/request/src/hooks/useSseRequest.ts create mode 100644 apps/vben5/packages/@abp/settings/src/api/useTimeZoneSettingsApi.ts create mode 100644 apps/vben5/packages/@abp/ui/src/hooks/index.ts create mode 100644 apps/vben5/packages/@abp/ui/src/hooks/useMessage.ts create mode 100644 apps/vben5/packages/@core/base/shared/src/utils/__tests__/date.test.ts create mode 100644 apps/vben5/packages/@core/base/shared/src/utils/__tests__/resources.test.ts create mode 100644 apps/vben5/packages/@core/base/shared/src/utils/__tests__/stack.test.ts create mode 100644 apps/vben5/packages/@core/base/shared/src/utils/resources.ts create mode 100644 apps/vben5/packages/@core/base/shared/src/utils/stack.ts create mode 100644 apps/vben5/packages/@core/ui-kit/layout-ui/src/hooks/use-sidebar-drag.ts create mode 100644 apps/vben5/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-refresh.vue create mode 100644 apps/vben5/packages/effects/common-ui/src/components/captcha/slider-translate-captcha/index.vue create mode 100644 apps/vben5/packages/effects/common-ui/src/components/cropper/cropper.vue create mode 100644 apps/vben5/packages/effects/common-ui/src/components/cropper/index.ts create mode 100644 apps/vben5/packages/effects/common-ui/src/components/tree/index.ts create mode 100644 apps/vben5/packages/effects/common-ui/src/components/tree/tree.vue create mode 100644 apps/vben5/packages/effects/common-ui/src/ui/authentication/dingding-login.vue create mode 100644 apps/vben5/packages/effects/common-ui/src/ui/profile/base-setting.vue create mode 100644 apps/vben5/packages/effects/common-ui/src/ui/profile/index.ts create mode 100644 apps/vben5/packages/effects/common-ui/src/ui/profile/notification-setting.vue create mode 100644 apps/vben5/packages/effects/common-ui/src/ui/profile/password-setting.vue create mode 100644 apps/vben5/packages/effects/common-ui/src/ui/profile/profile.vue create mode 100644 apps/vben5/packages/effects/common-ui/src/ui/profile/security-setting.vue create mode 100644 apps/vben5/packages/effects/common-ui/src/ui/profile/types.ts create mode 100644 apps/vben5/packages/effects/layouts/src/hooks/index.ts create mode 100644 apps/vben5/packages/effects/layouts/src/route-cached/index.ts create mode 100644 apps/vben5/packages/effects/layouts/src/route-cached/route-cached-page.vue create mode 100644 apps/vben5/packages/effects/layouts/src/route-cached/route-cached-view.vue create mode 100644 apps/vben5/packages/effects/layouts/src/widgets/preferences/blocks/theme/font-size.vue create mode 100644 apps/vben5/packages/effects/layouts/src/widgets/timezone/index.ts create mode 100644 apps/vben5/packages/effects/layouts/src/widgets/timezone/timezone-button.vue create mode 100644 apps/vben5/packages/effects/request/src/request-client/modules/sse.test.ts create mode 100644 apps/vben5/packages/effects/request/src/request-client/modules/sse.ts create mode 100644 apps/vben5/packages/icons/src/svg/icons/antdv-next-logo.svg create mode 100644 apps/vben5/packages/icons/src/svg/icons/dingding.svg create mode 100644 apps/vben5/packages/icons/src/svg/icons/github.svg create mode 100644 apps/vben5/packages/icons/src/svg/icons/google.svg create mode 100644 apps/vben5/packages/icons/src/svg/icons/qqchat.svg create mode 100644 apps/vben5/packages/icons/src/svg/icons/tdesign-logo.svg create mode 100644 apps/vben5/packages/icons/src/svg/icons/wechat.svg create mode 100644 apps/vben5/packages/locales/src/langs/en-US/profile.json create mode 100644 apps/vben5/packages/locales/src/langs/zh-CN/profile.json create mode 100644 apps/vben5/packages/stores/src/modules/timezone.ts create mode 100644 apps/vben5/packages/styles/src/antdv-next/index.css create mode 100644 apps/vben5/playground/src/api/core/timezone.ts create mode 100644 apps/vben5/playground/src/timezone-init.ts create mode 100644 apps/vben5/playground/src/views/_core/profile/base-setting.vue create mode 100644 apps/vben5/playground/src/views/_core/profile/index.vue create mode 100644 apps/vben5/playground/src/views/_core/profile/notification-setting.vue create mode 100644 apps/vben5/playground/src/views/_core/profile/password-setting.vue create mode 100644 apps/vben5/playground/src/views/_core/profile/security-setting.vue create mode 100644 apps/vben5/playground/src/views/examples/captcha/slider-translate-captcha.vue create mode 100644 apps/vben5/playground/src/views/examples/context-menu/index.vue create mode 100644 apps/vben5/playground/src/views/examples/cropper/index.vue create mode 100644 apps/vben5/playground/src/views/examples/form/scroll-to-error-test.vue delete mode 100644 apps/vben5/vitest.workspace.ts diff --git a/apps/vben5/.gitignore b/apps/vben5/.gitignore index c2a8a771f..8cb16e7e0 100644 --- a/apps/vben5/.gitignore +++ b/apps/vben5/.gitignore @@ -19,6 +19,7 @@ coverage dev-dist .stylelintcache yarn.lock +pnpm-lock.yaml package-lock.json .VSCodeCounter **/backend-mock/data @@ -49,3 +50,4 @@ vite.config.ts.* *.sln *.sw? .history +.cursor diff --git a/apps/vben5/.node-version b/apps/vben5/.node-version index ee5c24469..85e502778 100644 --- a/apps/vben5/.node-version +++ b/apps/vben5/.node-version @@ -1 +1 @@ -22.1.0 +22.22.0 diff --git a/apps/vben5/.npmrc b/apps/vben5/.npmrc index 21147aff2..aeac1ae91 100644 --- a/apps/vben5/.npmrc +++ b/apps/vben5/.npmrc @@ -1,4 +1,4 @@ -registry = "https://registry.npmmirror.com" +registry=https://registry.npmmirror.com public-hoist-pattern[]=lefthook public-hoist-pattern[]=eslint public-hoist-pattern[]=prettier diff --git a/apps/vben5/.prettierignore b/apps/vben5/.prettierignore index d0b0ca133..7c572fd66 100644 --- a/apps/vben5/.prettierignore +++ b/apps/vben5/.prettierignore @@ -16,3 +16,5 @@ CODEOWNERS public .npmrc *-lock.yaml + +packages/@abp/components diff --git a/apps/vben5/.stylelintignore b/apps/vben5/.stylelintignore index f4b2db2c1..339bcf6c2 100644 --- a/apps/vben5/.stylelintignore +++ b/apps/vben5/.stylelintignore @@ -2,3 +2,4 @@ dist public __tests__ coverage +packages/@abp/components diff --git a/apps/vben5/.vscode/settings.json b/apps/vben5/.vscode/settings.json index f38c42781..8da37dc96 100644 --- a/apps/vben5/.vscode/settings.json +++ b/apps/vben5/.vscode/settings.json @@ -180,7 +180,8 @@ "markdown", "json", "jsonc", - "json5" + "json5", + "yaml" ], "tailwindCSS.experimental.classRegex": [ @@ -226,16 +227,5 @@ "commentTranslate.multiLineMerge": true, "vue.server.hybridMode": true, "typescript.tsdk": "node_modules/typescript/lib", - "oxc.enable": false, - "cSpell.words": [ - "archiver", - "axios", - "dotenv", - "isequal", - "jspm", - "napi", - "nolebase", - "rollup", - "vitest" - ] + "oxc.enable": false } diff --git a/apps/vben5/README.ja-JP.md b/apps/vben5/README.ja-JP.md index f7847a1d9..4ce285a74 100644 --- a/apps/vben5/README.ja-JP.md +++ b/apps/vben5/README.ja-JP.md @@ -140,8 +140,12 @@ pnpm build ## 貢献者 + + Contribution Leaderboard + + - Contributors + Contributors ## Discord diff --git a/apps/vben5/README.md b/apps/vben5/README.md index e027949ab..ce8e89758 100644 --- a/apps/vben5/README.md +++ b/apps/vben5/README.md @@ -10,7 +10,7 @@

Vue Vben Admin

-[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vbenjs_vue-vben-admin&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin) ![codeql](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml/badge.svg) ![build](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml/badge.svg) ![ci](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml/badge.svg) ![deploy](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml/badge.svg) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vbenjs_vue-vben-admin&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin) [![codeql](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml/badge.svg)](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml) [![build](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml/badge.svg)](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml) [![ci](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml/badge.svg)](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml) [![deploy](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml/badge.svg)](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml) **English** | [中文](./README.zh-CN.md) | [日本語](./README.ja-JP.md) @@ -140,8 +140,12 @@ If you think this project is helpful to you, you can help the author buy a cup o ## Contributors + + Contribution Leaderboard + + - Contributors + Contributors ## Discord diff --git a/apps/vben5/README.zh-CN.md b/apps/vben5/README.zh-CN.md index 5a6b191b8..d3193ef65 100644 --- a/apps/vben5/README.zh-CN.md +++ b/apps/vben5/README.zh-CN.md @@ -140,8 +140,12 @@ pnpm build ## 贡献者 + + Contribution Leaderboard + + - Contributors + Contributors ## Discord diff --git a/apps/vben5/apps/app-antd/.env.development b/apps/vben5/apps/app-antd/.env.development index b9e43314c..b638cd47c 100644 --- a/apps/vben5/apps/app-antd/.env.development +++ b/apps/vben5/apps/app-antd/.env.development @@ -16,12 +16,12 @@ VITE_DEVTOOLS=false VITE_INJECT_APP_LOADING=true # 是否仅允许OIDC登录 -VITE_GLOB_ONLY_OIDC=false +VITE_GLOB_AUTH_ONLY_OIDC=false # 认证服务器 -VITE_GLOB_AUTHORITY="http://localhost:30000" +VITE_GLOB_AUTH_AUTHORITY="http://localhost:44385" # 授权范围 -VITE_GLOB_AUDIENCE="openid email address phone profile offline_access lingyun-abp-application" +VITE_GLOB_AUTH_AUDIENCE="openid email address phone profile offline_access lingyun-abp-application" # 客户端Id -VITE_GLOB_CLIENT_ID=vue-admin-client +VITE_GLOB_AUTH_CLIENT_ID=vue-admin-client # 客户端密钥【生产环境请勿设置此值,建议启用仅允许OIDC登录,将使用授权码类型登录】 -VITE_GLOB_CLIENT_SECRET=1q2w3e* +VITE_GLOB_AUTH_CLIENT_SECRET=1q2w3e* diff --git a/apps/vben5/apps/app-antd/.env.production b/apps/vben5/apps/app-antd/.env.production index 40c957808..169103a71 100644 --- a/apps/vben5/apps/app-antd/.env.production +++ b/apps/vben5/apps/app-antd/.env.production @@ -20,13 +20,13 @@ VITE_INJECT_APP_LOADING=true VITE_ARCHIVER=true # 是否仅允许OIDC登录 -VITE_GLOB_ONLY_OIDC=false +VITE_GLOB_AUTH_ONLY_OIDC=false # 认证服务器 -VITE_GLOB_AUTHORITY="http://127.0.0.1:30001" +VITE_GLOB_AUTH_AUTHORITY="http://127.0.0.1:30001" # 授权范围 -VITE_GLOB_AUDIENCE="openid email address phone profile offline_access lingyun-abp-application" +VITE_GLOB_AUTH_AUDIENCE="openid email address phone profile offline_access lingyun-abp-application" # 客户端Id -VITE_GLOB_CLIENT_ID=vue-oauth-client +VITE_GLOB_AUTH_CLIENT_ID=vue-oauth-client diff --git a/apps/vben5/apps/app-antd/index.html b/apps/vben5/apps/app-antd/index.html index 480eb84de..33d34a9e2 100644 --- a/apps/vben5/apps/app-antd/index.html +++ b/apps/vben5/apps/app-antd/index.html @@ -14,19 +14,6 @@ <%= VITE_APP_TITLE %> -
diff --git a/apps/vben5/apps/app-antd/package.json b/apps/vben5/apps/app-antd/package.json index 6b1852596..3fb801bb5 100644 --- a/apps/vben5/apps/app-antd/package.json +++ b/apps/vben5/apps/app-antd/package.json @@ -1,6 +1,6 @@ { "name": "@abp/app-antd", - "version": "9.2.0", + "version": "10.0.2", "homepage": "https://github.com/colinin/abp-next-admin", "bugs": "https://github.com/colinin/abp-next-admin/issues", "repository": { @@ -27,6 +27,7 @@ }, "dependencies": { "@abp/account": "workspace:*", + "@abp/ai-management": "workspace:*", "@abp/auditing": "workspace:*", "@abp/components": "workspace:*", "@abp/core": "workspace:*", diff --git a/apps/vben5/apps/app-antd/public/resource/img/logo.png b/apps/vben5/apps/app-antd/public/resource/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f2dbd8ae5eb67053a2e00131708df0981223a6e9 GIT binary patch literal 120444 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 QVgLXD07*qoM6N<$f@sw`CIA2c literal 0 HcmV?d00001 diff --git a/apps/vben5/apps/app-antd/src/adapter/component/index.ts b/apps/vben5/apps/app-antd/src/adapter/component/index.ts index e98ba7f61..4ac7e46f3 100644 --- a/apps/vben5/apps/app-antd/src/adapter/component/index.ts +++ b/apps/vben5/apps/app-antd/src/adapter/component/index.ts @@ -3,26 +3,51 @@ * 可用于 vben-form、vben-modal、vben-drawer 等组件使用, */ -import type { Component } from 'vue'; +/* eslint-disable vue/one-component-per-file */ + +import type { + UploadChangeParam, + UploadFile, + UploadProps, +} from 'ant-design-vue'; + +import type { Component, Ref } from 'vue'; import type { BaseFormComponentType } from '@vben/common-ui'; import type { Recordable } from '@vben/types'; import { + computed, defineAsyncComponent, defineComponent, - getCurrentInstance, h, ref, + render, + unref, + watch, } from 'vue'; -import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui'; +import { + ApiComponent, + globalShareState, + IconPicker, + VCropper, +} from '@vben/common-ui'; +import { IconifyIcon } from '@vben/icons'; import { $t } from '@vben/locales'; +import { isEmpty } from '@vben/utils'; import { FeatureStateCheck, GlobalFeatureStateCheck } from '@abp/features'; import { PermissionStateCheck } from '@abp/permissions'; import { TenantSelect } from '@abp/saas'; -import { notification } from 'ant-design-vue'; +import { message, Modal, notification } from 'ant-design-vue'; + +const ColorPicker = defineAsyncComponent(() => + import('vue3-colorpicker').then((res) => { + import('vue3-colorpicker/style.css'); + return res.ColorPicker; + }), +); const AutoComplete = defineAsyncComponent( () => import('ant-design-vue/es/auto-complete'), @@ -31,12 +56,6 @@ const Button = defineAsyncComponent(() => import('ant-design-vue/es/button')); const Checkbox = defineAsyncComponent( () => import('ant-design-vue/es/checkbox'), ); -const ColorPicker = defineAsyncComponent(() => - import('vue3-colorpicker').then((res) => { - import('vue3-colorpicker/style.css'); - return res.ColorPicker; - }), -); const CheckboxGroup = defineAsyncComponent(() => import('ant-design-vue/es/checkbox').then((res) => res.CheckboxGroup), ); @@ -49,12 +68,12 @@ const Input = defineAsyncComponent(() => import('ant-design-vue/es/input')); const InputNumber = defineAsyncComponent( () => import('ant-design-vue/es/input-number'), ); -const InputSearch = defineAsyncComponent(() => - import('ant-design-vue/es/input').then((res) => res.InputSearch), -); const InputPassword = defineAsyncComponent(() => import('ant-design-vue/es/input').then((res) => res.InputPassword), ); +const InputSearch = defineAsyncComponent(() => + import('ant-design-vue/es/input').then((res) => res.InputSearch), +); const Mentions = defineAsyncComponent( () => import('ant-design-vue/es/mentions'), ); @@ -79,7 +98,14 @@ const Tree = defineAsyncComponent(() => import('ant-design-vue/es/tree')); const TreeSelect = defineAsyncComponent( () => import('ant-design-vue/es/tree-select'), ); +const Cascader = defineAsyncComponent( + () => import('ant-design-vue/es/cascader'), +); const Upload = defineAsyncComponent(() => import('ant-design-vue/es/upload')); +const Image = defineAsyncComponent(() => import('ant-design-vue/es/image')); +const PreviewGroup = defineAsyncComponent(() => + import('ant-design-vue/es/image').then((res) => res.ImagePreviewGroup), +); const withDefaultPlaceholder = ( component: T, @@ -96,16 +122,15 @@ const withDefaultPlaceholder = ( $t(`ui.placeholder.${type}`); // 透传组件暴露的方法 const innerRef = ref(); - const publicApi: Recordable = {}; - expose(publicApi); - const instance = getCurrentInstance(); - instance?.proxy?.$nextTick(() => { - for (const key in innerRef.value) { - if (typeof innerRef.value[key] === 'function') { - publicApi[key] = innerRef.value[key]; - } - } - }); + expose( + new Proxy( + {}, + { + get: (_target, key) => innerRef.value?.[key], + has: (_target, key) => key in (innerRef.value || {}), + }, + ), + ); return () => h( component, @@ -116,11 +141,369 @@ const withDefaultPlaceholder = ( }); }; +const withPreviewUpload = () => { + // 检查是否为图片文件的辅助函数 + const isImageFile = (file: UploadFile): boolean => { + const imageExtensions = new Set([ + 'bmp', + 'gif', + 'jpeg', + 'jpg', + 'png', + 'svg', + 'webp', + ]); + if (file.url) { + try { + const pathname = new URL(file.url, 'http://localhost').pathname; + const ext = pathname.split('.').pop()?.toLowerCase(); + return ext ? imageExtensions.has(ext) : false; + } catch { + const ext = file.url?.split('.').pop()?.toLowerCase(); + return ext ? imageExtensions.has(ext) : false; + } + } + if (!file.type) { + const ext = file.name?.split('.').pop()?.toLowerCase(); + return ext ? imageExtensions.has(ext) : false; + } + return file.type.startsWith('image/'); + }; + // 创建默认的上传按钮插槽 + const createDefaultSlotsWithUpload = ( + listType: string, + placeholder: string, + ) => { + switch (listType) { + case 'picture-card': { + return { + default: () => placeholder, + }; + } + default: { + return { + default: () => + h( + Button, + { + icon: h(IconifyIcon, { + icon: 'ant-design:upload-outlined', + class: 'mb-1 size-4', + }), + }, + () => placeholder, + ), + }; + } + } + }; + // 构建预览图片组 + const previewImage = async ( + file: UploadFile, + visible: Ref, + fileList: Ref, + ) => { + // 如果当前文件不是图片,直接打开 + if (!isImageFile(file)) { + if (file.url) { + window.open(file.url, '_blank'); + } else if (file.preview) { + window.open(file.preview, '_blank'); + } else { + message.error($t('ui.formRules.previewWarning')); + } + return; + } + + // 对于图片文件,继续使用预览组 + const [ImageComponent, PreviewGroupComponent] = await Promise.all([ + Image, + PreviewGroup, + ]); + + const getBase64 = (file: File) => { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.addEventListener('load', () => resolve(reader.result)); + reader.addEventListener('error', (error) => reject(error)); + }); + }; + // 从fileList中过滤出所有图片文件 + const imageFiles = (unref(fileList) || []).filter((element) => + isImageFile(element), + ); + + // 为所有没有预览地址的图片生成预览 + for (const imgFile of imageFiles) { + if (!imgFile.url && !imgFile.preview && imgFile.originFileObj) { + imgFile.preview = (await getBase64(imgFile.originFileObj)) as string; + } + } + const container: HTMLElement | null = document.createElement('div'); + document.body.append(container); + + // 用于追踪组件是否已卸载 + let isUnmounted = false; + + const PreviewWrapper = { + setup() { + return () => { + if (isUnmounted) return null; + return h( + PreviewGroupComponent, + { + class: 'hidden', + preview: { + visible: visible.value, + // 设置初始显示的图片索引 + current: imageFiles.findIndex((f) => f.uid === file.uid), + onVisibleChange: (value: boolean) => { + visible.value = value; + if (!value) { + // 延迟清理,确保动画完成 + setTimeout(() => { + if (!isUnmounted && container) { + isUnmounted = true; + render(null, container); + container.remove(); + } + }, 300); + } + }, + }, + }, + () => + // 渲染所有图片文件 + imageFiles.map((imgFile) => + h(ImageComponent, { + key: imgFile.uid, + src: imgFile.url || imgFile.preview, + }), + ), + ); + }; + }, + }; + + render(h(PreviewWrapper), container); + }; + + // 图片裁剪操作 + const cropImage = (file: File, aspectRatio: string | undefined) => { + return new Promise((resolve, reject) => { + const container: HTMLElement | null = document.createElement('div'); + document.body.append(container); + + // 用于追踪组件是否已卸载 + let isUnmounted = false; + let objectUrl: null | string = null; + + const open = ref(true); + const cropperRef = ref | null>(null); + + const closeModal = () => { + open.value = false; + // 延迟清理,确保动画完成 + setTimeout(() => { + if (!isUnmounted && container) { + if (objectUrl) { + URL.revokeObjectURL(objectUrl); + } + isUnmounted = true; + render(null, container); + container.remove(); + } + }, 300); + }; + + const CropperWrapper = { + setup() { + return () => { + if (isUnmounted) return null; + if (!objectUrl) { + objectUrl = URL.createObjectURL(file); + } + return h( + Modal, + { + open: open.value, + title: h('div', {}, [ + $t('ui.crop.title'), + h( + 'span', + { + class: `${aspectRatio ? '' : 'hidden'} ml-2 text-sm text-gray-400 font-normal`, + }, + $t('ui.crop.titleTip', [aspectRatio]), + ), + ]), + centered: true, + width: 548, + keyboard: false, + maskClosable: false, + closable: false, + cancelText: $t('common.cancel'), + okText: $t('ui.crop.confirm'), + destroyOnClose: true, + onOk: async () => { + const cropper = cropperRef.value; + if (!cropper) { + reject(new Error('Cropper not found')); + closeModal(); + return; + } + try { + const dataUrl = await cropper.getCropImage(); + resolve(dataUrl); + } catch { + reject(new Error($t('ui.crop.errorTip'))); + } finally { + closeModal(); + } + }, + onCancel() { + resolve(''); + closeModal(); + }, + }, + () => + h(VCropper, { + ref: (ref: any) => (cropperRef.value = ref), + img: objectUrl as string, + aspectRatio, + }), + ); + }; + }, + }; + + render(h(CropperWrapper), container); + }); + }; + + return defineComponent({ + name: Upload.name, + emits: ['update:modelValue'], + setup: ( + props: any, + { attrs, slots, emit }: { attrs: any; emit: any; slots: any }, + ) => { + const previewVisible = ref(false); + + const placeholder = attrs?.placeholder || $t(`ui.placeholder.upload`); + + const listType = attrs?.listType || attrs?.['list-type'] || 'text'; + + const fileList = ref( + attrs?.fileList || attrs?.['file-list'] || [], + ); + + const maxSize = computed(() => attrs?.maxSize ?? attrs?.['max-size']); + const aspectRatio = computed( + () => attrs?.aspectRatio ?? attrs?.['aspect-ratio'], + ); + + const handleBeforeUpload = async ( + file: UploadFile, + originFileList: Array, + ) => { + if (maxSize.value && (file.size || 0) / 1024 / 1024 > maxSize.value) { + message.error($t('ui.formRules.sizeLimit', [maxSize.value])); + file.status = 'removed'; + return false; + } + // 多选或者非图片不唤起裁剪框 + if ( + attrs.crop && + !attrs.multiple && + originFileList[0] && + isImageFile(file) + ) { + file.status = 'removed'; + // antd Upload组件问题 file参数获取的是UploadFile类型对象无法取到File类型 所以通过originFileList[0]获取 + const blob = await cropImage(originFileList[0], aspectRatio.value); + return new Promise((resolve, reject) => { + if (!blob) { + return reject(new Error($t('ui.crop.errorTip'))); + } + resolve(blob); + }); + } + + return attrs.beforeUpload?.(file) ?? true; + }; + + const handleChange = (event: UploadChangeParam) => { + try { + // 行内写法 handleChange: (event) => {} + attrs.handleChange?.(event); + // template写法 @handle-change="(event) => {}" + attrs.onHandleChange?.(event); + } catch (error) { + // Avoid breaking internal v-model sync on user handler errors + console.error(error); + } + fileList.value = event.fileList.filter( + (file) => file.status !== 'removed', + ); + emit( + 'update:modelValue', + event.fileList?.length ? fileList.value : undefined, + ); + }; + + const handlePreview = async (file: UploadFile) => { + previewVisible.value = true; + await previewImage(file, previewVisible, fileList); + }; + + const renderUploadButton = (): any => { + const isDisabled = attrs.disabled; + + // 如果禁用,不渲染上传按钮 + if (isDisabled) { + return null; + } + + // 否则渲染默认上传按钮 + return isEmpty(slots) + ? createDefaultSlotsWithUpload(listType, placeholder) + : slots; + }; + + // 可以监听到表单API设置的值 + watch( + () => attrs.modelValue, + (res) => { + fileList.value = res; + }, + ); + + return () => + h( + Upload, + { + ...props, + ...attrs, + fileList: fileList.value, + beforeUpload: handleBeforeUpload, + onChange: handleChange, + onPreview: handlePreview, + }, + renderUploadButton(), + ); + }, + }); +}; + // 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 export type ComponentType = + | 'ApiCascader' | 'ApiSelect' | 'ApiTreeSelect' | 'AutoComplete' + | 'Cascader' | 'Checkbox' | 'CheckboxGroup' | 'ColorPicker' @@ -158,35 +541,30 @@ async function initComponentAdapter() { // 如果你的组件体积比较大,可以使用异步加载 // Button: () => // import('xxx').then((res) => res.Button), - ApiSelect: withDefaultPlaceholder( - { - ...ApiComponent, - name: 'ApiSelect', - }, - 'select', - { - component: Select, - loadingSlot: 'suffixIcon', - visibleEvent: 'onDropdownVisibleChange', - modelPropName: 'value', - }, - ), - ApiTreeSelect: withDefaultPlaceholder( - { - ...ApiComponent, - name: 'ApiTreeSelect', - }, - 'select', - { - component: TreeSelect, - fieldNames: { label: 'label', value: 'value', children: 'children' }, - loadingSlot: 'suffixIcon', - modelPropName: 'value', - optionsPropName: 'treeData', - visibleEvent: 'onVisibleChange', - }, - ), + + ApiCascader: withDefaultPlaceholder(ApiComponent, 'select', { + component: Cascader, + fieldNames: { label: 'label', value: 'value', children: 'children' }, + loadingSlot: 'suffixIcon', + modelPropName: 'value', + visibleEvent: 'onVisibleChange', + }), + ApiSelect: withDefaultPlaceholder(ApiComponent, 'select', { + component: Select, + loadingSlot: 'suffixIcon', + modelPropName: 'value', + visibleEvent: 'onVisibleChange', + }), + ApiTreeSelect: withDefaultPlaceholder(ApiComponent, 'select', { + component: TreeSelect, + fieldNames: { label: 'label', value: 'value', children: 'children' }, + loadingSlot: 'suffixIcon', + modelPropName: 'value', + optionsPropName: 'treeData', + visibleEvent: 'onVisibleChange', + }), AutoComplete, + Cascader, Checkbox, CheckboxGroup, ColorPicker, @@ -222,7 +600,7 @@ async function initComponentAdapter() { TimePicker, Tree, TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'), - Upload, + Upload: withPreviewUpload(), FeatureStateCheck, GlobalFeatureStateCheck, PermissionStateCheck, diff --git a/apps/vben5/apps/app-antd/src/adapter/request/index.ts b/apps/vben5/apps/app-antd/src/adapter/request/index.ts index 1fe3242a1..072e67d12 100644 --- a/apps/vben5/apps/app-antd/src/adapter/request/index.ts +++ b/apps/vben5/apps/app-antd/src/adapter/request/index.ts @@ -3,7 +3,7 @@ import { authenticateResponseInterceptor, errorMessageResponseInterceptor, } from '@vben/request'; -import { useAccessStore } from '@vben/stores'; +import { useAccessStore, useTimezoneStore } from '@vben/stores'; import { useOAuthError } from '@abp/account'; import { useAbpStore } from '@abp/core'; @@ -53,17 +53,24 @@ export function initRequestClient() { fulfilled: async (config) => { const abpStore = useAbpStore(); const accessStore = useAccessStore(); + const timezoneStore = useTimezoneStore(); + if (accessStore.accessToken) { config.headers.Authorization = `${accessStore.accessToken}`; } config.headers['Accept-Language'] = preferences.app.locale; config.headers['X-Request-From'] = 'vben'; if (abpStore.tenantId) { + // see: https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/TenantResolverConsts.cs config.headers.__tenant = abpStore.tenantId; } if (abpStore.xsrfToken) { config.headers.RequestVerificationToken = abpStore.xsrfToken; } + if (timezoneStore.timezone) { + // see: https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimeZoneConsts.cs + config.headers.__timezone = timezoneStore.timezone; + } return config; }, }); diff --git a/apps/vben5/apps/app-antd/src/bootstrap.ts b/apps/vben5/apps/app-antd/src/bootstrap.ts index d34a6f46b..f75585871 100644 --- a/apps/vben5/apps/app-antd/src/bootstrap.ts +++ b/apps/vben5/apps/app-antd/src/bootstrap.ts @@ -16,6 +16,7 @@ import { initSetupVbenForm } from './adapter/form'; import { initRequestClient } from './adapter/request'; import App from './app.vue'; import { router } from './router'; +import { initTimezone } from './timezone-init'; async function bootstrap(namespace: string) { // 初始化组件适配器 @@ -50,6 +51,9 @@ async function bootstrap(namespace: string) { // 国际化 i18n 配置 await setupI18n(app); + // 初始化时区HANDLER + initTimezone(); + // 安装权限指令 registerAccessDirective(app); diff --git a/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json b/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json index 84408903b..bf17c63ce 100644 --- a/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json +++ b/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json @@ -52,7 +52,8 @@ "title": "Notifications", "myNotifilers": "My Notifilers", "groups": "Groups", - "definitions": "Definitions" + "definitions": "Definitions", + "sendRecords": "Send Records" }, "localization": { "title": "Localization", @@ -98,7 +99,8 @@ "authenticatorSettings": "Authenticator Settings", "changeAvatar": "Change Avatar", "sessionSettings": "Session Settings", - "personalDataSettings": "Personal Data Settings" + "personalDataSettings": "Personal Data Settings", + "systemSettings": "System Settings" }, "profile": "My Profile" }, @@ -147,5 +149,10 @@ "wechat": { "title": "WeChat", "settings": "Settings" + }, + "ai": { + "title": "Artificial Intelligence", + "workspaces": "Workspaces", + "conversations": "Conversations" } } diff --git a/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json b/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json index bdf07a393..3e5fe5888 100644 --- a/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json +++ b/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json @@ -52,7 +52,8 @@ "title": "通知管理", "myNotifilers": "我的通知", "groups": "通知分组", - "definitions": "通知定义" + "definitions": "通知定义", + "sendRecords": "发送记录" }, "localization": { "title": "本地化管理", @@ -98,7 +99,8 @@ "authenticatorSettings": "身份验证程序", "changeAvatar": "更改头像", "sessionSettings": "会话管理", - "personalDataSettings": "个人信息管理" + "personalDataSettings": "个人信息管理", + "systemSettings": "系统设置" }, "profile": "个人中心" }, @@ -147,5 +149,10 @@ "wechat": { "title": "微信集成", "settings": "微信设置" + }, + "ai": { + "title": "人工智能", + "workspaces": "工作区管理", + "conversations": "会话管理" } } diff --git a/apps/vben5/apps/app-antd/src/preferences.ts b/apps/vben5/apps/app-antd/src/preferences.ts index 94d2a3ca8..1591bd1d8 100644 --- a/apps/vben5/apps/app-antd/src/preferences.ts +++ b/apps/vben5/apps/app-antd/src/preferences.ts @@ -13,6 +13,9 @@ export const overridesPreferences = defineOverridesPreferences({ enableRefreshToken: true, name: import.meta.env.VITE_APP_TITLE, }, + logo: { + source: '/resource/img/logo.png', + }, theme: { mode: 'auto', radius: '0.25', diff --git a/apps/vben5/apps/app-antd/src/timezone-init.ts b/apps/vben5/apps/app-antd/src/timezone-init.ts new file mode 100644 index 000000000..704ec022b --- /dev/null +++ b/apps/vben5/apps/app-antd/src/timezone-init.ts @@ -0,0 +1,28 @@ +import { setTimezoneHandler } from '@vben/stores'; + +import { useTimeZoneSettingsApi } from '@abp/settings'; + +/** + * 初始化时区处理,通过API保存时区设置 + */ +export function initTimezone() { + const { getMyTimezoneApi, getTimezonesApi, updateMyTimezoneApi } = + useTimeZoneSettingsApi(); + setTimezoneHandler({ + getTimezone() { + return getMyTimezoneApi(); + }, + setTimezone(timezone: string) { + return updateMyTimezoneApi(timezone); + }, + async getTimezoneOptions() { + const timezones = await getTimezonesApi(); + return timezones.map((timezone) => { + return { + label: timezone.name, + value: timezone.value, + }; + }); + }, + }); +} diff --git a/apps/vben5/apps/app-antd/src/views/_core/authentication/login.vue b/apps/vben5/apps/app-antd/src/views/_core/authentication/login.vue index 157db329a..32a11cfc0 100644 --- a/apps/vben5/apps/app-antd/src/views/_core/authentication/login.vue +++ b/apps/vben5/apps/app-antd/src/views/_core/authentication/login.vue @@ -26,7 +26,7 @@ interface LoginInstance { defineOptions({ name: 'Login' }); -const { onlyOidc } = useAppConfig(import.meta.env, import.meta.env.PROD); +const { auth } = useAppConfig(import.meta.env, import.meta.env.PROD); const abpStore = useAbpStore(); const authStore = useAuthStore(); @@ -38,7 +38,7 @@ const { getConfigApi } = useAbpConfigApi(); const login = useTemplateRef('login'); const formSchema = computed((): VbenFormSchema[] => { - if (onlyOidc) { + if (auth.onlyOidc) { return []; } let schemas: VbenFormSchema[] = [ @@ -82,22 +82,26 @@ const [ShouldChangePasswordModal, changePasswordModalApi] = useVbenModal({ connectedComponent: ShouldChangePassword, }); async function onInit() { - if (onlyOidc === true) { - setTimeout(() => { - Modal.confirm({ - centered: true, - title: $t('page.auth.oidcLogin'), - content: $t('page.auth.oidcLoginMessage'), - maskClosable: false, - closable: false, - cancelButtonProps: { - disabled: true, - }, - async onOk() { - await authStore.oidcLogin(); - }, - }); - }, 300); + if (auth.onlyOidc === true) { + if (auth.onlyOidcHint) { + setTimeout(() => { + Modal.confirm({ + centered: true, + title: $t('page.auth.oidcLogin'), + content: $t('page.auth.oidcLoginMessage'), + maskClosable: false, + closable: false, + cancelButtonProps: { + disabled: true, + }, + async onOk() { + await authStore.oidcLogin(); + }, + }); + }, 300); + } else { + await authStore.oidcLogin(); + } return; } const abpConfig = await getConfigApi(); @@ -108,7 +112,7 @@ async function onInit() { }); } async function onLogin(params: Recordable) { - if (onlyOidc === true) { + if (auth.onlyOidc === true) { await authStore.oidcLogin(); return; } @@ -144,7 +148,7 @@ onMounted(onInit);