using Hotline.Caching.Interfaces; 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 Microsoft.Extensions.Logging; using SqlSugar; 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.Diagnostics; using System.Text; using Hotline.Share.Dtos.File; namespace Hotline.FlowEngine.Workflows { public class WorkflowDomainService : IWorkflowDomainService, IScopeDependency { private readonly IWorkflowRepository _workflowRepository; private readonly IRepository _workflowStepRepository; private readonly IRepository _workflowTraceRepository; private readonly IRepository _workflowCountersignRepository; private readonly IMapper _mapper; private readonly Publisher _publisher; private readonly ILogger _logger; private readonly IFileRepository _fileRepository; private readonly IRepository _userRepository; private readonly ISystemSettingCacheManager _systemSettingCacheManager; private readonly IWfModuleCacheManager _wfModuleCacheManager; private readonly ISessionContextProvider _sessionContextProvider; public WorkflowDomainService( IWorkflowRepository workflowRepository, IRepository workflowStepRepository, IRepository workflowTraceRepository, IRepository workflowCountersignRepository, ISystemSettingCacheManager systemSettingCacheManager, IWfModuleCacheManager wfModuleCacheManager, ISessionContextProvider sessionContextProvider, IMapper mapper, Publisher publisher, ILogger logger, IFileRepository fileRepository) { _workflowRepository = workflowRepository; _workflowStepRepository = workflowStepRepository; _workflowTraceRepository = workflowTraceRepository; _workflowCountersignRepository = workflowCountersignRepository; _mapper = mapper; _publisher = publisher; _logger = logger; _fileRepository = fileRepository; _systemSettingCacheManager = systemSettingCacheManager; _wfModuleCacheManager = wfModuleCacheManager; _sessionContextProvider = sessionContextProvider; } 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, CancellationToken cancellationToken) { if (firstStepDefine.StepType is EStepType.End) { await _publisher.PublishAsync( new StartWorkflowNotify(workflow, dto, flowAssignInfo.FlowAssignType, startStep.WorkflowTrace), PublishStrategy.ParallelWhenAll, cancellationToken); //firstStep是否为end,t: 实际办理节点为startStep, 并且handlerId赋值 f: 实际办理节点为firstStep, handlerId未赋值 workflow.UpdateActualStepWhenHandle(startStep, _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel); workflow.UpdateCurrentStepWhenHandle(startStep, _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel); var endTrace = await EndAsync(_sessionContextProvider.SessionContext, workflow, dto, firstStepDefine, startStep, expiredTime, cancellationToken); return; } //firststeps var firstSteps = await CreateNextStepsAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic, flowAssignInfo.FlowAssignType, expiredTime, dto.IsStartCountersign, cancellationToken: cancellationToken); await _workflowStepRepository.UpdateAsync(startStep, cancellationToken); //handle trace var trace = await NextTraceAsync(workflow, dto, startStep, cancellationToken); //指派实际办理节点 UpdateActualStep(workflow, dto, firstStepDefine, firstSteps); //更新实际办理节点 UpdateCurrentStep(workflow, dto, firstStepDefine, firstSteps); //发起会签时记录顶层会签节点(必须在update currentStep之后) if (dto.IsStartCountersign && !workflow.IsInCountersign) workflow.StartCountersign(startStep.Id, counterSignType); await _workflowRepository.UpdateAsync(workflow, cancellationToken); //publish await _publisher.PublishAsync(new StartWorkflowNotify(workflow, dto, flowAssignInfo.FlowAssignType, trace), PublishStrategy.ParallelWhenAll, cancellationToken); } /// /// new(开启流程并停留在开始节点,开始节点为待办节点,指派给当前操作人) /// public async Task<(Workflow, WorkflowStep)> StartAsync(StartWorkflowDto dto, string externalId, DateTime? expiredTime, CancellationToken cancellationToken = default) { // var validator = new StartWorkflowDtoValidator(); // var validResult = await validator.ValidateAsync(dto, cancellationToken); // if (!validResult.IsValid) // throw new UserFriendlyException( // $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}"); var wfModule = await GetWorkflowModuleAsync(dto.DefinitionModuleCode, cancellationToken); var definition = wfModule.Definition; if (definition == null) throw new UserFriendlyException("无效模板编码"); if (definition.Status is not EDefinitionStatus.Enable) throw new UserFriendlyException("该模板不可用"); //如果发起会签需检查是否支持发起会签 var startStepDefine = definition.FindStartStepDefine(); //下一节点是否为动态节点 var isNextDynamic = startStepDefine.InstanceMode is EInstanceMode.Dynamic && !DynamicShouldTerminal(startStepDefine, _sessionContextProvider.SessionContext.OrgLevel); var firstStepDefine = isNextDynamic ? startStepDefine : definition.FindStepDefine(dto.NextStepCode); if (firstStepDefine is null) throw new UserFriendlyException("未查询到下一步节点配置"); //1. 如果不是按角色指派,handlers必填 2. 如果按角色指派,handlers可以不选 if (firstStepDefine.HandlerType is not EHandlerType.Role && !dto.NextHandlers.Any()) throw UserFriendlyException.SameMessage("未指派办理人"); if (dto.IsStartCountersign) { if (!startStepDefine.CanStartCountersign) throw new UserFriendlyException("当前节点不支持发起会签"); //if (startStepDefine.HandlerType is EHandlerType.Role) // throw new UserFriendlyException("当前节点不支持发起会签"); //即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签 if (firstStepDefine.StepType is EStepType.Summary or EStepType.End) throw new UserFriendlyException("下一节点不允许发起会签"); //下一节点是会签汇总节点也不允许发起会签 if (dto.BackToCountersignEnd) throw new UserFriendlyException("下一节点不允许发起会签"); } var workflow = CreateWorkflow(wfModule, dto.Title, externalId); var defineHandler = startStepDefine.HandlerTypeItems.First(); //todo 重构为流程开放策略参数,业务决定 var handler = new FlowStepHandler { Key = _sessionContextProvider.SessionContext.RequiredUserId, Value = _sessionContextProvider.SessionContext.UserName, UserId = _sessionContextProvider.SessionContext.UserId, Username = _sessionContextProvider.SessionContext.UserName, OrgId = _sessionContextProvider.SessionContext.RequiredOrgId, OrgName = _sessionContextProvider.SessionContext.OrgName, RoleId = defineHandler.Key, RoleName = defineHandler.Value, }; var startStep = CreateStartStep(workflow, startStepDefine, dto, handler, expiredTime); if (dto.Files.Any()) startStep.FileJson = await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId, startStep.Id, cancellationToken); await _workflowStepRepository.AddAsync(startStep, cancellationToken); workflow.Steps.Add(startStep); //starttrace var startTrace = _mapper.Map(startStep); startTrace.StepId = startStep.Id; startTrace.TraceType = EWorkflowTraceType.Normal; await _workflowTraceRepository.AddAsync(startTrace, cancellationToken); workflow.Traces.Add(startTrace); startStep.WorkflowTrace = startTrace; //更新受理人信息 workflow.UpdateAcceptor( _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName, _sessionContextProvider.SessionContext.StaffNo, _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName); workflow.UpdateActualStepWhenAssign(startStep, handler); workflow.UpdateCurrentStepWhenAssign(startStep, handler); await _workflowRepository.AddAsync(workflow, cancellationToken); var flowAssignInfo = await GetNextStepFlowAssignInfoAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic, cancellationToken); //publish await _publisher.PublishAsync(new StartWorkflowNotify(workflow, dto, flowAssignInfo.FlowAssignType, startTrace), PublishStrategy.ParallelWhenAll, cancellationToken); return (workflow, startStep); } /// /// new /// public async Task> NextAsync(ISessionContext current, NextWorkflowDto dto, DateTime? expiredTime = null, bool isAutoFillSummaryOpinion = false, CancellationToken cancellationToken = default) { var workflow = await GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true, withTraces: true, withCountersigns: true, cancellationToken: cancellationToken); CheckWhetherRunnable(workflow.Status); var currentStep = workflow.Steps.FirstOrDefault(d => d.Id == dto.StepId); if (currentStep == null) throw new UserFriendlyException( $"未找到对应节点, workflowId: {dto.WorkflowId}, stepId: {dto.StepId}", "未找到对应节点"); if (currentStep.Status is EWorkflowStepStatus.Handled) throw new UserFriendlyException("该状态不支持继续办理"); //todo 校验currentStep 能否由 current 办理 var currentStepDefine = GetStepDefine(workflow.WorkflowDefinition, currentStep.Code); //下一节点是否为动态节点 var isNextDynamic = currentStepDefine.InstanceMode is EInstanceMode.Dynamic && !DynamicShouldTerminal(currentStepDefine, current.OrgLevel); StepDefine nextStepDefine; if (isNextDynamic || (workflow.IsInCountersign && currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)) || dto.IsStartCountersign) { //下一步配置为当前节点配置 nextStepDefine = currentStepDefine; } else { //下一步配置为下一步节点配置 nextStepDefine = GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode); } //普通节点往汇总节点办理时可以不选,不选的场景主动去查之前的办理对象 //todo 按照指派策略生成办理对象 if (nextStepDefine.StepType is EStepType.Summary && !dto.NextHandlers.Any()) { var handler = GetSummaryTargetFlowStepHandler(workflow, nextStepDefine.SummaryTargetCode); dto.NextHandlers.Add(handler); } //需求:按角色选择办理人可以不选,表示该角色下所有人都可以办理,同时依据配置:是否本部门人办理显示待选办理人。角色下只要一人办理即可(即:角色下不发起会签) if (nextStepDefine.HandlerType != EHandlerType.Role && nextStepDefine.StepType != EStepType.End && !dto.NextHandlers.Any()) throw new UserFriendlyException("未指定节点处理者"); if (dto.IsStartCountersign) { if (!currentStepDefine.CanStartCountersign) throw UserFriendlyException.SameMessage("当前节点不支持发起会签"); //即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签 if (nextStepDefine.StepType is EStepType.Summary or EStepType.End) throw UserFriendlyException.SameMessage("下一汇总节点不允许发起会签"); //下一节点是会签汇总节点也不允许发起会签 if (dto.BackToCountersignEnd) throw UserFriendlyException.SameMessage("下一会签汇总节点不允许发起会签"); } var flowAssignInfo = await GetNextStepFlowAssignInfoAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, cancellationToken); //todo 加入到重构获取办理对象方法中 if (nextStepDefine.StepType != EStepType.End && nextStepDefine.HandlerType == EHandlerType.Role && !dto.NextHandlers.Any()) { //todo 指派给配置的角色 flowAssignInfo.FlowAssignType = EFlowAssignType.Role; var handler = nextStepDefine.HandlerTypeItems.First(); dto.NextHandlers.Add(new FlowStepHandler { Key = handler.Key, Value = handler.Value, RoleId = handler.Key, RoleName = handler.Value }); } #region 办理当前节点 if (dto.Files != null && dto.Files.Any()) currentStep.FileJson = await _fileRepository.AddFileAsync( dto.Files, workflow.ExternalId, currentStep.Id, cancellationToken); var counterSignType = GetCounterSignType(dto.IsStartCountersign, currentStep.BusinessType); 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.UserId, current.UserName, current.OrgId, current.OrgName, current.OrgAreaCode, current.OrgAreaName); await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken); } } await HandleStepAsync(currentStep, workflow, dto, counterSignType, expiredTime, cancellationToken); currentStep.IsActualHandled = CheckIsActualHandle(workflow, currentStep, nextStepDefine, dto); _mapper.Map(dto, workflow); //会签办理节点办理时更新会签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.UserId, current.OrgId); //update cs await _workflowCountersignRepository.UpdateNav(countersign) .Include(d => d.Members) .ExecuteCommandAsync(); } } } await _workflowStepRepository.UpdateRangeAsync(updateSteps, cancellationToken); //更新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 (nextStepDefine.StepType is EStepType.End) { //更新实际办理节点信息 workflow.UpdateActualStepWhenHandle(currentStep, _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel); workflow.ActualHandleStepAcceptTime = currentStep.AcceptTime; var endTrace = await EndAsync(current, workflow, dto, nextStepDefine, currentStep, expiredTime, cancellationToken); return new List(); } var isStartCountersign = currentStep.CountersignPosition switch { ECountersignPosition.None => dto.IsStartCountersign, ECountersignPosition.Multi => !dto.BackToCountersignEnd, ECountersignPosition.Single => !dto.BackToCountersignEnd, ECountersignPosition.End => !dto.BackToCountersignEnd && (workflow.IsInCountersign || dto.IsStartCountersign), _ => throw new ArgumentOutOfRangeException() }; var currentTrace = workflow.Traces.First(d => d.Id == currentStep.Id); //创建会签数据 if (isStartCountersign) { var exists = workflow.Countersigns.Any(d => !d.IsCompleted() && !string.IsNullOrEmpty(current.UserId) && d.StarterId == current.UserId); if (exists) throw new UserFriendlyException($"该用户在当前流程存在未结束会签, workflowId: {workflow.Id}, userId: {current.UserId}"); var countersign = await StartCountersignAsync(current, workflow, currentStep, dto, flowAssignInfo.FlowAssignType, counterSignType, expiredTime, cancellationToken); currentTrace.StartCountersign(countersign.Id); await _workflowStepRepository.UpdateAsync(currentStep, cancellationToken); await _workflowTraceRepository.UpdateAsync(currentTrace, cancellationToken); } //发起会签时记录顶层会签节点 if (dto.IsStartCountersign && !workflow.IsInCountersign) workflow.StartCountersign(currentStep.Id, counterSignType); //创建下一/N个节点(会签汇总节点:会签未全部办理时不创建,最后一个会签办理节点创建会签汇总节点) var nextSteps = await CreateNextStepsAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, flowAssignInfo.FlowAssignType, expiredTime, dto.IsStartCountersign, isAutoFillSummaryOpinion, 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 (currentStep.IsActualHandled) workflow.AddCsActualHandler(current.UserId, current.OrgId); await _workflowRepository.UpdateAsync(workflow, cancellationToken); #endregion #region 流转记录 //var trace = await NextTraceAsync(workflow, dto, currentStep, cancellationToken); #endregion await _publisher.PublishAsync( new NextStepNotify(workflow, dto, flowAssignInfo, currentTrace, nextStepDefine, current.OrgId, expiredTime.HasValue), PublishStrategy.ParallelWhenAll, cancellationToken); return nextSteps; } 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 ); 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(_sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.Roles)) return null; //工单完成以后查看的场景 if (workflow.Status != EWorkflowStatus.Runnable) return null; var currentStep = GetUnHandleStep(workflow.Steps, _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.Roles); if (currentStep.Status is not EWorkflowStepStatus.WaitForAccept) return null; //if (currentStep.Handlers.All(d => d.Key != orgId && d.Key != userId)) return null; 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.UpdateNav(currentStep) .Include(d => d.WorkflowTrace) .ExecuteCommandAsync(); workflow.ActualHandleStepAcceptTime = currentStep.AcceptTime; await _workflowRepository.Updateable(workflow).ExecuteCommandAsync(cancellationToken); return workflow.ActualHandleStepAcceptTime; } /// /// 办理(流转至下一节点) /// public async Task NextAsync(Workflow workflow, WorkflowStep currentStep, NextWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, FlowAssignInfo flowAssignInfo, DateTime? expiredTime, 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, currentStep.BusinessType); 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, _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName, _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName, _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName); await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken); } } await HandleStepAsync(currentStep, workflow, dto, counterSignType, expiredTime, cancellationToken); //创建会签数据 if (isStartCountersign) { var exists = workflow.Countersigns.Any(d => !d.IsCompleted() && d.StarterId == _sessionContextProvider.SessionContext.RequiredUserId); if (exists) throw new UserFriendlyException("该用户在当前流程存在未结束会签"); await StartCountersignAsync(_sessionContextProvider.SessionContext, 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(_sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.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, _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel); } if (workflow.CurrentStepId == currentStep.Id) { workflow.UpdateCurrentStepWhenHandle(currentStep, _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel); } //检查是否流转到流程终点 if (nextStepDefine.StepType is EStepType.End) { var endTrace = await EndAsync(_sessionContextProvider.SessionContext, workflow, dto, nextStepDefine, currentStep, expiredTime, cancellationToken); return; } //创建下一/N个节点(会签汇总节点:会签未全部办理时不创建,最后一个会签办理节点创建会签汇总节点) var nextSteps = await CreateNextStepsAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken: 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(_sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.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, _sessionContextProvider.SessionContext.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); 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.DepartmentLeader => EFlowDirection.CenterToOrg, EBusinessType.File => EFlowDirection.CenterToFile, _ => throw new ArgumentOutOfRangeException(nameof(directionStepBusinessType), directionStepBusinessType, null) }; case EBusinessType.Department: case EBusinessType.DepartmentLeader: return directionStepBusinessType switch { EBusinessType.Seat => EFlowDirection.OrgToCenter, EBusinessType.Send => EFlowDirection.OrgToCenter, EBusinessType.Department => EFlowDirection.OrgToOrg, EBusinessType.DepartmentLeader => EFlowDirection.OrgToOrg, EBusinessType.File => EFlowDirection.OrgToFile, _ => throw new ArgumentOutOfRangeException(nameof(directionStepBusinessType), directionStepBusinessType, null) }; case EBusinessType.File: return directionStepBusinessType switch { EBusinessType.Seat => EFlowDirection.FiledToCenter, EBusinessType.Send => EFlowDirection.FiledToCenter, EBusinessType.Department => EFlowDirection.FiledToOrg, EBusinessType.DepartmentLeader => EFlowDirection.FiledToOrg, _ => 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, _sessionContextProvider.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); } /// /// 根据汇总对象id找到被汇总节点,生成指派到用户的办理对象 /// /// /// /// public FlowStepHandler GetSummaryTargetFlowStepHandler(Workflow workflow, string summaryTargetStepCode) { //根据汇总对象id找到被汇总节点 var summaryTargetStep = workflow.Steps.FirstOrDefault(d => d.StepType == EStepType.Normal && d.Code == summaryTargetStepCode && d.Status == EWorkflowStepStatus.Handled && d.IsOrigin); if (summaryTargetStep is null) throw UserFriendlyException.SameMessage("未查询到汇总对象节点"); var handler = new FlowStepHandler { Key = summaryTargetStep.HandlerId, Value = summaryTargetStep.HandlerName, UserId = summaryTargetStep.HandlerId, Username = summaryTargetStep.HandlerName, OrgId = summaryTargetStep.HandlerOrgId, OrgName = summaryTargetStep.HandlerOrgName, RoleId = summaryTargetStep.RoleId, RoleName = summaryTargetStep.RoleName }; return handler; } /// /// 追加归档信息(接收ds推送12315归档信息) /// public async Task AppendFileOpinionAsync(string workflowId, string opinion, List files, CancellationToken cancellationToken) { //归档意见,附件追加在归档节点前一节点上 var workflow = await GetWorkflowAsync(workflowId, withSteps: true, withTraces: true, cancellationToken: cancellationToken); var endStep = workflow.Steps.FirstOrDefault(d => d.StepType == EStepType.End); if (endStep is null) throw new UserFriendlyException($"该流程还未归档, workflowId: {workflowId}"); var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == endStep.PrevStepId); if (prevStep is null) throw new UserFriendlyException($"未找到归档节点的前一节点, workflowId: {workflowId}, endStepId: {endStep.Id}"); prevStep.Opinion += opinion; if (files != null && files.Any()) { var filejsons = await _fileRepository.AddFileAsync(files, workflow.ExternalId, prevStep.Id, cancellationToken); prevStep.FileJson.AddRange(filejsons); } await _workflowStepRepository.UpdateAsync(prevStep, cancellationToken); } /// /// 跳转至结束节点(无视流程模板配置以及当前办理对象,直接跳至结束节点) /// public async Task JumpToEndAsync(ISessionContext current, string workflowId, string opinion, List files, DateTime? expiredTime, EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default) { var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true, withCountersigns: true, cancellationToken: cancellationToken); await JumpToEndAsync(current, workflow, opinion, files, expiredTime, reviewResult, cancellationToken); } /// /// 跳转至结束节点(无视流程模板配置以及当前办理对象,直接跳至结束节点) /// public async Task JumpToEndAsync(ISessionContext current, Workflow workflow, string opinion, List files, DateTime? expiredTime, EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default) { var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine(); if (endStepDefine is null) throw new UserFriendlyException("未正确配置结束节点"); var dto = new BasicWorkflowDto { NextStepCode = endStepDefine.Code, NextStepName = endStepDefine.Name, FlowDirection = EFlowDirection.OrgToFile, BusinessType = endStepDefine.BusinessType, ReviewResult = reviewResult, Opinion = opinion, Files = files }; var unhandleSteps = workflow.Steps .Where(d => d.Status != EWorkflowStepStatus.Handled).ToList(); var unhandleTraces = workflow.Traces .Where(d => d.Status != EWorkflowStepStatus.Handled).ToList(); //get currentStep var currentStep = unhandleSteps.MaxBy(d => d.CreationTime) ?? workflow.Steps.MaxBy(d => d.CreationTime); foreach (var step in unhandleSteps) { await HandleStepAsync(step, workflow, dto, null, null, cancellationToken); if (step.IsStartCountersign) step.CountersignEnd(); var trace = unhandleTraces.First(d => d.StepId == step.Id); _mapper.Map(dto, trace); _mapper.Map(step, trace); } await _workflowStepRepository.UpdateRangeAsync(unhandleSteps, cancellationToken); await _workflowTraceRepository.UpdateRangeAsync(unhandleTraces, cancellationToken); //结束会签 var counstersigns = workflow.Countersigns .Where(d => !d.EndTime.HasValue) .ToList(); foreach (var counstersign in counstersigns) { //结束会签 counstersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType, current.UserId, current.UserName, current.OrgId, current.OrgName, current.OrgAreaCode, current.OrgAreaName); } await _workflowCountersignRepository.UpdateRangeAsync(counstersigns, cancellationToken); //更新实际办理节点信息 if (currentStep.StepType != EStepType.Summary && currentStep.StepType != EStepType.End) { workflow.UpdateActualStepWhenHandle(currentStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel); workflow.ActualHandleStepAcceptTime = currentStep.AcceptTime; } // // workflow.UpdateCurrentStepWhenHandle(currentStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel); if (workflow.Steps.All(d => d.StepType != EStepType.End)) { await EndAsync(current, workflow, dto, endStepDefine, currentStep, expiredTime, 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, bool isOrderFiled, DateTime? expiredTime, 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, withCountersigns: 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); var targetStepDefine = workflow.WorkflowDefinition.Steps.FirstOrDefault(d => d.StepType == EStepType.Start); var dto = new RecallDto { Opinion = opinion, NextStepCode = startStep.Code, NextStepName = startStep.Name, BusinessType = startStep.BusinessType, StepType = startStep.StepType, HandlerType = targetStepDefine.HandlerType, NextHandlers = new List { new FlowStepHandler { Key = startStep.RoleId, Value = startStep.RoleName, RoleId = startStep.RoleId, RoleName = startStep.RoleName, UserId = startStep.HandlerId, Username = startStep.HandlerName, OrgId = startStep.HandlerOrgId, OrgName = startStep.HandlerOrgName, } } }; var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign, dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken); //flowAssignInfo.FlowAssignType = EFlowAssignType.Role; await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled, cancellationToken); } /// /// 撤回至派单节点 /// public async Task RecallToSendStepAsync(string workflowId, string opinion, bool isOrderFiled, DateTime? expiredTime, CancellationToken cancellationToken) { var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true, withCountersigns: 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); var targetStepDefine = workflow.WorkflowDefinition.Steps.FirstOrDefault(d => d.BusinessType == EBusinessType.Send); var dto = new RecallDto { Opinion = opinion, NextStepCode = sendStep.Code, NextStepName = sendStep.Name, BusinessType = sendStep.BusinessType, StepType = sendStep.StepType, HandlerType = targetStepDefine.HandlerType, NextHandlers = new List { new FlowStepHandler { Key = sendStep.RoleId, Value = sendStep.RoleName, RoleId = sendStep.RoleId, RoleName = sendStep.RoleName, UserId = sendStep.HandlerId, Username = sendStep.HandlerName, OrgId = sendStep.HandlerOrgId, OrgName = sendStep.HandlerOrgName, } } }; var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign, dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken); //flowAssignInfo.FlowAssignType = EFlowAssignType.Role; await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled, cancellationToken); } /// /// 特提至中心(优先派单组其次坐席) /// /// true 派单组 false 话务部 public async Task RecallToCenterFirstToSendAsync(string workflowId, string opinion, bool isOrderFiled, DateTime? expiredTime, CancellationToken cancellationToken) { bool isPaiDan = true; var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true, withCountersigns: true, cancellationToken: cancellationToken); var sendStep = workflow.Steps.FirstOrDefault(d => d.BusinessType == EBusinessType.Send); if (sendStep is not null) { var targetStepDefine = workflow.WorkflowDefinition.Steps.FirstOrDefault(d => d.BusinessType == EBusinessType.Send); var dto = new RecallDto { Opinion = opinion, NextStepCode = sendStep.Code, NextStepName = sendStep.Name, BusinessType = sendStep.BusinessType, StepType = sendStep.StepType, HandlerType = targetStepDefine.HandlerType, NextHandlers = new List { new FlowStepHandler { Key = sendStep.RoleId, Value = sendStep.RoleName, RoleId = sendStep.RoleId, RoleName = sendStep.RoleName, UserId = sendStep.HandlerId, Username = sendStep.HandlerName, OrgId = sendStep.HandlerOrgId, OrgName = sendStep.HandlerOrgName, } } }; var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign, dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken); await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled, 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); var targetStepDefine = workflow.WorkflowDefinition.Steps.FirstOrDefault(d => d.StepType == EStepType.Start); var dto = new RecallDto { Opinion = opinion, NextStepCode = startStep.Code, NextStepName = startStep.Name, BusinessType = startStep.BusinessType, StepType = startStep.StepType, HandlerType = targetStepDefine.HandlerType, NextHandlers = new List { new FlowStepHandler { Key = startStep.RoleId, Value = startStep.RoleName, RoleId = startStep.RoleId, RoleName = startStep.RoleName, UserId = startStep.HandlerId, Username = startStep.HandlerName, OrgId = startStep.HandlerOrgId, OrgName = startStep.HandlerOrgName, } } }; var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign, dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken); //flowAssignInfo.FlowAssignType = EFlowAssignType.Role; await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled, cancellationToken); isPaiDan = false; } return isPaiDan; } //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 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 async Task AddTracesAsync(string workflowId, List steps, CancellationToken cancellationToken) { var workflow = await GetWorkflowAsync(workflowId, cancellationToken: cancellationToken); if (workflow is null) throw new UserFriendlyException("找不到该流程"); await _workflowStepRepository.AddNav(steps) .Include(d => d.WorkflowTrace) .ExecuteCommandAsync(); } /// /// 创建开始节点 /// 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.NextSteps = nextSteps; startStep.IsMain = true; startStep.IsOrigin = true; startStep.Status = EWorkflowStepStatus.WaitForAccept; startStep.FlowDirection = dto.FlowDirection; startStep.PrevChosenStepCode = null; if (expiredTime.HasValue) 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(ISessionContext current, Workflow workflow, BasicWorkflowDto dto, StepDefine endStepDefine, WorkflowStep currentStep, DateTime? expiredTime, CancellationToken cancellationToken) { //create endStep var endStep = await CreateEndStepAsync(workflow, endStepDefine, currentStep, dto, 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.CurrentStepAcceptTime = endStep.AcceptTime; // workflow.UpdateActualStepWhenHandle(endStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel); // workflow.ActualHandleStepAcceptTime = 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, EBusinessType currentStepBusinessType) { if (!isStartCountersign) return null; return currentStepBusinessType switch { EBusinessType.Seat => ECounterSignType.Center, EBusinessType.Send => ECounterSignType.Center, EBusinessType.Department => ECounterSignType.Department, EBusinessType.DepartmentLeader => ECounterSignType.Department, EBusinessType.File => null, _ => throw new ArgumentOutOfRangeException(nameof(currentStepBusinessType), currentStepBusinessType, null) }; // return _sessionContextProvider.SessionContext.OrgIsCenter ? ECounterSignType.Center : ECounterSignType.Department; } /// /// 办理节点 /// public async Task HandleStepAsync(WorkflowStep step, Workflow workflow, BasicWorkflowDto dto, 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(step, dto.Opinion, dto.NextStepCode); } #region private method private Workflow CreateWorkflow(WorkflowModule wfModule, string title, string? externalId = null) { 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, FlowType = definition.FlowType, }; workflow.InitId(); return workflow; } 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 (nextSteps.Count > 1) return; //多个下级节点不更新workflow的实际办理信息 var nextStep = nextSteps.First(); //todo 重构为办理对象由参数传入,指派给中心?派单员?待确认 if (nextStepDefine.BusinessType is EBusinessType.Seat or EBusinessType.Send) { //坐席->派单不选办理对象时 workflow.UpdateActualStepWhenAssign(nextStep, new FlowStepHandler { Key = OrgSeedData.CenterId, Value = OrgSeedData.CenterName, OrgId = OrgSeedData.CenterId, OrgName = OrgSeedData.CenterName }); } else { var nextHandler = dto.NextHandlers.First(); workflow.UpdateActualStepWhenAssign(nextStep, nextHandler); } //与实际办理节点的接办状态保持一致 workflow.ActualHandleStepAcceptTime = nextStep.AcceptTime; } 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, EFlowAssignType flowAssignType, DateTime? expiredTime, bool isStartCountersign, bool isAutoFillSummaryOpinion = false, CancellationToken cancellationToken = default) { 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, flowAssignType, expiredTime, cancellationToken); } else { //创建普通节点(根据配置) nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignType, EWorkflowTraceType.Normal, expiredTime, isAutoFillSummaryOpinion, 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, isAutoFillSummaryOpinion, cancellationToken); } else { //依据会签策略创建会签下一级节点 nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignType, expiredTime, cancellationToken); } } } else { if (dto.BackToCountersignEnd) { // check if cs all complete, create next nextSteps = await CreateCsEndStepsByTargetPrevAsync(workflow, currentStep, dto, expiredTime, isAutoFillSummaryOpinion, cancellationToken); } else { //依据会签策略创建会签下一级节点 nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignType, expiredTime, cancellationToken); } } } else if (isStartCountersign) //top { //依据会签策略创建会签下一级节点 nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignType, expiredTime, cancellationToken); } else if (isNextDynamic) { //创建动态下一级节点 nextSteps = await CreateDynamicStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignType, expiredTime, cancellationToken); } else { //创建普通节点(根据配置) nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignType, EWorkflowTraceType.Normal, expiredTime, isAutoFillSummaryOpinion, 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, EFlowAssignType flowAssignType, DateTime? expiredTime, CancellationToken cancellationToken) { var handlerType = nextStepDefine.InstancePolicy switch { EDynamicPolicy.OrgUpCenterTop => EHandlerType.OrgLevel, EDynamicPolicy.OrgUp => EHandlerType.OrgLevel, EDynamicPolicy.OrgUpHandleCenterTop => EHandlerType.OrgLevel, EDynamicPolicy.OrgUpHandle => EHandlerType.OrgLevel, EDynamicPolicy.OrgUpLeadCenterTop => EHandlerType.OrgLevel, EDynamicPolicy.OrgUpLead => EHandlerType.OrgLevel, EDynamicPolicy.ArriveCenter => EHandlerType.OrgLevel, EDynamicPolicy.ArriveOneOrg => EHandlerType.OrgLevel, EDynamicPolicy.OrgDownCenterTop => EHandlerType.OrgLevel, EDynamicPolicy.OrgDown => EHandlerType.OrgLevel, null => throw new ArgumentOutOfRangeException(), _ => throw new ArgumentOutOfRangeException() }; return await CreateStepsAsync(workflow, nextStepDefine, prevStep, dto, 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, CancellationToken cancellationToken = default ) { //var countersignId = dto.IsStartCountersign ? prevStep.StartCountersignId : prevStep.CountersignId; var countersignId = prevStep.StartCountersignId; //当前策略均为orglevel var handlerType = EHandlerType.OrgLevel; 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, bool isAutoFillSummaryOpinion = false, CancellationToken cancellationToken = default) { 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; string? opinion = null; if (isAutoFillSummaryOpinion) { //依据某节点作为根节点,查找最底层办理节点无论普通还是汇总节点 var preSteps = GetLastStepsFromRootStep(workflow.Steps, countersignStartStep); var sb = new StringBuilder(); foreach (var prevStep in preSteps.OrderBy(d => d.HandleTime).ToList()) { sb.AppendLine($"【会签时间】:{prevStep.HandleTime?.ToString("yyyy-MM-dd HH:mm:ss")}"); sb.AppendLine($"【会签人】:{prevStep.HandlerName}"); sb.AppendLine($"【会签结果】:{prevStep.Opinion}"); sb.AppendLine(""); } opinion = sb.ToString(); } // 创建会签汇总节点 var countersignEndStep = await CreateCountersignEndStepAsync(workflow, countersignStartStep, dto, expiredTime, opinion, 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 List GetLastStepsFromRootStep(List steps, WorkflowStep rootStep) { var lastSteps = new List(); var nextSteps = steps.Where(d => d.PrevStepId == rootStep.Id).ToList(); foreach (var nextStep in nextSteps) { if (nextStep.IsStartCountersign) { //find last csend var lastCsEndStep = GetLastCountersignEndStep(steps, nextStep); if (lastCsEndStep != null) lastSteps.Add(lastCsEndStep); } else { lastSteps.Add(nextStep); } } return lastSteps; } private WorkflowStep? GetLastCountersignEndStep(List steps, WorkflowStep nextStep) { var csEndStep = steps.FirstOrDefault(d => d.IsCountersignEndStep && d.CountersignStartStepId == nextStep.Id); if (csEndStep == null) return null; return !csEndStep.IsStartCountersign ? csEndStep : GetLastCountersignEndStep(steps, csEndStep); } private async Task CreateCountersignEndStepAsync( Workflow workflow, WorkflowStep countersignStartStep, BasicWorkflowDto dto, DateTime? expiredTime, string? opinion = null, 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(); //需求调整:汇总节点指派给发起人部门办理 //todo 待重构 csEndStep.FlowAssignType = EFlowAssignType.Org; csEndStep.Reset(); csEndStep.ResetParameters(); if (!string.IsNullOrEmpty(opinion)) csEndStep.Opinion = opinion; 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(WorkflowStep step, string opinion, string nextStepCode) { //todo 重构:ISessionContext传入 step.Handle(_sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName, _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName, _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.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 countersignId = startStep.CountersignId; if (startStep.IsCountersignEndStep) { var topStartCsStep = GetCsLoopStartStep(workflow, startStep); countersignId = topStartCsStep.CountersignId; } var countersign = await CreateCountersignAsync(current, workflow, startStep, dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), flowAssignType, counterSignType, expiredTime, countersignId, cancellationToken); startStep.StartCountersign(countersign.Id); return countersign; } /// /// 检查是否从中心流转至部门 /// 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.StartCountersignId = step.StartCountersignId; newStep.CountersignId = step.CountersignId; newStep.IsStartedCountersignEnd = step.IsStartedCountersignEnd; //退回场景:指派给原办理人,其余场景:按照原节点原始指派方式复制 //todo 重构为参数传入办理对象 if (traceType is EWorkflowTraceType.Previous) { //newStep.FlowAssignType = EFlowAssignType.User; // 是否中心 临时紧急修改 后续在流程模版定义是否原办理人退回类型 来实现流程 禅道200 newStep.FlowAssignType = step.BusinessType is EBusinessType.Seat or EBusinessType.Send ? 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.UserId, StarterName = current.UserName ?? string.Empty, StarterOrgId = current.OrgId, 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 (workflow.IsInCountersign && step.IsInCountersign()) { if (step.IsCountersignEndStep) { //var startTrace = await GetWorkflowTraceAsync(workflow.Id, step.CountersignStartStepId, cancellationToken); var startTrace = workflow.Traces.FirstOrDefault(d => d.Id == step.CountersignStartStepId); if (startTrace == null) throw new UserFriendlyException( $"未找到startTrace, workflowId: {workflow.Id}, step.CountersignStartStepId: {step.CountersignStartStepId}"); 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); var prevTrace = workflow.Traces.FirstOrDefault(d => d.Id == step.PrevStepId); if (prevTrace == null) throw new UserFriendlyException($"未找到prevTrace, workflowId: {workflow.Id}, step.PrevStepId: {step.PrevStepId}"); 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( _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName, _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName, _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.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); 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); //结束会签 var unCompleteCountersigns = workflow.Countersigns.Where(d => !d.IsCompleted()).ToList(); if (unCompleteCountersigns.Any()) { foreach (var unCompleteCountersign in unCompleteCountersigns) { unCompleteCountersign.End(null, null, EBusinessType.File, _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName, _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName, _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName); } await _workflowCountersignRepository.UpdateRangeAsync(unCompleteCountersigns, 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, flowAssignInfo.FlowAssignType, 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得冗余字段(FlowedOrgIds, HandlerOrgs) //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( Workflow workflow, StepDefine endStepDefine, WorkflowStep prevStep, BasicWorkflowDto dto, DateTime? expiredTime, CancellationToken cancellationToken) { if (workflow.Steps.Any(d => d.StepType == EStepType.End)) throw UserFriendlyException.SameMessage("无法重复创建结束节点"); var handler = new FlowStepHandler { Key = _sessionContextProvider.SessionContext.UserId, Value = _sessionContextProvider.SessionContext.UserName, UserId = _sessionContextProvider.SessionContext.RequiredUserId, Username = _sessionContextProvider.SessionContext.UserName, OrgId = _sessionContextProvider.SessionContext.OrgId, OrgName = _sessionContextProvider.SessionContext.OrgName, }; var step = CreateStep(workflow, endStepDefine, prevStep, EFlowAssignType.User, handler, null, null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, expiredTime, endStepDefine.Name, true, businessType: EBusinessType.File, flowDirection: dto.FlowDirection); //step.Accept(_sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName, // _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName, // _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName); HandleStep(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, EFlowAssignType flowAssignType, EWorkflowTraceType traceType, DateTime? expiredTime, bool isAutoFillSummaryOpinion = false, CancellationToken cancellationToken = default) { 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; } string? opinion = null; if (isAutoFillSummaryOpinion && stepDefine.StepType is EStepType.Summary) opinion = prevStep.Opinion; return await CreateStepsAsync(workflow, stepDefine, prevStep, dto, flowAssignType, handlers, null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceType, null, expiredTime, opinion, 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, string? opinion = 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, dto.FlowDirection, opinion); steps.Add(step); } await _workflowStepRepository.AddRangeAsync(steps, cancellationToken); workflow.Steps.AddRange(steps); //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: case EDynamicPolicy.ArriveCenter: case EDynamicPolicy.ArriveOneOrg: case EDynamicPolicy.OrgUpHandleCenterTop: case EDynamicPolicy.OrgUpHandle: if (!int.TryParse(currentStepDefine.TerminalDynamicMark, out var tMark)) throw new UserFriendlyException( $"TerminalDynamicMark parse to int failed, tMark: {currentStepDefine.TerminalDynamicMark}"); return currentOrgLevel <= tMark; case EDynamicPolicy.OrgUpLeadCenterTop: case EDynamicPolicy.OrgUpLead: if (!int.TryParse(currentStepDefine.TerminalDynamicMark, out var tMark2)) throw new UserFriendlyException( $"TerminalDynamicMark parse to int failed, tMark: {currentStepDefine.TerminalDynamicMark}"); var leadRoleCode = _systemSettingCacheManager.GetSetting(SettingConstants.RoleLingDao)?.SettingValue[0]; var isLead = _sessionContextProvider.SessionContext.Roles.Any(x => x == leadRoleCode); return (currentOrgLevel <= tMark2) && (isLead || _sessionContextProvider.SessionContext.OrgIsCenter); 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, RoleId = startCountersignStep.RoleId, RoleName = startCountersignStep.RoleName }); workflow.UpdateCurrentStepWhenAssign(newStep, new FlowStepHandler { UserId = startCountersignStep.HandlerId, Username = startCountersignStep.HandlerName, OrgId = startCountersignStep.HandlerOrgId, OrgName = startCountersignStep.HandlerOrgName, RoleId = startCountersignStep.RoleId, RoleName = startCountersignStep.RoleName }); } // //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 = $"会签未办理完成,由 {_sessionContextProvider.SessionContext.OrgName} 的 {_sessionContextProvider.SessionContext.UserName} 终止办理"; if (step.IsStartCountersign) step.CountersignEnd(); if (step.Status is not EWorkflowStepStatus.Handled) { step.Handle(_sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName, _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName, _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.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, _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName, _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName, _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName); /* * //结束step会签信息 countersignStartStep.CountersignEnd(); updateSteps.Add(countersignStartStep); //结束会签 currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType, _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName, _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName, _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.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, EFlowDirection? flowDirection = null, string? opinion = 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.FlowDirection = flowDirection; if (expiredTime.HasValue) 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; if (!string.IsNullOrEmpty(opinion)) step.Opinion = opinion; return step; } private async Task GetNextStepFlowAssignInfoByDefineAsync(StepDefine nextStepDefine, EHandlerType handlerType, bool isStartCountersign, List handlers, CancellationToken cancellationToken) { switch (handlerType) { case EHandlerType.Role: if (!handlers.Any()) { //var roles = await _roleRepository.Queryable() // .Includes(d => d.Accounts, x => x.User) // .Where(d => nextStepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Name)) // .ToListAsync(cancellationToken); //handlers = roles.SelectMany(d => d.Accounts).Distinct() // .Select(d => new Kv(d.Id, d.User.Name)) // .ToList(); handlers = nextStepDefine.HandlerTypeItems; return FlowAssignInfo.Create(EFlowAssignType.Role, handlers, isStartCountersign); } return FlowAssignInfo.Create(EFlowAssignType.User, handlers, isStartCountersign); case EHandlerType.OrgLevel: case EHandlerType.OrgType: case EHandlerType.AssignedOrg: return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign); case EHandlerType.AssignedUser: return FlowAssignInfo.Create(EFlowAssignType.User, handlers, isStartCountersign); //case EHandlerType.AssignedOrgOrRole: // return FlowAssignInfo.Create(EFlowAssignType.OrgAndRole, handlers, isStartCountersign); default: throw new ArgumentOutOfRangeException(); } } //new /// /// 查询流程业务模块 /// /// /// /// /// private async Task GetWorkflowModuleAsync(string code, CancellationToken cancellationToken) { var wfModule = await _wfModuleCacheManager.GetWorkflowModuleAsync(code, cancellationToken); if (wfModule == null) throw UserFriendlyException.SameMessage("无效流程模块编码"); if (wfModule.Definition is null) throw new UserFriendlyException($"{code} 未配置流程模板", "未配置流程模板"); return wfModule; } // // private StepAssignInfo GetStepAssignInfo(TargetStepAssignPolicyInfo targetStepAssignPolicyInfo, bool anyHandlers, // StepDefine? stepDefine = null, WorkflowStep? targetStep = null, FlowStepHandler? handler = null) // { // switch (targetStepAssignPolicyInfo.TargetStepAssignPolicy) // { // case ETargetStepAssignPolicy.Config: // if (stepDefine is null) throw new UserFriendlyException($"{nameof(stepDefine)} is null"); // return new StepAssignInfo // { // FlowAssignType = GetNextStepFlowAssignTypeByDefine(stepDefine, anyHandlers), // //todo // // Handler = // }; // case ETargetStepAssignPolicy.TargetStep: // if (targetStep is null) throw new UserFriendlyException($"{nameof(targetStep)} is null"); // if (targetStep.FlowAssignType is null) throw new UserFriendlyException($"targetStep.FlowAssignType is null"); // return new StepAssignInfo // { // FlowAssignType = targetStep.FlowAssignType.Value, // Handler = targetStep.GetWorkflowStepHandler() // }; // case ETargetStepAssignPolicy.AssignHandler: // if(handler is null) throw new UserFriendlyException($"{nameof(handler)} is null"); // return new() // { // FlowAssignType = EFlowAssignType.User, // Handler = handler // }; // default: // throw new ArgumentOutOfRangeException(); // } // } // // /// // /// 按流程模板配置获取下一节点指派类型 // /// // private EFlowAssignType GetNextStepFlowAssignTypeByDefine(StepDefine nextStepDefine, bool anyHandlers) // { // switch (nextStepDefine.HandlerType) // { // case EHandlerType.Role: // return !anyHandlers ? EFlowAssignType.Role : EFlowAssignType.User; // // case EHandlerType.OrgLevel: // case EHandlerType.OrgType: // case EHandlerType.AssignedOrg: // return EFlowAssignType.Org; // // case EHandlerType.AssignedUser: // return EFlowAssignType.User; // // default: // throw new ArgumentOutOfRangeException(); // } // } /// /// 查询下一节点办理对象类型(user or org)及实际办理对象 /// private async Task GetNextStepFlowAssignInfoAsync(Workflow workflow, WorkflowStep currentStep, BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, CancellationToken cancellationToken) { if (nextStepDefine.StepType is EStepType.End) return new(); var isStartCountersign = dto.IsStartCountersign; var handlers = dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(); if (isStartCountersign) { var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType, dto.NextHandlers.Any()); //按会签策略判断,目前所有策略为org return FlowAssignInfo.Create(assignType, handlers, isStartCountersign); } //if (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)) // return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign); if (currentStep.IsInCountersign()) { if (currentStep.IsCountersignEndStep) { //汇总节点(非顶级) if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)) { if (dto.BackToCountersignEnd) { var csStartStep = GetCsLoopStartStep(workflow, currentStep); var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == csStartStep.PrevStepId); if (prevStep is null) throw new UserFriendlyException("未查询到目标节点的前一节点"); return FlowAssignInfo.Create(prevStep.FlowAssignType.Value, prevStep.Handlers, isStartCountersign); } } } else { if (dto.BackToCountersignEnd) { var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId); if (prevStep is null) throw new UserFriendlyException($"未查询到当前节点的上级节点"); return FlowAssignInfo.Create(prevStep.FlowAssignType.Value, prevStep.Handlers, isStartCountersign); } else { var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType, dto.NextHandlers.Any()); //按会签策略判断,目前所有策略为org return FlowAssignInfo.Create(assignType, handlers, isStartCountersign); } } } if (isNextDynamic) { switch (currentStep.InstancePolicy) { case EDynamicPolicy.OrgUpCenterTop: case EDynamicPolicy.OrgUp: case EDynamicPolicy.OrgDownCenterTop: case EDynamicPolicy.OrgDown: case EDynamicPolicy.ArriveCenter: case EDynamicPolicy.ArriveOneOrg: return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign); case EDynamicPolicy.OrgUpHandleCenterTop: case EDynamicPolicy.OrgUpHandle: case EDynamicPolicy.OrgUpLeadCenterTop: case EDynamicPolicy.OrgUpLead: return FlowAssignInfo.Create(EFlowAssignType.OrgAndRole, handlers, isStartCountersign); default: throw new ArgumentOutOfRangeException(); } } return await GetNextStepFlowAssignInfoByDefineAsync(nextStepDefine, dto.HandlerType, isStartCountersign, handlers, cancellationToken); } private WorkflowStep GetCsLoopStartStep(Workflow workflow, WorkflowStep currentStep) { var startCountersignStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId); if (startCountersignStep is null) throw new UserFriendlyException( $"未查询到会签开始节点,workflowId: {workflow.Id}, startStepId: {currentStep.CountersignStartStepId}", "未查询到会签开始节点,数据异常"); if (!startCountersignStep.IsCountersignEndStep) return startCountersignStep; return GetCsLoopStartStep(workflow, startCountersignStep); } #endregion } }