committed by
GitHub
158 changed files with 3424 additions and 9584 deletions
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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> `"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -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> |
|||
@ -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> |
|||
@ -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 |
|||
{ |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
# LINGYUN.Abp.WorkflowCore.Components |
|||
|
|||
预设的工作流组件 |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -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> |
|||
@ -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> |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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>()); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -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> |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -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> |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -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> |
|||
@ -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> |
|||
@ -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>()); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch |
|||
{ |
|||
public interface IPersistenceIndexInitializer |
|||
{ |
|||
Task InitializeAsync(); |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch |
|||
{ |
|||
public interface IPersistenceIndexNameNormalizer |
|||
{ |
|||
string NormalizeIndex(string index); |
|||
} |
|||
} |
|||
@ -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 |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -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"; |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -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> |
|||
@ -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> |
|||
|
|||
@ -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)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -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> |
|||
@ -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> |
|||
|
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
|
|||
@ -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)); |
|||
} |
|||
} |
|||
} |
|||
@ -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)); |
|||
} |
|||
} |
|||
} |
|||
@ -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)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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)); |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -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> |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -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> |
|||
@ -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> |
|||
|
|||
@ -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(); |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,10 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace LINGYUN.Abp.WorkflowCore |
|||
{ |
|||
public interface IWorkflowManager |
|||
{ |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using WorkflowCore.Models; |
|||
|
|||
namespace LINGYUN.Abp.WorkflowCore |
|||
{ |
|||
public abstract class StepBodyAsyncBase : StepBodyAsync |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using WorkflowCore.Models; |
|||
|
|||
namespace LINGYUN.Abp.WorkflowCore |
|||
{ |
|||
public abstract class StepBodyBase : StepBody |
|||
{ |
|||
} |
|||
} |
|||
@ -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 }); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -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> |
|||
@ -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> |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace LINGYUN.Abp.WorkflowManagement.Authorization |
|||
{ |
|||
public static class WorkflowManagementPermissions |
|||
{ |
|||
public const string GroupName = "WorkflowManagement"; |
|||
|
|||
public const string ManageSettings = GroupName + ".ManageSettings"; |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Abp.WorkflowManagement |
|||
{ |
|||
public static class WorkflowManagementRemoteServiceConsts |
|||
{ |
|||
public const string RemoteServiceName = "WorkflowManagement"; |
|||
} |
|||
} |
|||
@ -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…
Reference in new issue