|
@@ -0,0 +1,1508 @@
|
|
|
+using Hotline.FlowEngine.Definitions;
|
|
|
+using Hotline.FlowEngine.Notifications;
|
|
|
+using Hotline.FlowEngine.WorkflowModules;
|
|
|
+using Hotline.Settings;
|
|
|
+using Hotline.Share.Dtos;
|
|
|
+using Hotline.Share.Dtos.FlowEngine;
|
|
|
+using Hotline.Share.Enums.FlowEngine;
|
|
|
+using MapsterMapper;
|
|
|
+using MediatR;
|
|
|
+using Microsoft.Extensions.Logging;
|
|
|
+using XF.Domain.Authentications;
|
|
|
+using XF.Domain.Dependency;
|
|
|
+using XF.Domain.Exceptions;
|
|
|
+using XF.Domain.Repository;
|
|
|
+
|
|
|
+namespace Hotline.FlowEngine.Workflows
|
|
|
+{
|
|
|
+ public class WorkflowDomainService1 : IWorkflowDomainService, IScopeDependency
|
|
|
+ {
|
|
|
+ private readonly IWorkflowRepository _workflowRepository;
|
|
|
+ private readonly IRepository<WorkflowStep> _workflowStepRepository;
|
|
|
+ private readonly IRepository<WorkflowTrace> _workflowTraceRepository;
|
|
|
+ private readonly IRepository<WorkflowAssign> _workflowAssignRepository;
|
|
|
+ private readonly IRepository<WorkflowSupplement> _workflowSupplementRepository;
|
|
|
+ private readonly IRepository<WorkflowCountersign> _workflowCountersignRepository;
|
|
|
+ private readonly ISessionContext _sessionContext;
|
|
|
+ private readonly IMapper _mapper;
|
|
|
+ private readonly IMediator _mediator;
|
|
|
+ private readonly ILogger<WorkflowDomainService> _logger;
|
|
|
+
|
|
|
+ public WorkflowDomainService(
|
|
|
+ IWorkflowRepository workflowRepository,
|
|
|
+ IRepository<WorkflowStep> workflowStepRepository,
|
|
|
+ IRepository<WorkflowTrace> workflowTraceRepository,
|
|
|
+ IRepository<WorkflowAssign> workflowAssignRepository,
|
|
|
+ IRepository<WorkflowSupplement> workflowSupplementRepository,
|
|
|
+ IRepository<WorkflowCountersign> workflowCountersignRepository,
|
|
|
+ ISessionContext sessionContext,
|
|
|
+ IMapper mapper,
|
|
|
+ IMediator mediator,
|
|
|
+ ILogger<WorkflowDomainService> logger)
|
|
|
+ {
|
|
|
+ _workflowRepository = workflowRepository;
|
|
|
+ _workflowStepRepository = workflowStepRepository;
|
|
|
+ _workflowTraceRepository = workflowTraceRepository;
|
|
|
+ _workflowAssignRepository = workflowAssignRepository;
|
|
|
+ _workflowSupplementRepository = workflowSupplementRepository;
|
|
|
+ _workflowCountersignRepository = workflowCountersignRepository;
|
|
|
+
|
|
|
+ _sessionContext = sessionContext;
|
|
|
+ _mapper = mapper;
|
|
|
+ _mediator = mediator;
|
|
|
+ _logger = logger;
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<Workflow> CreateWorkflowAsync(WorkflowModule wfModule, string title, string userId, string userCode,
|
|
|
+ string? externalId = null, CancellationToken cancellationToken = default)
|
|
|
+ {
|
|
|
+ var definition = wfModule.Definition;
|
|
|
+ if (definition is null)
|
|
|
+ throw new UserFriendlyException("无效流程模板");
|
|
|
+ var workflow = new Workflow
|
|
|
+ {
|
|
|
+ Title = title,
|
|
|
+ ModuleId = wfModule.Id,
|
|
|
+ ModuleName = wfModule.Name,
|
|
|
+ ModuleCode = wfModule.Code,
|
|
|
+ DefinitionId = definition.Id,
|
|
|
+ Status = EWorkflowStatus.Runnable,
|
|
|
+ TimeLimit = GetTimeLimit(definition.Code),//todo 过期时间
|
|
|
+ ExpiredTime = CalculateExpiredTime(definition.Code),//todo 过期时间
|
|
|
+ StepBoxes = new(),
|
|
|
+ Traces = new(),
|
|
|
+ WorkflowDefinition = definition,
|
|
|
+ CenterToOrgTime = DateTime.Now,
|
|
|
+ ExternalId = externalId ?? string.Empty,
|
|
|
+ FlowedOrgIds = new List<string> { userCode },
|
|
|
+ FlowedUserIds = new List<string> { userId },
|
|
|
+ };
|
|
|
+
|
|
|
+ await _workflowRepository.AddAsync(workflow, cancellationToken);
|
|
|
+
|
|
|
+ return workflow;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 流程开始
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="workflow"></param>
|
|
|
+ /// <param name="dto"></param>
|
|
|
+ /// <param name="cancellationToken"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public async Task StartAsync(Workflow workflow, BasicWorkflowDto dto, StepDefine nextStepBoxDefine,
|
|
|
+ FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ //创建开始节点
|
|
|
+ var (startStepBox, startStep, firstStepBox) = await CreateStartAndFirstStepAsync(workflow, dto, cancellationToken);
|
|
|
+
|
|
|
+ //1.创建开始节点trace 2.创建firstStep(开始节点的下一个节点),办理firstStep 3.创建sec节点
|
|
|
+ //办理firstStep
|
|
|
+ var firstStep = firstStepBox.Steps.First();
|
|
|
+ var counterSignType = GetCounterSignType(firstStep.BusinessType);
|
|
|
+
|
|
|
+ await HandleStepAsync(workflow, dto, firstStepBox, firstStep, counterSignType, cancellationToken);
|
|
|
+
|
|
|
+ await _workflowStepRepository.UpdateRangeAsync(new List<WorkflowStep> { firstStepBox, firstStep }, cancellationToken);
|
|
|
+
|
|
|
+ //firstStep trace
|
|
|
+ await NextTraceAsync(workflow, dto, firstStep, cancellationToken);
|
|
|
+
|
|
|
+ //secondStep
|
|
|
+ var secondStepDefine = workflow.WorkflowDefinition.FindStepDefine(dto.NextStepCode);
|
|
|
+ var secondStepBox = await CreateStepAsync(workflow, secondStepDefine, dto,
|
|
|
+ EWorkflowStepStatus.Created, firstStepBox, firstStep, EWorkflowTraceStatus.Normal,
|
|
|
+ workflow.ExpiredTime, cancellationToken);
|
|
|
+
|
|
|
+ //更新实际办理节点信息
|
|
|
+ workflow.UpdateWorkflowActualHandleInfo(firstStepBox, firstStep,
|
|
|
+ _sessionContext.RequiredUserId, _sessionContext.UserName,
|
|
|
+ _sessionContext.RequiredOrgId, _sessionContext.OrgName,
|
|
|
+ _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName);
|
|
|
+
|
|
|
+ //todo 计算办理工作时长
|
|
|
+
|
|
|
+ //更新当前办理节点信息
|
|
|
+ workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, firstStep, secondStepBox.Steps.First());
|
|
|
+
|
|
|
+ //发起会签时记录顶层会签节点(必须在update currentStep之后)
|
|
|
+ if (dto.IsStartCountersign && !workflow.IsInCountersign())
|
|
|
+ workflow.StartCountersign(firstStepBox.Code, counterSignType);
|
|
|
+
|
|
|
+ //更新受理人信息
|
|
|
+ workflow.UpdateAcceptor(
|
|
|
+ _sessionContext.RequiredUserId,
|
|
|
+ _sessionContext.UserName,
|
|
|
+ _sessionContext.StaffNo,
|
|
|
+ _sessionContext.RequiredOrgId,
|
|
|
+ _sessionContext.OrgName);
|
|
|
+
|
|
|
+ workflow.UpdateHandlers(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId,
|
|
|
+ flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
|
|
|
+
|
|
|
+ //更新指派信息
|
|
|
+ workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlers());
|
|
|
+
|
|
|
+ await _workflowRepository.UpdateAsync(workflow, cancellationToken);
|
|
|
+
|
|
|
+ //publish
|
|
|
+ await _mediator.Publish(new StartWorkflowNotify(workflow, dto, flowAssignInfo), cancellationToken);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<Workflow> GetWorkflowAsync(string workflowId,
|
|
|
+ bool withDefine = false, bool withSteps = false,
|
|
|
+ bool withTraces = false, bool withSupplements = false,
|
|
|
+ bool withAssigns = false, bool withCountersigns = false,
|
|
|
+ CancellationToken cancellationToken = default)
|
|
|
+ {
|
|
|
+ var query = _workflowRepository.Queryable().Where(d => d.Id == workflowId);
|
|
|
+ if (withDefine)
|
|
|
+ query = query.Includes(d => d.WorkflowDefinition);
|
|
|
+ if (withSupplements)
|
|
|
+ query = query.Includes(d => d.Supplements, d => d.Creator);
|
|
|
+ //if (withAssigns)
|
|
|
+ // query = query.Includes(d => d.Assigns);
|
|
|
+ if (withCountersigns)
|
|
|
+ query = query.Includes(d => d.Countersigns);
|
|
|
+
|
|
|
+ var workflow = await query.FirstAsync();
|
|
|
+ if (workflow is null)
|
|
|
+ throw new UserFriendlyException("无效workflowId");
|
|
|
+
|
|
|
+ if (withSteps)
|
|
|
+ {
|
|
|
+ var steps = await _workflowStepRepository.Queryable()
|
|
|
+ .Where(d => d.WorkflowId == workflow.Id)
|
|
|
+ .OrderBy(d => d.CreationTime)
|
|
|
+ .ToTreeAsync(d => d.Steps, d => d.ParentId, null);
|
|
|
+ workflow.StepBoxes = steps;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (withTraces)
|
|
|
+ {
|
|
|
+ var traces = await _workflowTraceRepository.Queryable()
|
|
|
+ .Where(d => d.WorkflowId == workflow.Id)
|
|
|
+ .OrderBy(d => d.CreationTime)
|
|
|
+ .ToTreeAsync(d => d.Traces, d => d.ParentId, null);
|
|
|
+ workflow.Traces = traces;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ return workflow;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 查询工作流包含当前用户办理权限(是否可办理)
|
|
|
+ /// </summary>
|
|
|
+ public async Task<(Workflow, bool)> GetWorkflowHandlePermissionAsync(string workflowId, string userId, string orgCode, bool withDefine = false,
|
|
|
+ bool withSteps = false, bool withTraces = false, bool withSupplements = false, bool withAssigns = false,
|
|
|
+ bool withCountersigns = false, CancellationToken cancellationToken = default)
|
|
|
+ {
|
|
|
+ var workflow = await GetWorkflowAsync(workflowId, withDefine, withSteps, withTraces, withSupplements, withAssigns, withCountersigns, cancellationToken);
|
|
|
+ var canHandle = workflow.CanHandle(userId, orgCode);
|
|
|
+ return (workflow, canHandle);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 受理(接办)
|
|
|
+ /// </summary>
|
|
|
+ public async Task AcceptAsync(Workflow workflow, string userId, string userName, string orgCode, string orgName, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ if (!workflow.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId)) return;
|
|
|
+ //工单完成以后查看的场景
|
|
|
+ if (workflow.Status != EWorkflowStatus.Runnable) return;
|
|
|
+
|
|
|
+ var (currentStepBox, currentStep) = GetUnCompleteStepOrDefault(workflow.StepBoxes, orgCode, userId);
|
|
|
+ if (currentStep is null) return;
|
|
|
+ if (currentStep.Status is EWorkflowStepStatus.Handling) return;
|
|
|
+ if (currentStep.HandlerType is EHandlerType.AssignedUser or EHandlerType.Role)
|
|
|
+ {
|
|
|
+ //userId
|
|
|
+ if (currentStep.AssignedHandlers.All(d => d.Id != userId)) return;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //orgId
|
|
|
+ if (currentStep.AssignedHandlers.All(d => d.Id != orgCode)) return;
|
|
|
+ }
|
|
|
+ if (currentStep.StepType is EStepType.End)
|
|
|
+ throw new UserFriendlyException("当前流程已流转到最终步骤");
|
|
|
+
|
|
|
+ var changedSteps = new List<WorkflowStep> { currentStep };
|
|
|
+ if (currentStepBox.Status is EWorkflowStepStatus.WaitForAccept)
|
|
|
+ {
|
|
|
+ currentStepBox.Status = EWorkflowStepStatus.Handling;
|
|
|
+ changedSteps.Add(currentStepBox);
|
|
|
+ }
|
|
|
+ currentStep.Accept(userId, userName, _sessionContext.RequiredOrgId, _sessionContext.OrgName);
|
|
|
+
|
|
|
+ //接办时非会签并且有多个接办部门时需更新接办部门
|
|
|
+ if (!workflow.IsInCountersign())
|
|
|
+ {
|
|
|
+ var assigns = await _workflowAssignRepository.QueryAsync(d => d.WorkflowId == workflow.Id);
|
|
|
+ if (assigns.Count > 1)
|
|
|
+ {
|
|
|
+ await _workflowAssignRepository.RemoveRangeAsync(assigns, cancellationToken);
|
|
|
+
|
|
|
+ var assign = WorkflowAssign.Create(workflow.Id, orgCode, orgName);
|
|
|
+ await _workflowAssignRepository.AddAsync(assign, cancellationToken);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ await _workflowStepRepository.UpdateRangeAsync(changedSteps, cancellationToken);
|
|
|
+
|
|
|
+ await AcceptTraceAsync(workflow, currentStep, cancellationToken);
|
|
|
+
|
|
|
+ await _mediator.Publish(new AcceptWorkflowNotify(workflow), cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 办理(流转至下一节点)
|
|
|
+ /// </summary>
|
|
|
+ public async Task NextAsync(Workflow workflow, NextWorkflowDto dto, StepDefine nextStepBoxDefine,
|
|
|
+ FlowAssignInfo flowAssignInfo, DateTime expiredTime, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ ValidatePermission(workflow);
|
|
|
+ CheckWhetherRunnable(workflow.Status);
|
|
|
+
|
|
|
+ #region 办理当前节点
|
|
|
+
|
|
|
+ var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
|
|
|
+
|
|
|
+ var counterSignType = GetCounterSignType(currentStep.BusinessType);
|
|
|
+
|
|
|
+ await HandleStepAsync(workflow, dto, currentStepBox, currentStep, counterSignType, cancellationToken);
|
|
|
+
|
|
|
+ //update realhandle info
|
|
|
+ _mapper.Map(dto, workflow);
|
|
|
+
|
|
|
+ var updateSteps = new List<WorkflowStep> { currentStepBox, currentStep };
|
|
|
+ //结束当前会签流程
|
|
|
+ if (currentStep.StepType is EStepType.Summary && currentStep.IsInCountersign)
|
|
|
+ {
|
|
|
+ var currentCountersign = workflow.Countersigns.FirstOrDefault(d => d.Id == currentStep.CountersignId);
|
|
|
+ if (currentCountersign is null)
|
|
|
+ throw new UserFriendlyException(
|
|
|
+ $"未查询到对应会签信息,workflowId:{workflow.Id}, countersignId:{currentStep.CountersignId}", "无效会签编号");
|
|
|
+
|
|
|
+ //1.根据当前节点配置查找结束节点对应开始节点 2.如该开始节点与当前会签开始节点吻合说明可以结束
|
|
|
+
|
|
|
+ //如果!=,说明未发起会签而是继承的外层会签
|
|
|
+ if (currentStepBox.CountersignStartStepCode == currentCountersign.StartStepCode)
|
|
|
+ {
|
|
|
+ //结束step会签信息
|
|
|
+ var countersignStartStepBox =
|
|
|
+ workflow.StepBoxes.FirstOrDefault(d => d.Code == currentCountersign.StartStepCode);
|
|
|
+ if (countersignStartStepBox is null)
|
|
|
+ throw new UserFriendlyException(
|
|
|
+ $"未查询到会签开始stepBox, workflowId: {workflow.Id}, startStepCode: {currentCountersign.StartStepCode}", "未查询到会签开始节点");
|
|
|
+ var countersignStartStep =
|
|
|
+ countersignStartStepBox.Steps.FirstOrDefault(d => d.HasStartedCountersign && d.Id == currentCountersign.StartStepId);
|
|
|
+ if (countersignStartStep is null)
|
|
|
+ throw new UserFriendlyException(
|
|
|
+ $"未查询到会签开始step, workflowId: {workflow.Id}, startStepId: {currentCountersign.StartStepId}", "未查询到会签开始节点");
|
|
|
+ countersignStartStep.CountersignComplete();
|
|
|
+ updateSteps.Add(countersignStartStep);
|
|
|
+
|
|
|
+ //结束会签
|
|
|
+ currentCountersign.Complete(currentStep.Id, currentStep.Code, currentStep.BusinessType);
|
|
|
+ await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ await _workflowStepRepository.UpdateRangeAsync(updateSteps, cancellationToken);
|
|
|
+
|
|
|
+ await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region 处理流程
|
|
|
+
|
|
|
+ //检查会签是否结束,并更新当前会签节点字段
|
|
|
+ var isCountersignOver = false;
|
|
|
+ if (workflow.IsInCountersign() && currentStep.StepType is EStepType.Summary)
|
|
|
+ {
|
|
|
+ isCountersignOver = workflow.CheckIfCountersignOver();
|
|
|
+ if (isCountersignOver)
|
|
|
+ workflow.EndCountersign();
|
|
|
+ }
|
|
|
+
|
|
|
+ //检查是否流转到流程终点
|
|
|
+ if (nextStepBoxDefine.StepType is EStepType.End && !workflow.IsInCountersign())
|
|
|
+ {
|
|
|
+ var endTrace = await WorkflowEnd(workflow, dto, nextStepBoxDefine, currentStepBox, currentStep, cancellationToken);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //是否从中心流转出去,重新计算expiredTime
|
|
|
+ var isCenterToOrg = CheckIfFlowFromCenterToOrg(currentStepBox, nextStepBoxDefine);
|
|
|
+ if (isCenterToOrg)
|
|
|
+ workflow.CenterToOrg(CalculateExpiredTime(workflow.WorkflowDefinition.Code));//todo 过期时间
|
|
|
+
|
|
|
+ //创建下一节点(会签汇总节点不重复创建)
|
|
|
+ var nextStepBox = await CreateStepAsync(workflow, nextStepBoxDefine, dto, EWorkflowStepStatus.Created,
|
|
|
+ currentStepBox, currentStep, EWorkflowTraceStatus.Normal, expiredTime, cancellationToken);
|
|
|
+
|
|
|
+ //下一节点为汇总节点时,检查下一节点是否可办理
|
|
|
+ var nextStepCanHandle = true;
|
|
|
+ if (nextStepBox.StepType is EStepType.Summary && currentStep.IsInCountersign)
|
|
|
+ {
|
|
|
+ //同一会签Id,非汇总节点
|
|
|
+ var steps = await _workflowStepRepository.QueryAsync(d =>
|
|
|
+ d.WorkflowId == workflow.Id
|
|
|
+ && d.CountersignId == currentStep.CountersignId
|
|
|
+ && d.StepType == EStepType.Normal
|
|
|
+ && !string.IsNullOrEmpty(d.ParentId));
|
|
|
+ //(当前办理节点所处同一会签内的所有step全都办理完成并且如果开启了会签的step,必须会签结束)
|
|
|
+ var unComplete = steps.Any(d =>
|
|
|
+ d.Status != EWorkflowStepStatus.Handled ||
|
|
|
+ (d.HasStartedCountersign && !(d.IsStartedCountersignSummary ?? false)));
|
|
|
+ nextStepCanHandle = !unComplete;
|
|
|
+ }
|
|
|
+
|
|
|
+ //更新办理人
|
|
|
+ workflow.UpdateHandlers(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId,
|
|
|
+ flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
|
|
|
+
|
|
|
+ if (nextStepCanHandle)
|
|
|
+ {
|
|
|
+ //将下一节点处理为已指派/可接办
|
|
|
+ await SetNextCountersignEndAssignedAsync(nextStepBox, currentStep, cancellationToken);
|
|
|
+
|
|
|
+ await _mediator.Publish(new CountersignEndAssigned(workflow), cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ //更新实际办理节点名称、时间
|
|
|
+ workflow.UpdateWorkflowActualHandleInfo(currentStepBox, currentStep,
|
|
|
+ _sessionContext.RequiredUserId, _sessionContext.UserName,
|
|
|
+ _sessionContext.RequiredOrgId, _sessionContext.OrgName,
|
|
|
+ _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName);
|
|
|
+
|
|
|
+ //todo 计算办理工作时长
|
|
|
+
|
|
|
+ //更新当前办理节点信息
|
|
|
+ workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, currentStep, nextStepBox.Steps.First());
|
|
|
+
|
|
|
+ //发起会签时记录顶层会签节点
|
|
|
+ if (dto.IsStartCountersign && !workflow.IsInCountersign())
|
|
|
+ workflow.StartCountersign(currentStepBox.Code, counterSignType);
|
|
|
+
|
|
|
+ //更新指派信息
|
|
|
+ workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlers());
|
|
|
+
|
|
|
+ await _workflowRepository.UpdateAsync(workflow, cancellationToken);
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region 流转记录
|
|
|
+
|
|
|
+ var trace = await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region 处理额外参数(短信通知、办理时限、省延期)
|
|
|
+
|
|
|
+ //需统一处理的放在这里,与业务关联的放在业务中去处理,如:省延期
|
|
|
+ //todo
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ await _mediator.Publish(new NextStepNotify(workflow, dto, trace,
|
|
|
+ isCenterToOrg, isCountersignOver,
|
|
|
+ _sessionContext.RequiredOrgId, flowAssignInfo), cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 退回(返回前一节点)
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ public async Task PreviousAsync(Workflow workflow, PreviousWorkflowDto dto, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ ValidatePermission(workflow);
|
|
|
+ CheckWhetherRunnable(workflow.Status);
|
|
|
+ if (workflow.IsInCountersign())
|
|
|
+ throw UserFriendlyException.SameMessage("会签流程不支持退回");
|
|
|
+ var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
|
|
|
+ if (currentStepBox.StepType is EStepType.Start)
|
|
|
+ throw UserFriendlyException.SameMessage("当前流程已退回到开始节点");
|
|
|
+
|
|
|
+ //find prevStep, update handler
|
|
|
+ var prevStepBox = workflow.StepBoxes.FirstOrDefault(d => d.Id == currentStepBox.PreviousStepId);
|
|
|
+ if (prevStepBox == null)
|
|
|
+ throw UserFriendlyException.SameMessage("未查询到上级节点");
|
|
|
+ var prevStep = prevStepBox.Steps.FirstOrDefault(d => d.Id == currentStep.PreviousStepId);
|
|
|
+ if (prevStep == null)
|
|
|
+ throw UserFriendlyException.SameMessage("未查询到前一节点");
|
|
|
+ if (prevStep.StepType is EStepType.Start)
|
|
|
+ throw UserFriendlyException.SameMessage("当前流程已退回到第一节点");
|
|
|
+
|
|
|
+ //update trace
|
|
|
+ await PreviousTraceAsync(workflow.Id, dto, currentStep, cancellationToken);
|
|
|
+
|
|
|
+ //检查并重置上级stepbox状态为待接办
|
|
|
+ await ResetStepBoxStatusAsync(prevStepBox, cancellationToken);
|
|
|
+
|
|
|
+ //复制上一个节点为待接办
|
|
|
+ var newPrevStep = await CreatePrevStepAsync(workflow, prevStep, cancellationToken);
|
|
|
+
|
|
|
+ //remove workflow.steps
|
|
|
+ await _workflowStepRepository.RemoveRangeAsync(new List<WorkflowStep> { currentStepBox, currentStep, prevStep }, cancellationToken);
|
|
|
+
|
|
|
+ //更新当前办理节点信息
|
|
|
+ workflow.UpdateWorkflowCurrentStepInfo(false, nextStep: newPrevStep);
|
|
|
+
|
|
|
+ //更新流程可办理对象
|
|
|
+ workflow.UpdatePreviousHandlers(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, newPrevStep);
|
|
|
+
|
|
|
+ //orgToCenter会触发重新计算期满时间,1.无需审核按当前时间进行计算 2.需审核按审核通过时间计算
|
|
|
+ var isOrgToCenter = CheckIfFlowFromOrgToCenter(currentStepBox, prevStepBox);
|
|
|
+ if (isOrgToCenter)
|
|
|
+ workflow.OrgToCenter(CalculateExpiredTime(""));//todo 过期时间
|
|
|
+
|
|
|
+ await _workflowRepository.UpdateAsync(workflow, cancellationToken);
|
|
|
+
|
|
|
+ await _mediator.Publish(new PreviousNotify(workflow, dto, isOrgToCenter), cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 撤回(返回到之前任意节点)
|
|
|
+ /// </summary>
|
|
|
+ public async Task RecallAsync(Workflow workflow, RecallDto dto, StepDefine targetStepDefine, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ if (targetStepDefine.StepType is EStepType.Start or EStepType.End)
|
|
|
+ throw UserFriendlyException.SameMessage("开始/结束节点不支持撤回");
|
|
|
+
|
|
|
+ var targetStepBox = workflow.StepBoxes.FirstOrDefault(d => d.Code == dto.NextStepCode);
|
|
|
+ if (targetStepBox is null)
|
|
|
+ throw UserFriendlyException.SameMessage("该流程尚未流转至该节点");
|
|
|
+
|
|
|
+ //update uncompleted traces
|
|
|
+ await RecallTraceAsync(workflow.Id, cancellationToken);
|
|
|
+
|
|
|
+ var isOrgToCenter = await RecallAsync(workflow, dto, targetStepDefine, targetStepBox, EWorkflowTraceStatus.Recall, cancellationToken);
|
|
|
+
|
|
|
+ workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
|
|
|
+
|
|
|
+ await _workflowRepository.UpdateAsync(workflow, cancellationToken);
|
|
|
+
|
|
|
+ await _mediator.Publish(new RecallNotify(workflow, dto, isOrgToCenter), cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 跳转(直接将流程跳转至任意节点)
|
|
|
+ /// </summary>
|
|
|
+ public async Task JumpAsync(Workflow workflow, RecallDto dto, StepDefine targetStepDefine,
|
|
|
+ bool isStartCountersign, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ if (targetStepDefine.StepType is EStepType.Start or EStepType.End)
|
|
|
+ throw UserFriendlyException.SameMessage("开始/结束节点不支持跳转");
|
|
|
+
|
|
|
+ //update uncompleted traces
|
|
|
+ await JumpTraceAsync(workflow.Id, dto, cancellationToken);
|
|
|
+
|
|
|
+ bool isOrgToCenter = false, isCenterToOrg = false;
|
|
|
+ var targetStepBox = workflow.StepBoxes.FirstOrDefault(d => d.Code == dto.NextStepCode);
|
|
|
+ if (targetStepBox == null)
|
|
|
+ {
|
|
|
+ //向后跳转
|
|
|
+
|
|
|
+ //此场景并非按配置流转,默认最靠后的节点做为targetStep的prevStep
|
|
|
+ var lastStepBox = workflow.StepBoxes.MaxBy(d => d.CreationTime);
|
|
|
+ if (lastStepBox is null || lastStepBox.StepType is EStepType.End)
|
|
|
+ throw new UserFriendlyException($"流程流转数据异常,未结束流程出现endStep, flowId: {workflow.Id}", "流程流转数据异常");
|
|
|
+
|
|
|
+ targetStepBox = await CreateStepAsync(workflow, targetStepDefine, dto,
|
|
|
+ EWorkflowStepStatus.WaitForAccept, lastStepBox, lastStepBox.Steps.First(),
|
|
|
+ EWorkflowTraceStatus.Jump, workflow.ExpiredTime, cancellationToken);
|
|
|
+
|
|
|
+ workflow.EndCountersign();
|
|
|
+ workflow.ResetOption();
|
|
|
+
|
|
|
+ //更新当前办理节点信息
|
|
|
+ workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, nextStep: targetStepBox.Steps.First());
|
|
|
+
|
|
|
+ //calc workflow expired time
|
|
|
+ isCenterToOrg = CheckIfFlowFromCenterToOrg(workflow, targetStepBox);
|
|
|
+ if (isCenterToOrg)
|
|
|
+ workflow.ExpiredTime = CalculateExpiredTime("");
|
|
|
+
|
|
|
+ #region 补充中间节点处理方案(暂不需要)
|
|
|
+
|
|
|
+ //var completeStepCodes = workflow.StepBoxes.Select(d => d.Code);
|
|
|
+ //var uncompleteStepDefines = workflow.Definition.Steps.Where(d => !completeStepCodes.Contains(d.Code));
|
|
|
+ //创建当前节点与目标节点中间节点
|
|
|
+ //var jumpDto = new BasicWorkflowDto
|
|
|
+ //{
|
|
|
+ // Opinion = "跳转补充"
|
|
|
+ //};
|
|
|
+
|
|
|
+ //foreach (var stepDefine in uncompleteStepDefines)
|
|
|
+ //{
|
|
|
+ // var previousStepId = lastStepBox.Steps.Count > 1 ? lastStepBox.Id : lastStepBox.Steps.First().Id;
|
|
|
+ // if (dto.TargetStepCode == stepDefine.Code)
|
|
|
+ // {
|
|
|
+ // await CreateStepAsync(workflow, stepDefine, dto, lastStepBox.Id, previousStepId, cancellationToken);
|
|
|
+ // break;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // //jump业务下,如果当前节点为会签节点,第一个补充节点的subStep.PreviousId无法确定从哪个子节点跳转过来,统一处理为当前节点的stepBox.Id
|
|
|
+ // lastStepBox = await CreateStepAsync(workflow, stepDefine, dto, lastStepBox.Id, previousStepId, cancellationToken);
|
|
|
+ //}
|
|
|
+
|
|
|
+ #endregion
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //返回之前节点
|
|
|
+ isOrgToCenter = await RecallAsync(workflow, dto, targetStepDefine, targetStepBox, EWorkflowTraceStatus.Jump, cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
|
|
|
+ await _workflowRepository.UpdateAsync(workflow, cancellationToken);
|
|
|
+
|
|
|
+ await _mediator.Publish(new JumpNotify(workflow, dto, flowAssignInfo, isCenterToOrg, isOrgToCenter), cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 重办
|
|
|
+ /// </summary>
|
|
|
+ public async Task RedoAsync(Workflow workflow, RecallDto dto, StepDefine targetStepDefine,
|
|
|
+ FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ if (targetStepDefine.StepType is EStepType.Start or EStepType.End)
|
|
|
+ throw UserFriendlyException.SameMessage("开始/结束节点不支持重办");
|
|
|
+
|
|
|
+ var targetStepBox = workflow.StepBoxes.FirstOrDefault(d => d.Code == dto.NextStepCode);
|
|
|
+ if (targetStepBox is null)
|
|
|
+ throw UserFriendlyException.SameMessage("未找到该节点配置");
|
|
|
+
|
|
|
+ var isOrgToCenter = await RecallAsync(workflow, dto, targetStepDefine, targetStepBox, EWorkflowTraceStatus.Redo, cancellationToken);
|
|
|
+
|
|
|
+ workflow.Redo();
|
|
|
+ workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
|
|
|
+
|
|
|
+ //todo calc expiredTime
|
|
|
+ //dto.Extension.TimeLimitCount
|
|
|
+
|
|
|
+
|
|
|
+ await _workflowRepository.UpdateAsync(workflow, cancellationToken);
|
|
|
+
|
|
|
+ await _mediator.Publish(new RedoNotify(workflow, dto, isOrgToCenter), cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 否决(审批流程不通过)
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ public async Task RejectAsync(Workflow workflow, BasicWorkflowDto dto, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
|
|
|
+
|
|
|
+ var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
|
|
|
+
|
|
|
+ var endTrace = await WorkflowEnd(workflow, dto, endStepDefine, currentStepBox, currentStep, cancellationToken);
|
|
|
+
|
|
|
+ await _mediator.Publish(new RejectNotify(workflow, dto), cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 补充
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ public async Task SupplementAsync(Workflow workflow, EndWorkflowDto dto, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ CheckWhetherRunnable(workflow.Status);
|
|
|
+ //todo 检查当前办理人是否为该流程中的办理人
|
|
|
+
|
|
|
+ var supplement = _mapper.Map<WorkflowSupplement>(dto);
|
|
|
+ await _workflowSupplementRepository.AddAsync(supplement, cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 终止流程
|
|
|
+ /// </summary>
|
|
|
+ public async Task TerminateAsync(TerminateDto dto, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var workflow = await _workflowRepository.GetAsync(dto.WorkflowId, cancellationToken);
|
|
|
+ if (workflow == null)
|
|
|
+ throw UserFriendlyException.SameMessage("无效的流程编号");
|
|
|
+ //workflow.Terminate(dto.Opinion);
|
|
|
+ await _workflowRepository.UpdateAsync(workflow, cancellationToken);
|
|
|
+
|
|
|
+ await _mediator.Publish(new TerminalWorkflowNotify(workflow), cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 根据stepCode查询流程配置中对应的节点
|
|
|
+ /// </summary>
|
|
|
+ public StepDefine GetStepBoxDefine(WorkflowDefinition workflowDefinition, string stepCode)
|
|
|
+ {
|
|
|
+ if (workflowDefinition == null) throw new ArgumentNullException(nameof(workflowDefinition));
|
|
|
+ if (string.IsNullOrEmpty(stepCode)) throw new ArgumentNullException(nameof(stepCode));
|
|
|
+ var stepDefine = workflowDefinition.FindStepDefine(stepCode);
|
|
|
+ if (stepDefine == null)
|
|
|
+ throw new UserFriendlyException($"未找到流程中对应的节点,DefineCode: {workflowDefinition.Code}, stepCode: {stepCode}",
|
|
|
+ "未查询到对应节点");
|
|
|
+ return stepDefine;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 查询当前待办节点的下一级节点配置(办理参数)
|
|
|
+ /// </summary>
|
|
|
+ public IReadOnlyList<StepDefine> GetNextStepDefines(Workflow workflow)
|
|
|
+ {
|
|
|
+ var (currentStepBox, _) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
|
|
|
+ return workflow.WorkflowDefinition.FindStepDefines(currentStepBox.NextSteps.Select(d => d.Code));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 查找当前办理节点
|
|
|
+ /// </summary>
|
|
|
+ public (WorkflowStep StepBox, WorkflowStep Step) FindCurrentStep(Workflow workflow) =>
|
|
|
+ GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 查询待回访部门
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ public async Task<(Kv, IReadOnlyList<Kv>)> GetUnvisitOrgsAsync(string workflowId, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellationToken);
|
|
|
+ if (workflow.CounterSignType is not ECounterSignType.Center)
|
|
|
+ return new(new Kv(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), new List<Kv>());
|
|
|
+ var steps = workflow.StepBoxes
|
|
|
+ .Where(d => d.StepType is EStepType.Normal && d.BusinessType is EBusinessType.Department)
|
|
|
+ .SelectMany(d => d.Steps)
|
|
|
+ .ToList();
|
|
|
+ var items = steps.Select(d => new Kv(d.HandlerOrgId, d.HandlerOrgName)).ToList();
|
|
|
+ return (new Kv(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), items);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 更新一级部门信息
|
|
|
+ /// </summary>
|
|
|
+ public async Task UpdateOrgLevelOneAsync(Workflow workflow, string orgCode, string orgName, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ workflow.UpdateOrgLevelOne(orgCode, orgName);
|
|
|
+ await _workflowRepository.UpdateAsync(workflow, cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 依据配置过滤下一节点
|
|
|
+ /// </summary>
|
|
|
+ public List<StepDefine> NextStepDefineFilter(EPathPolicy pathPolicy, List<StepDefine> nextStepDefines)
|
|
|
+ {
|
|
|
+ switch (pathPolicy)
|
|
|
+ {
|
|
|
+ case EPathPolicy.DirectUpper:
|
|
|
+ break;
|
|
|
+ case EPathPolicy.DirectUpperCenterIsTop:
|
|
|
+ var currentOrgLevel = _sessionContext.RequiredOrgId.CalcOrgLevel();
|
|
|
+ if (currentOrgLevel == 1)
|
|
|
+ {
|
|
|
+ nextStepDefines = nextStepDefines.Where(d => d.IsCenter()).ToList();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ var upperLevel = (--currentOrgLevel).ToString();
|
|
|
+ nextStepDefines = nextStepDefines
|
|
|
+ .Where(d => d.HandlerType is EHandlerType.OrgLevel &&
|
|
|
+ d.HandlerTypeItems.Any(x => x.Key == upperLevel))
|
|
|
+ .ToList();
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new ArgumentOutOfRangeException();
|
|
|
+ }
|
|
|
+
|
|
|
+ return nextStepDefines;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 撤销流程
|
|
|
+ /// </summary>
|
|
|
+ public async Task CancelAsync(CancelDto dto, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var workflow = await GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true, cancellationToken: cancellationToken);
|
|
|
+
|
|
|
+ var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
|
|
|
+
|
|
|
+ var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
|
|
|
+
|
|
|
+ var basicDto = _mapper.Map<BasicWorkflowDto>(dto);
|
|
|
+ var endTrace = await WorkflowEnd(workflow, basicDto, endStepDefine, currentStepBox, currentStep, cancellationToken);
|
|
|
+
|
|
|
+ await _mediator.Publish(new CancelWorkflowNotify(workflow), cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ #region private method
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 流程结束
|
|
|
+ /// </summary>
|
|
|
+ private async Task<WorkflowTrace> WorkflowEnd(Workflow workflow, BasicWorkflowDto dto, StepDefine nextStepBoxDefine,
|
|
|
+ WorkflowStep currentStepBox, WorkflowStep currentStep, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ //create endStep
|
|
|
+ var (_, endStep) = await CreateEndStepAsync(workflow, nextStepBoxDefine, currentStepBox, currentStep, cancellationToken);
|
|
|
+
|
|
|
+ //update endTrace
|
|
|
+ var endTrace = await NextTraceAsync(workflow, dto, endStep, cancellationToken);
|
|
|
+
|
|
|
+ workflow.Complete();
|
|
|
+
|
|
|
+ await _workflowRepository.UpdateAsync(workflow, cancellationToken);
|
|
|
+
|
|
|
+ await _mediator.Publish(new EndWorkflowNotify(workflow, endTrace), cancellationToken);
|
|
|
+
|
|
|
+ return endTrace;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 判断会签类型(中心会签或部门会签)
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="businessType"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
|
+ private ECounterSignType GetCounterSignType(EBusinessType businessType) =>
|
|
|
+ businessType switch
|
|
|
+ {
|
|
|
+ EBusinessType.Center => ECounterSignType.Center,
|
|
|
+ EBusinessType.Send => ECounterSignType.Center,
|
|
|
+ EBusinessType.Department => ECounterSignType.Department,
|
|
|
+ _ => throw new ArgumentOutOfRangeException(nameof(businessType), businessType, null)
|
|
|
+ };
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 办理节点
|
|
|
+ /// </summary>
|
|
|
+ private async Task HandleStepAsync(Workflow workflow, BasicWorkflowDto dto, WorkflowStep currentStepBox, WorkflowStep currentStep,
|
|
|
+ ECounterSignType counterSignType, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ if (currentStep.Status is EWorkflowStepStatus.Handled or EWorkflowStepStatus.Created)
|
|
|
+ throw UserFriendlyException.SameMessage("当前节点状态无法办理");
|
|
|
+
|
|
|
+ if (currentStep.Status is EWorkflowStepStatus.WaitForAccept)
|
|
|
+ await AcceptAsync(workflow,
|
|
|
+ _sessionContext.RequiredUserId,
|
|
|
+ _sessionContext.UserName,
|
|
|
+ _sessionContext.RequiredOrgId,
|
|
|
+ _sessionContext.OrgName,
|
|
|
+ cancellationToken);
|
|
|
+ if (currentStep.StepType is EStepType.End)
|
|
|
+ throw new UserFriendlyException("当前流程已流转到最终步骤");
|
|
|
+
|
|
|
+ //创建会签数据
|
|
|
+ if (dto.IsStartCountersign)
|
|
|
+ await StartCountersignAsync(workflow, dto, currentStepBox, currentStep, counterSignType, cancellationToken);
|
|
|
+
|
|
|
+ _mapper.Map(dto, currentStep);
|
|
|
+
|
|
|
+ //step办理状态
|
|
|
+ currentStep.Handled(
|
|
|
+ _sessionContext.RequiredUserId, _sessionContext.UserName,
|
|
|
+ _sessionContext.RequiredOrgId, _sessionContext.OrgName,
|
|
|
+ _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName,
|
|
|
+ dto.NextStepCode);
|
|
|
+
|
|
|
+ //stepBox办理状态
|
|
|
+ currentStepBox.CheckStepBoxStatusAndUpdate();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 开始会签(创建会签数据,更新currentStep会签数据)
|
|
|
+ /// </summary>
|
|
|
+ private async Task StartCountersignAsync(Workflow workflow, BasicWorkflowDto dto,
|
|
|
+ WorkflowStep currentStepBox, WorkflowStep currentStep, ECounterSignType counterSignType,
|
|
|
+ CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var countersign = await CreateCountersignAsync(workflow.Id, currentStep, currentStepBox.CountersignEndStepCode,
|
|
|
+ dto.NextHandlers.Count, counterSignType, currentStep.CountersignId, cancellationToken);
|
|
|
+ currentStep.StartCountersign(countersign.Id);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检查是否从中心流转至部门
|
|
|
+ /// </summary>
|
|
|
+ private bool CheckIfFlowFromCenterToOrg(WorkflowStep sourceStepBox, StepDefine targetStepBoxDefine)
|
|
|
+ {
|
|
|
+ var isFromCenter = sourceStepBox.IsCenter();
|
|
|
+ if (!isFromCenter) return false;
|
|
|
+
|
|
|
+ var isToOrg = targetStepBoxDefine.IsOrg();
|
|
|
+ return isFromCenter && isToOrg;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检查是否从中心流转至部门
|
|
|
+ /// </summary>
|
|
|
+ private bool CheckIfFlowFromCenterToOrg(Workflow workflow, WorkflowStep targetStepBox)
|
|
|
+ {
|
|
|
+ var isToOrg = targetStepBox.IsOrg();
|
|
|
+ if (!isToOrg) return false;
|
|
|
+
|
|
|
+ var isFromCenter = workflow.StepBoxes.All(d => d.BusinessType is not EBusinessType.Department);
|
|
|
+ return isFromCenter && isToOrg;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检查是否从部门流转至中心
|
|
|
+ /// </summary>
|
|
|
+ private bool CheckIfFlowFromOrgToCenter(WorkflowStep sourceStepBox, StepDefine targetStepBoxDefine)
|
|
|
+ {
|
|
|
+ var isFromOrg = sourceStepBox.IsOrg();
|
|
|
+ if (!isFromOrg) return false;
|
|
|
+
|
|
|
+ var isToCenter = targetStepBoxDefine.IsCenter();
|
|
|
+ return isFromOrg && isToCenter;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检查是否从部门流转至中心
|
|
|
+ /// </summary>
|
|
|
+ private bool CheckIfFlowFromOrgToCenter(WorkflowStep sourceStepBox, WorkflowStep targetStepBox)
|
|
|
+ {
|
|
|
+ var isFromOrg = sourceStepBox.IsOrg();
|
|
|
+ if (!isFromOrg) return false;
|
|
|
+
|
|
|
+ var isToCenter = targetStepBox.IsCenter();
|
|
|
+ return isFromOrg && isToCenter;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检查是否从部门流转至中心
|
|
|
+ /// </summary>
|
|
|
+ private bool CheckIfFlowFromOrgToCenter(Workflow workflow, WorkflowStep targetStepBox)
|
|
|
+ {
|
|
|
+ var isToCenter = targetStepBox.IsCenter();
|
|
|
+ if (!isToCenter) return false;
|
|
|
+
|
|
|
+ var isFromOrg = workflow.StepBoxes.Any(d => d.BusinessType is EBusinessType.Department);
|
|
|
+ return isFromOrg && isToCenter;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 复制一个节点为待接办
|
|
|
+ /// </summary>
|
|
|
+ private async Task<WorkflowStep> CreatePrevStepAsync(Workflow workflow, WorkflowStep step, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ step.Reset();
|
|
|
+ var newStep = _mapper.Map<WorkflowStep>(step);
|
|
|
+ newStep.Status = EWorkflowStepStatus.WaitForAccept;
|
|
|
+ newStep.PreviousStepId = step.PreviousStepId;
|
|
|
+ newStep.IsMain = step.IsMain;
|
|
|
+ newStep.ParentId = step.ParentId;
|
|
|
+ newStep.AssignedHandlers = step.AssignedHandlers;
|
|
|
+ newStep.StartCountersignId = step.StartCountersignId;
|
|
|
+ newStep.CountersignId = step.CountersignId;
|
|
|
+ newStep.IsStartedCountersignSummary = step.IsStartedCountersignSummary;
|
|
|
+ await _workflowStepRepository.AddAsync(newStep, cancellationToken);
|
|
|
+
|
|
|
+ await CreateTraceAsync(workflow, newStep, EWorkflowTraceStatus.Previous, cancellationToken);
|
|
|
+
|
|
|
+ return newStep;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检查并重置目标stepbox状态为待接办
|
|
|
+ /// </summary>
|
|
|
+ private async Task ResetStepBoxStatusAsync(WorkflowStep stepBox, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ if (stepBox.Status is EWorkflowStepStatus.Handled)
|
|
|
+ {
|
|
|
+ stepBox.Status = EWorkflowStepStatus.WaitForAccept;
|
|
|
+ await _workflowStepRepository.UpdateAsync(stepBox, cancellationToken);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task<WorkflowCountersign> CreateCountersignAsync(
|
|
|
+ string workflowId, WorkflowStep startStep, string endStepCode, int count,
|
|
|
+ ECounterSignType counterSignType, string? parentId = null, CancellationToken cancellationToken = default)
|
|
|
+ {
|
|
|
+ var countersign = new WorkflowCountersign
|
|
|
+ {
|
|
|
+ WorkflowId = workflowId,
|
|
|
+ StartStepId = startStep.Id,
|
|
|
+ StartStepCode = startStep.Code,
|
|
|
+ StartStepBusiType = startStep.BusinessType,
|
|
|
+ EndStepCode = endStepCode,
|
|
|
+ Members = count,
|
|
|
+ ParentId = parentId,
|
|
|
+ CounterSignType = counterSignType,
|
|
|
+ };
|
|
|
+ await _workflowCountersignRepository.AddAsync(countersign, cancellationToken);
|
|
|
+ return countersign;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 更新下级汇总节点可办理状态
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="nextStepBox"></param>
|
|
|
+ /// <param name="currentStep"></param>
|
|
|
+ /// <param name="cancellationToken"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private async Task SetNextCountersignEndAssignedAsync(WorkflowStep nextStepBox, WorkflowStep currentStep, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var nextSteps = currentStep.StepCountersignStatus is EStepCountersignStatus.InCountersign
|
|
|
+ ? nextStepBox.Steps.Where(d => d.CountersignId == currentStep.CountersignId).ToList()
|
|
|
+ : nextStepBox.Steps.Where(d => d.PreviousStepId == currentStep.Id).ToList();
|
|
|
+
|
|
|
+ if (!nextSteps.Any())
|
|
|
+ throw new UserFriendlyException($"未查询到下一节点, currentStepId: {currentStep.Id}");
|
|
|
+
|
|
|
+ foreach (var nextStep in nextSteps)
|
|
|
+ {
|
|
|
+ nextStep.SetAssigned();
|
|
|
+ }
|
|
|
+
|
|
|
+ await _workflowStepRepository.UpdateRangeAsync(nextSteps, cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task JumpTraceAsync(string workflowId, RecallDto dto, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ //未办理的traces
|
|
|
+ var uncompleteTraces =
|
|
|
+ await _workflowTraceRepository.QueryAsync(d =>
|
|
|
+ d.WorkflowId == workflowId && string.IsNullOrEmpty(d.HandlerId));
|
|
|
+ foreach (var trace in uncompleteTraces)
|
|
|
+ {
|
|
|
+ trace.Jump(
|
|
|
+ _sessionContext.RequiredUserId,
|
|
|
+ _sessionContext.UserName,
|
|
|
+ _sessionContext.RequiredOrgId,
|
|
|
+ _sessionContext.OrgName,
|
|
|
+ dto.Opinion);
|
|
|
+ }
|
|
|
+
|
|
|
+ await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task RecallTraceAsync(string workflowId, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ //未办理的traces
|
|
|
+ var uncompleteTraces =
|
|
|
+ await _workflowTraceRepository.QueryAsync(d =>
|
|
|
+ d.WorkflowId == workflowId && string.IsNullOrEmpty(d.HandlerId));
|
|
|
+
|
|
|
+ if (uncompleteTraces.Any())
|
|
|
+ {
|
|
|
+ foreach (var trace in uncompleteTraces)
|
|
|
+ {
|
|
|
+ trace.Complete(
|
|
|
+ _sessionContext.RequiredUserId,
|
|
|
+ _sessionContext.UserName,
|
|
|
+ _sessionContext.RequiredOrgId,
|
|
|
+ _sessionContext.OrgName);
|
|
|
+ }
|
|
|
+
|
|
|
+ await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task PreviousTraceAsync(string workflowId, PreviousWorkflowDto dto, WorkflowStep step, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var trace = await GetWorkflowTraceAsync(workflowId, step.Id, cancellationToken);
|
|
|
+ _mapper.Map(dto, trace);
|
|
|
+ trace.Complete(
|
|
|
+ _sessionContext.RequiredUserId,
|
|
|
+ _sessionContext.UserName,
|
|
|
+ _sessionContext.RequiredOrgId,
|
|
|
+ _sessionContext.OrgName);
|
|
|
+ await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ //private async Task EndTraceAsync(Workflow workflow, BasicWorkflowDto dto, WorkflowStep step, CancellationToken cancellationToken)
|
|
|
+ //{
|
|
|
+ // var trace = _mapper.Map<WorkflowTrace>(step);
|
|
|
+ // trace.Status = EWorkflowTraceStatus.Normal;
|
|
|
+ // trace.ExpiredTime = workflow.ExpiredTime;
|
|
|
+ // trace.TimeLimit = workflow.TimeLimit;
|
|
|
+ // await _workflowTraceRepository.AddAsync(trace, cancellationToken);
|
|
|
+ //}
|
|
|
+
|
|
|
+ private async Task<WorkflowTrace> NextTraceAsync(Workflow workflow, BasicWorkflowDto dto, WorkflowStep step, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var trace = await GetWorkflowTraceAsync(workflow.Id, step.Id, cancellationToken);
|
|
|
+ _mapper.Map(dto, trace);
|
|
|
+ _mapper.Map(step, trace);
|
|
|
+ await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
|
|
|
+
|
|
|
+ return trace;
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task AcceptTraceAsync(Workflow workflow, WorkflowStep step, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var trace = await GetWorkflowTraceAsync(workflow.Id, step.Id, cancellationToken);
|
|
|
+ _mapper.Map(step, trace);
|
|
|
+ await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task CreateTraceAsync(Workflow workflow, WorkflowStep currentStep,
|
|
|
+ EWorkflowTraceStatus traceStatus = EWorkflowTraceStatus.Normal, CancellationToken cancellationToken = default)
|
|
|
+ {
|
|
|
+ var trace = _mapper.Map<WorkflowTrace>(currentStep);
|
|
|
+ trace.Status = traceStatus;
|
|
|
+
|
|
|
+ //1.如果是汇总节点,trace.parentId=会签开始节点对应的trace.parentId(即与会签开始节点trace同级)
|
|
|
+ //2.普通节点:2.1: in 判断上级节点是否发起会签,有则赋值parentId为上级trace.Id, 2.2: outer 与上级节点trace保持同级,取值上级节点对应trace.parentId
|
|
|
+
|
|
|
+ if (currentStep.StepType is EStepType.Summary)
|
|
|
+ {
|
|
|
+ if (currentStep.IsInCountersign)
|
|
|
+ {
|
|
|
+ var countersign =
|
|
|
+ await _workflowCountersignRepository.GetAsync(currentStep.CountersignId!, cancellationToken);
|
|
|
+ if (countersign == null)
|
|
|
+ throw new UserFriendlyException(
|
|
|
+ $"汇总节点处于会签中,未查询到对应会签,countersignId: {currentStep.CountersignId}");
|
|
|
+ var startTrace = await GetWorkflowTraceAsync(workflow.Id, countersign.StartStepId, cancellationToken);
|
|
|
+ trace.ParentId = startTrace.ParentId;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (currentStep.StepType is EStepType.Normal)
|
|
|
+ {
|
|
|
+ if (currentStep.StepCountersignStatus is EStepCountersignStatus.InCountersign)
|
|
|
+ {
|
|
|
+ var prevTrace = await GetWorkflowTraceAsync(workflow.Id, currentStep.PreviousStepId, cancellationToken);
|
|
|
+ trace.ParentId = prevTrace.Id;
|
|
|
+ }
|
|
|
+ else if (currentStep.StepCountersignStatus is EStepCountersignStatus.OuterCountersign)
|
|
|
+ {
|
|
|
+ var prevTrace = await GetWorkflowTraceAsync(workflow.Id, currentStep.PreviousStepId, cancellationToken);
|
|
|
+ trace.ParentId = prevTrace.ParentId;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ////处于会签中的节点,其对应的trace.parentId赋值上级trace.Id
|
|
|
+ //if (currentStep.StepCountersignStatus is EStepCountersignStatus.InCountersign)
|
|
|
+ //{
|
|
|
+ // var parentTrace = await GetWorkflowTraceAsync(workflow.Id, currentStep.PreviousId, cancellationToken);
|
|
|
+ // trace.ParentId = parentTrace.Id;
|
|
|
+ //}
|
|
|
+
|
|
|
+ await _workflowTraceRepository.AddAsync(trace, cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task<WorkflowTrace> GetWorkflowTraceAsync(string workflowId, string stepId, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var parentTrace = await _workflowTraceRepository.GetAsync(d =>
|
|
|
+ d.WorkflowId == workflowId && d.StepId == stepId, cancellationToken);
|
|
|
+ if (parentTrace == null)
|
|
|
+ throw new UserFriendlyException($"未找到对应trace, workflowId: {workflowId}, stepId: {stepId}");
|
|
|
+ return parentTrace;
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task<bool> RecallAsync(Workflow workflow, BasicWorkflowDto dto, StepDefine targetStepDefine, WorkflowStep targetStepBox, EWorkflowTraceStatus traceStatus, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ //get targetStep's previous
|
|
|
+ var targetPrevStepBox = workflow.StepBoxes.FirstOrDefault(d => d.Id == targetStepBox.PreviousStepId);
|
|
|
+ if (targetPrevStepBox == null)
|
|
|
+ throw new UserFriendlyException($"{nameof(RecallAsync)}, 未找到目标节点的前一节点, flowId: {workflow.Id}, targetStepBoxPrevId: {targetPrevStepBox?.PreviousStepId}");
|
|
|
+ //真实的前一节点并不存在(非正常流转造成的),所以取前一stepbox任意一个step替代
|
|
|
+ var targetPrevStep = targetPrevStepBox.Steps.FirstOrDefault();
|
|
|
+ if (targetPrevStep == null)
|
|
|
+ throw new UserFriendlyException($"{nameof(RecallAsync)}, 未找到目标节点的前一节点, flowId: {workflow.Id}");
|
|
|
+
|
|
|
+ //查询所有目标节点之后的节点,然后删掉(包括目标节点)
|
|
|
+ var removeSteps = GetStepsBehindTargetStepBox(workflow.StepBoxes, targetStepBox);
|
|
|
+ if (removeSteps.Any())
|
|
|
+ {
|
|
|
+ await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken);
|
|
|
+ workflow.StepBoxes.RemoveAll(d => removeSteps.Contains(d));
|
|
|
+ }
|
|
|
+
|
|
|
+ workflow.EndCountersign();
|
|
|
+ workflow.ResetOption();
|
|
|
+
|
|
|
+ //recreate targetStep
|
|
|
+ var targetStepBoxNew = await CreateStepAsync(workflow, targetStepDefine, dto, EWorkflowStepStatus.WaitForAccept,
|
|
|
+ targetPrevStepBox, targetPrevStep, traceStatus, workflow.ExpiredTime, cancellationToken);
|
|
|
+
|
|
|
+ //更新当前办理节点信息
|
|
|
+ workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, nextStep: targetStepBoxNew.Steps.First());
|
|
|
+
|
|
|
+ //calc workflow expired time
|
|
|
+ var isOrgToCenter = CheckIfFlowFromOrgToCenter(workflow, targetStepBox);
|
|
|
+ if (isOrgToCenter)
|
|
|
+ workflow.ExpiredTime = CalculateExpiredTime("");
|
|
|
+
|
|
|
+ return isOrgToCenter;
|
|
|
+ }
|
|
|
+
|
|
|
+ private ICollection<WorkflowStep> GetStepsBehindTargetStepBox(List<WorkflowStep> stepBoxes, WorkflowStep targetStepBox)
|
|
|
+ {
|
|
|
+ var steps = GetStepsIncludeStepBox(targetStepBox);
|
|
|
+ var nextStepBoxes = stepBoxes.Where(d => d.PreviousStepId == targetStepBox.Id).ToList();
|
|
|
+ if (!nextStepBoxes.Any())
|
|
|
+ return steps;
|
|
|
+ foreach (var nextStepBox in nextStepBoxes)
|
|
|
+ {
|
|
|
+ steps.AddRange(GetStepsBehindTargetStepBox(stepBoxes, nextStepBox));
|
|
|
+ }
|
|
|
+ return steps;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<WorkflowStep> GetStepsIncludeStepBox(WorkflowStep stepBox)
|
|
|
+ {
|
|
|
+ var steps = new List<WorkflowStep> { stepBox };
|
|
|
+ steps.AddRange(stepBox.Steps);
|
|
|
+ return steps;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void CheckWhetherRunnable(EWorkflowStatus status)
|
|
|
+ {
|
|
|
+ if (status != EWorkflowStatus.Runnable)
|
|
|
+ throw UserFriendlyException.SameMessage("当前流程状态不可继续流转");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ValidatePermission(Workflow workflow)
|
|
|
+ {
|
|
|
+ if (!workflow.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId))
|
|
|
+ throw UserFriendlyException.SameMessage("无办理权限");
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task<(WorkflowStep startStepBox, WorkflowStep startStep, WorkflowStep firstStepBox)> CreateStartAndFirstStepAsync(
|
|
|
+ Workflow workflow, BasicWorkflowDto dto, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ if (workflow.StepBoxes.Any())
|
|
|
+ throw UserFriendlyException.SameMessage("无法重复创建开始节点");
|
|
|
+
|
|
|
+ var startStepDefinition = workflow.WorkflowDefinition.Steps.FirstOrDefault(d => d.StepType == EStepType.Start);
|
|
|
+ if (startStepDefinition == null)
|
|
|
+ throw new UserFriendlyException($"模板未配置开始节点, defineCode: {workflow.WorkflowDefinition.Code}", "模板未配置开始节点");
|
|
|
+
|
|
|
+ var startStepBox = CreateStepBox(workflow.Id, startStepDefinition, string.Empty);
|
|
|
+ await _workflowStepRepository.AddAsync(startStepBox, cancellationToken);
|
|
|
+
|
|
|
+
|
|
|
+ //start节点的办理人分类默认为用户,即为当前发起流程的操作员
|
|
|
+ var handler = new Kv { Id = _sessionContext.RequiredUserId, Name = _sessionContext.UserName };
|
|
|
+
|
|
|
+ //开始节点的下一个节点(工单业务:话务员节点)
|
|
|
+ var firstStepCode = workflow.WorkflowDefinition.FindStartStepDefine().NextSteps.First().Code;
|
|
|
+ var startStep = await CreateStartSubStepAsync(handler, firstStepCode, startStepBox, dto, cancellationToken);
|
|
|
+
|
|
|
+ //开始节点trace
|
|
|
+ await CreateTraceAsync(workflow, startStep, cancellationToken: cancellationToken);
|
|
|
+
|
|
|
+ //创建firstStep
|
|
|
+ var firsStepDefine = workflow.WorkflowDefinition.FindStepDefine(firstStepCode);
|
|
|
+ var firstStepBox = await CreateStepAsync(workflow, firsStepDefine, dto, EWorkflowStepStatus.Handling,
|
|
|
+ startStepBox, startStep, EWorkflowTraceStatus.Normal, workflow.ExpiredTime, cancellationToken);
|
|
|
+
|
|
|
+ return (startStepBox, startStep, firstStepBox);
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task<(WorkflowStep stepBox, WorkflowStep step)> CreateEndStepAsync(
|
|
|
+ Workflow workflow,
|
|
|
+ StepDefine endStepDefine,
|
|
|
+ WorkflowStep prevStepBox,
|
|
|
+ WorkflowStep prevStep,
|
|
|
+ CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ if (workflow.StepBoxes.Any(d => d.StepType == EStepType.End))
|
|
|
+ throw UserFriendlyException.SameMessage("无法重复创建结束节点");
|
|
|
+
|
|
|
+ var stepBox = CreateStepBox(workflow.Id, endStepDefine, prevStepBox.Id);
|
|
|
+ await _workflowStepRepository.AddAsync(stepBox, cancellationToken);
|
|
|
+
|
|
|
+ var handler = new Kv { Id = _sessionContext.RequiredUserId, Name = _sessionContext.UserName };
|
|
|
+ var step = await CreateEndSubStepAsync(handler, stepBox, prevStep, cancellationToken);
|
|
|
+
|
|
|
+ //end trace
|
|
|
+ await CreateTraceAsync(workflow, step, cancellationToken: cancellationToken);
|
|
|
+
|
|
|
+ return (stepBox, step);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 创建节点(不含开始、结束节点)
|
|
|
+ /// </summary>
|
|
|
+ private async Task<WorkflowStep> CreateStepAsync(
|
|
|
+ Workflow workflow,
|
|
|
+ StepDefine stepBoxDefine,
|
|
|
+ BasicWorkflowDto dto,
|
|
|
+ EWorkflowStepStatus status,
|
|
|
+ WorkflowStep prevStepBox,
|
|
|
+ WorkflowStep prevStep,
|
|
|
+ EWorkflowTraceStatus traceStatus,
|
|
|
+ DateTime expiredTime,
|
|
|
+ CancellationToken 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(workflow.Id, stepBoxDefine, prevStepBox.Id);
|
|
|
+ await _workflowStepRepository.AddAsync(stepBox, cancellationToken);
|
|
|
+ workflow.StepBoxes.Add(stepBox);
|
|
|
+ }
|
|
|
+ else if (stepBox.Status != EWorkflowStepStatus.Created)
|
|
|
+ {
|
|
|
+ stepBox.Status = EWorkflowStepStatus.Created;
|
|
|
+ await _workflowStepRepository.UpdateAsync(stepBox, cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ //下一节点为汇总节点时,同一会签只需要创建一次汇总节点
|
|
|
+ if (stepBoxDefine.StepType is EStepType.Summary && prevStep.StepCountersignStatus == EStepCountersignStatus.InCountersign)
|
|
|
+ {
|
|
|
+ var step = stepBox.Steps.FirstOrDefault(d =>
|
|
|
+ d.IsInCountersign && d.CountersignId == prevStep.CountersignId);
|
|
|
+ if (step != null)
|
|
|
+ return stepBox;
|
|
|
+ }
|
|
|
+
|
|
|
+ await CreateSubStepsAsync(workflow, stepBoxDefine, dto, stepBox, status, prevStep, traceStatus, expiredTime, cancellationToken);
|
|
|
+
|
|
|
+
|
|
|
+ return stepBox;
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task<WorkflowStep> CreateStartSubStepAsync(
|
|
|
+ Kv handler,
|
|
|
+ string nextStepCode,
|
|
|
+ WorkflowStep stepBox,
|
|
|
+ BasicWorkflowDto dto,
|
|
|
+ CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ //开始节点既不发起会签,也不处于会签中
|
|
|
+ var subStep = CreateSubStep(stepBox, new List<Kv> { handler }, nextStepCode, null,
|
|
|
+ null, null, EWorkflowStepStatus.Handled, EStepCountersignStatus.None, DateTime.Today,
|
|
|
+ _mapper.Map<StepExtension>(dto.Extension));
|
|
|
+ subStep.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName,
|
|
|
+ _sessionContext.RequiredOrgId, _sessionContext.OrgName);
|
|
|
+
|
|
|
+ //step办理状态
|
|
|
+ subStep.Handled(_sessionContext.RequiredUserId, _sessionContext.UserName,
|
|
|
+ _sessionContext.RequiredOrgId, _sessionContext.OrgName,
|
|
|
+ _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName,
|
|
|
+ nextStepCode);
|
|
|
+
|
|
|
+ subStep.Opinion = "流程开启";
|
|
|
+ stepBox.Steps.Add(subStep);
|
|
|
+ await _workflowStepRepository.AddAsync(subStep, cancellationToken);
|
|
|
+ return subStep;
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task<WorkflowStep> CreateEndSubStepAsync(
|
|
|
+ Kv handler,
|
|
|
+ WorkflowStep currentStepBox,
|
|
|
+ WorkflowStep prevStep,
|
|
|
+ CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var subStep = CreateSubStep(currentStepBox, new List<Kv> { handler }, null, null, prevStep.Id,
|
|
|
+ null, EWorkflowStepStatus.Handled, EStepCountersignStatus.None, DateTime.Today, new());
|
|
|
+ subStep.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId,
|
|
|
+ _sessionContext.OrgName);
|
|
|
+ subStep.Handled(_sessionContext.RequiredUserId, _sessionContext.UserName,
|
|
|
+ _sessionContext.RequiredOrgId, _sessionContext.OrgName,
|
|
|
+ _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName,
|
|
|
+ string.Empty);
|
|
|
+
|
|
|
+ currentStepBox.Steps.Add(subStep);
|
|
|
+ await _workflowStepRepository.AddAsync(subStep, cancellationToken);
|
|
|
+ return subStep;
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task CreateSubStepsAsync(
|
|
|
+ Workflow workflow,
|
|
|
+ StepDefine stepBoxDefine,
|
|
|
+ BasicWorkflowDto dto,
|
|
|
+ WorkflowStep stepBox,
|
|
|
+ EWorkflowStepStatus stepStatus,
|
|
|
+ WorkflowStep prevStep,
|
|
|
+ EWorkflowTraceStatus traceStatus,
|
|
|
+ DateTime expiredTime,
|
|
|
+ CancellationToken cancellationToken = default)
|
|
|
+ {
|
|
|
+ var countersignStatus = stepBoxDefine.StepType is EStepType.Summary
|
|
|
+ ? prevStep.IsInCountersign
|
|
|
+ ? EStepCountersignStatus.InCountersign
|
|
|
+ : EStepCountersignStatus.None
|
|
|
+ : prevStep.GetNextStepCountersignStatus();
|
|
|
+
|
|
|
+ var countersignId = dto.IsStartCountersign ? prevStep.StartCountersignId : prevStep.CountersignId;
|
|
|
+
|
|
|
+ List<WorkflowStep> subSteps;
|
|
|
+ var stepExtension = _mapper.Map<StepExtension>(dto.Extension);
|
|
|
+ if (stepBoxDefine.HandlerType is EHandlerType.AssignedUser or EHandlerType.AssignedOrg)
|
|
|
+ {
|
|
|
+ subSteps = CreateSubSteps(dto.IsStartCountersign, stepBox, stepBox.HandlerClassifies, dto.NextStepCode, dto.NextMainHandler,
|
|
|
+ prevStep?.Id, countersignId, stepStatus, countersignStatus, expiredTime, stepExtension);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (stepBoxDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any())
|
|
|
+ throw new UserFriendlyException("未指定节点处理者");
|
|
|
+ subSteps = CreateSubSteps(dto.IsStartCountersign, stepBox, dto.NextHandlers, dto.NextStepCode, dto.NextMainHandler,
|
|
|
+ prevStep?.Id, countersignId, stepStatus, countersignStatus, expiredTime, stepExtension);
|
|
|
+ }
|
|
|
+ stepBox.Steps.AddRange(subSteps);
|
|
|
+ await _workflowStepRepository.AddRangeAsync(subSteps, cancellationToken);
|
|
|
+
|
|
|
+ //create traces
|
|
|
+ foreach (var step in subSteps)
|
|
|
+ {
|
|
|
+ await CreateTraceAsync(workflow, step, traceStatus, cancellationToken);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 查询未完成节点
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="stepBoxes"></param>
|
|
|
+ /// <param name="orgCode"></param>
|
|
|
+ /// <param name="userId"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private (WorkflowStep, WorkflowStep) GetUnCompleteStep(List<WorkflowStep> stepBoxes, string orgCode, string userId)
|
|
|
+ {
|
|
|
+ var (stepBox, step) = GetStep(stepBoxes, orgCode, userId, d => d != EWorkflowStepStatus.Handled);
|
|
|
+ if (step == null)
|
|
|
+ throw new UserFriendlyException(
|
|
|
+ $"未找到对应节点, workflowId: {stepBoxes.FirstOrDefault()?.WorkflowId} orgCode:{orgCode}, userId: {userId}",
|
|
|
+ "未找到对应节点");
|
|
|
+ return (stepBox, step);
|
|
|
+ }
|
|
|
+
|
|
|
+ private (WorkflowStep, WorkflowStep) GetUnCompleteStepOrDefault(List<WorkflowStep> stepBoxes, string orgCode, string userId) =>
|
|
|
+ GetStep(stepBoxes, orgCode, userId, d => d != EWorkflowStepStatus.Handled);
|
|
|
+
|
|
|
+ private (WorkflowStep, WorkflowStep) GetStep(List<WorkflowStep> stepBoxes, string orgCode, string userId, Func<EWorkflowStepStatus, bool> predicate)
|
|
|
+ {
|
|
|
+ if (!stepBoxes.Any()) throw new UserFriendlyException("该流程中暂无节点");
|
|
|
+ foreach (var stepBox in stepBoxes)
|
|
|
+ {
|
|
|
+ foreach (var step in stepBox.Steps)
|
|
|
+ {
|
|
|
+ if (predicate(step.Status) && (step.AssignedHandlers.Any(d => d.Id == orgCode) || step.AssignedHandlers.Any(d => d.Id == userId)))
|
|
|
+ return (stepBox, step);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return new();
|
|
|
+ }
|
|
|
+
|
|
|
+ private WorkflowStep CreateStepBox(string workflowId, StepDefine stepDefine, string prevStepBoxId)
|
|
|
+ {
|
|
|
+ var stepBox = _mapper.Map<WorkflowStep>(stepDefine);
|
|
|
+ stepBox.WorkflowId = workflowId;
|
|
|
+ stepBox.PreviousStepId = prevStepBoxId;
|
|
|
+ stepBox.NextStepCode = string.Empty;
|
|
|
+ stepBox.Opinion = string.Empty;
|
|
|
+ stepBox.CountersignStartStepCode = stepDefine.CountersignStartStepCode;
|
|
|
+ stepBox.CountersignEndStepCode = stepDefine.CountersignEndStepCode;
|
|
|
+ return stepBox;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<WorkflowStep> CreateSubSteps(
|
|
|
+ bool isPrevStartCountersign,
|
|
|
+ WorkflowStep stepBox,
|
|
|
+ List<Kv> handlers,
|
|
|
+ string nextStepCode,
|
|
|
+ string? nextMainHandler,
|
|
|
+ string? prevStepId,
|
|
|
+ string? countersignId,
|
|
|
+ EWorkflowStepStatus stepStatus,
|
|
|
+ EStepCountersignStatus countersignStatus,
|
|
|
+ DateTime expiredTime,
|
|
|
+ StepExtension extension)
|
|
|
+ {
|
|
|
+ if (countersignStatus is EStepCountersignStatus.None && !string.IsNullOrEmpty(countersignId))
|
|
|
+ throw UserFriendlyException.SameMessage("非法参数");
|
|
|
+ if (countersignStatus is not EStepCountersignStatus.None && string.IsNullOrEmpty(countersignId))
|
|
|
+ throw UserFriendlyException.SameMessage("非法参数");
|
|
|
+
|
|
|
+ //依据是否发起会签创建step,发起会签表示一个handler创建一个step,未发起会签表示多人处理同一个节点,只创建一个step
|
|
|
+ var steps = new List<WorkflowStep>();
|
|
|
+ if (isPrevStartCountersign)
|
|
|
+ {
|
|
|
+ foreach (var handler in handlers)
|
|
|
+ {
|
|
|
+ var step = CreateSubStep(stepBox, new List<Kv> { handler }, nextStepCode, nextMainHandler,
|
|
|
+ prevStepId, countersignId, stepStatus, countersignStatus, expiredTime, extension);
|
|
|
+
|
|
|
+ steps.Add(step);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ var step = CreateSubStep(stepBox, handlers, nextStepCode, nextMainHandler,
|
|
|
+ prevStepId, countersignId, stepStatus, countersignStatus, expiredTime, extension);
|
|
|
+
|
|
|
+ steps.Add(step);
|
|
|
+ }
|
|
|
+
|
|
|
+ return steps;
|
|
|
+ }
|
|
|
+
|
|
|
+ private WorkflowStep CreateSubStep(
|
|
|
+ WorkflowStep stepBox,
|
|
|
+ List<Kv> handlers,
|
|
|
+ string nextStepCode,
|
|
|
+ string? nextMainHandler,
|
|
|
+ string? prevStepId,
|
|
|
+ string? countersignId,
|
|
|
+ EWorkflowStepStatus stepStatus,
|
|
|
+ EStepCountersignStatus countersignStatus,
|
|
|
+ DateTime expiredTime,
|
|
|
+ StepExtension extension)
|
|
|
+ {
|
|
|
+ if (!handlers.Any())
|
|
|
+ throw new UserFriendlyException("非法参数");
|
|
|
+ var step = _mapper.Map<WorkflowStep>(stepBox);
|
|
|
+ var handlerIds = handlers.Select(d => d.Id).ToList();
|
|
|
+ var isMain = handlers.Count == 1 || (handlers.Count > 1 || handlerIds.First() == nextMainHandler);
|
|
|
+
|
|
|
+ step.ParentId = stepBox.Id;
|
|
|
+ step.AssignedHandlers = handlers;
|
|
|
+ step.NextStepCode = step.StepType is EStepType.End ? string.Empty : nextStepCode;
|
|
|
+ step.IsMain = isMain;
|
|
|
+ step.PreviousStepId = prevStepId;
|
|
|
+ step.CountersignId = countersignId;
|
|
|
+ step.Status = stepStatus;
|
|
|
+ step.StepCountersignStatus = countersignStatus;
|
|
|
+ step.ExpiredTime = expiredTime;
|
|
|
+ step.TimeLimit = GetTimeLimit("");//todo 过期时间
|
|
|
+ step.Extension = extension;
|
|
|
+
|
|
|
+ return step;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 依据配置生成过期时间
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ private DateTime CalculateExpiredTime(string defineCode, DateTime? time = null)
|
|
|
+ {
|
|
|
+ time ??= DateTime.Now;
|
|
|
+ var config = GetConfig(defineCode);
|
|
|
+ return time.Value.AddDays(config.Days);
|
|
|
+ }
|
|
|
+
|
|
|
+ private string GetTimeLimit(string defineCode)
|
|
|
+ {
|
|
|
+ return GetConfig(defineCode).Description;
|
|
|
+ }
|
|
|
+
|
|
|
+ private ConfigIncludeDescriptionAndTime GetConfig(string defineCode)
|
|
|
+ {
|
|
|
+ return new ConfigIncludeDescriptionAndTime
|
|
|
+ {
|
|
|
+ Days = 7,
|
|
|
+ Description = "7个工作日"//todo 依据配置生成, Think about 工作日
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public class ConfigIncludeDescriptionAndTime
|
|
|
+ {
|
|
|
+ public int Days { get; set; }
|
|
|
+ public string Description { get; set; }
|
|
|
+ }
|
|
|
+}
|