123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- using Hotline.FlowEngine.Definitions;
- using Hotline.FlowEngine.Workflows;
- using Hotline.Identity.Accounts;
- using Hotline.Identity.Roles;
- using Hotline.SeedData;
- using Hotline.Settings;
- using Hotline.Share.Dtos.FlowEngine;
- using Hotline.Share.Enums.FlowEngine;
- using Hotline.Users;
- 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 IWorkflowRepository _workflowRepository;
- private readonly IWorkflowAssignRepository _workflowAssignRepository;
- private readonly IUserRepository _userRepository;
- private readonly IAccountRepository _accountRepository;
- private readonly ISystemOrganizeRepository _organizeRepository;
- private readonly IRoleRepository _roleRepository;
- private readonly IUserDomainService _userDomainService;
- private readonly IAccountDomainService _accountDomainService;
- private readonly ISessionContext _sessionContext;
- public WorkflowApplication(
- IDefinitionDomainService definitionDomainService,
- IWorkflowDomainService workflowDomainService,
- IWorkflowRepository workflowRepository,
- IWorkflowAssignRepository workflowAssignRepository,
- IUserRepository userRepository,
- IAccountRepository accountRepository,
- ISystemOrganizeRepository organizeRepository,
- IRoleRepository roleRepository,
- ISessionContext sessionContext)
- {
- _definitionDomainService = definitionDomainService;
- _workflowDomainService = workflowDomainService;
- _workflowRepository = workflowRepository;
- _workflowAssignRepository = workflowAssignRepository;
- _userRepository = userRepository;
- _accountRepository = accountRepository;
- _organizeRepository = organizeRepository;
- _roleRepository = roleRepository;
- _sessionContext = sessionContext;
- }
- public async Task<string> StartWorkflowAsync(StartWorkflowDto dto, string externalId, CancellationToken cancellationToken = default)
- {
- var definition = await _definitionDomainService.GetLastVersionDefinitionAsync(dto.DefinitionCode, cancellationToken);
- if (definition == null)
- throw new UserFriendlyException("无效模板名称");
- if (definition.Status != EDefinitionStatus.Enable)
- throw new UserFriendlyException("该模板不可用");
- var exists = await _workflowDomainService.ExsitsAsync(definition.ModuleCode, externalId, cancellationToken);
- if (exists)
- throw UserFriendlyException.SameMessage($"该工单已发起过{WorkflowModule.Modules[definition.ModuleCode]}流程");
- var nextStepBoxDefine = _workflowDomainService.GetStepBoxDefine(definition, dto.NextStepCode);
- var workflow = await _workflowDomainService.CreateWorkflowAsync(definition, dto.Title, externalId, cancellationToken);
- await _workflowDomainService.StartAsync(workflow, dto, nextStepBoxDefine, cancellationToken);
- //更新接办部门(详情页面展示)
- await AddOrUpdateAssignAsync(workflow, dto, nextStepBoxDefine, cancellationToken);
- return workflow.Id;
- }
- /// <summary>
- /// 流转至下一节点(节点办理)
- /// </summary>
- /// <param name="dto"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
- public async Task NextAsync(NextWorkflowDto dto, CancellationToken cancellationToken)
- {
- var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, true, true, cancellationToken: cancellationToken);
- var nextStepBoxDefine = _workflowDomainService.GetStepBoxDefine(workflow.Definition, dto.NextStepCode);
- var isOutOfCallCenter =
- await CheckIfFlowOutOfCallCenterAsync(nextStepBoxDefine, dto.NextMainHandler, cancellationToken);
- var isStartCountersign = nextStepBoxDefine.IsStartCountersign(dto.Handlers.Count);
- await _workflowDomainService.NextAsync(workflow, dto, nextStepBoxDefine, isOutOfCallCenter, isStartCountersign, cancellationToken);
- //更新接办部门(详情页面展示)
- await AddOrUpdateAssignAsync(workflow, dto, nextStepBoxDefine, cancellationToken);
- }
- /// <summary>
- /// 查询流程下一节点配置参数
- /// </summary>
- /// <param name="moduleCode"></param>
- /// <param name="externalId"></param>
- /// <returns></returns>
- public async Task<IReadOnlyList<NextStepOptions>> GetNextStepOptionsAsync(string moduleCode, string externalId, CancellationToken cancellationToken)
- {
- var workflow = await _workflowDomainService.GetWorkflowAsync(moduleCode, externalId, true, true, cancellationToken: cancellationToken);
- if (workflow == null)
- throw new UserFriendlyException($"未查询到流程, moduleCode:{moduleCode}, externalId: {externalId}", "未查询到该流程");
- return await GetNextStepOptionsAsync(workflow, cancellationToken);
- }
- public async Task<IReadOnlyList<NextStepOptions>> GetNextStepOptionsAsync(Workflow workflow, CancellationToken cancellationToken)
- {
- var nextStepDefines = _workflowDomainService.GetNextStepOptions(workflow, cancellationToken);
- //todo 性能问题
- var items = new List<NextStepOptions>();
- foreach (var nextStepDefine in nextStepDefines)
- {
- var options = new NextStepOptions
- {
- Code = nextStepDefine.Code,
- Name = nextStepDefine.Name,
- };
- switch (nextStepDefine.HandlerType)
- {
- case EHandlerType.AssignUser:
- var users = await _userRepository.QueryAsync(d => nextStepDefine.HandlerClassifies.Select(d => d.Id).Contains(d.Id));
- options.NextSteps = users.Select(d => new KeyValuePair<string, string>(d.Id, d.Name)).ToList();
- break;
- case EHandlerType.AssignOrg:
- var orgs = await _organizeRepository.QueryAsync(d => nextStepDefine.HandlerClassifies.Select(d => d.Id).Contains(d.OrgCode));
- options.NextSteps = orgs.Select(d => new KeyValuePair<string, string>(d.OrgCode, d.OrgName)).ToList();
- break;
- case EHandlerType.Role:
- var roles = await _roleRepository.Queryable().Includes(d => d.Accounts, d => d.User)
- .Where(d => nextStepDefine.HandlerClassifies.Select(d => d.Id).Contains(d.Name)).ToListAsync();
- var users1 = roles.SelectMany(d => d.Accounts).Select(d => d.User);
- if (nextStepDefine.OnlySelfOrg ?? false)
- users1 = users1.Where(d => d.OrgCode == _sessionContext.RequiredOrgCode);
- options.NextSteps = users1.Select(d => new KeyValuePair<string, string>(d.Id, d.Name)).ToList();
- break;
- case EHandlerType.OrgLevel:
- //当前操作人所属部门的下级部门并且属于配置orgLevel的部门
- var levels = nextStepDefine.HandlerClassifies.Select(d => d.Id).Select(d => int.Parse(d));
- var orgs1 = await _organizeRepository.QueryAsync(d =>
- d.IsEnable && d.OrgCode.StartsWith(_sessionContext.RequiredOrgCode) &&
- levels.Contains(d.OrgLevel));
- options.NextSteps = orgs1.Select(d => new KeyValuePair<string, string>(d.OrgCode, d.OrgName)).ToList();
- break;
- case EHandlerType.OrgType:
- var types = nextStepDefine.HandlerClassifies.Select(d => d.Id).Select(d => Enum.Parse<EOrgType>(d));
- var org2 = await _organizeRepository.QueryAsync(d =>
- d.IsEnable && d.OrgCode.StartsWith(_sessionContext.RequiredOrgCode) &&
- types.Contains(d.OrgType));
- options.NextSteps = org2.Select(d => new KeyValuePair<string, string>(d.OrgCode, d.OrgName)).ToList();
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- items.Add(options);
- }
- return items;
- }
- /// <summary>
- /// 指派下一节点办理人
- /// </summary>
- public async Task AssignAsync<TEntity>(TEntity entity, EFlowAssignType flowAssignType, IEnumerable<string> handlers, CancellationToken cancellationToken)
- where TEntity : IWorkflow
- {
- switch (flowAssignType)
- {
- case EFlowAssignType.Org:
- case EFlowAssignType.User:
- entity.Assign(flowAssignType, handlers);
- break;
- case EFlowAssignType.Role:
- var roles = await _roleRepository.Queryable()
- .Includes(d => d.Accounts)
- .Where(d => handlers.Contains(d.Name))
- .ToListAsync();
- var userIds = roles.SelectMany(d => d.Accounts).Select(d => d.Id);
- entity.Assign(EFlowAssignType.User, userIds);
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(flowAssignType), flowAssignType, null);
- }
- }
- #region private
- /// <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.Handlers.Any())
- {
- //选了handler,handler为userId
- var users1 = await _userRepository.Queryable()
- .Includes(d => d.Organization)
- .Where(d => dto.Handlers.Select(d => d.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.Handlers.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.Handlers.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.Handlers.Select(d => d.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);
- }
- }
- private async Task<bool> CheckIfFlowOutOfCallCenterAsync(StepDefine nextStepBoxDefine, string mainHandler, CancellationToken CancellationToken)
- {
- //current is center & next is not center return true
- var isFromCallCenter = IsOrgFromCallCenter(_sessionContext.RequiredOrgCode);
- if (!isFromCallCenter) return false;
- var isOutCallCenter = await CheckNextStepHandlerIsOutCallCenterAsync(nextStepBoxDefine, mainHandler, CancellationToken);
- return isFromCallCenter && isOutCallCenter;
- }
- private async Task<bool> CheckNextStepHandlerIsOutCallCenterAsync(StepDefine nextStepBoxDefine, string mainHandler, CancellationToken cancellationToken)
- {
- if (nextStepBoxDefine.HandlerType is EHandlerType.AssignUser or EHandlerType.Role)
- {
- //mainHandler is userId
- var handler = await _userRepository.GetAsync(mainHandler, cancellationToken);
- if (handler == null)
- throw new UserFriendlyException($"mainHandler未找到对应User, handler: {mainHandler}");
- if (string.IsNullOrEmpty(handler.OrgCode))
- throw UserFriendlyException.SameMessage($"该用户未配置所属部门, userId: {mainHandler}");
- return !IsOrgFromCallCenter(handler.OrgCode);
- }
- else
- {
- //mainHandler is orgCode
- return !IsOrgFromCallCenter(mainHandler);
- }
- }
- /// <summary>
- /// 是否为呼叫中心部门
- /// </summary>
- /// <returns></returns>
- private bool IsOrgFromCallCenter(string orgCode) => orgCode.StartsWith(OrgSeedData.CallCenterCode);
- #endregion
- }
|