using Dm; using Hotline.File; using Hotline.FlowEngine.Definitions; using Hotline.FlowEngine.Notifications; using Hotline.FlowEngine.WorkflowModules; using Hotline.SeedData; 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.Users; using MapsterMapper; using MediatR; using Microsoft.Extensions.Logging; using SqlSugar; using System.Diagnostics; using Hotline.EventBus; using XF.Domain.Authentications; using XF.Domain.Dependency; using XF.Domain.Entities; using XF.Domain.Exceptions; using XF.Domain.Repository; using System.Reflection.Metadata; 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 IRepository _workflowStepHandlerRepository; private readonly ISessionContext _sessionContext; private readonly IMapper _mapper; private readonly Publisher _publisher; private readonly ILogger _logger; private readonly IFileRepository _fileRepository; private readonly IRepository _userRepository; public WorkflowDomainService( IWorkflowRepository workflowRepository, IRepository workflowStepRepository, IRepository workflowTraceRepository, IRepository workflowSupplementRepository, IRepository workflowCountersignRepository, //IRepository workflowStepHandlerRepository, ISessionContext sessionContext, IMapper mapper, Publisher publisher, ILogger logger, IFileRepository fileRepository) { _workflowRepository = workflowRepository; _workflowStepRepository = workflowStepRepository; _workflowTraceRepository = workflowTraceRepository; _workflowSupplementRepository = workflowSupplementRepository; _workflowCountersignRepository = workflowCountersignRepository; //_workflowStepHandlerRepository = workflowStepHandlerRepository; _sessionContext = sessionContext; _mapper = mapper; _publisher = publisher; _logger = logger; _fileRepository = fileRepository; } public async Task CreateWorkflowAsync(WorkflowModule wfModule, string title, string userId, string orgId, 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, Steps = new(), Traces = new(), WorkflowDefinition = definition, ExternalId = externalId ?? string.Empty, //FlowedOrgIds = new List { orgId }, 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, bool isNextDynamic, FlowAssignInfo flowAssignInfo, ECounterSignType? counterSignType, DateTime? expiredTime, ISessionContext current, CancellationToken cancellationToken) { if (firstStepDefine.StepType is EStepType.End) { await _publisher.PublishAsync( new StartWorkflowNotify(workflow, dto, flowAssignInfo, startStep.WorkflowTrace), PublishStrategy.ParallelWhenAll, cancellationToken); //firstStep是否为end,t: 实际办理节点为startStep, 并且handlerId赋值 f: 实际办理节点为firstStep, handlerId未赋值 workflow.UpdateActualStepWhenHandle(startStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel); workflow.UpdateCurrentStepWhenHandle(startStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel); var endTrace = await EndAsync(workflow, dto, firstStepDefine, startStep, current, expiredTime, cancellationToken); return; } //firststeps var firstSteps = await CreateNextStepsAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic, flowAssignInfo, expiredTime, dto.IsStartCountersign, 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 var trace = await NextTraceAsync(workflow, dto, startStep, cancellationToken); //todo 计算办理工作时长 ////更新当前办理节点信息 //workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, // current.RequiredUserId, current.UserName, // current.RequiredOrgId, current.OrgName, // current.OrgAreaCode, current.OrgAreaName, // startStep, firstSteps.First()); //指派实际办理节点 UpdateActualStep(workflow, dto, firstStepDefine, firstSteps); //更新实际办理节点 UpdateCurrentStep(workflow, dto, firstStepDefine, firstSteps); //发起会签时记录顶层会签节点(必须在update currentStep之后) if (dto.IsStartCountersign && !workflow.IsInCountersign) workflow.StartCountersign(startStep.Id, counterSignType); ////更新实际办理节点信息 //workflow.UpdateWorkflowActualHandleInfo(startStep, // current.RequiredUserId, current.UserName, // current.RequiredOrgId, current.OrgName, // current.OrgAreaCode, current.OrgAreaName, // current.OrgLevel); workflow.UpdateHandlers(current.RequiredUserId, current.RequiredOrgId, flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects, true); //更新指派信息 workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds()); await _workflowRepository.UpdateAsync(workflow, cancellationToken); //publish await _publisher.PublishAsync(new StartWorkflowNotify(workflow, dto, flowAssignInfo, trace), PublishStrategy.ParallelWhenAll, cancellationToken); } public async Task GetWorkflowAsync(string workflowId, bool withDefine = false, bool withSteps = false, bool withTraces = false, bool withTracesTree = false, bool withSupplements = false, bool withCountersigns = false, CancellationToken cancellationToken = default) { if (withTraces && withTracesTree) throw new UserFriendlyException("traces只能在集合与树形集合结构中二选一"); 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, x => x.Members); if (withSteps) query = query.Includes(d => d.Steps); //if (withTraces) // query = query.Includes(d => d.Traces); 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 (withTracesTree) { workflow.Traces = await _workflowTraceRepository.Queryable() .Where(d => d.WorkflowId == workflow.Id) .OrderBy(d => d.CreationTime) .ToTreeAsync(d => d.Traces, d => d.ParentId, null); } if (withTraces) { workflow.Traces = await _workflowTraceRepository.Queryable() .Where(d => d.WorkflowId == workflow.Id) .OrderBy(d => d.CreationTime) .ToListAsync(cancellationToken); } return workflow; } /// /// 查询工作流包含当前用户结束会签权限(是否可结束) /// public async Task<(Workflow Workflow, string? CountersignId, bool CanHandle, bool CanPrevious, WorkflowTrace? Trace)> GetWorkflowHandlePermissionAsync( string workflowId, string userId, string orgId, string[] roleIds, CancellationToken cancellationToken = default) { var workflow = await GetWorkflowAsync(workflowId, withSteps: true, withTraces: true, withCountersigns: true, cancellationToken: cancellationToken); var canHandle = workflow.IsCanHandle(userId, orgId, roleIds); var canPrevious = false; if (canHandle) { var currentStep = FindCurrentStepWaitForHandle(workflow, userId, orgId, roleIds); if (currentStep.Status is not EWorkflowStepStatus.Handled) { canPrevious = !(currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)); } } var unhandlePreviousTrace = workflow.Traces.FirstOrDefault(d => d.Status is not EWorkflowStepStatus.Handled //&& d.TraceType is EWorkflowTraceType.Previous ); //var previousOpinion = unhandlePreviousTrace?.Opinion ?? null; var unCompletedCountersign = workflow.Countersigns .FirstOrDefault(d => !d.IsCompleted() && d.StarterOrgId == orgId); if (unCompletedCountersign is null) return (workflow, null, canHandle, canPrevious, unhandlePreviousTrace); //var existCountersignEndStep = workflow.Steps.Exists(d => // d.IsCountersignEndStep && d.CountersignStartStepId == unCompletedCountersign.StartStepId); //return (workflow, existCountersignEndStep ? null : unCompletedCountersign.Id, canPrevious); return (workflow, unCompletedCountersign.Id, canHandle, canPrevious, unhandlePreviousTrace); } /// /// 受理(接办) /// public async Task AcceptAsync(Workflow workflow, string userId, string? userName, string orgId, string? orgName, string? orgAreaCode, string? orgAreaName, CancellationToken cancellationToken) { if (!workflow.IsCanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles)) return; //工单完成以后查看的场景 if (workflow.Status != EWorkflowStatus.Runnable) return; var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId, _sessionContext.Roles); 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("当前流程已流转到最终步骤"); currentStep.Accept(userId, userName, orgId, orgName, orgAreaCode, orgAreaName); var trace = workflow.Traces.First(d => d.Id == currentStep.Id); _mapper.Map(currentStep, trace); currentStep.WorkflowTrace = trace; //await _workflowStepRepository.UpdateAsync(currentStep, cancellationToken); await _workflowStepRepository.UpdateNav(currentStep) .Include(d => d.Workflow) .Include(d => d.WorkflowTrace) .ExecuteCommandAsync(); //await AcceptTraceAsync(workflow, currentStep, cancellationToken); //await _mediator.Publish(new AcceptWorkflowNotify(workflow), cancellationToken); } /// /// 办理(流转至下一节点) /// public async Task NextAsync(Workflow workflow, WorkflowStep currentStep, NextWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, FlowAssignInfo flowAssignInfo, DateTime? expiredTime, ISessionContext current, CancellationToken cancellationToken) { //ValidatePermission(workflow, current.RequiredOrgId, current.RequiredUserId, current.Roles); CheckWhetherRunnable(workflow.Status); #region 办理当前节点 if (dto.Files != null && dto.Files.Any()) currentStep.FileJson = await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId, currentStep.Id, cancellationToken); //(currentStep.IsInCountersign() && !dto.BackToCountersignEnd) || dto.IsStartCountersign; var isStartCountersign = currentStep.CountersignPosition switch { ECountersignPosition.None => dto.IsStartCountersign, ECountersignPosition.Multi => !dto.BackToCountersignEnd, ECountersignPosition.Single => !dto.BackToCountersignEnd, ECountersignPosition.End => dto.IsStartCountersign, _ => throw new ArgumentOutOfRangeException() }; var counterSignType = GetCounterSignType(dto.IsStartCountersign); 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, current.RequiredUserId, current.UserName, current.RequiredOrgId, current.OrgName, current.OrgAreaCode, current.OrgAreaName); await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken); } } await HandleStepAsync(current, currentStep, workflow, dto, flowAssignInfo.FlowAssignType, counterSignType, expiredTime, cancellationToken); //创建会签数据 if (isStartCountersign) { var exists = workflow.Countersigns.Any(d => !d.IsCompleted() && d.StarterId == current.RequiredUserId); if (exists) throw new UserFriendlyException("该用户在当前流程存在未结束会签"); await StartCountersignAsync(current, workflow, currentStep, dto, flowAssignInfo.FlowAssignType, counterSignType, expiredTime, cancellationToken); } currentStep.IsActualHandled = CheckIsActualHandle(workflow, currentStep, nextStepDefine, dto); _mapper.Map(dto, workflow); ////操作为回到会签汇总时,更新开始会签节点的会签办理状态 //if (currentStep.IsInCountersign() && dto.BackToCountersignEnd) //{ // if (currentStep.IsCountersignEndStep) // { // if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)) // { // //汇总节点(非顶级) // var csStartStep = // workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId); // if (csStartStep is null) // throw new UserFriendlyException("未查询到会签开始节点"); // PrevStepCsHandled(workflow, csStartStep, ref updateSteps); // } // } // else if (currentStep.CountersignPosition is ECountersignPosition.Inner) // { // PrevStepCsHandled(workflow, currentStep, ref updateSteps); // } //} //会签办理节点办理时更新会签members字段 if (currentStep.CountersignPosition is ECountersignPosition.Multi or ECountersignPosition.Single) { if (!string.IsNullOrEmpty(currentStep.CountersignId)) { //会签中正常办理节点,更新会签members办理状态 var countersign = workflow.Countersigns.FirstOrDefault(d => !d.IsCompleted() && d.Id == currentStep.CountersignId); if (countersign is not null) { //throw new UserFriendlyException( // $"会签数据异常, workflowId: {currentStep.WorkflowId}, countersignId: {currentStep.CountersignId}", // "会签数据异常"); countersign.MemberHandled(current.RequiredUserId, current.RequiredOrgId); //update cs await _workflowCountersignRepository.UpdateNav(countersign) .Include(d => d.Members) .ExecuteCommandAsync(); } } } await _workflowStepRepository.UpdateRangeAsync(updateSteps, cancellationToken); //await _workflowStepRepository.UpdateNav(updateSteps) // .Include(d => d.StepHandlers) // .ExecuteCommandAsync(); //更新traces var updateTraces = new List(); foreach (var updateStep in updateSteps) { var updateTrace = workflow.Traces.First(d => d.Id == updateStep.Id); _mapper.Map(updateStep, updateTrace); updateTraces.Add(updateTrace); } await _workflowTraceRepository.UpdateRangeAsync(updateTraces, cancellationToken); //var trace = await NextTraceAsync(workflow, dto, currentStep, cancellationToken); #endregion #region 处理流程 //检查会签是否结束,并更新当前会签节点字段 var isCountersignOver = false; if (workflow.IsInCountersign && currentStep.IsCountersignEndStep) { isCountersignOver = workflow.CheckIfCountersignOver(); if (isCountersignOver) workflow.EndCountersign(); } if (workflow.ActualHandleStepId == currentStep.Id) { //更新实际办理节点信息 workflow.UpdateActualStepWhenHandle(currentStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel); } if (workflow.CurrentStepId == currentStep.Id) { workflow.UpdateCurrentStepWhenHandle(currentStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel); } //检查是否流转到流程终点 if (nextStepDefine.StepType is EStepType.End) { var endTrace = await EndAsync(workflow, dto, nextStepDefine, currentStep, current, expiredTime, cancellationToken); return; } //创建下一/N个节点(会签汇总节点:会签未全部办理时不创建,最后一个会签办理节点创建会签汇总节点) var nextSteps = await CreateNextStepsAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, flowAssignInfo, expiredTime, isStartCountersign, cancellationToken); ////赋值当前节点的下级办理节点 //if (dto.IsStartCountersign // //|| (currentStep.IsInCountersign() && // // !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)) // ) //{ // currentStep.CreateCountersignSteps(nextSteps); // await _workflowStepRepository.UpdateAsync(currentStep, cancellationToken); //} //更新办理对象(nextSteps无元素表示当前节点为会签办理节点且当前会签没有全部办理完成) workflow.UpdateHandlers(current.RequiredUserId, current.RequiredOrgId, flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects, nextSteps.Any()); //todo 计算办理工作时长 //指派实际办理节点 UpdateActualStep(workflow, dto, nextStepDefine, nextSteps); //更新实际办理节点 UpdateCurrentStep(workflow, dto, nextStepDefine, nextSteps); //发起会签时记录顶层会签节点 if (dto.IsStartCountersign && !workflow.IsInCountersign) workflow.StartCountersign(currentStep.Id, counterSignType); //更新指派信息 workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds()); //更新会签实际办理对象信息 if (currentStep.IsActualHandled) workflow.AddCsActualHandler(current.RequiredUserId, current.RequiredOrgId); await _workflowRepository.UpdateAsync(workflow, cancellationToken); #endregion #region 流转记录 //var trace = await NextTraceAsync(workflow, dto, currentStep, cancellationToken); #endregion var currentTrace = workflow.Traces.First(d => d.Id == currentStep.Id); await _publisher.PublishAsync( new NextStepNotify(workflow, dto, flowAssignInfo, currentTrace, nextStepDefine, current.RequiredOrgId, expiredTime.HasValue), PublishStrategy.ParallelWhenAll, cancellationToken); } /// /// 退回(返回前一节点) /// /// public async Task PreviousAsync(Workflow workflow, PreviousWorkflowDto dto, string applicantId, string applicantName, string applicantOrgId, string applicantOrgName, string applicantOrgAreaCode, string applicantOrgAreaName, bool applicantIsCenter, string[] applicantRoleIds, CancellationToken cancellationToken) { //ValidatePermission(workflow, operater.OrgId, operater.Id); var (currentStep, prevStep, countersignStartStep) = GetPreviousStep(workflow, applicantId, applicantOrgId, applicantRoleIds); //保存附件 if (dto.Files.Any()) currentStep.FileJson = await _fileRepository.AddFileAsync( dto.Files, workflow.ExternalId, currentStep.Id, cancellationToken); // add prev current to remove list var removeSteps = new List { currentStep, prevStep }; if (countersignStartStep is not null) { //add cs steps to remove list SearchCountersignSteps(countersignStartStep, workflow.Steps, ref removeSteps); //end cs 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(); await _workflowStepRepository.UpdateAsync(countersignStartStep, cancellationToken); //updateSteps.Add(countersignStartStep); //结束会签 //currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType, // current.RequiredUserId, current.UserName, // current.RequiredOrgId, current.OrgName, // current.OrgAreaCode, current.OrgAreaName); currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType, applicantId, applicantName, applicantOrgId, applicantOrgName, applicantOrgAreaCode, applicantOrgAreaName); await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken); //update workflow cs status if (workflow.CheckIfCountersignOver()) workflow.EndCountersign(); } //update trace //var trace = await PreviousTraceAsync(workflow.Id, dto, currentStep, // applicantId, applicantName, // applicantOrgId, applicantOrgName, // applicantOrgAreaCode, applicantOrgAreaName, // applicantIsCenter, cancellationToken); var trace = workflow.Traces.First(t => t.StepId == currentStep.Id); _mapper.Map(dto, trace); trace.FileJson = currentStep.FileJson; //HandleTrace(trace, dto.Opinion, current); trace.Handle(applicantId, applicantName, applicantOrgId, applicantOrgName, applicantOrgAreaCode, applicantOrgAreaName, applicantIsCenter, dto.Opinion); //await _workflowTraceRepository.UpdateAsync(trace, cancellationToken); //如果有传入期满时间 新节点为传入的期满时间 if (dto.ExpiredTime.HasValue) prevStep.StepExpiredTime = dto.ExpiredTime; //复制上一个节点为待接办 var newPrevStep = await DuplicateStepWithTraceAsync(workflow, prevStep, EWorkflowTraceType.Previous, cancellationToken); //退给派单组节点,需按照平均分配原则派给一个派单员 禅道299 TODO if (dto.Handler != null)//todo 改为按策略判断 { var handle = dto.Handler; newPrevStep.Assign(handle.UserId, handle.Username, handle.OrgId, handle.OrgName, handle.RoleId, handle.RoleName); } //remove workflow.steps await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken); //await _workflowStepRepository.RemoveNav(removeSteps) // .Include(d => d.StepHandlers) // .ExecuteCommandAsync(); var stepIds = removeSteps.Select(d => d.Id).ToList(); var updateTraces = workflow.Traces.Where(d => stepIds.Contains(d.StepId)).ToList(); await UpdateTracesStateAsync(updateTraces, EWorkflowTraceState.StepRemoveByPrevious, cancellationToken); if (workflow.Status is EWorkflowStatus.Completed) workflow.SetStatusRunnable(); //更新实际办理节点信息 workflow.UpdateActualStepWhenAssign(newPrevStep, new FlowStepHandler { UserId = prevStep.HandlerId, Username = prevStep.HandlerName, OrgId = prevStep.HandlerOrgId, OrgName = prevStep.HandlerOrgName, }); workflow.UpdateCurrentStepWhenAssign(newPrevStep, new FlowStepHandler { UserId = prevStep.HandlerId, Username = prevStep.HandlerName, OrgId = prevStep.HandlerOrgId, OrgName = prevStep.HandlerOrgName, }); //更新流程可办理对象 workflow.UpdatePreviousHandlers(applicantId, applicantOrgId, prevStep); //orgToCenter会触发重新计算期满时间,1.无需审核按当前时间进行计算 2.需审核按审核通过时间计算 var isOrgToCenter = prevStep.BusinessType is EBusinessType.Send && prevStep.IsOrigin; await _workflowRepository.UpdateAsync(workflow, cancellationToken); await _publisher.PublishAsync(new PreviousNotify(workflow, newPrevStep, dto, isOrgToCenter), PublishStrategy.ParallelWhenAll, cancellationToken); return GetFlowDirection(currentStep.BusinessType, prevStep.BusinessType); } private async Task UpdateTracesStateAsync(List traces, EWorkflowTraceState traceState, CancellationToken cancellationToken) { foreach (var trace in traces) { trace.TraceState = traceState; } await _workflowTraceRepository.UpdateRangeAsync(traces, cancellationToken); } /// /// 查询退回节点信息 /// public (WorkflowStep currentStep, WorkflowStep prevStep, WorkflowStep? countersignStartStep) GetPreviousStep( Workflow workflow, string operaterId, string operaterOrgId, string[] roleIds) { var currentStep = GetUnHandleStep(workflow.Steps, operaterOrgId, operaterId, roleIds); var isCurrentTopCountersignEndStep = workflow.IsInCountersign && currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId); if (currentStep.IsInCountersign() && !isCurrentTopCountersignEndStep) throw UserFriendlyException.SameMessage("会签节点不支持退回"); if (workflow.FlowType is EFlowType.Review && currentStep.StepType is EStepType.Start && currentStep.IsOrigin) throw UserFriendlyException.SameMessage("当前流程已退回到开始节点"); //当退回操作遇到会签时,删除所有会签节点直达topCsStep //find prevStep, update handler WorkflowStep? prevStep, countersignStartStep = null; if (isCurrentTopCountersignEndStep) { //prev is topstart's prev countersignStartStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId); if (countersignStartStep is null) throw new UserFriendlyException("未查询到对应会签开始节点"); prevStep = workflow.Steps.FirstOrDefault(d => d.Id == countersignStartStep.PrevStepId); } else { prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId); } if (prevStep == null) throw UserFriendlyException.SameMessage("未查询到前一节点"); if (prevStep.IsCountersignEndStep) { countersignStartStep = workflow.Steps.FirstOrDefault(d => d.Id == prevStep.CountersignStartStepId); prevStep = countersignStartStep ?? throw new UserFriendlyException("未查询到对应会签开始节点"); if (prevStep == null) throw UserFriendlyException.SameMessage("未查询到前一节点"); } return (currentStep, prevStep, countersignStartStep); } /// /// 查询派单池中流程节点id /// public async Task> GetUnhandleStepIdsFromSendPoolAsync(string sendPoolId, CancellationToken cancellationToken) { return await _workflowStepRepository.Queryable() .Where(d => SqlFunc.JsonListObjectAny(d.Handlers, "Key", sendPoolId)) .Select(d => d.Id) .ToListAsync(cancellationToken); } /// /// 查询归属某用户的所有流程节点 /// public async Task> GetStepsBelongsToAsync(string userId, CancellationToken cancellationToken) { return await _workflowStepRepository.Queryable() .Includes(d => d.WorkflowTrace) .Where(d => d.HandlerId == userId) .OrderBy(d => d.CreationTime) .ToListAsync(cancellationToken); } ///// ///// 批量改变办理对象 ///// //public async Task> ChangeHandlerRangeAsync(string sendPoolId, // IReadOnlyList<(string userId, string username, string orgId, string orgName, IReadOnlyList stepIds)> handlers, // CancellationToken cancellationToken) //{ // var stepsIds = handlers.SelectMany(d => d.stepIds).ToList(); // var steps = await _workflowStepRepository.Queryable() // .Includes(d => d.Workflow) // .Includes(d => d.WorkflowTrace) // //.Includes(d => d.StepHandlers) // .Where(d => stepsIds.Contains(d.Id)) // .ToListAsync(cancellationToken); // foreach (var handler in handlers) // { // var thisHandlers = new List { new(handler.userId, handler.username) }; // var thisHandlerGroup = new HandlerGroupItem // { // GroupId = Guid.NewGuid().ToString(), // Key = handler.userId, // Value = handler.username, // }; // var thisSteps = steps.Where(d => handler.stepIds.Contains(d.Id)).ToList(); // foreach (var thisStep in thisSteps) // { // //var stepHandler = WorkflowStepHandler.Create(thisStep.Workflow.Id, thisStep.Workflow.ExternalId, // // thisStep.FlowAssignType ?? EFlowAssignType.User, handler.userId, handler.username, handler.orgId, handler.orgName); // //thisStep.StepHandlers.Clear(); // //thisStep.StepHandlers.Add(stepHandler); // thisStep.Handlers = thisHandlers; // //update trace // thisStep.WorkflowTrace.Handlers = thisStep.Handlers; // // update workflow // thisStep.Workflow.FlowedUserIds.Remove(sendPoolId); // thisStep.Workflow.FlowedUserIds.Add(handler.userId); // thisStep.Workflow.UpdateHandlers(sendPoolId, null, EFlowAssignType.User, // new List { thisHandlerGroup }, true); // var handlerUser = thisStep.Workflow.HandlerUsers.FirstOrDefault(d => d.Key == sendPoolId); // if (handlerUser == null) continue; // handlerUser.Key = handler.userId; // handlerUser.Value = handler.username; // } // } // await _workflowStepRepository.UpdateNav(steps) // .Include(d => d.WorkflowTrace) // .Include(d => d.Workflow) // //.Include(d => d.StepHandlers) // .ExecuteCommandAsync(); // return steps.Select(d => d.WorkflowId).ToList(); //} /// /// 批量修改工单办理对象 /// public async Task ChangeHandlerBatchAsync( IReadOnlyList<(string userId, string username, string orgId, string orgName, string? roleId, string? roleName, ICollection steps )> handlers, CancellationToken cancellationToken) { foreach (var handler in handlers) { foreach (var step in handler.steps) { step.FlowAssignType = EFlowAssignType.User; step.Assign(handler.userId, handler.username, handler.orgId, handler.orgName, handler.roleId, handler.roleName); if (step.WorkflowTrace is null) throw new UserFriendlyException("未查询节点对应快照信息"); step.WorkflowTrace.FlowAssignType = EFlowAssignType.User; step.WorkflowTrace.Assign(handler.userId, handler.username, handler.orgId, handler.orgName, handler.roleId, handler.roleName); } } var steps = handlers.SelectMany(d => d.steps).ToList(); //await _workflowStepRepository.UpdateRangeAsync(steps, cancellationToken); await _workflowStepRepository.UpdateNav(steps) .Include(d => d.WorkflowTrace) .ExecuteCommandAsync(); } /// /// 查询工单办理中的一级部门 /// public async Task> GetLevelOneOrgsAsync(string workflowId, CancellationToken cancellation) { var traces = await _workflowTraceRepository.Queryable() .LeftJoin((t, o) => t.HandlerOrgId == o.Id) .Where((t, o) => t.WorkflowId == workflowId && !string.IsNullOrEmpty(t.HandlerOrgId) && o.Level == 1) .ToListAsync(cancellation); //var handlers = await _workflowStepHandlerRepository.Queryable() // .InnerJoin((wsh, wt) => wsh.WorkflowStepId == wt.StepId) // .LeftJoin((wsh, wt, o) => wsh.OrgId == o.Id) // .Where((wsh, wt, o) => wsh.WorkflowId == workflowId && // //wt.BusinessType == EBusinessType.Department && // //wt.HandlerType == EHandlerType.OrgLevel && // !string.IsNullOrEmpty(wsh.OrgId) && // o.Level == 1) // .ToListAsync(cancellation); //var orgs = handlers.Select(d => new Kv(d.OrgId, d.OrgName)) // .DistinctBy(d => d.Key) // .ToList(); var orgs = traces .DistinctBy(d => d.HandlerOrgId) .Select(d => new Kv(d.HandlerOrgId, d.HandlerOrgName)) .ToList(); return orgs; //var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellation); //var list = workflow.Steps.Distinct().Where(d => d.BusinessType == EBusinessType.Department && // d.HandlerType == EHandlerType.OrgLevel && // d.StepHandlers.Any(d => // !string.IsNullOrEmpty(d.OrgId) && d.OrgId.CheckIfOrgLevelIs(1))) // .Select(d => new Kv(d.StepHandlers.First().OrgId, d.StepHandlers.First().OrgName)) // .ToList(); //return list.Where((x, i) => list.FindIndex(z => z.Key == x.Key) == i).ToList(); } /// /// 更新未办理节点的期满时间 /// public async Task UpdateUnhandleExpiredTimeAsync(string workflowId, DateTime expiredTime, CancellationToken cancellation) { var steps = await _workflowStepRepository.Queryable() .Includes(d => d.WorkflowTrace) .Where(d => d.WorkflowId == workflowId && d.Status < EWorkflowStepStatus.Handled) .ToListAsync(cancellation); foreach (var step in steps) { step.StepExpiredTime = expiredTime; step.WorkflowTrace.StepExpiredTime = expiredTime; } await _workflowStepRepository.UpdateNav(steps) .Include(d => d.WorkflowTrace) .ExecuteCommandAsync(); } /// /// 查询该部门最后办理节点 /// /// public async Task FindLastHandleStepAsync(string workflowId, string orgId, CancellationToken cancellation) { return await _workflowStepRepository.Queryable() .Where(d => d.WorkflowId == workflowId && d.HandlerOrgId == orgId && d.StepType != EStepType.End && d.StepType != EStepType.Summary) //.Where(d => d.StepHandlers.Any(sh => sh.OrgId == orgId) && d.WorkflowId == workflowId) .OrderByDescending(d => d.HandleTime) .FirstAsync(cancellation); } /// /// 部门会签工单获取流程最顶级办理节点 /// /// public async Task FindTopHandleStepAsync(string workflowId, CancellationToken cancellation) { var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellation); return workflow.Steps.FirstOrDefault(x => x.Id == workflow.TopCountersignStepId); } /// /// 查询流转方向 /// public EFlowDirection GetFlowDirection(EBusinessType sourceStepBusinessType, EBusinessType directionStepBusinessType) { switch (sourceStepBusinessType) { case EBusinessType.Seat: case EBusinessType.Send: return directionStepBusinessType switch { EBusinessType.Seat => EFlowDirection.CenterToCenter, EBusinessType.Send => EFlowDirection.CenterToCenter, EBusinessType.Department => EFlowDirection.CenterToOrg, EBusinessType.File => EFlowDirection.CenterToFile, _ => throw new ArgumentOutOfRangeException(nameof(directionStepBusinessType), directionStepBusinessType, null) }; case EBusinessType.Department: case EBusinessType.File: return directionStepBusinessType switch { EBusinessType.Seat => EFlowDirection.OrgToCenter, EBusinessType.Send => EFlowDirection.OrgToCenter, EBusinessType.Department => EFlowDirection.OrgToOrg, EBusinessType.File => EFlowDirection.OrgToFile, _ => throw new ArgumentOutOfRangeException(nameof(directionStepBusinessType), directionStepBusinessType, null) }; default: throw new ArgumentOutOfRangeException(nameof(sourceStepBusinessType), sourceStepBusinessType, null); } } /// /// 流程被签收至某个用户(更新流转对象,办理对象,节点办理对象以及stepHandlers) /// public async Task SignToSomebodyAsync(string workflowId, string userId, string username, string orgId, string orgName, CancellationToken cancellationToken) { var workflow = await GetWorkflowAsync(workflowId, withSteps: true, withTraces: true, cancellationToken: cancellationToken); workflow.Assign(EFlowAssignType.User, _sessionContext.RequiredUserId); workflow.HandlerOrgs = new(); workflow.HandlerUsers = new List { new() { GroupId = Guid.NewGuid().ToString(), Key = userId, Value = username } }; var startStep = workflow.Steps.First(d => d.StepType == EStepType.Start && d.IsOrigin); startStep.Handlers = new List { new(userId, username) }; startStep.AcceptorId = userId; startStep.AcceptorName = username; startStep.AcceptTime = DateTime.Now; startStep.AcceptorOrgId = orgId; startStep.AcceptorOrgName = orgName; startStep.FlowAssignType = EFlowAssignType.User; startStep.Assign(userId, username, orgId, orgName); //var stepHandler = startStep.StepHandlers.First(); //startStep.StepHandlers.RemoveAll(d => d.Id != stepHandler.Id); //stepHandler.UserId = userId; //stepHandler.Username = username; //stepHandler.OrgId = orgId; //stepHandler.OrgName = orgName; startStep.WorkflowTrace = workflow.Traces.First(d => d.Id == startStep.Id); _mapper.Map(startStep, startStep.WorkflowTrace); await _workflowStepRepository.UpdateNav(startStep) //.Include(d => d.StepHandlers) .Include(d => d.WorkflowTrace) .ExecuteCommandAsync(); await _workflowRepository.UpdateAsync(workflow, cancellationToken); return workflow; } /// /// 非节点办理人员查询待办节点 /// /// public async Task> GetUnhandleStepsByOthersAsync(string workflowId, CancellationToken cancellationToken) { return await _workflowStepRepository.Queryable() .Where(d => d.WorkflowId == workflowId && d.Status != EWorkflowStepStatus.Handled) .ToListAsync(cancellationToken); } /// /// 查找当前会签内所有节点(含start,end) /// private void SearchCountersignSteps(WorkflowStep startStep, List steps, ref List csSteps) { if (startStep.IsStartCountersign) { var countersignSteps = steps.Where(d => d.CountersignId == startStep.StartCountersignId).ToList(); if (countersignSteps.Any()) { foreach (var countersignStep in countersignSteps) { SearchCountersignSteps(countersignStep, steps, ref csSteps); } } } csSteps.Add(startStep); } /// /// 撤回(返回到之前任意节点) /// public async Task RecallAsync(Workflow workflow, RecallDto dto, StepDefine targetStepDefine, FlowAssignInfo flowAssignInfo, EWorkflowTraceType traceType, DateTime? expiredTime, bool isOrderFiled, 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.Traces, dto.Opinion, _sessionContext, cancellationToken); var isOrgToCenter = await RecallAsync(workflow, dto, flowAssignInfo, targetStepDefine, targetStep, traceType, expiredTime, isOrderFiled, cancellationToken); await _workflowRepository.UpdateAsync(workflow, cancellationToken); await _publisher.PublishAsync(new RecallNotify(workflow, targetStep, dto, isOrgToCenter), PublishStrategy.ParallelWhenAll, cancellationToken); } /// /// 撤回至开始节点 /// public async Task RecallToStartStepAsync(string workflowId, string opinion, ISessionContext current, CancellationToken cancellationToken) { //todo 1.当前待办节点删掉 2.当前待办trace更新(status, opinion) 3.复制startStep为待办 4.更新workflow(status, csStatus, handlers) 5.publish event var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true, cancellationToken: cancellationToken); var startStep = workflow.Steps.First(d => d.StepType == EStepType.Start); if (startStep is null) throw new UserFriendlyException($"数据异常, workflowId: {workflowId}", "该流程无开始节点"); await RecallToTargetStepAsync(workflow, startStep, opinion, current, cancellationToken); } /// /// 撤回至派单节点 /// public async Task RecallToSendStepAsync(string workflowId, string opinion, ISessionContext current, CancellationToken cancellationToken) { var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true, cancellationToken: cancellationToken); var sendStep = workflow.Steps.FirstOrDefault(d => d.BusinessType == EBusinessType.Send); if (sendStep is null) throw new UserFriendlyException($"未找到派单节点, workflowId: {workflowId}", "该流程无派单节点"); await RecallToTargetStepAsync(workflow, sendStep, opinion, current, cancellationToken); } /// /// 特提至中心(优先派单组其次坐席) /// /// public async Task RecallToCenterFirstToSendAsync(string workflowId, string opinion, ISessionContext current, CancellationToken cancellationToken) { var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true, cancellationToken: cancellationToken); var sendStep = workflow.Steps.FirstOrDefault(d => d.BusinessType == EBusinessType.Send); if (sendStep is not null) { await RecallToTargetStepAsync(workflow, sendStep, opinion, current, cancellationToken); } else { var startStep = workflow.Steps.First(d => d.StepType == EStepType.Start); if (startStep is null) throw new UserFriendlyException($"数据异常, workflowId: {workflowId}", "该流程无开始节点"); await RecallToTargetStepAsync(workflow, startStep, opinion, current, cancellationToken); } } private async Task RecallToTargetStepAsync(Workflow workflow, WorkflowStep targetStep, string opinion, ISessionContext current, CancellationToken cancellationToken) { //update uncompleted traces await RecallTraceAsync(workflow.Traces, opinion, current, cancellationToken); await _workflowStepRepository.RemoveRangeAsync(workflow.Steps, cancellationToken); workflow.Steps.RemoveAll(_ => true); workflow.EndCountersign(); workflow.ResetOption(); if (workflow.Status is EWorkflowStatus.Completed) workflow.SetStatusRunnable(); var newStartStep = await DuplicateStepWithTraceAsync(workflow, targetStep, EWorkflowTraceType.Recall, cancellationToken); workflow.UpdateActualStepWhenAssign(targetStep, new FlowStepHandler { UserId = targetStep.HandlerId, Username = targetStep.HandlerName, OrgId = targetStep.HandlerOrgId, OrgName = targetStep.HandlerOrgName, }); workflow.UpdateCurrentStepWhenAssign(targetStep, new FlowStepHandler { UserId = targetStep.HandlerId, Username = targetStep.HandlerName, OrgId = targetStep.HandlerOrgId, OrgName = targetStep.HandlerOrgName, }); var isOrgToCenter = CheckIfFlowFromOrgToCenter(workflow, targetStep); var flowAssignInfo = FlowAssignInfo.Create(targetStep.FlowAssignType.Value, targetStep.Handlers); workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects); await _workflowRepository.UpdateAsync(workflow, cancellationToken); var dto = _mapper.Map(targetStep); dto.WorkflowId = workflow.Id; await _publisher.PublishAsync(new RecallNotify(workflow, targetStep, dto, isOrgToCenter), PublishStrategy.ParallelWhenAll, 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, null, cancellationToken); // await _workflowStepRepository.UpdateAsync(currentStep, cancellationToken); // workflow.UpdateActualStepWhenHandle(currentStep, // _sessionContext.RequiredUserId, _sessionContext.UserName, // _sessionContext.RequiredOrgId, _sessionContext.OrgName, // _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName, // _sessionContext.OrgLevel); // var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine(); // var endTrace = await EndAsync(workflow, dto, endStepDefine, currentStep, 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 _publisher.PublishAsync(new TerminalWorkflowNotify(workflow), PublishStrategy.ParallelWhenAll, 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, string userId, string orgId, string[] roleIds) => GetUnHandleStep(workflow.Steps, orgId, userId, roleIds); /// /// 查询当前节点中最后一个节点 /// public async Task FindLastStepAsync(string workflowId, CancellationToken cancellationToken) { var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellationToken); return workflow.Steps.MaxBy(d => d.CreationTime); } /// /// 查询所有办理部门及实际办理部门 /// /// public async Task<(Kv, IReadOnlyList)> GetHandleOrgsAsync(string workflowId, CancellationToken cancellationToken) { var workflow = await GetWorkflowAsync(workflowId, withTraces: true, cancellationToken: cancellationToken); var steps = workflow.Traces .Where(d => d.StepType is EStepType.Normal) .ToList(); var items = steps.Where(d => d.TraceType == EWorkflowTraceType.Normal || d.TraceType == EWorkflowTraceType.Jump) .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, DateTime? expiredTime, ISessionContext current, CancellationToken cancellationToken) { var workflow = await GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true, cancellationToken: cancellationToken); var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId, _sessionContext.Roles); //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, current, expiredTime, cancellationToken: cancellationToken); await _publisher.PublishAsync(new CancelWorkflowNotify(workflow), PublishStrategy.ParallelWhenAll, 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, FlowStepHandler handler, DateTime? expiredTime, EFlowAssignType? flowAssignType = EFlowAssignType.User) { //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); _mapper.Map(workflow, startStep); startStep.FlowAssignType = flowAssignType; startStep.Handlers = new List { new(handler.Key, handler.Value) }; //startStep.StepHandlers = stepHandlers; startStep.NextSteps = nextSteps; startStep.IsMain = true; startStep.IsOrigin = true; startStep.Status = EWorkflowStepStatus.WaitForAccept; startStep.PrevChosenStepCode = null; startStep.StepExpiredTime = expiredTime; startStep.Assign(handler.UserId, handler.Username, handler.OrgId, handler.OrgName, handler.RoleId, handler.RoleName); startStep.InitId(); return startStep; } /// /// 流程结束 /// public async Task EndAsync(Workflow workflow, BasicWorkflowDto dto, StepDefine endStepDefine, WorkflowStep currentStep, ISessionContext current, DateTime? expiredTime, CancellationToken cancellationToken) { //var endStepHandles = new List //{ // WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId, EFlowAssignType.User, // current.RequiredUserId, current.UserName, current.RequiredOrgId, current.OrgName) //}; //create endStep var endStep = await CreateEndStepAsync(current, workflow, endStepDefine, currentStep, expiredTime, cancellationToken); //workflow.Steps.Add(endStep); //update endTrace var endTrace = await NextTraceAsync(workflow, dto, endStep, cancellationToken); workflow.Complete(endStep, dto.ReviewResult); //需求调整:归档时当前节点显示为归档节点 workflow.UpdateCurrentStepWhenHandle(endStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel); workflow.UpdateCurrentStepAcceptTime(endStep.AcceptTime.Value); //workflow.UpdateActualStepWhenHandle(endStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel); //workflow.UpdateActualStepAcceptTime(endStep.AcceptTime.Value); if (string.IsNullOrEmpty(workflow.OrgLevelOneCode)) workflow.UpdateLevelOneOrg(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName); await _workflowRepository.UpdateAsync(workflow, cancellationToken); await _publisher.PublishAsync(new EndWorkflowNotify(workflow, endTrace, dto), PublishStrategy.ParallelWhenAll, cancellationToken); return endTrace; } ///// ///// 判断会签类型(中心会签或部门会签) ///// ///// ///// ///// //public ECounterSignType? GetCounterSignType(EBusinessType businessType) => // businessType switch // { // EBusinessType.Seat => ECounterSignType.Seat, // EBusinessType.Send => ECounterSignType.Seat, // EBusinessType.Department => ECounterSignType.Department, // EBusinessType.File => null, // _ => throw new ArgumentOutOfRangeException(nameof(businessType), businessType, null) // }; public ECounterSignType? GetCounterSignType(bool isStartCountersign) { if (!isStartCountersign) return null; return _sessionContext.OrgIsCenter ? ECounterSignType.Center : ECounterSignType.Department; } /// /// 办理节点 /// public async Task HandleStepAsync(ISessionContext current, WorkflowStep step, Workflow workflow, BasicWorkflowDto dto, EFlowAssignType? flowAssignType, ECounterSignType? counterSignType, DateTime? expiredTime, 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("缺少会签类型参数"); //办理参数 //_mapper.Map(dto, step); step.NextHandlers = dto.NextHandlers; step.NextMainHandler = dto.NextMainHandler; step.NextStepCode = dto.NextStepCode; step.IsSms = dto.IsSms; step.Opinion = dto.Opinion; step.Remark = dto.Remark; //step办理状态 HandleStep(current, step, dto.Opinion, dto.NextStepCode); } #region private method private static void UpdateCurrentStep(Workflow workflow, BasicWorkflowDto dto, StepDefine nextStepDefine, List nextSteps) { if (dto.IsStartCountersign) return; if (workflow.IsInCountersign) return; if (nextStepDefine.BusinessType is EBusinessType.Seat or EBusinessType.Send) { //坐席->派单不选办理对象时 workflow.UpdateCurrentStepWhenAssign(nextSteps.First(), new FlowStepHandler { OrgId = OrgSeedData.CenterId, OrgName = OrgSeedData.CenterName }); } else { var nextHandler = dto.NextHandlers.First(); workflow.UpdateCurrentStepWhenAssign(nextSteps.First(), nextHandler); } } private static void UpdateActualStep(Workflow workflow, BasicWorkflowDto dto, StepDefine nextStepDefine, List nextSteps) { if (dto.IsStartCountersign) return; if (workflow.IsInCountersign) return; if (nextStepDefine.StepType is EStepType.Summary or EStepType.End) return; if (nextStepDefine.BusinessType is EBusinessType.Seat or EBusinessType.Send) { //坐席->派单不选办理对象时 workflow.UpdateActualStepWhenAssign(nextSteps.First(), new FlowStepHandler { OrgId = OrgSeedData.CenterId, OrgName = OrgSeedData.CenterName }); } else { var nextHandler = dto.NextHandlers.First(); workflow.UpdateActualStepWhenAssign(nextSteps.First(), nextHandler); } //if ( /*workflow.FlowType is EFlowType.Handle &&*/ // !workflow.IsInCountersign && // nextStepDefine.StepType != EStepType.Summary && // nextStepDefine.StepType != EStepType.End) //{ // if (nextStepDefine.BusinessType is EBusinessType.Seat or EBusinessType.Send) // { // workflow.UpdateActualStepWhenAssign(firstSteps.First(), actualHandleOrgCode: OrgSeedData.CenterId, // actualHandleOrgName: "市民热线服务中心"); // } // else // { // var nextHandler = dto.NextHandlers.First(); // workflow.UpdateActualStepWhenAssign(firstSteps.First(), // actualHandleOrgCode: nextHandler.Key, actualHandleOrgName: nextHandler.Value); // } //} } private async Task CreateStartStepAsync(Workflow workflow, StepDefine startStepDefine, BasicWorkflowDto dto, FlowStepHandler handler, EWorkflowTraceType traceType, DateTime? expiredTime, EFlowAssignType? flowAssignType, CancellationToken cancellationToken) { var startStep = CreateStartStep(workflow, startStepDefine, dto, handler, expiredTime, flowAssignType); await _workflowStepRepository.AddAsync(startStep, cancellationToken); //await _workflowStepRepository.AddNav(startStep) // .Include(d => d.StepHandlers) // .ExecuteCommandAsync(); await CreateTraceAsync(workflow, startStep, traceType, 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, WorkflowStep currentStep, BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, FlowAssignInfo flowAssignInfo, DateTime? expiredTime, bool isStartCountersign, CancellationToken cancellationToken) { List nextSteps = new(); if (currentStep.IsInCountersign()) { if (currentStep.IsCountersignEndStep) { // check if current is topend f: csStartStep.prev // t: check if dto.StartCs t: csconfig f: config if (currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)) { if (isStartCountersign) { //依据会签策略创建会签下一级节点 nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken); } else { //创建普通节点(根据配置) nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo, EWorkflowTraceType.Normal, expiredTime, cancellationToken); } } else { if (dto.BackToCountersignEnd) { // csStartStep.prev var csStartStep = GetRealCsStartHandleStep(workflow.Steps, currentStep.CountersignStartStepId); if (csStartStep is null) throw new UserFriendlyException("未查询到会签节点"); nextSteps = await CreateCsEndStepsByTargetPrevAsync(workflow, csStartStep, dto, expiredTime, cancellationToken); } else { //依据会签策略创建会签下一级节点 nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken); } } } else { if (dto.BackToCountersignEnd) { // check if cs all complete, create next nextSteps = await CreateCsEndStepsByTargetPrevAsync(workflow, currentStep, dto, expiredTime, cancellationToken); } else { //依据会签策略创建会签下一级节点 nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken); } } } else if (isStartCountersign) //top { //依据会签策略创建会签下一级节点 nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken); } else if (isNextDynamic) { //创建动态下一级节点 nextSteps = await CreateDynamicStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo, expiredTime, cancellationToken); } else { //创建普通节点(根据配置) nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo, EWorkflowTraceType.Normal, expiredTime, cancellationToken); } return nextSteps; } /// /// 查询当前会签开始的第一级办理节点(会签汇总再次发起会签得场景) /// private static WorkflowStep? GetRealCsStartHandleStep(List steps, string countersignStartStepId) { if (string.IsNullOrEmpty(countersignStartStepId)) throw new UserFriendlyException("会签汇总节点未正确赋值会签开始节点编号"); var csStartStep = steps.FirstOrDefault(d => d.Id == countersignStartStepId); if (csStartStep.IsCountersignEndStep) csStartStep = GetRealCsStartHandleStep(steps, csStartStep.CountersignStartStepId); return csStartStep; } private async Task> CreateDynamicStepsAsync( Workflow workflow, StepDefine nextStepDefine, WorkflowStep prevStep, BasicWorkflowDto dto, FlowAssignInfo flowAssignInfo, DateTime? expiredTime, CancellationToken cancellationToken) { var handlerType = nextStepDefine.InstancePolicy switch { EDynamicPolicy.OrgUpCenterTop => EHandlerType.OrgLevel, EDynamicPolicy.OrgUp => EHandlerType.OrgLevel, EDynamicPolicy.OrgDownCenterTop => EHandlerType.OrgLevel, EDynamicPolicy.OrgDown => EHandlerType.OrgLevel, null => throw new ArgumentOutOfRangeException(), _ => throw new ArgumentOutOfRangeException() }; return await CreateStepsAsync(workflow, nextStepDefine, prevStep, dto, flowAssignInfo.FlowAssignType, dto.NextHandlers, null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, false, EWorkflowTraceType.Normal, handlerType, expiredTime, cancellationToken: cancellationToken); } private Task> CreateCountersignStepsAsync( Workflow workflow, StepDefine stepDefine, WorkflowStep prevStep, BasicWorkflowDto dto, EFlowAssignType flowAssignType, DateTime? expiredTime, bool isStartCountersign, CancellationToken cancellationToken = default ) { //var countersignId = dto.IsStartCountersign ? prevStep.StartCountersignId : prevStep.CountersignId; var countersignId = prevStep.StartCountersignId; var handlerType = stepDefine.CountersignPolicy switch { EDynamicPolicyCountersign.OrgUpCenterTop => EHandlerType.OrgLevel, EDynamicPolicyCountersign.OrgUp => EHandlerType.OrgLevel, EDynamicPolicyCountersign.OrgDownCenterTop => EHandlerType.OrgLevel, EDynamicPolicyCountersign.OrgDown => EHandlerType.OrgLevel, null => throw new ArgumentOutOfRangeException(), _ => throw new ArgumentOutOfRangeException() }; var nextStepCountersignPosition = dto.NextHandlers.Count > 1 ? ECountersignPosition.Multi : ECountersignPosition.Single; return CreateStepsAsync(workflow, stepDefine, prevStep, dto, flowAssignType, dto.NextHandlers, countersignId, EWorkflowStepStatus.WaitForAccept, nextStepCountersignPosition, false, EWorkflowTraceType.Normal, handlerType, expiredTime, cancellationToken: cancellationToken); } /// /// 根据传入节点的上一节点创建会签汇总节点(汇总传入节点的前一节点) /// private async Task> CreateCsEndStepsByTargetPrevAsync(Workflow workflow, WorkflowStep step, BasicWorkflowDto dto, DateTime? expiredTime, CancellationToken cancellationToken) { var countersignStartStep = workflow.Steps.FirstOrDefault(d => d.Id == step.PrevStepId); if (countersignStartStep is null) throw new UserFriendlyException("未查询到当前节点上级节点"); var nextSteps = new List(); //会签未全部办理则不创建汇总节点 var csInnerSteps = workflow.Steps.Where(d => d.PrevStepId == countersignStartStep.Id).ToList(); if (csInnerSteps.Any(d => d.Status != EWorkflowStepStatus.Handled || (d.IsStartCountersign && !d.IsStartedCountersignEnd))) return nextSteps; //if (csInnerSteps.All(d => d.Status == EWorkflowStepStatus.Handled)) //{ // 创建会签汇总节点 var countersignEndStep = await CreateCountersignEndStepAsync(workflow, countersignStartStep, dto, expiredTime, cancellationToken); nextSteps = new List { countersignEndStep }; //create trace await CreateTraceAsync(workflow, countersignEndStep, EWorkflowTraceType.Normal, cancellationToken); await _publisher.PublishAsync(new CountersignEndAssigned(workflow), PublishStrategy.ParallelWhenAll, cancellationToken); //} return nextSteps; } private async Task CreateCountersignEndStepAsync( Workflow workflow, WorkflowStep countersignStartStep, BasicWorkflowDto dto, DateTime? expiredTime, CancellationToken cancellationToken = default) { 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.End; //csEndStep.CountersignSteps = new(); csEndStep.IsCountersignEndStep = true; csEndStep.CountersignStartStepId = countersignStartStep.Id; csEndStep.Name = dto.NextStepName; //csEndStep.TimeLimit = GetTimeLimit(""); csEndStep.StepExpiredTime = expiredTime; csEndStep.BusinessType = dto.BusinessType; csEndStep.Handlers = countersignStartStep.Handlers .Where(d => d.Key == countersignStartStep.HandlerId || d.Key == countersignStartStep.HandlerOrgId) .ToList(); //需求调整:汇总节点指派给发起人部门办理 csEndStep.FlowAssignType = EFlowAssignType.Org; //csEndStep.StepHandlers = stepHandlers; csEndStep.Reset(); csEndStep.ResetParameters(); await _workflowStepRepository.AddAsync(csEndStep, cancellationToken); workflow.Steps.Add(csEndStep); //await _workflowStepRepository.AddNav(csEndStep) // .Include(d => d.StepHandlers) // .ExecuteCommandAsync(); return csEndStep; } 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 !step.IsCountersignEndStep && dto.BackToCountersignEnd; } else { return step.StepType is EStepType.Normal && nextStepDefine.StepType is EStepType.Summary or EStepType.End; } } /// /// 办理节点(赋值节点的办理对象信息) /// private void HandleStep(ISessionContext current, WorkflowStep step, string opinion, string nextStepCode) { step.Handle(current.RequiredUserId, current.UserName, current.RequiredOrgId, current.OrgName, current.OrgAreaCode, current.OrgAreaName, current.OrgIsCenter, opinion, nextStepCode); //var handler = step.FindActualHandler(current.Roles, current.RequiredUserId, current.RequiredOrgId); //if (handler is not null) // handler.IsActualHandler = true; } /// /// 开始会签(创建会签数据,更新currentStep会签数据) /// private async Task StartCountersignAsync(ISessionContext current, Workflow workflow, WorkflowStep startStep, BasicWorkflowDto dto, EFlowAssignType? flowAssignType, ECounterSignType? counterSignType, DateTime? expiredTime, CancellationToken cancellationToken) { var countersign = await CreateCountersignAsync(current, workflow, startStep, dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), flowAssignType, counterSignType, expiredTime, 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 targetStep) { var isFromOrg = sourceStepBox.IsOrg(); if (!isFromOrg) return false; var isToCenter = targetStep.IsCenter(); return isFromOrg && isToCenter; } /// /// 检查是否从部门流转至中心 /// private bool CheckIfFlowFromOrgToCenter(Workflow workflow, WorkflowStep targetStep) { var isToCenter = targetStep.IsCenter(); if (!isToCenter) return false; var isFromOrg = workflow.Steps.Any(d => d.BusinessType is EBusinessType.Department); return isFromOrg && isToCenter; } /// /// 复制一个节点为待接办 /// private async Task DuplicateStepWithTraceAsync(Workflow workflow, WorkflowStep step, EWorkflowTraceType traceType, CancellationToken cancellationToken) { var newStep = _mapper.Map(step); newStep.Reset(); newStep.Status = EWorkflowStepStatus.WaitForAccept; newStep.PrevStepId = step.PrevStepId; newStep.IsMain = step.IsMain; newStep.IsOrigin = step.IsOrigin; //newStep.ParentId = step.ParentId; newStep.Handlers = step.Handlers; //newStep.StepHandlers = _mapper.Map>(step.StepHandlers); newStep.StartCountersignId = step.StartCountersignId; newStep.CountersignId = step.CountersignId; newStep.IsStartedCountersignEnd = step.IsStartedCountersignEnd; //退回场景:指派给原办理人,其余场景:按照原节点原始指派方式复制 if (traceType is EWorkflowTraceType.Previous) { //newStep.FlowAssignType = EFlowAssignType.User; // 是否中心 临时紧急修改 后续在流程模版定义是否原办理人退回类型 来实现流程 禅道200 newStep.FlowAssignType = step.HandlerOrgIsCenter!.Value ? step.BusinessType is EBusinessType.Send ? EFlowAssignType.User : EFlowAssignType.Role : EFlowAssignType.Org; //if (newStep is { FlowAssignType: EFlowAssignType.Role, BusinessType: EBusinessType.Send }) // newStep.FlowAssignType = EFlowAssignType.User; newStep.Assign(step.HandlerId, step.HandlerName, step.HandlerOrgId, step.HandlerOrgName, step.RoleId, step.RoleName); } await _workflowStepRepository.AddAsync(newStep, cancellationToken); //await _workflowStepRepository.AddNav(newStep) // .Include(d => d.StepHandlers) // .ExecuteCommandAsync(); await CreateTraceAsync(workflow, newStep, traceType, cancellationToken); return newStep; } private async Task CreateCountersignAsync( ISessionContext current, Workflow workflow, WorkflowStep startStep, List handlers, EFlowAssignType? flowAssignType, ECounterSignType? counterSignType, DateTime? expiredTime, string? parentId = null, CancellationToken cancellationToken = default) { var members = handlers.Select(d => new WorkflowCountersignMember { Key = d.Key, Value = d.Value, FlowAssignType = flowAssignType }).ToList(); var countersign = new WorkflowCountersign { WorkflowId = workflow.Id, StartStepId = startStep.Id, StartStepCode = startStep.Code, StartStepBusiType = startStep.BusinessType, StarterId = current.RequiredUserId, StarterName = current.UserName ?? string.Empty, StarterOrgId = current.RequiredOrgId, StarterOrgName = current.OrgName, StarterOrgAreaCode = current.OrgAreaCode ?? string.Empty, StarterOrgAreaName = current.OrgAreaName ?? string.Empty, ParentId = parentId, Members = members, FlowAssignType = flowAssignType, CounterSignType = counterSignType, ExpiredTime = expiredTime, //ExternalId = workflow.ExternalId, }; //await _workflowCountersignRepository.AddAsync(countersign, cancellationToken); await _workflowCountersignRepository.AddNav(countersign) .Include(d => d.Members) .ExecuteCommandAsync(); 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 async Task RecallTraceAsync(List traces, string opinion, ISessionContext current, CancellationToken cancellationToken) { //未办理的traces //var uncompleteTraces = // await _workflowTraceRepository.QueryAsync(d => // d.WorkflowId == workflowId && string.IsNullOrEmpty(d.HandlerId)); var uncompleteTraces = traces.Where(d => d.Status != EWorkflowStepStatus.Handled).ToList(); if (uncompleteTraces.Any()) { foreach (var trace in uncompleteTraces) { trace.Handle( current.RequiredUserId, current.UserName, current.RequiredOrgId, current.OrgName, current.OrgAreaCode, current.OrgAreaName, current.OrgIsCenter, opinion); } await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken); } } private async Task PreviousTraceAsync(string workflowId, PreviousWorkflowDto dto, WorkflowStep step, string applicantId, string applicantName, string applicantOrgId, string applicantOrgName, string applicantOrgAreaCode, string applicantOrgAreaName, bool applicantIsCenter, CancellationToken cancellationToken) { var trace = await GetWorkflowTraceAsync(workflowId, step.Id, cancellationToken); _mapper.Map(dto, trace); //HandleTrace(trace, dto.Opinion, current); trace.Handle(applicantId, applicantName, applicantOrgId, applicantOrgName, applicantOrgAreaCode, applicantOrgAreaName, applicantIsCenter, 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); var trace = workflow.Traces.FirstOrDefault(d => d.Id == step.Id); if (trace == null) throw new UserFriendlyException($"未找到对应trace, workflowId: {workflow.Id}, stepId: {step.Id}"); _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, EWorkflowTraceType traceType = EWorkflowTraceType.Normal, CancellationToken cancellationToken = default) { var sendHandleTimes = 0; if (step.BusinessType == EBusinessType.Send) { var sendHandleCount = workflow.Traces.Count(d => d.StepType == EStepType.Normal && d.BusinessType == EBusinessType.Send); sendHandleTimes = sendHandleCount + 1; } var trace = _mapper.Map(step); trace.TraceType = traceType; trace.SendHandleTimes = sendHandleTimes; 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.Multi) //{ // var prevTrace = await GetWorkflowTraceAsync(workflow.Id, step.PrevStepId, cancellationToken); // trace.ParentId = prevTrace.Id; //} //else if (step.CountersignPosition is ECountersignPosition.Single) //{ // var prevTrace = await GetWorkflowTraceAsync(workflow.Id, step.PrevStepId, cancellationToken); // trace.ParentId = prevTrace.ParentId; //} var prevTrace = await GetWorkflowTraceAsync(workflow.Id, step.PrevStepId, cancellationToken); trace.ParentId = prevTrace.Id; } } await _workflowTraceRepository.AddAsync(trace, cancellationToken); workflow.Traces.Add(trace); } 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, EWorkflowTraceType traceType, DateTime? expiredTime, bool isOrderFiled, CancellationToken cancellationToken) { var targetIsStartStep = targetStepDefine.StepType is EStepType.Start; var updateTraces = new List(); //update uncomplete traces var uncompleteTraces = workflow.Traces.Where(d => d.Status != EWorkflowStepStatus.Handled).ToList(); if (uncompleteTraces.Any()) { foreach (var trace in uncompleteTraces) { trace.Handle( _sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId, _sessionContext.OrgName, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName, _sessionContext.OrgIsCenter, dto.Opinion); } //await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken); updateTraces.AddRange(uncompleteTraces); } else { var endTrace = workflow.Traces.Where(d => d.StepType == EStepType.End).MaxBy(d => d.CreationTime); if (endTrace is not null) { endTrace.Opinion += ("\r\n" + dto.Opinion); updateTraces.Add(endTrace); } } //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); //await _workflowStepRepository.RemoveNav(removeSteps) // .Include(d => d.StepHandlers) // .ExecuteCommandAsync(); workflow.Steps.RemoveAll(d => removeSteps.Contains(d)); //更新快照对应节点状态 var stepIds = removeSteps.Select(d => d.Id).ToList(); var traces = workflow.Traces.Where(d => stepIds.Contains(d.StepId)).ToList(); //await UpdateTracesStateAsync(updateTraces, EWorkflowTraceState.StepRemoveByRecall, cancellationToken); foreach (var trace in traces) { trace.TraceState = isOrderFiled ? EWorkflowTraceState.StepRemoveByRecallWhenFiled : EWorkflowTraceState.StepRemoveByRecall; } updateTraces.AddRange(traces); } await _workflowTraceRepository.UpdateRangeAsync(updateTraces, cancellationToken); workflow.EndCountersign(); workflow.ResetOption(); if (workflow.Status is EWorkflowStatus.Completed) workflow.SetStatusRunnable(); var targetStepNew = targetIsStartStep ? await CreateStartStepAsync(workflow, targetStepDefine, dto, dto.NextHandlers.First(), traceType, expiredTime, EFlowAssignType.Org, cancellationToken) : (await CreateStepsAsync(workflow, targetStepDefine, targetPrevStep, dto, flowAssignInfo.FlowAssignType, dto.NextHandlers, null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceType, null, expiredTime, cancellationToken: cancellationToken)).First(); //更新实际办理节点信息 workflow.UpdateActualStepWhenAssign(targetStepNew, new FlowStepHandler { UserId = targetStep.HandlerId, Username = targetStep.HandlerName, OrgId = targetStep.HandlerOrgId, OrgName = targetStep.HandlerOrgName }); workflow.UpdateCurrentStepWhenAssign(targetStepNew, new FlowStepHandler { UserId = targetStep.HandlerId, Username = targetStep.HandlerName, OrgId = targetStep.HandlerOrgId, OrgName = targetStep.HandlerOrgName }); workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds()); workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects); //calc workflow expired time var isOrgToCenter = CheckIfFlowFromOrgToCenter(workflow, targetStep); return isOrgToCenter; } private List GetStepsBehindTargetStep(List steps, WorkflowStep targetStep) { var behindSteps = new List { targetStep }; if (!steps.Any()) return behindSteps; var nextSteps = targetStep.IsStartCountersign ? steps.Where(d => d.CountersignId == targetStep.StartCountersignId).ToList() : steps.Where(d => d.PrevStepId == targetStep.Id).ToList(); //var nextSteps = steps.Where(d => d.PrevStepId == targetStep.Id).ToList(); if (!nextSteps.Any()) return behindSteps; foreach (var nextStep in nextSteps) { var leftSteps = steps.Except(behindSteps).ToList(); behindSteps.AddRange(GetStepsBehindTargetStep(leftSteps, nextStep)); } return behindSteps; } private static void CheckWhetherRunnable(EWorkflowStatus status) { if (status != EWorkflowStatus.Runnable) throw UserFriendlyException.SameMessage("当前流程状态不可继续流转"); } private void ValidatePermission(Workflow workflow, string OrgId, string UserId, string[] roleIds) { if (!workflow.IsCanHandle(UserId, OrgId, roleIds)) throw UserFriendlyException.SameMessage("无办理权限"); } private async Task CreateEndStepAsync( ISessionContext current, Workflow workflow, StepDefine endStepDefine, WorkflowStep prevStep, DateTime? expiredTime, CancellationToken cancellationToken) { if (workflow.Steps.Any(d => d.StepType == EStepType.End)) throw UserFriendlyException.SameMessage("无法重复创建结束节点"); var handler = new FlowStepHandler { Key = current.UserId, Value = current.UserName, UserId = current.RequiredUserId, Username = current.UserName, OrgId = current.OrgId, OrgName = current.OrgName, }; var step = CreateStep(workflow, endStepDefine, prevStep, EFlowAssignType.User, handler, null, null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, expiredTime, endStepDefine.Name, true); //step.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName, // _sessionContext.RequiredOrgId, _sessionContext.OrgName, // _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName); HandleStep(current, step, "流程归档", string.Empty); await _workflowStepRepository.AddAsync(step, cancellationToken); workflow.Steps.Add(step); //end trace await CreateTraceAsync(workflow, step, cancellationToken: cancellationToken); return step; } public async Task> CreateConfigStepsAsync( Workflow workflow, StepDefine stepDefine, WorkflowStep prevStep, BasicWorkflowDto dto, FlowAssignInfo flowAssignInfo, EWorkflowTraceType traceType, DateTime? expiredTime, CancellationToken cancellationToken) { List handlers; if (stepDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any()) throw new UserFriendlyException("未指定节点处理者"); if (stepDefine.HandlerType == EHandlerType.Role && !dto.NextHandlers.Any()) { var handler = stepDefine.HandlerTypeItems.First(); handlers = new List { new() { Key = handler.Key, Value = handler.Value, RoleId = handler.Key, RoleName = handler.Value } }; } else { handlers = dto.NextHandlers; } return await CreateStepsAsync(workflow, stepDefine, prevStep, dto, /*dto.IsStartCountersign,*/ flowAssignInfo.FlowAssignType, handlers, null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceType, null, expiredTime, cancellationToken); } private async Task> CreateStepsAsync( Workflow workflow, StepDefine stepDefine, WorkflowStep prevStep, BasicWorkflowDto dto, //bool isStartCountersign, EFlowAssignType? flowAssignType, List handlers, string? countersignId, EWorkflowStepStatus stepStatus, ECountersignPosition csPosition, bool isOrigin, EWorkflowTraceType traceType, EHandlerType? handlerType = null, DateTime? expiredTime = null, CancellationToken cancellationToken = default ) { List steps = new(); foreach (var handler in handlers) { var isMain = handlers.Count == 1 || (handlers.Count > 1 && handler.Key == dto.NextMainHandler); var step = CreateStep(workflow, stepDefine, prevStep, flowAssignType, handler, dto.NextStepCode, countersignId, stepStatus, csPosition, expiredTime, dto.NextStepName, isOrigin, isMain, handlerType, dto.BusinessType); //var stepHandler = stepHandlers.First(d => d.GetHandler().Key == handler.Key); //step.StepHandlers = new List { stepHandler }; steps.Add(step); } await _workflowStepRepository.AddRangeAsync(steps, cancellationToken); workflow.Steps.AddRange(steps); //await _workflowStepRepository.AddNav(steps) // .Include(d => d.StepHandlers) // .ExecuteCommandAsync(); //create traces todo add range traces foreach (var step in steps) { await CreateTraceAsync(workflow, step, traceType, cancellationToken); } return steps; } /// /// 查询未完成节点 /// public WorkflowStep GetUnHandleStep(List steps, string orgId, string userId, string[] roleIds) { //var step = GetStep(steps, orgCode, userId, d => d != EWorkflowStepStatus.Handled); var step = steps.FirstOrDefault(d => d.IsCanHandle(userId, orgId, roleIds)); if (step == null) throw new UserFriendlyException( $"未找到对应节点, workflowId: {steps.FirstOrDefault()?.WorkflowId} orgCode:{orgId}, userId: {userId}, roleIds: {string.Join(',', roleIds)}", "未找到对应节点"); return step; } /// /// 检查当前办理节点是否为开始节点 /// /// /// 当前办理人Id /// 当前办理人orgId /// /// public async Task CheckCurrentIsStartStepAsync(string workflowId, string userId, string orgId, CancellationToken cancellationToken) { var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellationToken); var currentStep = GetStep(workflow.Steps, orgId, userId, d => d != EWorkflowStepStatus.Handled); if (currentStep is null) return false; return workflow.Steps.Count == 1 && currentStep.StepType is EStepType.Start && currentStep.IsOrigin; } /// /// 检查动态节点是否该终止 /// public bool DynamicShouldTerminal(StepDefine currentStepDefine, int currentOrgLevel) { if (currentStepDefine.InstanceMode is not EInstanceMode.Dynamic) throw new UserFriendlyException("非动态节点"); switch (currentStepDefine.InstancePolicy) { case EDynamicPolicy.OrgUpCenterTop: case EDynamicPolicy.OrgUp: if (!int.TryParse(currentStepDefine.TerminalDynamicMark, out var tMark)) throw new UserFriendlyException( $"TerminalDynamicMark parse to int failed, tMark: {currentStepDefine.TerminalDynamicMark}"); return currentOrgLevel <= tMark; case EDynamicPolicy.OrgDownCenterTop: case EDynamicPolicy.OrgDown: if (!int.TryParse(currentStepDefine.TerminalDynamicMark, out var tMark1)) throw new UserFriendlyException( $"TerminalDynamicMark parse to int failed, tMark: {currentStepDefine.TerminalDynamicMark}"); return currentOrgLevel >= tMark1; default: throw new ArgumentOutOfRangeException(); } } /// /// 终止会签 /// /// /// /// public async Task TerminalCountersignAsync(string countersignId, CancellationToken cancellationToken) { var countersign = await _workflowCountersignRepository.GetAsync(countersignId, cancellationToken); if (countersign is null) throw new UserFriendlyException("无效会签编号"); //1. 检查会签是否已结束 t: return 2.检查是否有嵌套会签 t: 一起结束 3.结束会签 4.trace 5.检查workflow会签状态,如果会签全结束需更新状态 6.cp会签发起节点变为待办节点 if (countersign.IsCompleted()) throw new UserFriendlyException("该会签已结束"); var workflow = await GetWorkflowAsync(countersign.WorkflowId, withSteps: true, withTraces: true, withCountersigns: true, cancellationToken: cancellationToken); if (!workflow.IsInCountersign) throw new UserFriendlyException("该流程未处于会签中"); countersign = workflow.Countersigns.First(d => d.Id == countersignId); var startCountersignStep = workflow.Steps.Find(d => d.StartCountersignId == countersignId); if (startCountersignStep is null) throw new UserFriendlyException("未查询到发起会签节点"); if (startCountersignStep.IsStartedCountersignEnd) throw new UserFriendlyException("该会签已汇总"); var updateCountersigns = new List(); EndCountersignWithCascade(countersign, workflow.Countersigns, startCountersignStep.BusinessType, ref updateCountersigns); if (updateCountersigns.Any()) { var updateSteps = new List(); var updateTraces = new List(); HandleStepsByTerminalCs(startCountersignStep, workflow.Steps, workflow.Traces, ref updateSteps, ref updateTraces); if (updateSteps.Any()) await _workflowStepRepository.RemoveRangeAsync(updateSteps, cancellationToken); //await _workflowStepRepository.RemoveNav(updateSteps) // .Include(d => d.StepHandlers) // .ExecuteCommandAsync(); if (updateTraces.Any()) await _workflowTraceRepository.UpdateRangeAsync(updateTraces, cancellationToken); await _workflowCountersignRepository.UpdateRangeAsync(updateCountersigns, cancellationToken); //cp会签发起节点变为待办节点 //1. create terminal trace 2. 撤回至startStep var newStep = await DuplicateStepWithTraceAsync(workflow, startCountersignStep, EWorkflowTraceType.Normal, cancellationToken); //当topcsStep结束cs时,实际办理节点应该更新为newStep if (startCountersignStep.Id == workflow.TopCountersignStepId) { workflow.UpdateActualStepWhenAssign(newStep, new FlowStepHandler { UserId = startCountersignStep.HandlerId, Username = startCountersignStep.HandlerName, OrgId = startCountersignStep.HandlerOrgId, OrgName = startCountersignStep.HandlerOrgName }); workflow.UpdateCurrentStepWhenAssign(newStep, new FlowStepHandler { UserId = startCountersignStep.HandlerId, Username = startCountersignStep.HandlerName, OrgId = startCountersignStep.HandlerOrgId, OrgName = startCountersignStep.HandlerOrgName }); } //csEndStep又开启了cs,在结束会签时,如果该节点是topcs的end节点, workflow.topcsStep应该更新为前一cs开启stepId if (startCountersignStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)) workflow.TopCountersignStepId = startCountersignStep.CountersignStartStepId; if (workflow.CheckIfCountersignOver()) workflow.EndCountersign(); var removeHandlers = updateSteps.SelectMany(d => d.Handlers).Select(d => d.Key).ToList(); var handlerObjs = newStep.Handlers.Select(d => new HandlerGroupItem { GroupId = Guid.NewGuid().ToString(), Key = d.Key, Value = d.Value }).ToList(); workflow.UpdateHandlers(removeHandlers, newStep.FlowAssignType.Value, handlerObjs); await _workflowRepository.UpdateAsync(workflow, cancellationToken); } return workflow; } private void HandleStepsByTerminalCs(WorkflowStep step, List steps, List traces, ref List updateSteps, ref List updateTraces) { if (step.IsStartCountersign) { var countersignSteps = steps.Where(d => d.CountersignId == step.StartCountersignId).ToList(); if (countersignSteps.Any()) { foreach (var countersignStep in countersignSteps) { HandleStepsByTerminalCs(countersignStep, steps, traces, ref updateSteps, ref updateTraces); } } } EndStepByTerminalCs(step, traces, ref updateSteps, ref updateTraces); } private void EndStepByTerminalCs(WorkflowStep step, List traces, ref List updateSteps, ref List updateTraces) { var isHandled = step.Status is EWorkflowStepStatus.Handled; var opinion = $"会签未办理完成,由 {_sessionContext.OrgName} 的 {_sessionContext.UserName} 终止办理"; if (step.IsStartCountersign) step.CountersignEnd(); if (step.Status is not EWorkflowStepStatus.Handled) { step.Handle(_sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId, _sessionContext.OrgName, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName, _sessionContext.OrgIsCenter, opinion); } updateSteps.Add(step); if (isHandled) return; var trace = traces.FirstOrDefault(d => d.StepId == step.Id); if (trace != null) { _mapper.Map(step, trace); updateTraces.Add(trace); } } /// /// 结束会签(包含子项) /// /// /// private void EndCountersignWithCascade(WorkflowCountersign countersign, List countersigns, EBusinessType businessType, ref List updateCountersigns) { if (countersign is null) return; var childCountersigns = countersigns.Where(d => d.ParentId == countersign.Id).ToList(); if (childCountersigns.Any()) { foreach (var childCountersign in childCountersigns) { EndCountersignWithCascade(childCountersign, countersigns, businessType, ref updateCountersigns); } } EndCountersign(countersign, countersigns, businessType, ref updateCountersigns); } private void EndCountersign(WorkflowCountersign countersign, List countersigns, EBusinessType businessType, ref List updateCountersigns) { //todo 1. trace? 先确定展现形式 2. end cs countersign.End(null, null, businessType, _sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId, _sessionContext.OrgName, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName); /* * //结束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); */ updateCountersigns.Add(countersign); } 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( Workflow workflow, StepDefine stepDefine, WorkflowStep prevStep, EFlowAssignType? flowAssignType, FlowStepHandler handler, string nextStepCode, string? countersignId, EWorkflowStepStatus stepStatus, ECountersignPosition countersignPosition, DateTime? expiredTime, string stepName, bool isOrigin, bool isMainHandler = false, EHandlerType? handlerType = null, //动态节点依据动态策略判断 EBusinessType? businessType = null ) { //if (!handlers.Any()) // throw new UserFriendlyException($"非法参数, handlers为空, method: {nameof(CreateStep)}"); var step = _mapper.Map(stepDefine); _mapper.Map(workflow, step); step.FlowAssignType = flowAssignType; step.Handlers = new List { new(handler.Key, handler.Value) }; //step.StepHandlers = stepHandlers; step.NextStepCode = step.StepType is EStepType.End ? string.Empty : nextStepCode; step.IsMain = isMainHandler; 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; //新增需求: 部门汇总节点由部门办理//todo 待确认中心由部门处理还是由之前办理人办理 if (step.StepType == EStepType.Summary && step.BusinessType == EBusinessType.Department) step.FlowAssignType = EFlowAssignType.Org; step.Assign(handler.UserId, handler.Username, handler.OrgId, handler.OrgName, handler.RoleId, handler.RoleName); if (handlerType.HasValue) step.HandlerType = handlerType.Value; if (businessType.HasValue) step.BusinessType = businessType.Value; return step; } #endregion } }