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; using Hotline.Configurations; using Hotline.Share.Dtos.Order.Handle; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; using Newtonsoft.Json; using NPOI.SS.Formula.Functions; using DocumentFormat.OpenXml.Drawing; using Hotline.Share.Dtos.FlowEngine.Workflow; using SqlSugar; using Hotline.SeedData; namespace Hotline.Application.FlowEngine; public class WorkflowApplication : IWorkflowApplication, IScopeDependency { private readonly IWorkflowDomainService _workflowDomainService; private IRepository _workflowStepRepository; private IRepository _workflowTraceRepository; private readonly IRepository _workflowCountersignRepository; private readonly IRepository _userRepository; private readonly ISystemOrganizeRepository _organizeRepository; private readonly IRepository _roleRepository; private readonly IWfModuleCacheManager _wfModuleCacheManager; private readonly ISessionContextProvider _sessionContextProvider; private readonly IMapper _mapper; private readonly IFileRepository _fileRepository; private readonly ILogger _logger; private readonly ISystemSettingCacheManager _systemSettingCacheManager; public WorkflowApplication( IWorkflowDomainService workflowDomainService, IRepository workflowStepRepository, IRepository workflowTraceRepository, IRepository workflowCountersignRepository, IRepository userRepository, ISystemOrganizeRepository organizeRepository, IRepository roleRepository, IWfModuleCacheManager wfModuleCacheManager, ISessionContextProvider sessionContextProvider, IMapper mapper, IFileRepository fileRepository, ISystemSettingCacheManager systemSettingCacheManager, ILogger logger) { _workflowDomainService = workflowDomainService; _workflowStepRepository = workflowStepRepository; _workflowTraceRepository = workflowTraceRepository; _workflowCountersignRepository = workflowCountersignRepository; _userRepository = userRepository; _organizeRepository = organizeRepository; _roleRepository = roleRepository; _wfModuleCacheManager = wfModuleCacheManager; _sessionContextProvider = sessionContextProvider; _mapper = mapper; _fileRepository = fileRepository; _logger = logger; _systemSettingCacheManager = systemSettingCacheManager; } public async Task StartWorkflowAsync(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 && !_workflowDomainService.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 = await _workflowDomainService.CreateWorkflowAsync(wfModule, dto.Title, _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.RequiredOrgId, externalId, cancellationToken); //var startStepHandles = new List{WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId, // EFlowAssignType.User, current.RequiredUserId, current.UserName, // current.RequiredOrgId, current.OrgName)}; var defineHandler = startStepDefine.HandlerTypeItems.First(); var startStep = _workflowDomainService.CreateStartStep(workflow, startStepDefine, dto, 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, }, expiredTime); var flowAssignInfo = await GetNextStepFlowAssignInfoAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic, cancellationToken); var counterSignType = _workflowDomainService.GetCounterSignType(dto.IsStartCountersign, startStep.BusinessType); //办理开始节点 await _workflowDomainService.HandleStepAsync(startStep, workflow, dto, counterSignType, expiredTime, cancellationToken); 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; //_mapper.Map(dto, startTrace); 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); await _workflowDomainService.StartAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic, flowAssignInfo, counterSignType, expiredTime, cancellationToken); return workflow.Id; } /// /// 开始流程并停留在开始节点(开始节点作为待办节点) /// public async Task StartWorkflowToStartStepAsync(StartWorkflowDto dto, string externalId, DateTime? expiredTime = null, 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 workflow = await _workflowDomainService.CreateWorkflowAsync(wfModule, dto.Title, _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.RequiredOrgId, externalId, cancellationToken); var defineHandler = startStepDefine.HandlerTypeItems.First(); //todo 需求:所有坐席都可以办,临时方案,后期重构:依据策略决定指派对象 var startStep = _workflowDomainService.CreateStartStep(workflow, startStepDefine, dto, 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, }, expiredTime, EFlowAssignType.Role); 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; //_mapper.Map(dto, startTrace); 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); return workflow.Id; } /// /// 流转至下一节点(节点办理) /// public async Task NextAsync(NextWorkflowDto dto, DateTime? expiredTime = null, CancellationToken cancellationToken = default) { var validator = new NextWorkflowDtoValidator(); var validResult = await validator.ValidateAsync(dto, cancellationToken); if (!validResult.IsValid) throw new UserFriendlyException( $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}"); 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); 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("该状态不支持继续办理"); var currentStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, currentStep.Code); //下一节点是否为动态节点 var isNextDynamic = currentStepDefine.InstanceMode is EInstanceMode.Dynamic && !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, _sessionContextProvider.SessionContext.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, 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); return await _workflowDomainService.PreviousAsync(workflow, dto, _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName, _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName, _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgIsCenter, _sessionContextProvider.SessionContext.Roles, cancellationToken); } /// /// 工单退回(返回前一节点) /// public async Task PreviousAsync(PreviousWorkflowDto dto, string applicantId, string applicantOrgId, string[] applicantRoleIds, 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 == applicantId, cancellationToken); return await _workflowDomainService.PreviousAsync(workflow, dto, applicantId, user.Name, applicantOrgId, user.Organization.Name, user.Organization.AreaCode, user.Organization.AreaName, user.Organization.IsCenter, applicantRoleIds, cancellationToken); } /// /// 撤回至之前任意节点 /// public async Task RecallAsync(RecallDto dto, DateTime? expiredTime, bool isOrderFiled, EWorkflowTraceType traceType, 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, withCountersigns: 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 targetStep = workflow.Steps.FirstOrDefault(d => d.Code == dto.NextStepCode && d.IsOrigin); if (targetStep is null) throw UserFriendlyException.SameMessage("该流程尚未流转至该节点"); ///退回到派单组 没有下一步办理人 获取之前节点办理人 if (!dto.NextHandlers.Any()) { dto.NextHandlers.Add(new FlowStepHandler() { UserId = targetStep.HandlerId, Username = targetStep.HandlerName, OrgId = targetStep.HandlerOrgId, OrgName = targetStep.HandlerOrgName, Key = targetStep.HandlerId, Value = targetStep.HandlerName, RoleId = targetStep.RoleId, RoleName = targetStep.RoleName }); } var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, 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, traceType, expiredTime, isOrderFiled, cancellationToken); } /// /// 无视流程模板配置直接将当前节点办理至结束节点 /// public async Task HandleToEndAsync(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 unHandleStep = workflow.Steps.FirstOrDefault(d => d.Status is not EWorkflowStepStatus.Handled); if (unHandleStep is null) throw new UserFriendlyException($"无待办节点, workflowId: {workflowId}", "无待办节点"); var dto = new NextWorkflowDto { WorkflowId = workflowId, NextStepCode = endStepDefine.Code, NextStepName = endStepDefine.Name, FlowDirection = EFlowDirection.OrgToFile, BusinessType = endStepDefine.BusinessType, ReviewResult = reviewResult, Opinion = opinion, Files = files, StepId = unHandleStep.Id }; await NextAsync(dto, cancellationToken: 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, _sessionContextProvider.SessionContext.OrgLevel)) { var settingHandle = _systemSettingCacheManager.GetSetting(SettingConstants.RoleJingBanRen); var settingLead = _systemSettingCacheManager.GetSetting(SettingConstants.RoleLingDao); var nextStepOption = await GetDynamicStepAsync(startStepDefine.InstancePolicy.Value, startStepDefine.StepType, startStepDefine.BusinessType, settingHandle?.SettingValue[0], settingLead?.SettingValue[0], 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 { TimeTypeOptions = EnumExts.GetDescriptions().ToList(), 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, _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.Roles); if (currentStep.StepType is EStepType.End) throw new UserFriendlyException("结束节点无需办理"); return await GetNextStepsAsync(workflow, currentStep, cancellationToken); } public async Task> GetNextStepsAsync(string workflowId, string stepId, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, cancellationToken: cancellationToken); var currentStep = workflow.Steps.FirstOrDefault(d => d.Id == stepId); if (currentStep == null) throw new UserFriendlyException($"未查询到选择节点, workflowId: {workflowId}, stepId: {stepId}"); if (currentStep.StepType is EStepType.End) throw new UserFriendlyException("结束节点无需办理"); return await GetNextStepsAsync(workflow, currentStep, cancellationToken); } /// /// 跨级指派查询下一步可选节点及办理对象参数 /// public async Task> GetCrossLevelStepsAsync(GetCrossLevelStepsDto dto, CancellationToken cancellationToken) { WorkflowDefinition definition; if (string.IsNullOrEmpty(dto.WorkflowId)) { var wfModule = await GetWorkflowModuleAsync(WorkflowModuleConsts.OrderHandle, cancellationToken); definition = wfModule.Definition; } else { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withDefine: true, cancellationToken: cancellationToken); definition = workflow.WorkflowDefinition; } if (definition == null) throw new UserFriendlyException("无效模板编码"); if (definition.Status is not EDefinitionStatus.Enable) throw new UserFriendlyException("该模板不可用"); var currentStepDefine = definition.FindStepDefine(dto.StepCode); var nextStepDefines = definition.FindStepDefines(currentStepDefine.NextSteps.Select(d => d.Code)) .Where(d => d.StepType == EStepType.Normal && d.BusinessType == EBusinessType.Department) .ToList(); var stepOptions = new List(); foreach (var stepDefine in nextStepDefines) { var success = int.TryParse(stepDefine.HandlerTypeItems.First().Key, out var level); int? orgLevel = success ? level : null; var nextStepOption = new NextStepOption { Key = stepDefine.Code, Value = stepDefine.Name, StepType = stepDefine.StepType, BusinessType = stepDefine.BusinessType, HandlerType = stepDefine.HandlerType, OrgLevel = stepDefine.HandlerType is EHandlerType.OrgLevel ? orgLevel : null, }; var orgs = await _organizeRepository.Queryable() .Where(d => d.IsEnable && d.Level == orgLevel && dto.OrgIds.Contains(d.ParentId)) .ToListAsync(cancellationToken); nextStepOption.Items = orgs.Select(d => new FlowStepHandler { Key = d.Id, Value = d.Name, OrgId = d.Id, OrgName = d.Name }) .ToList(); nextStepOption.FlowDirection = _workflowDomainService.GetFlowDirection(dto.BusinessType, stepDefine.BusinessType); stepOptions.Add(nextStepOption); } return new NextStepsDto { CanReject = false, CanStartCountersign = true, CurrentStepBusinessType = dto.BusinessType, Steps = stepOptions }; } private async Task> GetNextStepsAsync(Workflow workflow, WorkflowStep currentStep, CancellationToken cancellationToken) { var dto = new NextStepsWithOpinionDto { CanReject = workflow.IsReviewType() && currentStep.CanReject, //ExpiredTime = workflow.ExpiredTime, CanStartCountersign = currentStep.CanStartCountersign, CurrentStepBusinessType = currentStep.BusinessType, CurrentStepType = currentStep.StepType, CurrentHandlerType = currentStep.HandlerType, TimeTypeOptions = EnumExts.GetDescriptions().ToList(), IsMainHandlerShow = workflow.WorkflowDefinition.IsMainHandlerShow, StepId = currentStep.Id, CurrentOrgLevel = string.IsNullOrEmpty(currentStep.HandlerOrgId) ? null : currentStep.HandlerOrgId.CalcOrgLevel(), Opinion = currentStep.Opinion, }; var currentStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, currentStep.Code); if (currentStep.InstanceMode is EInstanceMode.Dynamic && !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, _sessionContextProvider.SessionContext.OrgLevel)) { var settingHandle = _systemSettingCacheManager.GetSetting(SettingConstants.RoleJingBanRen); var settingLead = _systemSettingCacheManager.GetSetting(SettingConstants.RoleLingDao); //动态生成下一步 var nextStepOption = await GetDynamicStepAsync(currentStep.InstancePolicy.Value, currentStep.StepType, currentStep.BusinessType, settingHandle?.SettingValue[0], settingLead?.SettingValue[0], cancellationToken); dto.Steps = new List { nextStepOption }; return dto; } if (workflow.IsInCountersign && 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 query = workflow.Steps.Where(d => d.StepType != EStepType.End && d.IsOrigin); if (!isEnd) { query = query.Where(d => d.Id != currentStep.Id); } var originSteps = query.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 //todo 依据归档节点的busiType判断 stepOption.FlowDirection = isWorkflowFiled ? stepDefine.BusinessType is EBusinessType.Seat or EBusinessType.Send ? EFlowDirection.FiledToCenter : stepDefine.BusinessType is EBusinessType.Department or EBusinessType.DepartmentLeader ? EFlowDirection.FiledToOrg : null //: CheckFlowDirection(currentStep.BusinessType, stepDefine.BusinessType); : _workflowDomainService.GetFlowDirection(currentStep.BusinessType, stepDefine.BusinessType); //需求已调整为特提必重算期满时间 //需求:撤回选择办理对象时,默认选中该节点原办理对象 //if (originStep.Handlers.Any()) // stepOption.Handler = originStep.Handlers.FirstOrDefault(d => d.Key == originStep.HandlerId || d.Key == originStep.HandlerOrgId); stepOption.Handler = originStep.GetWorkflowStepHandler(); //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 = _sessionContextProvider.SessionContext.OrgIsCenter ? EFlowDirection.CenterToFile : EFlowDirection.OrgToFile; await NextAsync(nextDto, 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, 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) { var handler = _workflowDomainService.GetSummaryTargetFlowStepHandler(workflow, stepDefine.SummaryTargetCode); 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 = _sessionContextProvider.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); if (flowType is EFlowType.Handle) { //工单办理:除一级部门选择中心汇总(中心会签流程,返回topCountersignStep场景),其余只能选下级部门 if (stepDefine.BusinessType is EBusinessType.Department && stepDefine.StepType != EStepType.Summary) { users1 = users1.Where(d => d.OrgId.StartsWith(levelOneOrgId)); } else if (stepDefine.BusinessType is EBusinessType.DepartmentLeader) { users1 = users1.Where(d => d.OrgId == orgId); } } var defineTypeItem = stepDefine.HandlerTypeItems.First(); 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, RoleId = defineTypeItem.Key, RoleName = defineTypeItem.Value }) .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 <= _sessionContextProvider.SessionContext.OrgLevel).ToList(); var lowLevels = levels.Where(d => d > _sessionContextProvider.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(); } var success = int.TryParse(stepDefine.HandlerTypeItems.First().Key, out var level); int? orgLevel = success ? level : null; return new NextStepOption { Key = stepDefine.Code, Value = stepDefine.Name, StepType = stepDefine.StepType, BusinessType = stepDefine.BusinessType, HandlerType = stepDefine.HandlerType, OrgLevel = stepDefine.HandlerType is EHandlerType.OrgLevel ? orgLevel : null, 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 = _sessionContextProvider.SessionContext.OrgLevel - 1; if (orgLevel < 0) orgLevel = 0; break; case EDynamicPolicy.OrgUp: orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1; if (orgLevel <= 0) orgLevel = 1; break; case EDynamicPolicy.OrgDownCenterTop: orgLevel = _sessionContextProvider.SessionContext.OrgIsCenter ? 1 : _sessionContextProvider.SessionContext.OrgLevel + 1; break; case EDynamicPolicy.OrgDown: orgLevel = _sessionContextProvider.SessionContext.OrgLevel + 1; break; default: throw new ArgumentOutOfRangeException(nameof(policy), policy, null); } var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0]; return new NextStepOption { Key = orgLevel.ToString(), Value = orgLevel == 0 ? centerOrgName : $"{orgLevel.ToChinese()}级部门", }; } /// /// 动态策略 /// /// /// private async Task GetDynamicStepAsync( EDynamicPolicy policy, EStepType stepType, EBusinessType currentBusinessType, string handleRoleCode, string leadRoleCode, CancellationToken cancellationToken) { int orgLevel; List items; string upperOrgId; EBusinessType businessType; EFlowDirection? flowDirection = null; bool isLead = false; bool isSkip = false; string roleId = string.Empty; string roleName = string.Empty; string handleRoleName = "经办人"; string leadRoleName = "领导"; EHandlerType handlerType = EHandlerType.OrgLevel; switch (policy) { case EDynamicPolicy.OrgUpCenterTop: orgLevel = _sessionContextProvider.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 = _sessionContextProvider.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 = _sessionContextProvider.SessionContext.OrgIsCenter ? EBusinessType.Send : _sessionContextProvider.SessionContext.RequiredOrgId.CalcOrgLevel() == 1 ? EBusinessType.Send : EBusinessType.Department; orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1; if (orgLevel <= 0) orgLevel = 1; //上级部门Id upperOrgId = _sessionContextProvider.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.OrgUpHandleCenterTop: orgLevel = _sessionContextProvider.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; roleName = handleRoleName; //上级部门Id upperOrgId = _sessionContextProvider.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, RoleId = handleRoleCode, RoleName = handleRoleName, }) .ToListAsync(cancellationToken); } break; case EDynamicPolicy.OrgUpHandle: businessType = _sessionContextProvider.SessionContext.OrgIsCenter ? EBusinessType.Send : _sessionContextProvider.SessionContext.RequiredOrgId.CalcOrgLevel() == 1 ? EBusinessType.Send : EBusinessType.Department; orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1; if (orgLevel <= 0) orgLevel = 1; roleName = handleRoleName; //上级部门Id upperOrgId = _sessionContextProvider.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, RoleId = handleRoleCode, RoleName = handleRoleName, }) .ToListAsync(cancellationToken); break; case EDynamicPolicy.OrgUpLeadCenterTop: orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1; if (orgLevel < 0) orgLevel = 0; isLead = _sessionContextProvider.SessionContext.Roles.Any(x => x == leadRoleCode); isSkip = await _userRepository.Queryable() .AnyAsync(x => x.OrgId == _sessionContextProvider.SessionContext.RequiredOrgId && x.Roles.Any(r => r.Name == leadRoleCode), cancellationToken); if (orgLevel == 0 && (isLead || !isSkip)) { 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; upperOrgId = _sessionContextProvider.SessionContext.RequiredOrgId.GetHigherOrgId(_sessionContextProvider.SessionContext.OrgLevel); if (!isLead) { if (isSkip) { roleId = leadRoleCode; roleName = leadRoleName; } } if (isLead || !isSkip) { //上级部门Id upperOrgId = _sessionContextProvider.SessionContext.RequiredOrgId.GetHigherOrgId(orgLevel); roleId = handleRoleCode; roleName = handleRoleName; } else { orgLevel += 1; } 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, RoleId = roleId, RoleName = roleName }) .ToListAsync(cancellationToken); } break; case EDynamicPolicy.OrgUpLead: businessType = _sessionContextProvider.SessionContext.OrgIsCenter ? EBusinessType.Send : _sessionContextProvider.SessionContext.RequiredOrgId.CalcOrgLevel() == 1 ? EBusinessType.Send : EBusinessType.Department; orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1; if (orgLevel <= 0) orgLevel = 1; upperOrgId = _sessionContextProvider.SessionContext.RequiredOrgId.GetHigherOrgId(_sessionContextProvider.SessionContext.OrgLevel); isLead = _sessionContextProvider.SessionContext.Roles.Any(x => x == leadRoleCode); if (!isLead) { isSkip = await _userRepository.Queryable() .AnyAsync(x => x.OrgId == _sessionContextProvider.SessionContext.RequiredOrgId && x.Roles.Any(r => r.Name == leadRoleCode), cancellationToken); if (isSkip) { roleId = leadRoleCode; roleName = leadRoleName; } } if (isLead || !isSkip) { //上级部门Id upperOrgId = _sessionContextProvider.SessionContext.RequiredOrgId.GetHigherOrgId(orgLevel); roleId = handleRoleCode; roleName = handleRoleName; } else { orgLevel += 1; } 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, RoleId = roleId, RoleName = roleName }) .ToListAsync(cancellationToken); break; case EDynamicPolicy.ArriveCenter: businessType = _sessionContextProvider.SessionContext.OrgIsCenter ? EBusinessType.Send : _sessionContextProvider.SessionContext.RequiredOrgId.CalcOrgLevel() == 1 ? EBusinessType.Send : EBusinessType.Department; orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1; if (orgLevel <= 0) orgLevel = 1; 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); break; case EDynamicPolicy.ArriveOneOrg: businessType = _sessionContextProvider.SessionContext.OrgIsCenter ? EBusinessType.Send : EBusinessType.Department; orgLevel = _sessionContextProvider.SessionContext.OrgIsCenter ? 0 : 1; //上级部门Id upperOrgId = _sessionContextProvider.SessionContext.OrgIsCenter ? _sessionContextProvider.SessionContext.RequiredOrgId.Substring(0, 3) : _sessionContextProvider.SessionContext.RequiredOrgId.Substring(0, 6); 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 (_sessionContextProvider.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 = _sessionContextProvider.SessionContext.OrgLevel + 1; items = await _organizeRepository.Queryable() .Where(d => !d.IsCenter && d.Level == orgLevel && d.Id.StartsWith(_sessionContextProvider.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 = _sessionContextProvider.SessionContext.OrgLevel + 1; items = await _organizeRepository.Queryable() .Where(d => d.Level == orgLevel && d.Id.StartsWith(_sessionContextProvider.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); } var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0]; return new NextStepOption { Key = orgLevel.ToString(), Value = orgLevel == 0 ? centerOrgName : $"{orgLevel.ToChinese()}级部门 {roleName}", FlowDirection = flowDirection, StepType = stepType, BusinessType = businessType, HandlerType = handlerType, //目前所有动态策略均属于部门等级 Items = items }; } /// /// 会签 动态策略 /// /// private async Task GetDynamicStepAsync( ECountersignPolicy policy, EStepType stepType, EBusinessType currentBusinessType, CancellationToken cancellationToken) { int orgLevel; List items; string upperOrgId; EBusinessType businessType; EFlowDirection? flowDirection = null; switch (policy) { case ECountersignPolicy.OrgUpCenterTop: orgLevel = _sessionContextProvider.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 = _sessionContextProvider.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 ECountersignPolicy.OrgUp: businessType = _sessionContextProvider.SessionContext.OrgIsCenter ? EBusinessType.Send : _sessionContextProvider.SessionContext.RequiredOrgId.CalcOrgLevel() == 1 ? EBusinessType.Send : EBusinessType.Department; orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1; if (orgLevel <= 0) orgLevel = 1; //上级部门Id upperOrgId = _sessionContextProvider.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 ECountersignPolicy.OrgDownCenterTop: businessType = EBusinessType.Department; if (_sessionContextProvider.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 = _sessionContextProvider.SessionContext.OrgLevel + 1; items = await _organizeRepository.Queryable() .Where(d => !d.IsCenter && d.Level == orgLevel && d.Id.StartsWith(_sessionContextProvider.SessionContext.RequiredOrgId)) .Select(d => new FlowStepHandler { Key = d.Id, Value = d.Name, OrgId = d.Id, OrgName = d.Name }) .ToListAsync(cancellationToken); } break; case ECountersignPolicy.OrgDown: businessType = EBusinessType.Department; orgLevel = _sessionContextProvider.SessionContext.OrgLevel + 1; items = await _organizeRepository.Queryable() .Where(d => d.Level == orgLevel && d.Id.StartsWith(_sessionContextProvider.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); } var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0]; return new NextStepOption { Key = orgLevel.ToString(), Value = orgLevel == 0 ? centerOrgName : $"{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, 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 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(); } } public ISugarQueryable QueryOrderCountersigns(QueryOrderCountersignDto dto, ISessionContext _sessionContext) { var Role = _sessionContext.Roles; var query = _workflowCountersignRepository.Queryable() .Includes(x => x.Members) .LeftJoin((c, w) => c.WorkflowId == w.Id) .InnerJoin((c, w, o) => w.ExternalId == o.Id) .WhereIF(!string.IsNullOrEmpty(dto.Title), (c, w, o) => o.Title.Contains(dto.Title)) .WhereIF(!string.IsNullOrEmpty(dto.OrderNo), (c, w, o) => o.No == dto.OrderNo) .WhereIF(!string.IsNullOrEmpty(dto.AcceptType), (c, w, o) => o.AcceptTypeCode == dto.AcceptType)//受理类型 .WhereIF(!string.IsNullOrEmpty(dto.Channel), (c, w, o) => o.SourceChannelCode == dto.Channel)//受理类型 .WhereIF(!string.IsNullOrEmpty(dto.Hotspot), (c, w, o) => o.HotspotSpliceName != null && o.HotspotSpliceName.Contains(dto.Hotspot))//热点类型 .WhereIF(!string.IsNullOrEmpty(dto.OrgId), (c, w, o) => c.FinisherOrgId == dto.OrgId) //接办部门 .WhereIF(dto.CounterSignType != null, (c, w, o) => c.CounterSignType == dto.CounterSignType) //会签类型 ; var rolePaiDan = _systemSettingCacheManager.GetSetting(SettingConstants.RolePaiDan)?.SettingValue[0];//派单员角色 var seatsMonitor = _systemSettingCacheManager.GetSetting(SettingConstants.SeatsMonitor)?.SettingValue[0];//班长角色 var isAdmin = false; var systemAdministrator = _systemSettingCacheManager.GetSetting(SettingConstants.SystemAdministrator)?.SettingValue[0];//管理员角色 if (!string.IsNullOrEmpty(systemAdministrator) && (_sessionContext.Roles.Contains(systemAdministrator) || _sessionContext.Roles.Contains(RoleSeedData.AdminRole))) isAdmin = true; //发起会签:班长角色能看所有会签信件;新增管理员能看到所有会签 //派单员角色只能看到自己发起的会签信件; //承办部门用户能看到自己发起的和同级部门用户发起的会签件 if (dto.InitiatedCountersignature.HasValue && dto.InitiatedCountersignature == true) { if (_sessionContext.Roles.Any(p => p == seatsMonitor) || isAdmin) { } else if (_sessionContext.Roles.Any(p => p == rolePaiDan)) { query = query.Where((c, w, o) => c.StarterId == _sessionContext.UserId); } else { query = query.Where((c, w, o) => c.StarterOrgId == _sessionContext.RequiredOrgId); } } //会签已办(会签状态为已结束): //班长角色能看所有已结束的会签信件;新增管理员能看到所有会签 //派单员不会办理会签件只会发起会签件所以这一点派单员角色可以忽略; //承办部门用户能看到和同级部门用户已办理过的会签件 if (dto.HandleCountersignature.HasValue && dto.HandleCountersignature == true) { if (_sessionContext.Roles.Any(p => p == seatsMonitor) || isAdmin) { query = query.Where((c, w, o) => c.EndTime.HasValue); } else if (_sessionContext.Roles.Any(p => p == rolePaiDan)) { query = query.Where((c, w, o) => c.Members.Any(m => m.Key == _sessionContext.RequiredOrgId) && c.EndTime.HasValue); } else { query = query.Where((c, w, o) => c.Members.Any(m => m.Key == _sessionContext.OrgId || m.Key == _sessionContext.RequiredOrgId) && c.EndTime.HasValue); } } var items = query .OrderByDescending((c, w, o) => o.ExpiredTime); return items; } #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.Seat 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, 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, externalId, expiredTime, cancellationToken); } /// /// 更新省平台办理结果节点附件 /// public async Task UpdateProvinceHandleResultFilesAsync(string workflowId, List files, CancellationToken cancellationToken) { var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0]; CityBaseConfiguration cityBase = JsonConvert.DeserializeObject(setting); var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withSteps: true, withTraces: true, cancellationToken: cancellationToken); var step = workflow.Steps.FirstOrDefault(d => //d.StepHandlers.Any(d => d.OrgId == _cityBaseConfiguration.Value.CityProvince.OrgId || d.OrgId == _cityBaseConfiguration.Value.CityProvinceAssign.OrgId)); d.HandlerOrgId == cityBase.CityProvince.OrgId || d.HandlerOrgId == cityBase.CityProvinceAssign.OrgId); 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 }