|
@@ -42,31 +42,14 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
_mediator = mediator;
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 根据操作人获取当前未完成节点
|
|
|
- /// </summary>
|
|
|
- /// <returns></returns>
|
|
|
- public async Task<(WorkflowStep, WorkflowStep)> GetStepAsync(string workflowId, string orgCode, string userId, CancellationToken cancellationToken)
|
|
|
- {
|
|
|
- var workflow = await GetWorkflowAsync(workflowId, cancellationToken);
|
|
|
- return GetUnCompleteStep(workflow.StepBoxes, orgCode, userId);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 获取可能前往的下一节点
|
|
|
- /// </summary>
|
|
|
- /// <returns></returns>
|
|
|
- public async Task<IReadOnlyList<NextStepDefine>> GetNextStepsAsync(QueryWorkflowDto dto, CancellationToken cancellationToken)
|
|
|
- {
|
|
|
- var (_, currentStep) = await GetStepAsync(dto.WorkflowId, dto.OrgCode, dto.UserId, cancellationToken);
|
|
|
- //获取人工判定的所有下一节点
|
|
|
- return currentStep.NextSteps.Where(d => string.IsNullOrEmpty(d.Predicate)).ToList();
|
|
|
- }
|
|
|
-
|
|
|
- public async Task<Workflow> CreateWorkflowAsync(Definition definition, CancellationToken cancellationToken)
|
|
|
+ public async Task<Workflow> CreateWorkflowAsync(Definition definition, string title, CancellationToken cancellationToken)
|
|
|
{
|
|
|
var workflow = new Workflow
|
|
|
{
|
|
|
+ Title = title,
|
|
|
+ ModuleId = definition.ModuleId,
|
|
|
+ ModuleName = definition.ModuleName,
|
|
|
+ ModuleCode = definition.ModuleCode,
|
|
|
DefinitionId = definition.Id,
|
|
|
Status = EWorkflowStatus.Runnable,
|
|
|
ExpiredTime = GenerateExpiredTime(definition.Code),
|
|
@@ -89,39 +72,53 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
/// <returns></returns>
|
|
|
public async Task StartAsync(Workflow workflow, BasicWorkflowDto dto, CancellationToken cancellationToken)
|
|
|
{
|
|
|
+ var nextStepBoxDefine = GetStepBoxDefine(workflow.Definition, dto.NextStepCode);
|
|
|
+
|
|
|
if (dto.Handlers.Count > 1)
|
|
|
{
|
|
|
- var startStepDefine = workflow.Definition.Steps.First(d => d.StepType == EStepType.Start);
|
|
|
//检查是否支持会签
|
|
|
- if (startStepDefine.CountersignMode == ECountersignMode.UnSupport)
|
|
|
- throw UserFriendlyException.SameMessage($"当前开始节点不支持开启会签, defineCode: {workflow.Definition.Code}");
|
|
|
+ if (nextStepBoxDefine.CountersignMode == ECountersignMode.UnSupport)
|
|
|
+ throw new UserFriendlyException($"当前节点不支持会签, defineCode: {workflow.Definition.Code}", "当前节点不支持会签");
|
|
|
}
|
|
|
|
|
|
- var nextStepBoxDefine = GetStepBoxDefine(workflow.Definition, dto.NextStepCode);
|
|
|
-
|
|
|
//第二节点的previousId is string.Empty
|
|
|
- await CreateStepAsync(workflow, nextStepBoxDefine, dto, string.Empty, string.Empty, cancellationToken);
|
|
|
+ await CreateStepAsync(workflow, nextStepBoxDefine, dto, cancellationToken: cancellationToken);
|
|
|
+
|
|
|
+ //publish
|
|
|
+ await _mediator.Publish(new StartWorkflowNotify(workflow, nextStepBoxDefine.GFlowAssignType(), dto), cancellationToken);
|
|
|
}
|
|
|
|
|
|
- public async Task<Workflow> GetWorkflowAsync(string workflowId, CancellationToken cancellationToken)
|
|
|
+ public async Task<Workflow> GetWorkflowAsync(string workflowId,
|
|
|
+ bool withDefine = false, bool withSteps = false,
|
|
|
+ bool withTraces = false, bool withSupplements = false,
|
|
|
+ CancellationToken cancellationToken = default)
|
|
|
{
|
|
|
- var workflow = await _workflowRepository.Queryable()
|
|
|
- .Includes(d => d.Definition)
|
|
|
- .FirstAsync(d => d.Id == workflowId);
|
|
|
+ var query = _workflowRepository.Queryable();
|
|
|
+ if (withDefine)
|
|
|
+ query = query.Includes(d => d.Definition);
|
|
|
+ if (withSupplements)
|
|
|
+ query = query.Includes(d => d.Supplements);
|
|
|
+
|
|
|
+ var workflow = await query.FirstAsync(d => d.Id == workflowId);
|
|
|
if (workflow is null)
|
|
|
throw new UserFriendlyException("无效workflowId");
|
|
|
|
|
|
- var steps = await _workflowStepRepository.Queryable()
|
|
|
- .Where(d => d.WorkflowId == workflowId)
|
|
|
- .OrderBy(d => d.CreationTime)
|
|
|
- .ToTreeAsync(d => d.Steps, d => d.ParentId, null);
|
|
|
-
|
|
|
- //var traces = await _workflowTraceRepository.Queryable()
|
|
|
- // .Where(d => d.WorkflowId == workflowId)
|
|
|
- // .ToTreeAsync(d => d.Traces, d => d.ParentId, null);
|
|
|
+ if (withSteps)
|
|
|
+ {
|
|
|
+ var steps = await _workflowStepRepository.Queryable()
|
|
|
+ .Where(d => d.WorkflowId == workflowId)
|
|
|
+ .OrderBy(d => d.CreationTime)
|
|
|
+ .ToTreeAsync(d => d.Steps, d => d.ParentId, null);
|
|
|
+ workflow.StepBoxes = steps;
|
|
|
+ }
|
|
|
|
|
|
- workflow.StepBoxes = steps;
|
|
|
- //workflow.Traces = traces;
|
|
|
+ if (withTraces)
|
|
|
+ {
|
|
|
+ var traces = await _workflowTraceRepository.Queryable()
|
|
|
+ .Where(d => d.WorkflowId == workflowId)
|
|
|
+ .ToTreeAsync(d => d.Traces, d => d.ParentId, null);
|
|
|
+ workflow.Traces = traces;
|
|
|
+ }
|
|
|
|
|
|
return workflow;
|
|
|
}
|
|
@@ -137,6 +134,16 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
var (currentStepBox, currentStep) = GetUnCompleteStepOrDefault(workflow.StepBoxes, _sessionContext.RequiredOrgCode, _sessionContext.RequiredUserId);
|
|
|
if (currentStep is null) return;
|
|
|
if (currentStep.Status is EWorkflowStepStatus.Accepted) return;
|
|
|
+ if (currentStep.HandlerType is EHandlerType.AssignUser or EHandlerType.Role)
|
|
|
+ {
|
|
|
+ //userId
|
|
|
+ if (currentStep.HandlerId != _sessionContext.RequiredUserId) return;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //orgId
|
|
|
+ if (currentStep.HandlerId != _sessionContext.RequiredOrgCode) return;
|
|
|
+ }
|
|
|
if (currentStep.StepType is EStepType.End)
|
|
|
throw new UserFriendlyException("当前流程已流转到最终步骤");
|
|
|
|
|
@@ -146,7 +153,7 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
|
|
|
await AcceptTraceAsync(workflow, currentStepBox, currentStep, cancellationToken);
|
|
|
|
|
|
- //todo publish accept
|
|
|
+ await _mediator.Publish(new AcceptWorkflowNotify(workflow), cancellationToken);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
@@ -156,41 +163,55 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
{
|
|
|
CheckWhetherRunnable(workflow.Status);
|
|
|
|
|
|
- //赋值当前节点
|
|
|
+ #region 办理当前节点
|
|
|
+
|
|
|
var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgCode, _sessionContext.RequiredUserId);
|
|
|
if (currentStep.Status is EWorkflowStepStatus.Assigned)
|
|
|
await AcceptAsync(workflow, cancellationToken);
|
|
|
if (currentStep.StepType is EStepType.End)
|
|
|
throw new UserFriendlyException("当前流程已流转到最终步骤");
|
|
|
|
|
|
- //下一节点是否发起会签
|
|
|
- var isCountersign = dto.Handlers.Count > 1;
|
|
|
- //检查是否支持开启会签
|
|
|
- if (isCountersign && currentStep.CountersignMode == ECountersignMode.UnSupport)
|
|
|
- throw UserFriendlyException.SameMessage($"当前节点不支持开启会签, code: {currentStep.Code}");
|
|
|
+ //是否发起会签
|
|
|
+ var isStartCountersign = dto.Handlers.Count > 1;
|
|
|
+ //检查是否支持会签办理
|
|
|
+ if (isStartCountersign && nextStepBoxDefine.CountersignMode == ECountersignMode.UnSupport)
|
|
|
+ throw UserFriendlyException.SameMessage($"下一节点不支持会签办理, code: {currentStep.Code}");
|
|
|
+ if (isStartCountersign)
|
|
|
+ currentStep.StartCountersign();
|
|
|
|
|
|
_mapper.Map(dto, currentStep);
|
|
|
|
|
|
- currentStep.Complete(
|
|
|
- _sessionContext.RequiredUserId,
|
|
|
- _sessionContext.UserName,
|
|
|
- _sessionContext.RequiredOrgCode,
|
|
|
- _sessionContext.OrgName,
|
|
|
+ //step办理状态
|
|
|
+ currentStep.StepComplete(
|
|
|
+ _sessionContext.RequiredUserId, _sessionContext.UserName,
|
|
|
+ _sessionContext.RequiredOrgCode, _sessionContext.OrgName,
|
|
|
dto.NextStepCode);
|
|
|
|
|
|
- if (currentStepBox.Status is not EWorkflowStepStatus.Completed &&
|
|
|
- currentStepBox.Steps.All(d => d.Status is EWorkflowStepStatus.Completed))
|
|
|
+ //stepBox办理状态
|
|
|
+ currentStepBox.CheckStepBoxStatusAndUpdate();
|
|
|
+
|
|
|
+ var updateSteps = new List<WorkflowStep> { currentStepBox, currentStep };
|
|
|
+ //结束当前会签流程
|
|
|
+ if (currentStep.StepType is EStepType.CountersignEnd && currentStep.IsInCountersign)
|
|
|
{
|
|
|
- currentStepBox.Status = EWorkflowStepStatus.Completed;
|
|
|
- currentStepBox.CompleteTime = currentStepBox.Steps.Max(d => d.CompleteTime);
|
|
|
+ var countersignStartStep = FindCountersignStartStep(workflow, currentStep.CountersignStartCode, currentStep.PrevCountersignId);
|
|
|
+ if (countersignStartStep.HasStartCountersign)
|
|
|
+ {
|
|
|
+ countersignStartStep.CountersignComplete();
|
|
|
+ updateSteps.Add(countersignStartStep);
|
|
|
+ }
|
|
|
}
|
|
|
- await _workflowStepRepository.UpdateRangeAsync(new List<WorkflowStep> { currentStepBox, currentStep }, cancellationToken);
|
|
|
|
|
|
- //检查会签是否结束,并更新当前会签节点
|
|
|
- workflow.CheckCountersignAndUpdate();
|
|
|
+ await _workflowStepRepository.UpdateRangeAsync(updateSteps, cancellationToken);
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region 处理流程
|
|
|
+
|
|
|
+ //检查会签是否结束,并更新当前会签节点字段
|
|
|
+ workflow.CompleteCountersign();
|
|
|
|
|
|
//检查是否流转到流程终点
|
|
|
- //var nextStepBoxDefine = GetStepBoxDefine(workflow.Definition, dto.NextStepCode);
|
|
|
if (nextStepBoxDefine.StepType is EStepType.End)
|
|
|
{
|
|
|
workflow.Complete();
|
|
@@ -200,25 +221,88 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- //判断是否从中心流转出去,重新计算expiredTime
|
|
|
+ //是否从中心流转出去,重新计算expiredTime
|
|
|
if (isOutOfCallCenter)
|
|
|
workflow.ExpiredTime = GenerateExpiredTime(workflow.Definition.Code);
|
|
|
|
|
|
//创建下一节点
|
|
|
- var nextStepBox = await CreateStepAsync(workflow, nextStepBoxDefine, dto, currentStepBox.Id, currentStep.Id, cancellationToken);
|
|
|
+ var nextStepBox = await CreateStepAsync(workflow, nextStepBoxDefine, dto, currentStepBox, currentStep, cancellationToken);
|
|
|
|
|
|
- //不在会签中的流程,更新当前节点名称、时间、会签节点code
|
|
|
- if (!workflow.IsInCountersign())
|
|
|
- SetWorkflowCurrentStepInfo(workflow, isCountersign, nextStepBox);
|
|
|
+ //下一节点为汇总节点时,检查下一节点是否可办理
|
|
|
+ if (nextStepBox.StepType is EStepType.CountersignEnd)
|
|
|
+ {
|
|
|
+ if (currentStep.IsInCountersign)
|
|
|
+ {
|
|
|
+ var stepCode = currentStep.StepType is EStepType.CountersignEnd
|
|
|
+ ? currentStep.CountersignStartCode
|
|
|
+ : currentStep.Code;
|
|
|
+
|
|
|
+ var countersignId = string.IsNullOrEmpty(currentStep.TopCountersignId)
|
|
|
+ ? currentStep.PrevCountersignId
|
|
|
+ : currentStep.TopCountersignId;
|
|
|
+
|
|
|
+ var stepBox = workflow.StepBoxes.First(d => d.Code == stepCode);
|
|
|
+ var countersignSteps =
|
|
|
+ stepBox.Steps.Where(d => d.PrevCountersignId == countersignId);
|
|
|
+
|
|
|
+ //check all complete or cs complete
|
|
|
+ var canHandle = true;
|
|
|
+ foreach (var countersignStep in countersignSteps)
|
|
|
+ {
|
|
|
+ if (countersignStep.Status != EWorkflowStepStatus.Completed) break;
|
|
|
+ if (countersignStep.HasStartCountersign && !countersignStep.IsCountersignComplete.GetValueOrDefault()) break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (canHandle)
|
|
|
+ {
|
|
|
+ await UpdateNextCountersignEndAssignedAsync(nextStepBox, currentStep, cancellationToken);
|
|
|
+ await _mediator.Publish(new CountersignEndAssigned(workflow), cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ await UpdateNextCountersignEndAssignedAsync(nextStepBox, currentStep, cancellationToken);
|
|
|
+ await _mediator.Publish(new CountersignEndAssigned(workflow), cancellationToken);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //更新当前节点名称、时间、会签节点code 等字段
|
|
|
+ workflow.SetWorkflowCurrentStepInfo(isStartCountersign, nextStepBox);
|
|
|
|
|
|
await _workflowRepository.UpdateAsync(workflow, cancellationToken);
|
|
|
|
|
|
- //trace
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region 流转记录
|
|
|
+
|
|
|
await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
|
|
|
|
|
|
- await _mediator.Publish(new NextStepNotify(workflow.Id, dto), cancellationToken);
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ await _mediator.Publish(new NextStepNotify(workflow, nextStepBoxDefine.GFlowAssignType(), dto), cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 更新下级汇总节点可办理状态
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="nextStepBox"></param>
|
|
|
+ /// <param name="currentStep"></param>
|
|
|
+ /// <param name="cancellationToken"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private async Task UpdateNextCountersignEndAssignedAsync(WorkflowStep nextStepBox, WorkflowStep currentStep, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var countersignId = string.IsNullOrEmpty(currentStep.TopCountersignId)
|
|
|
+ ? currentStep.PrevCountersignId
|
|
|
+ : currentStep.TopCountersignId;
|
|
|
+
|
|
|
+ var nextStep = nextStepBox.Steps.First(d => d.PrevCountersignId == countersignId);
|
|
|
+ nextStep.SetAssigned();
|
|
|
+
|
|
|
+ await _workflowStepRepository.UpdateAsync(nextStep, cancellationToken);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// 退回(返回前一节点)
|
|
|
/// </summary>
|
|
@@ -269,19 +353,10 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
var targetStepBox = workflow.StepBoxes.FirstOrDefault(d => d.Code == dto.TargetStepCode);
|
|
|
if (targetStepBox == null)
|
|
|
{
|
|
|
- //向后跳转
|
|
|
- var lastStepBox = workflow.StepBoxes.OrderBy(d => d.CreationTime).Last();
|
|
|
- if (lastStepBox.StepType is EStepType.Start or EStepType.End)
|
|
|
- throw UserFriendlyException.SameMessage("无法跳转至开始或结束节点");
|
|
|
-
|
|
|
var nextStepBoxDefine = GetStepBoxDefine(workflow.Definition, dto.NextStepCode);
|
|
|
- var nextStepBox = await CreateStepAsync(workflow, nextStepBoxDefine, dto, lastStepBox.Id, lastStepBox.Id, cancellationToken);
|
|
|
+ var nextStepBox = await CreateStepAsync(workflow, nextStepBoxDefine, dto, cancellationToken: cancellationToken);
|
|
|
|
|
|
- //更新当前节点名称、时间、会签节点code
|
|
|
- workflow.CloseCountersignStatus();
|
|
|
- var isCountersign = dto.Handlers.Count > 1;
|
|
|
- SetWorkflowCurrentStepInfo(workflow, isCountersign, nextStepBox);
|
|
|
- await _workflowRepository.UpdateAsync(workflow, cancellationToken);
|
|
|
+ await ResetWorkflowCurrentStepInfo(workflow, dto, nextStepBox, cancellationToken);
|
|
|
|
|
|
#region 补充中间节点处理方案
|
|
|
|
|
@@ -317,6 +392,15 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
//todo publish
|
|
|
}
|
|
|
|
|
|
+ private async Task ResetWorkflowCurrentStepInfo(Workflow workflow, RecallDto dto, WorkflowStep stepBox, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ //更新当前节点名称、时间、会签节点code
|
|
|
+ workflow.CloseCountersignStatus();
|
|
|
+ var isCountersign = dto.Handlers.Count > 1;
|
|
|
+ workflow.SetWorkflowCurrentStepInfo(isCountersign, stepBox);
|
|
|
+ await _workflowRepository.UpdateAsync(workflow, cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// 补充
|
|
|
/// </summary>
|
|
@@ -330,6 +414,20 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
await _workflowSupplementRepository.AddAsync(supplement, cancellationToken);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 终止流程
|
|
|
+ /// </summary>
|
|
|
+ public async Task TerminateAsync(string id, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var workflow = await _workflowRepository.GetAsync(id, cancellationToken);
|
|
|
+ if (workflow == null)
|
|
|
+ throw UserFriendlyException.SameMessage("无效的流程编号");
|
|
|
+ workflow.Terminate();
|
|
|
+ await _workflowRepository.UpdateAsync(workflow, cancellationToken);
|
|
|
+
|
|
|
+ //todo publish
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// 根据stepCode查询流程配置中对应的节点
|
|
|
/// </summary>
|
|
@@ -337,32 +435,34 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
{
|
|
|
if (definition == null) throw new ArgumentNullException(nameof(definition));
|
|
|
if (string.IsNullOrEmpty(stepCode)) throw new ArgumentNullException(nameof(stepCode));
|
|
|
- if (!definition.Steps.Any())
|
|
|
- throw new UserFriendlyException($"流程未配置节点,DefineCode: {definition.Code}", "流程配置错误");
|
|
|
- var stepDefine = definition.Steps.FirstOrDefault(d => d.Code == stepCode);
|
|
|
+ var stepDefine = definition.FindStep(stepCode);
|
|
|
if (stepDefine == null)
|
|
|
throw new UserFriendlyException($"未找到流程中对应的节点,DefineCode: {definition.Code}, stepCode: {stepCode}",
|
|
|
"未查询到对应节点");
|
|
|
return stepDefine;
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 查询当前待办节点的下一级节点配置(办理参数)
|
|
|
+ /// </summary>
|
|
|
+ public IReadOnlyList<StepDefine> GetNextStepOptions(Workflow workflow, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var (currentStepBox, _) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgCode, _sessionContext.RequiredUserId);
|
|
|
+ return workflow.Definition.FindSteps(currentStepBox.NextSteps);
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
#region private
|
|
|
|
|
|
/// <summary>
|
|
|
- /// 更新workflow中当前停留节点,时间和会签开始节点code
|
|
|
+ /// 在stepCode对应的stepBox中找到开启会签流程的节点
|
|
|
/// </summary>
|
|
|
- private static void SetWorkflowCurrentStepInfo(Workflow workflow, bool isCountersign, WorkflowStep stepBox)
|
|
|
+ private static WorkflowStep FindCountersignStartStep(Workflow workflow, string startCountersignStepCode, string startCountersignId)
|
|
|
{
|
|
|
- //1.不在会签中,未发起会签(普通处理) 2.不在会签中,发起会签(保存会签节点),3.会签中,不更新
|
|
|
- if (isCountersign)
|
|
|
- {
|
|
|
- workflow.FLow(stepBox.Name, stepBox.CreationTime, stepBox.Code);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- workflow.FLow(stepBox.Name, stepBox.CreationTime);
|
|
|
- }
|
|
|
+ var countersignStartStepBox = workflow.StepBoxes.First(d => d.Code == startCountersignStepCode);
|
|
|
+ var countersignStartStep =
|
|
|
+ countersignStartStepBox.Steps.First(d => d.StartCountersignId == startCountersignId);
|
|
|
+ return countersignStartStep;
|
|
|
}
|
|
|
|
|
|
private async Task JumpTraceAsync(string workflowId, RecallDto dto, CancellationToken cancellationToken)
|
|
@@ -447,8 +547,6 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
return parentTrace;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
private async Task RecallAsync(Workflow workflow, RecallDto dto, WorkflowStep targetStepBox, CancellationToken cancellationToken)
|
|
|
{
|
|
|
//update uncompleted traces
|
|
@@ -456,7 +554,7 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
|
|
|
//remove completedSteps include target self
|
|
|
var completeStepBoxes = workflow.StepBoxes.Where(d =>
|
|
|
- d.Code == dto.TargetStepCode && d.CreationTime > targetStepBox.CreationTime);
|
|
|
+ d.Code == dto.TargetStepCode || d.CreationTime > targetStepBox.CreationTime);
|
|
|
var removeSteps = new List<WorkflowStep>();
|
|
|
foreach (var stepBox in completeStepBoxes)
|
|
|
{
|
|
@@ -468,7 +566,18 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
|
|
|
//recreate targetStep
|
|
|
var nextStepBoxDefine = GetStepBoxDefine(workflow.Definition, dto.NextStepCode);
|
|
|
- await CreateStepAsync(workflow, nextStepBoxDefine, dto, targetStepBox.PreviousId, targetStepBox.Steps.First().PreviousId, cancellationToken);
|
|
|
+ await CreateStepAsync(workflow, nextStepBoxDefine, dto, targetStepBox, targetStepBox.Steps.First(), cancellationToken);
|
|
|
+
|
|
|
+ //flow manage
|
|
|
+ if (workflow.IsInCountersign())
|
|
|
+ {
|
|
|
+ var currentCountersignStepBox =
|
|
|
+ workflow.StepBoxes.First(d => d.Code == workflow.CurrentCountersignStepCode);
|
|
|
+ //目标节点在初始会签节点之前或正好
|
|
|
+ if (targetStepBox.Code == workflow.CurrentCountersignStepCode || targetStepBox.CreationTime < currentCountersignStepBox.CreationTime)
|
|
|
+ await ResetWorkflowCurrentStepInfo(workflow, dto, targetStepBox, cancellationToken);
|
|
|
+
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private static void CheckWhetherRunnable(EWorkflowStatus status)
|
|
@@ -477,28 +586,88 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
throw new UserFriendlyException("当前流程状态不可继续流转");
|
|
|
}
|
|
|
|
|
|
- private async Task<WorkflowStep> CreateStepAsync(Workflow workflow, StepDefine stepBoxDefine, BasicWorkflowDto dto, string previousStepBoxId, string previousStepId, CancellationToken cancellationToken)
|
|
|
+ private async Task<WorkflowStep> CreateStepAsync(Workflow workflow, StepDefine stepBoxDefine, BasicWorkflowDto dto,
|
|
|
+ WorkflowStep? prevStepBox = null, WorkflowStep? prevStep = null, CancellationToken cancellationToken = default)
|
|
|
{
|
|
|
- var nextStepBox = workflow.StepBoxes.FirstOrDefault(d => d.Code == stepBoxDefine.Code);
|
|
|
- nextStepBox ??= CreateStepBox(stepBoxDefine, dto, previousStepBoxId);
|
|
|
- await _workflowStepRepository.AddAsync(nextStepBox, cancellationToken);
|
|
|
+ if (stepBoxDefine.StepType is EStepType.Start or EStepType.End)
|
|
|
+ throw new UserFriendlyException("开始和结束节点无法创建子节点");
|
|
|
+ var stepBox = workflow.StepBoxes.FirstOrDefault(d => d.Code == stepBoxDefine.Code);
|
|
|
+ if (stepBox == null)
|
|
|
+ {
|
|
|
+ stepBox = CreateStepBox(stepBoxDefine, dto, prevStepBox?.Id ?? string.Empty);
|
|
|
+ await _workflowStepRepository.AddAsync(stepBox, cancellationToken);
|
|
|
+ }
|
|
|
|
|
|
- if (stepBoxDefine.HandlerType is EHandlerType.AssignUser or EHandlerType.AssignDep)
|
|
|
+ if (stepBoxDefine.StepType is EStepType.CountersignEnd)
|
|
|
{
|
|
|
- var subSteps = CreateSubSteps(nextStepBox, nextStepBox.HandlerClassifies, dto.NextMainHandler, previousStepId);
|
|
|
- nextStepBox.Steps.AddRange(subSteps);
|
|
|
+ if (prevStep is null)
|
|
|
+ throw new UserFriendlyException($"汇总节点的上级节点不能为空节点,workflowId: {workflow.Id}", "创建汇总节点异常");
|
|
|
+
|
|
|
+ var countersignId = string.IsNullOrEmpty(prevStep.TopCountersignId)
|
|
|
+ ? prevStep.PrevCountersignId
|
|
|
+ : prevStep.TopCountersignId;
|
|
|
+
|
|
|
+ var step = stepBox.Steps.FirstOrDefault(d => d.PrevCountersignId == countersignId);
|
|
|
+ if (step != null) return stepBox;
|
|
|
+
|
|
|
+ var countersignStartStep = FindCountersignStartStep(workflow, stepBoxDefine.CountersignStartCode, countersignId);
|
|
|
+ string? topCountersignId = countersignStartStep.StepType is EStepType.CountersignEnd
|
|
|
+ ? countersignStartStep.TopCountersignId
|
|
|
+ : countersignStartStep.IsInCountersign
|
|
|
+ ? countersignStartStep.PrevCountersignId
|
|
|
+ : null;
|
|
|
+
|
|
|
+ await CreateSubStepsAsync(stepBoxDefine, dto, stepBox, prevStep.Id, EWorkflowStepStatus.Created,
|
|
|
+ countersignId, topCountersignId, cancellationToken);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (prevStep is null)
|
|
|
+ {
|
|
|
+ //创建流程或特殊处理场景
|
|
|
+ await CreateSubStepsAsync(stepBoxDefine, dto, stepBox, string.Empty, EWorkflowStepStatus.Assigned,
|
|
|
+ null, null, cancellationToken);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ var prevCountersignId = prevStep.HasStartCountersign
|
|
|
+ ? prevStep.StartCountersignId
|
|
|
+ : prevStep.PrevCountersignId;
|
|
|
+
|
|
|
+ await CreateSubStepsAsync(stepBoxDefine, dto, stepBox, prevStep.Id, EWorkflowStepStatus.Assigned,
|
|
|
+ prevCountersignId, null, cancellationToken);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return stepBox;
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task CreateSubStepsAsync(
|
|
|
+ StepDefine stepBoxDefine,
|
|
|
+ BasicWorkflowDto dto,
|
|
|
+ WorkflowStep stepBox,
|
|
|
+ string prevStepId,
|
|
|
+ EWorkflowStepStatus stepStatus,
|
|
|
+ string? prevCountersignId = null,
|
|
|
+ string? topCountersignId = null,
|
|
|
+ CancellationToken cancellationToken = default)
|
|
|
+ {
|
|
|
+ if (stepBoxDefine.HandlerType is EHandlerType.AssignUser or EHandlerType.AssignOrg)
|
|
|
+ {
|
|
|
+ var subSteps = CreateSubSteps(stepBox, stepBox.HandlerClassifies, dto.NextMainHandler,
|
|
|
+ prevStepId, prevCountersignId, topCountersignId, stepStatus);
|
|
|
+ stepBox.Steps.AddRange(subSteps);
|
|
|
await _workflowStepRepository.AddRangeAsync(subSteps, cancellationToken);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if (!dto.Handlers.Any())
|
|
|
throw new UserFriendlyException("未指定节点处理者");
|
|
|
- var subSteps = CreateSubSteps(nextStepBox, dto.Handlers, dto.NextMainHandler, previousStepId);
|
|
|
- nextStepBox.Steps.AddRange(subSteps);
|
|
|
+ var subSteps = CreateSubSteps(stepBox, dto.Handlers, dto.NextMainHandler,
|
|
|
+ prevStepId, prevCountersignId, topCountersignId, stepStatus);
|
|
|
+ stepBox.Steps.AddRange(subSteps);
|
|
|
await _workflowStepRepository.AddRangeAsync(subSteps, cancellationToken);
|
|
|
}
|
|
|
-
|
|
|
- return nextStepBox;
|
|
|
}
|
|
|
|
|
|
private (WorkflowStep stepBox, WorkflowStep step) GetStep(List<WorkflowStep> stepBoxes, string stepId)
|
|
@@ -548,51 +717,22 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
return new();
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- private List<WorkflowStep> CreateStepByStepDefine(StepBasic stepBasic, List<string> handlers, string previousId)
|
|
|
- {
|
|
|
- return handlers.Select(d =>
|
|
|
- {
|
|
|
- var step = _mapper.Map<WorkflowStep>(stepBasic);
|
|
|
- step.Id = Guid.NewGuid().ToString();
|
|
|
- step.HandlerId = d;
|
|
|
- if (!string.IsNullOrEmpty(previousId))
|
|
|
- step.PreviousId = previousId;
|
|
|
- return step;
|
|
|
- }).ToList();
|
|
|
- }
|
|
|
-
|
|
|
- private WorkflowStep CreateStepBox(StepBasic stepBasic, List<WorkflowStep>? steps = null)
|
|
|
- {
|
|
|
- var stepBox = _mapper.Map<WorkflowStep>(stepBasic);
|
|
|
- if (steps != null && steps.Any())
|
|
|
- stepBox.Steps.AddRange(steps);
|
|
|
- return stepBox;
|
|
|
- }
|
|
|
-
|
|
|
- private WorkflowStep CreateStepBox(StepDefine stepBasic, BasicWorkflowDto dto, string previousStepBoxId)
|
|
|
+ private WorkflowStep CreateStepBox(StepDefine stepBasic, BasicWorkflowDto dto, string prevStepBoxId)
|
|
|
{
|
|
|
var stepBox = _mapper.Map<WorkflowStep>(stepBasic);
|
|
|
_mapper.Map(dto, stepBox);
|
|
|
- stepBox.PreviousId = previousStepBoxId;
|
|
|
+ stepBox.PreviousId = prevStepBoxId;
|
|
|
return stepBox;
|
|
|
}
|
|
|
|
|
|
- //private WorkflowStep CreateStepBox(
|
|
|
- // StepDefine stepBasic,
|
|
|
- // NextWorkflowDto dto,
|
|
|
- // string workflowId,
|
|
|
- // string previousId = "")
|
|
|
- //{
|
|
|
- // var stepBox = _mapper.Map<WorkflowStep>(stepBasic);
|
|
|
- // _mapper.Map(dto, stepBox);
|
|
|
- // stepBox.WorkflowId = workflowId;
|
|
|
- // if (!string.IsNullOrEmpty(previousId))
|
|
|
- // stepBox.PreviousId = previousId;
|
|
|
- // return stepBox;
|
|
|
- //}
|
|
|
-
|
|
|
- private List<WorkflowStep> CreateSubSteps(WorkflowStep stepBox, List<string> nextHandlers, string nextMainHandler, string previousStepId)
|
|
|
+ private List<WorkflowStep> CreateSubSteps(
|
|
|
+ WorkflowStep stepBox,
|
|
|
+ List<string> nextHandlers,
|
|
|
+ string nextMainHandler,
|
|
|
+ string prevStepId,
|
|
|
+ string? prevCountersignId,
|
|
|
+ string? topCountersignId,
|
|
|
+ EWorkflowStepStatus stepStatus)
|
|
|
{
|
|
|
return nextHandlers.Select(d =>
|
|
|
{
|
|
@@ -600,43 +740,14 @@ namespace Hotline.FlowEngine.Workflows
|
|
|
step.ParentId = stepBox.Id;
|
|
|
step.HandlerId = d;
|
|
|
step.IsMain = d == nextMainHandler;
|
|
|
- step.PreviousId = previousStepId;
|
|
|
+ step.PreviousId = prevStepId;
|
|
|
+ step.PrevCountersignId = prevCountersignId;
|
|
|
+ step.TopCountersignId = topCountersignId;
|
|
|
+ step.Status = stepStatus;
|
|
|
return step;
|
|
|
}).ToList();
|
|
|
}
|
|
|
|
|
|
- private WorkflowStep InitSubStepsInStepBox(WorkflowStep stepBox, List<string> nextHandlers)
|
|
|
- {
|
|
|
- var subSteps = nextHandlers.Select(d =>
|
|
|
- {
|
|
|
- var step = _mapper.Map<WorkflowStep>(stepBox);
|
|
|
- step.ParentId = stepBox.Id;
|
|
|
- step.HandlerId = d;
|
|
|
- return step;
|
|
|
- }).ToList();
|
|
|
-
|
|
|
- stepBox.Steps = subSteps;
|
|
|
- return stepBox;
|
|
|
- }
|
|
|
-
|
|
|
- private WorkflowTrace CreateTrace(string workflowId, WorkflowStep currentStep, NextWorkflowDto dto, EWorkflowTraceStatus status = EWorkflowTraceStatus.Normal)
|
|
|
- {
|
|
|
- var trace = _mapper.Map<WorkflowTrace>(currentStep);
|
|
|
- _mapper.Map(dto, trace);
|
|
|
- trace.WorkflowId = workflowId;
|
|
|
- trace.Status = status;
|
|
|
- return trace;
|
|
|
- }
|
|
|
-
|
|
|
- private WorkflowTrace CreateTrace(string workflowId, WorkflowStep currentStep, PreviousWorkflowDto dto, EWorkflowTraceStatus status = EWorkflowTraceStatus.Normal)
|
|
|
- {
|
|
|
- var trace = _mapper.Map<WorkflowTrace>(currentStep);
|
|
|
- _mapper.Map(dto, trace);
|
|
|
- trace.WorkflowId = workflowId;
|
|
|
- trace.Status = status;
|
|
|
- return trace;
|
|
|
- }
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// 依据配置生成过期时间
|
|
|
/// </summary>
|