add apies

This commit is contained in:
Behzad Alizadeh
2026-01-06 19:55:48 +03:30
parent d31c2ab0c7
commit ccb3a24758
11 changed files with 482 additions and 150 deletions
@@ -5,7 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackageId>Caspian.WorkflowFramework</PackageId>
<Version>1.1.0-preview.6</Version>
<Version>1.1.0-preview.7</Version>
<Authors>Digital banking team</Authors>
<Company>Caspian</Company>
<Product>Caspian.WorkflowFramework</Product>
@@ -7,30 +7,46 @@ namespace Caspian.WorkflowFramework.Contracts
/// <summary>
/// Create and executes a new workflow instance.
/// </summary>
/// <param name="input"></param>
/// <returns>A new <see cref="WorkflowResponse<WorkflowModel>"/> object.</returns>
/// <remarks>Create a new workflow instance and executes it and waits for it to complete or reach a suspend point.</remarks>
Task<WorkflowResponse<WorkflowModel>> CreateAndExecuteAsync(CreateAndExecuteWorkflowModel input);
/// <summary>
/// Create and get a last workflow running instance.
/// Create a new workflow instance if it does not exist or get the running instance.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<WorkflowResponse<WorkflowModel>> CreateOrGetInstanceAsync(CreateAndExecuteWorkflowModel input);
/// <summary>
/// Publishes the specified event.
/// Publishes the specified event to a workflow instance.
/// </summary>
/// <param name="input"></param>
/// <returns>A new <see cref="WorkflowResponse<WorkflowModel>"/> object.</returns>
Task<WorkflowResponse<WorkflowModel>> PublishEventAsync(PublishEventModel input);
/// <summary>
/// Inquiry last state of workflow instance.
/// </summary>
/// <param name="input"></param>
/// <returns>A new <see cref="WorkflowResponse<WorkflowModel>"/> object.</returns>
Task<WorkflowResponse<WorkflowModel>> InquiryStateAsync(InquiryStateModel input);
/// <summary>
/// Gets all active workflow instances for a specific user.
/// </summary>
Task<WorkflowResponse<List<WorkflowModel>>> GetActiveInstancesAsync(GetActiveInstancesModel input);
/// <summary>
/// Gets all active workflow instance IDs for a specific user.
/// </summary>
Task<WorkflowResponse<List<string>>> GetActiveInstanceIdsAsync(GetActiveInstancesModel input);
/// <summary>
/// Gets the latest active workflow instance for a specific user and workflow name.
/// </summary>
Task<WorkflowResponse<WorkflowModel>> GetLatestActiveInstanceAsync(GetLatestActiveInstanceModel input);
/// <summary>
/// Gets the ID of the latest active workflow instance for a specific user and workflow name.
/// </summary>
Task<WorkflowResponse<dynamic>> GetLatestActiveInstanceIdAsync(GetLatestActiveInstanceModel input);
/// <summary>
/// Cancels the specified workflow instance.
/// </summary>
Task<WorkflowResponse<dynamic>> CancelInstanceAsync(CancelWorkflowInstanceModel input);
}
}
@@ -0,0 +1,6 @@
namespace Caspian.WorkflowFramework.Models;
public class CancelWorkflowInstanceModel
{
public string WorkflowInstanceId { get; set; }
public long UserId { get; set; }
}
@@ -1,17 +1,11 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using Elsa.Common.Models;
using JetBrains.Annotations;
namespace Caspian.WorkflowFramework.Models
namespace Caspian.WorkflowFramework.Models;
[PublicAPI]
public class CreateAndExecuteWorkflowModel
{
[PublicAPI]
public class CreateAndExecuteWorkflowModel
{
/// <summary>
/// Workflow name
/// </summary>
@@ -29,5 +23,4 @@ namespace Caspian.WorkflowFramework.Models
/// </summary>
[Required]
public long? UserId { get; set; }
}
}
@@ -0,0 +1,5 @@
namespace Caspian.WorkflowFramework.Models;
public class GetActiveInstancesModel
{
public long UserId { get; set; }
}
@@ -0,0 +1,6 @@
namespace Caspian.WorkflowFramework.Models;
public class GetLatestActiveInstanceModel
{
public long UserId { get; set; }
public string WorkflowName { get; set; }
}
@@ -1,15 +1,8 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
namespace Caspian.WorkflowFramework.Models
namespace Caspian.WorkflowFramework.Models;
public class InquiryStateModel
{
public class InquiryStateModel
{
[Required]
public string WorkflowInstanceId { get; set; }
}
}
@@ -1,12 +1,11 @@
using JetBrains.Annotations;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using System.Text.Json;
using JetBrains.Annotations;
namespace Caspian.WorkflowFramework.Models
namespace Caspian.WorkflowFramework.Models;
[PublicAPI]
public class PublishEventModel
{
[PublicAPI]
public class PublishEventModel
{
[Required]
public string WorkflowInstanceId { get; set; }
@@ -15,5 +14,4 @@ namespace Caspian.WorkflowFramework.Models
[Required]
public JsonElement EventData { get; set; }
}
}
@@ -1,16 +1,10 @@
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Text.Json;
using JetBrains.Annotations;
namespace Caspian.WorkflowFramework.Models
namespace Caspian.WorkflowFramework.Models;
[PublicAPI]
public class WorkflowModel
{
[PublicAPI]
public class WorkflowModel
{
/// <summary>
/// شناسه گردش کار
/// </summary>
@@ -53,5 +47,9 @@ namespace Caspian.WorkflowFramework.Models
/// رویدادی که باید پس از سابمیت صفحه بعدی منتشر شود
/// </summary>
public string? NextPageEventName { get; set; }
}
/// <summary>
/// جای ذخیره‌ی موقت برای استفاده های بعدی
/// </summary>
public JsonElement? WorkflowStash { get; set; }
}
@@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using JetBrains.Annotations;
namespace Caspian.WorkflowFramework.Models
namespace Caspian.WorkflowFramework.Models;
[PublicAPI]
public class WorkflowResponse<T> : WorkflowResponseBase where T : class, new()
{
[PublicAPI]
public class WorkflowResponse<T> : WorkflowResponseBase where T : class, new()
{
public WorkflowResponse()
{
Data = new T();
@@ -19,11 +13,11 @@ namespace Caspian.WorkflowFramework.Models
/// پاسخ
/// </summary>
public T? Data { get; set; }
}
}
[PublicAPI]
public class WorkflowResponseBase
{
[PublicAPI]
public class WorkflowResponseBase
{
/// <summary>
/// وضعیت پاسخ
/// </summary>
@@ -33,5 +27,4 @@ namespace Caspian.WorkflowFramework.Models
/// پیام پاسخ
/// </summary>
public string? Message { get; set; }
}
}
@@ -1,6 +1,7 @@
using Caspian.WorkflowFramework.Contracts;
using Caspian.WorkflowFramework.Models;
using Caspian.WorkflowFramework.Utility;
using Elsa.Common.Entities;
using Elsa.Common.Models;
using Elsa.Workflows;
using Elsa.Workflows.Management;
@@ -26,7 +27,9 @@ namespace Caspian.WorkflowFramework.Services
private readonly IWorkflowDefinitionService _workflowDefinitionService;
private readonly IWorkflowInstanceVariableManager _workflowInstanceVariableManager;
private readonly IWorkflowInstanceManager _workflowInstanceManager;
private readonly IWorkflowInstanceStore _workflowInstanceStore;
private readonly IEventPublisher _eventPublisher;
private readonly IWorkflowDefinitionCacheManager _workflowDefinitionCacheManager;
private readonly IMdbLogger<WorkflowService> _logger;
#endregion
@@ -39,6 +42,7 @@ namespace Caspian.WorkflowFramework.Services
_workflowDefinitionService = _serviceProvider.GetRequiredService<IWorkflowDefinitionService>();
_workflowInstanceVariableManager = _serviceProvider.GetRequiredService<IWorkflowInstanceVariableManager>();
_workflowInstanceManager = _serviceProvider.GetRequiredService<IWorkflowInstanceManager>();
_workflowInstanceStore = _serviceProvider.GetRequiredService<IWorkflowInstanceStore>();
_eventPublisher = _serviceProvider.GetRequiredService<IEventPublisher>();
_logger = logger;
}
@@ -167,13 +171,25 @@ namespace Caspian.WorkflowFramework.Services
return response;
}
var runningWorkflowInstance = await _workflowInstanceManager
.FindAsync(new WorkflowInstanceFilter()
var instances = await _workflowInstanceStore.FindManyAsync(
new WorkflowInstanceFilter
{
ExternalUserId = input.UserId,
DefinitionId = definition.DefinitionId,
WorkflowStatus = WorkflowStatus.Running
});
},
new WorkflowInstanceOrder<DateTimeOffset>(x => x.UpdatedAt!, OrderDirection.Descending)
);
var runningWorkflowInstance = instances.FirstOrDefault();
//var runningWorkflowInstance = await _workflowInstanceManager
// .FindAsync(new WorkflowInstanceFilter()
// {
// ExternalUserId = input.UserId,
// DefinitionId = definition.DefinitionId,
// WorkflowStatus = WorkflowStatus.Running
// });
var workflowInstanceId = string.Empty;
if (runningWorkflowInstance != null)
@@ -195,14 +211,15 @@ namespace Caspian.WorkflowFramework.Services
workflowInstanceId = result.WorkflowInstanceId;
}
var id = workflowInstanceId;
var variables = await _workflowInstanceVariableManager
.GetVariablesAsync(workflowInstanceId);
.GetVariablesAsync(id);
// تبدیل به دیکشنری
var dictionary = variables.ToDictionary(a => a.Variable.Name, a => a.Value);
var workflowModel = dictionary.ConvertToWorkflowModel();
workflowModel.InstanceId = workflowClient.WorkflowInstanceId;
workflowModel.InstanceId = workflowInstanceId;
workflowModel.UserId = input.UserId;
response = new WorkflowResponse<WorkflowModel>()
@@ -282,6 +299,7 @@ namespace Caspian.WorkflowFramework.Services
payload = JsonConvert.DeserializeObject<ExpandoObject>(json, settings);
}
//await _eventPublisher.PublishAsync(input.EventName, null, input.WorkflowInstanceId, null, input.EventData, asynchronous: true);
await _eventPublisher.PublishAsync(input.EventName, null, input.WorkflowInstanceId, null, payload);
var variables = await _workflowInstanceVariableManager.GetVariablesAsync(input.WorkflowInstanceId);
@@ -393,5 +411,311 @@ namespace Caspian.WorkflowFramework.Services
}
}
#endregion
#region GetActiveInstancesAsync
public async Task<WorkflowResponse<List<WorkflowModel>>> GetActiveInstancesAsync(GetActiveInstancesModel input)
{
var sw = Stopwatch.StartNew();
try
{
_logger.Log("Start of GetActiveInstancesAsync", input);
var instances = await _workflowInstanceStore.FindManyAsync(
new WorkflowInstanceFilter
{
ExternalUserId = input.UserId,
WorkflowStatus = WorkflowStatus.Running
});
var result = new List<WorkflowModel>();
foreach (var instance in instances)
{
var variables = await _workflowInstanceVariableManager.GetVariablesAsync(instance.Id);
var dict = variables.ToDictionary(x => x.Variable.Name, x => x.Value);
var model = dict.ConvertToWorkflowModel();
model.InstanceId = instance.Id;
model.UserId = instance.ExternalUserId;
result.Add(model);
}
var response = new WorkflowResponse<List<WorkflowModel>>
{
Status = 0,
Message = "Success",
Data = result
};
sw.Stop();
_logger.Log("End of GetActiveInstancesAsync", response, sw.ElapsedMilliseconds);
return response;
}
catch (Exception ex)
{
var response = new WorkflowResponse<List<WorkflowModel>>
{
Status = 100,
Message = ex.Message,
Data = null
};
sw.Stop();
_logger.Log("Exception in GetActiveInstancesAsync", response, sw.ElapsedMilliseconds, ex);
return response;
}
}
#endregion
#region GetActiveInstanceIdsAsync
public async Task<WorkflowResponse<List<string>>> GetActiveInstanceIdsAsync(GetActiveInstancesModel input)
{
var sw = Stopwatch.StartNew();
try
{
_logger.Log("Start of GetActiveInstanceIdsAsync", input);
var instances = await _workflowInstanceStore.FindManyAsync(
new WorkflowInstanceFilter
{
ExternalUserId = input.UserId,
WorkflowStatus = WorkflowStatus.Running
});
var instanceIds = instances.Select(i => i.Id).ToList();
var response = new WorkflowResponse<List<string>>
{
Status = 0,
Message = "Success",
Data = instanceIds
};
sw.Stop();
_logger.Log("End of GetActiveInstanceIdsAsync", response, sw.ElapsedMilliseconds);
return response;
}
catch (Exception ex)
{
var response = new WorkflowResponse<List<string>>
{
Status = 100,
Message = ex.Message,
Data = null
};
sw.Stop();
_logger.Log("Exception in GetActiveInstanceIdsAsync", response, sw.ElapsedMilliseconds, ex);
return response;
}
}
#endregion
#region GetLatestActiveInstanceAsync
public async Task<WorkflowResponse<WorkflowModel>> GetLatestActiveInstanceAsync(GetLatestActiveInstanceModel input)
{
var sw = Stopwatch.StartNew();
try
{
_logger.Log("Start of GetLatestActiveInstanceAsync", input);
var definition = await _workflowDefinitionService.FindWorkflowDefinitionAsync(
new WorkflowDefinitionFilter { Name = input.WorkflowName });
if (definition == null)
{
return new WorkflowResponse<WorkflowModel>
{
Status = 1,
Message = $"Workflow '{input.WorkflowName}' not found",
Data = null
};
}
var instances = await _workflowInstanceStore.FindManyAsync(
new WorkflowInstanceFilter
{
ExternalUserId = input.UserId,
DefinitionId = definition.DefinitionId,
WorkflowStatus = WorkflowStatus.Running
},
new WorkflowInstanceOrder<DateTimeOffset>(x => x.UpdatedAt!, OrderDirection.Descending)
);
var latest = instances.FirstOrDefault();
if (latest == null)
{
return new WorkflowResponse<WorkflowModel>
{
Status = 1,
Message = "No active workflow instance found",
Data = null
};
}
var variables = await _workflowInstanceVariableManager.GetVariablesAsync(latest.Id);
var dict = variables.ToDictionary(x => x.Variable.Name, x => x.Value);
var model = dict.ConvertToWorkflowModel();
model.InstanceId = latest.Id;
model.UserId = latest.ExternalUserId;
var response = new WorkflowResponse<WorkflowModel>
{
Status = 0,
Message = "Success",
Data = model
};
sw.Stop();
_logger.Log("End of GetLatestActiveInstanceAsync", response, sw.ElapsedMilliseconds);
return response;
}
catch (Exception ex)
{
var response = new WorkflowResponse<WorkflowModel>
{
Status = 100,
Message = ex.Message,
Data = null
};
sw.Stop();
_logger.Log("Exception in GetLatestActiveInstanceAsync", response, sw.ElapsedMilliseconds, ex);
return response;
}
}
#endregion
#region GetLatestActiveInstanceIdAsync
public async Task<WorkflowResponse<dynamic>> GetLatestActiveInstanceIdAsync(GetLatestActiveInstanceModel input)
{
var sw = Stopwatch.StartNew();
try
{
_logger.Log("Start of GetLatestActiveInstanceIdAsync", input);
var definition = await _workflowDefinitionService.FindWorkflowDefinitionAsync(
new WorkflowDefinitionFilter { Name = input.WorkflowName });
if (definition == null)
{
return new WorkflowResponse<dynamic>
{
Status = 1,
Message = $"Workflow '{input.WorkflowName}' not found",
Data = null
};
}
var instances = await _workflowInstanceStore.FindManyAsync(
new WorkflowInstanceFilter
{
ExternalUserId = input.UserId,
DefinitionId = definition.DefinitionId,
WorkflowStatus = WorkflowStatus.Running
},
new WorkflowInstanceOrder<DateTimeOffset>(x => x.UpdatedAt!, OrderDirection.Descending)
);
var latest = instances.FirstOrDefault();
if (latest == null)
{
return new WorkflowResponse<dynamic>
{
Status = 1,
Message = "No active workflow instance found",
Data = null
};
}
var response = new WorkflowResponse<dynamic>
{
Status = 0,
Message = "Success",
Data = latest.Id
};
sw.Stop();
_logger.Log("End of GetLatestActiveInstanceIdAsync", response, sw.ElapsedMilliseconds);
return response;
}
catch (Exception ex)
{
var response = new WorkflowResponse<dynamic>
{
Status = 100,
Message = ex.Message,
Data = null
};
sw.Stop();
_logger.Log("Exception in GetLatestActiveInstanceIdAsync", response, sw.ElapsedMilliseconds, ex);
return response;
}
}
#endregion
#region CancelInstanceAsync
public async Task<WorkflowResponse<dynamic>> CancelInstanceAsync(CancelWorkflowInstanceModel input)
{
var sw = Stopwatch.StartNew();
try
{
_logger.Log("Start of CancelInstanceAsync", WFInstanceId: input.WorkflowInstanceId, input);
// پیدا کردن instance
var instance = await _workflowInstanceStore.FindAsync(
new WorkflowInstanceFilter
{
ExternalUserId = input.UserId,
WorkflowStatus = WorkflowStatus.Running
});
if (instance == null)
{
return new WorkflowResponse<dynamic>
{
Status = 1,
Message = "Workflow instance not found",
Data = false
};
}
// ایجاد client روی workflow instance موردنظر
var client = await _workflowRuntime.CreateClientAsync(instance.Id);
// لغو workflow
await client.CancelAsync();
var response = new WorkflowResponse<dynamic>
{
Status = 0,
Message = "Success",
Data = true
};
sw.Stop();
_logger.Log("End of CancelInstanceAsync", WFInstanceId: instance.Id, response, sw.ElapsedMilliseconds);
return response;
}
catch (Exception ex)
{
var response = new WorkflowResponse<dynamic>
{
Status = 100,
Message = ex.Message,
Data = false
};
sw.Stop();
_logger.Log("Exception in CancelInstanceAsync", WFInstanceId: input.WorkflowInstanceId, response, sw.ElapsedMilliseconds, ex);
return response;
}
}
#endregion
}
}