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
}