43 changed files with 1737 additions and 389 deletions
@ -0,0 +1,33 @@ |
|||
using Microsoft.Extensions.Logging; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.ExceptionHandling; |
|||
using WorkflowCore.Interface; |
|||
using WorkflowCore.Models; |
|||
|
|||
namespace LINGYUN.Abp.WorkflowCore.ExceptionHandling |
|||
{ |
|||
/// <summary>
|
|||
/// 不会自动注册到容器
|
|||
/// 需要时手动注册
|
|||
/// </summary>
|
|||
public class ExceptionNotifierHandler : IWorkflowErrorHandler |
|||
{ |
|||
public WorkflowErrorHandling Type => WorkflowErrorHandling.Terminate; |
|||
|
|||
private readonly IExceptionNotifier _exceptionNotifier; |
|||
|
|||
public ExceptionNotifierHandler(IExceptionNotifier exceptionNotifier) |
|||
{ |
|||
_exceptionNotifier = exceptionNotifier; |
|||
} |
|||
|
|||
public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception, Queue<ExecutionPointer> bubbleUpQueue) |
|||
{ |
|||
_ = _exceptionNotifier.NotifyAsync( |
|||
new ExceptionNotificationContext( |
|||
exception, |
|||
LogLevel.Warning)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace LINGYUN.Abp.WorkflowCore |
|||
{ |
|||
/// <summary>
|
|||
/// 实现接口用于启动多租户
|
|||
/// </summary>
|
|||
public interface IStepMultiTenant |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
using Microsoft.Extensions.Logging; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Features; |
|||
using WorkflowCore.Interface; |
|||
using WorkflowCore.Models; |
|||
|
|||
namespace LINGYUN.Abp.WorkflowCore.Middleware |
|||
{ |
|||
public class FeatureCheckWorkflowMiddleware : IWorkflowMiddleware |
|||
{ |
|||
private readonly IFeatureChecker _featureChecker; |
|||
private readonly ILogger<FeatureCheckWorkflowMiddleware> _logger; |
|||
public WorkflowMiddlewarePhase Phase => WorkflowMiddlewarePhase.PreWorkflow; |
|||
|
|||
public FeatureCheckWorkflowMiddleware( |
|||
IFeatureChecker featureChecker, |
|||
ILogger<FeatureCheckWorkflowMiddleware> logger) |
|||
{ |
|||
_featureChecker = featureChecker; |
|||
_logger = logger; |
|||
} |
|||
|
|||
public async Task HandleAsync(WorkflowInstance workflow, WorkflowDelegate next) |
|||
{ |
|||
if (workflow.Data is IDictionary<string, object> dictionary && |
|||
dictionary.TryGetValue(WorkflowCoreConsts.FeatureField, out var defFeatures)) |
|||
{ |
|||
var requiresFeatures = defFeatures.ToString().Split(';'); |
|||
var passed = await _featureChecker.IsEnabledAsync(requiresAll: true, requiresFeatures); |
|||
if (!passed) |
|||
{ |
|||
_logger.LogWarning("Workflow {0} was forcibly terminated for the following reasons: These required functions must be enabled: {1}", |
|||
workflow.Id, |
|||
requiresFeatures); |
|||
workflow.Status = WorkflowStatus.Terminated; |
|||
} |
|||
} |
|||
|
|||
await next(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
using Microsoft.Extensions.Options; |
|||
using Newtonsoft.Json.Linq; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.MultiTenancy; |
|||
using WorkflowCore.Interface; |
|||
using WorkflowCore.Models; |
|||
|
|||
namespace LINGYUN.Abp.WorkflowCore.Middleware |
|||
{ |
|||
public class MultiTenancyStepMiddleware : IWorkflowStepMiddleware |
|||
{ |
|||
private readonly AbpMultiTenancyOptions _multiTenancyOptions; |
|||
private readonly ICurrentTenant _currentTenant; |
|||
|
|||
public MultiTenancyStepMiddleware( |
|||
ICurrentTenant currentTenant, |
|||
IOptions<AbpMultiTenancyOptions> options) |
|||
{ |
|||
_currentTenant = currentTenant; |
|||
_multiTenancyOptions = options.Value; |
|||
} |
|||
|
|||
public async Task<ExecutionResult> HandleAsync(IStepExecutionContext context, IStepBody body, WorkflowStepDelegate next) |
|||
{ |
|||
// 触发多租户中间件条件
|
|||
// 1、需要启用多租户
|
|||
// 2、步骤需要实现接口 IStepMultiTenant
|
|||
// 3.1、传递的工作流实现接口 IMultiTenant
|
|||
// 3.2、传递的工作流数据包含 TenantId 字段
|
|||
|
|||
if (!_multiTenancyOptions.IsEnabled) |
|||
{ |
|||
return await next(); |
|||
} |
|||
|
|||
if (typeof(IStepMultiTenant).IsAssignableFrom(body.GetType())) |
|||
{ |
|||
if (context.Workflow.Data != null) |
|||
{ |
|||
if (context.Workflow.Data is IMultiTenant tenant) |
|||
{ |
|||
using (_currentTenant.Change(tenant.TenantId)) |
|||
{ |
|||
return await next(); |
|||
} |
|||
} |
|||
|
|||
var dataObj = JObject.FromObject(context.Workflow.Data); |
|||
var tenantToken = dataObj.GetOrDefault(nameof(IMultiTenant.TenantId)); |
|||
if (tenantToken?.HasValues == true) |
|||
{ |
|||
using (_currentTenant.Change(tenantToken.Value<Guid?>())) |
|||
{ |
|||
return await next(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return await next(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Abp.WorkflowCore |
|||
{ |
|||
public static class WorkflowCoreConsts |
|||
{ |
|||
public const string FeatureField = "Feature"; |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
namespace LINGYUN.Abp.WorkflowManagement.Workflows |
|||
{ |
|||
public class WorkflowDataDto |
|||
{ |
|||
/// <summary>
|
|||
/// 名称
|
|||
/// </summary>
|
|||
public string Name { get; set; } |
|||
/// <summary>
|
|||
/// 显示名称
|
|||
/// </summary>
|
|||
public string DisplayName { get; set; } |
|||
/// <summary>
|
|||
/// 数据类型
|
|||
/// </summary>
|
|||
public DataType DataType { get; set; } |
|||
/// <summary>
|
|||
/// 是否必输
|
|||
/// 默认: false
|
|||
/// </summary>
|
|||
public bool IsRequired { get; set; } |
|||
/// <summary>
|
|||
/// 是否区分大小写
|
|||
/// 默认: false
|
|||
/// 暂无用
|
|||
/// </summary>
|
|||
public bool IsCaseSensitive { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
namespace LINGYUN.Abp.WorkflowManagement |
|||
{ |
|||
public enum DataType |
|||
{ |
|||
Object, |
|||
String, |
|||
Number, |
|||
Date, |
|||
DateTime, |
|||
Booleaen |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace LINGYUN.Abp.WorkflowManagement |
|||
{ |
|||
public static class WorkflowDataConsts |
|||
{ |
|||
public static int MaxNameLength { get; set; } = 100; |
|||
public static int MaxDisplayNameLength { get; set; } = 100; |
|||
} |
|||
} |
|||
@ -0,0 +1,108 @@ |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Entities; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.WorkflowManagement |
|||
{ |
|||
public class WorkflowData : Entity<Guid>, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
public virtual Guid WorkflowId { get; protected set; } |
|||
/// <summary>
|
|||
/// 名称
|
|||
/// </summary>
|
|||
public virtual string Name { get; protected set; } |
|||
/// <summary>
|
|||
/// 显示名称
|
|||
/// </summary>
|
|||
public virtual string DisplayName { get; protected set; } |
|||
/// <summary>
|
|||
/// 数据类型
|
|||
/// </summary>
|
|||
public virtual DataType DataType { get; protected set; } |
|||
/// <summary>
|
|||
/// 是否必输
|
|||
/// 默认: false
|
|||
/// </summary>
|
|||
public virtual bool IsRequired { get; protected set; } |
|||
/// <summary>
|
|||
/// 是否区分大小写
|
|||
/// 默认: false
|
|||
/// 暂无用
|
|||
/// </summary>
|
|||
public virtual bool IsCaseSensitive { get; protected set; } |
|||
protected WorkflowData() { } |
|||
public WorkflowData( |
|||
Guid id, |
|||
Guid workflowId, |
|||
string name, |
|||
string displayName, |
|||
DataType dataType = DataType.String, |
|||
bool isRequired = false, |
|||
bool isCaseSensitive = false, |
|||
Guid? tenantId = null) : base(id) |
|||
{ |
|||
WorkflowId = workflowId; |
|||
Name = name; |
|||
DisplayName = displayName; |
|||
DataType = dataType; |
|||
IsRequired = isRequired; |
|||
IsCaseSensitive = isCaseSensitive; |
|||
TenantId = tenantId; |
|||
} |
|||
|
|||
public bool TryParse(object input, out object value) |
|||
{ |
|||
if (input == null) |
|||
{ |
|||
if (IsRequired) |
|||
{ |
|||
throw new BusinessException(WorkflowManagementErrorCodes.InvalidInputNullable) |
|||
.WithData("Property", DisplayName); |
|||
} |
|||
// 字典类型不能为空
|
|||
value = new { }; |
|||
return true; |
|||
} |
|||
|
|||
switch (DataType) |
|||
{ |
|||
case DataType.String: |
|||
value = input.ToString(); |
|||
return true; |
|||
case DataType.Booleaen: |
|||
if (input is bool boValue) |
|||
{ |
|||
value = boValue; |
|||
return true; |
|||
} |
|||
value = input.ToString().ToLower() == "true"; |
|||
return true; |
|||
case DataType.Date: |
|||
case DataType.DateTime: |
|||
if (input is DateTime dateValue) |
|||
{ |
|||
value = DataType == DataType.Date |
|||
? dateValue.Date |
|||
: dateValue; |
|||
return true; |
|||
} |
|||
value = DateTime.Parse(input.ToString()); |
|||
return true; |
|||
case DataType.Number: |
|||
if (int.TryParse(input.ToString(), out int intValue)) |
|||
{ |
|||
value = intValue; |
|||
return true; |
|||
} |
|||
value = 0; |
|||
return false; |
|||
case DataType.Object: |
|||
default: |
|||
value = input; |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,13 +1,12 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace LY.MicroService.WorkflowManagement.Controllers |
|||
namespace LY.MicroService.WorkflowManagement.Controllers; |
|||
|
|||
public class HomeController : AbpController |
|||
{ |
|||
public class HomeController : AbpController |
|||
public IActionResult Index() |
|||
{ |
|||
public IActionResult Index() |
|||
{ |
|||
return Redirect("/swagger/index.html"); |
|||
} |
|||
return Redirect("/swagger/index.html"); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,610 @@ |
|||
// <auto-generated />
|
|||
using System; |
|||
using LY.MicroService.WorkflowManagement.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Infrastructure; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace LY.MicroService.WorkflowManagement.Migrations |
|||
{ |
|||
[DbContext(typeof(WorkflowManagementMigrationsDbContext))] |
|||
[Migration("20211214085456_Add-Entity-Workflow-Data-With-WF-Management")] |
|||
partial class AddEntityWorkflowDataWithWFManagement |
|||
{ |
|||
protected override void BuildTargetModel(ModelBuilder modelBuilder) |
|||
{ |
|||
#pragma warning disable 612, 618
|
|||
modelBuilder |
|||
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.MySql) |
|||
.HasAnnotation("ProductVersion", "6.0.0") |
|||
.HasAnnotation("Relational:MaxIdentifierLength", 64); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedEvent", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<DateTime>("CreationTime") |
|||
.HasColumnType("datetime(6)") |
|||
.HasColumnName("CreationTime"); |
|||
|
|||
b.Property<string>("EventData") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<string>("EventKey") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.Property<string>("EventName") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.Property<bool>("IsProcessed") |
|||
.HasColumnType("tinyint(1)"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("TenantId"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("CreationTime"); |
|||
|
|||
b.HasIndex("IsProcessed"); |
|||
|
|||
b.HasIndex("EventName", "EventKey"); |
|||
|
|||
b.ToTable("WF_Event", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionError", b => |
|||
{ |
|||
b.Property<int>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("int"); |
|||
|
|||
b.Property<DateTime>("ErrorTime") |
|||
.HasColumnType("datetime(6)"); |
|||
|
|||
b.Property<Guid>("ExecutionPointerId") |
|||
.HasMaxLength(50) |
|||
.HasColumnType("char(50)"); |
|||
|
|||
b.Property<string>("Message") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("TenantId"); |
|||
|
|||
b.Property<Guid>("WorkflowId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("WF_ExecutionError", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionPointer", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("char(50)"); |
|||
|
|||
b.Property<bool>("Active") |
|||
.HasColumnType("tinyint(1)"); |
|||
|
|||
b.Property<string>("Children") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<string>("ContextItem") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<DateTime?>("EndTime") |
|||
.HasColumnType("datetime(6)"); |
|||
|
|||
b.Property<string>("EventData") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<string>("EventKey") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.Property<string>("EventName") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.Property<bool>("EventPublished") |
|||
.HasColumnType("tinyint(1)"); |
|||
|
|||
b.Property<string>("Outcome") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<string>("PersistenceData") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<string>("PredecessorId") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("varchar(100)"); |
|||
|
|||
b.Property<int>("RetryCount") |
|||
.HasColumnType("int"); |
|||
|
|||
b.Property<string>("Scope") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<DateTime?>("SleepUntil") |
|||
.HasColumnType("datetime(6)"); |
|||
|
|||
b.Property<DateTime?>("StartTime") |
|||
.HasColumnType("datetime(6)"); |
|||
|
|||
b.Property<int>("Status") |
|||
.HasColumnType("int"); |
|||
|
|||
b.Property<int>("StepId") |
|||
.HasColumnType("int"); |
|||
|
|||
b.Property<string>("StepName") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("varchar(100)"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("TenantId"); |
|||
|
|||
b.Property<Guid>("WorkflowId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("WorkflowId"); |
|||
|
|||
b.ToTable("WF_ExecutionPointer", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExtensionAttribute", b => |
|||
{ |
|||
b.Property<long>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("bigint"); |
|||
|
|||
b.Property<Guid>("ExecutionPointerId") |
|||
.HasMaxLength(50) |
|||
.HasColumnType("char(50)"); |
|||
|
|||
b.Property<string>("Key") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("varchar(100)"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("TenantId"); |
|||
|
|||
b.Property<string>("Value") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("ExecutionPointerId"); |
|||
|
|||
b.ToTable("WF_ExtensionAttribute", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedScheduledCommand", b => |
|||
{ |
|||
b.Property<long>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("bigint"); |
|||
|
|||
b.Property<string>("CommandName") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.Property<string>("Data") |
|||
.HasMaxLength(500) |
|||
.HasColumnType("varchar(500)"); |
|||
|
|||
b.Property<long>("ExecuteTime") |
|||
.HasColumnType("bigint"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("TenantId"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("ExecuteTime"); |
|||
|
|||
b.HasIndex("CommandName", "Data") |
|||
.IsUnique(); |
|||
|
|||
b.ToTable("WF_ScheduledCommand", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedSubscription", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<string>("EventKey") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.Property<string>("EventName") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.Property<Guid>("ExecutionPointerId") |
|||
.HasMaxLength(50) |
|||
.HasColumnType("char(50)"); |
|||
|
|||
b.Property<string>("ExternalToken") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.Property<DateTime?>("ExternalTokenExpiry") |
|||
.HasColumnType("datetime(6)"); |
|||
|
|||
b.Property<string>("ExternalWorkerId") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.Property<int>("StepId") |
|||
.HasColumnType("int"); |
|||
|
|||
b.Property<DateTime>("SubscribeAsOf") |
|||
.HasColumnType("datetime(6)"); |
|||
|
|||
b.Property<string>("SubscriptionData") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("TenantId"); |
|||
|
|||
b.Property<Guid>("WorkflowId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("EventKey"); |
|||
|
|||
b.HasIndex("EventName"); |
|||
|
|||
b.ToTable("WF_Subscription", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedWorkflow", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<DateTime?>("CompleteTime") |
|||
.HasColumnType("datetime(6)"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasMaxLength(40) |
|||
.HasColumnType("varchar(40)") |
|||
.HasColumnName("ConcurrencyStamp"); |
|||
|
|||
b.Property<DateTime>("CreationTime") |
|||
.HasColumnType("datetime(6)") |
|||
.HasColumnName("CreationTime"); |
|||
|
|||
b.Property<Guid?>("CreatorId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("CreatorId"); |
|||
|
|||
b.Property<string>("Data") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(500) |
|||
.HasColumnType("varchar(500)"); |
|||
|
|||
b.Property<string>("ExtraProperties") |
|||
.HasColumnType("longtext") |
|||
.HasColumnName("ExtraProperties"); |
|||
|
|||
b.Property<DateTime?>("LastModificationTime") |
|||
.HasColumnType("datetime(6)") |
|||
.HasColumnName("LastModificationTime"); |
|||
|
|||
b.Property<Guid?>("LastModifierId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("LastModifierId"); |
|||
|
|||
b.Property<long?>("NextExecution") |
|||
.HasColumnType("bigint"); |
|||
|
|||
b.Property<string>("Reference") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.Property<int>("Status") |
|||
.HasColumnType("int"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("TenantId"); |
|||
|
|||
b.Property<int>("Version") |
|||
.HasColumnType("int"); |
|||
|
|||
b.Property<string>("WorkflowDefinitionId") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("NextExecution"); |
|||
|
|||
b.ToTable("WF_Workflow", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowManagement.CompensateNode", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<string>("CancelCondition") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.Property<int?>("ErrorBehavior") |
|||
.HasColumnType("int"); |
|||
|
|||
b.Property<string>("Inputs") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<string>("Name") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("varchar(100)"); |
|||
|
|||
b.Property<string>("Outputs") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<Guid?>("ParentId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<TimeSpan?>("RetryInterval") |
|||
.HasColumnType("time(6)"); |
|||
|
|||
b.Property<bool>("Saga") |
|||
.HasColumnType("tinyint(1)"); |
|||
|
|||
b.Property<string>("SelectNextStep") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<string>("StepType") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("varchar(100)"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("TenantId"); |
|||
|
|||
b.Property<Guid>("WorkflowId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("WF_Compensate", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowManagement.StepNode", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<string>("CancelCondition") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.Property<int?>("ErrorBehavior") |
|||
.HasColumnType("int"); |
|||
|
|||
b.Property<string>("Inputs") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<string>("Name") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("varchar(100)"); |
|||
|
|||
b.Property<string>("Outputs") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<Guid?>("ParentId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<TimeSpan?>("RetryInterval") |
|||
.HasColumnType("time(6)"); |
|||
|
|||
b.Property<bool>("Saga") |
|||
.HasColumnType("tinyint(1)"); |
|||
|
|||
b.Property<string>("SelectNextStep") |
|||
.HasColumnType("longtext"); |
|||
|
|||
b.Property<string>("StepType") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("varchar(100)"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("TenantId"); |
|||
|
|||
b.Property<Guid>("WorkflowId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("WF_Step", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowManagement.Workflow", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasMaxLength(40) |
|||
.HasColumnType("varchar(40)") |
|||
.HasColumnName("ConcurrencyStamp"); |
|||
|
|||
b.Property<DateTime>("CreationTime") |
|||
.HasColumnType("datetime(6)") |
|||
.HasColumnName("CreationTime"); |
|||
|
|||
b.Property<Guid?>("CreatorId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("CreatorId"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.Property<string>("DisplayName") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("varchar(200)"); |
|||
|
|||
b.Property<int>("ErrorBehavior") |
|||
.HasColumnType("int"); |
|||
|
|||
b.Property<TimeSpan?>("ErrorRetryInterval") |
|||
.HasColumnType("time(6)"); |
|||
|
|||
b.Property<string>("ExtraProperties") |
|||
.HasColumnType("longtext") |
|||
.HasColumnName("ExtraProperties"); |
|||
|
|||
b.Property<bool>("IsEnabled") |
|||
.HasColumnType("tinyint(1)"); |
|||
|
|||
b.Property<DateTime?>("LastModificationTime") |
|||
.HasColumnType("datetime(6)") |
|||
.HasColumnName("LastModificationTime"); |
|||
|
|||
b.Property<Guid?>("LastModifierId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("LastModifierId"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(100) |
|||
.HasColumnType("varchar(100)"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("TenantId"); |
|||
|
|||
b.Property<int>("Version") |
|||
.HasColumnType("int"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("WF_Definition", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowManagement.WorkflowData", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<int>("DataType") |
|||
.HasColumnType("int"); |
|||
|
|||
b.Property<string>("DisplayName") |
|||
.IsRequired() |
|||
.HasMaxLength(100) |
|||
.HasColumnType("varchar(100)"); |
|||
|
|||
b.Property<bool>("IsCaseSensitive") |
|||
.HasColumnType("tinyint(1)"); |
|||
|
|||
b.Property<bool>("IsRequired") |
|||
.HasColumnType("tinyint(1)"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(100) |
|||
.HasColumnType("varchar(100)"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnType("char(36)") |
|||
.HasColumnName("TenantId"); |
|||
|
|||
b.Property<Guid>("WorkflowId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("WorkflowId"); |
|||
|
|||
b.ToTable("WF_DefinitionData", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionPointer", b => |
|||
{ |
|||
b.HasOne("LINGYUN.Abp.WorkflowCore.Persistence.PersistedWorkflow", "Workflow") |
|||
.WithMany("ExecutionPointers") |
|||
.HasForeignKey("WorkflowId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("Workflow"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExtensionAttribute", b => |
|||
{ |
|||
b.HasOne("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionPointer", "ExecutionPointer") |
|||
.WithMany("ExtensionAttributes") |
|||
.HasForeignKey("ExecutionPointerId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("ExecutionPointer"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowManagement.WorkflowData", b => |
|||
{ |
|||
b.HasOne("LINGYUN.Abp.WorkflowManagement.Workflow", null) |
|||
.WithMany("Datas") |
|||
.HasForeignKey("WorkflowId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedExecutionPointer", b => |
|||
{ |
|||
b.Navigation("ExtensionAttributes"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowCore.Persistence.PersistedWorkflow", b => |
|||
{ |
|||
b.Navigation("ExecutionPointers"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.WorkflowManagement.Workflow", b => |
|||
{ |
|||
b.Navigation("Datas"); |
|||
}); |
|||
#pragma warning restore 612, 618
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,147 @@ |
|||
using System; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace LY.MicroService.WorkflowManagement.Migrations |
|||
{ |
|||
public partial class AddEntityWorkflowDataWithWFManagement : Migration |
|||
{ |
|||
protected override void Up(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.AlterColumn<Guid>( |
|||
name: "ExecutionPointerId", |
|||
table: "WF_Subscription", |
|||
type: "char(50)", |
|||
maxLength: 50, |
|||
nullable: false, |
|||
collation: "ascii_general_ci", |
|||
oldClrType: typeof(string), |
|||
oldType: "char(50)", |
|||
oldMaxLength: 50) |
|||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
|||
|
|||
migrationBuilder.AlterColumn<Guid>( |
|||
name: "ExecutionPointerId", |
|||
table: "WF_ExtensionAttribute", |
|||
type: "char(50)", |
|||
maxLength: 50, |
|||
nullable: false, |
|||
collation: "ascii_general_ci", |
|||
oldClrType: typeof(string), |
|||
oldType: "char(50)", |
|||
oldMaxLength: 50) |
|||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
|||
|
|||
migrationBuilder.AlterColumn<Guid>( |
|||
name: "Id", |
|||
table: "WF_ExecutionPointer", |
|||
type: "char(50)", |
|||
maxLength: 50, |
|||
nullable: false, |
|||
collation: "ascii_general_ci", |
|||
oldClrType: typeof(string), |
|||
oldType: "char(50)", |
|||
oldMaxLength: 50) |
|||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
|||
|
|||
migrationBuilder.AlterColumn<Guid>( |
|||
name: "ExecutionPointerId", |
|||
table: "WF_ExecutionError", |
|||
type: "char(50)", |
|||
maxLength: 50, |
|||
nullable: false, |
|||
collation: "ascii_general_ci", |
|||
oldClrType: typeof(string), |
|||
oldType: "char(50)", |
|||
oldMaxLength: 50) |
|||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
|||
|
|||
migrationBuilder.CreateTable( |
|||
name: "WF_DefinitionData", |
|||
columns: table => new |
|||
{ |
|||
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"), |
|||
TenantId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
|||
WorkflowId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"), |
|||
Name = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: false) |
|||
.Annotation("MySql:CharSet", "utf8mb4"), |
|||
DisplayName = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: false) |
|||
.Annotation("MySql:CharSet", "utf8mb4"), |
|||
DataType = table.Column<int>(type: "int", nullable: false), |
|||
IsRequired = table.Column<bool>(type: "tinyint(1)", nullable: false), |
|||
IsCaseSensitive = table.Column<bool>(type: "tinyint(1)", nullable: false) |
|||
}, |
|||
constraints: table => |
|||
{ |
|||
table.PrimaryKey("PK_WF_DefinitionData", x => x.Id); |
|||
table.ForeignKey( |
|||
name: "FK_WF_DefinitionData_WF_Definition_WorkflowId", |
|||
column: x => x.WorkflowId, |
|||
principalTable: "WF_Definition", |
|||
principalColumn: "Id", |
|||
onDelete: ReferentialAction.Cascade); |
|||
}) |
|||
.Annotation("MySql:CharSet", "utf8mb4"); |
|||
|
|||
migrationBuilder.CreateIndex( |
|||
name: "IX_WF_DefinitionData_WorkflowId", |
|||
table: "WF_DefinitionData", |
|||
column: "WorkflowId"); |
|||
} |
|||
|
|||
protected override void Down(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.DropTable( |
|||
name: "WF_DefinitionData"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "ExecutionPointerId", |
|||
table: "WF_Subscription", |
|||
type: "char(50)", |
|||
maxLength: 50, |
|||
nullable: false, |
|||
oldClrType: typeof(Guid), |
|||
oldType: "char(50)", |
|||
oldMaxLength: 50) |
|||
.Annotation("MySql:CharSet", "utf8mb4") |
|||
.OldAnnotation("Relational:Collation", "ascii_general_ci"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "ExecutionPointerId", |
|||
table: "WF_ExtensionAttribute", |
|||
type: "char(50)", |
|||
maxLength: 50, |
|||
nullable: false, |
|||
oldClrType: typeof(Guid), |
|||
oldType: "char(50)", |
|||
oldMaxLength: 50) |
|||
.Annotation("MySql:CharSet", "utf8mb4") |
|||
.OldAnnotation("Relational:Collation", "ascii_general_ci"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "Id", |
|||
table: "WF_ExecutionPointer", |
|||
type: "char(50)", |
|||
maxLength: 50, |
|||
nullable: false, |
|||
oldClrType: typeof(Guid), |
|||
oldType: "char(50)", |
|||
oldMaxLength: 50) |
|||
.Annotation("MySql:CharSet", "utf8mb4") |
|||
.OldAnnotation("Relational:Collation", "ascii_general_ci"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "ExecutionPointerId", |
|||
table: "WF_ExecutionError", |
|||
type: "char(50)", |
|||
maxLength: 50, |
|||
nullable: false, |
|||
oldClrType: typeof(Guid), |
|||
oldType: "char(50)", |
|||
oldMaxLength: 50) |
|||
.Annotation("MySql:CharSet", "utf8mb4") |
|||
.OldAnnotation("Relational:Collation", "ascii_general_ci"); |
|||
} |
|||
} |
|||
} |
|||
@ -1,18 +1,30 @@ |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System.IO; |
|||
using Volo.Abp.IO; |
|||
using Volo.Abp.Modularity.PlugIns; |
|||
|
|||
namespace LY.MicroService.WorkflowManagement |
|||
namespace LY.MicroService.WorkflowManagement; |
|||
|
|||
public class Startup |
|||
{ |
|||
public class Startup |
|||
public void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
public void ConfigureServices(IServiceCollection services) |
|||
services.AddApplication<WorkflowManagementHttpApiHostModule>(options => |
|||
{ |
|||
services.AddApplication<WorkflowManagementHttpApiHostModule>(); |
|||
} |
|||
// 搜索 Modules 目录下所有文件作为插件
|
|||
// 取消显示引用所有其他项目的模块,改为通过插件的形式引用
|
|||
var pluginFolder = Path.Combine( |
|||
Directory.GetCurrentDirectory(), "Modules"); |
|||
DirectoryHelper.CreateIfNotExists(pluginFolder); |
|||
options.PlugInSources.AddFolder( |
|||
pluginFolder, |
|||
SearchOption.AllDirectories); |
|||
}); |
|||
} |
|||
|
|||
public void Configure(IApplicationBuilder app) |
|||
{ |
|||
app.InitializeApplication(); |
|||
} |
|||
public void Configure(IApplicationBuilder app) |
|||
{ |
|||
app.InitializeApplication(); |
|||
} |
|||
} |
|||
|
|||
@ -1,18 +1,19 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<IsPackable>false</IsPackable> |
|||
</PropertyGroup> |
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<IsPackable>false</IsPackable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> |
|||
<!--<PackageReference Include="WorkflowCore.Testing" Version="3.5.2" />--> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\modules\workflow\LINGYUN.Abp.WorkflowCore\LINGYUN.Abp.WorkflowCore.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\modules\workflow\LINGYUN.Abp.WorkflowCore\LINGYUN.Abp.WorkflowCore.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
|
|||
@ -0,0 +1,72 @@ |
|||
using Shouldly; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.MultiTenancy; |
|||
using WorkflowCore.Interface; |
|||
using WorkflowCore.Models; |
|||
using Xunit; |
|||
|
|||
namespace LINGYUN.Abp.WorkflowCore.Middleware |
|||
{ |
|||
public class MultiTenancyStepMiddleware_Tests : AbpWorkflowCoreTestBase |
|||
{ |
|||
public static bool IsCompleted = false; |
|||
public static readonly Guid TenantId = Guid.NewGuid(); |
|||
private readonly IWorkflowController _controller; |
|||
public MultiTenancyStepMiddleware_Tests() |
|||
{ |
|||
_controller = GetRequiredService<IWorkflowController>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Be_Resolved_Multi_Tenant_Id_On_Step() |
|||
{ |
|||
await _controller.StartWorkflow( |
|||
"MiddlewareWorkflow", |
|||
new MiddlewareWorkflowData |
|||
{ |
|||
TenantId = TenantId |
|||
}); |
|||
|
|||
} |
|||
} |
|||
|
|||
public class MiddlewareWorkflowData : IMultiTenant |
|||
{ |
|||
public Guid? TenantId { get; set; } |
|||
} |
|||
|
|||
public class MiddlewareWorkflow : IWorkflow<MiddlewareWorkflowData> |
|||
{ |
|||
public string Id => "MiddlewareWorkflow"; |
|||
|
|||
public int Version => 1; |
|||
|
|||
public void Build(IWorkflowBuilder<MiddlewareWorkflowData> builder) |
|||
{ |
|||
builder.StartWith((_) => Console.WriteLine("Start Workflow")) |
|||
.Then<MessageStep>() |
|||
.Then((_) => MultiTenancyStepMiddleware_Tests.IsCompleted = true) |
|||
.EndWorkflow(); |
|||
} |
|||
} |
|||
|
|||
public class MessageStep : StepBodyBase |
|||
{ |
|||
private readonly ICurrentTenant _currentTenant; |
|||
|
|||
public MessageStep(ICurrentTenant currentTenant) |
|||
{ |
|||
_currentTenant = currentTenant; |
|||
} |
|||
|
|||
public override ExecutionResult Run(IStepExecutionContext context) |
|||
{ |
|||
MultiTenancyStepMiddleware_Tests.TenantId.ShouldBe(_currentTenant.Id.Value); |
|||
|
|||
Console.WriteLine("Current Tenant Id: {0}", _currentTenant.Id?.ToString() ?? "Null"); |
|||
|
|||
return ExecutionResult.Next(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue