using Hotline.File; 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.Dtos.FlowEngine.Definition; using Hotline.Share.Enums.FlowEngine; using Hotline.Share.Enums.Settings; using MapsterMapper; using MediatR; using Microsoft.Extensions.Logging; using XF.Domain.Authentications; using XF.Domain.Dependency; using XF.Domain.Entities; using XF.Domain.Exceptions; using XF.Domain.Repository; namespace Hotline.FlowEngine.Workflows { public class WorkflowDomainService : IWorkflowDomainService, IScopeDependency { private readonly IWorkflowRepository _workflowRepository; private readonly IRepository _workflowStepRepository; private readonly IRepository _workflowTraceRepository; private readonly IRepository _workflowSupplementRepository; private readonly IRepository _workflowCountersignRepository; private readonly ISessionContext _sessionContext; private readonly IMapper _mapper; private readonly IMediator _mediator; private readonly ILogger _logger; private readonly IFileRepository _fileRepository; public WorkflowDomainService( IWorkflowRepository workflowRepository, IRepository workflowStepRepository, IRepository workflowTraceRepository, IRepository workflowSupplementRepository, IRepository workflowCountersignRepository, ISessionContext sessionContext, IMapper mapper, IMediator mediator, ILogger logger, IFileRepository fileRepository) { _workflowRepository = workflowRepository; _workflowStepRepository = workflowStepRepository; _workflowTraceRepository = workflowTraceRepository; _workflowSupplementRepository = workflowSupplementRepository; _workflowCountersignRepository = workflowCountersignRepository; _sessionContext = sessionContext; _mapper = mapper; _mediator = mediator; _logger = logger; _fileRepository = fileRepository; } public async Task CreateWorkflowAsync(WorkflowModule wfModule, string title, string userId, string userCode, string? externalId = null, string? timelimit = null, int? timelimitCount = null, ETimeType? timeType = null, DateTime? expiredTime = null, CancellationToken cancellationToken = default) { var definition = wfModule.Definition; if (definition is null) throw new UserFriendlyException("无效流程模板"); timelimit ??= "1个自然日"; var workflow = new Workflow { Title = title, ModuleId = wfModule.Id, ModuleName = wfModule.Name, ModuleCode = wfModule.Code, DefinitionId = definition.Id, Status = EWorkflowStatus.Runnable, TimeLimit = timelimit, TimeLimitCount = timelimitCount, TimeLimitUnit = timeType, ExpiredTime = expiredTime.HasValue ? expiredTime.Value : DateTime.Now.AddDays(1), Steps = new(), Traces = new(), WorkflowDefinition = definition, ExternalId = externalId ?? string.Empty, FlowedOrgIds = new List { userCode }, FlowedUserIds = new List { userId }, FlowType = definition.FlowType, }; await _workflowRepository.AddAsync(workflow, cancellationToken); return workflow; } /// /// 流程开始 /// public async Task StartAsync(Workflow workflow, WorkflowStep startStep, BasicWorkflowDto dto, StepDefine firstStepDefine, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken) { //1. 创建first节点 (和trace)2.办理开始节点 //firststeps var firstSteps = await CreateNextStepsAsync(workflow, firstStepDefine, startStep, dto, flowAssignInfo, cancellationToken); if (firstSteps.Any()) workflow.Steps.AddRange(firstSteps); //办理开始节点 var counterSignType = GetCounterSignType(startStep.BusinessType); await HandleStepAsync(startStep, workflow, dto, flowAssignInfo.FlowAssignType, counterSignType, cancellationToken); //赋值当前节点的下级办理节点 if (dto.IsStartCountersign) startStep.CreateCountersignSteps(firstSteps); await _workflowStepRepository.UpdateAsync(startStep, cancellationToken); //handle trace await NextTraceAsync(workflow, dto, startStep, cancellationToken); //todo 计算办理工作时长 //更新当前办理节点信息 workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, _sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId, _sessionContext.OrgName, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName, startStep, firstSteps.First()); //发起会签时记录顶层会签节点(必须在update currentStep之后) if (dto.IsStartCountersign && !workflow.IsInCountersign) workflow.StartCountersign(startStep.Id, counterSignType); //更新实际办理节点信息 workflow.UpdateWorkflowActualHandleInfo(startStep, _sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId, _sessionContext.OrgName, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName, _sessionContext.OrgLevel); //更新受理人信息 workflow.UpdateAcceptor( _sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.StaffNo, _sessionContext.RequiredOrgId, _sessionContext.OrgName); workflow.UpdateHandlers(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects, true); //更新指派信息 workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds()); await _workflowRepository.UpdateAsync(workflow, cancellationToken); //publish await _mediator.Publish(new StartWorkflowNotify(workflow, dto, flowAssignInfo), cancellationToken); } public async Task GetWorkflowAsync(string workflowId, bool withDefine = false, bool withSteps = false, bool withTraces = false, bool withSupplements = 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); //if (withAssigns) // query = query.Includes(d => d.Assigns); if (withCountersigns) query = query.Includes(d => d.Countersigns); if (withSteps) query = query.Includes(d => d.Steps); var workflow = await query.FirstAsync(cancellationToken); 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.Steps = 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; } /// /// 查询工作流包含当前用户办理权限(是否可办理) /// 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 withCountersigns = false, CancellationToken cancellationToken = default) { var workflow = await GetWorkflowAsync(workflowId, withDefine, withSteps, withTraces, withSupplements, withCountersigns, cancellationToken); var canHandle = workflow.CanHandle(userId, orgCode); return (workflow, canHandle); } /// /// 受理(接办) /// public async Task AcceptAsync(Workflow workflow, string userId, string? userName, string orgId, string? orgName, string? orgAreaCode, string? orgAreaName, CancellationToken cancellationToken) { if (!workflow.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId)) return; //工单完成以后查看的场景 if (workflow.Status != EWorkflowStatus.Runnable) return; var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId); if (currentStep.Status is not EWorkflowStepStatus.WaitForAccept) return; if (currentStep.Handlers.All(d => d.Key != orgId && d.Key != userId)) return; if (currentStep.StepType is EStepType.End) throw new UserFriendlyException("当前流程已流转到最终步骤"); //var changedSteps = new List { currentStep }; currentStep.Accept(userId, userName, orgId, orgName, orgAreaCode, orgAreaName); ////接办时非会签并且有多个接办部门时需更新接办部门 //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, orgId, orgName); // await _workflowAssignRepository.AddAsync(assign, cancellationToken); // } //} //await _workflowStepRepository.UpdateRangeAsync(changedSteps, cancellationToken); await _workflowStepRepository.UpdateAsync(currentStep, cancellationToken); await AcceptTraceAsync(workflow, currentStep, cancellationToken); await _mediator.Publish(new AcceptWorkflowNotify(workflow), cancellationToken); } /// /// 办理(流转至下一节点) /// public async Task NextAsync(Workflow workflow, WorkflowStep currentStep, NextWorkflowDto dto, StepDefine nextStepDefine, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken) { ValidatePermission(workflow); CheckWhetherRunnable(workflow.Status); #region 办理当前节点 if (dto.Files.Any()) currentStep.FileJson = await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId, currentStep.Id, cancellationToken); var counterSignType = GetCounterSignType(currentStep.BusinessType); await HandleStepAsync(currentStep, workflow, dto, flowAssignInfo.FlowAssignType, counterSignType, cancellationToken); currentStep.IsActualHandled = CheckIsActualHandle(workflow, currentStep, nextStepDefine, dto); _mapper.Map(dto, workflow); var updateSteps = new List { currentStep }; //结束当前会签流程 if (currentStep.IsCountersignEndStep) { var countersignStartStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId); if (countersignStartStep is null) throw new UserFriendlyException( $"未查询到会签开始step, workflowId: {workflow.Id}, currentStepId: {currentStep.Id}", "未查询到会签开始节点"); if (countersignStartStep.IsStartCountersign) { var currentCountersign = workflow.Countersigns.FirstOrDefault(d => d.Id == countersignStartStep.StartCountersignId); if (currentCountersign is null) throw new UserFriendlyException( $"未查询到对应会签信息,workflowId:{workflow.Id}, countersignId:{currentStep.CountersignId}", "无效会签编号"); //结束step会签信息 countersignStartStep.CountersignEnd(); updateSteps.Add(countersignStartStep); //结束会签 currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType, _sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId, _sessionContext.OrgName, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName); await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken); } } if (currentStep.IsInCountersign()) { //操作为回到会签汇总时,更新开始会签节点的会签办理状态 if (dto.BackToCountersignEnd) { if (currentStep.IsCountersignEndStep) { //汇总节点(非顶级) var csStartStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId); if (csStartStep is null) throw new UserFriendlyException("未查询到会签开始节点"); PrevStepCsHandled(workflow, csStartStep, ref updateSteps); } else { PrevStepCsHandled(workflow, currentStep, ref updateSteps); } } else { //会签中正常办理节点,更新会签members办理状态 var countersign = workflow.Countersigns.FirstOrDefault(d => d.Id == currentStep.CountersignId); if (countersign is null) throw new UserFriendlyException( $"会签数据异常, workflowId: {currentStep.WorkflowId}, countersignId: {currentStep.CountersignId}", "会签数据异常"); countersign.MemberHandled(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId); //update cs await _workflowCountersignRepository.UpdateAsync(countersign, cancellationToken); } } await _workflowStepRepository.UpdateRangeAsync(updateSteps, cancellationToken); await NextTraceAsync(workflow, dto, currentStep, cancellationToken); #endregion #region 处理流程 //检查会签是否结束,并更新当前会签节点字段 var isCountersignOver = false; if (workflow.IsInCountersign && currentStep.IsCountersignEndStep) { isCountersignOver = workflow.CheckIfCountersignOver(); if (isCountersignOver) workflow.EndCountersign(); } //更新实际办理节点信息 workflow.UpdateWorkflowActualHandleInfo(currentStep, _sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId, _sessionContext.OrgName, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName, _sessionContext.OrgLevel); //检查是否流转到流程终点 if (nextStepDefine.StepType is EStepType.End) { var endTrace = await EndAsync(workflow, dto, nextStepDefine, currentStep, EReviewResult.Approval, cancellationToken); return; } ////是否从中心流转出去,重新计算expiredTime //var isCenterToOrg = CheckIfFlowFromCenterToOrg(currentStep, nextStepDefine); //var isOrgToCenter = false; ////if (isCenterToOrg) //// workflow.CenterToOrg(CalculateExpiredTime(workflow.WorkflowDefinition.Code));//todo 过期时间 //创建下一/N个节点(会签汇总节点:会签未全部办理时不创建,最后一个会签办理节点创建会签汇总节点) var nextSteps = await CreateNextStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo, cancellationToken); //赋值当前节点的下级办理节点 if (dto.IsStartCountersign || (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))) { currentStep.CreateCountersignSteps(nextSteps); await _workflowStepRepository.UpdateAsync(currentStep, cancellationToken); } //更新办理人(nextSteps无元素表示当前节点为会签办理节点且当前会签没有全部办理完成) workflow.UpdateHandlers(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects, nextSteps.Any()); //todo 计算办理工作时长 //更新当前办理节点信息 workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, _sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId, _sessionContext.OrgName, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName, currentStep, nextSteps?.FirstOrDefault()); //发起会签时记录顶层会签节点 if (dto.IsStartCountersign && !workflow.IsInCountersign) workflow.StartCountersign(currentStep.Id, counterSignType); //更新指派信息 workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds()); //更新会签实际办理对象信息 if (currentStep.IsActualHandled) workflow.AddCsActualHandler(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId); await _workflowRepository.UpdateAsync(workflow, cancellationToken); #endregion #region 流转记录 var trace = await NextTraceAsync(workflow, dto, currentStep, cancellationToken); #endregion await _mediator.Publish( new NextStepNotify(workflow, dto, trace, nextStepDefine, _sessionContext.RequiredOrgId), cancellationToken); } /// /// 退回(返回前一节点) /// /// public async Task PreviousAsync(Workflow workflow, PreviousWorkflowDto dto, CancellationToken cancellationToken) { ValidatePermission(workflow); CheckWhetherRunnable(workflow.Status); if (workflow.IsInCountersign) throw UserFriendlyException.SameMessage("会签流程不支持退回"); var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId); if (currentStep.StepType is EStepType.Start) throw UserFriendlyException.SameMessage("当前流程已退回到开始节点"); //find prevStep, update handler var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId); if (prevStep == null) throw UserFriendlyException.SameMessage("未查询到前一节点"); //update trace var trace = await PreviousTraceAsync(workflow.Id, dto, currentStep, cancellationToken); //保存附件 if (dto.Files.Any()) trace.FileJson = await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId, trace.Id, cancellationToken); //复制上一个节点为待接办 var newPrevStep = await CreatePrevStepAsync(workflow, prevStep, cancellationToken); //remove workflow.steps await _workflowStepRepository.RemoveRangeAsync(new List { currentStep, prevStep }, cancellationToken); if (workflow.Status is EWorkflowStatus.Completed) workflow.SetStatusRunnable(); //更新当前办理节点信息 workflow.UpdateWorkflowCurrentStepInfo(false, _sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId, _sessionContext.OrgName, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName, nextStep: newPrevStep); //更新流程可办理对象 workflow.UpdatePreviousHandlers(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, newPrevStep); //orgToCenter会触发重新计算期满时间,1.无需审核按当前时间进行计算 2.需审核按审核通过时间计算 var isOrgToCenter = prevStep.BusinessType is EBusinessType.Send && prevStep.IsOrigin; await _workflowRepository.UpdateAsync(workflow, cancellationToken); await _mediator.Publish(new PreviousNotify(workflow, newPrevStep, dto, isOrgToCenter), cancellationToken); } /// /// 撤回(返回到之前任意节点) /// public async Task RecallAsync(Workflow workflow, RecallDto dto, StepDefine targetStepDefine, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken) { var targetStep = workflow.Steps.FirstOrDefault(d => d.Code == dto.NextStepCode && d.IsOrigin); if (targetStep is null) throw UserFriendlyException.SameMessage("该流程尚未流转至该节点"); //update uncompleted traces await RecallTraceAsync(workflow.Id, dto.Opinion, cancellationToken); var isOrgToCenter = await RecallAsync(workflow, dto, flowAssignInfo, targetStepDefine, targetStep, EWorkflowTraceStatus.Recall, cancellationToken); workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects); await _workflowRepository.UpdateAsync(workflow, cancellationToken); await _mediator.Publish(new RecallNotify(workflow, targetStep, dto, isOrgToCenter), cancellationToken); } /// /// 跳转(直接将流程跳转至任意节点) /// public async Task JumpAsync(Workflow workflow, RecallDto dto, StepDefine targetStepDefine, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken) { //todo 跳转至结束节点,(自动办理) //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 targetStep = workflow.Steps.FirstOrDefault(d => d.Code == dto.NextStepCode && d.IsOrigin); if (targetStep == null) { //向后跳转 //此场景并非按配置流转,默认最靠后的节点做为targetStep的prevStep var lastStep = workflow.Steps.Where(d => d.IsOrigin).MaxBy(d => d.CreationTime); if (lastStep is null || lastStep.StepType is EStepType.End) throw new UserFriendlyException($"流程流转数据异常,未结束流程出现endStep, flowId: {workflow.Id}", "流程流转数据异常"); var targetSteps = await CreateConfigStepsAsync(workflow, targetStepDefine, lastStep, dto, flowAssignInfo, EWorkflowTraceStatus.Jump, cancellationToken); targetStep = targetSteps.First(); workflow.EndCountersign(); workflow.ResetOption(); //更新当前办理节点信息 workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, _sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId, _sessionContext.OrgName, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName, nextStep: targetStep); //calc workflow expired time isCenterToOrg = CheckIfFlowFromCenterToOrg(workflow, targetStep); //if (isCenterToOrg) // workflow.ExpiredTime = CalculateExpiredTime("");//todo calc expiredTime #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, flowAssignInfo, targetStepDefine, targetStep, EWorkflowTraceStatus.Jump, cancellationToken); } workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects); await _workflowRepository.UpdateAsync(workflow, cancellationToken); await _mediator.Publish( new JumpNotify(workflow, targetStep, dto, flowAssignInfo, isCenterToOrg, isOrgToCenter), cancellationToken); } /// /// 重办 /// 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.Steps.FirstOrDefault(d => d.Code == dto.NextStepCode); if (targetStepBox is null) throw UserFriendlyException.SameMessage("未找到该节点配置"); var isOrgToCenter = await RecallAsync(workflow, dto, flowAssignInfo, 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); } /// /// 否决(审批流程不通过) /// /// public async Task RejectAsync(Workflow workflow, BasicWorkflowDto dto, CancellationToken cancellationToken) { var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId); await HandleStepAsync(currentStep, workflow, dto, null, null, cancellationToken); var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine(); var endTrace = await EndAsync(workflow, dto, endStepDefine, currentStep, EReviewResult.Failed, cancellationToken); //await _mediator.Publish(new RejectNotify(workflow, dto), cancellationToken); } /// /// 补充 /// /// public async Task SupplementAsync(Workflow workflow, EndWorkflowDto dto, CancellationToken cancellationToken) { CheckWhetherRunnable(workflow.Status); //todo 检查当前办理人是否为该流程中的办理人 var supplement = _mapper.Map(dto); await _workflowSupplementRepository.AddAsync(supplement, cancellationToken); } /// /// 终止流程 /// 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); } /// /// 根据stepCode查询流程配置中对应的节点 /// public StepDefine GetStepDefine(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; } /// /// 查询当前待办理节点 /// public WorkflowStep FindCurrentStepWaitForHandle(Workflow workflow) => GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId); /// /// 查询待回访部门 /// /// public async Task<(Kv, IReadOnlyList)> 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()); var steps = workflow.Steps .Where(d => d.StepType is EStepType.Normal) .ToList(); var items = steps.Select(d => new Kv(d.HandlerOrgId, d.HandlerOrgName)) .DistinctBy(d => d.Key).ToList(); return (new Kv(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), items); } /// /// 依据配置过滤下一节点 /// public List NextStepDefineFilter(EPathPolicy pathPolicy, List 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; } /// /// 撤销流程 /// public async Task CancelAsync(CancelDto dto, CancellationToken cancellationToken) { var workflow = await GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true, cancellationToken: cancellationToken); var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId); //var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId); var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine(); var basicDto = _mapper.Map(dto); var endTrace = await EndAsync(workflow, basicDto, endStepDefine, currentStep, EReviewResult.Unknown, cancellationToken); await _mediator.Publish(new CancelWorkflowNotify(workflow), cancellationToken); } /// /// 更新期满时间 /// public async Task UpdateExpiredTimeAsync(Workflow workflow, DateTime expiredTime, string timelimit, int? timelimiteCount, ETimeType? timelimitUnit, CancellationToken cancellationToken) { workflow.ExpiredTime = expiredTime; workflow.TimeLimit = timelimit; workflow.TimeLimitUnit = timelimitUnit; workflow.TimeLimitCount = timelimiteCount; await _workflowRepository.UpdateAsync(workflow, cancellationToken); } /// /// 新增流程流转记录 /// public async Task AddTracesAsync(string workflowId, List traces, CancellationToken cancellationToken) { var workflow = await GetWorkflowAsync(workflowId, cancellationToken: cancellationToken); if (workflow is null) throw new UserFriendlyException("找不到该流程"); await _workflowTraceRepository.AddRangeAsync(traces, cancellationToken); } /// /// 创建开始节点 /// public WorkflowStep CreateStartStep(Workflow workflow, StepDefine startStepDefine, BasicWorkflowDto dto, List handles) { //startstep var nextSteps = _mapper.Map>(startStepDefine.NextSteps); if (startStepDefine.InstanceMode is EInstanceMode.Config) { var selectedStep = nextSteps.FirstOrDefault(d => d.Code == dto.NextStepCode); if (selectedStep is not null) selectedStep.Selected = true; } var startStep = _mapper.Map(startStepDefine); startStep.WorkflowId = workflow.Id; startStep.Handlers = handles; startStep.NextSteps = nextSteps; startStep.IsMain = true; startStep.IsOrigin = true; startStep.Status = EWorkflowStepStatus.WaitForHandle; startStep.PrevChosenStepCode = null; startStep.StepExpiredTime = workflow.ExpiredTime; startStep.InitId(); return startStep; } #region private method public async Task CreateStartStepAsync(Workflow workflow, StepDefine startStepDefine, BasicWorkflowDto dto, List handles, EWorkflowTraceStatus traceStatus, CancellationToken cancellationToken) { var startStep = CreateStartStep(workflow, startStepDefine, dto, handles); await _workflowStepRepository.AddAsync(startStep, cancellationToken); await CreateTraceAsync(workflow, startStep, traceStatus, cancellationToken); return startStep; } //更新目标节点前一节点的会签办理完成状态 private void PrevStepCsHandled(Workflow workflow, WorkflowStep targetStep, ref List updateSteps) { var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == targetStep.PrevStepId); if (prevStep is null) throw new UserFriendlyException("未查询到目标节点的前一节点"); var csStep = prevStep.CountersignSteps.FirstOrDefault(d => d.StepId == targetStep.Id); if (csStep is null) throw new UserFriendlyException("未查询到当前待办节点"); csStep.Completed = true; updateSteps.Add(prevStep); } /// /// 创建下1/N个节点 /// private async Task> CreateNextStepsAsync(Workflow workflow, StepDefine nextStepDefine, WorkflowStep currentStep, BasicWorkflowDto dto, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken) { List nextSteps = new(); if (currentStep.IsInCountersign()) { if (currentStep.IsCountersignEndStep) { //todo check if current is topend f: csStartStep.prev //todo t: check if dto.StartCs t: csconfig f: config if (currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)) { if (dto.IsStartCountersign) { //todo 依据会签策略创建会签下一级节点 nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo.FlowAssignType, cancellationToken); } else { //todo 创建普通节点(根据配置) nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo, EWorkflowTraceStatus.Normal, cancellationToken); } } else { //todo csStartStep.prev var csStartStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId); if (csStartStep is null) throw new UserFriendlyException("未查询到会签节点"); nextSteps = await CreateCsEndStepsByPrevStepAsync(workflow, csStartStep, dto, cancellationToken); } } else { if (dto.BackToCountersignEnd) { //todo check if cs all complete, create next nextSteps = await CreateCsEndStepsByPrevStepAsync(workflow, currentStep, dto, cancellationToken); } else { //todo 依据会签策略创建会签下一级节点 nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo.FlowAssignType, cancellationToken); } } } else if (dto.IsStartCountersign) //top { //todo 依据会签策略创建会签下一级节点 nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo.FlowAssignType, cancellationToken); } else if (currentStep.InstanceMode is EInstanceMode.Dynamic && !currentStep.DynamicShouldTerminal()) { //todo 创建动态下一级节点 nextSteps = await CreateStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo.FlowAssignType, dto.NextHandlers, null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, false, EWorkflowTraceStatus.Normal, cancellationToken); } else { //todo 创建普通节点(根据配置) nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo, EWorkflowTraceStatus.Normal, cancellationToken); } return nextSteps; } private Task> CreateCountersignStepsAsync( Workflow workflow, StepDefine stepDefine, WorkflowStep prevStep, BasicWorkflowDto dto, EFlowAssignType flowAssignType, //DateTime expiredTime, CancellationToken cancellationToken ) { var countersignId = prevStep.IsStartCountersign ? prevStep.StartCountersignId : prevStep.CountersignId; return CreateStepsAsync(workflow, stepDefine, prevStep, dto, flowAssignType, dto.NextHandlers, countersignId, EWorkflowStepStatus.WaitForAccept, prevStep.GetNextStepCountersignPosition(), false, EWorkflowTraceStatus.Normal, cancellationToken); } /// /// 根据传入节点的上一节点创建会签汇总节点(汇总传入节点的前一节点) /// private async Task> CreateCsEndStepsByPrevStepAsync(Workflow workflow, WorkflowStep step, BasicWorkflowDto dto, CancellationToken cancellationToken) { var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == step.PrevStepId); if (prevStep is null) throw new UserFriendlyException("未查询到当前节点上级节点"); var nextSteps = new List(); //会签未全部办理则不创建汇总节点 if (prevStep.StartedCountersignHasAllHandled()) { //todo 创建会签汇总节点 var countersignEndStep = await CreateCountersignEndStepAsync(prevStep, dto, workflow.ExpiredTime, cancellationToken); nextSteps = new List { countersignEndStep }; //create trace await CreateTraceAsync(workflow, countersignEndStep, EWorkflowTraceStatus.Normal, cancellationToken); await _mediator.Publish(new CountersignEndAssigned(workflow), cancellationToken); } return nextSteps; } private async Task CreateCountersignEndStepAsync(WorkflowStep countersignStartStep, BasicWorkflowDto dto, DateTime expiredTime, CancellationToken cancellationToken) { var csEndStep = _mapper.Map(countersignStartStep); csEndStep.Status = EWorkflowStepStatus.WaitForAccept; csEndStep.PrevStepId = null; csEndStep.PrevStepCode = null; csEndStep.IsOrigin = false; csEndStep.CountersignId = countersignStartStep.StartCountersignId; csEndStep.CountersignPosition = ECountersignPosition.Outer; csEndStep.CountersignSteps = new(); csEndStep.IsCountersignEndStep = true; csEndStep.CountersignStartStepId = countersignStartStep.Id; csEndStep.Name = dto.NextStepName; //csEndStep.TimeLimit = GetTimeLimit(""); csEndStep.StepExpiredTime = expiredTime; csEndStep.Handlers = countersignStartStep.Handlers .Where(d => d.Key == countersignStartStep.HandlerId || d.Key == countersignStartStep.HandlerOrgId) .ToList(); csEndStep.Reset(); csEndStep.ResetParameters(); await _workflowStepRepository.AddAsync(csEndStep, cancellationToken); return csEndStep; } /// /// 流程结束 /// public async Task EndAsync(Workflow workflow, BasicWorkflowDto dto, StepDefine endStepDefine, WorkflowStep currentStep, EReviewResult? reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default) { //create endStep var endStep = await CreateEndStepAsync(workflow, endStepDefine, currentStep, cancellationToken); //update endTrace var endTrace = await NextTraceAsync(workflow, dto, endStep, cancellationToken); workflow.Complete(reviewResult); await _workflowRepository.UpdateAsync(workflow, cancellationToken); await _mediator.Publish(new EndWorkflowNotify(workflow, endTrace), cancellationToken); return endTrace; } /// /// 判断会签类型(中心会签或部门会签) /// /// /// /// 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) }; /// /// 办理节点 /// private async Task HandleStepAsync(WorkflowStep step, Workflow workflow, BasicWorkflowDto dto, EFlowAssignType? flowAssignType, ECounterSignType? counterSignType, CancellationToken cancellationToken) { if (step.Status is EWorkflowStepStatus.Handled) throw UserFriendlyException.SameMessage("当前节点状态已办理"); if (step.StepType is EStepType.End) throw new UserFriendlyException("当前流程已流转到最终步骤"); if (dto.IsStartCountersign && !counterSignType.HasValue) throw new UserFriendlyException("缺少会签类型参数"); //创建会签数据 if (dto.IsStartCountersign) await StartCountersignAsync(workflow, step, dto, flowAssignType, counterSignType, cancellationToken); //办理参数 _mapper.Map(dto, step); //step办理状态 HandleStep(step, dto.NextStepCode); } private bool CheckIsActualHandle(Workflow workflow, WorkflowStep step, StepDefine nextStepDefine, BasicWorkflowDto dto) { //1. workflow是否为办理类型 2. 非会签:当前是否为普通节点and下一节点是否为汇总 or endStep 3. 会签:当前操作为汇总还是继续往下办理?thk: 汇总以后但未回到top又往下办理的场景,前面实际办理部门也算作办理部门 if (workflow.FlowType is not EFlowType.Handle) return false; if (workflow.IsInCountersign) { return dto.BackToCountersignEnd; } else { return step.StepType is EStepType.Normal && nextStepDefine.StepType is EStepType.Summary or EStepType.End; } } /// /// 办理节点(赋值节点的办理对象信息) /// private void HandleStep(WorkflowStep step, string nextStepCode) { step.Handle(_sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId, _sessionContext.OrgName, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName, _sessionContext.OrgIsCenter, nextStepCode); } /// /// 开始会签(创建会签数据,更新currentStep会签数据) /// private async Task StartCountersignAsync(Workflow workflow, WorkflowStep startStep, BasicWorkflowDto dto, EFlowAssignType? flowAssignType, ECounterSignType? counterSignType, CancellationToken cancellationToken) { var countersign = await CreateCountersignAsync( workflow.Id, startStep, dto.NextHandlers, flowAssignType, counterSignType, startStep.StepExpiredTime.Value, startStep.CountersignId, cancellationToken); startStep.StartCountersign(countersign.Id); } /// /// 检查是否从中心流转至部门 /// private bool CheckIfFlowFromCenterToOrg(WorkflowStep sourceStep, StepDefine targetStepBoxDefine) { var isFromCenter = sourceStep.IsCenter(); if (!isFromCenter) return false; var isToOrg = targetStepBoxDefine.IsOrg(); return isFromCenter && isToOrg; } /// /// 检查是否从中心流转至部门 /// private bool CheckIfFlowFromCenterToOrg(Workflow workflow, WorkflowStep targetStepBox) { var isToOrg = targetStepBox.IsOrg(); if (!isToOrg) return false; var isFromCenter = workflow.Steps.All(d => d.BusinessType is not EBusinessType.Department); return isFromCenter && isToOrg; } /// /// 检查是否从部门流转至中心 /// private bool CheckIfFlowFromOrgToCenter(WorkflowStep sourceStepBox, StepDefine targetStepBoxDefine) { var isFromOrg = sourceStepBox.IsOrg(); if (!isFromOrg) return false; var isToCenter = targetStepBoxDefine.IsCenter(); return isFromOrg && isToCenter; } /// /// 检查是否从部门流转至中心 /// private bool CheckIfFlowFromOrgToCenter(WorkflowStep sourceStepBox, WorkflowStep targetStepBox) { var isFromOrg = sourceStepBox.IsOrg(); if (!isFromOrg) return false; var isToCenter = targetStepBox.IsCenter(); return isFromOrg && isToCenter; } /// /// 检查是否从部门流转至中心 /// private bool CheckIfFlowFromOrgToCenter(Workflow workflow, WorkflowStep targetStepBox) { var isToCenter = targetStepBox.IsCenter(); if (!isToCenter) return false; var isFromOrg = workflow.Steps.Any(d => d.BusinessType is EBusinessType.Department); return isFromOrg && isToCenter; } /// /// 复制一个节点为待接办 /// private async Task CreatePrevStepAsync(Workflow workflow, WorkflowStep step, CancellationToken cancellationToken) { step.Reset(); var newStep = _mapper.Map(step); newStep.Status = EWorkflowStepStatus.WaitForAccept; newStep.PrevStepId = step.PrevStepId; newStep.IsMain = step.IsMain; //newStep.ParentId = step.ParentId; newStep.Handlers = step.Handlers; newStep.StartCountersignId = step.StartCountersignId; newStep.CountersignId = step.CountersignId; newStep.IsStartedCountersignEnd = step.IsStartedCountersignEnd; await _workflowStepRepository.AddAsync(newStep, cancellationToken); await CreateTraceAsync(workflow, newStep, EWorkflowTraceStatus.Previous, cancellationToken); return newStep; } private async Task CreateCountersignAsync( string workflowId, WorkflowStep startStep, List handlers, EFlowAssignType? flowAssignType, ECounterSignType? counterSignType, DateTime stepExpiredTime, string? parentId = null, CancellationToken cancellationToken = default) { var countersign = new WorkflowCountersign { WorkflowId = workflowId, StartStepId = startStep.Id, StartStepCode = startStep.Code, StartStepBusiType = startStep.BusinessType, StarterId = _sessionContext.RequiredUserId, StarterName = _sessionContext.UserName, StarterOrgId = _sessionContext.RequiredOrgId, StarterOrgName = _sessionContext.OrgName, StarterOrgAreaCode = _sessionContext.OrgAreaCode, StarterOrgAreaName = _sessionContext.OrgAreaName, ParentId = parentId, Members = _mapper.Map>(handlers), FlowAssignType = flowAssignType, CounterSignType = counterSignType, }; await _workflowCountersignRepository.AddAsync(countersign, cancellationToken); return countersign; } 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) { HandleTrace(trace, dto.Opinion); } await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken); } private void HandleTrace(WorkflowTrace trace, string? opinion) { trace.Handle( _sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId, _sessionContext.OrgName, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName, _sessionContext.OrgIsCenter, opinion); } private async Task RecallTraceAsync(string workflowId, string opinion, CancellationToken cancellationToken) { //未办理的traces var uncompleteTraces = await _workflowTraceRepository.QueryAsync(d => d.WorkflowId == workflowId && string.IsNullOrEmpty(d.HandlerId)); if (uncompleteTraces.Any()) { foreach (var trace in uncompleteTraces) { HandleTrace(trace, opinion); } 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); HandleTrace(trace, dto.Opinion); await _workflowTraceRepository.UpdateAsync(trace, cancellationToken); return trace; } //private async Task EndTraceAsync(Workflow workflow, BasicWorkflowDto dto, WorkflowStep step, CancellationToken cancellationToken) //{ // var trace = _mapper.Map(step); // trace.Status = EWorkflowTraceStatus.Normal; // trace.ExpiredTime = workflow.ExpiredTime; // trace.TimeLimit = workflow.TimeLimit; // await _workflowTraceRepository.AddAsync(trace, cancellationToken); //} private async Task 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 step, EWorkflowTraceStatus traceStatus = EWorkflowTraceStatus.Normal, CancellationToken cancellationToken = default) { var trace = _mapper.Map(step); trace.Status = traceStatus; if (step.IsInCountersign()) { if (step.IsCountersignEndStep) { var startTrace = await GetWorkflowTraceAsync(workflow.Id, step.CountersignStartStepId, cancellationToken); trace.ParentId = startTrace.ParentId; } else { if (step.CountersignPosition is ECountersignPosition.Inner) { var prevTrace = await GetWorkflowTraceAsync(workflow.Id, step.PrevStepId, cancellationToken); trace.ParentId = prevTrace.Id; } else if (step.CountersignPosition is ECountersignPosition.Outer) { var prevTrace = await GetWorkflowTraceAsync(workflow.Id, step.PrevStepId, cancellationToken); trace.ParentId = prevTrace.ParentId; } } } await _workflowTraceRepository.AddAsync(trace, cancellationToken); } private async Task 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 RecallAsync(Workflow workflow, BasicWorkflowDto dto, FlowAssignInfo flowAssignInfo, StepDefine targetStepDefine, WorkflowStep targetStep, EWorkflowTraceStatus traceStatus, CancellationToken cancellationToken) { var targetIsStartStep = targetStepDefine.StepType is EStepType.Start; //get targetStep's previous WorkflowStep? targetPrevStep = null; if (!targetIsStartStep) { targetPrevStep = workflow.Steps.FirstOrDefault(d => d.Id == targetStep.PrevStepId); if (targetPrevStep == null) throw new UserFriendlyException($"{nameof(RecallAsync)}, 未找到目标节点的前一节点, flowId: {workflow.Id}"); } //查询所有目标节点之后的节点,然后删掉(包括目标节点) var removeSteps = GetStepsBehindTargetStep(workflow.Steps, targetStep); if (removeSteps.Any()) { await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken); workflow.Steps.RemoveAll(d => removeSteps.Contains(d)); } workflow.EndCountersign(); workflow.ResetOption(); if (workflow.Status is EWorkflowStatus.Completed) workflow.SetStatusRunnable(); var targetStepNew = targetIsStartStep ? await CreateStartStepAsync(workflow, targetStepDefine, dto, dto.NextHandlers, traceStatus, cancellationToken) : (await CreateStepsAsync(workflow, targetStepDefine, targetPrevStep, dto, flowAssignInfo.FlowAssignType, dto.NextHandlers, null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceStatus, cancellationToken)).First(); //更新当前办理节点信息 workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, _sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId, _sessionContext.OrgName, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName, nextStep: targetStepNew); //calc workflow expired time var isOrgToCenter = CheckIfFlowFromOrgToCenter(workflow, targetStep); //if (isOrgToCenter) // workflow.ExpiredTime = CalculateExpiredTime("");//todo calc expiredTime return isOrgToCenter; } private ICollection GetStepsBehindTargetStep(List steps, WorkflowStep targetStep) { var behindSteps = new List { targetStep }; var nextSteps = steps.Where(d => d.PrevStepId == targetStep.Id).ToList(); if (!nextSteps.Any()) return behindSteps; foreach (var nextStep in nextSteps) { behindSteps.AddRange(GetStepsBehindTargetStep(steps, nextStep)); } return behindSteps; } 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 CreateEndStepAsync( Workflow workflow, StepDefine endStepDefine, WorkflowStep prevStep, CancellationToken cancellationToken) { if (workflow.Steps.Any(d => d.StepType == EStepType.End)) throw UserFriendlyException.SameMessage("无法重复创建结束节点"); var handler = new Kv { Key = _sessionContext.RequiredUserId, Value = _sessionContext.UserName }; var step = CreateStep(endStepDefine, prevStep, workflow.Id, null, new List { handler }, null, null, null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, DateTime.Now, endStepDefine.Name, true); //step.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName, // _sessionContext.RequiredOrgId, _sessionContext.OrgName, // _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName); HandleStep(step, string.Empty); await _workflowStepRepository.AddAsync(step, cancellationToken); //end trace await CreateTraceAsync(workflow, step, cancellationToken: cancellationToken); return step; } public async Task> CreateConfigStepsAsync( Workflow workflow, StepDefine stepDefine, WorkflowStep prevStep, BasicWorkflowDto dto, FlowAssignInfo flowAssignInfo, //DateTime expiredTime, EWorkflowTraceStatus traceStatus, CancellationToken cancellationToken) { List handlers; if (stepDefine.HandlerType is EHandlerType.AssignedUser or EHandlerType.AssignedOrg) { handlers = stepDefine.HandlerTypeItems; } else { if (stepDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any()) throw new UserFriendlyException("未指定节点处理者"); handlers = dto.NextHandlers; } return await CreateStepsAsync(workflow, stepDefine, prevStep, dto, flowAssignInfo.FlowAssignType, handlers, null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceStatus, cancellationToken); } private async Task> CreateStepsAsync( Workflow workflow, StepDefine stepDefine, WorkflowStep prevStep, BasicWorkflowDto dto, EFlowAssignType? flowAssignType, List handlers, string? countersignId, EWorkflowStepStatus stepStatus, ECountersignPosition csPosition, //DateTime expiredTime, bool isOrigin, EWorkflowTraceStatus traceStatus, CancellationToken cancellationToken ) { //var countersignId = prevStep.IsStartCountersign // ? prevStep.StartCountersignId // : prevStep.IsInCountersign() ? prevStep.Id : null; List steps = new(); if (dto.IsStartCountersign) { foreach (var handler in handlers) { var step = CreateStep(stepDefine, prevStep, workflow.Id, flowAssignType, new List { handler }, dto.NextStepCode, dto.NextMainHandler, countersignId, stepStatus, csPosition, workflow.ExpiredTime, dto.NextStepName, isOrigin); steps.Add(step); } } else { var step = CreateStep(stepDefine, prevStep, workflow.Id, flowAssignType, handlers, dto.NextStepCode, dto.NextMainHandler, countersignId, stepStatus, csPosition, workflow.ExpiredTime, dto.NextStepName, isOrigin); steps.Add(step); } await _workflowStepRepository.AddRangeAsync(steps, cancellationToken); //create traces todo add range traces foreach (var step in steps) { await CreateTraceAsync(workflow, step, traceStatus, cancellationToken); } return steps; } /// /// 查询未完成节点 /// private WorkflowStep GetUnHandleStep(List steps, string orgCode, string userId) { var step = GetStep(steps, orgCode, userId, d => d != EWorkflowStepStatus.Handled); if (step == null) throw new UserFriendlyException( $"未找到对应节点, workflowId: {steps.FirstOrDefault()?.WorkflowId} orgCode:{orgCode}, userId: {userId}", "未找到对应节点"); return step; } private WorkflowStep? GetStep(List steps, string orgCode, string userId, Func predicate) => steps.FirstOrDefault(d => predicate(d.Status) && d.Handlers.Any(x => x.Key == orgCode || x.Key == userId)); private WorkflowStep CreateStep( StepDefine stepDefine, WorkflowStep prevStep, string workflowId, EFlowAssignType? flowAssignType, List handlers, string nextStepCode, string? nextMainHandler, string? countersignId, EWorkflowStepStatus stepStatus, ECountersignPosition countersignPosition, DateTime expiredTime, string stepName, bool isOrigin ) { if (!handlers.Any()) throw new UserFriendlyException($"非法参数, method: {nameof(CreateStep)}"); var step = _mapper.Map(stepDefine); var handlerIds = handlers.Select(d => d.Key).ToList(); var isMain = handlers.Count == 1 || (handlers.Count > 1 || handlerIds.First() == nextMainHandler); step.WorkflowId = workflowId; step.FlowAssignType = flowAssignType; step.Handlers = handlers; step.NextStepCode = step.StepType is EStepType.End ? string.Empty : nextStepCode; step.IsMain = isMain; step.PrevStepId = prevStep.Id; step.PrevStepCode = prevStep.Code; step.CountersignId = countersignId; step.Status = stepStatus; step.CountersignPosition = countersignPosition; step.StepExpiredTime = expiredTime; //step.TimeLimit = GetTimeLimit(""); step.IsOrigin = isOrigin; step.Name = stepName; return step; } ///// ///// 依据配置生成过期时间 ///// ///// //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; } } }