diff --git a/apps/vue/src/views/account/center/index.vue b/apps/vue/src/views/account/center/index.vue index 1f700b577..38d73c451 100644 --- a/apps/vue/src/views/account/center/index.vue +++ b/apps/vue/src/views/account/center/index.vue @@ -59,7 +59,7 @@ }, { key: 'setting', - name: L('User'), + name: L('DisplayName:UserSetting'), component: 'setting', }, ]; diff --git a/aspnet-core/Directory.Build.props b/aspnet-core/Directory.Build.props index 50aa64a65..0cdfddd29 100644 --- a/aspnet-core/Directory.Build.props +++ b/aspnet-core/Directory.Build.props @@ -3,6 +3,7 @@ 5.0.0-rc.2 5.0.0-rc.2 1.5.0 + 1.0.1 5.2.0 1.5.10 2.13.0 diff --git a/aspnet-core/LINGYUN.MicroService.Workflow.sln b/aspnet-core/LINGYUN.MicroService.Workflow.sln index 8d629f7bf..18d1a7cf1 100644 --- a/aspnet-core/LINGYUN.MicroService.Workflow.sln +++ b/aspnet-core/LINGYUN.MicroService.Workflow.sln @@ -17,7 +17,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WorkflowCore.Pe EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WorkflowCore.RabbitMQ", "modules\workflow\LINGYUN.Abp.WorkflowCore.RabbitMQ\LINGYUN.Abp.WorkflowCore.RabbitMQ.csproj", "{8F904E49-E6DA-499D-8127-DB60DA6B2EE9}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "components", "components", "{15F788FB-C7D0-4EE2-B1D9-EB9F3CCB7F33}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflow-components", "workflow-components", "{15F788FB-C7D0-4EE2-B1D9-EB9F3CCB7F33}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WorkflowCore.Components", "modules\workflow\LINGYUN.Abp.WorkflowCore.Components\LINGYUN.Abp.WorkflowCore.Components.csproj", "{13D116F7-C158-48D3-A78A-6F2BFAED1CDF}" EndProject @@ -37,7 +37,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WorkflowManagem EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore", "modules\workflow\LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore\LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore.csproj", "{C4B1160A-BF25-4868-B962-342ABA4A96EF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch", "modules\workflow\LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch\LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch.csproj", "{9F9453F3-7124-4C22-91E3-0DC41A4FD8AB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch", "modules\workflow\LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch\LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch.csproj", "{9F9453F3-7124-4C22-91E3-0DC41A4FD8AB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "host", "host", "{6CB521FC-AC40-49A6-B9A5-91399CAA59AB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LY.MicroService.WorkflowManagement.HttpApi.Host", "services\LY.MicroService.WorkflowManagement.HttpApi.Host\LY.MicroService.WorkflowManagement.HttpApi.Host.csproj", "{D5ED348D-D6F0-4093-BD7D-20E05AA1EB7B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -105,6 +109,10 @@ Global {9F9453F3-7124-4C22-91E3-0DC41A4FD8AB}.Debug|Any CPU.Build.0 = Debug|Any CPU {9F9453F3-7124-4C22-91E3-0DC41A4FD8AB}.Release|Any CPU.ActiveCfg = Release|Any CPU {9F9453F3-7124-4C22-91E3-0DC41A4FD8AB}.Release|Any CPU.Build.0 = Release|Any CPU + {D5ED348D-D6F0-4093-BD7D-20E05AA1EB7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5ED348D-D6F0-4093-BD7D-20E05AA1EB7B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5ED348D-D6F0-4093-BD7D-20E05AA1EB7B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5ED348D-D6F0-4093-BD7D-20E05AA1EB7B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -125,6 +133,7 @@ Global {99BDFED5-907F-44C9-8BA0-90E1725BE211} = {C4496993-41F5-4821-829E-B80A8B3BC260} {C4B1160A-BF25-4868-B962-342ABA4A96EF} = {C4496993-41F5-4821-829E-B80A8B3BC260} {9F9453F3-7124-4C22-91E3-0DC41A4FD8AB} = {A2963E0D-D290-40B2-9B36-75F4A5922ABF} + {D5ED348D-D6F0-4093-BD7D-20E05AA1EB7B} = {6CB521FC-AC40-49A6-B9A5-91399CAA59AB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6BB7A5DE-DA12-44DC-BC9B-0F6CA524346F} diff --git a/aspnet-core/modules/auditing/LINGYUN.Abp.AuditLogging.Elasticsearch/LINGYUN/Abp/AuditLogging/Elasticsearch/IndexInitializer.cs b/aspnet-core/modules/auditing/LINGYUN.Abp.AuditLogging.Elasticsearch/LINGYUN/Abp/AuditLogging/Elasticsearch/IndexInitializer.cs index 079394ed1..f49259704 100644 --- a/aspnet-core/modules/auditing/LINGYUN.Abp.AuditLogging.Elasticsearch/LINGYUN/Abp/AuditLogging/Elasticsearch/IndexInitializer.cs +++ b/aspnet-core/modules/auditing/LINGYUN.Abp.AuditLogging.Elasticsearch/LINGYUN/Abp/AuditLogging/Elasticsearch/IndexInitializer.cs @@ -74,7 +74,7 @@ namespace LINGYUN.Abp.AuditLogging.Elasticsearch .Properties((np => np.Object(p => p.Name(n => n.ExtraProperties)) .Date(p => p.Name(n => n.ExecutionTime).Format(dateTimeFormat)))))))); - if (indexCreateResponse.IsValid) + if (!indexCreateResponse.IsValid) { Logger.LogWarning("Failed to initialize index and audit log may not be retrieved."); Logger.LogWarning(indexCreateResponse.OriginalException.ToString()); @@ -96,7 +96,7 @@ namespace LINGYUN.Abp.AuditLogging.Elasticsearch .Properties(mp => mp.Object(p => p.Name(n => n.ExtraProperties)) .Date(p => p.Name(n => n.CreationTime).Format(dateTimeFormat))))); - if (indexCreateResponse.IsValid) + if (!indexCreateResponse.IsValid) { Logger.LogWarning("Failed to initialize index and security log may not be retrieved."); Logger.LogWarning(indexCreateResponse.OriginalException.ToString()); diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application.Contracts/LINGYUN/Abp/SettingManagement/Localization/ApplicationContracts/en.json b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application.Contracts/LINGYUN/Abp/SettingManagement/Localization/ApplicationContracts/en.json index c7d960e1c..30efeb7e6 100644 --- a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application.Contracts/LINGYUN/Abp/SettingManagement/Localization/ApplicationContracts/en.json +++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application.Contracts/LINGYUN/Abp/SettingManagement/Localization/ApplicationContracts/en.json @@ -5,8 +5,8 @@ "Permission:Settings": "Settings", "Permission:Update": "Update", "Permission:Manager": "Manager", - "DisplayName:User": "User Settings", - "Description:User": "User defined settings", + "DisplayName:UserSetting": "User Settings", + "Description:UserSetting": "User defined settings", "DisplayName:System": "System", "Description:System": "System", "DisplayName:System.Language": "Language", diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application.Contracts/LINGYUN/Abp/SettingManagement/Localization/ApplicationContracts/zh-Hans.json b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application.Contracts/LINGYUN/Abp/SettingManagement/Localization/ApplicationContracts/zh-Hans.json index eab5dec9d..34fbdafc2 100644 --- a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application.Contracts/LINGYUN/Abp/SettingManagement/Localization/ApplicationContracts/zh-Hans.json +++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application.Contracts/LINGYUN/Abp/SettingManagement/Localization/ApplicationContracts/zh-Hans.json @@ -5,8 +5,8 @@ "Permission:Settings": "配置管理", "Permission:Update": "变更", "Permission:Manager": "管理", - "DisplayName:User": "用户设置", - "Description:User": "用户自定义的设置项", + "DisplayName:UserSetting": "用户设置", + "Description:UserSetting": "用户自定义的设置项", "DisplayName:System": "系统设置", "Description:System": "与系统相关的配置项", "DisplayName:System.Language": "语言", diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/LINGYUN.Abp.WorkflowCore.DistributedLock.csproj b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/LINGYUN.Abp.WorkflowCore.DistributedLock.csproj index 4d1174192..bf77fa720 100644 --- a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/LINGYUN.Abp.WorkflowCore.DistributedLock.csproj +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/LINGYUN.Abp.WorkflowCore.DistributedLock.csproj @@ -9,7 +9,7 @@ - + diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/LINGYUN/Abp/WorkflowCore/DistributedLock/AbpWorkflowCoreDistributedLockModule.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/LINGYUN/Abp/WorkflowCore/DistributedLock/AbpWorkflowCoreDistributedLockModule.cs index 908258912..578713b8d 100644 --- a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/LINGYUN/Abp/WorkflowCore/DistributedLock/AbpWorkflowCoreDistributedLockModule.cs +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/LINGYUN/Abp/WorkflowCore/DistributedLock/AbpWorkflowCoreDistributedLockModule.cs @@ -12,6 +12,7 @@ namespace LINGYUN.Abp.WorkflowCore.DistributedLock { public override void PreConfigureServices(ServiceConfigurationContext context) { + context.Services.AddMemoryCache(); context.Services.AddSingleton(); context.Services.AddSingleton(); diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/AbpWorkflowPersistenceProvider.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/AbpWorkflowPersistenceProvider.cs index d9d89b885..7e2be5df5 100644 --- a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/AbpWorkflowPersistenceProvider.cs +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/AbpWorkflowPersistenceProvider.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging.Abstractions; using System; using System.Collections.Generic; +using System.Data; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -20,6 +21,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence private readonly ICurrentTenant _currentTenant; private readonly IGuidGenerator _guidGenerator; + //private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly IWorkflowRepository _workflowRepository; private readonly IWorkflowEventRepository _workflowEventRepository; private readonly IWorkflowExecutionErrorRepository _executionErrorRepository; @@ -33,6 +35,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence public AbpWorkflowPersistenceProvider( ICurrentTenant currentTenant, IGuidGenerator guidGenerator, + //IUnitOfWorkManager unitOfWorkManager, IAsyncQueryableExecuter asyncQueryableExecuter, IWorkflowRepository workflowRepository, IWorkflowEventRepository workflowEventRepository, @@ -42,6 +45,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence { _currentTenant = currentTenant; _guidGenerator = guidGenerator; + //_unitOfWorkManager = unitOfWorkManager; _asyncQueryableExecuter = asyncQueryableExecuter; _workflowRepository = workflowRepository; @@ -58,6 +62,8 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence string token, CancellationToken cancellationToken = default) { + //using var unitOfWork = _unitOfWorkManager.Begin(); + var uid = Guid.Parse(eventSubscriptionId); var existingEntity = await _subscriptionRepository.GetAsync(uid, cancellationToken: cancellationToken); @@ -67,16 +73,22 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence existingEntity.SetSubscriptionToken(null, null, null); await _subscriptionRepository.UpdateAsync(existingEntity, cancellationToken: cancellationToken); + + //await unitOfWork.SaveChangesAsync(); } public virtual async Task CreateEvent( Event newEvent, CancellationToken cancellationToken = default) { + //using var unitOfWork = _unitOfWorkManager.Begin(); + var we = newEvent.ToPersistable(_guidGenerator, _currentTenant); await _workflowEventRepository.InsertAsync(we, cancellationToken: cancellationToken); + //await unitOfWork.SaveChangesAsync(); + newEvent.Id = we.Id.ToString(); return newEvent.Id; @@ -86,10 +98,14 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence EventSubscription subscription, CancellationToken cancellationToken = default) { + //using var unitOfWork = _unitOfWorkManager.Begin(); + var wes = subscription.ToPersistable(_guidGenerator, _currentTenant); await _subscriptionRepository.InsertAsync(wes, cancellationToken: cancellationToken); + //await unitOfWork.SaveChangesAsync(); + subscription.Id = wes.Id.ToString(); return subscription.Id; @@ -99,21 +115,25 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence WorkflowInstance workflow, CancellationToken cancellationToken = default) { + //using var unitOfWork = _unitOfWorkManager.Begin(); + var wf = workflow.ToPersistable(_guidGenerator, _currentTenant); await _workflowRepository.InsertAsync(wf, cancellationToken: cancellationToken); + //await unitOfWork.SaveChangesAsync(); + workflow.Id = wf.Id.ToString(); return workflow.Id; } - [UnitOfWork(IsDisabled = true)] public void EnsureStoreExists() { // TODO: } + //[UnitOfWork] public virtual async Task GetEvent(string id, CancellationToken cancellationToken = default) { var eventId = Guid.Parse(id); @@ -123,6 +143,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence return workflowEvent.ToEvent(); } + //[UnitOfWork] public virtual async Task> GetEvents( string eventName, string eventKey, @@ -139,6 +160,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence return workflowEventIds.Select(e => e.ToString()); } + //[UnitOfWork] public virtual async Task GetFirstOpenSubscription( string eventName, string eventKey, @@ -153,6 +175,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence return workflowEventSubscription?.ToEventSubscription(); } + //[UnitOfWork] public virtual async Task> GetRunnableEvents( DateTime asAt, CancellationToken cancellationToken = default) @@ -170,6 +193,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence return workflowEventIdList.Select(e => e.ToString()); } + //[UnitOfWork] public virtual async Task> GetRunnableInstances( DateTime asAt, CancellationToken cancellationToken = default) @@ -185,6 +209,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence return workflowIdList.Select(e => e.ToString()); } + //[UnitOfWork] public virtual async Task GetSubscription( string eventSubscriptionId, CancellationToken cancellationToken = default) @@ -195,6 +220,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence return subscription?.ToEventSubscription(); } + //[UnitOfWork] public virtual async Task> GetSubscriptions( string eventName, string eventKey, @@ -210,6 +236,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence return eventSubscriptions.Select(x => x.ToEventSubscription()); } + [UnitOfWork(true, IsolationLevel.ReadUncommitted)] public virtual async Task GetWorkflowInstance( string Id, CancellationToken cancellationToken = default) @@ -223,6 +250,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence return workflow?.ToWorkflowInstance(); } + //[UnitOfWork] public virtual async Task> GetWorkflowInstances( WorkflowStatus? status, string type, @@ -236,6 +264,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence return workflows.Select(x => x.ToWorkflowInstance()); } + //[UnitOfWork] public virtual async Task> GetWorkflowInstances( IEnumerable ids, CancellationToken cancellationToken = default) @@ -252,36 +281,50 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence public virtual async Task MarkEventProcessed(string id, CancellationToken cancellationToken = default) { + //using var unitOfWork = _unitOfWorkManager.Begin(); + var eventId = Guid.Parse(id); var workflowEvent = await _workflowEventRepository.GetAsync(eventId, cancellationToken: cancellationToken); workflowEvent.IsProcessed = true; await _workflowEventRepository.UpdateAsync(workflowEvent, cancellationToken: cancellationToken); + + //await unitOfWork.SaveChangesAsync(cancellationToken); } public virtual async Task MarkEventUnprocessed(string id, CancellationToken cancellationToken = default) { + //using var unitOfWork = _unitOfWorkManager.Begin(); + var eventId = Guid.Parse(id); var workflowEvent = await _workflowEventRepository.GetAsync(eventId, cancellationToken: cancellationToken); workflowEvent.IsProcessed = false; await _workflowEventRepository.UpdateAsync(workflowEvent, cancellationToken: cancellationToken); + + //await unitOfWork.SaveChangesAsync(cancellationToken); } public virtual async Task PersistErrors(IEnumerable errors, CancellationToken cancellationToken = default) { if (errors.Any()) { + //using var unitOfWork = _unitOfWorkManager.Begin(); + var workflowExecutionErrors = errors.Select(x => x.ToPersistable(_currentTenant)); await _executionErrorRepository.InsertManyAsync(workflowExecutionErrors, cancellationToken: cancellationToken); + + //await unitOfWork.SaveChangesAsync(cancellationToken); } } public virtual async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default) { + //using var unitOfWork = _unitOfWorkManager.Begin(); + if (!Guid.TryParse(workflow.Id, out Guid workflowId)) { workflowId = _guidGenerator.Create(); @@ -300,6 +343,8 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence await _workflowRepository.UpdateAsync(wf, cancellationToken: cancellationToken); } + + //await unitOfWork.SaveChangesAsync(cancellationToken); } public virtual async Task ProcessCommands( @@ -309,6 +354,8 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence { try { + //using var unitOfWork = _unitOfWorkManager.Begin(); + var quertable = await _scheduledCommandRepository.GetQueryableAsync(); var commands = await _asyncQueryableExecuter.ToListAsync( quertable.Where(x => x.ExecuteTime < asOf.UtcDateTime.Ticks), @@ -320,6 +367,8 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence } await _scheduledCommandRepository.DeleteManyAsync(commands, cancellationToken: cancellationToken); + + //await unitOfWork.SaveChangesAsync(cancellationToken); } catch(Exception ex) { @@ -332,9 +381,13 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence { if (!await _scheduledCommandRepository.CheckExistsAsync(command.CommandName, command.Data)) { + //using var unitOfWork = _unitOfWorkManager.Begin(); + var workflowCommand = command.ToPersistable(_currentTenant); await _scheduledCommandRepository.InsertAsync(workflowCommand); + + //await unitOfWork.SaveChangesAsync(); } } @@ -347,12 +400,16 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence { var uid = Guid.Parse(eventSubscriptionId); + //using var unitOfWork = _unitOfWorkManager.Begin(); + var existingEntity = await _subscriptionRepository.GetAsync(uid, cancellationToken: cancellationToken); existingEntity.SetSubscriptionToken(token, workerId, expiry); await _subscriptionRepository.UpdateAsync(existingEntity, cancellationToken: cancellationToken); + //await unitOfWork.SaveChangesAsync(cancellationToken); + return true; } @@ -362,9 +419,13 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence { var uid = Guid.Parse(eventSubscriptionId); + //using var unitOfWork = _unitOfWorkManager.Begin(); + var existingEntity = await _subscriptionRepository.GetAsync(uid, cancellationToken: cancellationToken); await _subscriptionRepository.DeleteAsync(existingEntity, cancellationToken: cancellationToken); + + //await unitOfWork.SaveChangesAsync(cancellationToken); } } } diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/AbpRabbitMqQueueProvider.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/AbpRabbitMqQueueProvider.cs index c6580fb61..a604fcf94 100644 --- a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/AbpRabbitMqQueueProvider.cs +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/AbpRabbitMqQueueProvider.cs @@ -6,13 +6,15 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Volo.Abp; +using Volo.Abp.DependencyInjection; using Volo.Abp.RabbitMQ; using Volo.Abp.Threading; using WorkflowCore.Interface; namespace LINGYUN.Abp.WorkflowCore.RabbitMQ { - public class AbpRabbitMqQueueProvider : IQueueProvider + [Dependency(ReplaceServices = true)] + public class AbpRabbitMqQueueProvider : IQueueAdapterProvider, ISingletonDependency { protected bool IsDiposed { get; private set; } protected SemaphoreSlim SyncObj = new SemaphoreSlim(1, 1); @@ -61,6 +63,11 @@ namespace LINGYUN.Abp.WorkflowCore.RabbitMQ } public async Task QueueWork(string id, QueueType queue) + { + await QueueWorkAsync(id, queue); + } + + protected virtual async Task QueueWorkAsync(string id, QueueType queue) { CheckDisposed(); diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/AbpWorkflowCoreRabbitMQModule.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/AbpWorkflowCoreRabbitMQModule.cs index adda074f2..5d2d62f84 100644 --- a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/AbpWorkflowCoreRabbitMQModule.cs +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/AbpWorkflowCoreRabbitMQModule.cs @@ -1,32 +1,13 @@ -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp; -using Volo.Abp.Modularity; +using Volo.Abp.Modularity; using Volo.Abp.RabbitMQ; -using WorkflowCore.Interface; -using WorkflowCore.Models; +using Volo.Abp.Uow; namespace LINGYUN.Abp.WorkflowCore.RabbitMQ { + [DependsOn(typeof(AbpUnitOfWorkModule))] [DependsOn(typeof(AbpRabbitMqModule))] [DependsOn(typeof(AbpWorkflowCoreModule))] public class AbpWorkflowCoreRabbitMQModule : AbpModule { - public override void PreConfigureServices(ServiceConfigurationContext context) - { - context.Services.AddSingleton(); - context.Services.AddSingleton(); - - PreConfigure(options => - { - options.UseQueueProvider(provider => provider.GetRequiredService()); - }); - } - - public override void OnApplicationShutdown(ApplicationShutdownContext context) - { - context.ServiceProvider - .GetRequiredService() - .Dispose(); - } } } diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpUnitOfWorkQueueProvider.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpUnitOfWorkQueueProvider.cs new file mode 100644 index 000000000..a70b64cb4 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpUnitOfWorkQueueProvider.cs @@ -0,0 +1,67 @@ +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Uow; +using WorkflowCore.Interface; + +namespace LINGYUN.Abp.WorkflowCore +{ + /// + /// 当触发新的工作流时,如果持久层在事务中会导致默认队列获取工作流实例失败 + /// 建立一个事务适配队列在工作单元结束时再将消息入队 + /// + public class AbpUnitOfWorkQueueProvider : IQueueProvider + { + private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly IQueueAdapterProvider _queueProvider; + + public AbpUnitOfWorkQueueProvider( + IQueueAdapterProvider queueProvider, + IUnitOfWorkManager unitOfWorkManager) + { + _queueProvider = queueProvider; + _unitOfWorkManager = unitOfWorkManager; + } + + public bool IsDequeueBlocking => _queueProvider.IsDequeueBlocking; + + public virtual async Task DequeueWork(QueueType queue, CancellationToken cancellationToken) + { + if (_unitOfWorkManager.Current != null && !_unitOfWorkManager.Current.IsCompleted) + { + return null; + } + + return await _queueProvider.DequeueWork(queue, cancellationToken); + } + + public void Dispose() + { + _queueProvider.Dispose(); + } + + public async Task QueueWork(string id, QueueType queue) + { + if (_unitOfWorkManager.Current != null) + { + _unitOfWorkManager.Current.OnCompleted(async () => + { + await _queueProvider.QueueWork(id, queue); + }); + } + else + { + await _queueProvider.QueueWork(id, queue); + } + } + + public async Task Start() + { + await _queueProvider.Start(); + } + + public async Task Stop() + { + await _queueProvider.Stop(); + } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreModule.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreModule.cs index 842085fb4..7ed821f8a 100644 --- a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreModule.cs +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreModule.cs @@ -2,13 +2,12 @@ using Microsoft.Extensions.Options; using System; using System.Collections.Generic; -using System.Linq; -using System.Reflection; using Volo.Abp; using Volo.Abp.Modularity; using Volo.Abp.Threading; using Volo.Abp.Timing; using WorkflowCore.Interface; +using WorkflowCore.Models; namespace LINGYUN.Abp.WorkflowCore { @@ -23,6 +22,14 @@ namespace LINGYUN.Abp.WorkflowCore { context.Services.AddConventionalRegistrar(new AbpWorkflowCoreConventionalRegistrar()); + context.Services.AddSingleton(); + context.Services.AddSingleton(); + + PreConfigure(options => + { + options.UseQueueProvider(provider => provider.GetRequiredService()); + }); + AutoAddDefinitionWorkflows(context.Services); } diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/IQueueAdapterProvider.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/IQueueAdapterProvider.cs new file mode 100644 index 000000000..443119d54 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/IQueueAdapterProvider.cs @@ -0,0 +1,20 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using WorkflowCore.Interface; + +namespace LINGYUN.Abp.WorkflowCore +{ + public interface IQueueAdapterProvider : IDisposable + { + bool IsDequeueBlocking { get; } + + Task QueueWork(string id, QueueType queue); + + Task DequeueWork(QueueType queue, CancellationToken cancellationToken); + + Task Start(); + + Task Stop(); + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/SingleNodeQueueAdapterProvider.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/SingleNodeQueueAdapterProvider.cs new file mode 100644 index 000000000..836765990 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/SingleNodeQueueAdapterProvider.cs @@ -0,0 +1,50 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using WorkflowCore.Interface; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.WorkflowCore +{ + [Dependency(TryRegister = true)] + public class SingleNodeQueueAdapterProvider : IQueueAdapterProvider, ISingletonDependency + { + private readonly Dictionary> _queues = new Dictionary> + { + [QueueType.Workflow] = new BlockingCollection(), + [QueueType.Event] = new BlockingCollection(), + [QueueType.Index] = new BlockingCollection() + }; + + public bool IsDequeueBlocking => true; + + public Task QueueWork(string id, QueueType queue) + { + _queues[queue].Add(id); + return Task.CompletedTask; + } + + public Task DequeueWork(QueueType queue, CancellationToken cancellationToken) + { + if (_queues[queue].TryTake(out string id, 100, cancellationToken)) + return Task.FromResult(id); + + return Task.FromResult(null); + } + + public Task Start() + { + return Task.CompletedTask; + } + + public Task Stop() + { + return Task.CompletedTask; + } + + public void Dispose() + { + } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/ActivityFailureInput.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/ActivityFailureInput.cs new file mode 100644 index 000000000..1b7093101 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/ActivityFailureInput.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace LINGYUN.Abp.WorkflowManagement.Activitys +{ + public class ActivityFailureInput + { + [Required] + public string Token { get; set; } + + [Required] + public object Result { get; set; } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/ActivityReleaseInput.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/ActivityReleaseInput.cs new file mode 100644 index 000000000..f58b3287e --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/ActivityReleaseInput.cs @@ -0,0 +1,7 @@ +namespace LINGYUN.Abp.WorkflowManagement.Activitys +{ + public class ActivityReleaseInput + { + public string Token { get; set; } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/ActivitySuccessInput.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/ActivitySuccessInput.cs new file mode 100644 index 000000000..a32d959fb --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/ActivitySuccessInput.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace LINGYUN.Abp.WorkflowManagement.Activitys +{ + public class ActivitySuccessInput + { + [Required] + public string Token { get; set; } + + [Required] + public object Result { get; set; } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/GetPendingActivityInput.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/GetPendingActivityInput.cs new file mode 100644 index 000000000..b7d8ed790 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/GetPendingActivityInput.cs @@ -0,0 +1,13 @@ +using System; + +namespace LINGYUN.Abp.WorkflowManagement.Activitys +{ + public class GetPendingActivityInput + { + public string ActivityName { get; set; } + + public string WorkflowId { get; set; } + + public TimeSpan? Timeout { get; set; } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/PendingActivityDto.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/PendingActivityDto.cs new file mode 100644 index 000000000..cc44e87be --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/Dto/PendingActivityDto.cs @@ -0,0 +1,15 @@ +using System; + +namespace LINGYUN.Abp.WorkflowManagement.Activitys +{ + public class PendingActivityDto + { + public string Token { get; set; } + + public string ActivityName { get; set; } + + public object Parameters { get; set; } + + public DateTime TokenExpiry { get; set; } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/IActivityAppService.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/IActivityAppService.cs new file mode 100644 index 000000000..c859ab989 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Activitys/IActivityAppService.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Services; + +namespace LINGYUN.Abp.WorkflowManagement.Activitys +{ + public interface IActivityAppService : IApplicationService + { + Task GetAsync(GetPendingActivityInput input); + + Task DeleteAsync(ActivityReleaseInput input); + + Task SuccessAsync(ActivitySuccessInput input); + + Task FailureAsync(ActivityFailureInput input); + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Events/Dto/EventPublishInput.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Events/Dto/EventPublishInput.cs new file mode 100644 index 000000000..14f5c791d --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Events/Dto/EventPublishInput.cs @@ -0,0 +1,19 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace LINGYUN.Abp.WorkflowManagement.Events +{ + public class EventPublishInput + { + [Required] + public string EventName { get; set; } + + [Required] + public string EventKey { get; set; } + + [Required] + public object EventData { get; set; } + + public DateTime? EffectiveDate { get; set; } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Events/IEventAppService.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Events/IEventAppService.cs new file mode 100644 index 000000000..0f7181323 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Events/IEventAppService.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Services; + +namespace LINGYUN.Abp.WorkflowManagement.Events +{ + public interface IEventAppService : IApplicationService + { + Task PublishAsync(EventPublishInput input); + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/StepDto.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/StepDto.cs new file mode 100644 index 000000000..a79cab6c6 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/StepDto.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.Data; + +namespace LINGYUN.Abp.WorkflowManagement.Workflows +{ + public class StepDto + { + public string Name { get; set; } + public string StepType { get; set; } + public string CancelCondition { get; set; } + public TimeSpan? RetryInterval { get; set; } + public bool Saga { get; set; } + public string NextStep { get; set; } + public List CompensateWith { get; set; } = new List(); + public ExtraPropertyDictionary Inputs { get; set; } = new ExtraPropertyDictionary(); + public ExtraPropertyDictionary Outputs { get; set; } = new ExtraPropertyDictionary(); + public ExtraPropertyDictionary SelectNextStep { get; set; } = new ExtraPropertyDictionary(); + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/WorkflowCreateDto.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/WorkflowCreateDto.cs new file mode 100644 index 000000000..97e93e3b7 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/WorkflowCreateDto.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; + +namespace LINGYUN.Abp.WorkflowManagement.Workflows +{ + public class WorkflowCreateDto + { + /// + /// 是否启用 + /// + public bool IsEnabled { get; set; } + /// + /// 名称 + /// + public string Name { get; set; } + /// + /// 显示名称 + /// + public string DisplayName { get; set; } + /// + /// 描述 + /// + public string Description { get; set; } + /// + /// 版本号 + /// + public int Version { get; set; } + + public List Steps { get; set; } = new List(); + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/WorkflowDefinitionDto.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/WorkflowDefinitionDto.cs new file mode 100644 index 000000000..1f24f85f3 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/WorkflowDefinitionDto.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace LINGYUN.Abp.WorkflowManagement.Workflows +{ + public class WorkflowDefinitionDto + { + /// + /// 描述 + /// + public string Description { get; set; } + /// + /// 版本号 + /// + public int Version { get; set; } + + public List Steps { get; set; } = new List(); + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/WorkflowDto.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/WorkflowDto.cs new file mode 100644 index 000000000..180bf462a --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/WorkflowDto.cs @@ -0,0 +1,16 @@ +using System; + +namespace LINGYUN.Abp.WorkflowManagement.Workflows +{ + public class WorkflowDto + { + public string WorkflowId { get; set; } + public object Data { get; set; } + public string DefinitionId { get; set; } + public int Version { get; set; } + public string Status { get; set; } + public string Reference { get; set; } + public DateTime StartTime { get; set; } + public DateTime? EndTime { get; set; } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/WorkflowStartInput.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/WorkflowStartInput.cs new file mode 100644 index 000000000..d8f5b40e5 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/Dto/WorkflowStartInput.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace LINGYUN.Abp.WorkflowManagement.Workflows +{ + public class WorkflowStartInput + { + public Dictionary Data { get; set; } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/IWorkflowAppService.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/IWorkflowAppService.cs new file mode 100644 index 000000000..0d878cfe0 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Workflows/IWorkflowAppService.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Services; + +namespace LINGYUN.Abp.WorkflowManagement.Workflows +{ + public interface IWorkflowAppService : IApplicationService + { + Task GetAsync(string id); + + Task CreateAsync(WorkflowCreateDto input); + + Task StartAsync(string id, WorkflowStartInput input); + + Task SuspendAsync(string id); + + Task ResumeAsync(string id); + + Task TerminateAsync(string id); + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/LINGYUN/Abp/WorkflowManagement/Activitys/ActivityAppService.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/LINGYUN/Abp/WorkflowManagement/Activitys/ActivityAppService.cs new file mode 100644 index 000000000..d8c921907 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/LINGYUN/Abp/WorkflowManagement/Activitys/ActivityAppService.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Authorization; +using System.Threading.Tasks; +using WorkflowCore.Interface; + +namespace LINGYUN.Abp.WorkflowManagement.Activitys +{ + [Authorize] + public class ActivityAppService : WorkflowManagementAppServiceBase, IActivityAppService + { + private readonly IActivityController _controller; + + public ActivityAppService(IActivityController controller) + { + _controller = controller; + } + + public virtual async Task FailureAsync(ActivityFailureInput input) + { + await _controller.SubmitActivityFailure(input.Token, input.Result); + } + + public virtual async Task GetAsync(GetPendingActivityInput input) + { + var activity = await _controller.GetPendingActivity( + input.ActivityName, input.WorkflowId, input.Timeout); + + return ObjectMapper.Map(activity); + } + + public virtual async Task DeleteAsync(ActivityReleaseInput input) + { + await _controller.ReleaseActivityToken(input.Token); + } + + public virtual async Task SuccessAsync(ActivitySuccessInput input) + { + await _controller.SubmitActivitySuccess(input.Token, input.Result); + } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/LINGYUN/Abp/WorkflowManagement/Events/EventAppService.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/LINGYUN/Abp/WorkflowManagement/Events/EventAppService.cs new file mode 100644 index 000000000..dd3931881 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/LINGYUN/Abp/WorkflowManagement/Events/EventAppService.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Authorization; +using System.Threading.Tasks; +using WorkflowCore.Interface; + +namespace LINGYUN.Abp.WorkflowManagement.Events +{ + [Authorize] + public class EventAppService : WorkflowManagementAppServiceBase, IEventAppService + { + private readonly IWorkflowController _controller; + + public EventAppService(IWorkflowController controller) + { + _controller = controller; + } + + public virtual async Task PublishAsync(EventPublishInput input) + { + await _controller.PublishEvent(input.EventName, input.EventKey, input.EventData, input.EffectiveDate); + } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/LINGYUN/Abp/WorkflowManagement/WorkflowManagementApplicationMapperProfile.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/LINGYUN/Abp/WorkflowManagement/WorkflowManagementApplicationMapperProfile.cs index 2bb35efe9..6312d2f5a 100644 --- a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/LINGYUN/Abp/WorkflowManagement/WorkflowManagementApplicationMapperProfile.cs +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/LINGYUN/Abp/WorkflowManagement/WorkflowManagementApplicationMapperProfile.cs @@ -1,4 +1,8 @@ using AutoMapper; +using LINGYUN.Abp.WorkflowManagement.Activitys; +using LINGYUN.Abp.WorkflowManagement.Workflows; +using WorkflowCore.Interface; +using WorkflowCore.Models; namespace LINGYUN.Abp.WorkflowManagement { @@ -6,6 +10,12 @@ namespace LINGYUN.Abp.WorkflowManagement { public WorkflowManagementApplicationMapperProfile() { + CreateMap() + .ForMember(dto => dto.WorkflowId, map => map.MapFrom(src => src.Id.ToString())) + .ForMember(dto => dto.DefinitionId, map => map.MapFrom(src => src.Id.ToString())) + .ForMember(dto => dto.StartTime, map => map.MapFrom(src => src.CreateTime)) + .ForMember(dto => dto.EndTime, map => map.MapFrom(src => src.CompleteTime)); + CreateMap(); } } } diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/LINGYUN/Abp/WorkflowManagement/Workflows/WorkflowAppService.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/LINGYUN/Abp/WorkflowManagement/Workflows/WorkflowAppService.cs new file mode 100644 index 000000000..fc34c6c64 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/LINGYUN/Abp/WorkflowManagement/Workflows/WorkflowAppService.cs @@ -0,0 +1,165 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading.Tasks; +using Volo.Abp; +using WorkflowCore.Interface; +using WorkflowCore.Models; + +namespace LINGYUN.Abp.WorkflowManagement.Workflows +{ + //[Authorize] + public class WorkflowAppService : WorkflowManagementAppServiceBase, IWorkflowAppService + { + private readonly WorkflowManager _workflowManager; + private readonly IWorkflowController _controller; + private readonly IPersistenceProvider _persistence; + private readonly IWorkflowRepository _workflowRepository; + private readonly IStepNodeRepository _stepNodeRepository; + private readonly ICompensateNodeRepository _compensateNodeRepository; + + public WorkflowAppService( + IWorkflowController controller, + IPersistenceProvider persistence, + WorkflowManager workflowManager, + IWorkflowRepository workflowRepository, + IStepNodeRepository stepNodeRepository, + ICompensateNodeRepository compensateNodeRepository) + { + _controller = controller; + _persistence = persistence; + _workflowManager = workflowManager; + _workflowRepository = workflowRepository; + _stepNodeRepository = stepNodeRepository; + _compensateNodeRepository = compensateNodeRepository; + } + + public virtual async Task GetAsync(string id) + { + var workflow = await _persistence.GetWorkflowInstance(id); + + return ObjectMapper.Map(workflow); + } + + public virtual async Task ResumeAsync(string id) + { + var result = await _controller.ResumeWorkflow(id); + if (!result) + { + throw new BusinessException(); + } + } + + public virtual async Task CreateAsync(WorkflowCreateDto input) + { + if (await _workflowRepository.CheckVersionAsync(input.Name, input.Version)) + { + throw new BusinessException(); + } + + var workflow = new Workflow( + GuidGenerator.Create(), + input.Name, + input.Description, + input.Description, + input.Version, + tenantId: CurrentTenant.Id); + + var stepNodes = new List(); + var stepCompensateNodes = new List(); + + ICollection CreateCompensateNodes(StepNode node, ICollection steps) + { + var stepNodes = new List(); + foreach (var step in steps) + { + var stepNode = new CompensateNode( + GuidGenerator.Create(), + workflow.Id, + step.Name, + step.StepType, + step.CancelCondition, + saga: step.Saga, + parentId: node.Id, + tenantId: CurrentTenant.Id); + stepNode.Inputs.AddIfNotContains(step.Inputs); + stepNode.Outputs.AddIfNotContains(step.Outputs); + stepNode.SelectNextStep.AddIfNotContains(step.SelectNextStep); + + stepNodes.Add(stepNode); + } + return stepNodes; + } + + foreach (var stepInput in input.Steps) + { + var stepNode = new StepNode( + GuidGenerator.Create(), + workflow.Id, + stepInput.Name, + stepInput.StepType, + stepInput.CancelCondition, + saga: stepInput.Saga, + tenantId: CurrentTenant.Id); + stepNode.Inputs.AddIfNotContains(stepInput.Inputs); + stepNode.Outputs.AddIfNotContains(stepInput.Outputs); + stepNode.SelectNextStep.AddIfNotContains(stepInput.SelectNextStep); + + stepNodes.Add(stepNode); + stepCompensateNodes.AddRange(CreateCompensateNodes(stepNode, stepInput.CompensateWith)); + } + + await _workflowRepository.InsertAsync(workflow); + await _stepNodeRepository.InsertManyAsync(stepNodes); + await _compensateNodeRepository.InsertManyAsync(stepCompensateNodes); + + _workflowManager.Register(workflow, stepNodes, stepCompensateNodes); + } + + public virtual async Task StartAsync(string id, WorkflowStartInput input) + { + var workflowData = new Dictionary(); + foreach (var data in input.Data) + { + if (data.Value is JsonElement element) + { + //var dataDic = new Dictionary(); + //var children = element.EnumerateObject(); + //while (children.MoveNext()) + //{ + // dataDic.TryAdd(children.Current.Name, children.Current.Value.ToString()); + //} + //JsonConvert.DeserializeObject(element.ToString()) + workflowData.TryAdd(data.Key, JsonConvert.DeserializeObject(element.ToString())); + } + else + { + workflowData.TryAdd(data.Key, data.Value); + } + } + + var instanceId = await _controller.StartWorkflow(id, workflowData); + var result = await _persistence.GetWorkflowInstance(instanceId); + + return ObjectMapper.Map(result); + } + + public virtual async Task SuspendAsync(string id) + { + var result = await _controller.SuspendWorkflow(id); + if (!result) + { + throw new BusinessException(); + } + } + + public virtual async Task TerminateAsync(string id) + { + var result = await _controller.TerminateWorkflow(id); + if (!result) + { + throw new BusinessException(); + } + } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain.Shared/LINGYUN/Abp/WorkflowManagement/WorkflowConsts.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain.Shared/LINGYUN/Abp/WorkflowManagement/WorkflowConsts.cs new file mode 100644 index 000000000..d1055cfcd --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain.Shared/LINGYUN/Abp/WorkflowManagement/WorkflowConsts.cs @@ -0,0 +1,11 @@ +namespace LINGYUN.Abp.WorkflowManagement +{ + public static class WorkflowConsts + { + public static int MaxCancelConditionLength { get; set; } = 200; + public static int MaxNameLength { get; set; } = 100; + public static int MaxDisplayNameLength { get; set; } = 200; + public static int MaxDescriptionLength { get; set; } = 200; + public static int MaxStepTypeLength { get; set; } = 100; + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/CompensateNode.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/CompensateNode.cs new file mode 100644 index 000000000..c1e79a13c --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/CompensateNode.cs @@ -0,0 +1,27 @@ +using System; +using WorkflowCore.Models; + +namespace LINGYUN.Abp.WorkflowManagement +{ + public class CompensateNode : Step + { + public CompensateNode() + { + } + + public CompensateNode( + Guid id, + Guid workflowId, + string name, + string stepType, + string cancelCondition, + WorkflowErrorHandling? errorBehavior = null, + TimeSpan? retryInterval = null, + bool saga = false, + Guid? parentId = null, + Guid? tenantId = null) + : base(id, workflowId, name, stepType, cancelCondition, errorBehavior, retryInterval, saga, parentId, tenantId) + { + } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/ICompensateNodeRepository.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/ICompensateNodeRepository.cs new file mode 100644 index 000000000..d9c7d1f7f --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/ICompensateNodeRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace LINGYUN.Abp.WorkflowManagement +{ + public interface ICompensateNodeRepository : IRepository + { + Task> GetAllChildrenWithWorkflowAsync( + Guid workflowId, + CancellationToken cancellationToken = default); + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/IStep.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/IStep.cs new file mode 100644 index 000000000..a0e35a309 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/IStep.cs @@ -0,0 +1,21 @@ +using System; +using Volo.Abp.Data; +using WorkflowCore.Models; + +namespace LINGYUN.Abp.WorkflowManagement +{ + public interface IStep + { + Guid WorkflowId { get; } + string Name { get; } + string StepType { get; } + Guid? ParentId { get; } + WorkflowErrorHandling? ErrorBehavior { get; } + string CancelCondition { get; set; } + TimeSpan? RetryInterval { get; set; } + bool Saga { get; set; } + ExtraPropertyDictionary Inputs { get; set; } + ExtraPropertyDictionary Outputs { get; set; } + ExtraPropertyDictionary SelectNextStep { get; set; } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/IStepNodeRepository.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/IStepNodeRepository.cs new file mode 100644 index 000000000..f800795c1 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/IStepNodeRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace LINGYUN.Abp.WorkflowManagement +{ + public interface IStepNodeRepository : IRepository + { + Task> GetAllChildrenWithWorkflowAsync( + Guid workflowId, + CancellationToken cancellationToken = default); + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/IWorkflowRepository.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/IWorkflowRepository.cs new file mode 100644 index 000000000..348804633 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/IWorkflowRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace LINGYUN.Abp.WorkflowManagement +{ + public interface IWorkflowRepository : IRepository + { + Task CheckVersionAsync( + string name, + int version, + CancellationToken cancellationToken = default); + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/Step.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/Step.cs index 3fc65ef3a..c310257f2 100644 --- a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/Step.cs +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/Step.cs @@ -6,7 +6,7 @@ using WorkflowCore.Models; namespace LINGYUN.Abp.WorkflowManagement { - public class Step : Entity, IMultiTenant + public abstract class Step : Entity, IMultiTenant { public virtual Guid? TenantId { get; protected set; } public virtual Guid WorkflowId { get; protected set; } @@ -14,9 +14,9 @@ namespace LINGYUN.Abp.WorkflowManagement public virtual string StepType { get; protected set; } public virtual string CancelCondition { get; set; } public virtual WorkflowErrorHandling? ErrorBehavior { get; protected set; } - public virtual int? RetryInterval { get; set; } + public virtual TimeSpan? RetryInterval { get; set; } public virtual bool Saga { get; set; } - public virtual Guid? NextStep { get; protected set; } + public virtual Guid? ParentId { get; protected set; } public virtual ExtraPropertyDictionary Inputs { get; set; } public virtual ExtraPropertyDictionary Outputs { get; set; } public virtual ExtraPropertyDictionary SelectNextStep { get; set; } @@ -27,16 +27,16 @@ namespace LINGYUN.Abp.WorkflowManagement SelectNextStep = new ExtraPropertyDictionary(); } - public Step( + protected Step( Guid id, Guid workflowId, string name, string stepType, string cancelCondition, WorkflowErrorHandling? errorBehavior = null, - int? retryInterval = null, + TimeSpan? retryInterval = null, bool saga = false, - Guid? nextStep = null, + Guid? parentId = null, Guid? tenantId = null) : base(id) { Name = name; @@ -46,7 +46,7 @@ namespace LINGYUN.Abp.WorkflowManagement ErrorBehavior = errorBehavior; RetryInterval = retryInterval; Saga = saga; - NextStep = nextStep; + ParentId = parentId; TenantId = tenantId; Inputs = new ExtraPropertyDictionary(); diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/StepNode.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/StepNode.cs new file mode 100644 index 000000000..0862881d4 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/StepNode.cs @@ -0,0 +1,27 @@ +using System; +using WorkflowCore.Models; + +namespace LINGYUN.Abp.WorkflowManagement +{ + public class StepNode : Step + { + protected StepNode() + { + } + + public StepNode( + Guid id, + Guid workflowId, + string name, + string stepType, + string cancelCondition, + WorkflowErrorHandling? errorBehavior = null, + TimeSpan? retryInterval = null, + bool saga = false, + Guid? parentId = null, + Guid? tenantId = null) + : base(id, workflowId, name, stepType, cancelCondition, errorBehavior, retryInterval, saga, parentId, tenantId) + { + } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/Workflow.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/Workflow.cs index abebeb49b..098c42a82 100644 --- a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/Workflow.cs +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/Workflow.cs @@ -1,6 +1,7 @@ using System; using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; +using WorkflowCore.Models; namespace LINGYUN.Abp.WorkflowManagement { @@ -18,6 +19,10 @@ namespace LINGYUN.Abp.WorkflowManagement /// public virtual bool IsEnabled { get; protected set; } /// + /// 名称 + /// + public virtual string Name { get; set; } + /// /// 显示名称 /// public virtual string DisplayName { get; set; } @@ -30,16 +35,31 @@ namespace LINGYUN.Abp.WorkflowManagement /// public virtual int Version { get; protected set; } + public virtual WorkflowErrorHandling ErrorBehavior { get; set; } + + public virtual TimeSpan? ErrorRetryInterval { get; set; } + protected Workflow() { } public Workflow( Guid id, + string name, string displayName, string description = "", - int version = 1) : base(id) + int version = 1, + WorkflowErrorHandling errorBehavior = WorkflowErrorHandling.Retry, + TimeSpan? errorRetryInterval = null, + Guid? tenantId = null) : base(id) { + Name = name; + DisplayName = displayName; + Description = description; + Version = version; + ErrorBehavior = errorBehavior; + ErrorRetryInterval = errorRetryInterval; + TenantId = tenantId; } } } diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/WorkflowManagementDbProperties.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/WorkflowManagementDbProperties.cs index 2f85c3451..4775e8a57 100644 --- a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/WorkflowManagementDbProperties.cs +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/WorkflowManagementDbProperties.cs @@ -2,7 +2,7 @@ { public static class WorkflowManagementDbProperties { - public static string DbTablePrefix { get; set; } = "WorkflowManagement_"; + public static string DbTablePrefix { get; set; } = "WF_"; public static string DbSchema { get; set; } = null; diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/WorkflowManagementDomainModule.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/WorkflowManagementDomainModule.cs index 9e3dede8e..2bb687818 100644 --- a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/WorkflowManagementDomainModule.cs +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/WorkflowManagementDomainModule.cs @@ -21,7 +21,10 @@ namespace LINGYUN.Abp.WorkflowManagement Configure(options => { + }); + + context.Services.AddHostedService(); } } } diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/WorkflowManager.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/WorkflowManager.cs new file mode 100644 index 000000000..953bd52cc --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/WorkflowManager.cs @@ -0,0 +1,530 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Linq.Expressions; +using System.Reflection; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Services; +using WorkflowCore.Exceptions; +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Models.DefinitionStorage.v1; +using WorkflowCore.Primitives; + +namespace LINGYUN.Abp.WorkflowManagement +{ + public class WorkflowManager : DomainService, ITransientDependency + { + private readonly IWorkflowRegistry _registry; + private readonly IPersistenceProvider _persistenceProvider; + + public WorkflowManager( + IWorkflowRegistry registry, + IPersistenceProvider persistenceProvider) + { + _registry = registry; + _persistenceProvider = persistenceProvider; + } + + public virtual WorkflowDefinition Register( + Workflow workflow, + ICollection steps, + ICollection compensates) + { + var dataType = typeof(Dictionary); + var source = new DefinitionSourceV1 + { + Id = workflow.Id.ToString(), + Version = workflow.Version, + Description = workflow.Description ?? workflow.DisplayName, + DataType = $"{dataType.FullName}, {dataType.Assembly}", + DefaultErrorBehavior = workflow.ErrorBehavior, + DefaultErrorRetryInterval = workflow.ErrorRetryInterval, + Steps = ConvertSteps(steps, compensates) + }; + + var def = Convert(source); + _registry.RegisterWorkflow(def); + + return def; + } + + private List ConvertSteps( + ICollection steps, + ICollection compensates) + { + var nodes = new List(); + + foreach (var step in steps) + { + var source = new StepSourceV1 + { + Id = step.Id.ToString(), + Saga = step.Saga, + StepType = step.StepType, + CancelCondition = step.CancelCondition, + ErrorBehavior = step.ErrorBehavior, + RetryInterval = step.RetryInterval, + Name = step.Name + }; + + foreach (var input in step.Inputs) + { + source.Inputs.AddIfNotContains(input); + } + + foreach (var output in step.Outputs) + { + source.Outputs.Add(output.Key, output.Value.ToString()); + } + + foreach (var nextStep in step.SelectNextStep) + { + source.SelectNextStep.Add(nextStep.Key, nextStep.Value.ToString()); + } + + var childrenNodes = steps.Where(x => Equals(x.ParentId, step.Id)).ToArray(); + if (childrenNodes.Any()) + { + source.NextStepId = childrenNodes[0].Id.ToString(); + + nodes.AddRange(ConvertSteps(childrenNodes, compensates)); + } + + var stepCps = compensates.Where(x => Equals(x.ParentId, step.Id)).ToArray(); + if (stepCps.Any()) + { + source.CompensateWith.AddRange(ConvertCompensateSteps(stepCps)); + } + + nodes.Add(source); + } + + return nodes; + } + + private List ConvertCompensateSteps( + ICollection compensates) + { + var nodes = new List(); + + foreach (var step in compensates) + { + var source = new StepSourceV1 + { + Id = step.Id.ToString(), + Saga = step.Saga, + StepType = step.StepType, + CancelCondition = step.CancelCondition, + ErrorBehavior = step.ErrorBehavior, + RetryInterval = step.RetryInterval, + Name = step.Name + }; + + foreach (var input in step.Inputs) + { + source.Inputs.AddIfNotContains(input); + } + + foreach (var output in step.Outputs) + { + source.Outputs.Add(output.Key, output.Value.ToString()); + } + + foreach (var nextStep in step.SelectNextStep) + { + source.SelectNextStep.Add(nextStep.Key, nextStep.Value.ToString()); + } + + var stepCps = compensates.Where(x => Equals(x.ParentId, step.Id)).ToArray(); + if (stepCps.Any()) + { + source.CompensateWith.AddRange(ConvertCompensateSteps(stepCps)); + } + + nodes.Add(source); + } + + return nodes; + } + + private WorkflowDefinition Convert(DefinitionSourceV1 source) + { + var dataType = typeof(object); + if (!string.IsNullOrEmpty(source.DataType)) + dataType = FindType(source.DataType); + + var result = new WorkflowDefinition + { + Id = source.Id, + Version = source.Version, + Steps = ConvertSteps(source.Steps, dataType), + DefaultErrorBehavior = source.DefaultErrorBehavior, + DefaultErrorRetryInterval = source.DefaultErrorRetryInterval, + Description = source.Description, + DataType = dataType + }; + + return result; + } + + + private WorkflowStepCollection ConvertSteps(ICollection source, Type dataType) + { + var result = new WorkflowStepCollection(); + int i = 0; + var stack = new Stack(source.Reverse()); + var parents = new List(); + var compensatables = new List(); + + while (stack.Count > 0) + { + var nextStep = stack.Pop(); + + var stepType = FindType(nextStep.StepType); + + WorkflowStep targetStep; + + Type containerType; + if (stepType.GetInterfaces().Contains(typeof(IStepBody))) + { + containerType = typeof(WorkflowStep<>).MakeGenericType(stepType); + + targetStep = (containerType.GetConstructor(new Type[] { }).Invoke(null) as WorkflowStep); + } + else + { + targetStep = stepType.GetConstructor(new Type[] { }).Invoke(null) as WorkflowStep; + if (targetStep != null) + stepType = targetStep.BodyType; + } + + if (nextStep.Saga) + { + containerType = typeof(SagaContainer<>).MakeGenericType(stepType); + targetStep = (containerType.GetConstructor(new Type[] { }).Invoke(null) as WorkflowStep); + } + + if (!string.IsNullOrEmpty(nextStep.CancelCondition)) + { + var cancelExprType = typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(dataType, typeof(bool))); + var dataParameter = Expression.Parameter(dataType, "data"); + var cancelExpr = DynamicExpressionParser.ParseLambda(new[] { dataParameter }, typeof(bool), nextStep.CancelCondition); + targetStep.CancelCondition = cancelExpr; + } + + targetStep.Id = i; + targetStep.Name = nextStep.Name; + targetStep.ErrorBehavior = nextStep.ErrorBehavior; + targetStep.RetryInterval = nextStep.RetryInterval; + targetStep.ExternalId = $"{nextStep.Id}"; + + AttachInputs(nextStep, dataType, stepType, targetStep); + AttachOutputs(nextStep, dataType, stepType, targetStep); + + if (nextStep.Do != null) + { + foreach (var branch in nextStep.Do) + { + foreach (var child in branch.Reverse()) + stack.Push(child); + } + + if (nextStep.Do.Count > 0) + parents.Add(nextStep); + } + + if (nextStep.CompensateWith != null) + { + foreach (var compChild in nextStep.CompensateWith.Reverse()) + stack.Push(compChild); + + if (nextStep.CompensateWith.Count > 0) + compensatables.Add(nextStep); + } + + AttachOutcomes(nextStep, dataType, targetStep); + + result.Add(targetStep); + + i++; + } + + foreach (var step in result) + { + if (result.Any(x => x.ExternalId == step.ExternalId && x.Id != step.Id)) + throw new WorkflowDefinitionLoadException($"Duplicate step Id {step.ExternalId}"); + + foreach (var outcome in step.Outcomes) + { + if (result.All(x => x.ExternalId != outcome.ExternalNextStepId)) + throw new WorkflowDefinitionLoadException($"Cannot find step id {outcome.ExternalNextStepId}"); + + outcome.NextStep = result.Single(x => x.ExternalId == outcome.ExternalNextStepId).Id; + } + } + + foreach (var parent in parents) + { + var target = result.Single(x => x.ExternalId == parent.Id); + foreach (var branch in parent.Do) + { + var childTags = branch.Select(x => x.Id).ToList(); + target.Children.AddRange(result + .Where(x => childTags.Contains(x.ExternalId)) + .OrderBy(x => x.Id) + .Select(x => x.Id) + .Take(1) + .ToList()); + } + } + + foreach (var item in compensatables) + { + var target = result.Single(x => x.ExternalId == item.Id); + var tag = item.CompensateWith.Select(x => x.Id).FirstOrDefault(); + if (tag != null) + { + var compStep = result.FirstOrDefault(x => x.ExternalId == tag); + if (compStep != null) + target.CompensationStepId = compStep.Id; + } + } + + return result; + } + + private void AttachInputs(StepSourceV1 source, Type dataType, Type stepType, WorkflowStep step) + { + foreach (var input in source.Inputs) + { + var dataParameter = Expression.Parameter(dataType, "data"); + var contextParameter = Expression.Parameter(typeof(IStepExecutionContext), "context"); + var environmentVarsParameter = Expression.Parameter(typeof(IDictionary), "environment"); + var stepProperty = stepType.GetProperty(input.Key); + + if (stepProperty == null) + { + throw new ArgumentException($"Unknown property for input {input.Key} on {source.Id}"); + } + + if (input.Value is string) + { + var acn = BuildScalarInputAction(input, dataParameter, contextParameter, environmentVarsParameter, stepProperty); + step.Inputs.Add(new ActionParameter(acn)); + continue; + } + + if ((input.Value is IDictionary) || (input.Value is IDictionary)) + { + var acn = BuildObjectInputAction(input, dataParameter, contextParameter, environmentVarsParameter, stepProperty); + step.Inputs.Add(new ActionParameter(acn)); + continue; + } + + throw new ArgumentException($"Unknown type for input {input.Key} on {source.Id}"); + } + } + + private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, WorkflowStep step) + { + foreach (var output in source.Outputs) + { + var stepParameter = Expression.Parameter(stepType, "step"); + var sourceExpr = DynamicExpressionParser.ParseLambda(new[] { stepParameter }, typeof(object), output.Value); + + var dataParameter = Expression.Parameter(dataType, "data"); + + + if (output.Key.Contains(".") || output.Key.Contains("[")) + { + AttachNestedOutput(output, step, source, sourceExpr, dataParameter); + } + else + { + AttachDirectlyOutput(output, step, dataType, sourceExpr, dataParameter); + } + } + } + + private void AttachDirectlyOutput(KeyValuePair output, WorkflowStep step, Type dataType, LambdaExpression sourceExpr, ParameterExpression dataParameter) + { + Expression targetProperty; + + // Check if our datatype has a matching property + var propertyInfo = dataType.GetProperty(output.Key); + if (propertyInfo != null) + { + targetProperty = Expression.Property(dataParameter, propertyInfo); + var targetExpr = Expression.Lambda(targetProperty, dataParameter); + step.Outputs.Add(new MemberMapParameter(sourceExpr, targetExpr)); + } + else + { + // If we did not find a matching property try to find a Indexer with string parameter + propertyInfo = dataType.GetProperty("Item"); + targetProperty = Expression.Property(dataParameter, propertyInfo, Expression.Constant(output.Key)); + + Action acn = (pStep, pData) => + { + object resolvedValue = sourceExpr.Compile().DynamicInvoke(pStep); ; + propertyInfo.SetValue(pData, resolvedValue, new object[] { output.Key }); + }; + + step.Outputs.Add(new ActionParameter(acn)); + } + + } + + private void AttachNestedOutput(KeyValuePair output, WorkflowStep step, StepSourceV1 source, LambdaExpression sourceExpr, ParameterExpression dataParameter) + { + PropertyInfo propertyInfo = null; + String[] paths = output.Key.Split('.'); + + Expression targetProperty = dataParameter; + + bool hasAddOutput = false; + + foreach (String propertyName in paths) + { + if (hasAddOutput) + { + throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}"); + } + + if (targetProperty == null) + { + break; + } + + if (propertyName.Contains("[")) + { + String[] items = propertyName.Split('['); + + if (items.Length != 2) + { + throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}"); + } + + items[1] = items[1].Trim().TrimEnd(']').Trim().Trim('"'); + + MemberExpression memberExpression = Expression.Property(targetProperty, items[0]); + + if (memberExpression == null) + { + throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}"); + } + propertyInfo = ((PropertyInfo)memberExpression.Member).PropertyType.GetProperty("Item"); + + Action acn = (pStep, pData) => + { + var targetExpr = Expression.Lambda(memberExpression, dataParameter); + object data = targetExpr.Compile().DynamicInvoke(pData); + object resolvedValue = sourceExpr.Compile().DynamicInvoke(pStep); ; + propertyInfo.SetValue(data, resolvedValue, new object[] { items[1] }); + }; + + step.Outputs.Add(new ActionParameter(acn)); + hasAddOutput = true; + } + else + { + try + { + targetProperty = Expression.Property(targetProperty, propertyName); + } + catch + { + targetProperty = null; + break; + } + } + } + + if (targetProperty != null && !hasAddOutput) + { + var targetExpr = Expression.Lambda(targetProperty, dataParameter); + step.Outputs.Add(new MemberMapParameter(sourceExpr, targetExpr)); + } + } + + private void AttachOutcomes(StepSourceV1 source, Type dataType, WorkflowStep step) + { + if (!string.IsNullOrEmpty(source.NextStepId)) + step.Outcomes.Add(new ValueOutcome { ExternalNextStepId = $"{source.NextStepId}" }); + + var dataParameter = Expression.Parameter(dataType, "data"); + var outcomeParameter = Expression.Parameter(typeof(object), "outcome"); + + foreach (var nextStep in source.SelectNextStep) + { + var sourceDelegate = DynamicExpressionParser.ParseLambda(new[] { dataParameter, outcomeParameter }, typeof(object), nextStep.Value).Compile(); + Expression> sourceExpr = (data, outcome) => System.Convert.ToBoolean(sourceDelegate.DynamicInvoke(data, outcome)); + step.Outcomes.Add(new ExpressionOutcome(sourceExpr) + { + ExternalNextStepId = $"{nextStep.Key}" + }); + } + } + + private Type FindType(string name) + { + return Type.GetType(name, true, true); + } + + private static Action BuildScalarInputAction(KeyValuePair input, ParameterExpression dataParameter, ParameterExpression contextParameter, ParameterExpression environmentVarsParameter, PropertyInfo stepProperty) + { + var expr = System.Convert.ToString(input.Value); + var sourceExpr = DynamicExpressionParser.ParseLambda(new[] { dataParameter, contextParameter, environmentVarsParameter }, typeof(object), expr); + + void acn(IStepBody pStep, object pData, IStepExecutionContext pContext) + { + object resolvedValue = sourceExpr.Compile().DynamicInvoke(pData, pContext, Environment.GetEnvironmentVariables()); + if (stepProperty.PropertyType.IsEnum) + stepProperty.SetValue(pStep, Enum.Parse(stepProperty.PropertyType, (string)resolvedValue, true)); + else + { + if ((resolvedValue != null) && (stepProperty.PropertyType.IsAssignableFrom(resolvedValue.GetType()))) + stepProperty.SetValue(pStep, resolvedValue); + else + stepProperty.SetValue(pStep, System.Convert.ChangeType(resolvedValue, stepProperty.PropertyType)); + } + } + return acn; + } + + private static Action BuildObjectInputAction(KeyValuePair input, ParameterExpression dataParameter, ParameterExpression contextParameter, ParameterExpression environmentVarsParameter, PropertyInfo stepProperty) + { + void acn(IStepBody pStep, object pData, IStepExecutionContext pContext) + { + var stack = new Stack(); + var destObj = JObject.FromObject(input.Value); + stack.Push(destObj); + + while (stack.Count > 0) + { + var subobj = stack.Pop(); + foreach (var prop in subobj.Properties().ToList()) + { + if (prop.Name.StartsWith("@")) + { + var sourceExpr = DynamicExpressionParser.ParseLambda(new[] { dataParameter, contextParameter, environmentVarsParameter }, typeof(object), prop.Value.ToString()); + object resolvedValue = sourceExpr.Compile().DynamicInvoke(pData, pContext, Environment.GetEnvironmentVariables()); + subobj.Remove(prop.Name); + subobj.Add(prop.Name.TrimStart('@'), JToken.FromObject(resolvedValue)); + } + } + + foreach (var child in subobj.Children()) + stack.Push(child); + } + + stepProperty.SetValue(pStep, destObj); + } + return acn; + } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/WorkflowRegisterService.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/WorkflowRegisterService.cs new file mode 100644 index 000000000..cdeb7b52f --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Domain/LINGYUN/Abp/WorkflowManagement/WorkflowRegisterService.cs @@ -0,0 +1,40 @@ +using Microsoft.Extensions.Hosting; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WorkflowManagement +{ + public class WorkflowRegisterService : BackgroundService + { + private readonly WorkflowManager _workflowManager; + + private readonly IWorkflowRepository _workflowRepository; + private readonly IStepNodeRepository _stepNodeRepository; + private readonly ICompensateNodeRepository _compensateNodeRepository; + + public WorkflowRegisterService( + WorkflowManager workflowManager, + IWorkflowRepository workflowRepository, + IStepNodeRepository stepNodeRepository, + ICompensateNodeRepository compensateNodeRepository) + { + _workflowManager = workflowManager; + _workflowRepository = workflowRepository; + _stepNodeRepository = stepNodeRepository; + _compensateNodeRepository = compensateNodeRepository; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + var workflows = await _workflowRepository.GetListAsync(cancellationToken: stoppingToken); + + foreach (var workflow in workflows) + { + var stepNodes = await _stepNodeRepository.GetAllChildrenWithWorkflowAsync(workflow.Id, cancellationToken: stoppingToken); + var compensateNodes = await _compensateNodeRepository.GetAllChildrenWithWorkflowAsync(workflow.Id, cancellationToken: stoppingToken); + + _workflowManager.Register(workflow, stepNodes, compensateNodes); + } + } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/EfCoreCompensateNodeRepository.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/EfCoreCompensateNodeRepository.cs new file mode 100644 index 000000000..6c5e69367 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/EfCoreCompensateNodeRepository.cs @@ -0,0 +1,27 @@ +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.WorkflowManagement.EntityFrameworkCore +{ + public class EfCoreCompensateNodeRepository : EfCoreRepository, ICompensateNodeRepository + { + public EfCoreCompensateNodeRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public virtual async Task> GetAllChildrenWithWorkflowAsync(Guid workflowId, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .Where(x => x.WorkflowId.Equals(workflowId)) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/EfCoreStepNodeRepository.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/EfCoreStepNodeRepository.cs new file mode 100644 index 000000000..96c3d43ea --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/EfCoreStepNodeRepository.cs @@ -0,0 +1,27 @@ +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.WorkflowManagement.EntityFrameworkCore +{ + public class EfCoreStepNodeRepository : EfCoreRepository, IStepNodeRepository + { + public EfCoreStepNodeRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public virtual async Task> GetAllChildrenWithWorkflowAsync(Guid workflowId, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .Where(x => x.WorkflowId.Equals(workflowId)) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/EfCoreWorkflowRepository.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/EfCoreWorkflowRepository.cs new file mode 100644 index 000000000..aff26025c --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/EfCoreWorkflowRepository.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore +{ + public class EfCoreWorkflowRepository : EfCoreRepository, IWorkflowRepository + { + public EfCoreWorkflowRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public virtual async Task CheckVersionAsync(string name, int version, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .AnyAsync(x => x.Name.Equals(name) && x.Version == version, + GetCancellationToken(cancellationToken)); + } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/WorkflowManagementDbContextModelCreatingExtensions.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/WorkflowManagementDbContextModelCreatingExtensions.cs index 7d88cc563..519c03fb8 100644 --- a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/WorkflowManagementDbContextModelCreatingExtensions.cs +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/WorkflowManagementDbContextModelCreatingExtensions.cs @@ -1,6 +1,10 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; using System; using Volo.Abp; +using Volo.Abp.EntityFrameworkCore.Modeling; +using Volo.Abp.EntityFrameworkCore.ValueComparers; +using Volo.Abp.EntityFrameworkCore.ValueConverters; namespace LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore { @@ -17,6 +21,52 @@ namespace LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore WorkflowManagementDbProperties.DbSchema ); optionsAction?.Invoke(options); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "Definition", options.Schema); + + b.Property(p => p.DisplayName).HasMaxLength(WorkflowConsts.MaxDisplayNameLength); + b.Property(p => p.Name).HasMaxLength(WorkflowConsts.MaxNameLength).IsRequired(); + b.Property(p => p.Description).HasMaxLength(WorkflowConsts.MaxDescriptionLength); + + b.ConfigureByConvention(); + }); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "Step", options.Schema); + + b.ConfigureStep(); + b.ConfigureByConvention(); + }); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "Compensate", options.Schema); + + b.ConfigureStep(); + b.ConfigureByConvention(); + }); + } + + public static void ConfigureStep( + this EntityTypeBuilder builder) + where TStep : Step + { + builder.Property(p => p.CancelCondition).HasMaxLength(WorkflowConsts.MaxCancelConditionLength); + builder.Property(p => p.Name).HasMaxLength(WorkflowConsts.MaxNameLength); + builder.Property(p => p.StepType).HasMaxLength(WorkflowConsts.MaxStepTypeLength); + + builder.Property(p => p.Inputs) + .HasConversion(new ExtraPropertiesValueConverter(builder.Metadata.ClrType)) + .Metadata.SetValueComparer(new ExtraPropertyDictionaryValueComparer()); + builder.Property(p => p.Outputs) + .HasConversion(new ExtraPropertiesValueConverter(builder.Metadata.ClrType)) + .Metadata.SetValueComparer(new ExtraPropertyDictionaryValueComparer()); + builder.Property(p => p.SelectNextStep) + .HasConversion(new ExtraPropertiesValueConverter(builder.Metadata.ClrType)) + .Metadata.SetValueComparer(new ExtraPropertyDictionaryValueComparer()); } } } diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/WorkflowManagementEntityFrameworkCoreModule.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/WorkflowManagementEntityFrameworkCoreModule.cs index 424a5e701..f41fe9523 100644 --- a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/WorkflowManagementEntityFrameworkCoreModule.cs +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore/LINGYUN/Abp/WorkflowManagement/EntityFrameworkCore/WorkflowManagementEntityFrameworkCoreModule.cs @@ -13,7 +13,9 @@ namespace LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore { context.Services.AddAbpDbContext(options => { - options.AddDefaultRepositories(includeAllEntities: true); + options.AddRepository(); + options.AddRepository(); + options.AddRepository(); }); } } diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.HttpApi/LINGYUN/Abp/WorkflowManagement/Activitys/ActivityController.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.HttpApi/LINGYUN/Abp/WorkflowManagement/Activitys/ActivityController.cs new file mode 100644 index 000000000..12e81bc2a --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.HttpApi/LINGYUN/Abp/WorkflowManagement/Activitys/ActivityController.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.AspNetCore.Mvc; + +namespace LINGYUN.Abp.WorkflowManagement.Activitys +{ + [RemoteService(Name = WorkflowManagementRemoteServiceConsts.RemoteServiceName)] + [Area("WorkflowManagement")] + [Route("api/workflow-management/activitys")] + public class ActivityController : AbpControllerBase, IActivityAppService + { + private readonly IActivityAppService _service; + + public ActivityController(IActivityAppService service) + { + _service = service; + } + + [HttpPost("fail/{token}")] + public virtual async Task FailureAsync(ActivityFailureInput input) + { + await _service.FailureAsync(input); + } + + [HttpGet("{ActivityName}")] + public virtual async Task GetAsync(GetPendingActivityInput input) + { + return await _service.GetAsync(input); + } + + [HttpDelete("{Token}")] + public virtual async Task DeleteAsync(ActivityReleaseInput input) + { + await _service.DeleteAsync(input); + } + + [HttpPost("success/{Token}")] + public virtual async Task SuccessAsync(ActivitySuccessInput input) + { + await _service.SuccessAsync(input); + } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.HttpApi/LINGYUN/Abp/WorkflowManagement/Events/EventController.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.HttpApi/LINGYUN/Abp/WorkflowManagement/Events/EventController.cs new file mode 100644 index 000000000..fc3ef545f --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.HttpApi/LINGYUN/Abp/WorkflowManagement/Events/EventController.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.AspNetCore.Mvc; + +namespace LINGYUN.Abp.WorkflowManagement.Events +{ + [RemoteService(Name = WorkflowManagementRemoteServiceConsts.RemoteServiceName)] + [Area("WorkflowManagement")] + [Route("api/workflow-management/events")] + public class EventController : AbpControllerBase, IEventAppService + { + private readonly IEventAppService _service; + + public EventController(IEventAppService service) + { + _service = service; + } + + [HttpPost] + [Route("{EventName}/{EventKey}")] + public virtual async Task PublishAsync(EventPublishInput input) + { + await _service.PublishAsync(input); + } + } +} diff --git a/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.HttpApi/LINGYUN/Abp/WorkflowManagement/Workflows/WorkflowController.cs b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.HttpApi/LINGYUN/Abp/WorkflowManagement/Workflows/WorkflowController.cs new file mode 100644 index 000000000..d7c984c77 --- /dev/null +++ b/aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.HttpApi/LINGYUN/Abp/WorkflowManagement/Workflows/WorkflowController.cs @@ -0,0 +1,61 @@ +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.AspNetCore.Mvc; + +namespace LINGYUN.Abp.WorkflowManagement.Workflows +{ + [RemoteService(Name = WorkflowManagementRemoteServiceConsts.RemoteServiceName)] + [Area("WorkflowManagement")] + [Route("api/workflow-management/workflows")] + public class WorkflowController : AbpControllerBase, IWorkflowAppService + { + private readonly IWorkflowAppService _service; + + public WorkflowController(IWorkflowAppService service) + { + _service = service; + } + + [HttpGet] + [Route("{id}")] + public virtual async Task GetAsync(string id) + { + return await _service.GetAsync(id); + } + + [HttpPut] + [Route("{id}/resume")] + public virtual async Task ResumeAsync(string id) + { + await _service.ResumeAsync(id); + } + + [HttpPost] + public virtual async Task CreateAsync(WorkflowCreateDto input) + { + await _service.CreateAsync(input); + } + + [HttpPost] + [Route("{id}/start")] + public virtual async Task StartAsync(string id, WorkflowStartInput input) + { + return await _service.StartAsync(id, input); + } + + [HttpPut] + [Route("{id}/suspend")] + public virtual async Task SuspendAsync(string id) + { + await _service.SuspendAsync(id); + } + + [HttpPut] + [Route("{id}/terminate")] + public virtual async Task TerminateAsync(string id) + { + await _service.TerminateAsync(id); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Controllers/HomeController.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Controllers/HomeController.cs new file mode 100644 index 000000000..2c1d4b35f --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Controllers/HomeController.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; + +namespace LY.MicroService.WorkflowManagement.Controllers +{ + public class HomeController : AbpController + { + public IActionResult Index() + { + return Redirect("/swagger/index.html"); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Dockerfile b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Dockerfile new file mode 100644 index 000000000..b7b9dc6cf --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Dockerfile @@ -0,0 +1,15 @@ +FROM mcr.microsoft.com/dotnet/aspnet:5.0 +LABEL maintainer="colin.in@foxmail.com" +WORKDIR /app + +COPY . /app + +#�Ϻ�ʱ�� +#ENV TZ=Asia/Shanghai +#RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone + +EXPOSE 80/tcp +VOLUME [ "./app/Logs" ] +VOLUME [ "./app/Modules" ] + +ENTRYPOINT ["dotnet", "LY.MicroService.WorkflowManagement.HttpApi.Host.dll"] diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/EntityFrameworkCore/WorkflowManagementMigrationsDbContext.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/EntityFrameworkCore/WorkflowManagementMigrationsDbContext.cs new file mode 100644 index 000000000..9428da377 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/EntityFrameworkCore/WorkflowManagementMigrationsDbContext.cs @@ -0,0 +1,24 @@ +using LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore; +using LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace LY.MicroService.WorkflowManagement.EntityFrameworkCore +{ + public class WorkflowManagementMigrationsDbContext : AbpDbContext + { + public WorkflowManagementMigrationsDbContext(DbContextOptions options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.ConfigureWorkflow(); + modelBuilder.ConfigureWorkflowManagement(); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/EntityFrameworkCore/WorkflowManagementMigrationsDbContextFactory.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/EntityFrameworkCore/WorkflowManagementMigrationsDbContextFactory.cs new file mode 100644 index 000000000..4c461c150 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/EntityFrameworkCore/WorkflowManagementMigrationsDbContextFactory.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; +using System.IO; + +namespace LY.MicroService.WorkflowManagement.EntityFrameworkCore +{ + public class WorkflowManagementMigrationsDbContextFactory : IDesignTimeDbContextFactory + { + public WorkflowManagementMigrationsDbContext CreateDbContext(string[] args) + { + var configuration = BuildConfiguration(); + var connectionString = configuration.GetConnectionString("WorkflowManagement"); + + var builder = new DbContextOptionsBuilder() + .UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)); + + return new WorkflowManagementMigrationsDbContext(builder.Options); + } + + private static IConfigurationRoot BuildConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false) + .AddJsonFile("appsettings.Development.json", optional: true); + + return builder.Build(); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs new file mode 100644 index 000000000..84bdf5918 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs @@ -0,0 +1,70 @@ +using LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore; +using LINGYUN.Abp.Data.DbMigrator; +using LINGYUN.Abp.MultiTenancy; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Uow; + +namespace LY.MicroService.WorkflowManagement.EventBus.Handlers +{ + public class TenantSynchronizer : + IDistributedEventHandler, + ITransientDependency + { + protected IDataSeeder DataSeeder { get; } + protected ICurrentTenant CurrentTenant { get; } + protected IDbSchemaMigrator DbSchemaMigrator { get; } + protected IUnitOfWorkManager UnitOfWorkManager { get; } + + protected ILogger Logger { get; } + + public TenantSynchronizer( + IDataSeeder dataSeeder, + ICurrentTenant currentTenant, + IDbSchemaMigrator dbSchemaMigrator, + IUnitOfWorkManager unitOfWorkManager, + ILogger logger) + { + DataSeeder = dataSeeder; + CurrentTenant = currentTenant; + DbSchemaMigrator = dbSchemaMigrator; + UnitOfWorkManager = unitOfWorkManager; + + Logger = logger; + } + + /// + /// 租户创建之后需要预置种子数据 + /// + /// + /// + public virtual async Task HandleEventAsync(CreateEventData eventData) + { + using (var unitOfWork = UnitOfWorkManager.Begin()) + { + using (CurrentTenant.Change(eventData.Id, eventData.Name)) + { + Logger.LogInformation("Migrating the new tenant database with WorkflowManagement..."); + // 迁移租户数据 + await DbSchemaMigrator.MigrateAsync( + (connectionString, builder) => + { + builder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)); + + return new WorkflowManagementDbContext(builder.Options); + }); + Logger.LogInformation("Migrated the new tenant database with WorkflowManagement..."); + + await DataSeeder.SeedAsync(new DataSeedContext(eventData.Id)); + + await unitOfWork.SaveChangesAsync(); + } + } + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/LY.MicroService.WorkflowManagement.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/LY.MicroService.WorkflowManagement.HttpApi.Host.csproj new file mode 100644 index 000000000..87aa55abd --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/LY.MicroService.WorkflowManagement.HttpApi.Host.csproj @@ -0,0 +1,58 @@ + + + + net6.0 + LY.MicroService.WorkflowManagement + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Migrations/20211212074420_Add-Module-Workflow-Management.Designer.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Migrations/20211212074420_Add-Module-Workflow-Management.Designer.cs new file mode 100644 index 000000000..464735504 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Migrations/20211212074420_Add-Module-Workflow-Management.Designer.cs @@ -0,0 +1,557 @@ +// +using System; +using LY.MicroService.WorkflowManagement.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LY.MicroService.WorkflowManagement.Migrations +{ + [DbContext(typeof(WorkflowManagementMigrationsDbContext))] + [Migration("20211212074420_Add-Module-Workflow-Management")] + partial class AddModuleWorkflowManagement + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.MySql) + .HasAnnotation("ProductVersion", "6.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("EventData") + .HasColumnType("longtext"); + + b.Property("EventKey") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EventName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IsProcessed") + .HasColumnType("tinyint(1)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("CreationTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("WF_Event", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ErrorTime") + .HasColumnType("datetime(6)"); + + b.Property("ExecutionPointerId") + .HasMaxLength(50) + .HasColumnType("char(50)"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("WorkflowId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("WF_ExecutionError", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionPointer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasMaxLength(50) + .HasColumnType("char(50)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Children") + .HasColumnType("longtext"); + + b.Property("ContextItem") + .HasColumnType("longtext"); + + b.Property("EndTime") + .HasColumnType("datetime(6)"); + + b.Property("EventData") + .HasColumnType("longtext"); + + b.Property("EventKey") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EventName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EventPublished") + .HasColumnType("tinyint(1)"); + + b.Property("Outcome") + .HasColumnType("longtext"); + + b.Property("PersistenceData") + .HasColumnType("longtext"); + + b.Property("PredecessorId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RetryCount") + .HasColumnType("int"); + + b.Property("Scope") + .HasColumnType("longtext"); + + b.Property("SleepUntil") + .HasColumnType("datetime(6)"); + + b.Property("StartTime") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("StepName") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("WorkflowId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("WorkflowId"); + + b.ToTable("WF_ExecutionPointer", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExtensionAttribute", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ExecutionPointerId") + .HasMaxLength(50) + .HasColumnType("char(50)"); + + b.Property("Key") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("WF_ExtensionAttribute", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedScheduledCommand", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CommandName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Data") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ExecuteTime") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ExecuteTime"); + + b.HasIndex("CommandName", "Data") + .IsUnique(); + + b.ToTable("WF_ScheduledCommand", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("EventKey") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EventName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExecutionPointerId") + .HasMaxLength(50) + .HasColumnType("char(50)"); + + b.Property("ExternalToken") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExternalTokenExpiry") + .HasColumnType("datetime(6)"); + + b.Property("ExternalWorkerId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("SubscribeAsOf") + .HasColumnType("datetime(6)"); + + b.Property("SubscriptionData") + .HasColumnType("longtext"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("WorkflowId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.ToTable("WF_Subscription", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedWorkflow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CompleteTime") + .HasColumnType("datetime(6)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("CreatorId"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ExtraProperties") + .HasColumnType("longtext") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("datetime(6)") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("char(36)") + .HasColumnName("LastModifierId"); + + b.Property("NextExecution") + .HasColumnType("bigint"); + + b.Property("Reference") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("Version") + .HasColumnType("int"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("NextExecution"); + + b.ToTable("WF_Workflow", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowManagement.CompensateNode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CancelCondition") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ErrorBehavior") + .HasColumnType("int"); + + b.Property("Inputs") + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Outputs") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("RetryInterval") + .HasColumnType("time(6)"); + + b.Property("Saga") + .HasColumnType("tinyint(1)"); + + b.Property("SelectNextStep") + .HasColumnType("longtext"); + + b.Property("StepType") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("WorkflowId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("WF_Compensate", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowManagement.StepNode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CancelCondition") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ErrorBehavior") + .HasColumnType("int"); + + b.Property("Inputs") + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Outputs") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("RetryInterval") + .HasColumnType("time(6)"); + + b.Property("Saga") + .HasColumnType("tinyint(1)"); + + b.Property("SelectNextStep") + .HasColumnType("longtext"); + + b.Property("StepType") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("WorkflowId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("WF_Step", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowManagement.Workflow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ErrorBehavior") + .HasColumnType("int"); + + b.Property("ErrorRetryInterval") + .HasColumnType("time(6)"); + + b.Property("ExtraProperties") + .HasColumnType("longtext") + .HasColumnName("ExtraProperties"); + + b.Property("IsEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LastModificationTime") + .HasColumnType("datetime(6)") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("char(36)") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("WF_Definition", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionPointer", b => + { + b.HasOne("LINGYUN.Abp.WorkflowCore.Persistence.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Workflow"); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExtensionAttribute", b => + { + b.HasOne("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ExecutionPointer"); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionPointer", b => + { + b.Navigation("ExtensionAttributes"); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedWorkflow", b => + { + b.Navigation("ExecutionPointers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Migrations/20211212074420_Add-Module-Workflow-Management.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Migrations/20211212074420_Add-Module-Workflow-Management.cs new file mode 100644 index 000000000..e6abb6314 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Migrations/20211212074420_Add-Module-Workflow-Management.cs @@ -0,0 +1,385 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LY.MicroService.WorkflowManagement.Migrations +{ + public partial class AddModuleWorkflowManagement : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WF_Compensate", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + TenantId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + WorkflowId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + StepType = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + CancelCondition = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ErrorBehavior = table.Column(type: "int", nullable: true), + RetryInterval = table.Column(type: "time(6)", nullable: true), + Saga = table.Column(type: "tinyint(1)", nullable: false), + ParentId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + Inputs = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Outputs = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SelectNextStep = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_WF_Compensate", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WF_Definition", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + TenantId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + IsEnabled = table.Column(type: "tinyint(1)", nullable: false), + Name = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + DisplayName = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Description = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "int", nullable: false), + ErrorBehavior = table.Column(type: "int", nullable: false), + ErrorRetryInterval = table.Column(type: "time(6)", nullable: true), + ExtraProperties = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "varchar(40)", maxLength: 40, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + CreationTime = table.Column(type: "datetime(6)", nullable: false), + CreatorId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + LastModificationTime = table.Column(type: "datetime(6)", nullable: true), + LastModifierId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK_WF_Definition", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WF_Event", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + TenantId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + EventName = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EventKey = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EventData = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsProcessed = table.Column(type: "tinyint(1)", nullable: false), + CreationTime = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_WF_Event", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WF_ExecutionError", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + TenantId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + WorkflowId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ExecutionPointerId = table.Column(type: "char(50)", maxLength: 50, nullable: false, collation: "ascii_general_ci"), + ErrorTime = table.Column(type: "datetime(6)", nullable: false), + Message = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_WF_ExecutionError", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WF_ScheduledCommand", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + TenantId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + CommandName = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Data = table.Column(type: "varchar(500)", maxLength: 500, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ExecuteTime = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_WF_ScheduledCommand", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WF_Step", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + TenantId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + WorkflowId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + StepType = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + CancelCondition = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ErrorBehavior = table.Column(type: "int", nullable: true), + RetryInterval = table.Column(type: "time(6)", nullable: true), + Saga = table.Column(type: "tinyint(1)", nullable: false), + ParentId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + Inputs = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Outputs = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SelectNextStep = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_WF_Step", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WF_Subscription", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + TenantId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + WorkflowId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + StepId = table.Column(type: "int", nullable: false), + ExecutionPointerId = table.Column(type: "char(50)", maxLength: 50, nullable: false, collation: "ascii_general_ci"), + EventName = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EventKey = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SubscribeAsOf = table.Column(type: "datetime(6)", nullable: false), + SubscriptionData = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ExternalToken = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ExternalWorkerId = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ExternalTokenExpiry = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_WF_Subscription", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WF_Workflow", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + TenantId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + WorkflowDefinitionId = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "int", nullable: false), + Description = table.Column(type: "varchar(500)", maxLength: 500, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Reference = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NextExecution = table.Column(type: "bigint", nullable: true), + Status = table.Column(type: "int", nullable: false), + Data = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + CompleteTime = table.Column(type: "datetime(6)", nullable: true), + ExtraProperties = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "varchar(40)", maxLength: 40, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + CreationTime = table.Column(type: "datetime(6)", nullable: false), + CreatorId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + LastModificationTime = table.Column(type: "datetime(6)", nullable: true), + LastModifierId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK_WF_Workflow", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WF_ExecutionPointer", + columns: table => new + { + Id = table.Column(type: "char(50)", maxLength: 50, nullable: false, collation: "ascii_general_ci"), + TenantId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + WorkflowId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + StepId = table.Column(type: "int", nullable: false), + Active = table.Column(type: "tinyint(1)", nullable: false), + SleepUntil = table.Column(type: "datetime(6)", nullable: true), + PersistenceData = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + StartTime = table.Column(type: "datetime(6)", nullable: true), + EndTime = table.Column(type: "datetime(6)", nullable: true), + EventName = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EventKey = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EventPublished = table.Column(type: "tinyint(1)", nullable: false), + EventData = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + StepName = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + RetryCount = table.Column(type: "int", nullable: false), + Children = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ContextItem = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PredecessorId = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Outcome = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Status = table.Column(type: "int", nullable: false), + Scope = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_WF_ExecutionPointer", x => x.Id); + table.ForeignKey( + name: "FK_WF_ExecutionPointer_WF_Workflow_WorkflowId", + column: x => x.WorkflowId, + principalTable: "WF_Workflow", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WF_ExtensionAttribute", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + TenantId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + ExecutionPointerId = table.Column(type: "char(50)", maxLength: 50, nullable: false, collation: "ascii_general_ci"), + Key = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_WF_ExtensionAttribute", x => x.Id); + table.ForeignKey( + name: "FK_WF_ExtensionAttribute_WF_ExecutionPointer_ExecutionPointerId", + column: x => x.ExecutionPointerId, + principalTable: "WF_ExecutionPointer", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_WF_Event_CreationTime", + table: "WF_Event", + column: "CreationTime"); + + migrationBuilder.CreateIndex( + name: "IX_WF_Event_EventName_EventKey", + table: "WF_Event", + columns: new[] { "EventName", "EventKey" }); + + migrationBuilder.CreateIndex( + name: "IX_WF_Event_IsProcessed", + table: "WF_Event", + column: "IsProcessed"); + + migrationBuilder.CreateIndex( + name: "IX_WF_ExecutionPointer_WorkflowId", + table: "WF_ExecutionPointer", + column: "WorkflowId"); + + migrationBuilder.CreateIndex( + name: "IX_WF_ExtensionAttribute_ExecutionPointerId", + table: "WF_ExtensionAttribute", + column: "ExecutionPointerId"); + + migrationBuilder.CreateIndex( + name: "IX_WF_ScheduledCommand_CommandName_Data", + table: "WF_ScheduledCommand", + columns: new[] { "CommandName", "Data" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_WF_ScheduledCommand_ExecuteTime", + table: "WF_ScheduledCommand", + column: "ExecuteTime"); + + migrationBuilder.CreateIndex( + name: "IX_WF_Subscription_EventKey", + table: "WF_Subscription", + column: "EventKey"); + + migrationBuilder.CreateIndex( + name: "IX_WF_Subscription_EventName", + table: "WF_Subscription", + column: "EventName"); + + migrationBuilder.CreateIndex( + name: "IX_WF_Workflow_NextExecution", + table: "WF_Workflow", + column: "NextExecution"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "WF_Compensate"); + + migrationBuilder.DropTable( + name: "WF_Definition"); + + migrationBuilder.DropTable( + name: "WF_Event"); + + migrationBuilder.DropTable( + name: "WF_ExecutionError"); + + migrationBuilder.DropTable( + name: "WF_ExtensionAttribute"); + + migrationBuilder.DropTable( + name: "WF_ScheduledCommand"); + + migrationBuilder.DropTable( + name: "WF_Step"); + + migrationBuilder.DropTable( + name: "WF_Subscription"); + + migrationBuilder.DropTable( + name: "WF_ExecutionPointer"); + + migrationBuilder.DropTable( + name: "WF_Workflow"); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Migrations/WorkflowManagementMigrationsDbContextModelSnapshot.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Migrations/WorkflowManagementMigrationsDbContextModelSnapshot.cs new file mode 100644 index 000000000..b99116af3 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Migrations/WorkflowManagementMigrationsDbContextModelSnapshot.cs @@ -0,0 +1,555 @@ +// +using System; +using LY.MicroService.WorkflowManagement.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace LY.MicroService.WorkflowManagement.Migrations +{ + [DbContext(typeof(WorkflowManagementMigrationsDbContext))] + partial class WorkflowManagementMigrationsDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.MySql) + .HasAnnotation("ProductVersion", "6.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("EventData") + .HasColumnType("longtext"); + + b.Property("EventKey") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EventName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IsProcessed") + .HasColumnType("tinyint(1)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("CreationTime"); + + b.HasIndex("IsProcessed"); + + b.HasIndex("EventName", "EventKey"); + + b.ToTable("WF_Event", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ErrorTime") + .HasColumnType("datetime(6)"); + + b.Property("ExecutionPointerId") + .HasMaxLength(50) + .HasColumnType("char(50)"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("WorkflowId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("WF_ExecutionError", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionPointer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasMaxLength(50) + .HasColumnType("char(50)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Children") + .HasColumnType("longtext"); + + b.Property("ContextItem") + .HasColumnType("longtext"); + + b.Property("EndTime") + .HasColumnType("datetime(6)"); + + b.Property("EventData") + .HasColumnType("longtext"); + + b.Property("EventKey") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EventName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EventPublished") + .HasColumnType("tinyint(1)"); + + b.Property("Outcome") + .HasColumnType("longtext"); + + b.Property("PersistenceData") + .HasColumnType("longtext"); + + b.Property("PredecessorId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RetryCount") + .HasColumnType("int"); + + b.Property("Scope") + .HasColumnType("longtext"); + + b.Property("SleepUntil") + .HasColumnType("datetime(6)"); + + b.Property("StartTime") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("StepName") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("WorkflowId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("WorkflowId"); + + b.ToTable("WF_ExecutionPointer", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExtensionAttribute", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ExecutionPointerId") + .HasMaxLength(50) + .HasColumnType("char(50)"); + + b.Property("Key") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ExecutionPointerId"); + + b.ToTable("WF_ExtensionAttribute", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedScheduledCommand", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CommandName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Data") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ExecuteTime") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ExecuteTime"); + + b.HasIndex("CommandName", "Data") + .IsUnique(); + + b.ToTable("WF_ScheduledCommand", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("EventKey") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EventName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExecutionPointerId") + .HasMaxLength(50) + .HasColumnType("char(50)"); + + b.Property("ExternalToken") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExternalTokenExpiry") + .HasColumnType("datetime(6)"); + + b.Property("ExternalWorkerId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("StepId") + .HasColumnType("int"); + + b.Property("SubscribeAsOf") + .HasColumnType("datetime(6)"); + + b.Property("SubscriptionData") + .HasColumnType("longtext"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("WorkflowId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("EventKey"); + + b.HasIndex("EventName"); + + b.ToTable("WF_Subscription", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedWorkflow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CompleteTime") + .HasColumnType("datetime(6)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("CreatorId"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ExtraProperties") + .HasColumnType("longtext") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("datetime(6)") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("char(36)") + .HasColumnName("LastModifierId"); + + b.Property("NextExecution") + .HasColumnType("bigint"); + + b.Property("Reference") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("Version") + .HasColumnType("int"); + + b.Property("WorkflowDefinitionId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("NextExecution"); + + b.ToTable("WF_Workflow", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowManagement.CompensateNode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CancelCondition") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ErrorBehavior") + .HasColumnType("int"); + + b.Property("Inputs") + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Outputs") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("RetryInterval") + .HasColumnType("time(6)"); + + b.Property("Saga") + .HasColumnType("tinyint(1)"); + + b.Property("SelectNextStep") + .HasColumnType("longtext"); + + b.Property("StepType") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("WorkflowId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("WF_Compensate", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowManagement.StepNode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CancelCondition") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ErrorBehavior") + .HasColumnType("int"); + + b.Property("Inputs") + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Outputs") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("RetryInterval") + .HasColumnType("time(6)"); + + b.Property("Saga") + .HasColumnType("tinyint(1)"); + + b.Property("SelectNextStep") + .HasColumnType("longtext"); + + b.Property("StepType") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("WorkflowId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("WF_Step", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowManagement.Workflow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime(6)") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ErrorBehavior") + .HasColumnType("int"); + + b.Property("ErrorRetryInterval") + .HasColumnType("time(6)"); + + b.Property("ExtraProperties") + .HasColumnType("longtext") + .HasColumnName("ExtraProperties"); + + b.Property("IsEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LastModificationTime") + .HasColumnType("datetime(6)") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("char(36)") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TenantId") + .HasColumnType("char(36)") + .HasColumnName("TenantId"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("WF_Definition", (string)null); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionPointer", b => + { + b.HasOne("LINGYUN.Abp.WorkflowCore.Persistence.PersistedWorkflow", "Workflow") + .WithMany("ExecutionPointers") + .HasForeignKey("WorkflowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Workflow"); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExtensionAttribute", b => + { + b.HasOne("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionPointer", "ExecutionPointer") + .WithMany("ExtensionAttributes") + .HasForeignKey("ExecutionPointerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ExecutionPointer"); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionPointer", b => + { + b.Navigation("ExtensionAttributes"); + }); + + modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedWorkflow", b => + { + b.Navigation("ExecutionPointers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Program.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Program.cs new file mode 100644 index 000000000..59bc1aec9 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Program.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Serilog; + +namespace LY.MicroService.WorkflowManagement +{ + public class Program + { + public static int Main(string[] args) + { + try + { + var host = CreateHostBuilder(args).Build(); + Log.Information("Starting web host."); + host.Run(); + return 0; + } + finally + { + Log.CloseAndFlush(); + } + } + + internal static IHostBuilder CreateHostBuilder(string[] args) => + Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }) + .ConfigureAppConfiguration((context, config) => + { + var configuration = config.Build(); + if (configuration.GetSection("AgileConfig").Exists()) + { + config.AddAgileConfig(new AgileConfig.Client.ConfigClient(configuration)); + } + }) + .UseSerilog((context, config) => + { + config.ReadFrom.Configuration(context.Configuration); + }) + .UseAutofac(); + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Properties/launchSettings.json b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Properties/launchSettings.json new file mode 100644 index 000000000..cdb3a8756 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Properties/launchSettings.json @@ -0,0 +1,21 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:20890", + "sslPort": 0 + } + }, + "profiles": { + "LINGYUN.Abp.WorkflowManagement.HttpApi.Host": { + "commandName": "Project", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://127.0.0.1:30035", + "dotnetRunMessages": "true" + } + } +} \ No newline at end of file diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Startup.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Startup.cs new file mode 100644 index 000000000..43b1a77fb --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/Startup.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +namespace LY.MicroService.WorkflowManagement +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddApplication(); + } + + public void Configure(IApplicationBuilder app) + { + app.InitializeApplication(); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/TenantHeaderParamter.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/TenantHeaderParamter.cs new file mode 100644 index 000000000..b183313fa --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/TenantHeaderParamter.cs @@ -0,0 +1,32 @@ +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System.Collections.Generic; +using Volo.Abp.MultiTenancy; + +namespace LY.MicroService.WorkflowManagement +{ + public class TenantHeaderParamter : IOperationFilter + { + private readonly AbpMultiTenancyOptions _options; + public TenantHeaderParamter( + IOptions options) + { + _options = options.Value; + } + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + if (_options.IsEnabled) + { + operation.Parameters = operation.Parameters ?? new List(); + operation.Parameters.Add(new OpenApiParameter + { + Name = TenantResolverConsts.DefaultTenantKey, + In = ParameterLocation.Header, + Description = "Tenant Id/Name", + Required = false + }); + } + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.Configure.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.Configure.cs new file mode 100644 index 000000000..38284fa6f --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.Configure.cs @@ -0,0 +1,223 @@ +using LINGYUN.Abp.ExceptionHandling; +using LINGYUN.Abp.ExceptionHandling.Emailing; +using LINGYUN.Abp.Serilog.Enrichers.Application; +using Medallion.Threading; +using Medallion.Threading.Redis; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.Caching.StackExchangeRedis; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.OpenApi.Models; +using StackExchange.Redis; +using System; +using System.Text.Encodings.Web; +using System.Text.Unicode; +using Volo.Abp; +using Volo.Abp.Auditing; +using Volo.Abp.Caching; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Json; +using Volo.Abp.Json.SystemTextJson; +using Volo.Abp.Localization; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Uow; +using Volo.Abp.VirtualFileSystem; + +namespace LY.MicroService.WorkflowManagement +{ + public partial class WorkflowManagementHttpApiHostModule + { + private void PreConfigureApp() + { + AbpSerilogEnrichersConsts.ApplicationName = "WorkflowManagement"; + } + + private void ConfigureDistributedLock(IServiceCollection services, IConfiguration configuration) + { + var redis = ConnectionMultiplexer.Connect(configuration["DistributedLock:Redis:Configuration"]); + services.AddSingleton(_ => new RedisDistributedSynchronizationProvider(redis.GetDatabase())); + } + + private void ConfigureDbContext() + { + // 配置Ef + Configure(options => + { + options.UseMySQL(); + }); + + Configure(options => + { + + }); + } + + private void ConfigureJsonSerializer() + { + // 解决某些不支持类型的序列化 + Configure(options => + { + options.DefaultDateTimeFormat = "yyyy-MM-dd HH:mm:ss"; + }); + // 中文序列化的编码问题 + Configure(options => + { + options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); + }); + } + + private void ConfigureExceptionHandling() + { + // 自定义需要处理的异常 + Configure(options => + { + // 加入需要处理的异常类型 + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + }); + // 自定义需要发送邮件通知的异常类型 + Configure(options => + { + // 是否发送堆栈信息 + options.SendStackTrace = true; + // 未指定异常接收者的默认接收邮件 + // 指定自己的邮件地址 + }); + } + + private void ConfigureAuditing(IConfiguration configuration) + { + Configure(options => + { + options.ApplicationName = "WorkflowManagement"; + // 是否启用实体变更记录 + var entitiesChangedConfig = configuration.GetSection("App:TrackingEntitiesChanged"); + if (entitiesChangedConfig.Exists() && entitiesChangedConfig.Get()) + { + options + .EntityHistorySelectors + .AddAllEntities(); + } + }); + } + + private void ConfigureCaching(IConfiguration configuration) + { + Configure(options => + { + // 最好统一命名,不然某个缓存变动其他应用服务有例外发生 + options.KeyPrefix = "LINGYUN.Abp.Application"; + // 滑动过期30天 + options.GlobalCacheEntryOptions.SlidingExpiration = TimeSpan.FromDays(30d); + // 绝对过期60天 + options.GlobalCacheEntryOptions.AbsoluteExpiration = DateTimeOffset.Now.AddDays(60d); + }); + + Configure(options => + { + var redisConfig = ConfigurationOptions.Parse(options.Configuration); + options.ConfigurationOptions = redisConfig; + options.InstanceName = configuration["Redis:InstanceName"]; + }); + } + + private void ConfigureVirtualFileSystem() + { + Configure(options => + { + options.FileSets.AddEmbedded("LINGYUN.Abp.WorkflowManagement"); + }); + } + + 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 ConfigureSwagger(IServiceCollection services) + { + // Swagger + services.AddSwaggerGen( + options => + { + options.SwaggerDoc("v1", new OpenApiInfo { Title = "WorkflowManagement API", Version = "v1" }); + 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.AddDynamic(); + }); + } + + private void ConfigureSecurity(IServiceCollection services, IConfiguration configuration, bool isDevelopment = false) + { + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.Authority = configuration["AuthServer:Authority"]; + options.RequireHttpsMetadata = false; + options.Audience = configuration["AuthServer:ApiName"]; + }); + + if (!isDevelopment) + { + var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); + services + .AddDataProtection() + .SetApplicationName("LINGYUN.Abp.Application") + .PersistKeysToStackExchangeRedis(redis, "LINGYUN.Abp.Application:DataProtection:Protection-Keys"); + } + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.DataSeeder.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.DataSeeder.cs new file mode 100644 index 000000000..a7d79401e --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.DataSeeder.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Volo.Abp; +using Volo.Abp.Data; +using Volo.Abp.Threading; + +namespace LY.MicroService.WorkflowManagement +{ + public partial class WorkflowManagementHttpApiHostModule + { + private void SeedData(ApplicationInitializationContext context) + { + if (context.GetEnvironment().IsDevelopment()) + { + AsyncHelper.RunSync(async () => + await context.ServiceProvider.GetRequiredService() + .SeedAsync()); + } + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.cs new file mode 100644 index 000000000..8458c2e37 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/WorkflowManagementHttpApiHostModule.cs @@ -0,0 +1,124 @@ +using LINGYUN.Abp.AuditLogging.Elasticsearch; +using LINGYUN.Abp.ExceptionHandling.Emailing; +using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; +using LINGYUN.Abp.MultiTenancy.DbFinder; +using LINGYUN.Abp.Serilog.Enrichers.Application; +using LINGYUN.Abp.WorkflowCore.Components; +using LINGYUN.Abp.WorkflowCore.DistributedLock; +using LINGYUN.Abp.WorkflowCore.LifeCycleEvent; +using LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore; +using LINGYUN.Abp.WorkflowCore.RabbitMQ; +using LINGYUN.Abp.WorkflowManagement; +using LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System.Globalization; +using Volo.Abp; +using Volo.Abp.AspNetCore.Authentication.JwtBearer; +using Volo.Abp.AspNetCore.MultiTenancy; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Serilog; +using Volo.Abp.Autofac; +using Volo.Abp.Caching.StackExchangeRedis; +using Volo.Abp.EntityFrameworkCore.MySQL; +using Volo.Abp.EventBus.RabbitMq; +using Volo.Abp.FeatureManagement.EntityFrameworkCore; +using Volo.Abp.Http.Client.IdentityModel.Web; +using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement.EntityFrameworkCore; +using Volo.Abp.SettingManagement.EntityFrameworkCore; +using Volo.Abp.Swashbuckle; +using Volo.Abp.TenantManagement.EntityFrameworkCore; + +namespace LY.MicroService.WorkflowManagement +{ + [DependsOn( + typeof(AbpSerilogEnrichersApplicationModule), + typeof(AbpAuditLoggingElasticsearchModule), + typeof(AbpAspNetCoreSerilogModule), + typeof(AbpEventBusRabbitMqModule), + typeof(WorkflowManagementApplicationModule), + typeof(WorkflowManagementHttpApiModule), + typeof(WorkflowManagementEntityFrameworkCoreModule), + typeof(AbpWorkflowCoreComponentsModule), + typeof(AbpWorkflowCoreDistributedLockModule), + typeof(AbpWorkflowCoreLifeCycleEventModule), + typeof(AbpWorkflowCoreRabbitMQModule), + typeof(AbpWorkflowCorePersistenceEntityFrameworkCoreModule), + typeof(AbpEntityFrameworkCoreMySQLModule), + typeof(AbpAspNetCoreAuthenticationJwtBearerModule), + typeof(AbpEmailingExceptionHandlingModule), + typeof(AbpHttpClientIdentityModelWebModule), + typeof(AbpAspNetCoreMultiTenancyModule), + typeof(AbpDbFinderMultiTenancyModule), + typeof(AbpFeatureManagementEntityFrameworkCoreModule), + typeof(AbpPermissionManagementEntityFrameworkCoreModule), + typeof(AbpSettingManagementEntityFrameworkCoreModule), + typeof(AbpTenantManagementEntityFrameworkCoreModule), + typeof(AbpLocalizationManagementEntityFrameworkCoreModule), + typeof(AbpCachingStackExchangeRedisModule), + typeof(AbpAspNetCoreMvcModule), + typeof(AbpSwashbuckleModule), + typeof(AbpAutofacModule) + )] + public partial class WorkflowManagementHttpApiHostModule : AbpModule + { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAlwaysAllowAuthorization(); + + PreConfigureApp(); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + var hostingEnvironment = context.Services.GetHostingEnvironment(); + var configuration = hostingEnvironment.BuildConfiguration(); + + ConfigureDbContext(); + ConfigureLocalization(); + ConfigureJsonSerializer(); + ConfigureExceptionHandling(); + ConfigureVirtualFileSystem(); + ConfigureCaching(configuration); + ConfigureAuditing(configuration); + ConfigureMultiTenancy(configuration); + ConfigureSwagger(context.Services); + ConfigureDistributedLock(context.Services, configuration); + ConfigureSecurity(context.Services, configuration, hostingEnvironment.IsDevelopment()); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var app = context.GetApplicationBuilder(); + var env = context.GetEnvironment(); + + app.UseStaticFiles(); + app.UseCorrelationId(); + app.UseRouting(); + app.UseCors(); + app.UseAuthentication(); + app.UseJwtTokenMiddleware(); + app.UseMultiTenancy(); + app.UseAbpRequestLocalization(options => options.SetDefaultCulture(CultureInfo.CurrentCulture.Name)); + app.UseAuthorization(); + app.UseSwagger(); + app.UseAbpSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support APP API"); + + var configuration = context.GetConfiguration(); + options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]); + options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]); + options.OAuthScopes("WorkflowManagement"); + }); + app.UseAuditing(); + app.UseAbpSerilogEnrichers(); + app.UseConfiguredEndpoints(); + + SeedData(context); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/appsettings.Development.json b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/appsettings.Development.json new file mode 100644 index 000000000..458c54540 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/appsettings.Development.json @@ -0,0 +1,108 @@ +{ + "AgileConfig": { + "env": "DEV", + "appId": "LINGYUN.Abp.WorkflowManagement", + "secret": "1q2w3E*", + "nodes": "http://127.0.0.1:15000", + "name": "LINGYUN.Abp.WorkflowManagement", + "tag": "LINGYUN.Abp.WorkflowManagement" + }, + "App": { + "TrackingEntitiesChanged": true + }, + "ConnectionStrings": { + "Default": "Server=127.0.0.1;Database=Workflow;User Id=root;Password=123456", + "WorkflowManagement": "Server=127.0.0.1;Database=Workflow;User Id=root;Password=123456", + "AbpWorkflowCore": "Server=127.0.0.1;Database=Workflow;User Id=root;Password=123456", + "AbpFeatureManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", + "AbpPermissionManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", + "AbpLocalizationManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", + "AbpSettingManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456", + "AbpTenantManagement": "Server=127.0.0.1;Database=Platform;User Id=root;Password=123456" + }, + "RemoteServices": {}, + "IdentityClients": { + "InternalServiceClient": { + "Authority": "http://127.0.0.1:44385", + "RequireHttps": false, + "GrantType": "client_credentials", + "Scope": "lingyun-abp-application", + "ClientId": "InternalServiceClient", + "ClientSecret": "1q2w3E*" + } + }, + "RabbitMQ": { + "Connections": { + "AbpWorkflowCore": { + "HostName": "127.0.0.1", + "Port": 5672, + "UserName": "admin", + "Password": "123456", + "VirtualHost": "/" + } + }, + "EventBus": { + "ConnectionName": "AbpWorkflowCore", + "ClientName": "workflow.server", + "ExchangeName": "AbpWorkflowCore" + } + }, + "DistributedLock": { + "Redis": { + "Configuration": "127.0.0.1,defaultDatabase=15" + } + }, + "Redis": { + "Configuration": "127.0.0.1,defaultDatabase=10", + "InstanceName": "LINGYUN.Abp.Application" + }, + "AuthServer": { + "Authority": "http://127.0.0.1:44385/", + "ApiName": "lingyun-abp-application", + "SwaggerClientId": "InternalServiceClient", + "SwaggerClientSecret": "1q2w3E*" + }, + "Logging": { + "Serilog": { + "Elasticsearch": { + "IndexFormat": "abp.dev.logging-{0:yyyy.MM.dd}" + } + } + }, + "AuditLogging": { + "Elasticsearch": { + "IndexPrefix": "abp.dev.auditing" + } + }, + "Elasticsearch": { + "NodeUris": "http://127.0.0.1:9200" + }, + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "System": "Warning", + "Microsoft": "Warning", + "DotNetCore": "Debug" + } + }, + "WriteTo": [ + { + "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://127.0.0.1:9200", + "indexFormat": "abp.dev.logging-{0:yyyy.MM.dd}", + "autoRegisterTemplate": true, + "autoRegisterTemplateVersion": "ESv7" + } + } + ] + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/appsettings.json b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/appsettings.json new file mode 100644 index 000000000..95b9dd93b --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/appsettings.json @@ -0,0 +1,80 @@ +{ + "StringEncryption": { + "DefaultPassPhrase": "s46c5q55nxpeS8Ra", + "InitVectorBytes": "s83ng0abvd02js84", + "DefaultSalt": "sf&5)s3#" + }, + "AllowedHosts": "*", + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft.EntityFrameworkCore": "Debug", + "System": "Information", + "Microsoft": "Information" + } + }, + "Enrich": [ "FromLogContext", "WithProcessId", "WithThreadId", "WithEnvironmentName", "WithMachineName", "WithApplicationName" ], + "WriteTo": [ + { + "Name": "Console", + "Args": { + "initialMinimumLevel": "Verbose", + "standardErrorFromLevel": "Verbose", + "restrictedToMinimumLevel": "Verbose", + "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", + "fileSizeLimitBytes": 5242880, + "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", + "fileSizeLimitBytes": 5242880, + "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", + "fileSizeLimitBytes": 5242880, + "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", + "fileSizeLimitBytes": 5242880, + "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", + "fileSizeLimitBytes": 5242880, + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + } + ] + } +} diff --git a/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/dapr.sh b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/dapr.sh new file mode 100644 index 000000000..5b492fddc --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WorkflowManagement.HttpApi.Host/dapr.sh @@ -0,0 +1 @@ +dapr run --app-id workflow --app-port 30035 -H 36550 -- dotnet run --no-build \ No newline at end of file