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"> |
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
<PropertyGroup> |
<Import Project="..\..\..\configureawait.props" /> |
||||
<TargetFramework>netstandard2.1</TargetFramework> |
<Import Project="..\..\..\common.props" /> |
||||
<RootNamespace /> |
|
||||
</PropertyGroup> |
<PropertyGroup> |
||||
|
<TargetFramework>net6.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
<ItemGroup> |
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="4.4.0" /> |
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="$(VoloAbpPackageVersion)" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
|
|
||||
<ItemGroup> |
<ItemGroup> |
||||
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore.Persistence\LINGYUN.Abp.WorkflowCore.Persistence.csproj" /> |
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore.Persistence\LINGYUN.Abp.WorkflowCore.Persistence.csproj" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
|
|
||||
</Project> |
</Project> |
||||
|
|||||
@ -1,14 +1,27 @@ |
|||||
using LINGYUN.Abp.WorkflowCore.Persistence.EntityFrameworkCore; |
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.Domain.Repositories.EntityFrameworkCore; |
||||
using Volo.Abp.EntityFrameworkCore; |
using Volo.Abp.EntityFrameworkCore; |
||||
|
|
||||
namespace LINGYUN.Abp.WorkflowCore.Persistence |
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) |
public EfCoreWorkflowScheduledCommandRepository(IDbContextProvider<WorkflowDbContext> dbContextProvider) |
||||
: base(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"> |
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
<PropertyGroup> |
<Import Project="..\..\..\configureawait.props" /> |
||||
<TargetFramework>netstandard2.0</TargetFramework> |
<Import Project="..\..\..\common.props" /> |
||||
<RootNamespace /> |
|
||||
</PropertyGroup> |
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
<ItemGroup> |
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="4.4.0" /> |
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(VoloAbpPackageVersion)" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
|
|
||||
<ItemGroup> |
<ItemGroup> |
||||
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore\LINGYUN.Abp.WorkflowCore.csproj" /> |
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore\LINGYUN.Abp.WorkflowCore.csproj" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
|
|
||||
</Project> |
</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 |
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"> |
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
<PropertyGroup> |
<Import Project="..\..\..\configureawait.props" /> |
||||
<TargetFramework>netstandard2.0</TargetFramework> |
<Import Project="..\..\..\common.props" /> |
||||
<LangVersion>8.0</LangVersion> |
|
||||
<RootNamespace /> |
<PropertyGroup> |
||||
</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> |
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.Core" Version="4.4.0" /> |
<ProjectReference Include="..\..\..\consoles\source\src\WorkflowCore.DSL\WorkflowCore.DSL.csproj" /> |
||||
<PackageReference Include="WorkflowCore.DSL" Version="3.6.1" /> |
|
||||
</ItemGroup> |
</ItemGroup> |
||||
|
|
||||
</Project> |
</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 Microsoft.Extensions.DependencyInjection; |
||||
using System.Collections.Generic; |
using System; |
||||
using System.Text; |
|
||||
using Volo.Abp.DependencyInjection; |
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
namespace LINGYUN.Abp.WorkflowCore |
namespace LINGYUN.Abp.WorkflowCore |
||||
{ |
{ |
||||
public class AbpWorkflowCoreConventionalRegistrar : DefaultConventionalRegistrar |
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; |
namespace LINGYUN.Abp.WorkflowCore |
||||
using WorkflowCore.Interface; |
|
||||
|
|
||||
namespace LINGYUN.Abp.WorkflowCore |
|
||||
{ |
{ |
||||
public class AbpWorkflowCoreOptions |
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