50 changed files with 1527 additions and 15 deletions
@ -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,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.Guids" 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,22 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using WorkflowCore.Interface; |
||||
|
using WorkflowCore.Models; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch |
||||
|
{ |
||||
|
[DependsOn(typeof(AbpWorkflowCoreModule))] |
||||
|
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,14 @@ |
|||||
|
namespace LINGYUN.Abp.WorkflowCore.Persistence.Elasticsearch |
||||
|
{ |
||||
|
public class AbpWorkflowCorePersistenceElasticsearchOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Default Value: abp.workflows.persistence
|
||||
|
/// </summary>
|
||||
|
public string IndexFormat { get; set; } |
||||
|
public AbpWorkflowCorePersistenceElasticsearchOptions() |
||||
|
{ |
||||
|
IndexFormat = "abp.workflows.persistence"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,555 @@ |
|||||
|
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.Linq; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Guids; |
||||
|
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 IElasticsearchClientFactory _elasticsearchClientFactory; |
||||
|
private readonly AbpWorkflowCorePersistenceElasticsearchOptions _options; |
||||
|
|
||||
|
public ElasticsearchPersistenceProvider( |
||||
|
IGuidGenerator guidGenerator, |
||||
|
IElasticsearchClientFactory elasticsearchClientFactory, |
||||
|
IOptions<AbpWorkflowCorePersistenceElasticsearchOptions> options, |
||||
|
ILogger<ElasticsearchPersistenceProvider> logger) |
||||
|
{ |
||||
|
_guidGenerator = guidGenerator; |
||||
|
_elasticsearchClientFactory = elasticsearchClientFactory; |
||||
|
_options = options.Value; |
||||
|
_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()), |
||||
|
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()) |
||||
|
.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()) |
||||
|
.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()) |
||||
|
.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()) |
||||
|
.Id(newWorkflowId), |
||||
|
ct: cancellationToken); |
||||
|
|
||||
|
CheckResponse(response); |
||||
|
|
||||
|
return workflow.Id; |
||||
|
} |
||||
|
|
||||
|
public void EnsureStoreExists() |
||||
|
{ |
||||
|
// TODO: 为什么是同步API...
|
||||
|
var client = CreateClient(); |
||||
|
|
||||
|
var response = client.Indices.Exists(CreateIndex()); |
||||
|
if (!response.Exists) |
||||
|
{ |
||||
|
client.Indices.Create(CreateIndex()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
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()), |
||||
|
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()) |
||||
|
.Query(q => q.Bool(b => b.Filter(terms))) |
||||
|
.Source(s => s.Includes(e => e.Field(f => f.Id.Suffix("keyword")))), |
||||
|
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()) |
||||
|
.Query(q => q.Bool(b => b.Filter(terms))) |
||||
|
.Source(s => s.Includes(e => e.Field(f => f.Id.Suffix("keyword")))) |
||||
|
.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()) |
||||
|
.Query(q => q.Bool(b => b.Filter(terms))) |
||||
|
.Source(s => s.Includes(e => e.Field(f => f.Id.Suffix("keyword")))), |
||||
|
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()) |
||||
|
.Query(q => q.Bool(b => b.Filter(terms))) |
||||
|
.Source(s => s.Includes(e => e.Field(f => f.Id.Suffix("keyword")))), |
||||
|
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()), |
||||
|
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()) |
||||
|
.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()), |
||||
|
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()) |
||||
|
.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()) |
||||
|
.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 client = CreateClient(); |
||||
|
|
||||
|
var response = await client.GetAsync<Event>( |
||||
|
eventId, |
||||
|
dsl => dsl.Index(CreateIndex()), |
||||
|
ct: cancellationToken); |
||||
|
|
||||
|
CheckResponse(response); |
||||
|
|
||||
|
if (response.Found) |
||||
|
{ |
||||
|
response.Source.IsProcessed = true; |
||||
|
|
||||
|
await client.UpdateAsync<Event>( |
||||
|
id, |
||||
|
dsl => dsl.Index(CreateIndex()) |
||||
|
.Doc(response.Source), |
||||
|
ct: cancellationToken); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public virtual async Task MarkEventUnprocessed(string id, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
var eventId = Guid.Parse(id); |
||||
|
|
||||
|
var client = CreateClient(); |
||||
|
|
||||
|
var response = await client.GetAsync<Event>( |
||||
|
eventId, |
||||
|
dsl => dsl.Index(CreateIndex()), |
||||
|
ct: cancellationToken); |
||||
|
|
||||
|
CheckResponse(response); |
||||
|
|
||||
|
if (response.Found) |
||||
|
{ |
||||
|
response.Source.IsProcessed = false; |
||||
|
|
||||
|
await client.UpdateAsync<Event>( |
||||
|
id, |
||||
|
dsl => dsl.Index(CreateIndex()) |
||||
|
.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(), |
||||
|
cancellationToken: cancellationToken); |
||||
|
|
||||
|
CheckResponse(response); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public virtual async Task PersistWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
var workflowId = Guid.Parse(workflow.Id); |
||||
|
|
||||
|
var client = CreateClient(); |
||||
|
|
||||
|
var response = await client.GetAsync<WorkflowInstance>( |
||||
|
workflowId, |
||||
|
dsl => dsl.Index(CreateIndex()), |
||||
|
ct: cancellationToken); |
||||
|
|
||||
|
CheckResponse(response); |
||||
|
|
||||
|
await client.UpdateAsync<WorkflowInstance>( |
||||
|
workflowId, |
||||
|
dsl => dsl.Index(CreateIndex()) |
||||
|
.Doc(workflow), |
||||
|
ct: cancellationToken); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task ProcessCommands(DateTimeOffset asOf, Func<ScheduledCommand, Task> action, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
var client = CreateClient(); |
||||
|
|
||||
|
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(CreateIndex()) |
||||
|
.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(CreateIndex()), |
||||
|
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()).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 client = CreateClient(); |
||||
|
|
||||
|
var response = await client.GetAsync<EventSubscription>( |
||||
|
id, |
||||
|
dsl => dsl.Index(CreateIndex()), |
||||
|
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(CreateIndex()) |
||||
|
.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()), |
||||
|
ct: cancellationToken); |
||||
|
|
||||
|
CheckResponse(response); |
||||
|
} |
||||
|
|
||||
|
private IElasticClient CreateClient() |
||||
|
{ |
||||
|
return _elasticsearchClientFactory.Create(); |
||||
|
} |
||||
|
|
||||
|
private string CreateIndex() |
||||
|
{ |
||||
|
return _options.IndexFormat; |
||||
|
} |
||||
|
|
||||
|
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,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,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(); |
||||
|
} |
||||
|
} |
||||
@ -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,20 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net6.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Ddd.Application" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.WorkflowManagement.Application.Contracts\LINGYUN.Abp.WorkflowManagement.Application.Contracts.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.WorkflowManagement.Domain\LINGYUN.Abp.WorkflowManagement.Domain.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,14 @@ |
|||||
|
using LINGYUN.Abp.WorkflowManagement.Localization; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement |
||||
|
{ |
||||
|
public abstract class WorkflowManagementAppServiceBase : ApplicationService |
||||
|
{ |
||||
|
protected WorkflowManagementAppServiceBase() |
||||
|
{ |
||||
|
LocalizationResource = typeof(WorkflowManagementResource); |
||||
|
ObjectMapperContext = typeof(WorkflowManagementApplicationModule); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using AutoMapper; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement |
||||
|
{ |
||||
|
public class WorkflowManagementApplicationMapperProfile : Profile |
||||
|
{ |
||||
|
public WorkflowManagementApplicationMapperProfile() |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.Application; |
||||
|
using Volo.Abp.Authorization; |
||||
|
using Volo.Abp.AutoMapper; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpAuthorizationModule), |
||||
|
typeof(AbpDddApplicationContractsModule), |
||||
|
typeof(WorkflowManagementDomainSharedModule))] |
||||
|
public class WorkflowManagementApplicationModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
context.Services.AddAutoMapperObjectMapper<WorkflowManagementApplicationModule>(); |
||||
|
|
||||
|
Configure<AbpAutoMapperOptions>(options => |
||||
|
{ |
||||
|
options.AddProfile<WorkflowManagementApplicationMapperProfile>(validate: true); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<None Remove="LINGYUN\Abp\WorkflowManagement\Localization\Resources\en.json" /> |
||||
|
<None Remove="LINGYUN\Abp\WorkflowManagement\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\WorkflowManagement\Localization\Resources\en.json" /> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\WorkflowManagement\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Auditing" Version="$(VoloAbpPackageVersion)" /> |
||||
|
<PackageReference Include="Volo.Abp.EventBus" Version="$(VoloAbpPackageVersion)" /> |
||||
|
<PackageReference Include="Volo.Abp.Localization" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,7 @@ |
|||||
|
{ |
||||
|
"culture": "en", |
||||
|
"texts": { |
||||
|
"Permission:WorkflowManagement": "WorkflowManagement", |
||||
|
"Permission:ManageSettings": "Manage Settings" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
{ |
||||
|
"culture": "zh-Hans", |
||||
|
"texts": { |
||||
|
"Permission:WorkflowManagement": "WorkflowManagement", |
||||
|
"Permission:ManageSettings": "管理设置" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement.Localization |
||||
|
{ |
||||
|
[LocalizationResourceName("WorkflowManagement")] |
||||
|
public class WorkflowManagementResource |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
using LINGYUN.Abp.WorkflowManagement.Localization; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Localization.ExceptionHandling; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpLocalizationModule))] |
||||
|
public class WorkflowManagementDomainSharedModule: AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpVirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<WorkflowManagementDomainSharedModule>(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Add<WorkflowManagementResource>() |
||||
|
.AddVirtualJson("/Abp/WorkflowManagement/Localization/Resources"); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpExceptionLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.MapCodeNamespace(WorkflowManagementErrorCodes.Namespace, typeof(WorkflowManagementResource)); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace LINGYUN.Abp.WorkflowManagement |
||||
|
{ |
||||
|
public static class WorkflowManagementErrorCodes |
||||
|
{ |
||||
|
public const string Namespace = "WorkflowManagement"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
<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.AutoMapper" Version="$(VoloAbpPackageVersion)" /> |
||||
|
<PackageReference Include="Volo.Abp.Caching" Version="$(VoloAbpPackageVersion)" /> |
||||
|
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.WorkflowCore\LINGYUN.Abp.WorkflowCore.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.WorkflowManagement.Domain.Shared\LINGYUN.Abp.WorkflowManagement.Domain.Shared.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,11 @@ |
|||||
|
using Volo.Abp.Settings; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement.Settings |
||||
|
{ |
||||
|
public class WorkflowManagementSettingDefinitionProvider : SettingDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(ISettingDefinitionContext context) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace LINGYUN.Abp.WorkflowManagement.Settings |
||||
|
{ |
||||
|
public static class WorkflowManagementSettings |
||||
|
{ |
||||
|
public const string GroupName = "WorkflowManagement"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,57 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Data; |
||||
|
using Volo.Abp.Domain.Entities; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
using WorkflowCore.Models; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement |
||||
|
{ |
||||
|
public class Step : Entity<Guid>, IMultiTenant |
||||
|
{ |
||||
|
public virtual Guid? TenantId { get; protected set; } |
||||
|
public virtual Guid WorkflowId { get; protected set; } |
||||
|
public virtual string Name { get; protected set;} |
||||
|
public virtual string StepType { get; protected set; } |
||||
|
public virtual string CancelCondition { get; set; } |
||||
|
public virtual WorkflowErrorHandling? ErrorBehavior { get; protected set; } |
||||
|
public virtual int? RetryInterval { get; set; } |
||||
|
public virtual bool Saga { get; set; } |
||||
|
public virtual Guid? NextStep { get; protected set; } |
||||
|
public virtual ExtraPropertyDictionary Inputs { get; set; } |
||||
|
public virtual ExtraPropertyDictionary Outputs { get; set; } |
||||
|
public virtual ExtraPropertyDictionary SelectNextStep { get; set; } |
||||
|
protected Step() |
||||
|
{ |
||||
|
Inputs = new ExtraPropertyDictionary(); |
||||
|
Outputs = new ExtraPropertyDictionary(); |
||||
|
SelectNextStep = new ExtraPropertyDictionary(); |
||||
|
} |
||||
|
|
||||
|
public Step( |
||||
|
Guid id, |
||||
|
Guid workflowId, |
||||
|
string name, |
||||
|
string stepType, |
||||
|
string cancelCondition, |
||||
|
WorkflowErrorHandling? errorBehavior = null, |
||||
|
int? retryInterval = null, |
||||
|
bool saga = false, |
||||
|
Guid? nextStep = null, |
||||
|
Guid? tenantId = null) : base(id) |
||||
|
{ |
||||
|
Name = name; |
||||
|
WorkflowId = workflowId; |
||||
|
StepType = stepType; |
||||
|
CancelCondition = cancelCondition; |
||||
|
ErrorBehavior = errorBehavior; |
||||
|
RetryInterval = retryInterval; |
||||
|
Saga = saga; |
||||
|
NextStep = nextStep; |
||||
|
TenantId = tenantId; |
||||
|
|
||||
|
Inputs = new ExtraPropertyDictionary(); |
||||
|
Outputs = new ExtraPropertyDictionary(); |
||||
|
SelectNextStep = new ExtraPropertyDictionary(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Domain.Entities.Auditing; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 流程定义
|
||||
|
/// </summary>
|
||||
|
public class Workflow : AuditedAggregateRoot<Guid>, IMultiTenant |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 租户标识
|
||||
|
/// </summary>
|
||||
|
public virtual Guid? TenantId { get; protected set; } |
||||
|
/// <summary>
|
||||
|
/// 是否启用
|
||||
|
/// </summary>
|
||||
|
public virtual bool IsEnabled { get; protected set; } |
||||
|
/// <summary>
|
||||
|
/// 显示名称
|
||||
|
/// </summary>
|
||||
|
public virtual string DisplayName { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 描述
|
||||
|
/// </summary>
|
||||
|
public virtual string Description { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 版本号
|
||||
|
/// </summary>
|
||||
|
public virtual int Version { get; protected set; } |
||||
|
|
||||
|
protected Workflow() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public Workflow( |
||||
|
Guid id, |
||||
|
string displayName, |
||||
|
string description = "", |
||||
|
int version = 1) : base(id) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
namespace LINGYUN.Abp.WorkflowManagement |
||||
|
{ |
||||
|
public static class WorkflowManagementDbProperties |
||||
|
{ |
||||
|
public static string DbTablePrefix { get; set; } = "WorkflowManagement_"; |
||||
|
|
||||
|
public static string DbSchema { get; set; } = null; |
||||
|
|
||||
|
|
||||
|
public const string ConnectionStringName = "WorkflowManagement"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using AutoMapper; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement |
||||
|
{ |
||||
|
public class WorkflowManagementDomainMapperProfile: Profile |
||||
|
{ |
||||
|
public WorkflowManagementDomainMapperProfile() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.AutoMapper; |
||||
|
using Volo.Abp.Domain.Entities.Events.Distributed; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpAutoMapperModule), |
||||
|
typeof(WorkflowManagementDomainSharedModule))] |
||||
|
public class WorkflowManagementDomainModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
context.Services.AddAutoMapperObjectMapper<WorkflowManagementDomainModule>(); |
||||
|
|
||||
|
Configure<AbpAutoMapperOptions>(options => |
||||
|
{ |
||||
|
options.AddProfile<WorkflowManagementDomainMapperProfile>(validate: true); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpDistributedEntityEventOptions>(options => |
||||
|
{ |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net6.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.WorkflowManagement.Domain\LINGYUN.Abp.WorkflowManagement.Domain.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,10 @@ |
|||||
|
using Volo.Abp.Data; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
[ConnectionStringName(WorkflowManagementDbProperties.ConnectionStringName)] |
||||
|
public interface IWorkflowManagementDbContext : IEfCoreDbContext |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
public class WorkflowManagementDbContext : AbpDbContext<WorkflowManagementDbContext>, IWorkflowManagementDbContext |
||||
|
{ |
||||
|
public WorkflowManagementDbContext( |
||||
|
DbContextOptions<WorkflowManagementDbContext> options) : base(options) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder) |
||||
|
{ |
||||
|
base.OnModelCreating(modelBuilder); |
||||
|
|
||||
|
modelBuilder.ConfigureWorkflowManagement(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using System; |
||||
|
using Volo.Abp; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
public static class WorkflowManagementDbContextModelCreatingExtensions |
||||
|
{ |
||||
|
public static void ConfigureWorkflowManagement( |
||||
|
this ModelBuilder builder, |
||||
|
Action<WorkflowManagementModelBuilderConfigurationOptions> optionsAction = null) |
||||
|
{ |
||||
|
Check.NotNull(builder, nameof(builder)); |
||||
|
|
||||
|
var options = new WorkflowManagementModelBuilderConfigurationOptions( |
||||
|
WorkflowManagementDbProperties.DbTablePrefix, |
||||
|
WorkflowManagementDbProperties.DbSchema |
||||
|
); |
||||
|
optionsAction?.Invoke(options); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(WorkflowManagementDomainModule), |
||||
|
typeof(AbpEntityFrameworkCoreModule))] |
||||
|
public class WorkflowManagementEntityFrameworkCoreModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
context.Services.AddAbpDbContext<WorkflowManagementDbContext>(options => |
||||
|
{ |
||||
|
options.AddDefaultRepositories(includeAllEntities: true); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using Volo.Abp.EntityFrameworkCore.Modeling; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
public class WorkflowManagementModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions |
||||
|
{ |
||||
|
public WorkflowManagementModelBuilderConfigurationOptions( |
||||
|
[NotNull] string tablePrefix = "", |
||||
|
[CanBeNull] string schema = null) |
||||
|
: base( |
||||
|
tablePrefix, |
||||
|
schema) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net6.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.WorkflowManagement.Application.Contracts\LINGYUN.Abp.WorkflowManagement.Application.Contracts.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,41 @@ |
|||||
|
using LINGYUN.Abp.WorkflowManagement.Localization; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.AspNetCore.Mvc.Localization; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.Validation.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpAspNetCoreMvcModule), |
||||
|
typeof(WorkflowManagementApplicationContractsModule))] |
||||
|
public class WorkflowManagementHttpApiModule : AbpModule |
||||
|
{ |
||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
PreConfigure<IMvcBuilder>(mvcBuilder => |
||||
|
{ |
||||
|
mvcBuilder.AddApplicationPartIfNotExists(typeof(WorkflowManagementHttpApiModule).Assembly); |
||||
|
}); |
||||
|
|
||||
|
PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.AddAssemblyResource( |
||||
|
typeof(WorkflowManagementResource), |
||||
|
typeof(WorkflowManagementApplicationContractsModule).Assembly); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Get<WorkflowManagementResource>() |
||||
|
.AddBaseTypes(typeof(AbpValidationResource)); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="../../common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net5.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpPackageVersion)" /> |
||||
|
<PackageReference Include="Volo.Abp.SettingManagement.Domain" Version="$(VoloAbpPackageVersion)" /> |
||||
|
<PackageReference Include="LINGYUN.Abp.SettingManagement.Application.Contracts" Version="$(LINGYUNAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.WorkflowManagement.Application.Contracts\LINGYUN.Abp.WorkflowManagement.Application.Contracts.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,77 @@ |
|||||
|
using LINGYUN.Abp.WorkflowManagement.Authorization; |
||||
|
using LINGYUN.Abp.WorkflowManagement.Localization; |
||||
|
using LINGYUN.Abp.SettingManagement; |
||||
|
using Microsoft.AspNetCore.Authorization; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
using Volo.Abp.Features; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
using Volo.Abp.SettingManagement; |
||||
|
using Volo.Abp.Settings; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement.SettingManagement |
||||
|
{ |
||||
|
[Authorize(WorkflowManagementPermissions.ManageSettings)] |
||||
|
public class SettingAppService : ApplicationService, ISettingAppService |
||||
|
{ |
||||
|
protected ISettingManager SettingManager { get; } |
||||
|
protected ISettingDefinitionManager SettingDefinitionManager { get; } |
||||
|
|
||||
|
public SettingAppService( |
||||
|
ISettingManager settingManager, |
||||
|
ISettingDefinitionManager settingDefinitionManager) |
||||
|
{ |
||||
|
SettingManager = settingManager; |
||||
|
SettingDefinitionManager = settingDefinitionManager; |
||||
|
LocalizationResource = typeof(WorkflowManagementResource); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<ListResultDto<SettingGroupDto>> GetAllForCurrentTenantAsync() |
||||
|
{ |
||||
|
return await GetAllForProviderAsync(TenantSettingValueProvider.ProviderName, CurrentTenant.GetId().ToString()); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task SetCurrentTenantAsync(UpdateSettingsDto input) |
||||
|
{ |
||||
|
// 增加特性检查
|
||||
|
await CheckFeatureAsync(); |
||||
|
|
||||
|
if (CurrentTenant.IsAvailable) |
||||
|
{ |
||||
|
foreach (var setting in input.Settings) |
||||
|
{ |
||||
|
await SettingManager.SetForTenantAsync(CurrentTenant.GetId(), setting.Name, setting.Value); |
||||
|
} |
||||
|
|
||||
|
await CurrentUnitOfWork.SaveChangesAsync(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task CheckFeatureAsync() |
||||
|
{ |
||||
|
await FeatureChecker.CheckEnabledAsync(SettingManagementFeatures.Enable); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task<ListResultDto<SettingGroupDto>> GetAllForProviderAsync(string providerName, string providerKey) |
||||
|
{ |
||||
|
var settingGroups = new List<SettingGroupDto>(); |
||||
|
|
||||
|
await Task.CompletedTask; |
||||
|
|
||||
|
return new ListResultDto<SettingGroupDto>(settingGroups); |
||||
|
} |
||||
|
|
||||
|
public Task SetGlobalAsync(UpdateSettingsDto input) |
||||
|
{ |
||||
|
throw new NotSupportedException(); |
||||
|
} |
||||
|
|
||||
|
public Task<ListResultDto<SettingGroupDto>> GetAllForGlobalAsync() |
||||
|
{ |
||||
|
throw new NotSupportedException(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
using LINGYUN.Abp.SettingManagement; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement.SettingManagement |
||||
|
{ |
||||
|
[RemoteService(Name = WorkflowManagementRemoteServiceConsts.RemoteServiceName)] |
||||
|
[ApiVersion("2.0")] |
||||
|
[Area("WorkflowManagement")] |
||||
|
[Route("api/WorkflowManagement/settings")] |
||||
|
public class SettingController : AbpController, ISettingAppService |
||||
|
{ |
||||
|
private readonly ISettingAppService _settingAppService; |
||||
|
public SettingController(ISettingAppService settingAppService) |
||||
|
{ |
||||
|
_settingAppService = settingAppService; |
||||
|
} |
||||
|
|
||||
|
[HttpPut] |
||||
|
public virtual async Task SetCurrentTenantAsync(UpdateSettingsDto input) |
||||
|
{ |
||||
|
await _settingAppService.SetCurrentTenantAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
public virtual async Task<ListResultDto<SettingGroupDto>> GetAllForCurrentTenantAsync() |
||||
|
{ |
||||
|
return await _settingAppService.GetAllForCurrentTenantAsync(); |
||||
|
} |
||||
|
|
||||
|
[HttpPost] |
||||
|
[Route("by-global")] |
||||
|
public virtual async Task SetGlobalAsync(UpdateSettingsDto input) |
||||
|
{ |
||||
|
await _settingAppService.SetGlobalAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("by-global")] |
||||
|
public virtual async Task<ListResultDto<SettingGroupDto>> GetAllForGlobalAsync() |
||||
|
{ |
||||
|
return await _settingAppService.GetAllForGlobalAsync(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using LINGYUN.Abp.SettingManagement; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.SettingManagement; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WorkflowManagement.SettingManagement |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpSettingManagementApplicationContractsModule), |
||||
|
typeof(AbpAspNetCoreMvcModule), |
||||
|
typeof(AbpSettingManagementDomainModule))] |
||||
|
public class WorkflowManagementSettingManagementModule : AbpModule |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue