Browse Source

Merge pull request #432 from colinin/workflow

enhanced workflow
pull/474/head
yx lin 4 years ago
committed by GitHub
parent
commit
71fff024a6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .gitignore
  2. 2
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/AbpCliModule.cs
  3. 32
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/CommandSelector.cs
  4. 98
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/HelpCommand.cs
  5. 3
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/FodyWeavers.xml
  6. 30
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/FodyWeavers.xsd
  7. 21
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/LINGYUN.Abp.WorkflowCore.Components.csproj
  8. 14
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/LINGYUN/Abp/WorkflowCore/Components/AbpWorkflowCoreComponentsModule.cs
  9. 81
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/LINGYUN/Abp/WorkflowCore/Components/Primitives/RemoteService.cs
  10. 60
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/LINGYUN/Abp/WorkflowCore/Components/Primitives/SendEmail.cs
  11. 51
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/LINGYUN/Abp/WorkflowCore/Components/Primitives/SendSms.cs
  12. 3
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/README.md
  13. 3
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/FodyWeavers.xml
  14. 30
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/FodyWeavers.xsd
  15. 20
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/LINGYUN.Abp.WorkflowCore.DistributedLock.csproj
  16. 73
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/LINGYUN/Abp/WorkflowCore/DistributedLock/AbpDistributedLockProvider.cs
  17. 24
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/LINGYUN/Abp/WorkflowCore/DistributedLock/AbpWorkflowCoreDistributedLockModule.cs
  18. 3
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/FodyWeavers.xml
  19. 30
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/FodyWeavers.xsd
  20. 3
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/LINGYUN.Abp.WorkflowCore.Elasticsearch.csproj
  21. 3
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/LINGYUN/Abp/WorkflowCore/Elasticsearch/AbpElasticsearchIndexer.cs
  22. 4
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/LINGYUN/Abp/WorkflowCore/Elasticsearch/AbpWorkflowCoreElasticsearchOptions.cs
  23. 3
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/FodyWeavers.xml
  24. 30
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/FodyWeavers.xsd
  25. 5
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/LINGYUN.Abp.WorkflowCore.LifeCycleEvent.csproj
  26. 20
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/LINGYUN/Abp/WorkflowCore/LifeCycleEvent/AbpEventBusProvider.cs
  27. 5
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/LINGYUN/Abp/WorkflowCore/LifeCycleEvent/LifeCycleEventWrap.cs
  28. 3
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/FodyWeavers.xml
  29. 30
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/FodyWeavers.xsd
  30. 21
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch.csproj
  31. 26
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/AbpWorkflowCorePersistenceElasticsearchModule.cs
  32. 18
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/AbpWorkflowCorePersistenceElasticsearchOptions.cs
  33. 558
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/ElasticsearchPersistenceProvider.cs
  34. 9
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/IPersistenceIndexInitializer.cs
  35. 7
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/IPersistenceIndexNameNormalizer.cs
  36. 32
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/Models/PersistedScheduledCommand.cs
  37. 11
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/PersistenceIndexConsts.cs
  38. 163
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/PersistenceIndexInitializer.cs
  39. 21
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/PersistenceIndexNameNormalizer.cs
  40. 3
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/FodyWeavers.xml
  41. 30
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/FodyWeavers.xsd
  42. 19
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore.csproj
  43. 10
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/AbpWorkflowCorePersistenceEntityFrameworkCoreModule.cs
  44. 2
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowEventRepository.cs
  45. 2
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowEventSubscriptionRepository.cs
  46. 2
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowExecutionErrorRepository.cs
  47. 6
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowRepository.cs
  48. 15
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/EfCoreWorkflowScheduledCommandRepository.cs
  49. 14
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/IWorkflowDbContext.cs
  50. 14
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/WorkflowDbContext.cs
  51. 14
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/LINGYUN/Abp/WorkflowCore/Persistence/EntityFrameworkCore/WorkflowDbContextModelBuilderExtensions.cs
  52. 3
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/FodyWeavers.xml
  53. 30
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/FodyWeavers.xsd
  54. 19
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN.Abp.WorkflowCore.Persistence.csproj
  55. 4
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/AbpWorkflowCorePersistenceModule.cs
  56. 54
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/AbpWorkflowPersistenceProvider.cs
  57. 292
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/ExtensionMethods.cs
  58. 2
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowEventRepository.cs
  59. 2
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowEventSubscriptionRepository.cs
  60. 2
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowExecutionErrorRepository.cs
  61. 4
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowRepository.cs
  62. 10
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/IWorkflowScheduledCommandRepository.cs
  63. 6
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/PersistedEvent.cs
  64. 12
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/PersistedExecutionError.cs
  65. 123
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/PersistedExecutionPointer.cs
  66. 10
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/PersistedExtensionAttribute.cs
  67. 6
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/PersistedScheduledCommand.cs
  68. 17
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/PersistedSubscription.cs
  69. 66
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/PersistedWorkflow.cs
  70. 100
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/Workflow.cs
  71. 159
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowExecutionPointer.cs
  72. 113
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowExtensions.cs
  73. 139
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/WorkflowCore/Models/WorkflowExtensions.cs
  74. 3
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/FodyWeavers.xml
  75. 30
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/FodyWeavers.xsd
  76. 5
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN.Abp.WorkflowCore.RabbitMQ.csproj
  77. 5
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/LINGYUN/Abp/WorkflowCore/RabbitMQ/WorkflowQueueConfiguration.cs
  78. 3
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/FodyWeavers.xml
  79. 30
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/FodyWeavers.xsd
  80. 22
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN.Abp.WorkflowCore.csproj
  81. 22
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpDateTimeProvider.cs
  82. 19
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreConventionalRegistrar.cs
  83. 41
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreModule.cs
  84. 11
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpWorkflowCoreOptions.cs
  85. 10
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/IWorkflowManager.cs
  86. 14
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/NullStepBody.cs
  87. 8
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/StepBodyAsyncBase.cs
  88. 8
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/StepBodyBase.cs
  89. 15
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowBase.cs
  90. 33
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowRegisterHelper.cs
  91. 23
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/System/ObjectSerializerExtensions.cs
  92. 18
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/System/WorkflowTypeExtensions.cs
  93. 3
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/FodyWeavers.xml
  94. 30
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/FodyWeavers.xsd
  95. 20
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN.Abp.WorkflowManagement.Application.Contracts.csproj
  96. 23
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Authorization/WorkflowManagementPermissionDefinitionProvider.cs
  97. 9
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Authorization/WorkflowManagementPermissions.cs
  98. 14
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/WorkflowManagementApplicationContractsModule.cs
  99. 7
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/WorkflowManagementRemoteServiceConsts.cs
  100. 3
      aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/FodyWeavers.xml

1
.gitignore

@ -12,6 +12,7 @@ appsettings.*.json
tempkey.jwk
.vs
Publish
LocalNuget
/tests/e2e/videos/
/tests/e2e/screenshots/

2
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/AbpCliModule.cs

@ -15,6 +15,8 @@ namespace LINGYUN.Abp.Cli
{
Configure<AbpCliOptions>(options =>
{
options.Commands.Clear();
options.Commands["help"] = typeof(HelpCommand);
options.Commands["create"] = typeof(CreateCommand);
});
}

32
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/CommandSelector.cs

@ -0,0 +1,32 @@
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using Volo.Abp.Cli;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Commands;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Cli.Commands
{
[Dependency(ReplaceServices = true)]
public class CommandSelector : ICommandSelector, ITransientDependency
{
protected AbpCliOptions Options { get; }
public CommandSelector(IOptions<AbpCliOptions> options)
{
Options = options.Value;
}
public Type Select(CommandLineArgs commandLineArgs)
{
if (commandLineArgs.Command.IsNullOrWhiteSpace())
{
return typeof(HelpCommand);
}
return Options.Commands.GetOrDefault(commandLineArgs.Command)
?? typeof(HelpCommand);
}
}
}

98
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/HelpCommand.cs

@ -0,0 +1,98 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Cli;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Commands;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Cli.Commands
{
public class HelpCommand : IConsoleCommand, ITransientDependency
{
public ILogger<HelpCommand> Logger { get; set; }
protected AbpCliOptions AbpCliOptions { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
public HelpCommand(IOptions<AbpCliOptions> cliOptions,
IServiceScopeFactory serviceScopeFactory)
{
ServiceScopeFactory = serviceScopeFactory;
Logger = NullLogger<HelpCommand>.Instance;
AbpCliOptions = cliOptions.Value;
}
public Task ExecuteAsync(CommandLineArgs commandLineArgs)
{
if (string.IsNullOrWhiteSpace(commandLineArgs.Target))
{
Logger.LogInformation(GetUsageInfo());
return Task.CompletedTask;
}
if (!AbpCliOptions.Commands.ContainsKey(commandLineArgs.Target))
{
Logger.LogWarning($"There is no command named {commandLineArgs.Target}.");
Logger.LogInformation(GetUsageInfo());
return Task.CompletedTask;
}
var commandType = AbpCliOptions.Commands[commandLineArgs.Target];
using (var scope = ServiceScopeFactory.CreateScope())
{
var command = (IConsoleCommand)scope.ServiceProvider.GetRequiredService(commandType);
Logger.LogInformation(command.GetUsageInfo());
}
return Task.CompletedTask;
}
public string GetUsageInfo()
{
var sb = new StringBuilder();
sb.AppendLine("");
sb.AppendLine("Usage:");
sb.AppendLine("");
sb.AppendLine(" labp <command> <target> [options]");
sb.AppendLine("");
sb.AppendLine("Command List:");
sb.AppendLine("");
foreach (var command in AbpCliOptions.Commands.ToArray())
{
string shortDescription;
using (var scope = ServiceScopeFactory.CreateScope())
{
shortDescription = ((IConsoleCommand)scope.ServiceProvider
.GetRequiredService(command.Value)).GetShortDescription();
}
sb.Append(" > ");
sb.Append(command.Key);
sb.Append(string.IsNullOrWhiteSpace(shortDescription) ? "" : ":");
sb.Append(" ");
sb.AppendLine(shortDescription);
}
sb.AppendLine("");
sb.AppendLine("To get a detailed help for a command:");
sb.AppendLine("");
sb.AppendLine(" labp help <command>");
sb.AppendLine("");
return sb.ToString();
}
public string GetShortDescription()
{
return "Show command line help. Write ` labp help <command> `";
}
}
}

3
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

21
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/LINGYUN.Abp.WorkflowCore.Components.csproj

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Emailing" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Sms" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Http.Client" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore\LINGYUN.Abp.WorkflowCore.csproj" />
</ItemGroup>
</Project>

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

@ -0,0 +1,14 @@
using Volo.Abp.Emailing;
using Volo.Abp.Modularity;
using Volo.Abp.Sms;
namespace LINGYUN.Abp.WorkflowCore.Components
{
[DependsOn(
typeof(AbpSmsModule),
typeof(AbpEmailingModule),
typeof(AbpWorkflowCoreModule))]
public class AbpWorkflowCoreComponentsModule : AbpModule
{
}
}

81
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/LINGYUN/Abp/WorkflowCore/Components/Primitives/RemoteService.cs

@ -0,0 +1,81 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
using WorkflowCore.Interface;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Components.Primitives
{
public class RemoteService : StepBodyAsyncBase
{
private readonly ICurrentTenant _currentTenant;
private readonly IServiceProvider _serviceProvider;
public RemoteService(
ICurrentTenant currentTenant,
IServiceProvider serviceProvider)
{
_currentTenant = currentTenant;
_serviceProvider = serviceProvider;
Data = new Dictionary<string, object>();
}
/// <summary>
/// 远程服务接口类型
/// </summary>
public string Interface { get; set; }
/// <summary>
/// 远程服务方法名称
/// </summary>
public string Method { get; set; }
/// <summary>
/// 请求参数
/// </summary>
public Dictionary<string, object> Data { get; set; }
/// <summary>
/// 调用结果
/// </summary>
public object Result { get; set; }
public Guid? TenantId { get; set; }
public string CurrentCulture { get; set; }
public override async Task<ExecutionResult> RunAsync(IStepExecutionContext context)
{
var serviceType = Type.GetType(Interface, true, true);
var method = serviceType.GetMethod(Method);
var serviceFactory = _serviceProvider.GetRequiredService(serviceType);
using (_currentTenant.Change(TenantId))
{
using (CultureHelper.Use(CurrentCulture ?? CultureInfo.CurrentCulture.Name))
{
// TODO: 身份令牌?
// 工作流中是否需要调用API, 还是用户调用API之后传递事件激活下一个步骤
// Abp Api动态代理
var result = (Task)method.Invoke(serviceFactory, Data.Select(x => x.Value).ToArray());
await result;
if (!method.ReturnType.GenericTypeArguments.IsNullOrEmpty())
{
var resultType = method.ReturnType.GenericTypeArguments[0];
var resultProperty = typeof(Task<>)
.MakeGenericType(resultType)
.GetProperty(nameof(Task<object>.Result), BindingFlags.Instance | BindingFlags.Public);
Result = resultProperty.GetValue(result);
}
return ExecutionResult.Next();
}
}
}
}
}

60
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/LINGYUN/Abp/WorkflowCore/Components/Primitives/SendEmail.cs

@ -0,0 +1,60 @@
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Globalization;
using System.Threading.Tasks;
using Volo.Abp.Emailing;
using Volo.Abp.Emailing.Templates;
using Volo.Abp.TextTemplating;
using WorkflowCore.Interface;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Components.Primitives
{
public class SendEmail : StepBodyAsyncBase
{
public ILogger<SendEmail> Logger { protected get; set; }
private readonly IEmailSender _emailSender;
private readonly ITemplateRenderer _templateRenderer;
public SendEmail(
IEmailSender emailSender,
ITemplateRenderer templateRenderer)
{
_emailSender = emailSender;
_templateRenderer = templateRenderer;
Logger = NullLogger<SendEmail>.Instance;
}
[NotNull]
public string Title { get; set; }
[NotNull]
public string Receivers { get; set; }
[CanBeNull]
public object Data { get; set; }
[CanBeNull]
public string Template { get; set; }
public override async Task<ExecutionResult> RunAsync(IStepExecutionContext context)
{
Logger.LogInformation("Working on sending email step.");
var templateContent = await _templateRenderer.RenderAsync(
Template.IsNullOrWhiteSpace() ? StandardEmailTemplates.Message : Template,
Data,
CultureInfo.CurrentCulture.Name);
await _emailSender.SendAsync(Receivers, Title, templateContent);
Logger.LogInformation("Email sent, forward to next step.");
return ExecutionResult.Next();
}
}
}

51
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/LINGYUN/Abp/WorkflowCore/Components/Primitives/SendSms.cs

@ -0,0 +1,51 @@
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Threading.Tasks;
using Volo.Abp.Sms;
using WorkflowCore.Interface;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Components.Primitives
{
public class SendSms : StepBodyAsyncBase
{
public ILogger<SendSms> Logger { protected get; set; }
private readonly ISmsSender _smsSender;
public SendSms(ISmsSender smsSender)
{
_smsSender = smsSender;
Logger = NullLogger<SendSms>.Instance;
}
[NotNull]
public string Message { get; set; }
[NotNull]
public string PhoneNumber { get; set; }
[CanBeNull]
public string Template { get; set; }
public override async Task<ExecutionResult> RunAsync(IStepExecutionContext context)
{
Logger.LogInformation("Working on sending sms message step.");
var smsMessage = new SmsMessage(PhoneNumber, Message);
if (!Template.IsNullOrWhiteSpace())
{
smsMessage.Properties.Add("TemplateCode", Template);
}
await _smsSender.SendAsync(smsMessage);
Logger.LogInformation("Sms message sent, forward to next step.");
return ExecutionResult.Next();
}
}
}

3
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Components/README.md

@ -0,0 +1,3 @@
# LINGYUN.Abp.WorkflowCore.Components
预设的工作流组件

3
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

20
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/LINGYUN.Abp.WorkflowCore.DistributedLock.csproj

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Volo.Abp.DistributedLocking" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore\LINGYUN.Abp.WorkflowCore.csproj" />
</ItemGroup>
</Project>

73
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.DistributedLock/LINGYUN/Abp/WorkflowCore/DistributedLock/AbpDistributedLockProvider.cs

@ -0,0 +1,73 @@
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DistributedLocking;
using WorkflowCore.Interface;
namespace LINGYUN.Abp.WorkflowCore.DistributedLock
{
public class AbpDistributedLockProvider : IDistributedLockProvider
{
private readonly IMemoryCache _lockCache;
private readonly IAbpDistributedLock _distributedLock;
private readonly TimeSpan _lockTimeout = TimeSpan.FromMinutes(1);
public AbpDistributedLockProvider(
IMemoryCache memoryCache,
IAbpDistributedLock abpDistributedLock)
{
_lockCache = memoryCache;
_distributedLock = abpDistributedLock;
}
public virtual async Task<bool> AcquireLock(string Id, CancellationToken cancellationToken)
{
var handle = await _distributedLock.TryAcquireAsync(Id, _lockTimeout, cancellationToken);
if (handle == null)
{
return false;
}
var cacheItem = new LockCacheItem(Id, handle);
// 预留一点时间
_lockCache.Set(Id, cacheItem, TimeSpan.FromMinutes(1.5d));
return true;
}
public virtual async Task ReleaseLock(string Id)
{
var cacheItem = _lockCache.Get<LockCacheItem>(Id);
if (cacheItem == null)
{
await cacheItem.Handle.DisposeAsync();
}
}
public Task Start()
{
return Task.CompletedTask;
}
public Task Stop()
{
return Task.CompletedTask;
}
private class LockCacheItem
{
public string Id { get; set; }
public IAbpDistributedLockHandle Handle { get; set; }
public LockCacheItem() { }
public LockCacheItem(
string id,
IAbpDistributedLockHandle handle)
{
Id = id;
Handle = handle;
}
}
}
}

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

@ -0,0 +1,24 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Modularity;
using WorkflowCore.Interface;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.DistributedLock
{
[DependsOn(typeof(AbpWorkflowCoreModule))]
[DependsOn(typeof(AbpDistributedLockingModule))]
public class AbpWorkflowCoreDistributedLockModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddSingleton<IDistributedLockProvider, AbpDistributedLockProvider>();
context.Services.AddSingleton<AbpDistributedLockProvider>();
PreConfigure<WorkflowOptions>(options =>
{
options.UseDistributedLockManager(provider => provider.GetRequiredService<AbpDistributedLockProvider>());
});
}
}
}

3
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Elasticsearch/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

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

@ -1,5 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />

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

@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Volo.Abp;
using WorkflowCore.Interface;
using WorkflowCore.Models;
using WorkflowCore.Models.Search;
@ -46,7 +47,7 @@ namespace LINGYUN.Abp.WorkflowCore.Elasticsearch
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);
throw new AbpException($"Failed to index workflow {workflow.Id}", result.ApiCall.OriginalException);
}
}

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

@ -3,12 +3,12 @@
public class AbpWorkflowCoreElasticsearchOptions
{
/// <summary>
/// Default value: "workflows".
/// Default value: "abp.workflows".
/// </summary>
public string IndexFormat { get; set; }
public AbpWorkflowCoreElasticsearchOptions()
{
IndexFormat = "workflows";
IndexFormat = "abp.workflows";
}
}
}

3
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

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

@ -1,12 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.EventBus" Version="4.4.0" />
<PackageReference Include="Volo.Abp.EventBus" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>

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

@ -1,10 +1,8 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Json;
using WorkflowCore.Interface;
using EventData = WorkflowCore.Models.LifeCycleEvents.LifeCycleEvent;
@ -16,25 +14,21 @@ namespace LINGYUN.Abp.WorkflowCore.LifeCycleEvent
private Queue<Action<EventData>> _deferredSubscribers = new Queue<Action<EventData>>();
private readonly IDistributedEventBus _eventBus;
private readonly ILoggerFactory _loggerFactory;
private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
ReferenceLoopHandling = ReferenceLoopHandling.Error,
private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
};
public AbpEventBusProvider(
ILoggerFactory loggerFactory,
IDistributedEventBus distributedEventBus)
{
_loggerFactory = loggerFactory;
_eventBus = distributedEventBus;
}
public async Task PublishNotification(EventData evt)
{
var data = evt.SerializeObject(serializerSettings: _serializerSettings);
var data = JsonConvert.SerializeObject(evt, _serializerSettings);
var wrapEvent = new LifeCycleEventWrap(data);
await _eventBus.PublishAsync(wrapEvent);
}
@ -47,7 +41,7 @@ namespace LINGYUN.Abp.WorkflowCore.LifeCycleEvent
var action = _deferredSubscribers.Dequeue();
_eventBus.Subscribe<LifeCycleEventWrap>((data) =>
{
var unWrapData = data.Data.DeserializeObject(_serializerSettings);
var unWrapData = JsonConvert.DeserializeObject(data.Data, _serializerSettings);
action(unWrapData as EventData);
return Task.CompletedTask;
@ -71,7 +65,7 @@ namespace LINGYUN.Abp.WorkflowCore.LifeCycleEvent
{
_eventBus.Subscribe<LifeCycleEventWrap>((data) =>
{
var unWrapData = data.Data.DeserializeObject(_serializerSettings);
var unWrapData = JsonConvert.DeserializeObject(data.Data, _serializerSettings);
action(unWrapData as EventData);
return Task.CompletedTask;

5
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.LifeCycleEvent/LINGYUN/Abp/WorkflowCore/LifeCycleEvent/LifeCycleEventWrap.cs

@ -1,5 +1,8 @@
namespace LINGYUN.Abp.WorkflowCore.LifeCycleEvent
using Volo.Abp.EventBus;
namespace LINGYUN.Abp.WorkflowCore.LifeCycleEvent
{
[EventName("abp.workflowcore.life_cycle_event")]
public class LifeCycleEventWrap
{
public string Data { get; set; }

3
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

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

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Guids" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Json" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\elasticsearch\LINGYUN.Abp.Elasticsearch\LINGYUN.Abp.Elasticsearch.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore\LINGYUN.Abp.WorkflowCore.csproj" />
</ItemGroup>
</Project>

26
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/AbpWorkflowCorePersistenceElasticsearchModule.cs

@ -0,0 +1,26 @@
using LINGYUN.Abp.Elasticsearch;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
using WorkflowCore.Interface;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch
{
[DependsOn(typeof(AbpWorkflowCoreModule))]
[DependsOn(typeof(AbpJsonModule))]
[DependsOn(typeof(AbpElasticsearchModule))]
public class AbpWorkflowCorePersistenceElasticsearchModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddTransient<IPersistenceProvider, ElasticsearchPersistenceProvider>();
context.Services.AddTransient<ElasticsearchPersistenceProvider>();
PreConfigure<WorkflowOptions>(options =>
{
options.UsePersistence(provider => provider.GetRequiredService<ElasticsearchPersistenceProvider>());
});
}
}
}

18
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/AbpWorkflowCorePersistenceElasticsearchOptions.cs

@ -0,0 +1,18 @@
using Nest;
namespace LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch
{
public class AbpWorkflowCorePersistenceElasticsearchOptions
{
/// <summary>
/// Default Value: abp.workflows.persistence.{0}
/// </summary>
public string IndexFormat { get; set; }
public IIndexSettings IndexSettings { get; set; }
public AbpWorkflowCorePersistenceElasticsearchOptions()
{
IndexFormat = "abp.workflows.persistence.{0}";
IndexSettings = new IndexSettings();
}
}
}

558
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/ElasticsearchPersistenceProvider.cs

@ -0,0 +1,558 @@
using LINGYUN.Abp.Elasticsearch;
using LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch.Models;
using Microsoft.Extensions.Logging;
using Nest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Threading;
using WorkflowCore.Interface;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch
{
public class ElasticsearchPersistenceProvider : IPersistenceProvider, ITransientDependency
{
private readonly ILogger<ElasticsearchPersistenceProvider> _logger;
private readonly IGuidGenerator _guidGenerator;
private readonly IPersistenceIndexNameNormalizer _indexNameNormalizer;
private readonly IPersistenceIndexInitializer _indexInitializer;
private readonly IElasticsearchClientFactory _elasticsearchClientFactory;
public ElasticsearchPersistenceProvider(
IGuidGenerator guidGenerator,
IElasticsearchClientFactory elasticsearchClientFactory,
IPersistenceIndexInitializer indexInitializer,
IPersistenceIndexNameNormalizer indexNameNormalizer,
ILogger<ElasticsearchPersistenceProvider> logger)
{
_guidGenerator = guidGenerator;
_elasticsearchClientFactory = elasticsearchClientFactory;
_indexInitializer = indexInitializer;
_indexNameNormalizer = indexNameNormalizer;
_logger = logger;
}
public bool SupportsScheduledCommands => true;
public virtual async Task ClearSubscriptionToken(string eventSubscriptionId, string token, CancellationToken cancellationToken = default)
{
var id = Guid.Parse(eventSubscriptionId);
var client = CreateClient();
var response = await client.GetAsync<EventSubscription>(
id,
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.EventSubscriptionIndex)),
ct: cancellationToken);
CheckResponse(response);
if (response.Found)
{
if (response.Source.ExternalToken != token)
{
throw new InvalidOperationException();
}
response.Source.ExternalToken = null;
response.Source.ExternalWorkerId = null;
response.Source.ExternalTokenExpiry = null;
await client.UpdateAsync<EventSubscription>(
id,
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.EventSubscriptionIndex))
.Doc(response.Source),
ct: cancellationToken);
}
}
public virtual async Task<string> CreateEvent(Event newEvent, CancellationToken cancellationToken = default)
{
var client = CreateClient();
var newEventId = _guidGenerator.Create();
newEvent.Id = newEventId.ToString();
var response = await client.IndexAsync(
newEvent,
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.EventIndex))
.Id(newEventId),
ct: cancellationToken);
CheckResponse(response);
return newEvent.Id;
}
public virtual async Task<string> CreateEventSubscription(EventSubscription subscription, CancellationToken cancellationToken = default)
{
var client = CreateClient();
var newSubscriptionId = _guidGenerator.Create();
subscription.Id = newSubscriptionId.ToString();
var response = await client.IndexAsync(
subscription,
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.EventSubscriptionIndex))
.Id(newSubscriptionId),
ct: cancellationToken);
CheckResponse(response);
return subscription.Id;
}
public virtual async Task<string> CreateNewWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default)
{
var client = CreateClient();
var newWorkflowId = _guidGenerator.Create();
workflow.Id = newWorkflowId.ToString();
var response = await client.IndexAsync(
workflow,
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.WorkflowInstanceIndex))
.Id(newWorkflowId),
ct: cancellationToken);
CheckResponse(response);
return workflow.Id;
}
public void EnsureStoreExists()
{
AsyncHelper.RunSync(async () => await _indexInitializer.InitializeAsync());
}
public virtual async Task<Event> GetEvent(string id, CancellationToken cancellationToken = default)
{
var eventId = Guid.Parse(id);
var client = CreateClient();
var response = await client.GetAsync<Event>(
eventId,
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.EventIndex)),
ct: cancellationToken);
CheckResponse(response);
return response.Source;
}
public virtual async Task<IEnumerable<string>> GetEvents(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default)
{
var client = CreateClient();
var terms = new List<Func<QueryContainerDescriptor<Event>, QueryContainer>>();
terms.Add(x => x.Term(t => t.Field(f => f.EventName.Suffix("keyword")).Value(eventName)));
terms.Add(x => x.Term(t => t.Field(f => f.EventKey.Suffix("keyword")).Value(eventKey)));
terms.Add(x => x.DateRange(t => t.Field(f => f.EventTime).GreaterThanOrEquals(asOf)));
var response = await client.SearchAsync<Event>(
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.EventIndex))
.Query(q => q.Bool(b => b.Filter(terms)))
.Source(s => s.Includes(e => e.Field(f => f.Id))),
ct: cancellationToken);
CheckResponse(response);
return response.Documents.Select(x => x.Id);
}
public virtual async Task<EventSubscription> GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default)
{
var client = CreateClient();
var terms = new List<Func<QueryContainerDescriptor<EventSubscription>, QueryContainer>>();
terms.Add(x => x.Term(t => t.Field(f => f.EventName.Suffix("keyword")).Value(eventName)));
terms.Add(x => x.Term(t => t.Field(f => f.EventKey.Suffix("keyword")).Value(eventKey)));
terms.Add(x => x.Term(t => t.Field(f => f.ExternalToken.Suffix("keyword")).Value(null)));
terms.Add(x => x.DateRange(t => t.Field(f => f.SubscribeAsOf).LessThanOrEquals(asOf)));
var response = await client.SearchAsync<EventSubscription>(
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.EventSubscriptionIndex))
.Query(q => q.Bool(b => b.Filter(terms)))
.Source(s => s.Includes(e => e.Field(f => f.Id)))
.Sort(s => s.Field(f => f.SubscribeAsOf, SortOrder.Ascending))
.Take(1),
ct: cancellationToken);
CheckResponse(response);
return response.Documents.FirstOrDefault();
}
public virtual async Task<IEnumerable<string>> GetRunnableEvents(DateTime asAt, CancellationToken cancellationToken = default)
{
var client = CreateClient();
var now = asAt.ToUniversalTime();
var terms = new List<Func<QueryContainerDescriptor<Event>, QueryContainer>>();
terms.Add(x => x.Term(t => t.Field(f => f.IsProcessed).Value(false)));
terms.Add(x => x.DateRange(t => t.Field(f => f.EventTime).LessThanOrEquals(now)));
var response = await client.SearchAsync<Event>(
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.EventIndex))
.Query(q => q.Bool(b => b.Filter(terms)))
.Source(s => s.Includes(e => e.Field(f => f.Id))),
ct: cancellationToken);
CheckResponse(response);
return response.Documents.Select(x => x.Id);
}
public virtual async Task<IEnumerable<string>> GetRunnableInstances(DateTime asAt, CancellationToken cancellationToken = default)
{
var client = CreateClient();
var now = asAt.ToUniversalTime().Ticks;
var terms = new List<Func<QueryContainerDescriptor<WorkflowInstance>, QueryContainer>>();
terms.Add(x => x.LongRange(t => t.Field(f => f.NextExecution).LessThanOrEquals(now)));
terms.Add(x => x.Term(t => t.Field(f => f.Status).Value(WorkflowStatus.Runnable)));
var response = await client.SearchAsync<WorkflowInstance>(
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.WorkflowInstanceIndex))
.Query(q => q.Bool(b => b.Filter(terms)))
.Source(s => s.Includes(e => e.Field(f => f.Id))),
ct: cancellationToken);
CheckResponse(response);
return response.Documents.Select(x => x.Id);
}
public virtual async Task<EventSubscription> GetSubscription(string eventSubscriptionId, CancellationToken cancellationToken = default)
{
var client = CreateClient();
var id = Guid.Parse(eventSubscriptionId);
var response = await client.GetAsync<EventSubscription>(
id,
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.EventSubscriptionIndex)),
ct: cancellationToken);
CheckResponse(response);
return response.Source;
}
public virtual async Task<IEnumerable<EventSubscription>> GetSubscriptions(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default)
{
var client = CreateClient();
var now = asOf.ToUniversalTime();
var terms = new List<Func<QueryContainerDescriptor<EventSubscription>, QueryContainer>>();
terms.Add(x => x.Term(t => t.Field(f => f.EventName.Suffix("keyword")).Value(eventName)));
terms.Add(x => x.Term(t => t.Field(f => f.EventKey.Suffix("keyword")).Value(eventKey)));
terms.Add(x => x.DateRange(t => t.Field(f => f.SubscribeAsOf).LessThanOrEquals(now)));
var response = await client.SearchAsync<EventSubscription>(
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.EventSubscriptionIndex))
.Query(q => q.Bool(b => b.Filter(terms)))
.Source(s => s.IncludeAll()),
ct: cancellationToken);
CheckResponse(response);
return response.Documents;
}
public virtual async Task<WorkflowInstance> GetWorkflowInstance(string Id, CancellationToken cancellationToken = default)
{
var workflowId = Guid.Parse(Id);
var client = CreateClient();
var response = await client.GetAsync<WorkflowInstance>(
workflowId,
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.WorkflowInstanceIndex)),
ct: cancellationToken);
CheckResponse(response);
return response.Source;
}
public virtual async Task<IEnumerable<WorkflowInstance>> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take)
{
var client = CreateClient();
var terms = new List<Func<QueryContainerDescriptor<WorkflowInstance>, QueryContainer>>();
if (status.HasValue)
{
terms.Add(x => x.Term(t => t.Field(f => f.Status).Value(status.Value)));
}
if (!type.IsNullOrWhiteSpace())
{
terms.Add(x => x.Term(t => t.Field(f => f.WorkflowDefinitionId.Suffix("keyword")).Value(type)));
}
if (createdFrom.HasValue)
{
terms.Add(x => x.DateRange(t => t.Field(f => f.CreateTime).GreaterThanOrEquals(createdFrom.Value)));
}
if (createdTo.HasValue)
{
terms.Add(x => x.DateRange(t => t.Field(f => f.CreateTime).LessThanOrEquals(createdTo.Value)));
}
var response = await client.SearchAsync<WorkflowInstance>(
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.WorkflowInstanceIndex))
.Query(q => q.Bool(b => b.Filter(terms)))
.Source(s => s.IncludeAll())
.Skip(skip)
.Take(take));
CheckResponse(response);
return response.Documents;
}
public virtual async Task<IEnumerable<WorkflowInstance>> GetWorkflowInstances(IEnumerable<string> ids, CancellationToken cancellationToken = default)
{
var client = CreateClient();
var response = await client.SearchAsync<WorkflowInstance>(
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.WorkflowInstanceIndex))
.Query(q =>
q.Bool(b =>
b.Should(s =>
s.Terms(t => t.Field(f => f.Id.Suffix("keyword")).Terms(ids)))))
.Source(s => s.IncludeAll()),
ct: cancellationToken);
CheckResponse(response);
return response.Documents;
}
public virtual async Task MarkEventProcessed(string id, CancellationToken cancellationToken = default)
{
var eventId = Guid.Parse(id);
var indexName = CreateIndex(PersistenceIndexConsts.EventIndex);
var client = CreateClient();
var response = await client.GetAsync<Event>(
eventId,
dsl => dsl.Index(indexName),
ct: cancellationToken);
CheckResponse(response);
if (response.Found)
{
response.Source.IsProcessed = true;
await client.UpdateAsync<Event>(
id,
dsl => dsl.Index(indexName)
.Doc(response.Source),
ct: cancellationToken);
}
}
public virtual async Task MarkEventUnprocessed(string id, CancellationToken cancellationToken = default)
{
var eventId = Guid.Parse(id);
var indexName = CreateIndex(PersistenceIndexConsts.EventIndex);
var client = CreateClient();
var response = await client.GetAsync<Event>(
eventId,
dsl => dsl.Index(indexName),
ct: cancellationToken);
CheckResponse(response);
if (response.Found)
{
response.Source.IsProcessed = false;
await client.UpdateAsync<Event>(
id,
dsl => dsl.Index(indexName)
.Doc(response.Source),
ct: cancellationToken);
}
}
public virtual async Task PersistErrors(IEnumerable<ExecutionError> errors, CancellationToken cancellationToken = default)
{
var executionErrors = errors as ExecutionError[] ?? errors.ToArray();
if (executionErrors.Any())
{
var client = CreateClient();
var response = await client.IndexManyAsync(
errors,
CreateIndex(PersistenceIndexConsts.ExecutionErrorIndex),
cancellationToken: cancellationToken);
CheckResponse(response);
}
}
public virtual async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default)
{
var workflowId = Guid.Parse(workflow.Id);
var indexName = CreateIndex(PersistenceIndexConsts.WorkflowInstanceIndex);
var client = CreateClient();
var response = await client.GetAsync<WorkflowInstance>(
workflowId,
dsl => dsl.Index(indexName),
ct: cancellationToken);
CheckResponse(response);
await client.UpdateAsync<WorkflowInstance>(
workflowId,
dsl => dsl.Index(indexName)
.Doc(workflow),
ct: cancellationToken);
}
public virtual async Task ProcessCommands(DateTimeOffset asOf, Func<ScheduledCommand, Task> action, CancellationToken cancellationToken = default)
{
var client = CreateClient();
var indexName = CreateIndex(PersistenceIndexConsts.ScheduledCommandIndex);
var terms = new List<Func<QueryContainerDescriptor<PersistedScheduledCommand>, QueryContainer>>();
terms.Add(x => x.LongRange(t => t.Field(f => f.ExecuteTime).LessThan(asOf.Ticks)));
var response = await client.SearchAsync<PersistedScheduledCommand>(
dsl => dsl.Index(indexName)
.Query(q => q.Bool(b => b.Filter(terms)))
.Source(s => s.IncludeAll()),
ct: cancellationToken);
CheckResponse(response);
foreach (var command in response.Documents)
{
try
{
await action(command.ToScheduledCommand());
await client.DeleteAsync<PersistedScheduledCommand>(
command.Id,
dsl => dsl.Index(indexName),
ct: cancellationToken);
}
catch (Exception)
{
//TODO: add logger
}
}
}
public virtual async Task ScheduleCommand(ScheduledCommand command)
{
var persistedCommand = new PersistedScheduledCommand(
_guidGenerator.Create(),
command);
var client = CreateClient();
var response = await client.IndexAsync(
persistedCommand,
dsl => dsl
.Index(CreateIndex(PersistenceIndexConsts.ScheduledCommandIndex))
.Id(persistedCommand.Id));
CheckResponse(response);
}
public virtual async Task<bool> SetSubscriptionToken(string eventSubscriptionId, string token, string workerId, DateTime expiry, CancellationToken cancellationToken = default)
{
var id = Guid.Parse(eventSubscriptionId);
var indexName = CreateIndex(PersistenceIndexConsts.EventSubscriptionIndex);
var client = CreateClient();
var response = await client.GetAsync<EventSubscription>(
id,
dsl => dsl.Index(indexName),
ct: cancellationToken);
CheckResponse(response);
if (response.Found)
{
response.Source.ExternalToken = token;
response.Source.ExternalWorkerId = workerId;
response.Source.ExternalTokenExpiry = expiry;
var uptResponse = await client.UpdateAsync<EventSubscription>(
id,
dsl => dsl.Index(indexName)
.Doc(response.Source),
ct: cancellationToken);
return uptResponse.Result == Result.Updated;
}
return false;
}
public virtual async Task TerminateSubscription(string eventSubscriptionId, CancellationToken cancellationToken = default)
{
var id = Guid.Parse(eventSubscriptionId);
var client = CreateClient();
var response = await client.DeleteAsync<EventSubscription>(
id,
dsl => dsl.Index(CreateIndex(PersistenceIndexConsts.EventSubscriptionIndex)),
ct: cancellationToken);
CheckResponse(response);
}
private IElasticClient CreateClient()
{
return _elasticsearchClientFactory.Create();
}
private string CreateIndex(string index)
{
return _indexNameNormalizer.NormalizeIndex(index);
}
private void CheckResponse(IResponse response)
{
if (!response.ApiCall.Success)
{
_logger.LogError(default(EventId), response.ApiCall.OriginalException, $"ES Operation Failed");
throw new AbpException($"ES Operation Failed", response.ApiCall.OriginalException);
}
if (!response.IsValid)
{
_logger.LogWarning("ES Request Valid Error: {0}", response.DebugInformation);
throw new InvalidOperationException(response.DebugInformation, response.OriginalException);
}
}
}
}

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

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch
{
public interface IPersistenceIndexInitializer
{
Task InitializeAsync();
}
}

7
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/IPersistenceIndexNameNormalizer.cs

@ -0,0 +1,7 @@
namespace LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch
{
public interface IPersistenceIndexNameNormalizer
{
string NormalizeIndex(string index);
}
}

32
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/Models/PersistedScheduledCommand.cs

@ -0,0 +1,32 @@
using System;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch.Models
{
public class PersistedScheduledCommand
{
public Guid Id { get; set; }
public string CommandName { get; set; }
public string Data { get; set; }
public long ExecuteTime { get; set; }
public PersistedScheduledCommand() { }
public PersistedScheduledCommand(Guid id, ScheduledCommand command)
{
Id = id;
CommandName = command.CommandName;
Data = command.Data;
ExecuteTime = command.ExecuteTime;
}
public ScheduledCommand ToScheduledCommand()
{
return new ScheduledCommand
{
CommandName = CommandName,
Data = Data,
ExecuteTime = ExecuteTime
};
}
}
}

11
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/PersistenceIndexConsts.cs

@ -0,0 +1,11 @@
namespace LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch
{
internal static class PersistenceIndexConsts
{
public const string WorkflowInstanceIndex = "instances";
public const string EventIndex = "events";
public const string EventSubscriptionIndex = "subscriptions";
public const string ExecutionErrorIndex = "executionerrors";
public const string ScheduledCommandIndex = "scheduledcommands";
}
}

163
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch/LINGYUN/Abp/WorkflowCore/Persistence/Elasticsearch/PersistenceIndexInitializer.cs

@ -0,0 +1,163 @@
using LINGYUN.Abp.Elasticsearch;
using LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch.Models;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Nest;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch
{
public class PersistenceIndexInitializer : IPersistenceIndexInitializer, ISingletonDependency
{
private readonly ILogger<PersistenceIndexInitializer> _logger;
private readonly AbpJsonOptions _jsonOptions;
private readonly AbpWorkflowCorePersistenceElasticsearchOptions _elasticsearchOptions;
private readonly IPersistenceIndexNameNormalizer _nameNormalizer;
private readonly IElasticsearchClientFactory _clientFactory;
public PersistenceIndexInitializer(
IOptions<AbpJsonOptions> jsonOptions,
IOptions<AbpWorkflowCorePersistenceElasticsearchOptions> elasticsearchOptions,
IPersistenceIndexNameNormalizer nameNormalizer,
IElasticsearchClientFactory clientFactory,
ILogger<PersistenceIndexInitializer> logger)
{
_jsonOptions = jsonOptions.Value;
_elasticsearchOptions = elasticsearchOptions.Value;
_nameNormalizer = nameNormalizer;
_clientFactory = clientFactory;
_logger = logger;
}
public virtual async Task InitializeAsync()
{
var client = _clientFactory.Create();
var dateTimeFormat = !_jsonOptions.DefaultDateTimeFormat.IsNullOrWhiteSpace()
? $"{_jsonOptions.DefaultDateTimeFormat}||strict_date_optional_time||epoch_millis"
: "strict_date_optional_time||epoch_millis";
var indexState = new IndexState
{
Settings = _elasticsearchOptions.IndexSettings,
};
await InitlizeWorkflowInstanceIndex(client, indexState, dateTimeFormat);
await InitlizeEventIndex(client, indexState, dateTimeFormat);
await InitlizeEventSubscriptionIndex(client, indexState, dateTimeFormat);
await InitlizeExecutionErrorIndex(client, indexState, dateTimeFormat);
await InitlizeScheduledCommandIndex(client, indexState, dateTimeFormat);
}
protected virtual async Task InitlizeWorkflowInstanceIndex(IElasticClient client, IIndexState indexState, string dateTimeFormat)
{
var indexName = _nameNormalizer.NormalizeIndex("instances");
var indexExists = await client.Indices.ExistsAsync(indexName);
if (!indexExists.Exists)
{
var indexCreateResponse = await client.Indices.CreateAsync(
indexName,
dsl => dsl.InitializeUsing(indexState)
.Map<WorkflowInstance>(map => map.AutoMap()
.Properties(mp =>
mp.Date(p => p.Name(n => n.CreateTime).Format(dateTimeFormat))
.Date(p => p.Name(n => n.CompleteTime).Format(dateTimeFormat))
.Nested<ExecutionPointer>(p => p.Name(n => n.ExecutionPointers)
.AutoMap()
.Properties(np =>
np.Date(p => p.Name(n => n.EndTime).Format(dateTimeFormat))
.Date(p => p.Name(n => n.StartTime).Format(dateTimeFormat))
.Date(p => p.Name(n => n.SleepUntil).Format(dateTimeFormat))
.Object<Dictionary<string, object>>(p => p.Name(n => n.ExtensionAttributes)))))));
CheckResponse(indexCreateResponse);
}
}
protected virtual async Task InitlizeEventIndex(IElasticClient client, IIndexState indexState, string dateTimeFormat)
{
var indexName = _nameNormalizer.NormalizeIndex("events");
var indexExists = await client.Indices.ExistsAsync(indexName);
if (!indexExists.Exists)
{
var indexCreateResponse = await client.Indices.CreateAsync(
indexName,
dsl => dsl.InitializeUsing(indexState)
.Map<Event>(map => map.AutoMap()
.Properties(mp =>
mp.Date(p => p.Name(n => n.EventTime).Format(dateTimeFormat)))));
CheckResponse(indexCreateResponse);
}
}
protected virtual async Task InitlizeEventSubscriptionIndex(IElasticClient client, IIndexState indexState, string dateTimeFormat)
{
var indexName = _nameNormalizer.NormalizeIndex("subscriptions");
var indexExists = await client.Indices.ExistsAsync(indexName);
if (!indexExists.Exists)
{
var indexCreateResponse = await client.Indices.CreateAsync(
indexName,
dsl => dsl.InitializeUsing(indexState)
.Map<EventSubscription>(map => map.AutoMap()
.Properties(mp =>
mp.Date(p => p.Name(n => n.SubscribeAsOf).Format(dateTimeFormat))
.Date(p => p.Name(n => n.ExternalTokenExpiry).Format(dateTimeFormat)))));
CheckResponse(indexCreateResponse);
}
}
protected virtual async Task InitlizeExecutionErrorIndex(IElasticClient client, IIndexState indexState, string dateTimeFormat)
{
var indexName = _nameNormalizer.NormalizeIndex("executionerrors");
var indexExists = await client.Indices.ExistsAsync(indexName);
if (!indexExists.Exists)
{
var indexCreateResponse = await client.Indices.CreateAsync(
indexName,
dsl => dsl.InitializeUsing(indexState)
.Map<ExecutionError>(map => map.AutoMap()
.Properties(mp =>
mp.Date(p => p.Name(n => n.ErrorTime).Format(dateTimeFormat)))));
CheckResponse(indexCreateResponse);
}
}
protected virtual async Task InitlizeScheduledCommandIndex(IElasticClient client, IIndexState indexState, string dateTimeFormat)
{
var indexName = _nameNormalizer.NormalizeIndex("scheduledcommands");
var indexExists = await client.Indices.ExistsAsync(indexName);
if (!indexExists.Exists)
{
var indexCreateResponse = await client.Indices.CreateAsync(
indexName,
dsl => dsl.InitializeUsing(indexState)
.Map<PersistedScheduledCommand>(map => map.AutoMap()));
CheckResponse(indexCreateResponse);
}
}
private void CheckResponse(IResponse response)
{
if (!response.ApiCall.Success)
{
_logger.LogError(default(EventId), response.ApiCall.OriginalException, $"ES Persistence index initlize failed");
throw new AbpException($"ES Operation Failed", response.ApiCall.OriginalException);
}
if (!response.IsValid)
{
_logger.LogWarning("ES Persistence index initlize valid error: {0}", response.DebugInformation);
throw new InvalidOperationException(response.DebugInformation, response.OriginalException);
}
}
}
}

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

@ -0,0 +1,21 @@
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch
{
public class PersistenceIndexNameNormalizer : IPersistenceIndexNameNormalizer, ISingletonDependency
{
private readonly AbpWorkflowCorePersistenceElasticsearchOptions _options;
public PersistenceIndexNameNormalizer(
IOptions<AbpWorkflowCorePersistenceElasticsearchOptions> options)
{
_options = options.Value;
}
public string NormalizeIndex(string index)
{
return string.Format(_options.IndexFormat, index);
}
}
}

3
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

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

@ -1,16 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<RootNamespace />
</PropertyGroup>
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="4.4.0" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore.Persistence\LINGYUN.Abp.WorkflowCore.Persistence.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore.Persistence\LINGYUN.Abp.WorkflowCore.Persistence.csproj" />
</ItemGroup>
</Project>

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

@ -12,11 +12,11 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore
{
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>();
options.AddRepository<PersistedWorkflow, EfCoreWorkflowRepository>();
options.AddRepository<PersistedEvent, EfCoreWorkflowEventRepository>();
options.AddRepository<PersistedExecutionError, EfCoreWorkflowExecutionErrorRepository>();
options.AddRepository<PersistedScheduledCommand, EfCoreWorkflowScheduledCommandRepository>();
options.AddRepository<PersistedSubscription, EfCoreWorkflowEventSubscriptionRepository>();
});
}
}

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

@ -4,7 +4,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore
{
public class EfCoreWorkflowEventRepository : EfCoreRepository<WorkflowDbContext, WorkflowEvent, Guid>, IWorkflowEventRepository
public class EfCoreWorkflowEventRepository : EfCoreRepository<WorkflowDbContext, PersistedEvent, Guid>, IWorkflowEventRepository
{
public EfCoreWorkflowEventRepository(IDbContextProvider<WorkflowDbContext> dbContextProvider)
: base(dbContextProvider)

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

@ -5,7 +5,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class EfCoreWorkflowEventSubscriptionRepository : EfCoreRepository<WorkflowDbContext, WorkflowEventSubscription, Guid>, IWorkflowEventSubscriptionRepository
public class EfCoreWorkflowEventSubscriptionRepository : EfCoreRepository<WorkflowDbContext, PersistedSubscription, Guid>, IWorkflowEventSubscriptionRepository
{
public EfCoreWorkflowEventSubscriptionRepository(IDbContextProvider<WorkflowDbContext> dbContextProvider)
: base(dbContextProvider)

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

@ -4,7 +4,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class EfCoreWorkflowExecutionErrorRepository : EfCoreRepository<WorkflowDbContext, WorkflowExecutionError, int>, IWorkflowExecutionErrorRepository
public class EfCoreWorkflowExecutionErrorRepository : EfCoreRepository<WorkflowDbContext, PersistedExecutionError, int>, IWorkflowExecutionErrorRepository
{
public EfCoreWorkflowExecutionErrorRepository(IDbContextProvider<WorkflowDbContext> dbContextProvider)
: base(dbContextProvider)

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

@ -11,14 +11,14 @@ using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class EfCoreWorkflowRepository : EfCoreRepository<WorkflowDbContext, Workflow, Guid>, IWorkflowRepository
public class EfCoreWorkflowRepository : EfCoreRepository<WorkflowDbContext, PersistedWorkflow, Guid>, IWorkflowRepository
{
public EfCoreWorkflowRepository(IDbContextProvider<WorkflowDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<List<Workflow>> GetListAsync(
public virtual async Task<List<PersistedWorkflow>> GetListAsync(
WorkflowStatus? status,
string type,
DateTime? createdFrom,
@ -37,7 +37,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence
.ToListAsync();
}
public override async Task<IQueryable<Workflow>> WithDetailsAsync()
public override async Task<IQueryable<PersistedWorkflow>> WithDetailsAsync()
{
var quertable = await base.WithDetailsAsync();
return quertable

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

@ -1,14 +1,27 @@
using LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class EfCoreWorkflowScheduledCommandRepository : EfCoreRepository<WorkflowDbContext, WorkflowScheduledCommand, long>, IWorkflowScheduledCommandRepository
public class EfCoreWorkflowScheduledCommandRepository : EfCoreRepository<WorkflowDbContext, PersistedScheduledCommand, long>, IWorkflowScheduledCommandRepository
{
public EfCoreWorkflowScheduledCommandRepository(IDbContextProvider<WorkflowDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<bool> CheckExistsAsync(
string name,
string data,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.AnyAsync(x => x.CommandName.Equals(name) && x.Data.Equals(data),
GetCancellationToken(cancellationToken));
}
}
}

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

@ -7,12 +7,12 @@ 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; }
DbSet<PersistedWorkflow> Workflows { get; set; }
DbSet<PersistedEvent> WorkflowEvents { get; set; }
DbSet<PersistedSubscription> WorkflowEventSubscriptions { get; set; }
DbSet<PersistedExecutionError> WorkflowExecutionErrors { get; set; }
DbSet<PersistedExecutionPointer> WorkflowExecutionPointers { get; set; }
DbSet<PersistedExtensionAttribute> WorkflowExtensionAttributes { get; set; }
DbSet<PersistedScheduledCommand> WorkflowScheduledCommands { get; set; }
}
}

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

@ -7,13 +7,13 @@ 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 virtual DbSet<PersistedWorkflow> Workflows { get; set; }
public virtual DbSet<PersistedEvent> WorkflowEvents { get; set; }
public virtual DbSet<PersistedSubscription> WorkflowEventSubscriptions { get; set; }
public virtual DbSet<PersistedExecutionError> WorkflowExecutionErrors { get; set; }
public virtual DbSet<PersistedExecutionPointer> WorkflowExecutionPointers { get; set; }
public virtual DbSet<PersistedExtensionAttribute> WorkflowExtensionAttributes { get; set; }
public virtual DbSet<PersistedScheduledCommand> WorkflowScheduledCommands { get; set; }
public WorkflowDbContext(DbContextOptions<WorkflowDbContext> options) : base(options)
{

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

@ -11,7 +11,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore
{
Check.NotNull(builder, nameof(builder));
builder.Entity<Workflow>(b =>
builder.Entity<PersistedWorkflow>(b =>
{
b.ToTable(WorkflowDbProperties.TablePrefix + "Workflow");
@ -24,7 +24,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore
b.HasIndex(p => p.NextExecution);
});
builder.Entity<WorkflowExecutionPointer>(b =>
builder.Entity<PersistedExecutionPointer>(b =>
{
b.ToTable(WorkflowDbProperties.TablePrefix + "ExecutionPointer");
@ -37,7 +37,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore
b.ConfigureByConvention();
});
builder.Entity<WorkflowExtensionAttribute>(b =>
builder.Entity<PersistedExtensionAttribute>(b =>
{
b.ToTable(WorkflowDbProperties.TablePrefix + "ExtensionAttribute");
@ -47,7 +47,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore
b.ConfigureByConvention();
});
builder.Entity<WorkflowEvent>(b =>
builder.Entity<PersistedEvent>(b =>
{
b.ToTable(WorkflowDbProperties.TablePrefix + "Event");
@ -61,7 +61,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore
b.HasIndex(x => x.IsProcessed);
});
builder.Entity<WorkflowEventSubscription>(b =>
builder.Entity<PersistedSubscription>(b =>
{
b.ToTable(WorkflowDbProperties.TablePrefix + "Subscription");
@ -77,7 +77,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore
b.HasIndex(x => x.EventKey);
});
builder.Entity<WorkflowExecutionError>(b =>
builder.Entity<PersistedExecutionError>(b =>
{
b.ToTable(WorkflowDbProperties.TablePrefix + "ExecutionError");
@ -86,7 +86,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore
b.ConfigureByConvention();
});
builder.Entity<WorkflowScheduledCommand>(b =>
builder.Entity<PersistedScheduledCommand>(b =>
{
b.ToTable(WorkflowDbProperties.TablePrefix + "ScheduledCommand");

3
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

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

@ -1,16 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="4.4.0" />
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore\LINGYUN.Abp.WorkflowCore.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore\LINGYUN.Abp.WorkflowCore.csproj" />
</ItemGroup>
</Project>

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

@ -10,8 +10,8 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddSingleton<IPersistenceProvider, AbpWorkflowPersistenceProvider>();
context.Services.AddSingleton<AbpWorkflowPersistenceProvider>();
context.Services.AddTransient<IPersistenceProvider, AbpWorkflowPersistenceProvider>();
context.Services.AddTransient<AbpWorkflowPersistenceProvider>();
PreConfigure<WorkflowOptions>(options =>
{

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

@ -1,9 +1,10 @@
using System;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
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;
@ -13,8 +14,10 @@ using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class AbpWorkflowPersistenceProvider : IPersistenceProvider, IUnitOfWorkEnabled, ITransientDependency
public class AbpWorkflowPersistenceProvider : IPersistenceProvider, IUnitOfWorkEnabled
{
public ILogger<AbpWorkflowPersistenceProvider> Logger { protected get; set; }
private readonly ICurrentTenant _currentTenant;
private readonly IGuidGenerator _guidGenerator;
private readonly IWorkflowRepository _workflowRepository;
@ -46,6 +49,8 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence
_executionErrorRepository = executionErrorRepository;
_subscriptionRepository = subscriptionRepository;
_scheduledCommandRepository = scheduledCommandRepository;
Logger = NullLogger<AbpWorkflowPersistenceProvider>.Instance;
}
public virtual async Task ClearSubscriptionToken(
@ -68,7 +73,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence
Event newEvent,
CancellationToken cancellationToken = default)
{
var we = newEvent.ToWorkflowEvent(_guidGenerator, _currentTenant);
var we = newEvent.ToPersistable(_guidGenerator, _currentTenant);
await _workflowEventRepository.InsertAsync(we, cancellationToken: cancellationToken);
@ -81,7 +86,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence
EventSubscription subscription,
CancellationToken cancellationToken = default)
{
var wes = subscription.ToWorkflowEventSubscription(_guidGenerator, _currentTenant);
var wes = subscription.ToPersistable(_guidGenerator, _currentTenant);
await _subscriptionRepository.InsertAsync(wes, cancellationToken: cancellationToken);
@ -94,7 +99,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence
WorkflowInstance workflow,
CancellationToken cancellationToken = default)
{
var wf = workflow.ToWorkflow(_guidGenerator, _currentTenant);
var wf = workflow.ToPersistable(_guidGenerator, _currentTenant);
await _workflowRepository.InsertAsync(wf, cancellationToken: cancellationToken);
@ -269,7 +274,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence
{
if (errors.Any())
{
var workflowExecutionErrors = errors.Select(x => x.ToWorkflowExecutionError(_currentTenant));
var workflowExecutionErrors = errors.Select(x => x.ToPersistable(_currentTenant));
await _executionErrorRepository.InsertManyAsync(workflowExecutionErrors, cancellationToken: cancellationToken);
}
@ -285,13 +290,13 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence
var wf = await _workflowRepository.FindAsync(workflowId, includeDetails: true, cancellationToken: cancellationToken);
if (wf == null)
{
wf = workflow.ToWorkflow(_guidGenerator, _currentTenant);
wf = workflow.ToPersistable(_guidGenerator, _currentTenant);
await _workflowRepository.InsertAsync(wf, cancellationToken: cancellationToken);
}
else
{
wf.Update(workflow, _guidGenerator, _currentTenant);
wf = workflow.ToPersistable(_guidGenerator, _currentTenant, wf);
await _workflowRepository.UpdateAsync(wf, cancellationToken: cancellationToken);
}
@ -302,24 +307,35 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence
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);
try
{
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());
}
foreach (var command in commands)
await _scheduledCommandRepository.DeleteManyAsync(commands, cancellationToken: cancellationToken);
}
catch(Exception ex)
{
await action(command.ToScheduledCommand());
// TODO
Logger.LogWarning("Process Commands Error: {0}", ex.Message);
}
await _scheduledCommandRepository.DeleteManyAsync(commands, cancellationToken: cancellationToken);
}
public virtual async Task ScheduleCommand(ScheduledCommand command)
{
var workflowCommand = command.ToWorkflowScheduledCommand(_currentTenant);
if (!await _scheduledCommandRepository.CheckExistsAsync(command.CommandName, command.Data))
{
var workflowCommand = command.ToPersistable(_currentTenant);
await _scheduledCommandRepository.InsertAsync(workflowCommand);
await _scheduledCommandRepository.InsertAsync(workflowCommand);
}
}
public virtual async Task<bool> SetSubscriptionToken(

292
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/ExtensionMethods.cs

@ -0,0 +1,292 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
internal static class ExtensionMethods
{
private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
internal static PersistedWorkflow ToPersistable(
this WorkflowInstance instance,
IGuidGenerator generator,
ICurrentTenant currentTenant,
PersistedWorkflow persistable = null)
{
if (persistable == null)
{
persistable = new PersistedWorkflow(
generator.Create(),
instance.CreateTime,
instance.WorkflowDefinitionId,
JsonConvert.SerializeObject(instance.Data, SerializerSettings),
instance.Version,
instance.Description,
instance.Reference,
instance.Status,
instance.NextExecution,
instance.CompleteTime,
currentTenant.Id);
}
else
{
persistable.Data = JsonConvert.SerializeObject(instance.Data, SerializerSettings);
persistable.Description = instance.Description;
persistable.Reference = instance.Reference;
persistable.NextExecution = instance.NextExecution;
persistable.Version = instance.Version;
persistable.WorkflowDefinitionId = instance.WorkflowDefinitionId;
persistable.Status = instance.Status;
persistable.CreationTime = instance.CreateTime;
persistable.CompleteTime = instance.CompleteTime;
}
foreach (var ep in instance.ExecutionPointers)
{
var epId = ep.Id.IsNullOrWhiteSpace() ? Guid.Empty : Guid.Parse(ep.Id);
var persistedEP = persistable.FindPointer(epId);
if (persistedEP == null)
{
persistedEP = new PersistedExecutionPointer(
generator.Create(),
persistable.Id,
ep.StepId,
ep.StepName,
ep.Active,
JsonConvert.SerializeObject(ep.PersistenceData, SerializerSettings),
ep.EventName,
ep.EventKey,
ep.EventPublished,
JsonConvert.SerializeObject(ep.EventData, SerializerSettings),
ep.RetryCount,
ep.Children.JoinAsString(";"),
JsonConvert.SerializeObject(ep.ContextItem, SerializerSettings),
ep.PredecessorId,
JsonConvert.SerializeObject(ep.Outcome, SerializerSettings),
ep.Scope.JoinAsString(";"),
ep.Status,
ep.SleepUntil,
ep.StartTime,
ep.EndTime,
currentTenant.Id);
persistable.AddPointer(persistedEP);
}
else
{
persistedEP.StepId = ep.StepId;
persistedEP.Active = ep.Active;
persistedEP.SleepUntil = ep.SleepUntil;
persistedEP.PersistenceData = JsonConvert.SerializeObject(ep.PersistenceData, SerializerSettings);
persistedEP.StartTime = ep.StartTime;
persistedEP.EndTime = ep.EndTime;
persistedEP.StepName = ep.StepName;
persistedEP.RetryCount = ep.RetryCount;
persistedEP.PredecessorId = ep.PredecessorId;
persistedEP.ContextItem = JsonConvert.SerializeObject(ep.ContextItem, SerializerSettings);
persistedEP.Children = ep.Children.JoinAsString(";");
persistedEP.EventName = ep.EventName;
persistedEP.EventKey = ep.EventKey;
persistedEP.EventPublished = ep.EventPublished;
persistedEP.EventData = JsonConvert.SerializeObject(ep.EventData, SerializerSettings);
persistedEP.Outcome = JsonConvert.SerializeObject(ep.Outcome, SerializerSettings);
persistedEP.Status = ep.Status;
persistedEP.Scope = ep.Scope.JoinAsString(";");
}
foreach (var attr in ep.ExtensionAttributes)
{
var persistedAttr = persistedEP.FindAttribute(attr.Key);
if (persistedAttr == null)
{
persistedEP.AddAttribute(attr.Key, JsonConvert.SerializeObject(attr.Value, SerializerSettings));
}
else
{
persistedAttr.Key = attr.Key;
persistedAttr.Value = JsonConvert.SerializeObject(attr.Value, SerializerSettings);
}
}
}
return persistable;
}
internal static PersistedExecutionError ToPersistable(
this ExecutionError instance,
ICurrentTenant currentTenant)
{
var result = new PersistedExecutionError(
Guid.Parse(instance.WorkflowId),
Guid.Parse(instance.ExecutionPointerId),
instance.ErrorTime,
instance.Message,
currentTenant.Id);
return result;
}
internal static PersistedSubscription ToPersistable(
this EventSubscription instance,
IGuidGenerator generator,
ICurrentTenant currentTenant)
{
PersistedSubscription result = new PersistedSubscription(
generator.Create(),
Guid.Parse(instance.WorkflowId),
instance.StepId,
Guid.Parse(instance.ExecutionPointerId),
instance.EventName,
instance.EventKey,
DateTime.SpecifyKind(instance.SubscribeAsOf, DateTimeKind.Utc),
JsonConvert.SerializeObject(instance.SubscriptionData, SerializerSettings),
instance.ExternalToken,
instance.ExternalWorkerId,
instance.ExternalTokenExpiry,
currentTenant.Id);
return result;
}
internal static PersistedEvent ToPersistable(
this Event instance,
IGuidGenerator generator,
ICurrentTenant currentTenant)
{
PersistedEvent result = new PersistedEvent(
generator.Create(),
instance.EventName,
instance.EventKey,
JsonConvert.SerializeObject(instance.EventData, SerializerSettings),
DateTime.SpecifyKind(instance.EventTime, DateTimeKind.Utc),
currentTenant.Id);
return result;
}
internal static PersistedScheduledCommand ToPersistable(
this ScheduledCommand instance,
ICurrentTenant currentTenant)
{
var result = new PersistedScheduledCommand(
instance.CommandName,
instance.Data,
instance.ExecuteTime,
currentTenant.Id);
return result;
}
internal static WorkflowInstance ToWorkflowInstance(this PersistedWorkflow instance)
{
WorkflowInstance result = new WorkflowInstance();
result.Data = JsonConvert.DeserializeObject(instance.Data, SerializerSettings);
result.Description = instance.Description;
result.Reference = instance.Reference;
result.Id = instance.Id.ToString();
result.NextExecution = instance.NextExecution;
result.Version = instance.Version;
result.WorkflowDefinitionId = instance.WorkflowDefinitionId;
result.Status = instance.Status;
result.CreateTime = DateTime.SpecifyKind(instance.CreationTime, DateTimeKind.Utc);
if (instance.CompleteTime.HasValue)
result.CompleteTime = DateTime.SpecifyKind(instance.CompleteTime.Value, DateTimeKind.Utc);
result.ExecutionPointers = new ExecutionPointerCollection(instance.ExecutionPointers.Count + 8);
foreach (var ep in instance.ExecutionPointers)
{
var pointer = new ExecutionPointer();
pointer.Id = ep.Id.ToString();
pointer.StepId = ep.StepId;
pointer.Active = ep.Active;
if (ep.SleepUntil.HasValue)
pointer.SleepUntil = DateTime.SpecifyKind(ep.SleepUntil.Value, DateTimeKind.Utc);
pointer.PersistenceData = JsonConvert.DeserializeObject(ep.PersistenceData ?? string.Empty, SerializerSettings);
if (ep.StartTime.HasValue)
pointer.StartTime = DateTime.SpecifyKind(ep.StartTime.Value, DateTimeKind.Utc);
if (ep.EndTime.HasValue)
pointer.EndTime = DateTime.SpecifyKind(ep.EndTime.Value, DateTimeKind.Utc);
pointer.StepName = ep.StepName;
pointer.RetryCount = ep.RetryCount;
pointer.PredecessorId = ep.PredecessorId;
pointer.ContextItem = JsonConvert.DeserializeObject(ep.ContextItem ?? string.Empty, SerializerSettings);
if (!string.IsNullOrEmpty(ep.Children))
pointer.Children = ep.Children.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList();
pointer.EventName = ep.EventName;
pointer.EventKey = ep.EventKey;
pointer.EventPublished = ep.EventPublished;
pointer.EventData = JsonConvert.DeserializeObject(ep.EventData ?? string.Empty, SerializerSettings);
pointer.Outcome = JsonConvert.DeserializeObject(ep.Outcome ?? string.Empty, SerializerSettings);
pointer.Status = ep.Status;
if (!string.IsNullOrEmpty(ep.Scope))
pointer.Scope = new List<string>(ep.Scope.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries));
foreach (var attr in ep.ExtensionAttributes)
{
pointer.ExtensionAttributes[attr.Key] = JsonConvert.DeserializeObject(attr.Value, SerializerSettings);
}
result.ExecutionPointers.Add(pointer);
}
return result;
}
internal static EventSubscription ToEventSubscription(this PersistedSubscription instance)
{
EventSubscription result = new EventSubscription();
result.Id = instance.Id.ToString();
result.EventKey = instance.EventKey;
result.EventName = instance.EventName;
result.StepId = instance.StepId;
result.ExecutionPointerId = instance.ExecutionPointerId.ToString();
result.WorkflowId = instance.WorkflowId.ToString();
result.SubscribeAsOf = DateTime.SpecifyKind(instance.SubscribeAsOf, DateTimeKind.Utc);
result.SubscriptionData = JsonConvert.DeserializeObject(instance.SubscriptionData, SerializerSettings);
result.ExternalToken = instance.ExternalToken;
result.ExternalTokenExpiry = instance.ExternalTokenExpiry;
result.ExternalWorkerId = instance.ExternalWorkerId;
return result;
}
internal static Event ToEvent(this PersistedEvent instance)
{
Event result = new Event();
result.Id = instance.Id.ToString();
result.EventKey = instance.EventKey;
result.EventName = instance.EventName;
result.EventTime = DateTime.SpecifyKind(instance.CreationTime, DateTimeKind.Utc);
result.IsProcessed = instance.IsProcessed;
result.EventData = JsonConvert.DeserializeObject(instance.EventData, SerializerSettings);
return result;
}
internal static ScheduledCommand ToScheduledCommand(this PersistedScheduledCommand instance)
{
var result = new ScheduledCommand();
result.CommandName = instance.CommandName;
result.Data = instance.Data;
result.ExecuteTime = instance.ExecuteTime;
return result;
}
}
}

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

@ -3,7 +3,7 @@ using Volo.Abp.Domain.Repositories;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public interface IWorkflowEventRepository : IRepository<WorkflowEvent, Guid>
public interface IWorkflowEventRepository : IRepository<PersistedEvent, Guid>
{
}
}

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

@ -3,7 +3,7 @@ using Volo.Abp.Domain.Repositories;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public interface IWorkflowEventSubscriptionRepository : IRepository<WorkflowEventSubscription, Guid>
public interface IWorkflowEventSubscriptionRepository : IRepository<PersistedSubscription, Guid>
{
}
}

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

@ -2,7 +2,7 @@
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public interface IWorkflowExecutionErrorRepository : IRepository<WorkflowExecutionError, int>
public interface IWorkflowExecutionErrorRepository : IRepository<PersistedExecutionError, int>
{
}
}

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

@ -7,9 +7,9 @@ using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public interface IWorkflowRepository : IRepository<Workflow, Guid>
public interface IWorkflowRepository : IRepository<PersistedWorkflow, Guid>
{
Task<List<Workflow>> GetListAsync(
Task<List<PersistedWorkflow>> GetListAsync(
WorkflowStatus? status,
string type,
DateTime? createdFrom,

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

@ -1,8 +1,14 @@
using Volo.Abp.Domain.Repositories;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public interface IWorkflowScheduledCommandRepository : IRepository<WorkflowScheduledCommand, long>
public interface IWorkflowScheduledCommandRepository : IRepository<PersistedScheduledCommand, long>
{
Task<bool> CheckExistsAsync(
string name,
string data,
CancellationToken cancellationToken = default);
}
}

6
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowEvent.cs → aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/PersistedEvent.cs

@ -5,7 +5,7 @@ using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class WorkflowEvent : Entity<Guid>, IMultiTenant, IHasCreationTime
public class PersistedEvent : Entity<Guid>, IMultiTenant, IHasCreationTime
{
public virtual Guid? TenantId { get; protected set; }
/// <summary>
@ -28,8 +28,8 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence
/// 建立时间
/// </summary>
public virtual DateTime CreationTime { get; protected set; }
protected WorkflowEvent() { }
public WorkflowEvent(
protected PersistedEvent() { }
public PersistedEvent(
Guid id,
string name,
string key,

12
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowExecutionError.cs → aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/PersistedExecutionError.cs

@ -4,20 +4,20 @@ using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class WorkflowExecutionError : Entity<int>, IMultiTenant
public class PersistedExecutionError : 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 Guid ExecutionPointerId { get; protected set; }
public virtual DateTime ErrorTime { get; set; }
public virtual DateTime ErrorTime { get; protected set; }
public virtual string Message { get; set; }
public virtual string Message { get; protected set; }
protected WorkflowExecutionError() { }
public WorkflowExecutionError(
protected PersistedExecutionError() { }
public PersistedExecutionError(
Guid workflowId,
Guid executionPointerId,
DateTime errorTime,

123
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/PersistedExecutionPointer.cs

@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class PersistedExecutionPointer : Entity<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid WorkflowId { get; protected set; }
public virtual PersistedWorkflow Workflow { get; protected set; }
public virtual int StepId { get; set; }
public virtual bool Active { get; set; }
public virtual DateTime? SleepUntil { get; set; }
public virtual string PersistenceData { get; set; }
public virtual DateTime? StartTime { get; set; }
public virtual DateTime? EndTime { get; set; }
public virtual string EventName { get; set; }
public virtual string EventKey { get; set; }
public virtual bool EventPublished { get; set; }
public virtual string EventData { get; set; }
public virtual string StepName { get; set; }
public virtual int RetryCount { get; set; }
public virtual string Children { get; set; }
public virtual string ContextItem { get; set; }
public virtual string PredecessorId { get; set; }
public virtual string Outcome { get; set; }
public virtual PointerStatus Status { get; set; }
public virtual string Scope { get; set; }
public virtual ICollection<PersistedExtensionAttribute> ExtensionAttributes { get; protected set; }
protected PersistedExecutionPointer()
{
ExtensionAttributes = new Collection<PersistedExtensionAttribute>();
}
public PersistedExecutionPointer(
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<PersistedExtensionAttribute>();
}
public PersistedExtensionAttribute AddAttribute(string key, string value)
{
var attr = new PersistedExtensionAttribute(Id, key, value);
ExtensionAttributes.Add(attr);
return attr;
}
public PersistedExtensionAttribute FindAttribute(string key)
{
return ExtensionAttributes.FirstOrDefault(x => x.Key.Equals(key));
}
}
}

10
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowExtensionAttribute.cs → aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/PersistedExtensionAttribute.cs

@ -4,21 +4,21 @@ using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class WorkflowExtensionAttribute : Entity<long>, IMultiTenant
public class PersistedExtensionAttribute : Entity<long>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid ExecutionPointerId { get; set; }
public virtual Guid ExecutionPointerId { get; protected set; }
public virtual WorkflowExecutionPointer ExecutionPointer { get; set; }
public virtual PersistedExecutionPointer ExecutionPointer { get; protected set; }
public virtual string Key { get; set; }
public virtual string Value { get; set; }
protected WorkflowExtensionAttribute() { }
protected PersistedExtensionAttribute() { }
public WorkflowExtensionAttribute(
public PersistedExtensionAttribute(
Guid pointerId,
string key,
string value)

6
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowScheduledCommand.cs → aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/PersistedScheduledCommand.cs

@ -4,7 +4,7 @@ using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class WorkflowScheduledCommand : Entity<long>, IMultiTenant
public class PersistedScheduledCommand : Entity<long>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
@ -13,8 +13,8 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence
public virtual string Data { get; set; }
public virtual long ExecuteTime { get; set; }
protected WorkflowScheduledCommand() { }
public WorkflowScheduledCommand(
protected PersistedScheduledCommand() { }
public PersistedScheduledCommand(
string commandName,
string data,
long executeTime,

17
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/WorkflowEventSubscription.cs → aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/PersistedSubscription.cs

@ -4,7 +4,7 @@ using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class WorkflowEventSubscription : Entity<Guid>, IMultiTenant
public class PersistedSubscription : Entity<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
@ -22,17 +22,17 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence
public virtual string SubscriptionData { get; protected set; }
public virtual string ExternalToken { get; protected set; }
public virtual string ExternalToken { get; set; }
public virtual string ExternalWorkerId { get; protected set; }
public virtual string ExternalWorkerId { get; set; }
public virtual DateTime? ExternalTokenExpiry { get; protected set; }
public virtual DateTime? ExternalTokenExpiry { get; set; }
protected WorkflowEventSubscription()
protected PersistedSubscription()
{
}
public WorkflowEventSubscription(
public PersistedSubscription(
Guid id,
Guid workflowId,
int stepId,
@ -60,10 +60,7 @@ namespace LINGYUN.Abp.WorkflowCore.Persistence
TenantId = tenantId;
}
public void SetSubscriptionToken(
string token,
string workerId,
DateTime? expiry)
public void SetSubscriptionToken(string token, string workerId, DateTime? expiry = null)
{
ExternalToken = token;
ExternalWorkerId = workerId;

66
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.Persistence/LINGYUN/Abp/WorkflowCore/Persistence/PersistedWorkflow.cs

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore.Persistence
{
public class PersistedWorkflow : AuditedAggregateRoot<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual string WorkflowDefinitionId { get; set; }
public virtual int Version { get; set; }
public virtual string Description { get; set; }
public virtual string Reference { get; set; }
public virtual long? NextExecution { get; set; }
public virtual WorkflowStatus Status { get; set; }
public virtual string Data { get; set; }
public virtual DateTime? CompleteTime { get; set; }
public virtual ICollection<PersistedExecutionPointer> ExecutionPointers { get; protected set; }
protected PersistedWorkflow()
{
ExecutionPointers = new Collection<PersistedExecutionPointer>();
}
public PersistedWorkflow(
Guid id,
DateTime creationTime,
string defintionId,
string data,
int version,
string description,
string reference,
WorkflowStatus status,
long? nextExecution = null,
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<PersistedExecutionPointer>();
}
public void AddPointer(PersistedExecutionPointer pointer)
{
ExecutionPointers.Add(pointer);
}
public PersistedExecutionPointer FindPointer(Guid id)
{
return ExecutionPointers.FirstOrDefault(point => point.Id.Equals(id));
}
}
}

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

@ -1,100 +0,0 @@
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,
WorkflowStatus status,
long? nextExecution = null,
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 = instance.Data.SerializeObject();
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));
}
}
}
}

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

@ -1,159 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
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 = pointer.PersistenceData.SerializeObject();
EventName = pointer.EventName;
EventKey = pointer.EventKey;
EventPublished = pointer.EventPublished;
EventData = pointer.EventData.SerializeObject();
RetryCount = pointer.RetryCount;
Children = pointer.Children.JoinAsString(";");
ContextItem = pointer.ContextItem.SerializeObject();
PredecessorId = pointer.PredecessorId;
Outcome = pointer.Outcome.SerializeObject();
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)
{
AddAttribute(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));
}
}
}

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

@ -1,113 +0,0 @@
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.DeserializeObject(),
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;
}
}
}

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

@ -1,139 +0,0 @@
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(handlingString: true),
instance.Version,
instance.Description,
instance.Reference,
instance.Status,
instance.NextExecution,
instance.CompleteTime,
currentTenant.Id);
foreach (var pointer in instance.ExecutionPointers)
{
var toPointer = pointer.ToWorkflowExecutionPointer(workflow, generator, currentTenant);
workflow.AddPointer(toPointer);
}
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(handlingString: true),
executionPointer.EventName,
executionPointer.EventKey,
executionPointer.EventPublished,
executionPointer.EventData.SerializeObject(handlingString: true),
executionPointer.RetryCount,
executionPointer.Children.JoinAsString(";"),
executionPointer.ContextItem.SerializeObject(handlingString: true),
executionPointer.PredecessorId,
executionPointer.Outcome.SerializeObject(handlingString: true),
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(handlingString: true));
}
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(handlingString: true),
@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(handlingString: true),
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);
}
}
}

3
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore.RabbitMQ/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

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

@ -1,12 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.RabbitMQ" Version="4.4.0" />
<PackageReference Include="Volo.Abp.RabbitMQ" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>

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

@ -8,9 +8,8 @@ namespace LINGYUN.Abp.WorkflowCore.RabbitMQ
string queueName,
bool durable = true,
bool exclusive = false,
bool autoDelete = false,
string deadLetterQueueName = null)
: base(queueName, durable, exclusive, autoDelete, deadLetterQueueName)
bool autoDelete = false)
: base(queueName, durable, exclusive, autoDelete)
{
}
}

3
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

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

@ -1,14 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<RootNamespace />
</PropertyGroup>
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>9.0</LangVersion>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Timing" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Threading" Version="$(VoloAbpPackageVersion)" />
<!--<PackageReference Include="WorkflowCore.DSL" Version="3.6.1" />-->
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Core" Version="4.4.0" />
<PackageReference Include="WorkflowCore.DSL" Version="3.6.1" />
<ProjectReference Include="..\..\..\consoles\source\src\WorkflowCore.DSL\WorkflowCore.DSL.csproj" />
</ItemGroup>
</Project>

22
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/AbpDateTimeProvider.cs

@ -0,0 +1,22 @@
using System;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Timing;
using WorkflowCore.Interface;
namespace LINGYUN.Abp.WorkflowCore
{
[Dependency(ReplaceServices = true)]
public class AbpDateTimeProvider : IDateTimeProvider
{
private readonly IClock _clock;
public AbpDateTimeProvider(IClock clock)
{
_clock = clock;
}
public DateTime Now => _clock.Now;
public DateTime UtcNow => _clock.Now.ToUtcDateTime();
}
}

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

@ -1,11 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using System;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.WorkflowCore
{
public class AbpWorkflowCoreConventionalRegistrar : DefaultConventionalRegistrar
{
protected override bool IsConventionalRegistrationDisabled(Type type)
{
return !IsWorkflowComponent(type) || base.IsConventionalRegistrationDisabled(type);
}
private static bool IsWorkflowComponent(Type type)
{
return type.IsWorkflow() || type.IsStepBody();
}
protected override ServiceLifetime? GetDefaultLifeTimeOrNull(Type type)
{
return ServiceLifetime.Transient;
}
}
}

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

@ -1,19 +1,28 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Volo.Abp;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
using Volo.Abp.Timing;
using WorkflowCore.Interface;
using WorkflowCore.Services;
namespace LINGYUN.Abp.WorkflowCore
{
[DependsOn(
typeof(AbpTimingModule),
typeof(AbpThreadingModule))]
public class AbpWorkflowCoreModule : AbpModule
{
private readonly static IList<Type> _definitionWorkflows = new List<Type>();
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddConventionalRegistrar(new AbpWorkflowCoreConventionalRegistrar());
AutoAddDefinitionWorkflows(context.Services);
}
@ -24,34 +33,42 @@ namespace LINGYUN.Abp.WorkflowCore
context.Services.ExecutePreConfiguredActions(options);
});
context.Services.AddWorkflowDSL();
//context.Services.AddHostedService((provider) => provider.GetRequiredService<IWorkflowHost>());
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var workflowRegistry = context.ServiceProvider.GetRequiredService<IWorkflowRegistry>();
var workflowCoreOptions = context.ServiceProvider.GetRequiredService<IOptions<AbpWorkflowCoreOptions>>().Value;
foreach (var definitionWorkflow in _definitionWorkflows)
if (workflowCoreOptions.IsEnabled)
{
var workflow = context.ServiceProvider.GetRequiredService(definitionWorkflow);
workflowRegistry.RegisterWorkflow(workflow as WorkflowBase);
}
var workflowRegistry = context.ServiceProvider.GetRequiredService<IWorkflowRegistry>();
foreach (var definitionWorkflow in _definitionWorkflows)
{
var workflow = context.ServiceProvider.GetRequiredService(definitionWorkflow);
WorkflowRegisterHelper.RegisterWorkflow(workflowRegistry, workflow);
}
var workflowHost = context.ServiceProvider.GetRequiredService<IWorkflowHost>();
workflowHost.Start();
var workflowHost = context.ServiceProvider.GetRequiredService<IWorkflowHost>();
workflowHost.Start();
}
}
public override void OnApplicationShutdown(ApplicationShutdownContext context)
{
var workflowHost = context.ServiceProvider.GetRequiredService<IWorkflowHost>();
workflowHost.Stop();
var workflowCoreOptions = context.ServiceProvider.GetRequiredService<IOptions<AbpWorkflowCoreOptions>>().Value;
if (workflowCoreOptions.IsEnabled)
{
var workflowHost = context.ServiceProvider.GetRequiredService<IWorkflowHost>();
workflowHost.Stop();
}
}
private static void AutoAddDefinitionWorkflows(IServiceCollection services)
{
services.OnRegistred(context =>
{
if (typeof(WorkflowBase).IsAssignableFrom(context.ImplementationType))
if (context.ImplementationType.IsWorkflow())
{
_definitionWorkflows.Add(context.ImplementationType);
}

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

@ -1,10 +1,11 @@
using Volo.Abp.Collections;
using WorkflowCore.Interface;
namespace LINGYUN.Abp.WorkflowCore
namespace LINGYUN.Abp.WorkflowCore
{
public class AbpWorkflowCoreOptions
{
public ITypeList<IWorkflow> DefinitionProviders { get; }
public bool IsEnabled { get; set; }
public AbpWorkflowCoreOptions()
{
IsEnabled = true;
}
}
}

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

@ -1,10 +0,0 @@
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

@ -1,14 +0,0 @@
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();
}
}
}

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

@ -0,0 +1,8 @@
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore
{
public abstract class StepBodyAsyncBase : StepBodyAsync
{
}
}

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

@ -0,0 +1,8 @@
using WorkflowCore.Models;
namespace LINGYUN.Abp.WorkflowCore
{
public abstract class StepBodyBase : StepBody
{
}
}

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

@ -1,9 +1,8 @@
using Volo.Abp.DependencyInjection;
using WorkflowCore.Interface;
using WorkflowCore.Interface;
namespace LINGYUN.Abp.WorkflowCore
{
public abstract class WorkflowBase : IWorkflow, ITransientDependency
public abstract class WorkflowBase : IWorkflow
{
public abstract string Id { get; }
@ -11,4 +10,14 @@ namespace LINGYUN.Abp.WorkflowCore
public abstract void Build(IWorkflowBuilder<object> builder);
}
public abstract class WorkflowBase<TData> : IWorkflow<TData>
where TData: new()
{
public abstract string Id { get; }
public abstract int Version { get; }
public abstract void Build(IWorkflowBuilder<TData> builder);
}
}

33
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/LINGYUN/Abp/WorkflowCore/WorkflowRegisterHelper.cs

@ -0,0 +1,33 @@
using JetBrains.Annotations;
using System.Linq;
using System.Reflection;
using Volo.Abp;
using WorkflowCore.Interface;
namespace LINGYUN.Abp.WorkflowCore
{
public static class WorkflowRegisterHelper
{
public readonly static MethodInfo RegisterGenericWorkflowMethod =
typeof(IWorkflowRegistry)
.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.First(m => m.Name == nameof(IWorkflowRegistry.RegisterWorkflow) && m.IsGenericMethodDefinition);
public static void RegisterWorkflow(
[NotNull] IWorkflowRegistry registry,
[NotNull] object workflow)
{
Check.NotNull(registry, nameof(registry));
Check.NotNull(workflow, nameof(workflow));
var workflowDataType = workflow.GetType()
.GetInterfaces()
.First(x => x.IsGenericType)
.GetGenericArguments()[0];
RegisterGenericWorkflowMethod
.MakeGenericMethod(workflowDataType)
.Invoke(registry, new object[] { workflow });
}
}
}

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

@ -1,23 +0,0 @@
using Newtonsoft.Json;
namespace System
{
public static class ObjectSerializerExtensions
{
private readonly static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All };
public static string SerializeObject(this object obj, bool handlingString = false, JsonSerializerSettings serializerSettings = null)
{
if (obj is string objStr && !handlingString)
{
return objStr;
}
return JsonConvert.SerializeObject(obj, serializerSettings ?? SerializerSettings);
}
public static object DeserializeObject(this string str, JsonSerializerSettings serializerSettings = null)
{
return JsonConvert.DeserializeObject(str ?? string.Empty, serializerSettings ?? SerializerSettings);
}
}
}

18
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowCore/System/WorkflowTypeExtensions.cs

@ -0,0 +1,18 @@
using System.Linq;
using WorkflowCore.Interface;
namespace System
{
public static class WorkflowTypeExtensions
{
public static bool IsWorkflow(this Type type)
{
return type.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IWorkflow<>));
}
public static bool IsStepBody(this Type type)
{
return typeof(IStepBody).IsAssignableFrom(type);
}
}
}

3
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

20
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN.Abp.WorkflowManagement.Application.Contracts.csproj

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Authorization" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WorkflowManagement.Domain.Shared\LINGYUN.Abp.WorkflowManagement.Domain.Shared.csproj" />
</ItemGroup>
</Project>

23
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Authorization/WorkflowManagementPermissionDefinitionProvider.cs

@ -0,0 +1,23 @@
using LINGYUN.Abp.WorkflowManagement.Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.WorkflowManagement.Authorization
{
public class WorkflowManagementPermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var group = context.AddGroup(WorkflowManagementPermissions.GroupName, L("Permission:WorkflowManagement"));
group.AddPermission(
WorkflowManagementPermissions.ManageSettings,
L("Permission:ManageSettings"));
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<WorkflowManagementResource>(name);
}
}
}

9
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/Authorization/WorkflowManagementPermissions.cs

@ -0,0 +1,9 @@
namespace LINGYUN.Abp.WorkflowManagement.Authorization
{
public static class WorkflowManagementPermissions
{
public const string GroupName = "WorkflowManagement";
public const string ManageSettings = GroupName + ".ManageSettings";
}
}

14
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/WorkflowManagementApplicationContractsModule.cs

@ -0,0 +1,14 @@
using Volo.Abp.Application;
using Volo.Abp.Authorization;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.WorkflowManagement
{
[DependsOn(
typeof(AbpAuthorizationModule),
typeof(AbpDddApplicationContractsModule),
typeof(WorkflowManagementDomainSharedModule))]
public class WorkflowManagementApplicationContractsModule : AbpModule
{
}
}

7
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application.Contracts/LINGYUN/Abp/WorkflowManagement/WorkflowManagementRemoteServiceConsts.cs

@ -0,0 +1,7 @@
namespace LINGYUN.Abp.WorkflowManagement
{
public static class WorkflowManagementRemoteServiceConsts
{
public const string RemoteServiceName = "WorkflowManagement";
}
}

3
aspnet-core/modules/workflow/LINGYUN.Abp.WorkflowManagement.Application/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save