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.Share.Enums.Order; using Hotline.Users; using MapsterMapper; using System.Threading; 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; 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; 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 ) { _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; } public async Task StartWorkflowAsync(StartWorkflowDto dto, string externalId, ExpiredTimeWithConfig? expiredTimeConfig = null, CancellationToken cancellationToken = default) { var validator = new StartWorkflowDtoValidator(); var validResult = validator.Validate(dto); 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 firstStepDefine = startStepDefine.InstanceMode is EInstanceMode.Dynamic && !DynamicShouldTerminal(startStepDefine, _sessionContext.OrgLevel) ? 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, _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, externalId, expiredTimeConfig?.TimeText, expiredTimeConfig?.Count, expiredTimeConfig?.TimeType, expiredTimeConfig?.ExpiredTime, cancellationToken); var startStep = _workflowDomainService.CreateStartStep(workflow, startStepDefine, dto, new List { new(_sessionContext.RequiredUserId, _sessionContext.UserName) }); 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.Status = EWorkflowTraceStatus.Normal; _mapper.Map(dto, startTrace); await _workflowTraceRepository.AddAsync(startTrace, cancellationToken); workflow.Traces.Add(startTrace); var flowAssignInfo = await GetNextStepFlowAssignInfoAsync(workflow, startStepDefine, startStep, firstStepDefine, dto, cancellationToken); await _workflowDomainService.StartAsync(workflow, startStep, dto, firstStepDefine, flowAssignInfo, cancellationToken); return workflow.Id; } /// /// 流转至下一节点(节点办理) /// public async Task NextAsync(NextWorkflowDto dto, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, true, true, withCountersigns: true, cancellationToken: cancellationToken); //await NextAsync(workflow, dto, dto.ExpiredTime, dto.IsStartCountersign, cancellationToken); ////未超期工单,节点超期时间不能小于当前时间,不能大于流程整体超期时间 //if (workflow.ExpiredTime > DateTime.Now && // (dto.StepExpiredTime <= DateTime.Now || dto.StepExpiredTime > workflow.ExpiredTime)) // throw UserFriendlyException.SameMessage("节点期满时间无效"); var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow); if (currentStep.Status is EWorkflowStepStatus.Handled) throw new UserFriendlyException("该状态不支持继续办理"); var currentStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, currentStep.Code); StepDefine nextStepDefine; if ((currentStep.InstanceMode is EInstanceMode.Dynamic && !DynamicShouldTerminal(currentStep)) || (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, currentStepDefine, currentStep, nextStepDefine, dto, cancellationToken); await _workflowDomainService.NextAsync(workflow, currentStep, dto, nextStepDefine, flowAssignInfo, cancellationToken); } /// /// 退回(返回前一节点) /// public async Task PreviousAsync(PreviousWorkflowDto dto, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withSteps: true, cancellationToken: cancellationToken); await _workflowDomainService.PreviousAsync(workflow, dto, cancellationToken); } /// /// 撤回至任意节点 /// public async Task RecallAsync(RecallDto dto, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, true, 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, cancellationToken); await _workflowDomainService.RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, cancellationToken); } /// /// 撤回至派单节点 /// public async Task RecallToStartAsync(string workflowId, string opinion, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, true, true, cancellationToken: cancellationToken); var targetStepDefine = workflow.WorkflowDefinition.FindSendStepDefine(); //var targetStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, targetStep.Code); //var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, false, dto.NextHandlers, cancellationToken); //await _workflowDomainService.RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, cancellationToken); //todo } /// /// 跳转至任意节点 /// public async Task JumpAsync(RecallDto dto, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, true, true, cancellationToken: cancellationToken); await _orderDomainService.ReadyToRecallAsync(workflow.ExternalId, cancellationToken); var targetStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode); //var isStartCountersign = targetStepDefine.CouldPrevStartCountersign(dto.NextHandlers.Count); var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.IsStartCountersign, dto.NextHandlers, cancellationToken); await _workflowDomainService.JumpAsync(workflow, dto, targetStepDefine, flowAssignInfo, cancellationToken); } /// /// 跳转至结束节点(无视流程模板配置直接跳至结束节点) /// public async Task JumpToEndAsync(string workflowId, string opinion, List files, EReviewResult? reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default) { var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, true, 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("未找到实际办理节点"); await _workflowDomainService.EndAsync(workflow, new BasicWorkflowDto { Opinion = opinion, Files = files }, endStepDefine, currentStep, reviewResult, cancellationToken); } /// /// 重办 /// public async Task RedoAsync(RecallDto dto, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, true, true, cancellationToken: cancellationToken); var targetStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode); var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.IsStartCountersign, dto.NextHandlers, cancellationToken); await _workflowDomainService.RedoAsync(workflow, dto, targetStepDefine, flowAssignInfo, cancellationToken); } public async Task GetStartOptionsAsync(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 startStep = definition.Steps.FirstOrDefault(d => d.StepType == EStepType.Start); if (startStep == null) throw new UserFriendlyException("未正确配置发起人节点"); var firstStepDefines = definition.FindStepDefines(startStep.NextSteps.Select(d => d.Code)); if (!firstStepDefines.Any()) throw new UserFriendlyException("未正确配置首个办理节点"); return new DefinedStepDto { DefinitionId = definition.Id, Steps = firstStepDefines // Components = firstStepDefine.Components }; } /// /// 根据节点配置查询待选参数 /// /// /// /// /// public async Task> GetNextStepOptionsAsync(StepDefineBasic stepDefine, CancellationToken cancellationToken) { //if (stepDefine.StepType == EStepType.End) // throw new UserFriendlyException("结束节点无待选项"); //var handlers = new List(); //switch (stepDefine.HandlerType) //{ // // case EHandlerType.AssignedUser: // // var users = await _userRepository.Queryable() // // .Where(d => stepDefine.HandlerTypeItems.Select(x => x.Id).Contains(d.Id)) // // .ToListAsync(cancellationToken); // // handlers = users.Select(d => new Kv(d.Id, d.Name)).ToList(); // // break; // // case EHandlerType.AssignedOrg: // // var orgs = await _organizeRepository.QueryAsync(d => // // d.IsEnable && // // stepDefine.HandlerTypeItems.Select(d => d.Id).Contains(d.Id)); // // handlers = orgs.Select(d => new Kv(d.Id, d.OrgName)) // // .ToList(); // // break; // case EHandlerType.AssignedUser: // case EHandlerType.AssignedOrg: // handlers = stepDefine.HandlerTypeItems; // break; // case EHandlerType.Role: // //当前操作人所属部门的下级部门并且属于配置包含角色 // var roles = await _roleRepository.Queryable() // .Includes(d => d.Accounts.Where(x => !x.IsDeleted && x.Status == EAccountStatus.Normal).ToList(), // x => x.User) // .Where(d => stepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Name)) // .ToListAsync(cancellationToken); // var users1 = roles.SelectMany(d => d.Accounts).Select(d => d.User); // handlers = users1.Select(d => new Kv(d.Id, d.Name)).ToList(); // break; // case EHandlerType.OrgLevel: // //当前操作人所属部门的下级部门并且属于配置orgLevel的部门 // var levels = stepDefine.HandlerTypeItems.Select(d => d.Key).Select(d => int.Parse(d)); // var levelOneOrg = _sessionContext.RequiredOrgId.GetHigherOrgCode(); // //var orgs1 = await _organizeRepository.QueryAsync(d => // // d.IsEnable && d.OrgCode.StartsWith(levelOneOrg) && // // levels.Contains(d.OrgLevel)); // var orgs1 = await _organizeRepository.Queryable() // .Where(d => d.IsEnable && levels.Contains(d.Level)) // .WhereIF(!levelOneOrg.IsCenter(), d => d.Id.StartsWith(levelOneOrg)) // .ToListAsync(cancellationToken); // handlers = orgs1.Select(d => new Kv(d.Id, d.Name)).ToList(); // break; // case EHandlerType.OrgType: // var types = stepDefine.HandlerTypeItems.Select(d => d.Key) // .Select(d => Enum.Parse(d)); // var levelOneOrg1 = _sessionContext.RequiredOrgId.GetHigherOrgCode(); // //var org2 = await _organizeRepository.QueryAsync(d => // // d.IsEnable && d.OrgCode.StartsWith(levelOneOrg1) && // // types.Contains(d.OrgType)); // var orgs2 = await _organizeRepository.Queryable() // .Where(d => d.IsEnable && types.Contains(d.OrgType)) // .WhereIF(!levelOneOrg1.IsCenter(), d => d.Id.StartsWith(levelOneOrg1)) // .ToListAsync(cancellationToken); // handlers = orgs2.Select(d => new Kv(d.Id, d.Name)).ToList(); // break; // default: // throw new ArgumentOutOfRangeException(); //} //var dto = new NextStepOptionDto { Handlers = handlers }; //if (stepDefine.Components.Contains(SysDicTypeConsts.OrderRedoReason)) //{ // var orderRedoReasons = // await _systemDomainService.GetSysDicDataByCodeAsync(SysDicTypeConsts.OrderRedoReason, // cancellationToken); // dto.OrderRedoReasonOptions = orderRedoReasons.Select(d => new Kv(d.DicDataValue, d.DicDataName)).ToList(); //} //return dto; throw new NotImplementedException(); } /// /// 查询开始流程的下一步待选节点 /// 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(); //var dto = new NextStepsDto //{ // DefinitionId = definition.Id, // InstanceMode = startStep.InstanceMode, // DynamicPolicy = startStep.InstancePolicy //}; if (startStepDefine.InstanceMode is EInstanceMode.Dynamic && !DynamicShouldTerminal(startStepDefine, _sessionContext.OrgLevel)) { //var nextStepOption = CreateDynamicStep(startStep.InstancePolicy); //dto.Steps = new List { nextStepOption }; //return dto; 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("未正确配置首个办理节点"); //var steps = firstStepDefines // .Select(d => new NextStepOption { Key = d.Code, Value = d.Name }) // .ToList(); return new NextStepsDto { Steps = await GetConfigStepsAsync(definition.FlowType, startStepDefine.StepType, startStepDefine.BusinessType, firstStepDefines, cancellationToken) }; //dto.Steps = steps; //return dto; } /// /// 查询办理流程的下一步待选节点 /// public async Task> GetNextStepsAsync(string workflowId, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, true, true, cancellationToken: cancellationToken); var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow); if (currentStep.StepType is EStepType.End) throw new UserFriendlyException("结束节点无需办理"); var dto = new NextStepsDto { CanReject = workflow.IsReviewType() && currentStep.CanReject, ExpiredTime = workflow.ExpiredTime, CanStartCountersign = currentStep.CanStartCountersign, CurrentStepBusinessType = currentStep.BusinessType, TimeTypeOptions = EnumExts.GetDescriptions().ToList() }; if (currentStep.InstanceMode is EInstanceMode.Dynamic && !DynamicShouldTerminal(currentStep)) { //动态生成下一步 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) { //当前待办节点为会签汇总节点时:检查是否为顶级会签汇总节点,t:按配置往下走,f:继续往上汇总,不需要重复往下指派 if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)) { //查找当前节点对应会签开始节点的上级作为下一个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 = GetCsEndStepByPrev(workflow.Steps, startCountersignStep); dto.Steps = new List { countersignEndOption }; return dto; } } else { //汇总节点 var countersignEndOption = GetCsEndStepByPrev(workflow.Steps, currentStep); //按会签策略 var nextStepOption = await GetDynamicStepAsync(currentStep.CountersignPolicy.Value, currentStep.StepType, 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.FlowType, currentStep.StepType, currentStep.BusinessType, nextDefines, cancellationToken); //if (currentStep.IsInCountersign() && currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)) //{ // foreach (var nextStepOption in dto.Steps) // { // if (nextStepOption.StepType is EStepType.End or EStepType.Summary) // nextStepOption.InputRealHandler = true; // } //} return dto; } /// /// 查询撤回可选节点及办理对象 /// public async Task> GetRecallStepsAsync(string workflowId, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, true, true, cancellationToken: cancellationToken); var originSteps = workflow.Steps.Where(d => d.StepType != EStepType.End && d.IsOrigin).ToList(); //todo 恢复到可撤回至发起人节点 var stepCodes = originSteps.Select(d => d.Code).ToList(); var stepDefines = workflow.WorkflowDefinition.FindStepDefines(stepCodes); var currentStep = workflow.Steps.FirstOrDefault(d => d.Id == workflow.CurrentStepId); if (currentStep is null) throw new UserFriendlyException("无效当前节点编号"); 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); steps.Add(stepOption); } return steps; } /// /// 查询跳转可选节点 /// public async Task GetJumpStepsAsync(string workflowId, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, true, cancellationToken: cancellationToken); return new NextStepsDto { //Steps = await GetConfigStepsAsync(null, workflow.WorkflowDefinition.Steps, cancellationToken) }; } /// /// 查询重办可选节点 /// public async Task GetRedoStepsAsync(string workflowId, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, true, true, cancellationToken: cancellationToken); var steps = workflow.Steps.Where(d => d.StepType is EStepType.Normal && d.IsOrigin && d.IsOrg()); var stepDefines = workflow.WorkflowDefinition.FindStepDefines(steps.Select(d => d.Code)); return new NextStepsDto { //Steps = await GetConfigStepsAsync(null, stepDefines, cancellationToken) }; } /// /// 否决 /// public async Task RejectAsync(RejectDto dto, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, true, true, cancellationToken: cancellationToken); var basicDto = _mapper.Map(dto); basicDto.NextStepCode = string.Empty; basicDto.IsStartCountersign = false; await _workflowDomainService.RejectAsync(workflow, basicDto, 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 = CheckFlowDirection(currentBusinessType, stepDefine.BusinessType); //stepOptions.Add(nextStepOption); } 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, Items = handlers }; } var orgId = _sessionContext.RequiredOrgId; var levelOneOrgId = orgId.GetHigherOrgId(); var isCenter = levelOneOrgId.IsCenter(); switch (stepDefine.HandlerType) { case EHandlerType.AssignedUser: case EHandlerType.AssignedOrg: handlers = stepDefine.HandlerTypeItems; 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) .Where(d => stepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Name)) .ToListAsync(cancellationToken); 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 Kv(d.Id, d.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); 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 Kv(d.Id, 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 Kv(d.Id, d.Name)).ToList(); break; default: throw new ArgumentOutOfRangeException(); } return new NextStepOption { Key = stepDefine.Code, Value = stepDefine.Name, StepType = stepDefine.StepType, BusinessType = stepDefine.BusinessType, Items = handlers }; } private EFlowDirection? CheckFlowDirection(EBusinessType sourceStepBusinessType, EBusinessType directionStepBusinessType) { switch (sourceStepBusinessType) { case EBusinessType.Center: case EBusinessType.Send: return directionStepBusinessType is EBusinessType.Department ? EFlowDirection.CenterToOrg : null; case EBusinessType.Department: return directionStepBusinessType is EBusinessType.Center or EBusinessType.Send ? EFlowDirection.OrgToCenter : null; case EBusinessType.File: return null; default: throw new ArgumentOutOfRangeException(nameof(sourceStepBusinessType), sourceStepBusinessType, null); } } private bool DynamicShouldTerminal(WorkflowStep step) { if (step.InstanceMode is not EInstanceMode.Dynamic) throw new UserFriendlyException("非动态节点"); switch (step.InstancePolicy) { case EDynamicPolicy.OrgUpCenterTop: case EDynamicPolicy.OrgUp: case EDynamicPolicy.OrgDownCenterTop: case EDynamicPolicy.OrgDown: return step.DynamicShouldTerminal(); default: throw new ArgumentOutOfRangeException(); } } private 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.OrgDownCenterTop: case EDynamicPolicy.OrgDown: return currentStepDefine.TerminalDynamicMark == currentOrgLevel.ToString(); default: throw new ArgumentOutOfRangeException(); } } private NextStepOption GetCsEndStepByPrev(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(); return new NextStepOption { Key = prevStep.Code, Value = text, BackToCountersignEnd = true, StepType = prevStep.StepType, BusinessType = prevStep.BusinessType, Items = 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 businessType, CancellationToken cancellationToken) { int orgLevel; List items; var levelOneOrgCode = _sessionContext.RequiredOrgId.GetHigherOrgId(); switch (policy) { case EDynamicPolicy.OrgUpCenterTop: orgLevel = _sessionContext.OrgLevel - 1; if (orgLevel < 0) orgLevel = 0; if (orgLevel == 0) { items = await _organizeRepository.Queryable() .Where(d => d.IsCenter) .Select(d => new Kv { Key = d.Id, Value = d.Name }) .ToListAsync(cancellationToken); } else { items = await _organizeRepository.Queryable() .Where(d => !d.IsCenter && d.Level == orgLevel && d.Id.StartsWith(levelOneOrgCode)) .Select(d => new Kv { Key = d.Id, Value = d.Name }) .ToListAsync(cancellationToken); } break; case EDynamicPolicy.OrgUp: orgLevel = _sessionContext.OrgLevel - 1; if (orgLevel <= 0) orgLevel = 1; items = await _organizeRepository.Queryable() .Where(d => d.Level == orgLevel && d.Id.StartsWith(levelOneOrgCode)) .Select(d => new Kv { Key = d.Id, Value = d.Name }) .ToListAsync(cancellationToken); break; case EDynamicPolicy.OrgDownCenterTop: if (_sessionContext.OrgIsCenter) { orgLevel = 1; items = await _organizeRepository.Queryable() .Where(d => !d.IsCenter && d.Level == orgLevel) .Select(d => new Kv { Key = d.Id, Value = d.Name }) .ToListAsync(cancellationToken); } else { orgLevel = _sessionContext.OrgLevel + 1; items = await _organizeRepository.Queryable() .Where(d => !d.IsCenter && d.Level == orgLevel && d.Id.StartsWith(levelOneOrgCode)) .Select(d => new Kv { Key = d.Id, Value = d.Name }) .ToListAsync(cancellationToken); } break; case EDynamicPolicy.OrgDown: orgLevel = _sessionContext.OrgLevel + 1; items = await _organizeRepository.Queryable() .Where(d => d.Level == orgLevel && d.Id.StartsWith(levelOneOrgCode)) .Select(d => new Kv { Key = d.Id, Value = 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 = null, StepType = stepType, BusinessType = businessType, Items = items }; } //private async Task> GetConfigStepsAsync(List stepDefines, // CancellationToken cancellationToken) //{ // foreach (var stepDefine in stepDefines) // { // if (stepDefine.StepType == EStepType.End) // throw new UserFriendlyException("结束节点无待选项"); // var handlers = new List(); // switch (stepDefine.HandlerType) // { // case EHandlerType.AssignedUser: // case EHandlerType.AssignedOrg: // handlers = stepDefine.HandlerTypeItems; // break; // case EHandlerType.Role: // //当前操作人所属部门的下级部门并且属于配置包含角色 // var roles = await _roleRepository.Queryable() // .Includes(d => d.Accounts.Where(x => !x.IsDeleted && x.Status == EAccountStatus.Normal).ToList(), // x => x.User) // .Where(d => stepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Name)) // .ToListAsync(cancellationToken); // var users1 = roles.SelectMany(d => d.Accounts).Select(d => d.User); // handlers = users1.Select(d => new Kv(d.Id, d.Name)).ToList(); // break; // case EHandlerType.OrgLevel: // //当前操作人所属部门的下级部门并且属于配置orgLevel的部门 // var levels = stepDefine.HandlerTypeItems.Select(d => d.Key).Select(d => int.Parse(d)); // var levelOneOrg = _sessionContext.RequiredOrgId.GetHigherOrgCode(); // //var orgs1 = await _organizeRepository.QueryAsync(d => // // d.IsEnable && d.OrgCode.StartsWith(levelOneOrg) && // // levels.Contains(d.OrgLevel)); // var orgs1 = await _organizeRepository.Queryable() // .Where(d => d.IsEnable && levels.Contains(d.Level)) // .WhereIF(!levelOneOrg.IsCenter(), d => d.Id.StartsWith(levelOneOrg)) // .ToListAsync(cancellationToken); // handlers = orgs1.Select(d => new Kv(d.Id, d.Name)).ToList(); // break; // case EHandlerType.OrgType: // var types = stepDefine.HandlerTypeItems.Select(d => d.Key) // .Select(d => Enum.Parse(d)); // var levelOneOrg1 = _sessionContext.RequiredOrgId.GetHigherOrgCode(); // //var org2 = await _organizeRepository.QueryAsync(d => // // d.IsEnable && d.OrgCode.StartsWith(levelOneOrg1) && // // types.Contains(d.OrgType)); // var orgs2 = await _organizeRepository.Queryable() // .Where(d => d.IsEnable && types.Contains(d.OrgType)) // .WhereIF(!levelOneOrg1.IsCenter(), d => d.Id.StartsWith(levelOneOrg1)) // .ToListAsync(cancellationToken); // handlers = orgs2.Select(d => new Kv(d.Id, d.Name)).ToList(); // break; // default: // throw new ArgumentOutOfRangeException(); // } // var dto = new NextStepOptionDto { Handlers = handlers }; // if (stepDefine.Components.Contains(SysDicTypeConsts.OrderRedoReason)) // { // var orderRedoReasons = // await _systemDomainService.GetSysDicDataByCodeAsync(SysDicTypeConsts.OrderRedoReason, // cancellationToken); // dto.OrderRedoReasonOptions = orderRedoReasons.Select(d => new Kv(d.DicDataValue, d.DicDataName)).ToList(); // } // return dto; // } // throw new NotImplementedException(); //} /// /// 查询下一节点办理对象类型(user or org)及实际办理对象 /// public async Task GetNextStepFlowAssignInfoAsync(Workflow workflow, StepDefine currentStepDefine, WorkflowStep currentStep, StepDefine nextStepDefine, BasicWorkflowDto dto, CancellationToken cancellationToken) { if (nextStepDefine.StepType is EStepType.End) return new(); var isStartCountersign = dto.IsStartCountersign; var handlers = dto.NextHandlers; if (isStartCountersign) //按会签策略判断,目前所有策略为org return FlowAssignInfo.Create(EFlowAssignType.Org, 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 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 { //按会签策略判断,目前所有策略为org return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign); } } } if (currentStep.InstanceMode is EInstanceMode.Dynamic && !DynamicShouldTerminal(currentStepDefine, _sessionContext.OrgLevel)) return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign); return await GetNextStepFlowAssignInfoByDefineAsync(nextStepDefine, isStartCountersign, handlers, cancellationToken); } /// /// 按流程模板配置创建下一步办理对象 /// 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(d => d.Key).Contains(d.Name)) .ToListAsync(cancellationToken); handlers = roles.SelectMany(d => d.Accounts).Distinct() .Select(d => new Kv(d.Id, d.User.Name)) .ToList(); } 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 /// /// 查询流程业务模块 /// /// /// /// /// 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; } #endregion }