using Hotline.Application.Contracts.Validators.FlowEngine; using Hotline.Caching.Interfaces; using Hotline.Caching.Services; using Hotline.FlowEngine; using Hotline.FlowEngine.Definitions; using Hotline.FlowEngine.WfModules; using Hotline.FlowEngine.Workflows; using Hotline.Identity.Accounts; using Hotline.Identity.Roles; using Hotline.Orders; using Hotline.SeedData; using Hotline.Settings; using Hotline.Settings.TimeLimits; using Hotline.Share.Dtos; using Hotline.Share.Dtos.FlowEngine; using Hotline.Share.Enums.FlowEngine; using Hotline.Share.Enums.Identity; using Hotline.Users; using MapsterMapper; using SqlSugar; using XF.Domain.Authentications; using XF.Domain.Dependency; using XF.Domain.Entities; using XF.Domain.Exceptions; 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 readonly IWorkflowAssignRepository _workflowAssignRepository; private readonly IUserRepository _userRepository; private readonly IAccountRepository _accountRepository; private readonly ISystemOrganizeRepository _organizeRepository; private readonly IRoleRepository _roleRepository; private readonly IWfModuleCacheManager _wfModuleCacheManager; private readonly ISystemDomainService _systemDomainService; private readonly IUserDomainService _userDomainService; private readonly IAccountDomainService _accountDomainService; private readonly ISessionContext _sessionContext; private readonly IMapper _mapper; public WorkflowApplication( IDefinitionDomainService definitionDomainService, IWorkflowDomainService workflowDomainService, IOrderDomainService orderDomainService, IWorkflowRepository workflowRepository, IWorkflowAssignRepository workflowAssignRepository, IUserRepository userRepository, IAccountRepository accountRepository, ISystemOrganizeRepository organizeRepository, IRoleRepository roleRepository, IWfModuleCacheManager wfModuleCacheManager, ISystemDomainService systemDomainService, ISessionContext sessionContext, IMapper mapper) { _definitionDomainService = definitionDomainService; _workflowDomainService = workflowDomainService; _orderDomainService = orderDomainService; _workflowRepository = workflowRepository; _workflowAssignRepository = workflowAssignRepository; _userRepository = userRepository; _accountRepository = accountRepository; _organizeRepository = organizeRepository; _roleRepository = roleRepository; _wfModuleCacheManager = wfModuleCacheManager; _systemDomainService = systemDomainService; _sessionContext = sessionContext; _mapper = mapper; } public async Task StartWorkflowAsync(StartWorkflowDto dto, string externalId, CancellationToken cancellationToken) { 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 nextStepBoxDefine = _workflowDomainService.GetStepBoxDefine(definition, dto.NextStepCode); if (dto.IsStartCountersign && (nextStepBoxDefine.StepType is EStepType.CountersignEnd || nextStepBoxDefine.CountersignMode is not ECountersignMode.Support)) throw UserFriendlyException.SameMessage("下一节点不支持办理会签"); //1. 如果不是按角色指派,handlers必填 2. 如果按角色指派,handlers可以不选 if (nextStepBoxDefine.HandlerType is not EHandlerType.Role && !dto.NextHandlers.Any()) throw UserFriendlyException.SameMessage("未指派办理人"); var workflow = await _workflowDomainService.CreateWorkflowAsync(wfModule, dto.Title, _sessionContext.RequiredUserId, _sessionContext.RequiredOrgCode, externalId, cancellationToken); var flowAssignMode = await GetFlowAssignModeAsync(nextStepBoxDefine, dto.IsStartCountersign, dto.NextHandlers, cancellationToken); await _workflowDomainService.StartAsync(workflow, dto, nextStepBoxDefine, flowAssignMode, cancellationToken); ////更新接办部门(详情页面展示) //await AddOrUpdateAssignAsync(workflow, dto, nextStepBoxDefine, cancellationToken); } /// /// 流转至下一节点(节点办理) /// 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.ExpiredTime <= DateTime.Now || dto.ExpiredTime > workflow.ExpiredTime)) throw UserFriendlyException.SameMessage("节点期满时间无效"); var nextStepBoxDefine = _workflowDomainService.GetStepBoxDefine(workflow.Definition, dto.NextStepCode); //需求:按角色选择办理人可以不选,表示该角色下所有人都可以办理,同时依据配置:是否本部门人办理显示待选办理人。角色下只要一人办理即可(即:角色下不发起会签) if (nextStepBoxDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any()) throw new UserFriendlyException("未指定节点处理者"); if (dto.IsStartCountersign && (nextStepBoxDefine.StepType is EStepType.CountersignEnd || nextStepBoxDefine.CountersignMode is not ECountersignMode.Support)) throw UserFriendlyException.SameMessage("下一节点不支持办理会签"); var flowAssignMode = await GetFlowAssignModeAsync(nextStepBoxDefine, dto.IsStartCountersign, dto.NextHandlers, cancellationToken); await _workflowDomainService.NextAsync(workflow, dto, nextStepBoxDefine, dto.IsStartCountersign, flowAssignMode, dto.ExpiredTime, cancellationToken); ////更新接办部门(详情页面展示) //await AddOrUpdateAssignAsync(workflow, dto, nextStepBoxDefine, cancellationToken); } /// /// 撤回至任意节点 /// public async Task RecallAsync(NextWorkflowDto dto, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, true, true, cancellationToken: cancellationToken); var targetStepDefine = _workflowDomainService.GetStepBoxDefine(workflow.Definition, dto.NextStepCode); //var isStartCountersign = targetStepDefine.CouldPrevStartCountersign(dto.NextHandlers.Count); var flowAssignMode = await GetFlowAssignModeAsync(targetStepDefine, dto.IsStartCountersign, dto.NextHandlers, cancellationToken); await _workflowDomainService.RecallAsync(workflow, dto, targetStepDefine, flowAssignMode, cancellationToken); } /// /// 跳转至任意节点 /// public async Task JumpAsync(NextWorkflowDto dto, CancellationToken cancellationToken) { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, true, true, cancellationToken: cancellationToken); var targetStepDefine = _workflowDomainService.GetStepBoxDefine(workflow.Definition, dto.NextStepCode); //var isStartCountersign = targetStepDefine.CouldPrevStartCountersign(dto.NextHandlers.Count); var flowAssignMode = await GetFlowAssignModeAsync(targetStepDefine, dto.IsStartCountersign, dto.NextHandlers, cancellationToken); await _workflowDomainService.JumpAsync(workflow, dto, targetStepDefine, dto.IsStartCountersign, flowAssignMode, 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.FindSteps(startStep.NextSteps.Select(d => d.Code)); var firstStep = definition.FindStep(startStep.NextSteps.First().Code); var secondStepDefines = definition.FindSteps(firstStep.NextSteps.Select(d => d.Code)); return new DefinedNextStepsDto { Id = definition.Id, Steps = _mapper.Map>(secondStepDefines)//secondStepDefines.Select(d => new KeyValuePair(d.Code, d.Name)).ToList() }; } /// /// 根据节点配置查询待选参数 /// /// /// /// /// public async Task GetNextStepOptionsAsync(StepDefine stepDefine, CancellationToken cancellationToken) { if (stepDefine.StepType == EStepType.End) return new(); var handlers = new List(); switch (stepDefine.HandlerType) { case EHandlerType.AssignUser: var users = await _userRepository.QueryAsync(d => !d.IsDeleted && stepDefine.HandlerClassifies.Select(d => d.Id).Contains(d.Id)); handlers = users.Select(d => new IdName(d.Id, d.Name)).ToList(); break; case EHandlerType.AssignOrg: var orgs = await _organizeRepository.QueryAsync(d => d.IsEnable && stepDefine.HandlerClassifies.Select(d => d.Id).Contains(d.OrgCode)); handlers = orgs.Select(d => new IdName(d.OrgCode, d.OrgName)) .ToList(); 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.HandlerClassifies.Select(d => d.Id).Contains(d.Name)) .ToListAsync(cancellationToken); var users1 = roles.SelectMany(d => d.Accounts).Select(d => d.User); handlers = users1.Select(d => new IdName(d.Id, d.Name)).ToList(); break; case EHandlerType.OrgLevel: //当前操作人所属部门的下级部门并且属于配置orgLevel的部门 var levels = stepDefine.HandlerClassifies.Select(d => d.Id).Select(d => int.Parse(d)); var levelOneOrg = _sessionContext.RequiredOrgCode.GetOrgUpper(); var orgs1 = await _organizeRepository.QueryAsync(d => d.IsEnable && d.OrgCode.StartsWith(levelOneOrg) && levels.Contains(d.OrgLevel)); handlers = orgs1.Select(d => new IdName(d.OrgCode, d.OrgName)).ToList(); break; case EHandlerType.OrgType: var types = stepDefine.HandlerClassifies.Select(d => d.Id) .Select(d => Enum.Parse(d)); var levelOneOrg1 = _sessionContext.RequiredOrgCode.GetOrgUpper(); var org2 = await _organizeRepository.QueryAsync(d => d.IsEnable && d.OrgCode.StartsWith(levelOneOrg1) && types.Contains(d.OrgType)); handlers = org2.Select(d => new IdName(d.OrgCode, d.OrgName)).ToList(); break; default: throw new ArgumentOutOfRangeException(); } var dto = new NextStepOptionDto { Handlers = handlers }; if (stepDefine.Properties.Contains(SysDicTypeConsts.OrderRedoReason)) { var orderRedoReasons = await _systemDomainService.GetSysDicDataByCodeAsync(SysDicTypeConsts.OrderRedoReason); dto.OrderRedoReasonOptions = orderRedoReasons.Select(d => new IdName(d.DicDataValue, d.DicDataName)).ToList(); } return dto; } /// /// 查询指派办理人的处理方式及实际办理人 /// public async Task GetFlowAssignModeAsync(StepDefine stepDefine, bool isStartCountersign, List handlers, CancellationToken cancellationToken) { if (stepDefine.StepType is EStepType.Start or EStepType.End) return new(); switch (stepDefine.HandlerType) { case EHandlerType.Role: if (!handlers.Any()) { var roles = await _roleRepository.Queryable() .Includes(d => d.Accounts, x => x.User) .Where(d => stepDefine.HandlerClassifies.Select(d => d.Id).Contains(d.Name)) .ToListAsync(); handlers = roles.SelectMany(d => d.Accounts).Distinct().Select(d => new IdName(d.Id, d.User.Name)).ToList(); } return FlowAssignInfo.Create(EFlowAssignType.User, handlers, isStartCountersign); case EHandlerType.OrgLevel: case EHandlerType.OrgType: case EHandlerType.AssignOrg: return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign); case EHandlerType.AssignUser: 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; } /// /// 更新接办部门 /// private async Task AddOrUpdateAssignAsync(Workflow workflow, BasicWorkflowDto dto, StepDefine nextStepBoxDefine, CancellationToken cancellationToken) { if (nextStepBoxDefine.StepType is EStepType.Normal) { await _workflowAssignRepository.RemoveAsync(d => d.WorkflowId == workflow.Id && d.OrgCode == _sessionContext.RequiredOrgCode, cancellationToken: cancellationToken); var assigns = new List(); switch (nextStepBoxDefine.HandlerType) { case EHandlerType.Role: if (dto.NextHandlers.Any()) { //选了handler,handler为userId var users1 = await _userRepository.Queryable() .Includes(d => d.Organization) .Where(d => dto.NextHandlers.Select(x => x.Id).Contains(d.Id)) .ToListAsync(); assigns = users1.Select(d => WorkflowAssign.Create(workflow.Id, d.OrgCode, d.Organization.OrgName)) .ToList(); } else { ////如果模板配置为本部门办理,则为当前办理人orgCode,非本部门办理:属于该角色的所有用户所属部门 //if (nextStepBoxDefine.OnlySelfOrg.HasValue && nextStepBoxDefine.OnlySelfOrg.Value) //{ // assigns = new List // { // WorkflowAssign.Create(workflow.Id, _sessionContext.RequiredOrgCode, _sessionContext.OrgName) // }; //} //else //{ var accounts = await _accountRepository.Queryable() .Includes(d => d.User, d => d.Organization) .Where(d => dto.NextHandlers.Select(d => d.Id).Contains(d.Name)) .ToListAsync(); assigns = accounts.Select(d => d.User.Organization).Select(d => WorkflowAssign.Create(workflow.Id, d.OrgCode, d.OrgName)).ToList(); //} } break; case EHandlerType.OrgLevel: case EHandlerType.OrgType: case EHandlerType.AssignOrg: assigns = dto.NextHandlers.Select(d => WorkflowAssign.Create(workflow.Id, d.Id, d.Name)).ToList(); break; case EHandlerType.AssignUser: //指定人所属部门 var users = await _userRepository.Queryable() .Includes(d => d.Organization) .Where(d => dto.NextHandlers.Select(x => x.Id).Contains(d.Id)) .ToListAsync(); assigns = users.Select(d => WorkflowAssign.Create(workflow.Id, d.OrgCode, d.Organization.OrgName)) .ToList(); break; default: throw new ArgumentOutOfRangeException(); } if (assigns.Any()) await _workflowAssignRepository.AddRangeAsync(assigns, cancellationToken); } } #endregion }