using Hotline.Application.Contracts.Validators.FlowEngine; using Hotline.Caching.Interfaces; using Hotline.FlowEngine; using Hotline.FlowEngine.Definitions; using Hotline.FlowEngine.WorkflowModules; using Hotline.FlowEngine.Workflows; using Hotline.Identity.Accounts; using Hotline.Identity.Roles; using Hotline.Orders; using Hotline.Settings; using Hotline.Settings.TimeLimits; using Hotline.Share.Dtos; using Hotline.Share.Dtos.FlowEngine; using Hotline.Share.Dtos.FlowEngine.Definition; using Hotline.Share.Dtos.Settings; using Hotline.Share.Enums.FlowEngine; using Hotline.Share.Enums.Identity; using Hotline.Users; using MapsterMapper; using Hotline.File; using Hotline.Share.Enums.Settings; using XF.Domain.Authentications; using XF.Domain.Dependency; using XF.Domain.Entities; using XF.Domain.Exceptions; using XF.Domain.Extensions; using XF.Domain.Repository; using XF.Utility.EnumExtensions; using Hotline.Share.Dtos.File; using Microsoft.Extensions.Logging; using System.Text; using System.Diagnostics; namespace Hotline.Application.FlowEngine; public class WorkflowApplication : IWorkflowApplication, IScopeDependency { private readonly IDefinitionDomainService _definitionDomainService; private readonly IWorkflowDomainService _workflowDomainService; private readonly IOrderDomainService _orderDomainService; private readonly IWorkflowRepository _workflowRepository; private IRepository _definitionRepository; private IRepository _workflowStepRepository; private IRepository _workflowTraceRepository; private readonly IRepository _userRepository; private readonly IAccountRepository _accountRepository; private readonly ISystemOrganizeRepository _organizeRepository; private readonly IRepository _roleRepository; private readonly IWfModuleCacheManager _wfModuleCacheManager; private readonly ISystemDomainService _systemDomainService; private readonly ITimeLimitDomainService _timeLimitDomainService; private readonly IUserDomainService _userDomainService; private readonly IAccountDomainService _accountDomainService; private readonly ISessionContext _sessionContext; private readonly IMapper _mapper; private readonly IFileRepository _fileRepository; private readonly ILogger _logger; public WorkflowApplication( IDefinitionDomainService definitionDomainService, IWorkflowDomainService workflowDomainService, IOrderDomainService orderDomainService, IWorkflowRepository workflowRepository, IRepository definitionRepository, IRepository workflowStepRepository, IRepository workflowTraceRepository, IRepository userRepository, IAccountRepository accountRepository, ISystemOrganizeRepository organizeRepository, IRepository roleRepository, IWfModuleCacheManager wfModuleCacheManager, ISystemDomainService systemDomainService, ITimeLimitDomainService timeLimitDomainService, ISessionContext sessionContext, IMapper mapper, IFileRepository fileRepository, ILogger logger) { _definitionDomainService = definitionDomainService; _workflowDomainService = workflowDomainService; _orderDomainService = orderDomainService; _workflowRepository = workflowRepository; _definitionRepository = definitionRepository; _workflowStepRepository = workflowStepRepository; _workflowTraceRepository = workflowTraceRepository; _userRepository = userRepository; _accountRepository = accountRepository; _organizeRepository = organizeRepository; _roleRepository = roleRepository; _wfModuleCacheManager = wfModuleCacheManager; _systemDomainService = systemDomainService; _timeLimitDomainService = timeLimitDomainService; _sessionContext = sessionContext; _mapper = mapper; _fileRepository = fileRepository; _logger = logger; } public async Task StartWorkflowAsync(StartWorkflowDto dto, ISessionContext current, 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 && !_workflowDomainService.DynamicShouldTerminal(startStepDefine, current.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 = await _workflowDomainService.CreateWorkflowAsync(wfModule, dto.Title, current.RequiredUserId, current.RequiredOrgId, externalId, cancellationToken); //var startStepHandles = new List{WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId, // EFlowAssignType.User, current.RequiredUserId, current.UserName, // current.RequiredOrgId, current.OrgName)}; var startStep = _workflowDomainService.CreateStartStep(workflow, startStepDefine, dto, new FlowStepHandler { Key = current.RequiredUserId, Value = current.UserName, UserId = current.UserId, Username = current.UserName, OrgId = current.RequiredOrgId, OrgName = current.OrgName }, expiredTime); var flowAssignInfo = await GetNextStepFlowAssignInfoAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic, cancellationToken); //var firstStepHandlers = await GetNextStepHandlersAsync(workflow, firstStepDefine, dto, cancellationToken); var counterSignType = _workflowDomainService.GetCounterSignType(startStep.BusinessType); //办理开始节点 await _workflowDomainService.HandleStepAsync(current, startStep, workflow, dto, flowAssignInfo.FlowAssignType, counterSignType, expiredTime, cancellationToken); //startStep.Handle(current.RequiredUserId, current.UserName, // current.RequiredOrgId, current.OrgName, // current.OrgAreaCode, current.OrgAreaName, // current.OrgIsCenter, firstStepDefine.Code); if (dto.Files.Any()) startStep.FileJson = await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId, startStep.Id, cancellationToken); await _workflowStepRepository.AddAsync(startStep, cancellationToken); //await _workflowStepRepository.AddNav(startStep) // .Include(d => d.StepHandlers) // .ExecuteCommandAsync(); workflow.Steps.Add(startStep); //starttrace var startTrace = _mapper.Map(startStep); startTrace.StepId = startStep.Id; startTrace.TraceType = EWorkflowTraceType.Normal; _mapper.Map(dto, startTrace); await _workflowTraceRepository.AddAsync(startTrace, cancellationToken); workflow.Traces.Add(startTrace); //更新受理人信息 workflow.UpdateAcceptor( current.RequiredUserId, current.UserName, current.StaffNo, current.RequiredOrgId, current.OrgName); await _workflowDomainService.StartAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic, flowAssignInfo, counterSignType, expiredTime, current, cancellationToken); return workflow.Id; } /// /// 流转至下一节点(节点办理) /// public async Task NextAsync(NextWorkflowDto dto, ISessionContext current, DateTime? expiredTime = null, CancellationToken cancellationToken = default) { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true, withTraces: true, withCountersigns: true, cancellationToken: cancellationToken); var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow, current.RequiredUserId, current.RequiredOrgId, current.Roles); if (currentStep.Status is EWorkflowStepStatus.Handled) throw new UserFriendlyException("该状态不支持继续办理"); var currentStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, currentStep.Code); //下一节点是否为动态节点 var isNextDynamic = currentStepDefine.InstanceMode is EInstanceMode.Dynamic && !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, current.OrgLevel); StepDefine nextStepDefine; if (isNextDynamic || (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)) || dto.IsStartCountersign) { //下一步配置为当前节点配置 nextStepDefine = currentStepDefine; } else { //下一步配置为下一步节点配置 nextStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode); } //需求:按角色选择办理人可以不选,表示该角色下所有人都可以办理,同时依据配置:是否本部门人办理显示待选办理人。角色下只要一人办理即可(即:角色下不发起会签) if (nextStepDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any()) throw new UserFriendlyException("未指定节点处理者"); if (dto.IsStartCountersign) { if (!currentStepDefine.CanStartCountersign) throw new UserFriendlyException("当前节点不支持发起会签"); //if (currentStepDefine.HandlerType is EHandlerType.Role) // throw new UserFriendlyException("当前节点不支持发起会签"); //即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签 if (nextStepDefine.StepType is EStepType.Summary or EStepType.End) throw new UserFriendlyException("下一节点不允许发起会签"); //下一节点是会签汇总节点也不允许发起会签 if (dto.BackToCountersignEnd) throw new UserFriendlyException("下一节点不允许发起会签"); } var flowAssignInfo = await GetNextStepFlowAssignInfoAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, cancellationToken); //var nextStepHandlers = await GetNextStepHandlersAsync(workflow, nextStepDefine, dto, cancellationToken); await _workflowDomainService.NextAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, flowAssignInfo, expiredTime, current, cancellationToken); return workflow; } /// /// 退回(返回前一节点) /// public async Task PreviousAsync(PreviousWorkflowDto dto, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withSteps: true, withTraces: true, withCountersigns: true, cancellationToken: cancellationToken); //var user = await _userRepository.Queryable() // .Includes(x => x.Organization) // .FirstAsync(x => x.Id == _sessionContext.RequiredUserId, cancellationToken); return await _workflowDomainService.PreviousAsync(workflow, dto, _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles, _sessionContext, cancellationToken); } /// /// 工单退回(返回前一节点) /// public async Task PreviousAsync(PreviousWorkflowDto dto, string applicantId, string applicantOrgId, string[] applicantRoleIds, ISessionContext current, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withSteps: true, withTraces: true, withCountersigns: true, cancellationToken: cancellationToken); return await _workflowDomainService.PreviousAsync(workflow, dto, applicantId, applicantOrgId, applicantRoleIds, _sessionContext, cancellationToken); } /// /// 撤回至任意节点 /// public async Task RecallAsync(RecallDto dto, DateTime? expiredTime, CancellationToken cancellationToken) { var validator = new RecallDtoValidator(); var validationResult = await validator.ValidateAsync(dto, cancellationToken); if (!validationResult.IsValid) throw new UserFriendlyException(string.Join(',', validationResult.Errors)); var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true, withTraces: true, cancellationToken: cancellationToken); //await _orderDomainService.ReadyToRecallAsync(workflow.ExternalId, cancellationToken); var targetStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode); if (targetStepDefine.StepType is EStepType.End) throw UserFriendlyException.SameMessage("结束节点不支持撤回"); //var isStartCountersign = targetStepDefine.CouldPrevStartCountersign(dto.NextHandlers.Count); var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.IsStartCountersign, dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken); //var stepHandlers = await GetNextStepHandlersAsync(workflow, targetStepDefine, dto, cancellationToken); await _workflowDomainService.RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, expiredTime, _sessionContext, cancellationToken); } /// /// 无视流程模板配置直接将当前节点办理至结束节点 /// public async Task HandleToEndAsync(ISessionContext current, string workflowId, string opinion, List files, EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default) { var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, cancellationToken: cancellationToken); var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine(); if (endStepDefine is null) throw new UserFriendlyException("未正确配置结束节点"); var dto = new NextWorkflowDto { WorkflowId = workflowId, NextStepCode = endStepDefine.Code, NextStepName = endStepDefine.Name, FlowDirection = EFlowDirection.OrgToFile, BusinessType = endStepDefine.BusinessType, ReviewResult = reviewResult, Opinion = opinion, Files = files }; await NextAsync(dto, current, cancellationToken: cancellationToken); } /// /// 跳转至结束节点(无视流程模板配置以及当前办理对象,直接跳至结束节点) /// public async Task JumpToEndAsync(ISessionContext current, string workflowId, string opinion, List files, EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default) { var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true, cancellationToken: cancellationToken); var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine(); if (endStepDefine is null) throw new UserFriendlyException("未正确配置结束节点"); //var currentStep = workflow.GetActualStep(); //if (currentStep 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(); //todo 结束会签 foreach (var step in unhandleSteps) { await _workflowDomainService.HandleStepAsync(current, step, workflow, dto, null, null, null, cancellationToken); 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); await _workflowDomainService.EndAsync(workflow, dto, endStepDefine, unhandleSteps.First(), current, cancellationToken); } /// /// 查询开始流程的下一步待选节点 /// public async Task> GetStartStepsAsync(string moduleCode, CancellationToken cancellationToken) { var wfModule = await GetWorkflowModuleAsync(moduleCode, 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(); if (startStepDefine.InstanceMode is EInstanceMode.Dynamic && !_workflowDomainService.DynamicShouldTerminal(startStepDefine, _sessionContext.OrgLevel)) { var nextStepOption = await GetDynamicStepAsync(startStepDefine.InstancePolicy.Value, startStepDefine.StepType, startStepDefine.BusinessType, cancellationToken); return new NextStepsDto { Steps = new List { nextStepOption } }; } var firstStepDefines = definition.FindStepDefines(startStepDefine.NextSteps.Select(d => d.Code)); if (!firstStepDefines.Any()) throw new UserFriendlyException("未正确配置首个办理节点"); return new NextStepsDto { Steps = await GetConfigStepsAsync(definition.FlowType, startStepDefine.StepType, startStepDefine.BusinessType, firstStepDefines, cancellationToken) }; } /// /// 查询办理流程的下一步待选节点 /// public async Task> GetNextStepsAsync(string workflowId, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, cancellationToken: cancellationToken); var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow, _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles); if (currentStep.StepType is EStepType.End) throw new UserFriendlyException("结束节点无需办理"); var dto = new NextStepsWithOpinionDto { CanReject = workflow.IsReviewType() && currentStep.CanReject, //ExpiredTime = workflow.ExpiredTime, CanStartCountersign = currentStep.CanStartCountersign, CurrentStepBusinessType = currentStep.BusinessType, TimeTypeOptions = EnumExts.GetDescriptions().ToList(), IsMainHandlerShow = workflow.WorkflowDefinition.IsMainHandlerShow, }; var currentStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, currentStep.Code); if (currentStep.InstanceMode is EInstanceMode.Dynamic && !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, _sessionContext.OrgLevel)) { //动态生成下一步 var nextStepOption = await GetDynamicStepAsync(currentStep.InstancePolicy.Value, currentStep.StepType, currentStep.BusinessType, cancellationToken); dto.Steps = new List { nextStepOption }; return dto; } if (currentStep.IsInCountersign()) { if (currentStep.IsCountersignEndStep) { // 宜宾需求:会签汇总节点展示会签办理节点办理意见 var countersignHandleSteps = workflow.Steps.Where(d => d.CountersignId == currentStep.CountersignId && d.CountersignPosition is ECountersignPosition.Multi or ECountersignPosition.Single).ToList(); var sb = new StringBuilder(); foreach (var countersignHandleStep in countersignHandleSteps) { //sb.AppendLine($"{countersignHandleStep.GetActualHandler()?.GetHandler().Value} : {countersignHandleStep.Opinion}"); sb.AppendLine($"{countersignHandleStep.GetHandler().Value} : {countersignHandleStep.Opinion}"); } dto.Opinion = sb.ToString(); //当前待办节点为会签汇总节点时:检查是否为顶级会签汇总节点,t:按配置往下走,f:继续往上汇总,不需要重复往下指派 if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)) { var startCountersignStep = GetCsLoopStartStep(workflow, currentStep); ////查找当前节点对应会签开始节点的上级作为下一个cs汇总节点的汇总对象 //var startCountersignStep = // workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId); //if (startCountersignStep is null) // throw new UserFriendlyException( // $"未查询到会签开始节点,workflowId: {workflow.Id}, startStepId: {currentStep.CountersignStartStepId}", // "未查询到会签开始节点,数据异常"); var countersignEndOption = GetCsEndStepByTargetPrev(workflow.Steps, startCountersignStep); //按会签策略 var nextStepOption = await GetDynamicStepAsync(currentStep.CountersignPolicy.Value, EStepType.Normal, currentStep.BusinessType, cancellationToken); dto.Steps = new List { nextStepOption, countersignEndOption }; return dto; } } else { //汇总节点 var countersignEndOption = GetCsEndStepByTargetPrev(workflow.Steps, currentStep); //按会签策略 var nextStepOption = await GetDynamicStepAsync(currentStep.CountersignPolicy.Value, EStepType.Normal, currentStep.BusinessType, cancellationToken); dto.Steps = new List { nextStepOption, countersignEndOption }; return dto; } } var nextDefines = workflow.WorkflowDefinition.FindStepDefines(currentStep.NextSteps.Select(d => d.Code)); if (!nextDefines.Any()) throw new UserFriendlyException("未正确配置下一节点"); dto.Steps = await GetConfigStepsAsync(workflow, currentStep, nextDefines, cancellationToken); // 宜宾需求:汇总节点展示前一级节点办理意见 if (currentStep.StepType is EStepType.Summary && !currentStep.IsCountersignEndStep && string.IsNullOrEmpty(dto.Opinion)) { var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId); dto.Opinion = $"{prevStep?.GetHandler().Value} : {prevStep?.Opinion}"; } return dto; } 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); } /// /// 查询撤回可选节点及办理对象 /// public async Task> GetRecallStepsAsync(string workflowId, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, cancellationToken: cancellationToken); var isEnd = workflow.Steps.Any(x => x.StepType == EStepType.End); var currentStep = workflow.Steps.FirstOrDefault(d => d.Id == workflow.ActualHandleStepId); if (currentStep is null) throw new UserFriendlyException("无效当前节点编号"); var quer = workflow.Steps.Where(d => d.StepType != EStepType.End && d.IsOrigin); if (!isEnd) { quer = quer.Where(d => d.Id != currentStep.Id); } var originSteps = quer.ToList(); var stepCodes = originSteps.Select(d => d.Code).ToList(); var stepDefines = workflow.WorkflowDefinition.FindStepDefines(stepCodes); var dto = new NextStepsDto { TimeTypeOptions = EnumExts.GetDescriptions().ToList() }; var steps = await GetRecallConfigStepsAsync(originSteps, stepDefines, currentStep, workflow.FlowType, workflow.Status is EWorkflowStatus.Completed, cancellationToken); dto.Steps = steps; return dto; } private async Task> GetRecallConfigStepsAsync( List originSteps, List stepDefines, WorkflowStep currentStep, EFlowType flowType, bool isWorkflowFiled, CancellationToken cancellationToken) { var steps = new List(); foreach (var originStep in originSteps) { var stepDefine = stepDefines.First(d => d.Code == originStep.Code); var nextStepOption = await GetConfigStepAsync(flowType, stepDefine, cancellationToken); var stepOption = _mapper.Map(nextStepOption); if (stepDefine.StepType is EStepType.End) { steps.Add(stepOption); continue; } stepOption.InputRealHandler = false; ////已归档工单,撤回至中心看作otc,撤回至部门看作cto //stepOption.FlowDirection = isWorkflowFiled // ? stepDefine.BusinessType is EBusinessType.Center or EBusinessType.Send // ? EFlowDirection.OrgToCenter // : stepDefine.BusinessType is EBusinessType.Department // ? EFlowDirection.CenterToOrg // : null // : CheckFlowDirection(currentStep.BusinessType, stepDefine.BusinessType); //需求已调整为特提必重算期满时间 //需求:撤回选择办理对象时,默认选中该节点原办理对象 if (originStep.Handlers.Any()) stepOption.Handler = originStep.Handlers.FirstOrDefault(d => d.Key == originStep.HandlerId || d.Key == originStep.HandlerOrgId); //if (originStep.StepHandlers.Any()) // stepOption.Handler = originStep.GetActualHandler()?.GetHandler() ?? new(); steps.Add(stepOption); } return steps; } /// /// 否决 /// public async Task RejectAsync(RejectDto dto, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withDefine: true, cancellationToken: cancellationToken); //var basicDto = _mapper.Map(dto); //basicDto.NextStepCode = string.Empty; //basicDto.IsStartCountersign = false; //await _workflowDomainService.RejectAsync(workflow, basicDto, cancellationToken); var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine(); var nextDto = _mapper.Map(dto); nextDto.ReviewResult = EReviewResult.Failed; nextDto.NextStepCode = endStepDefine.Code; nextDto.NextStepName = endStepDefine.Name; nextDto.FlowDirection = _sessionContext.OrgIsCenter ? EFlowDirection.CenterToFile : EFlowDirection.OrgToFile; await NextAsync(nextDto, _sessionContext, cancellationToken: cancellationToken); } //供开启流程调用 private async Task> GetConfigStepsAsync( EFlowType flowType, EStepType currentStepType, EBusinessType currentBusinessType, List stepDefines, CancellationToken cancellationToken) { var stepOptions = new List(); foreach (var stepDefine in stepDefines) { var nextStepOption = await GetConfigStepAsync(flowType, stepDefine, cancellationToken); nextStepOption.InputRealHandler = currentStepType == EStepType.Normal && stepDefine.StepType is EStepType.Summary or EStepType.End; stepOptions.Add(nextStepOption); if (stepDefine.StepType is EStepType.End) continue; nextStepOption.FlowDirection = _workflowDomainService.GetFlowDirection(currentBusinessType, stepDefine.BusinessType); //stepOptions.Add(nextStepOption); } return stepOptions; } /// /// 办理流程调用(根据办理过程过滤汇总节点待选办理对象) /// private async Task> GetConfigStepsAsync( Workflow workflow, WorkflowStep currentStep, //EStepType currentStepType, //EBusinessType currentBusinessType, List stepDefines, CancellationToken cancellationToken) { var stepOptions = new List(); foreach (var stepDefine in stepDefines) { NextStepOption nextStepOption; //汇总节点只能选择对应节点办理对象 if (workflow.FlowType is EFlowType.Handle && stepDefine.StepType is EStepType.Summary) { //根据汇总对象id找到被汇总节点 var summaryTargetStep = workflow.Steps.FirstOrDefault(d => d.StepType == EStepType.Normal && d.Code == stepDefine.SummaryTargetCode && d.IsOrigin); if (summaryTargetStep is null) throw UserFriendlyException.SameMessage("未查询到汇总对象节点"); //var handlers = summaryTargetStep.Handlers // .Where(d => d.Key == summaryTargetStep.HandlerId || d.Key == summaryTargetStep.HandlerOrgId).ToList(); var handler = new FlowStepHandler { Key = summaryTargetStep.HandlerId, Value = summaryTargetStep.HandlerName, UserId = summaryTargetStep.HandlerId, Username = summaryTargetStep.HandlerName }; nextStepOption = new NextStepOption { Key = stepDefine.Code, Value = stepDefine.Name, StepType = stepDefine.StepType, BusinessType = stepDefine.BusinessType, //HandlerType = stepDefine.HandlerType, HandlerType = EHandlerType.AssignedUser,//指定办理人(业务需求) Items = new List { handler }//handlers }; } else { nextStepOption = await GetConfigStepAsync(workflow.FlowType, stepDefine, cancellationToken); } nextStepOption.InputRealHandler = currentStep.StepType == EStepType.Normal && stepDefine.StepType is EStepType.Summary or EStepType.End; stepOptions.Add(nextStepOption); if (stepDefine.StepType is EStepType.End) continue; nextStepOption.FlowDirection = _workflowDomainService.GetFlowDirection(currentStep.BusinessType, stepDefine.BusinessType); } return stepOptions; } public async Task GetConfigStepAsync(EFlowType flowType, StepDefine stepDefine, CancellationToken cancellationToken) { var handlers = new List(); if (stepDefine.StepType is EStepType.End) { return new NextStepOption { Key = stepDefine.Code, Value = stepDefine.Name, StepType = stepDefine.StepType, BusinessType = stepDefine.BusinessType, HandlerType = stepDefine.HandlerType, Items = handlers }; } var orgId = _sessionContext.RequiredOrgId; var levelOneOrgId = orgId.GetHigherOrgId(); var isCenter = levelOneOrgId.IsCenter(); switch (stepDefine.HandlerType) { case EHandlerType.AssignedUser: handlers = await _userRepository.Queryable() .Includes(d => d.Organization) .Where(d => stepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Id)) .Select(d => new FlowStepHandler { Key = d.Id, Value = d.Name, UserId = d.Id, Username = d.Name, OrgId = d.OrgId, OrgName = d.Organization.Name, }) .ToListAsync(cancellationToken); break; case EHandlerType.AssignedOrg: handlers = await _organizeRepository.Queryable() //stepDefine.HandlerTypeItems; .Where(d => d.IsEnable && stepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Id)) .Select(d => new FlowStepHandler { Key = d.Id, Value = d.Name, OrgId = d.Id, OrgName = d.Name }) .ToListAsync(cancellationToken); break; case EHandlerType.Role: //当前操作人所属部门的下级部门并且属于配置包含角色 var roles = await _roleRepository.Queryable() .Includes( d => d.Accounts.Where(x => !x.IsDeleted && x.Status == EAccountStatus.Normal && x.AccountType == EAccountType.Personal).ToList(), x => x.User, s => s.Organization) .Where(d => stepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Name)) .ToListAsync(cancellationToken); _logger.LogInformation($"角色: {string.Join(",", roles.Select(d => d.Name))}"); var users1 = roles.SelectMany(d => d.Accounts).Select(d => d.User); //工单办理:除一级部门选择中心汇总(中心会签流程,返回topCountersignStep场景),其余只能选下级部门 if (flowType is EFlowType.Handle && (stepDefine.StepType != EStepType.Summary && stepDefine.BusinessType != EBusinessType.Center)) users1 = users1.Where(d => d.OrgId.StartsWith(levelOneOrgId)); handlers = users1.Where(d => d != null && !string.IsNullOrEmpty(d.Id)) .Select(d => new FlowStepHandler { Key = d.Id, Value = d.Name, UserId = d.Id, Username = d.Name, OrgId = d.OrgId, OrgName = d.Organization.Name }) .ToList(); break; case EHandlerType.OrgLevel: //当前操作人所属部门的垂直部门并且属于配置orgLevel的部门 var levels = stepDefine.HandlerTypeItems.Select(d => int.Parse(d.Key)).ToList(); // var orgs1 = await _organizeRepository.Queryable() // .Where(d => d.IsEnable && levels.Contains(d.Level)) // .WhereIF(!isCenter, d => d.Id.StartsWith(levelOneOrgId)) // .ToListAsync(cancellationToken); _logger.LogInformation($"部门等级: {string.Join(",", levels)}"); var query = _organizeRepository.Queryable() .Where(d => d.IsEnable); List orgs1; if (isCenter) { orgs1 = await query .Where(d => levels.Contains(d.Level) && !d.IsCenter) .ToListAsync(cancellationToken); } else { var upLevels = levels.Where(d => d <= _sessionContext.OrgLevel).ToList(); var lowLevels = levels.Where(d => d > _sessionContext.OrgLevel).ToList(); orgs1 = await query .Where(d => (upLevels.Contains(d.Level) && d.Id.StartsWith(levelOneOrgId)) || (lowLevels.Contains(d.Level) && d.Id.Contains(orgId))) .ToListAsync(cancellationToken); } handlers = orgs1.Select(d => new FlowStepHandler { Key = d.Id, Value = d.Name, OrgId = d.Id, OrgName = d.Name }) .ToList(); break; case EHandlerType.OrgType: var types = stepDefine.HandlerTypeItems.Select(d => d.Key) .Select(d => Enum.Parse(d)); var orgs2 = await _organizeRepository.Queryable() .Where(d => d.IsEnable && types.Contains(d.OrgType)) .WhereIF(!isCenter, d => d.Id.StartsWith(levelOneOrgId)) .ToListAsync(cancellationToken); handlers = orgs2.Select(d => new FlowStepHandler { Key = d.Id, Value = d.Name, OrgId = d.Id, OrgName = d.Name }) .ToList(); break; default: throw new ArgumentOutOfRangeException(); } return new NextStepOption { Key = stepDefine.Code, Value = stepDefine.Name, StepType = stepDefine.StepType, BusinessType = stepDefine.BusinessType, HandlerType = stepDefine.HandlerType, Items = handlers }; } private NextStepOption GetCsEndStepByTargetPrev(List steps, WorkflowStep step) { var prevStep = steps.FirstOrDefault(d => d.Id == step.PrevStepId); if (prevStep is null) throw new UserFriendlyException("未查找到会签上级节点"); var text = prevStep.HandlerOrgIsCenter.Value ? "热线中心会签汇总" : $"{prevStep.HandlerOrgId.CalcOrgLevel().ToChinese()}级部门会签汇总"; var handlers = prevStep.Handlers .Where(d => d.Key == prevStep.HandlerId || d.Key == prevStep.HandlerOrgId).ToList(); //var handler = prevStep.GetActualHandler()?.GetHandler(); var handler = new FlowStepHandler { Key = prevStep.HandlerId, Value = prevStep.HandlerName, UserId = prevStep.HandlerId, Username = prevStep.HandlerName, OrgId = prevStep.HandlerOrgId, OrgName = prevStep.HandlerOrgName }; return new NextStepOption { Key = prevStep.Code, Value = text, BackToCountersignEnd = true, StepType = EStepType.Summary, BusinessType = prevStep.BusinessType, //HandlerType = prevStep.HandlerType, HandlerType = EHandlerType.AssignedUser,//指定办理人(业务需求) Items = new List { handler }//handlers //new List { new(prevStep.HandlerId, prevStep.HandlerName) }, }; } /// /// 依据当前操作人及动态策略生成下一步节点 /// private NextStepOption CreateDynamicStep(EDynamicPolicy policy) { int orgLevel; switch (policy) { case EDynamicPolicy.OrgUpCenterTop: orgLevel = _sessionContext.OrgLevel - 1; if (orgLevel < 0) orgLevel = 0; break; case EDynamicPolicy.OrgUp: orgLevel = _sessionContext.OrgLevel - 1; if (orgLevel <= 0) orgLevel = 1; break; case EDynamicPolicy.OrgDownCenterTop: orgLevel = _sessionContext.OrgIsCenter ? 1 : _sessionContext.OrgLevel + 1; break; case EDynamicPolicy.OrgDown: orgLevel = _sessionContext.OrgLevel + 1; break; default: throw new ArgumentOutOfRangeException(nameof(policy), policy, null); } return new NextStepOption { Key = orgLevel.ToString(), Value = orgLevel == 0 ? "热线中心" : $"{orgLevel.ToChinese()}级部门", }; } private async Task GetDynamicStepAsync( EDynamicPolicy policy, EStepType stepType, EBusinessType currentBusinessType, CancellationToken cancellationToken) { int orgLevel; List items; string upperOrgId; EBusinessType businessType; EFlowDirection? flowDirection = null; switch (policy) { case EDynamicPolicy.OrgUpCenterTop: orgLevel = _sessionContext.OrgLevel - 1; if (orgLevel < 0) orgLevel = 0; if (orgLevel == 0) { businessType = EBusinessType.Send; if (currentBusinessType == EBusinessType.Department) flowDirection = EFlowDirection.OrgToCenter; items = await _organizeRepository.Queryable() .Where(d => d.IsCenter) .Select(d => new FlowStepHandler { Key = d.Id, Value = d.Name, OrgId = d.Id, OrgName = d.Name }) .ToListAsync(cancellationToken); } else { businessType = EBusinessType.Department; //上级部门Id upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel); items = await _organizeRepository.Queryable() .Where(d => d.Id == upperOrgId) .Select(d => new FlowStepHandler { Key = d.Id, Value = d.Name, OrgId = d.Id, OrgName = d.Name }) .ToListAsync(cancellationToken); } break; case EDynamicPolicy.OrgUp: businessType = _sessionContext.OrgIsCenter ? EBusinessType.Send : _sessionContext.RequiredOrgId.CalcOrgLevel() == 1 ? EBusinessType.Send : EBusinessType.Department; orgLevel = _sessionContext.OrgLevel - 1; if (orgLevel <= 0) orgLevel = 1; //上级部门Id upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel); items = await _organizeRepository.Queryable() .Where(d => d.Id == upperOrgId) .Select(d => new FlowStepHandler { Key = d.Id, Value = d.Name, OrgId = d.Id, OrgName = d.Name }) .ToListAsync(cancellationToken); break; case EDynamicPolicy.OrgDownCenterTop: businessType = EBusinessType.Department; if (_sessionContext.OrgIsCenter) { orgLevel = 1; items = await _organizeRepository.Queryable() .Where(d => !d.IsCenter && d.Level == orgLevel) .Select(d => new FlowStepHandler { Key = d.Id, Value = d.Name, OrgId = d.Id, OrgName = d.Name }) .ToListAsync(cancellationToken); } else { orgLevel = _sessionContext.OrgLevel + 1; items = await _organizeRepository.Queryable() .Where(d => !d.IsCenter && d.Level == orgLevel && d.Id.StartsWith(_sessionContext.RequiredOrgId)) .Select(d => new FlowStepHandler { Key = d.Id, Value = d.Name, OrgId = d.Id, OrgName = d.Name }) .ToListAsync(cancellationToken); } break; case EDynamicPolicy.OrgDown: businessType = EBusinessType.Department; orgLevel = _sessionContext.OrgLevel + 1; items = await _organizeRepository.Queryable() .Where(d => d.Level == orgLevel && d.Id.StartsWith(_sessionContext.RequiredOrgId)) .Select(d => new FlowStepHandler { Key = d.Id, Value = d.Name, OrgId = d.Id, OrgName = d.Name }) .ToListAsync(cancellationToken); break; default: throw new ArgumentOutOfRangeException(nameof(policy), policy, null); } return new NextStepOption { Key = orgLevel.ToString(), Value = orgLevel == 0 ? "热线中心" : $"{orgLevel.ToChinese()}级部门", FlowDirection = flowDirection, StepType = stepType, BusinessType = businessType, HandlerType = EHandlerType.OrgLevel, //目前所有动态策略均属于部门等级 Items = items }; } /// /// 查询下一节点办理对象类型(user or org)及实际办理对象 /// public 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); //按会签策略判断,目前所有策略为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)) { //汇总节点(非顶级) //var csStartStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId); //if (csStartStep is null) // throw new UserFriendlyException("未查询到会签开始节点"); 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); //按会签策略判断,目前所有策略为org return FlowAssignInfo.Create(assignType, handlers, isStartCountersign); } } } if (isNextDynamic) return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign); return await GetNextStepFlowAssignInfoByDefineAsync(nextStepDefine, isStartCountersign, handlers, cancellationToken); } //private async ValueTask> GetNextStepHandlersAsync(Workflow workflow, // StepDefine nextStepDefine, BasicWorkflowDto dto, CancellationToken cancellationToken) //{ // var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType); // //var assignType = AssignInfo.GetAssignType(nextStepDefine.HandlerType, dto.NextHandlers.Any()); // switch (assignType) // { // case EFlowAssignType.Org: // return dto.NextHandlers.Select(d => WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId, // assignType, orgId: d.Key, orgName: d.Value)).ToList(); // case EFlowAssignType.User: // if (!dto.NextHandlers.Any() && dto.HandlerType is EHandlerType.Role) // { // var stepOption = await GetConfigStepAsync(EFlowType.Handle, nextStepDefine, cancellationToken); // var uIds = stepOption.Items.Select(d => d.Key).ToList(); // var users1 = await _userRepository.Queryable() // .Includes(d => d.Organization) // .Where(d => uIds.Contains(d.Id)) // .ToListAsync(cancellationToken); // return users1.Select(d => WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId, // assignType, d.Id, d.Name, d.OrgId, d.Organization.Name)) // .ToList(); // } // var userIds = dto.NextHandlers.Select(d => d.Key).ToList(); // var users = await _userRepository.Queryable() // .Includes(d => d.Organization) // .Where(d => userIds.Contains(d.Id)) // .ToListAsync(cancellationToken); // return users.Select(d => WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId, // assignType, d.Id, d.Name, d.OrgId, d.Organization.Name)) // .ToList(); // //case EFlowAssignType.Role: // // handlers = dto.NextHandlers.Select(d => WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId, // // assignType, roleId: d.Key, roleName: d.Value)).ToList(); // // break; // default: // throw new ArgumentOutOfRangeException(); // } //} /// /// 按流程模板配置创建下一步办理对象 /// private async Task GetNextStepFlowAssignInfoByDefineAsync(StepDefine nextStepDefine, bool isStartCountersign, List handlers, CancellationToken cancellationToken) { switch (nextStepDefine.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); default: throw new ArgumentOutOfRangeException(); } } #region private /// /// 查询流程业务模块 /// /// /// /// /// public 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; } /// /// 检查退回节点信息 /// public async Task<(WorkflowStep currentStep, WorkflowStep prevStep, bool isOrgToCenter, bool isSecondToFirstOrgLevel)> GetPreviousInformationAsync(string workflowId, string operatorId, string operatorOrgId, string[] roles, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withSteps: true, withCountersigns: true, cancellationToken: cancellationToken); var (currentStep, prevStep, _) = _workflowDomainService.GetPreviousStep(workflow, operatorId, operatorOrgId, roles); var isOrgToCenter = currentStep.BusinessType is EBusinessType.Department && prevStep.BusinessType is EBusinessType.Center or EBusinessType.Send; var isSecondToFirstOrgLevel = currentStep.HandlerType is EHandlerType.OrgLevel && currentStep.Handlers.First().Key.CheckIfOrgLevelIs(2) && prevStep.HandlerType is EHandlerType.OrgLevel && prevStep.Handlers.First().Key.CheckIfOrgLevelIs(1) && !prevStep.Handlers.First().Key.IsCenter(); return (currentStep, prevStep, isOrgToCenter, isSecondToFirstOrgLevel); } /// /// 开启流程直接归档 /// public async Task StartToEndAsync(StartWorkflowDto dto, ISessionContext current, string externalId, DateTime? expiredTime = null, CancellationToken cancellationToken = default) { 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 endStepDefine = definition.FindEndStepDefine(); dto.NextStepCode = endStepDefine.Code; dto.NextStepName = endStepDefine.Name; dto.FlowDirection = EFlowDirection.CenterToFile; await StartWorkflowAsync(dto, current, externalId, expiredTime, cancellationToken); } /// /// 更新省平台办理结果节点附件 /// public async Task UpdateProvinceHandleResultFilesAsync(string workflowId, List files, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withSteps: true, withTraces: true, cancellationToken: cancellationToken); var step = workflow.Steps.FirstOrDefault(d => //d.StepHandlers.Any(d => d.OrgId == "001171" || d.OrgId == "001178")); d.HandlerOrgId == "001171" || d.HandlerOrgId == "001178"); if (step is not null) { step.FileJson = await _fileRepository.AddFileAsync(files, workflow.ExternalId, step.Id, cancellationToken); var trace = workflow.Traces.First(d => d.StepId == step.Id); trace.FileJson = step.FileJson; await _workflowStepRepository.UpdateAsync(step, cancellationToken); await _workflowTraceRepository.UpdateAsync(trace, cancellationToken); } } #endregion }