123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- 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);
- }
- /// <summary>
- /// 流转至下一节点(节点办理)
- /// </summary>
- 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);
- }
- /// <summary>
- /// 撤回至任意节点
- /// </summary>
- 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);
- }
- /// <summary>
- /// 跳转至任意节点
- /// </summary>
- 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<DefinedNextStepsDto> 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<IReadOnlyList<StepBasicDto>>(secondStepDefines)//secondStepDefines.Select(d => new KeyValuePair<string, string>(d.Code, d.Name)).ToList()
- };
- }
- /// <summary>
- /// 根据节点配置查询待选参数
- /// </summary>
- /// <param name="stepDefine"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
- /// <exception cref="ArgumentOutOfRangeException"></exception>
- public async Task<NextStepOptionDto> GetNextStepOptionsAsync(StepDefine stepDefine, CancellationToken cancellationToken)
- {
- if (stepDefine.StepType == EStepType.End) return new();
- var handlers = new List<IdName>();
- 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<EOrgType>(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;
- }
- /// <summary>
- /// 查询指派办理人的处理方式及实际办理人
- /// </summary>
- public async Task<FlowAssignInfo> GetFlowAssignModeAsync(StepDefine stepDefine, bool isStartCountersign, List<IdName> 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
- /// <summary>
- /// 查询流程业务模块
- /// </summary>
- /// <param name="code"></param>
- /// <returns></returns>
- /// <exception cref="UserFriendlyException"></exception>
- private async Task<WorkflowModule> 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;
- }
- /// <summary>
- /// 更新接办部门
- /// </summary>
- 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<WorkflowAssign>();
- 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>
- // {
- // 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
- }
|