diff --git a/Caspian.WorkflowFramework.Package/Caspian.WorkflowFramework.Package.csproj b/Caspian.WorkflowFramework.Package/Caspian.WorkflowFramework.Package.csproj index 3365e3d..bbe623d 100644 --- a/Caspian.WorkflowFramework.Package/Caspian.WorkflowFramework.Package.csproj +++ b/Caspian.WorkflowFramework.Package/Caspian.WorkflowFramework.Package.csproj @@ -5,7 +5,7 @@ enable enable Caspian.WorkflowFramework - 1.1.0-preview.17 + 1.1.0-preview.18 Behzad Alizadeh Caspian Caspian.WorkflowFramework @@ -20,55 +20,55 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Caspian.WorkflowFramework.Package/Contracts/IWorkflowService.cs b/Caspian.WorkflowFramework.Package/Contracts/IWorkflowService.cs index a20639f..0dc221b 100644 --- a/Caspian.WorkflowFramework.Package/Contracts/IWorkflowService.cs +++ b/Caspian.WorkflowFramework.Package/Contracts/IWorkflowService.cs @@ -48,5 +48,15 @@ namespace Caspian.WorkflowFramework.Contracts /// Cancels the specified workflow instance. /// Task> CancelInstanceAsync(CancelWorkflowInstanceModel input); + + /// + /// Cancels the specified workflow instance. + /// + Task> ForceCancelInstanceAsync(CancelWorkflowInstanceModel input); + + /// + /// Evicts a workflow definition from cache (optional, commented out in service). + /// + Task EvictDefFromCacheAsync(string name, CancellationToken cancellationToken = default); } } diff --git a/Caspian.WorkflowFramework.Package/Services/WorkflowService.cs b/Caspian.WorkflowFramework.Package/Services/WorkflowService.cs index aad096c..5819a6f 100644 --- a/Caspian.WorkflowFramework.Package/Services/WorkflowService.cs +++ b/Caspian.WorkflowFramework.Package/Services/WorkflowService.cs @@ -1,7 +1,4 @@ -using System.Diagnostics; -using System.Dynamic; -using System.Text.Json; -using Caspian.WorkflowFramework.Contracts; +using Caspian.WorkflowFramework.Contracts; using Caspian.WorkflowFramework.Models; using Caspian.WorkflowFramework.Utility; using Elsa.Common.Entities; @@ -11,11 +8,15 @@ using Elsa.Workflows.Management; using Elsa.Workflows.Management.Filters; using Elsa.Workflows.Models; using Elsa.Workflows.Runtime; +using Elsa.Workflows.Runtime.Filters; using Elsa.Workflows.Runtime.Messages; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using San.MongoLogger; +using System.Diagnostics; +using System.Dynamic; +using System.Text.Json; namespace Caspian.WorkflowFramework.Services; public class WorkflowService : IWorkflowService, ILoggable @@ -29,6 +30,8 @@ public class WorkflowService : IWorkflowService, ILoggable private readonly IWorkflowInstanceStore _workflowInstanceStore; private readonly IEventPublisher _eventPublisher; private readonly IWorkflowDefinitionCacheManager _workflowDefinitionCacheManager; + private readonly IBookmarkStore _bookmarkStore; + private readonly IActivityExecutionStore _activityExecutionStore; private readonly IMdbLogger _logger; #endregion @@ -43,6 +46,8 @@ public class WorkflowService : IWorkflowService, ILoggable _workflowInstanceManager = _serviceProvider.GetRequiredService(); _workflowInstanceStore = _serviceProvider.GetRequiredService(); _eventPublisher = _serviceProvider.GetRequiredService(); + _bookmarkStore = _serviceProvider.GetRequiredService(); + _activityExecutionStore = _serviceProvider.GetRequiredService(); _logger = logger; } #endregion @@ -724,4 +729,117 @@ public class WorkflowService : IWorkflowService, ILoggable } } #endregion + + #region ForceCancelInstanceAsync + public async Task> ForceCancelInstanceAsync(CancelWorkflowInstanceModel input) + { + var sw = Stopwatch.StartNew(); + try + { + _logger.Log("Start ForceCancelInstanceAsync", WFInstanceId: input.WorkflowInstanceId, input); + + var instance = await _workflowInstanceManager.FindByIdAsync(input.WorkflowInstanceId); + + if (instance == null) + return new WorkflowResponse { Status = 1, Message = "Instance not found", Data = false }; + + // جلوگری از Resume شدن دوباره + instance.Status = WorkflowStatus.Finished; + instance.SubStatus = WorkflowSubStatus.Cancelled; + + // پاک‌سازی bookmarks از دیتابیس واقعی + var bookmarkFilter = new BookmarkFilter() + { + WorkflowInstanceId = input.WorkflowInstanceId + }; + await _bookmarkStore.DeleteAsync(bookmarkFilter); + + // پاک‌سازی scheduled activities (در صورتی که runtime distributed باشد) + var activityFilter = new ActivityExecutionRecordFilter() + { + WorkflowInstanceId = input.WorkflowInstanceId + }; + await _activityExecutionStore.DeleteManyAsync(activityFilter); + + //// پاک‌سازی incidents (در پروژه‌های بزرگ ضروری است) + //await _incidentStore.DeleteManyAsync( + // x => x.WorkflowInstanceId == input.WorkflowInstanceId + //); + + // ذخیره instance تغییر یافته + await _workflowInstanceManager.SaveAsync(instance); + + sw.Stop(); + + var response = new WorkflowResponse + { + Status = 0, + Message = "Force cancel completed", + Data = true + }; + + _logger.Log("End ForceCancelInstanceAsync", WFInstanceId: instance.Id, response, sw.ElapsedMilliseconds); + + return response; + } + catch (Exception ex) + { + sw.Stop(); + + var response = new WorkflowResponse + { + Status = 100, + Message = ex.Message, + Data = false + }; + + _logger.Log("Exception in ForceCancelInstanceAsync", WFInstanceId: input.WorkflowInstanceId, response, sw.ElapsedMilliseconds, ex); + + return response; + } + } + + #endregion + + #region EvictDefFromCacheAsync + /// + public async Task EvictDefFromCacheAsync(string name, CancellationToken cancellationToken = default) + { + var sw = Stopwatch.StartNew(); + + try + { + _logger.Log("Start of EvictDefFromCacheAsync", name); + + var defFilter = new WorkflowDefinitionFilter() + { + Name = name, + VersionOptions = VersionOptions.Published + }; + + var definition = await _workflowDefinitionService + .FindWorkflowDefinitionAsync(defFilter); + + await _workflowDefinitionCacheManager.EvictWorkflowDefinitionAsync(definition.DefinitionId); + + var workflowDefinitionFilter = new WorkflowDefinitionFilter() + { + DefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId(definition.DefinitionId, VersionOptions.Published) + }; + + var createWorkflowDefinitionFilterCacheKey = _workflowDefinitionCacheManager.CreateWorkflowDefinitionFilterCacheKey(workflowDefinitionFilter); + _workflowDefinitionCacheManager.Cache.Remove(createWorkflowDefinitionFilterCacheKey); + + + var createWorkflowFilterCacheKey = _workflowDefinitionCacheManager.CreateWorkflowFilterCacheKey(workflowDefinitionFilter); + _workflowDefinitionCacheManager.Cache.Remove(createWorkflowFilterCacheKey); + } + finally + { + sw.Stop(); + _logger.Log("End of EvictDefFromCacheAsync", name, sw.ElapsedMilliseconds, null); + } + } + + #endregion } \ No newline at end of file diff --git a/NuGet.Config b/NuGet.Config index 5af4652..5691179 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -9,5 +9,6 @@ + \ No newline at end of file