Browse Source

feat(workflow): integrate WorkflowCore to support workflow

pull/427/head
cKey 4 years ago
parent
commit
ebf9582515
  1. 12
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/LINGYUN.Abp.WorkflowCore.Elasticsearch.csproj
  2. 140
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/LINGYUN/Abp/WorkflowCore/Elasticsearch/AbpElasticsearchIndexer.cs
  3. 24
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/LINGYUN/Abp/WorkflowCore/Elasticsearch/AbpWorkflowCoreElasticsearchModule.cs
  4. 14
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/LINGYUN/Abp/WorkflowCore/Elasticsearch/AbpWorkflowCoreElasticsearchOptions.cs
  5. 120
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/LINGYUN/Abp/WorkflowCore/Elasticsearch/Models/WorkflowSearchModel.cs
  6. 16
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/LINGYUN.Abp.WorkflowCore.LifeCycleEvent.csproj
  7. 51
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/LINGYUN/Abp/WorkflowCore/LifeCycleEvent/AbpEventBusLifeCycleEventHub.cs
  8. 24
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/LINGYUN/Abp/WorkflowCore/LifeCycleEvent/AbpWorkflowCoreLifeCycleEventModule.cs
  9. 45
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/LINGYUN/Abp/WorkflowCore/LifeCycleEvent/LifeCycleEventHandler.cs
  10. 16
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore.csproj
  11. 23
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/AbpWorkflowCorePersistenceEntityFrameworkCoreModule.cs
  12. 14
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowEventRepository.cs
  13. 15
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowEventSubscriptionRepository.cs
  14. 14
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowExecutionErrorRepository.cs
  15. 40
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowRepository.cs
  16. 14
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowScheduledCommandRepository.cs
  17. 18
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/IWorkflowDbContext.cs
  18. 29
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/WorkflowDbContext.cs
  19. 103
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/WorkflowDbContextModelBuilderExtensions.cs
  20. 16
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN.Abp.WorkflowCore.Persistence.csproj
  21. 47
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Definitions/WorkflowDefinition.cs
  22. 30
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Definitions/WorkflowDefinitionConditionCondition.cs
  23. 37
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Definitions/WorkflowDefinitionConditionNode.cs
  24. 10
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Definitions/WorkflowDefinitionExtensions.cs
  25. 52
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Definitions/WorkflowDefinitionFormData.cs
  26. 47
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Definitions/WorkflowDefinitionNode.cs
  27. 28
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Definitions/WorkflowDefinitionStepBody.cs
  28. 9
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/AbpWorkflowCorePersistenceModule.cs
  29. 354
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/AbpWorkflowPersistenceProvider.cs
  30. 9
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowEventRepository.cs
  31. 9
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowEventSubscriptionRepository.cs
  32. 8
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowExecutionErrorRepository.cs
  33. 21
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowRepository.cs
  34. 8
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowScheduledCommandRepository.cs
  35. 101
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/Workflow.cs
  36. 9
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowDbProperties.cs
  37. 47
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowEvent.cs
  38. 73
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowEventSubscription.cs
  39. 34
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowExecutionError.cs
  40. 162
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowExecutionPointer.cs
  41. 31
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowExtensionAttribute.cs
  42. 113
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowExtensions.cs
  43. 29
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowScheduledCommand.cs
  44. 138
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/WorkflowCore/Models/WorkflowExtensions.cs
  45. 16
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN.Abp.WorkflowCore.RabbitMQ.csproj
  46. 15
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/AbpRabbitMQWorkflowCoreOptions.cs
  47. 135
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/AbpRabbitMqQueueProvider.cs
  48. 24
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/AbpWorkflowCoreRabbitMQModule.cs
  49. 9
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/IQueueNameNormalizer.cs
  50. 23
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/QueueNameNormalizer.cs
  51. 21
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/WorkflowQueueConfiguration.cs
  52. 14
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN.Abp.WorkflowCore.csproj
  53. 11
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreConventionalRegistrar.cs
  54. 58
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreModule.cs
  55. 10
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreOptions.cs
  56. 6
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/IWorkflowEnabled.cs
  57. 10
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/IWorkflowManager.cs
  58. 14
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/NullStepBody.cs
  59. 14
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowBase.cs
  60. 9
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowConditionCondition.cs
  61. 15
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowConditionNode.cs
  62. 17
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowDefinition.cs
  63. 24
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowFormData.cs
  64. 105
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowManager.cs
  65. 20
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowNode.cs
  66. 10
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowParam.cs
  67. 8
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowParamDictionary.cs
  68. 8
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowParamInput.cs
  69. 17
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowStepBody.cs
  70. 19
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/System/ObjectSerializerExtensions.cs
  71. 19
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/System/UtcDateTimeExtensions.cs
  72. 17
      aspnet-core/tests/LINGYUN.Abp.WorkflowCore.Tests/LINGYUN.Abp.WorkflowCore.Tests.csproj
  73. 8
      aspnet-core/tests/LINGYUN.Abp.WorkflowCore.Tests/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreTestBase.cs
  74. 11
      aspnet-core/tests/LINGYUN.Abp.WorkflowCore.Tests/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreTestModule.cs

12
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/LINGYUN.Abp.WorkflowCore.Elasticsearch.csproj

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\elasticsearch\LINGYUN.Abp.Elasticsearch\LINGYUN.Abp.Elasticsearch.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore\LINGYUN.Abp.WorkflowCore.csproj" />
</ItemGroup>
</Project>

140
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/LINGYUN/Abp/WorkflowCore/Elasticsearch/AbpElasticsearchIndexer.cs

@ -0,0 +1,140 @@
using LINGYUN.Abp.Elasticsearch;
using LINGYUN.Abp.WorkflowCore.Elasticsearch.Models;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Nest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using WorkflowCore.Interface;
using WorkflowCore.Models;
using WorkflowCore.Models.Search;
namespace LINGYUN.Abp.WorkflowCore.Elasticsearch
{
public class AbpElasticsearchIndexer : ISearchIndex
{
private IElasticClient _client;
private readonly IElasticsearchClientFactory _elasticsearchClientFactory;
private readonly AbpWorkflowCoreElasticsearchOptions _options;
private readonly ILogger<AbpElasticsearchIndexer> _logger;
public AbpElasticsearchIndexer(
ILogger<AbpElasticsearchIndexer> logger,
IOptions<AbpWorkflowCoreElasticsearchOptions> options,
IElasticsearchClientFactory elasticsearchClientFactory)
{
_logger = logger;
_options = options.Value;
_elasticsearchClientFactory = elasticsearchClientFactory;
}
public async Task IndexWorkflow(WorkflowInstance workflow)
{
if (_client == null)
throw new InvalidOperationException("Not started");
var denormModel = WorkflowSearchModel.FromWorkflowInstance(workflow);
var result = await _client.IndexAsync(
denormModel,
idx => idx.Index(_options.IndexFormat));
if (!result.ApiCall.Success)
{
_logger.LogError(default(EventId), result.ApiCall.OriginalException, $"Failed to index workflow {workflow.Id}");
throw new ApplicationException($"Failed to index workflow {workflow.Id}", result.ApiCall.OriginalException);
}
}
public async Task<Page<WorkflowSearchResult>> Search(string terms, int skip, int take, params SearchFilter[] filters)
{
if (_client == null)
throw new InvalidOperationException("Not started");
var result = await _client.SearchAsync<WorkflowSearchModel>(s => s
.Index(_options.IndexFormat)
.Skip(skip)
.Take(take)
.MinScore(!string.IsNullOrEmpty(terms) ? 0.1 : 0)
.Query(query => query
.Bool(b => b
.Filter(BuildFilterQuery(filters))
.Should(
should => should.Match(t => t.Field(f => f.Reference).Query(terms).Boost(1.2)),
should => should.Match(t => t.Field(f => f.DataTokens).Query(terms).Boost(1.1)),
should => should.Match(t => t.Field(f => f.WorkflowDefinitionId).Query(terms).Boost(0.9)),
should => should.Match(t => t.Field(f => f.Status).Query(terms).Boost(0.9)),
should => should.Match(t => t.Field(f => f.Description).Query(terms))
)
)
)
);
return new Page<WorkflowSearchResult>
{
Total = result.Total,
Data = result.Hits.Select(x => x.Source).Select(x => x.ToSearchResult()).ToList()
};
}
public async Task Start()
{
_client = _elasticsearchClientFactory.Create();
var nodeInfo = await _client.Nodes.InfoAsync();
if (nodeInfo.Nodes.Values.Any(x => Convert.ToUInt32(x.Version.Split('.')[0]) < 6))
throw new NotSupportedException("Elasticsearch verison 6 or greater is required");
var exists = await _client.Indices.ExistsAsync(_options.IndexFormat);
if (!exists.Exists)
{
await _client.Indices.CreateAsync(_options.IndexFormat);
}
}
public Task Stop()
{
return Task.CompletedTask;
}
private List<Func<QueryContainerDescriptor<WorkflowSearchModel>, QueryContainer>> BuildFilterQuery(SearchFilter[] filters)
{
var result = new List<Func<QueryContainerDescriptor<WorkflowSearchModel>, QueryContainer>>();
foreach (var filter in filters)
{
var field = new Field(filter.Property);
if (filter.IsData)
{
Expression<Func<WorkflowSearchModel, object>> dataExpr = x => x.Data[filter.DataType.FullName];
var fieldExpr = Expression.Convert(filter.Property, typeof(Func<object, object>));
field = new Field(Expression.Lambda(Expression.Invoke(fieldExpr, dataExpr), Expression.Parameter(typeof(WorkflowSearchModel))));
}
switch (filter)
{
case ScalarFilter f:
result.Add(x => x.Match(t => t.Field(field).Query(Convert.ToString(f.Value))));
break;
case DateRangeFilter f:
if (f.BeforeValue.HasValue)
result.Add(x => x.DateRange(t => t.Field(field).LessThan(f.BeforeValue)));
if (f.AfterValue.HasValue)
result.Add(x => x.DateRange(t => t.Field(field).GreaterThan(f.AfterValue)));
break;
case NumericRangeFilter f:
if (f.LessValue.HasValue)
result.Add(x => x.Range(t => t.Field(field).LessThan(f.LessValue)));
if (f.GreaterValue.HasValue)
result.Add(x => x.Range(t => t.Field(field).GreaterThan(f.GreaterValue)));
break;
}
}
return result;
}
}
}

24
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/LINGYUN/Abp/WorkflowCore/Elasticsearch/AbpWorkflowCoreElasticsearchModule.cs

@ -0,0 +1,24 @@
using LINGYUN.Abp.Elasticsearch;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
using WorkflowCore.Interface;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Elasticsearch
{
[DependsOn(typeof(AbpWorkflowCoreModule))]
[DependsOn(typeof(AbpElasticsearchModule))]
public class AbpWorkflowCoreElasticsearchModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddSingleton<ISearchIndex, AbpElasticsearchIndexer>();
context.Services.AddSingleton<AbpElasticsearchIndexer>();
PreConfigure<WorkflowOptions>(options =>
{
options.UseSearchIndex(provider => provider.GetRequiredService<AbpElasticsearchIndexer>());
});
}
}
}

14
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/LINGYUN/Abp/WorkflowCore/Elasticsearch/AbpWorkflowCoreElasticsearchOptions.cs

@ -0,0 +1,14 @@
namespace LINGYUN.Abp.WorkflowCore.Elasticsearch
{
public class AbpWorkflowCoreElasticsearchOptions
{
/// <summary>
/// Default value: "workflows".
/// </summary>
public string IndexFormat { get; set; }
public AbpWorkflowCoreElasticsearchOptions()
{
IndexFormat = "workflows";
}
}
}

120
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/LINGYUN/Abp/WorkflowCore/Elasticsearch/Models/WorkflowSearchModel.cs

@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using System.Linq;
using WorkflowCore.Interface;
using WorkflowCore.Models;
using WorkflowCore.Models.Search;
namespace LINGYUN.Abp.WorkflowCore.Elasticsearch.Models
{
public class WorkflowSearchModel
{
public string Id { get; set; }
public string WorkflowDefinitionId { get; set; }
public int Version { get; set; }
public string Description { get; set; }
public string Reference { get; set; }
public DateTime? NextExecutionUtc { get; set; }
public string Status { get; set; }
public Dictionary<string, object> Data { get; set; } = new Dictionary<string, object>();
public IEnumerable<string> DataTokens { get; set; }
public DateTime CreateTime { get; set; }
public DateTime? CompleteTime { get; set; }
public ICollection<StepInfo> WaitingSteps { get; set; } = new HashSet<StepInfo>();
public ICollection<StepInfo> SleepingSteps { get; set; } = new HashSet<StepInfo>();
public ICollection<StepInfo> FailedSteps { get; set; } = new HashSet<StepInfo>();
public WorkflowSearchResult ToSearchResult()
{
var result = new WorkflowSearchResult
{
Id = Id,
CompleteTime = CompleteTime,
CreateTime = CreateTime,
Description = Description,
NextExecutionUtc = NextExecutionUtc,
Reference = Reference,
Status = (WorkflowStatus)Enum.Parse(typeof(WorkflowStatus), Status, true),
Version = Version,
WorkflowDefinitionId = WorkflowDefinitionId,
FailedSteps = FailedSteps,
SleepingSteps = SleepingSteps,
WaitingSteps = WaitingSteps
};
if (Data.Count > 0)
result.Data = Data.First().Value;
return result;
}
public static WorkflowSearchModel FromWorkflowInstance(WorkflowInstance workflow)
{
var result = new WorkflowSearchModel();
result.Id = workflow.Id;
result.WorkflowDefinitionId = workflow.WorkflowDefinitionId;
result.Description = workflow.Description;
result.Reference = workflow.Reference;
if (workflow.Data != null)
result.Data.Add(workflow.Data.GetType().FullName, workflow.Data);
result.CompleteTime = workflow.CompleteTime;
result.CreateTime = workflow.CreateTime;
result.Version = workflow.Version;
result.Status = workflow.Status.ToString();
if (workflow.NextExecution.HasValue)
result.NextExecutionUtc = new DateTime(workflow.NextExecution.Value);
if (workflow.Data is ISearchable)
result.DataTokens = (workflow.Data as ISearchable).GetSearchTokens();
foreach (var ep in workflow.ExecutionPointers)
{
if (ep.Status == PointerStatus.Sleeping)
{
result.SleepingSteps.Add(new StepInfo
{
StepId = ep.StepId,
Name = ep.StepName
});
}
if (ep.Status == PointerStatus.WaitingForEvent)
{
result.WaitingSteps.Add(new StepInfo
{
StepId = ep.StepId,
Name = ep.StepName
});
}
if (ep.Status == PointerStatus.Failed)
{
result.FailedSteps.Add(new StepInfo
{
StepId = ep.StepId,
Name = ep.StepName
});
}
}
return result;
}
}
}

16
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/LINGYUN.Abp.WorkflowCore.LifeCycleEvent.csproj

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.EventBus" Version="4.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore\LINGYUN.Abp.WorkflowCore.csproj" />
</ItemGroup>
</Project>

51
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/LINGYUN/Abp/WorkflowCore/LifeCycleEvent/AbpEventBusLifeCycleEventHub.cs

@ -0,0 +1,51 @@
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
using Volo.Abp.EventBus.Distributed;
using WorkflowCore.Interface;
using EventData = WorkflowCore.Models.LifeCycleEvents.LifeCycleEvent;
namespace LINGYUN.Abp.WorkflowCore.LifeCycleEvent
{
public class AbpEventBusLifeCycleEventHub : ILifeCycleEventHub
{
private IDisposable _subscriber;
private readonly IDistributedEventBus _eventBus;
private readonly ILoggerFactory _loggerFactory;
public AbpEventBusLifeCycleEventHub(
ILoggerFactory loggerFactory,
IDistributedEventBus distributedEventBus)
{
_loggerFactory = loggerFactory;
_eventBus = distributedEventBus;
}
public async Task PublishNotification(EventData evt)
{
await _eventBus.PublishAsync(evt);
}
public Task Start()
{
_subscriber = _eventBus.Subscribe(new LifeCycleEventHandler(
_loggerFactory.CreateLogger<LifeCycleEventHandler>()));
return Task.CompletedTask;
}
public Task Stop()
{
// TODO
_subscriber?.Dispose();
return Task.CompletedTask;
}
public void Subscribe(Action<EventData> action)
{
LifeCycleEventHandler.Subscribers.Add(action);
}
}
}

24
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/LINGYUN/Abp/WorkflowCore/LifeCycleEvent/AbpWorkflowCoreLifeCycleEventModule.cs

@ -0,0 +1,24 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EventBus;
using Volo.Abp.Modularity;
using WorkflowCore.Interface;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.LifeCycleEvent
{
[DependsOn(typeof(AbpEventBusModule))]
[DependsOn(typeof(AbpWorkflowCoreModule))]
public class AbpWorkflowCoreLifeCycleEventModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddSingleton<ILifeCycleEventHub, AbpEventBusLifeCycleEventHub>();
context.Services.AddSingleton<AbpEventBusLifeCycleEventHub>();
PreConfigure<WorkflowOptions>(options =>
{
options.UseEventHub(provider => provider.GetRequiredService<AbpEventBusLifeCycleEventHub>());
});
}
}
}

45
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/LINGYUN/Abp/WorkflowCore/LifeCycleEvent/LifeCycleEventHandler.cs

@ -0,0 +1,45 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.EventBus.Distributed;
using EventData = WorkflowCore.Models.LifeCycleEvents.LifeCycleEvent;
namespace LINGYUN.Abp.WorkflowCore.LifeCycleEvent
{
public class LifeCycleEventHandler : IDistributedEventHandler<EventData>
{
private readonly ILogger<LifeCycleEventHandler> _logger;
internal static readonly ICollection<Action<EventData>> Subscribers = new HashSet<Action<EventData>>();
public LifeCycleEventHandler(
ILogger<LifeCycleEventHandler> logger)
{
_logger = logger;
}
public Task HandleEventAsync(EventData eventData)
{
NotifySubscribers(eventData);
return Task.CompletedTask;
}
private void NotifySubscribers(EventData evt)
{
foreach (var subscriber in Subscribers)
{
try
{
subscriber(evt);
}
catch (Exception ex)
{
_logger.LogWarning(
default, ex, $"Error on event subscriber: {ex.Message}");
}
}
}
}
}

16
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore.csproj

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="4.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore.Persistence\LINGYUN.Abp.WorkflowCore.Persistence.csproj" />
</ItemGroup>
</Project>

23
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/AbpWorkflowCorePersistenceEntityFrameworkCoreModule.cs

@ -0,0 +1,23 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore
{
[DependsOn(typeof(AbpWorkflowCorePersistenceModule))]
[DependsOn(typeof(AbpEntityFrameworkCoreModule))]
public class AbpWorkflowCorePersistenceEntityFrameworkCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<WorkflowDbContext>(options =>
{
options.AddRepository<Workflow, EfCoreWorkflowRepository>();
options.AddRepository<WorkflowEvent, EfCoreWorkflowEventRepository>();
options.AddRepository<WorkflowExecutionError, EfCoreWorkflowExecutionErrorRepository>();
options.AddRepository<WorkflowScheduledCommand, EfCoreWorkflowScheduledCommandRepository>();
options.AddRepository<WorkflowEventSubscription, EfCoreWorkflowEventSubscriptionRepository>();
});
}
}
}

14
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowEventRepository.cs

@ -0,0 +1,14 @@
using System;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore
{
public class EfCoreWorkflowEventRepository : EfCoreRepository<WorkflowDbContext, WorkflowEvent, Guid>, IWorkflowEventRepository
{
public EfCoreWorkflowEventRepository(IDbContextProvider<WorkflowDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}
}

15
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowEventSubscriptionRepository.cs

@ -0,0 +1,15 @@
using LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore;
using System;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class EfCoreWorkflowEventSubscriptionRepository : EfCoreRepository<WorkflowDbContext, WorkflowEventSubscription, Guid>, IWorkflowEventSubscriptionRepository
{
public EfCoreWorkflowEventSubscriptionRepository(IDbContextProvider<WorkflowDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}
}

14
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowExecutionErrorRepository.cs

@ -0,0 +1,14 @@
using LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class EfCoreWorkflowExecutionErrorRepository : EfCoreRepository<WorkflowDbContext, WorkflowExecutionError, int>, IWorkflowExecutionErrorRepository
{
public EfCoreWorkflowExecutionErrorRepository(IDbContextProvider<WorkflowDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}
}

40
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowRepository.cs

@ -0,0 +1,40 @@
using LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore;
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;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class EfCoreWorkflowRepository : EfCoreRepository<WorkflowDbContext, Workflow, Guid>, IWorkflowRepository
{
public EfCoreWorkflowRepository(IDbContextProvider<WorkflowDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<List<Workflow>> GetListAsync(
WorkflowStatus? status,
string type,
DateTime? createdFrom,
DateTime? createdTo,
int skip,
int take,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.Include(x => x.ExecutionPointers)
.WhereIf(status.HasValue, x => x.Status == status.Value)
.WhereIf(!type.IsNullOrWhiteSpace(), x => x.WorkflowDefinitionId.Equals(type))
.WhereIf(createdFrom.HasValue, x => x.CreationTime >= createdFrom.Value)
.WhereIf(createdTo.HasValue, x => x.CreationTime <= createdTo.Value)
.PageBy(skip, take)
.ToListAsync();
}
}
}

14
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowScheduledCommandRepository.cs

@ -0,0 +1,14 @@
using LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class EfCoreWorkflowScheduledCommandRepository : EfCoreRepository<WorkflowDbContext, WorkflowScheduledCommand, long>, IWorkflowScheduledCommandRepository
{
public EfCoreWorkflowScheduledCommandRepository(IDbContextProvider<WorkflowDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}
}

18
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/IWorkflowDbContext.cs

@ -0,0 +1,18 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore
{
[ConnectionStringName(WorkflowDbProperties.ConnectionStringName)]
public interface IWorkflowDbContext : IEfCoreDbContext
{
DbSet<Workflow> Workflows { get; set; }
DbSet<WorkflowEvent> WorkflowEvents { get; set; }
DbSet<WorkflowEventSubscription> WorkflowEventSubscriptions { get; set; }
DbSet<WorkflowExecutionError> WorkflowExecutionErrors { get; set; }
DbSet<WorkflowExecutionPointer> WorkflowExecutionPointers { get; set; }
DbSet<WorkflowExtensionAttribute> WorkflowExtensionAttributes { get; set; }
DbSet<WorkflowScheduledCommand> WorkflowScheduledCommands { get; set; }
}
}

29
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/WorkflowDbContext.cs

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore
{
[ConnectionStringName(WorkflowDbProperties.ConnectionStringName)]
public class WorkflowDbContext : AbpDbContext<WorkflowDbContext>, IWorkflowDbContext
{
public virtual DbSet<Workflow> Workflows { get; set; }
public virtual DbSet<WorkflowEvent> WorkflowEvents { get; set; }
public virtual DbSet<WorkflowEventSubscription> WorkflowEventSubscriptions { get; set; }
public virtual DbSet<WorkflowExecutionError> WorkflowExecutionErrors { get; set; }
public virtual DbSet<WorkflowExecutionPointer> WorkflowExecutionPointers { get; set; }
public virtual DbSet<WorkflowExtensionAttribute> WorkflowExtensionAttributes { get; set; }
public virtual DbSet<WorkflowScheduledCommand> WorkflowScheduledCommands { get; set; }
public WorkflowDbContext(DbContextOptions<WorkflowDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ConfigureWorkflow();
}
}
}

103
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/WorkflowDbContextModelBuilderExtensions.cs

@ -0,0 +1,103 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Volo.Abp;
using Volo.Abp.EntityFrameworkCore.Modeling;
namespace LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore
{
public static class WorkflowDbContextModelBuilderExtensions
{
public static void ConfigureWorkflow([NotNull] this ModelBuilder builder)
{
Check.NotNull(builder, nameof(builder));
builder.Entity<Workflow>(b =>
{
b.ToTable(WorkflowDbProperties.TablePrefix + "Workflow");
b.Property(p => p.WorkflowDefinitionId).HasMaxLength(200);
b.Property(p => p.Description).HasMaxLength(500);
b.Property(p => p.Reference).HasMaxLength(200);
b.ConfigureByConvention();
b.HasIndex(p => p.NextExecution);
});
builder.Entity<WorkflowExecutionPointer>(b =>
{
b.ToTable(WorkflowDbProperties.TablePrefix + "ExecutionPointer");
b.Property(p => p.Id).HasMaxLength(50);
b.Property(p => p.EventName).HasMaxLength(200);
b.Property(p => p.EventKey).HasMaxLength(200);
b.Property(p => p.StepName).HasMaxLength(100);
b.Property(p => p.PredecessorId).HasMaxLength(100);
b.ConfigureByConvention();
});
builder.Entity<WorkflowExtensionAttribute>(b =>
{
b.ToTable(WorkflowDbProperties.TablePrefix + "ExtensionAttribute");
b.Property(p => p.Key).HasMaxLength(100);
b.Property(p => p.ExecutionPointerId).HasMaxLength(50);
b.ConfigureByConvention();
});
builder.Entity<WorkflowEvent>(b =>
{
b.ToTable(WorkflowDbProperties.TablePrefix + "Event");
b.Property(p => p.EventName).HasMaxLength(200);
b.Property(p => p.EventKey).HasMaxLength(200);
b.ConfigureByConvention();
b.HasIndex(x => new { x.EventName, x.EventKey });
b.HasIndex(x => x.CreationTime);
b.HasIndex(x => x.IsProcessed);
});
builder.Entity<WorkflowEventSubscription>(b =>
{
b.ToTable(WorkflowDbProperties.TablePrefix + "Subscription");
b.Property(p => p.ExecutionPointerId).HasMaxLength(50);
b.Property(p => p.EventName).HasMaxLength(200);
b.Property(p => p.EventKey).HasMaxLength(200);
b.Property(p => p.ExternalToken).HasMaxLength(200);
b.Property(p => p.ExternalWorkerId).HasMaxLength(200);
b.ConfigureByConvention();
b.HasIndex(x => x.EventName);
b.HasIndex(x => x.EventKey);
});
builder.Entity<WorkflowExecutionError>(b =>
{
b.ToTable(WorkflowDbProperties.TablePrefix + "ExecutionError");
b.Property(p => p.ExecutionPointerId).HasMaxLength(50);
b.ConfigureByConvention();
});
builder.Entity<WorkflowScheduledCommand>(b =>
{
b.ToTable(WorkflowDbProperties.TablePrefix + "ScheduledCommand");
b.Property(p => p.CommandName).HasMaxLength(200);
b.Property(p => p.Data).HasMaxLength(500);
b.ConfigureByConvention();
b.HasIndex(x => x.ExecuteTime);
b.HasIndex(x => new { x.CommandName, x.Data }).IsUnique();
});
}
}
}

16
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN.Abp.WorkflowCore.Persistence.csproj

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="4.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore\LINGYUN.Abp.WorkflowCore.csproj" />
</ItemGroup>
</Project>

47
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Definitions/WorkflowDefinition.cs

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Definitions
{
public class WorkflowDefinition : FullAuditedAggregateRoot<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public string Title { get; protected set; }
public int Version { get; protected set; }
public string Description { get; protected set; }
public string Icon { get; protected set; }
public string Color { get; protected set; }
public string Group { get; protected set; }
public ICollection<WorkflowDefinitionNode> Nodes { get; protected set; }
public ICollection<WorkflowDefinitionFormData> Inputs { get; protected set; }
protected WorkflowDefinition()
{
Nodes = new Collection<WorkflowDefinitionNode>();
Inputs = new Collection<WorkflowDefinitionFormData>();
}
public WorkflowDefinition(
Guid id,
string title,
int version,
string group,
string icon,
string color,
string description = null,
Guid? tenantId = null) : base(id)
{
Title = title;
Version = version;
Group = group;
Icon = icon;
Color = color;
Description = description;
TenantId = tenantId;
Nodes = new Collection<WorkflowDefinitionNode>();
Inputs = new Collection<WorkflowDefinitionFormData>();
}
}
}

30
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Definitions/WorkflowDefinitionConditionCondition.cs

@ -0,0 +1,30 @@
using System;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Definitions
{
public class WorkflowDefinitionConditionCondition : Entity<long>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid ParentId { get; protected set; }
public virtual WorkflowDefinitionConditionNode ConditionNode { get; protected set; }
public virtual string Field { get; set; }
public virtual string Operator { get; set; }
public virtual string Value { get; set; }
protected WorkflowDefinitionConditionCondition() { }
public WorkflowDefinitionConditionCondition(
Guid parentId,
string field,
string opt,
string value,
Guid? tenantId = null)
{
ParentId = parentId;
Field = field;
Operator = opt;
Value = value;
TenantId = tenantId;
}
}
}

37
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Definitions/WorkflowDefinitionConditionNode.cs

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Definitions
{
public class WorkflowDefinitionConditionNode : Entity<long>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid ParentId { get; protected set; }
public virtual WorkflowDefinitionNode Node { get; protected set; }
public virtual string Label { get; protected set; }
public virtual string NodeId { get; protected set; }
public virtual ICollection<WorkflowDefinitionConditionCondition> Conditions { get; protected set; }
protected WorkflowDefinitionConditionNode()
{
Conditions = new Collection<WorkflowDefinitionConditionCondition>();
}
public WorkflowDefinitionConditionNode(
Guid parentId,
string label,
string nodeId,
Guid? tenantId = null)
{
ParentId = parentId;
Label = label;
NodeId = nodeId;
TenantId = tenantId;
Conditions = new Collection<WorkflowDefinitionConditionCondition>();
}
}
}

10
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Definitions/WorkflowDefinitionExtensions.cs

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace LINGYUN.Abp.WorkflowCore.Definitions
{
public class WorkflowDefinitionExtensions
{
}
}

52
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Definitions/WorkflowDefinitionFormData.cs

@ -0,0 +1,52 @@
using System;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Definitions
{
public class WorkflowDefinitionFormData : Entity<long>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid WorkflowId { get; protected set; }
public virtual WorkflowDefinition Workflow { get; protected set; }
public virtual string Name { get; protected set; }
public virtual string Label { get; protected set; }
public virtual string Type { get; protected set; }
public virtual string Value { get; protected set; }
public virtual ExtraPropertyDictionary Styles { get; protected set; }
public virtual int? MaxLength { get; protected set; }
public virtual int? MinLength { get; protected set; }
public virtual ExtraPropertyDictionary Items { get; protected set; }
public virtual ExtraPropertyDictionary Rules { get; protected set; }
protected WorkflowDefinitionFormData()
{
Styles = new ExtraPropertyDictionary();
Items = new ExtraPropertyDictionary();
Rules = new ExtraPropertyDictionary();
}
public WorkflowDefinitionFormData(
Guid workflowId,
string name,
string label,
string type,
string value,
int? minLength = null,
int? maxLength = null,
Guid? tenantId = null)
{
WorkflowId = workflowId;
Name = name;
Label = label;
Type = type;
Value = value;
MinLength = minLength;
MaxLength = maxLength;
TenantId = tenantId;
Styles = new ExtraPropertyDictionary();
Items = new ExtraPropertyDictionary();
Rules = new ExtraPropertyDictionary();
}
}
}

47
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Definitions/WorkflowDefinitionNode.cs

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Definitions
{
public class WorkflowDefinitionNode : Entity<long>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid WorkflowId { get; protected set; }
public virtual WorkflowDefinition Workflow { get; protected set; }
public virtual string Key { get; protected set; }
public virtual string Title { get; protected set; }
public virtual string Position { get; protected set; }
public virtual string Type { get; protected set; }
public virtual WorkflowDefinitionStepBody StepBody { get; protected set; }
public virtual string ParentNodes { get; protected set; }
public virtual ICollection<WorkflowDefinitionConditionNode> NextNodes { get; protected set; }
protected WorkflowDefinitionNode()
{
NextNodes = new Collection<WorkflowDefinitionConditionNode>();
}
public WorkflowDefinitionNode(
Guid workflowId,
string key,
string title,
int[] position,
string type,
WorkflowDefinitionStepBody stepBody,
string[] parentNodes,
Guid? tenantId = null)
{
WorkflowId = workflowId;
Key = key;
Title = title;
Position = position.JoinAsString(";");
Type = type;
StepBody = stepBody;
ParentNodes = parentNodes.JoinAsString(";");
TenantId = tenantId;
NextNodes = new Collection<WorkflowDefinitionConditionNode>();
}
}
}

28
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Definitions/WorkflowDefinitionStepBody.cs

@ -0,0 +1,28 @@
using System;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Definitions
{
public class WorkflowDefinitionStepBody : Entity<Guid>, IMultiTenant, IHasExtraProperties
{
public virtual Guid? TenantId { get; protected set; }
public virtual string Name { get; protected set; }
public ExtraPropertyDictionary ExtraProperties { get; protected set; }
protected WorkflowDefinitionStepBody()
{
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public WorkflowDefinitionStepBody(
Guid id,
string name) : base(id)
{
Name = name;
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
}
}

9
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/AbpWorkflowCorePersistenceModule.cs

@ -0,0 +1,9 @@
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
[DependsOn(typeof(AbpWorkflowCoreModule))]
public class AbpWorkflowCorePersistenceModule : AbpModule
{
}
}

354
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/AbpWorkflowPersistenceProvider.cs

@ -0,0 +1,354 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Linq;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
using WorkflowCore.Interface;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class AbpWorkflowPersistenceProvider : IPersistenceProvider, IUnitOfWorkEnabled, ITransientDependency
{
private readonly ICurrentTenant _currentTenant;
private readonly IGuidGenerator _guidGenerator;
private readonly IWorkflowRepository _workflowRepository;
private readonly IWorkflowEventRepository _workflowEventRepository;
private readonly IWorkflowExecutionErrorRepository _executionErrorRepository;
private readonly IWorkflowEventSubscriptionRepository _subscriptionRepository;
private readonly IWorkflowScheduledCommandRepository _scheduledCommandRepository;
private readonly IAsyncQueryableExecuter _asyncQueryableExecuter;
public bool SupportsScheduledCommands => true;
public AbpWorkflowPersistenceProvider(
ICurrentTenant currentTenant,
IGuidGenerator guidGenerator,
IAsyncQueryableExecuter asyncQueryableExecuter,
IWorkflowRepository workflowRepository,
IWorkflowEventRepository workflowEventRepository,
IWorkflowExecutionErrorRepository executionErrorRepository,
IWorkflowEventSubscriptionRepository subscriptionRepository,
IWorkflowScheduledCommandRepository scheduledCommandRepository)
{
_currentTenant = currentTenant;
_guidGenerator = guidGenerator;
_asyncQueryableExecuter = asyncQueryableExecuter;
_workflowRepository = workflowRepository;
_workflowEventRepository = workflowEventRepository;
_executionErrorRepository = executionErrorRepository;
_subscriptionRepository = subscriptionRepository;
_scheduledCommandRepository = scheduledCommandRepository;
}
public virtual async Task ClearSubscriptionToken(
string eventSubscriptionId,
string token,
CancellationToken cancellationToken = default)
{
var uid = Guid.Parse(eventSubscriptionId);
var existingEntity = await _subscriptionRepository.GetAsync(uid, cancellationToken: cancellationToken);
if (existingEntity.ExternalToken != token)
throw new InvalidOperationException();
existingEntity.SetSubscriptionToken(null, null, null);
await _subscriptionRepository.UpdateAsync(existingEntity, cancellationToken: cancellationToken);
}
public virtual async Task<string> CreateEvent(
Event newEvent,
CancellationToken cancellationToken = default)
{
var we = newEvent.ToWorkflowEvent(_guidGenerator, _currentTenant);
await _workflowEventRepository.InsertAsync(we, cancellationToken: cancellationToken);
newEvent.Id = we.Id.ToString();
return newEvent.Id;
}
public virtual async Task<string> CreateEventSubscription(
EventSubscription subscription,
CancellationToken cancellationToken = default)
{
var wes = subscription.ToWorkflowEventSubscription(_guidGenerator, _currentTenant);
await _subscriptionRepository.InsertAsync(wes, cancellationToken: cancellationToken);
subscription.Id = wes.Id.ToString();
return subscription.Id;
}
public virtual async Task<string> CreateNewWorkflow(
WorkflowInstance workflow,
CancellationToken cancellationToken = default)
{
var wf = workflow.ToWorkflow(_guidGenerator, _currentTenant);
await _workflowRepository.InsertAsync(wf, cancellationToken: cancellationToken);
workflow.Id = wf.Id.ToString();
return workflow.Id;
}
[UnitOfWork(IsDisabled = true)]
public void EnsureStoreExists()
{
// TODO:
}
public virtual async Task<Event> GetEvent(string id, CancellationToken cancellationToken = default)
{
var eventId = Guid.Parse(id);
var workflowEvent = await _workflowEventRepository.GetAsync(eventId, cancellationToken: cancellationToken);
return workflowEvent.ToEvent();
}
public virtual async Task<IEnumerable<string>> GetEvents(
string eventName,
string eventKey,
DateTime asOf,
CancellationToken cancellationToken = default)
{
var queryable = await _workflowEventRepository.GetQueryableAsync();
var workflowEventIds = await _asyncQueryableExecuter.ToListAsync(
queryable.Where(x => x.EventName == eventName && x.EventKey == eventKey)
.Where(x => x.CreationTime >= asOf)
.Select(x => x.Id),
cancellationToken);
return workflowEventIds.Select(e => e.ToString());
}
public virtual async Task<EventSubscription> GetFirstOpenSubscription(
string eventName,
string eventKey,
DateTime asOf,
CancellationToken cancellationToken = default)
{
var queryable = await _subscriptionRepository.GetQueryableAsync();
var workflowEventSubscription = await _asyncQueryableExecuter.FirstOrDefaultAsync(
queryable.Where(x => x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf && x.ExternalToken == null),
cancellationToken);
return workflowEventSubscription?.ToEventSubscription();
}
public virtual async Task<IEnumerable<string>> GetRunnableEvents(
DateTime asAt,
CancellationToken cancellationToken = default)
{
var workflowEvents = await _workflowEventRepository.GetQueryableAsync();
var now = asAt.ToUniversalTime();
var workflowEventIdList = await _asyncQueryableExecuter.ToListAsync(
workflowEvents.Where(x => !x.IsProcessed)
.Where(x => x.CreationTime <= now)
.Select(x => x.Id),
cancellationToken);
return workflowEventIdList.Select(e => e.ToString());
}
public virtual async Task<IEnumerable<string>> GetRunnableInstances(
DateTime asAt,
CancellationToken cancellationToken = default)
{
var now = asAt.ToUniversalTime().Ticks;
var workflows = await _workflowRepository.GetQueryableAsync();
var workflowIdList = await _asyncQueryableExecuter.ToListAsync(
workflows.Where(x => x.NextExecution.HasValue && (x.NextExecution <= now) && (x.Status == WorkflowStatus.Runnable))
.Select(x => x.Id),
cancellationToken);
return workflowIdList.Select(e => e.ToString());
}
public virtual async Task<EventSubscription> GetSubscription(
string eventSubscriptionId,
CancellationToken cancellationToken = default)
{
var subscriptionId = Guid.Parse(eventSubscriptionId);
var subscription = await _subscriptionRepository.FindAsync(subscriptionId, cancellationToken: cancellationToken);
return subscription?.ToEventSubscription();
}
public virtual async Task<IEnumerable<EventSubscription>> GetSubscriptions(
string eventName,
string eventKey,
DateTime asOf,
CancellationToken cancellationToken = default)
{
var now = asOf.ToUniversalTime();
var subscriptions = await _subscriptionRepository.GetQueryableAsync();
var eventSubscriptions = await _asyncQueryableExecuter.ToListAsync(
subscriptions.Where(x => x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= now),
cancellationToken);
return eventSubscriptions.Select(x => x.ToEventSubscription());
}
public virtual async Task<WorkflowInstance> GetWorkflowInstance(
string Id,
CancellationToken cancellationToken = default)
{
var workflowId = Guid.Parse(Id);
var workflow = await _workflowRepository.FindAsync(
workflowId,
includeDetails: true,
cancellationToken: cancellationToken);
return workflow?.ToWorkflowInstance();
}
public virtual async Task<IEnumerable<WorkflowInstance>> GetWorkflowInstances(
WorkflowStatus? status,
string type,
DateTime? createdFrom,
DateTime? createdTo,
int skip,
int take)
{
var workflows = await _workflowRepository.GetListAsync(status, type, createdFrom, createdTo, skip, take);
return workflows.Select(x => x.ToWorkflowInstance());
}
public virtual async Task<IEnumerable<WorkflowInstance>> GetWorkflowInstances(
IEnumerable<string> ids,
CancellationToken cancellationToken = default)
{
var workflowIds = ids.Select(id => Guid.Parse(id));
var queryable = await _workflowRepository.GetQueryableAsync();
var workflows = await _asyncQueryableExecuter.ToListAsync(
queryable.Where(x => workflowIds.Contains(x.Id)),
cancellationToken);
return workflows.Select(x => x.ToWorkflowInstance());
}
public virtual async Task MarkEventProcessed(string id, CancellationToken cancellationToken = default)
{
var eventId = Guid.Parse(id);
var workflowEvent = await _workflowEventRepository.GetAsync(eventId, cancellationToken: cancellationToken);
workflowEvent.IsProcessed = true;
await _workflowEventRepository.UpdateAsync(workflowEvent, cancellationToken: cancellationToken);
}
public virtual async Task MarkEventUnprocessed(string id, CancellationToken cancellationToken = default)
{
var eventId = Guid.Parse(id);
var workflowEvent = await _workflowEventRepository.GetAsync(eventId, cancellationToken: cancellationToken);
workflowEvent.IsProcessed = false;
await _workflowEventRepository.UpdateAsync(workflowEvent, cancellationToken: cancellationToken);
}
public virtual async Task PersistErrors(IEnumerable<ExecutionError> errors, CancellationToken cancellationToken = default)
{
if (errors.Any())
{
var workflowExecutionErrors = errors.Select(x => x.ToWorkflowExecutionError(_currentTenant));
await _executionErrorRepository.InsertManyAsync(workflowExecutionErrors, cancellationToken: cancellationToken);
}
}
public virtual async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default)
{
if (!Guid.TryParse(workflow.Id, out Guid workflowId))
{
workflowId = _guidGenerator.Create();
}
var wf = await _workflowRepository.FindAsync(workflowId, includeDetails: true, cancellationToken: cancellationToken);
if (wf == null)
{
wf = workflow.ToWorkflow(_guidGenerator, _currentTenant);
await _workflowRepository.InsertAsync(wf, cancellationToken: cancellationToken);
}
else
{
wf.Update(workflow, _guidGenerator, _currentTenant);
await _workflowRepository.UpdateAsync(wf, cancellationToken: cancellationToken);
}
}
public virtual async Task ProcessCommands(
DateTimeOffset asOf,
Func<ScheduledCommand, Task> action,
CancellationToken cancellationToken = default)
{
var quertable = await _scheduledCommandRepository.GetQueryableAsync();
var commands = await _asyncQueryableExecuter.ToListAsync(
quertable.Where(x => x.ExecuteTime < asOf.UtcDateTime.Ticks),
cancellationToken);
foreach (var command in commands)
{
await action(command.ToScheduledCommand());
}
await _scheduledCommandRepository.DeleteManyAsync(commands, cancellationToken: cancellationToken);
}
public virtual async Task ScheduleCommand(ScheduledCommand command)
{
var workflowCommand = command.ToWorkflowScheduledCommand(_currentTenant);
await _scheduledCommandRepository.InsertAsync(workflowCommand);
}
public virtual async Task<bool> SetSubscriptionToken(
string eventSubscriptionId,
string token,
string workerId,
DateTime expiry,
CancellationToken cancellationToken = default)
{
var uid = Guid.Parse(eventSubscriptionId);
var existingEntity = await _subscriptionRepository.GetAsync(uid, cancellationToken: cancellationToken);
existingEntity.SetSubscriptionToken(token, workerId, expiry);
await _subscriptionRepository.UpdateAsync(existingEntity, cancellationToken: cancellationToken);
return true;
}
public virtual async Task TerminateSubscription(
string eventSubscriptionId,
CancellationToken cancellationToken = default)
{
var uid = Guid.Parse(eventSubscriptionId);
var existingEntity = await _subscriptionRepository.GetAsync(uid, cancellationToken: cancellationToken);
await _subscriptionRepository.DeleteAsync(existingEntity, cancellationToken: cancellationToken);
}
}
}

9
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowEventRepository.cs

@ -0,0 +1,9 @@
using System;
using Volo.Abp.Domain.Repositories;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public interface IWorkflowEventRepository : IRepository<WorkflowEvent, Guid>
{
}
}

9
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowEventSubscriptionRepository.cs

@ -0,0 +1,9 @@
using System;
using Volo.Abp.Domain.Repositories;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public interface IWorkflowEventSubscriptionRepository : IRepository<WorkflowEventSubscription, Guid>
{
}
}

8
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowExecutionErrorRepository.cs

@ -0,0 +1,8 @@
using Volo.Abp.Domain.Repositories;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public interface IWorkflowExecutionErrorRepository : IRepository<WorkflowExecutionError, int>
{
}
}

21
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowRepository.cs

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public interface IWorkflowRepository : IRepository<Workflow, Guid>
{
Task<List<Workflow>> GetListAsync(
WorkflowStatus? status,
string type,
DateTime? createdFrom,
DateTime? createdTo,
int skip,
int take,
CancellationToken cancellationToken = default);
}
}

8
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowScheduledCommandRepository.cs

@ -0,0 +1,8 @@
using Volo.Abp.Domain.Repositories;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public interface IWorkflowScheduledCommandRepository : IRepository<WorkflowScheduledCommand, long>
{
}
}

101
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/Workflow.cs

@ -0,0 +1,101 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class Workflow : AuditedAggregateRoot<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual string WorkflowDefinitionId { get; protected set; }
public virtual int Version { get; protected set; }
public virtual string Description { get; protected set; }
public virtual string Reference { get; protected set; }
public virtual long? NextExecution { get; protected set; }
public virtual WorkflowStatus Status { get; protected set; }
public virtual string Data { get; protected set; }
public virtual DateTime? CompleteTime { get; protected set; }
public virtual ICollection<WorkflowExecutionPointer> ExecutionPointers { get; protected set; }
protected Workflow()
{
ExecutionPointers = new Collection<WorkflowExecutionPointer>();
}
public Workflow(
Guid id,
DateTime creationTime,
string defintionId,
string data,
int version,
string description,
string reference,
long? nextExecution = null,
WorkflowStatus status = WorkflowStatus.Terminated,
DateTime? completeTime = null,
Guid? tenantId = null) : base(id)
{
Data = data;
CreationTime = creationTime;
WorkflowDefinitionId = defintionId;
Version = version;
Description = description;
Reference = reference;
NextExecution = nextExecution;
Status = status;
CompleteTime = completeTime;
TenantId = tenantId;
ExecutionPointers = new Collection<WorkflowExecutionPointer>();
}
public void AddPointer(WorkflowExecutionPointer pointer)
{
ExecutionPointers.Add(pointer);
}
public WorkflowExecutionPointer FindPointer(Guid id)
{
return ExecutionPointers.FirstOrDefault(point => point.Id.Equals(id));
}
public void Update(
WorkflowInstance instance,
IGuidGenerator guidGenerator,
ICurrentTenant currentTenant)
{
Data = JsonConvert.SerializeObject(instance.Data);
CreationTime = instance.CreateTime;
WorkflowDefinitionId = instance.WorkflowDefinitionId;
Version = instance.Version;
Description = instance.Description;
Reference = instance.Reference;
NextExecution = instance.NextExecution;
Status = instance.Status;
CompleteTime = instance.CompleteTime;
foreach (var pointer in instance.ExecutionPointers)
{
if (!Guid.TryParse(pointer.Id, out Guid pointerId))
{
pointerId = guidGenerator.Create();
}
var currentPointer = FindPointer(pointerId);
if (currentPointer != null)
{
currentPointer.Update(pointer);
continue;
}
AddPointer(pointer.ToWorkflowExecutionPointer(this, guidGenerator, currentTenant));
}
}
}
}

9
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowDbProperties.cs

@ -0,0 +1,9 @@
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public static class WorkflowDbProperties
{
public const string ConnectionStringName = "WorkflowCore";
public static string TablePrefix = "WF_";
}
}

47
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowEvent.cs

@ -0,0 +1,47 @@
using System;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class WorkflowEvent : Entity<Guid>, IMultiTenant, IHasCreationTime
{
public virtual Guid? TenantId { get; protected set; }
/// <summary>
/// 名称
/// </summary>
public virtual string EventName { get; protected set; }
/// <summary>
/// Key
/// </summary>
public virtual string EventKey { get; protected set; }
/// <summary>
/// 数据
/// </summary>
public virtual string EventData { get; protected set; }
/// <summary>
/// 是否已处理
/// </summary>
public virtual bool IsProcessed { get; set; }
/// <summary>
/// 建立时间
/// </summary>
public virtual DateTime CreationTime { get; protected set; }
protected WorkflowEvent() { }
public WorkflowEvent(
Guid id,
string name,
string key,
string data,
DateTime creationTime,
Guid? tenantId = null) : base(id)
{
EventName = name;
EventKey = key;
EventData = data;
CreationTime = creationTime;
TenantId = tenantId;
}
}
}

73
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowEventSubscription.cs

@ -0,0 +1,73 @@
using System;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class WorkflowEventSubscription : Entity<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid WorkflowId { get; protected set; }
public virtual int StepId { get; protected set; }
public virtual Guid ExecutionPointerId { get; protected set; }
public virtual string EventName { get; protected set; }
public virtual string EventKey { get; protected set; }
public virtual DateTime SubscribeAsOf { get; protected set; }
public virtual string SubscriptionData { get; protected set; }
public virtual string ExternalToken { get; protected set; }
public virtual string ExternalWorkerId { get; protected set; }
public virtual DateTime? ExternalTokenExpiry { get; protected set; }
protected WorkflowEventSubscription()
{
}
public WorkflowEventSubscription(
Guid id,
Guid workflowId,
int stepId,
Guid pointerId,
string eventName,
string eventKey,
DateTime subscribeAsOf,
string subscriptionData,
string externalToken,
string externalWorkerId,
DateTime? externalTokenExpiry = null,
Guid? tenantId = null) : base(id)
{
WorkflowId = workflowId;
StepId = stepId;
ExecutionPointerId = pointerId;
EventName = eventName;
EventKey = eventKey;
SubscribeAsOf = subscribeAsOf;
SubscriptionData = subscriptionData;
ExternalToken = externalToken;
ExternalWorkerId = externalWorkerId;
ExternalTokenExpiry = externalTokenExpiry;
TenantId = tenantId;
}
public void SetSubscriptionToken(
string token,
string workerId,
DateTime? expiry)
{
ExternalToken = token;
ExternalWorkerId = workerId;
ExternalTokenExpiry = expiry;
}
}
}

34
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowExecutionError.cs

@ -0,0 +1,34 @@
using System;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class WorkflowExecutionError : Entity<int>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid WorkflowId { get; protected set; }
public virtual Guid ExecutionPointerId { get; set; }
public virtual DateTime ErrorTime { get; set; }
public virtual string Message { get; set; }
protected WorkflowExecutionError() { }
public WorkflowExecutionError(
Guid workflowId,
Guid executionPointerId,
DateTime errorTime,
string message,
Guid? tenantId = null)
{
WorkflowId = workflowId;
ExecutionPointerId = executionPointerId;
ErrorTime = errorTime;
Message = message;
TenantId = tenantId;
}
}
}

162
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowExecutionPointer.cs

@ -0,0 +1,162 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class WorkflowExecutionPointer : Entity<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid WorkflowId { get; protected set; }
public virtual Workflow Workflow { get; protected set; }
public virtual int StepId { get; protected set; }
public virtual bool Active { get; protected set; }
public virtual DateTime? SleepUntil { get; protected set; }
public virtual string PersistenceData { get; protected set; }
public virtual DateTime? StartTime { get; protected set; }
public virtual DateTime? EndTime { get; protected set; }
public virtual string EventName { get; protected set; }
public virtual string EventKey { get; protected set; }
public virtual bool EventPublished { get; protected set; }
public virtual string EventData { get; protected set; }
public virtual string StepName { get; protected set; }
public virtual int RetryCount { get; protected set; }
public virtual string Children { get; protected set; }
public virtual string ContextItem { get; protected set; }
public virtual string PredecessorId { get; protected set; }
public virtual string Outcome { get; protected set; }
public virtual PointerStatus Status { get; protected set; }
public virtual string Scope { get; protected set; }
public virtual ICollection<WorkflowExtensionAttribute> ExtensionAttributes { get; protected set; }
protected WorkflowExecutionPointer()
{
ExtensionAttributes = new Collection<WorkflowExtensionAttribute>();
}
public WorkflowExecutionPointer(
Guid id,
Guid workflowId,
int stepId,
string stepName,
bool active,
string persistenceData,
string eventName,
string eventKey,
bool eventPublished,
string eventData,
int retryCount,
string children,
string contextItem,
string predecessorId,
string outcome,
string scope,
PointerStatus status = PointerStatus.Legacy,
DateTime? sleepUntil = null,
DateTime? startTime = null,
DateTime? endTime = null,
Guid? tenantId = null) : base(id)
{
WorkflowId = workflowId;
StepId = stepId;
StepName = stepName;
Active = active;
PersistenceData = persistenceData;
EventName = eventName;
EventKey = eventKey;
EventPublished = eventPublished;
EventData = eventData;
RetryCount = retryCount;
Children = children;
ContextItem = contextItem;
PredecessorId = predecessorId;
Outcome = outcome;
Scope = scope;
Status = status;
SleepUntil = sleepUntil;
StartTime = startTime;
EndTime = endTime;
TenantId = tenantId;
ExtensionAttributes = new Collection<WorkflowExtensionAttribute>();
}
public void Update(ExecutionPointer pointer)
{
StepId = pointer.StepId;
StepName = pointer.StepName;
Active = pointer.Active;
PersistenceData = JsonConvert.SerializeObject(pointer.PersistenceData);
EventName = pointer.EventName;
EventKey = pointer.EventKey;
EventPublished = pointer.EventPublished;
EventData = JsonConvert.SerializeObject(pointer.EventData);
RetryCount = pointer.RetryCount;
Children = pointer.Children.JoinAsString(";");
ContextItem = JsonConvert.SerializeObject(pointer.ContextItem);
PredecessorId = pointer.PredecessorId;
Outcome = JsonConvert.SerializeObject(pointer.Outcome);
Scope = pointer.Scope.JoinAsString(";");
Status = pointer.Status;
SleepUntil = pointer.SleepUntil;
StartTime = pointer.StartTime;
EndTime = pointer.EndTime;
foreach (var attribute in pointer.ExtensionAttributes)
{
var findAttr = FindAttribute(attribute.Key);
if (findAttr == null)
{
findAttr = new WorkflowExtensionAttribute(Id, attribute.Key, attribute.Value.SerializeObject());
}
else
{
findAttr.Key = attribute.Key;
findAttr.Value = attribute.Value.SerializeObject();
}
}
}
public WorkflowExtensionAttribute AddAttribute(string key, string value)
{
var attr = new WorkflowExtensionAttribute(Id, key, value);
ExtensionAttributes.Add(attr);
return attr;
}
public WorkflowExtensionAttribute FindAttribute(string key)
{
return ExtensionAttributes.FirstOrDefault(x => x.Key.Equals(key));
}
}
}

31
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowExtensionAttribute.cs

@ -0,0 +1,31 @@
using System;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class WorkflowExtensionAttribute : Entity<long>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid ExecutionPointerId { get; set; }
public virtual WorkflowExecutionPointer ExecutionPointer { get; set; }
public virtual string Key { get; set; }
public virtual string Value { get; set; }
protected WorkflowExtensionAttribute() { }
public WorkflowExtensionAttribute(
Guid pointerId,
string key,
string value)
{
ExecutionPointerId = pointerId;
Key = key;
Value = value;
}
}
}

113
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowExtensions.cs

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Linq;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public static class WorkflowExtensions
{
public static Event ToEvent(this WorkflowEvent workflowEvent)
{
return new Event
{
Id = workflowEvent.Id.ToString(),
EventName = workflowEvent.EventName,
EventKey = workflowEvent.EventKey,
EventTime = workflowEvent.CreationTime.ToUtcDateTime(),
IsProcessed = workflowEvent.IsProcessed,
EventData = workflowEvent.EventData.DeserializeObject()
};
}
public static EventSubscription ToEventSubscription(this WorkflowEventSubscription workflowEventSubscription)
{
return new EventSubscription
{
Id = workflowEventSubscription.Id.ToString(),
StepId = workflowEventSubscription.StepId,
SubscribeAsOf = workflowEventSubscription.SubscribeAsOf.ToUtcDateTime(),
SubscriptionData = workflowEventSubscription.SubscriptionData.DeserializeObject(),
EventKey = workflowEventSubscription.EventKey,
EventName = workflowEventSubscription.EventName,
ExecutionPointerId = workflowEventSubscription.ExecutionPointerId.ToString(),
ExternalWorkerId = workflowEventSubscription.ExternalWorkerId,
ExternalToken = workflowEventSubscription.ExternalToken,
ExternalTokenExpiry = workflowEventSubscription.ExternalTokenExpiry.ToNullableUtcDateTime(),
WorkflowId = workflowEventSubscription.WorkflowId.ToString()
};
}
public static WorkflowInstance ToWorkflowInstance(this Workflow workflow)
{
return new WorkflowInstance
{
Id = workflow.Id.ToString(),
WorkflowDefinitionId = workflow.WorkflowDefinitionId,
CompleteTime = workflow.CompleteTime.ToNullableUtcDateTime(),
CreateTime = workflow.CreationTime.ToUtcDateTime(),
Data = workflow.Data.SerializeObject(),
Status = workflow.Status,
Description = workflow.Description,
NextExecution = workflow.NextExecution,
Reference = workflow.Reference,
Version = workflow.Version,
ExecutionPointers = new ExecutionPointerCollection(
workflow.ExecutionPointers
.Select(pointer => pointer.ToExecutionPointer())
.ToList())
};
}
public static ExecutionPointer ToExecutionPointer(this WorkflowExecutionPointer pointer)
{
return new ExecutionPointer
{
Id = pointer.Id.ToString(),
EventData = pointer.EventData.DeserializeObject(),
EventKey = pointer.StepName,
EventName = pointer.EventName,
EventPublished = pointer.EventPublished,
ExtensionAttributes = pointer.ExtensionAttributes.ToExtensionAttributes(),
Active = pointer.Active,
Children = pointer.Children.Split(';').ToList(),
ContextItem = pointer.ContextItem.DeserializeObject(),
Scope = pointer.Scope.Split(';').ToList(),
Outcome = pointer.Outcome.DeserializeObject(),
PersistenceData = pointer.PersistenceData.DeserializeObject(),
PredecessorId = pointer.PredecessorId,
RetryCount = pointer.RetryCount,
Status = pointer.Status,
StepId = pointer.StepId,
StepName = pointer.StepName,
EndTime = pointer.EndTime.ToNullableUtcDateTime(),
StartTime = pointer.StartTime.ToNullableUtcDateTime(),
SleepUntil = pointer.SleepUntil.ToNullableUtcDateTime(),
};
}
public static ScheduledCommand ToScheduledCommand(
this WorkflowScheduledCommand command)
{
return new ScheduledCommand
{
CommandName = command.CommandName,
Data = command.Data,
ExecuteTime = command.ExecuteTime
};
}
public static Dictionary<string, object> ToExtensionAttributes(
this ICollection<WorkflowExtensionAttribute> attributes)
{
var attrDic = new Dictionary<string, object>();
foreach (var attr in attributes)
{
attrDic.Add(attr.Key, attr.Value.DeserializeObject());
}
return attrDic;
}
}
}

29
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowScheduledCommand.cs

@ -0,0 +1,29 @@
using System;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class WorkflowScheduledCommand : Entity<long>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual string CommandName { get; set; }
public virtual string Data { get; set; }
public virtual long ExecuteTime { get; set; }
protected WorkflowScheduledCommand() { }
public WorkflowScheduledCommand(
string commandName,
string data,
long executeTime,
Guid? tenantId = null)
{
CommandName = commandName;
Data = data;
ExecuteTime = executeTime;
TenantId = tenantId;
}
}
}

138
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/WorkflowCore/Models/WorkflowExtensions.cs

@ -0,0 +1,138 @@
using LINGYUN.Abp.WorkflowCore.Persistence;
using System;
using System.Collections.Generic;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
namespace WorkflowCore.Models
{
public static class WorkflowExtensions
{
public static Workflow ToWorkflow(
this WorkflowInstance instance,
IGuidGenerator generator,
ICurrentTenant currentTenant)
{
var workflow = new Workflow(
generator.Create(),
instance.CreateTime,
instance.WorkflowDefinitionId,
instance.Data.SerializeObject(),
instance.Version,
instance.Description,
instance.Reference,
instance.NextExecution,
instance.Status,
instance.CompleteTime,
currentTenant.Id);
foreach (var pointer in instance.ExecutionPointers)
{
pointer.ToWorkflowExecutionPointer(workflow, generator, currentTenant);
}
return workflow;
}
public static WorkflowExecutionPointer ToWorkflowExecutionPointer(
this ExecutionPointer executionPointer,
Workflow workflow,
IGuidGenerator generator,
ICurrentTenant currentTenant)
{
var pointer = new WorkflowExecutionPointer(
generator.Create(),
workflow.Id,
executionPointer.StepId,
executionPointer.StepName,
executionPointer.Active,
executionPointer.PersistenceData.SerializeObject(),
executionPointer.EventName,
executionPointer.EventKey,
executionPointer.EventPublished,
executionPointer.EventData.SerializeObject(),
executionPointer.RetryCount,
executionPointer.Children.JoinAsString(";"),
executionPointer.ContextItem.SerializeObject(),
executionPointer.PredecessorId,
executionPointer.Outcome.SerializeObject(),
executionPointer.Scope.JoinAsString(";"),
executionPointer.Status,
executionPointer.SleepUntil,
executionPointer.StartTime,
executionPointer.EndTime,
currentTenant.Id);
foreach (var attribute in executionPointer.ExtensionAttributes)
{
pointer.AddAttribute(attribute.Key, attribute.Value.SerializeObject());
}
executionPointer.Id = pointer.Id.ToString();
return pointer;
}
public static WorkflowEvent ToWorkflowEvent(
this Event @event,
IGuidGenerator generator,
ICurrentTenant currentTenant)
{
var we = new WorkflowEvent(
generator.Create(),
@event.EventName,
@event.EventKey,
@event.EventData.SerializeObject(),
@event.EventTime,
currentTenant.Id)
{
IsProcessed = @event.IsProcessed
};
return we;
}
public static WorkflowEventSubscription ToWorkflowEventSubscription(
this EventSubscription subscription,
IGuidGenerator generator,
ICurrentTenant currentTenant)
{
return new WorkflowEventSubscription(
generator.Create(),
Guid.Parse(subscription.WorkflowId),
subscription.StepId,
Guid.Parse(subscription.ExecutionPointerId),
subscription.EventName,
subscription.EventKey,
subscription.SubscribeAsOf,
subscription.SubscriptionData.SerializeObject(),
subscription.ExternalToken,
subscription.ExternalWorkerId,
subscription.ExternalTokenExpiry,
currentTenant.Id);
}
public static WorkflowExecutionError ToWorkflowExecutionError(
this ExecutionError executionError,
ICurrentTenant currentTenant)
{
return new WorkflowExecutionError(
Guid.Parse(executionError.WorkflowId),
Guid.Parse(executionError.ExecutionPointerId),
executionError.ErrorTime,
executionError.Message,
currentTenant.Id);
}
public static WorkflowScheduledCommand ToWorkflowScheduledCommand(
this ScheduledCommand command,
ICurrentTenant currentTenant)
{
return new WorkflowScheduledCommand(
command.CommandName,
command.Data,
command.ExecuteTime,
currentTenant.Id);
}
}
}

16
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN.Abp.WorkflowCore.RabbitMQ.csproj

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.RabbitMQ" Version="4.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore\LINGYUN.Abp.WorkflowCore.csproj" />
</ItemGroup>
</Project>

15
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/AbpRabbitMQWorkflowCoreOptions.cs

@ -0,0 +1,15 @@
namespace LINGYUN.Abp.WorkflowCore.RabbitMQ
{
public class AbpRabbitMQWorkflowCoreOptions
{
/// <summary>
/// Default value: "AbpWorkflows.".
/// </summary>
public string DefaultQueueNamePrefix { get; set; }
public AbpRabbitMQWorkflowCoreOptions()
{
DefaultQueueNamePrefix = "AbpWorkflows.";
}
}
}

135
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/AbpRabbitMqQueueProvider.cs

@ -0,0 +1,135 @@
using Microsoft.Extensions.Options;
using RabbitMQ.Client;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.RabbitMQ;
using Volo.Abp.Threading;
using WorkflowCore.Interface;
namespace LINGYUN.Abp.WorkflowCore.RabbitMQ
{
public class AbpRabbitMqQueueProvider : IQueueProvider
{
private string ChannelPrefix = "WorkflowQueue.";
protected bool IsDiposed { get; private set; }
protected SemaphoreSlim SyncObj = new SemaphoreSlim(1, 1);
protected IChannelAccessor ChannelAccessor { get; private set; }
protected IChannelPool ChannelPool { get; }
protected IQueueNameNormalizer QueueNameNormalizer { get; }
protected AbpRabbitMQWorkflowCoreOptions RabbitMQWorkflowCoreOptions { get; }
protected WorkflowQueueConfiguration QueueConfiguration { get; }
public bool IsDequeueBlocking => false;
public AbpRabbitMqQueueProvider(
IChannelPool channelPool,
IQueueNameNormalizer queueNameNormalizer,
IOptions<AbpRabbitMQWorkflowCoreOptions> options)
{
ChannelPool = channelPool;
QueueNameNormalizer = queueNameNormalizer;
RabbitMQWorkflowCoreOptions = options.Value;
QueueConfiguration = GetOrCreateWorkflowQueueConfiguration();
}
protected virtual WorkflowQueueConfiguration GetOrCreateWorkflowQueueConfiguration()
{
return new WorkflowQueueConfiguration(
RabbitMQWorkflowCoreOptions.DefaultQueueNamePrefix + "Workflow-Core",
durable: true,
exclusive: false,
autoDelete: false);
}
public async Task<string> DequeueWork(QueueType queue, CancellationToken cancellationToken)
{
using (await SyncObj.LockAsync(cancellationToken))
{
ChannelAccessor.Channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);
var msg = ChannelAccessor.Channel.BasicGet(QueueNameNormalizer.NormalizeKey(queue), false);
if (msg != null)
{
var data = Encoding.UTF8.GetString(msg.Body.ToArray());
ChannelAccessor.Channel.BasicAck(msg.DeliveryTag, false);
return data;
}
return null;
}
}
public async Task QueueWork(string id, QueueType queue)
{
using (await SyncObj.LockAsync())
{
var body = Encoding.UTF8.GetBytes(id);
ChannelAccessor.Channel.BasicPublish(
exchange: "",
routingKey: QueueNameNormalizer.NormalizeKey(queue),
basicProperties: null,
body: body
);
}
}
public async Task Start()
{
CheckDisposed();
using (await SyncObj.LockAsync())
{
await EnsureInitializedAsync();
}
}
public Task Stop()
{
Dispose();
return Task.CompletedTask;
}
public void Dispose()
{
if (IsDiposed)
{
return;
}
IsDiposed = true;
ChannelAccessor?.Dispose();
}
protected virtual Task EnsureInitializedAsync()
{
if (ChannelAccessor != null)
{
return Task.CompletedTask;
}
ChannelAccessor = ChannelPool.Acquire(
ChannelPrefix + QueueConfiguration.QueueName,
QueueConfiguration.ConnectionName
);
return Task.CompletedTask;
}
protected void CheckDisposed()
{
if (IsDiposed)
{
throw new AbpException("This object is disposed!");
}
}
}
}

24
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/AbpWorkflowCoreRabbitMQModule.cs

@ -0,0 +1,24 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.RabbitMQ;
using WorkflowCore.Interface;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.RabbitMQ
{
[DependsOn(typeof(AbpRabbitMqModule))]
[DependsOn(typeof(AbpWorkflowCoreModule))]
public class AbpWorkflowCoreRabbitMQModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddSingleton<IQueueProvider, AbpRabbitMqQueueProvider>();
context.Services.AddSingleton<AbpRabbitMqQueueProvider>();
PreConfigure<WorkflowOptions>(options =>
{
options.UseQueueProvider(provider => provider.GetRequiredService<AbpRabbitMqQueueProvider>());
});
}
}
}

9
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/IQueueNameNormalizer.cs

@ -0,0 +1,9 @@
using WorkflowCore.Interface;
namespace LINGYUN.Abp.WorkflowCore.RabbitMQ
{
public interface IQueueNameNormalizer
{
string NormalizeKey(QueueType queue);
}
}

23
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/QueueNameNormalizer.cs

@ -0,0 +1,23 @@
using Volo.Abp.DependencyInjection;
using WorkflowCore.Interface;
namespace LINGYUN.Abp.WorkflowCore.RabbitMQ
{
public class QueueNameNormalizer : IQueueNameNormalizer, ISingletonDependency
{
public string NormalizeKey(QueueType queue)
{
switch (queue)
{
case QueueType.Workflow:
return "wfc.workflow_queue";
case QueueType.Event:
return "wfc.event_queue";
case QueueType.Index:
return "wfc.index_queue";
default:
return null;
}
}
}
}

21
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/WorkflowQueueConfiguration.cs

@ -0,0 +1,21 @@
using Volo.Abp.RabbitMQ;
namespace LINGYUN.Abp.WorkflowCore.RabbitMQ
{
public class WorkflowQueueConfiguration : QueueDeclareConfiguration
{
public string ConnectionName { get; set; }
public WorkflowQueueConfiguration(
string queueName,
string connectionName = null,
bool durable = true,
bool exclusive = false,
bool autoDelete = false,
string deadLetterQueueName = null)
: base(queueName, durable, exclusive, autoDelete, deadLetterQueueName)
{
ConnectionName = connectionName;
}
}
}

14
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN.Abp.WorkflowCore.csproj

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Core" Version="4.4.0" />
<PackageReference Include="WorkflowCore.DSL" Version="3.6.1" />
</ItemGroup>
</Project>

11
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreConventionalRegistrar.cs

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.WorkflowCore
{
public class AbpWorkflowCoreConventionalRegistrar : DefaultConventionalRegistrar
{
}
}

58
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreModule.cs

@ -0,0 +1,58 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using Volo.Abp.Modularity;
using WorkflowCore.Interface;
namespace LINGYUN.Abp.WorkflowCore
{
public class AbpWorkflowCoreModule : AbpModule
{
private readonly static IList<Type> _definitionWorkflows = new List<Type>();
public override void PreConfigureServices(ServiceConfigurationContext context)
{
AutoAddDefinitionWorkflows(context.Services);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddWorkflow(options =>
{
context.Services.ExecutePreConfiguredActions(options);
});
context.Services.AddWorkflowDSL();
}
public override void OnApplicationInitialization(Volo.Abp.ApplicationInitializationContext context)
{
var workflowRegistry = context.ServiceProvider.GetRequiredService<IWorkflowRegistry>();
foreach (var definitionWorkflow in _definitionWorkflows)
{
var workflow = context.ServiceProvider.GetRequiredService(definitionWorkflow);
workflowRegistry.RegisterWorkflow(workflow as IWorkflow<WorkflowParamDictionary>);
}
var workflowHost = context.ServiceProvider.GetRequiredService<IWorkflowHost>();
workflowHost.Start();
}
public override void OnApplicationShutdown(Volo.Abp.ApplicationShutdownContext context)
{
var workflowHost = context.ServiceProvider.GetRequiredService<IWorkflowHost>();
workflowHost.Stop();
}
private static void AutoAddDefinitionWorkflows(IServiceCollection services)
{
services.OnRegistred(context =>
{
if (typeof(WorkflowBase).IsAssignableFrom(context.ImplementationType))
{
_definitionWorkflows.Add(context.ImplementationType);
}
});
}
}
}

10
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreOptions.cs

@ -0,0 +1,10 @@
using Volo.Abp.Collections;
using WorkflowCore.Interface;
namespace LINGYUN.Abp.WorkflowCore
{
public class AbpWorkflowCoreOptions
{
public ITypeList<IWorkflow> DefinitionProviders { get; }
}
}

6
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/IWorkflowEnabled.cs

@ -0,0 +1,6 @@
namespace LINGYUN.Abp.WorkflowCore
{
public interface IWorkflowEnabled
{
}
}

10
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/IWorkflowManager.cs

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace LINGYUN.Abp.WorkflowCore
{
public interface IWorkflowManager
{
}
}

14
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/NullStepBody.cs

@ -0,0 +1,14 @@
using Volo.Abp.DependencyInjection;
using WorkflowCore.Interface;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore
{
public class NullStepBody : StepBody, ITransientDependency
{
public override ExecutionResult Run(IStepExecutionContext context)
{
return ExecutionResult.Next();
}
}
}

14
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowBase.cs

@ -0,0 +1,14 @@
using WorkflowCore.Interface;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.WorkflowCore
{
public abstract class WorkflowBase : IWorkflow<WorkflowParamDictionary>, ISingletonDependency
{
public abstract string Id { get; }
public abstract int Version { get; }
public abstract void Build(IWorkflowBuilder<WorkflowParamDictionary> builder);
}
}

9
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowConditionCondition.cs

@ -0,0 +1,9 @@
namespace LINGYUN.Abp.WorkflowCore
{
public class WorkflowConditionCondition
{
public string Field { get; set; }
public string Operator { get; set; }
public object Value { get; set; }
}
}

15
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowConditionNode.cs

@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.WorkflowCore
{
public class WorkflowConditionNode
{
public string Label { get; set; }
public string NodeId { get; set; }
public IEnumerable<WorkflowConditionCondition> Conditions { get; set; }
public WorkflowConditionNode()
{
Conditions = new List<WorkflowConditionCondition>();
}
}
}

17
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowDefinition.cs

@ -0,0 +1,17 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.WorkflowCore
{
public class WorkflowDefinition
{
public string Id { get; set; }
public string Title { get; set; }
public int Version { get; set; }
public string Description { get; set; }
public string Icon { get; set; }
public string Color { get; set; }
public string Group { get; set; }
public ICollection<WorkflowNode> Nodes { get; set; }
public ICollection<WorkflowFormData> Inputs { get; set; }
}
}

24
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowFormData.cs

@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.WorkflowCore
{
public class WorkflowFormData
{
public string Id { get; set; }
public string Name { get; set; }
public string Label { get; set; }
public string Type { get; set; }
public object Value { get; set; }
public IEnumerable<object> Styles { get; set; }
public int? MaxLength { get; set; }
public int? MinLength { get; set; }
public IEnumerable<object> Items { get; set; }
public IEnumerable<object> Rules { get; set; }
public WorkflowFormData()
{
Styles = new List<object>();
Items = new List<object>();
Rules =new List<object>();
}
}
}

105
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowManager.cs

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Volo.Abp;
using WorkflowCore.Interface;
using WorkflowCore.Models.DefinitionStorage.v1;
using WorkflowCore.Services.DefinitionStorage;
using WDF = WorkflowCore.Models.WorkflowDefinition;
namespace LINGYUN.Abp.WorkflowCore
{
public class WorkflowManager : IWorkflowManager
{
private readonly IWorkflowRegistry _workflowRegistry;
private readonly IDefinitionLoader _definitionLoader;
protected IReadOnlyCollection<WorkflowStepBody> _stepBodys;
internal void Initlize()
{
}
public WDF BuildWorkflow(WorkflowDefinition definition)
{
if (_workflowRegistry.IsRegistered(definition.Id, definition.Version))
{
throw new AbpException($"Workflow {definition.Id} has ben registered!");
}
var definitionSource = new DefinitionSourceV1()
{
Id = definition.Id,
Version = definition.Version,
DataType = $"{typeof(Dictionary<string, object>).FullName}, {typeof(Dictionary<string, object>).Assembly.FullName}",
Description = definition.Title,
};
BuildWorkflow(definition.Nodes, definitionSource, _stepBodys, definition.Nodes.First(u => u.Key.ToLower().StartsWith("start")));
var json = definitionSource.SerializeObject();
var def = _definitionLoader.LoadDefinition(json, Deserializers.Json);
return def;
}
protected virtual void BuildWorkflow(IEnumerable<WorkflowNode> allNodes, DefinitionSourceV1 source, IEnumerable<WorkflowStepBody> stepBodys, WorkflowNode node)
{
if (source.Steps.Any(u => u.Id == node.Key))
{
return;
}
var stepSource = new StepSourceV1
{
Id = node.Key,
Name = node.Key
};
WorkflowStepBody stepbody = stepBodys.FirstOrDefault(u => u.Name == node.StepBody.Name);
if (stepbody == null)
{
stepbody = new WorkflowStepBody() { StepBodyType = typeof(NullStepBody) };
}
stepSource.StepType = $"{stepbody.StepBodyType.FullName}, {stepbody.StepBodyType.Assembly.FullName}";
foreach (var input in stepbody.Inputs)
{
var value = node.StepBody.Inputs[input.Key].Value;
if (!(value is IDictionary<string, object> || value is IDictionary<object, object>))
{
value = $"\"{value}\"";
}
stepSource.Inputs.AddIfNotContains(new KeyValuePair<string, object>(input.Key, value));
}
source.Steps.Add(stepSource);
BuildBranching(allNodes, source, stepSource, stepBodys, node.NextNodes);
}
protected virtual void BuildBranching(IEnumerable<WorkflowNode> allNodes, DefinitionSourceV1 source, StepSourceV1 stepSource, IEnumerable<WorkflowStepBody> stepBodys, IEnumerable<WorkflowConditionNode> nodes)
{
foreach (var nextNode in nodes)
{
var node = allNodes.First(u => u.Key == nextNode.NodeId);
stepSource.SelectNextStep[nextNode.NodeId] = "1==1";
if (nextNode.Conditions.Count() > 0)
{
List<string> exps = new List<string>();
foreach (var cond in nextNode.Conditions)
{
if (cond.Value is string && (!decimal.TryParse(cond.Value.ToString(), out decimal tempValue)))
{
if (cond.Operator != "==" && cond.Operator != "!=")
{
throw new AbpException($" if {cond.Field} is type of 'String', the Operator must be \"==\" or \"!=\"");
}
exps.Add($"data[\"{cond.Field}\"].ToString() {cond.Operator} \"{cond.Value}\"");
continue;
}
exps.Add($"decimal.Parse(data[\"{cond.Field}\"].ToString()) {cond.Operator} {cond.Value}");
}
stepSource.SelectNextStep[nextNode.NodeId] = string.Join(" && ", exps);
}
BuildWorkflow(allNodes, source, stepBodys, node);
}
}
}
}

20
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowNode.cs

@ -0,0 +1,20 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.WorkflowCore
{
public class WorkflowNode
{
public string Key { get; set; }
public string Title { get; set; }
public int[] Position { get; set; }
public string Type { get; set; }
public WorkflowStepBody StepBody { get; set; }
public IEnumerable<string> ParentNodes { get; set; }
public IEnumerable<WorkflowConditionNode> NextNodes { get; set; }
public WorkflowNode()
{
StepBody = new WorkflowStepBody();
NextNodes = new List<WorkflowConditionNode>();
}
}
}

10
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowParam.cs

@ -0,0 +1,10 @@
namespace LINGYUN.Abp.WorkflowCore
{
public class WorkflowParam
{
public string Name { get; set; }
public string DisplayName { get; set; }
public string InputType { get; set; }
public object Value { get; set; }
}
}

8
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowParamDictionary.cs

@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.WorkflowCore
{
public class WorkflowParamDictionary : Dictionary<string, WorkflowParam>
{
}
}

8
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowParamInput.cs

@ -0,0 +1,8 @@
namespace LINGYUN.Abp.WorkflowCore
{
public class WorkflowParamInput
{
public string Name { get; set; }
public object Value { get; set; }
}
}

17
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowStepBody.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
namespace LINGYUN.Abp.WorkflowCore
{
public class WorkflowStepBody
{
public string Name { get; set; }
public Type StepBodyType { get; set; }
public string DisplayName { get; set; }
public Dictionary<string, WorkflowParamInput> Inputs { get; set; }
public WorkflowStepBody()
{
Inputs = new Dictionary<string, WorkflowParamInput>();
}
}
}

19
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/System/ObjectSerializerExtensions.cs

@ -0,0 +1,19 @@
using Newtonsoft.Json;
namespace System
{
public static class ObjectSerializerExtensions
{
private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All };
public static string SerializeObject(this object obj)
{
return JsonConvert.SerializeObject(obj, SerializerSettings);
}
public static object DeserializeObject(this string str)
{
return JsonConvert.DeserializeObject(str, SerializerSettings);
}
}
}

19
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/System/UtcDateTimeExtensions.cs

@ -0,0 +1,19 @@
namespace System
{
public static class UtcDateTimeExtensions
{
public static DateTime? ToNullableUtcDateTime(this DateTime? dateTime)
{
if (dateTime.HasValue)
{
return dateTime.Value.ToUtcDateTime();
}
return null;
}
public static DateTime ToUtcDateTime(this DateTime dateTime)
{
return DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
}
}
}

17
aspnet-core/tests/LINGYUN.Abp.WorkflowCore.Tests/LINGYUN.Abp.WorkflowCore.Tests.csproj

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace />
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" />
</ItemGroup>
</Project>

8
aspnet-core/tests/LINGYUN.Abp.WorkflowCore.Tests/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreTestBase.cs

@ -0,0 +1,8 @@
using LINGYUN.Abp.Tests;
namespace LINGYUN.Abp.WorkflowCore
{
public abstract class AbpWorkflowCoreTestBase : AbpTestsBase<AbpWorkflowCoreTestModule>
{
}
}

11
aspnet-core/tests/LINGYUN.Abp.WorkflowCore.Tests/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreTestModule.cs

@ -0,0 +1,11 @@
using LINGYUN.Abp.Tests;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.WorkflowCore
{
[DependsOn(typeof(AbpTestsBaseModule))]
[DependsOn(typeof(AbpWorkflowCoreModule))]
public class AbpWorkflowCoreTestModule : AbpModule
{
}
}
Loading…
Cancel
Save