WorkflowApplication.cs 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169
  1. using Hotline.Application.Contracts.Validators.FlowEngine;
  2. using Hotline.Caching.Interfaces;
  3. using Hotline.FlowEngine;
  4. using Hotline.FlowEngine.Definitions;
  5. using Hotline.FlowEngine.WorkflowModules;
  6. using Hotline.FlowEngine.Workflows;
  7. using Hotline.Identity.Accounts;
  8. using Hotline.Identity.Roles;
  9. using Hotline.Orders;
  10. using Hotline.Settings;
  11. using Hotline.Settings.TimeLimits;
  12. using Hotline.Share.Dtos;
  13. using Hotline.Share.Dtos.FlowEngine;
  14. using Hotline.Share.Dtos.FlowEngine.Definition;
  15. using Hotline.Share.Dtos.Settings;
  16. using Hotline.Share.Enums.FlowEngine;
  17. using Hotline.Share.Enums.Identity;
  18. using Hotline.Users;
  19. using MapsterMapper;
  20. using Hotline.File;
  21. using Hotline.Share.Enums.Settings;
  22. using XF.Domain.Authentications;
  23. using XF.Domain.Dependency;
  24. using XF.Domain.Entities;
  25. using XF.Domain.Exceptions;
  26. using XF.Domain.Extensions;
  27. using XF.Domain.Repository;
  28. using XF.Utility.EnumExtensions;
  29. using Hotline.Share.Dtos.File;
  30. using Microsoft.Extensions.Logging;
  31. using System.Text;
  32. namespace Hotline.Application.FlowEngine;
  33. public class WorkflowApplication : IWorkflowApplication, IScopeDependency
  34. {
  35. private readonly IDefinitionDomainService _definitionDomainService;
  36. private readonly IWorkflowDomainService _workflowDomainService;
  37. private readonly IOrderDomainService _orderDomainService;
  38. private readonly IWorkflowRepository _workflowRepository;
  39. private IRepository<WorkflowDefinition> _definitionRepository;
  40. private IRepository<WorkflowStep> _workflowStepRepository;
  41. private IRepository<WorkflowTrace> _workflowTraceRepository;
  42. private readonly IRepository<User> _userRepository;
  43. private readonly IAccountRepository _accountRepository;
  44. private readonly ISystemOrganizeRepository _organizeRepository;
  45. private readonly IRepository<Role> _roleRepository;
  46. private readonly IWfModuleCacheManager _wfModuleCacheManager;
  47. private readonly ISystemDomainService _systemDomainService;
  48. private readonly ITimeLimitDomainService _timeLimitDomainService;
  49. private readonly IUserDomainService _userDomainService;
  50. private readonly IAccountDomainService _accountDomainService;
  51. private readonly ISessionContext _sessionContext;
  52. private readonly IMapper _mapper;
  53. private readonly IFileRepository _fileRepository;
  54. private readonly ILogger<WorkflowApplication> _logger;
  55. public WorkflowApplication(
  56. IDefinitionDomainService definitionDomainService,
  57. IWorkflowDomainService workflowDomainService,
  58. IOrderDomainService orderDomainService,
  59. IWorkflowRepository workflowRepository,
  60. IRepository<WorkflowDefinition> definitionRepository,
  61. IRepository<WorkflowStep> workflowStepRepository,
  62. IRepository<WorkflowTrace> workflowTraceRepository,
  63. IRepository<User> userRepository,
  64. IAccountRepository accountRepository,
  65. ISystemOrganizeRepository organizeRepository,
  66. IRepository<Role> roleRepository,
  67. IWfModuleCacheManager wfModuleCacheManager,
  68. ISystemDomainService systemDomainService,
  69. ITimeLimitDomainService timeLimitDomainService,
  70. ISessionContext sessionContext,
  71. IMapper mapper,
  72. IFileRepository fileRepository,
  73. ILogger<WorkflowApplication> logger)
  74. {
  75. _definitionDomainService = definitionDomainService;
  76. _workflowDomainService = workflowDomainService;
  77. _orderDomainService = orderDomainService;
  78. _workflowRepository = workflowRepository;
  79. _definitionRepository = definitionRepository;
  80. _workflowStepRepository = workflowStepRepository;
  81. _workflowTraceRepository = workflowTraceRepository;
  82. _userRepository = userRepository;
  83. _accountRepository = accountRepository;
  84. _organizeRepository = organizeRepository;
  85. _roleRepository = roleRepository;
  86. _wfModuleCacheManager = wfModuleCacheManager;
  87. _systemDomainService = systemDomainService;
  88. _timeLimitDomainService = timeLimitDomainService;
  89. _sessionContext = sessionContext;
  90. _mapper = mapper;
  91. _fileRepository = fileRepository;
  92. _logger = logger;
  93. }
  94. public async Task<string> StartWorkflowAsync(StartWorkflowDto dto, ISessionContext current, string externalId,
  95. DateTime? expiredTime, CancellationToken cancellationToken = default)
  96. {
  97. var validator = new StartWorkflowDtoValidator();
  98. var validResult = await validator.ValidateAsync(dto, cancellationToken);
  99. if (!validResult.IsValid)
  100. throw new UserFriendlyException(
  101. $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}");
  102. var wfModule = await GetWorkflowModuleAsync(dto.DefinitionModuleCode, cancellationToken);
  103. var definition = wfModule.Definition;
  104. if (definition == null)
  105. throw new UserFriendlyException("无效模板编码");
  106. if (definition.Status is not EDefinitionStatus.Enable)
  107. throw new UserFriendlyException("该模板不可用");
  108. //如果发起会签需检查是否支持发起会签
  109. var startStepDefine = definition.FindStartStepDefine();
  110. //下一节点是否为动态节点
  111. var isNextDynamic = startStepDefine.InstanceMode is EInstanceMode.Dynamic &&
  112. !_workflowDomainService.DynamicShouldTerminal(startStepDefine, current.OrgLevel);
  113. var firstStepDefine = isNextDynamic
  114. ? startStepDefine
  115. : definition.FindStepDefine(dto.NextStepCode);
  116. if (firstStepDefine is null)
  117. throw new UserFriendlyException("未查询到下一步节点配置");
  118. //1. 如果不是按角色指派,handlers必填 2. 如果按角色指派,handlers可以不选
  119. if (firstStepDefine.HandlerType is not EHandlerType.Role && !dto.NextHandlers.Any())
  120. throw UserFriendlyException.SameMessage("未指派办理人");
  121. if (dto.IsStartCountersign)
  122. {
  123. if (!startStepDefine.CanStartCountersign)
  124. throw new UserFriendlyException("当前节点不支持发起会签");
  125. //if (startStepDefine.HandlerType is EHandlerType.Role)
  126. // throw new UserFriendlyException("当前节点不支持发起会签");
  127. //即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签
  128. if (firstStepDefine.StepType is EStepType.Summary or EStepType.End)
  129. throw new UserFriendlyException("下一节点不允许发起会签");
  130. //下一节点是会签汇总节点也不允许发起会签
  131. if (dto.BackToCountersignEnd)
  132. throw new UserFriendlyException("下一节点不允许发起会签");
  133. }
  134. var workflow = await _workflowDomainService.CreateWorkflowAsync(wfModule, dto.Title,
  135. current.RequiredUserId, current.RequiredOrgId,
  136. externalId, cancellationToken);
  137. var startStepHandles = new List<WorkflowStepHandler>{WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
  138. EFlowAssignType.User, current.RequiredUserId, current.UserName,
  139. current.RequiredOrgId, current.OrgName)};
  140. var startStep = _workflowDomainService.CreateStartStep(workflow, startStepDefine, dto,
  141. new List<Kv> { new(current.RequiredUserId, current.UserName) },
  142. startStepHandles, expiredTime);
  143. var flowAssignInfo =
  144. await GetNextStepFlowAssignInfoAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic, cancellationToken);
  145. var firstStepHandlers = await GetNextStepHandlersAsync(workflow, firstStepDefine, dto, cancellationToken);
  146. var counterSignType = _workflowDomainService.GetCounterSignType(startStep.BusinessType);
  147. //办理开始节点
  148. await _workflowDomainService.HandleStepAsync(current, startStep, workflow, dto, flowAssignInfo.FlowAssignType,
  149. counterSignType, expiredTime, cancellationToken);
  150. //startStep.Handle(current.RequiredUserId, current.UserName,
  151. // current.RequiredOrgId, current.OrgName,
  152. // current.OrgAreaCode, current.OrgAreaName,
  153. // current.OrgIsCenter, firstStepDefine.Code);
  154. if (dto.Files.Any())
  155. startStep.FileJson =
  156. await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId, startStep.Id, cancellationToken);
  157. //await _workflowStepRepository.AddAsync(startStep, cancellationToken);
  158. await _workflowStepRepository.AddNav(startStep)
  159. .Include(d => d.StepHandlers)
  160. .ExecuteCommandAsync();
  161. workflow.Steps.Add(startStep);
  162. //starttrace
  163. var startTrace = _mapper.Map<WorkflowTrace>(startStep);
  164. startTrace.StepId = startStep.Id;
  165. startTrace.TraceType = EWorkflowTraceType.Normal;
  166. _mapper.Map(dto, startTrace);
  167. await _workflowTraceRepository.AddAsync(startTrace, cancellationToken);
  168. workflow.Traces.Add(startTrace);
  169. //更新受理人信息
  170. workflow.UpdateAcceptor(
  171. current.RequiredUserId,
  172. current.UserName,
  173. current.StaffNo,
  174. current.RequiredOrgId,
  175. current.OrgName);
  176. await _workflowDomainService.StartAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic,
  177. flowAssignInfo, counterSignType, expiredTime, firstStepHandlers, current, cancellationToken);
  178. return workflow.Id;
  179. }
  180. /// <summary>
  181. /// 流转至下一节点(节点办理)
  182. /// </summary>
  183. public async Task<Workflow> NextAsync(NextWorkflowDto dto, ISessionContext current, DateTime? expiredTime = null,
  184. CancellationToken cancellationToken = default)
  185. {
  186. var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true,
  187. withTraces: true, withCountersigns: true, cancellationToken: cancellationToken);
  188. var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow,
  189. current.RequiredUserId, current.RequiredOrgId);
  190. if (currentStep.Status is EWorkflowStepStatus.Handled)
  191. throw new UserFriendlyException("该状态不支持继续办理");
  192. var currentStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, currentStep.Code);
  193. //下一节点是否为动态节点
  194. var isNextDynamic = currentStepDefine.InstanceMode is EInstanceMode.Dynamic &&
  195. !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, current.OrgLevel);
  196. StepDefine nextStepDefine;
  197. if (isNextDynamic
  198. || (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
  199. || dto.IsStartCountersign)
  200. {
  201. //下一步配置为当前节点配置
  202. nextStepDefine = currentStepDefine;
  203. }
  204. else
  205. {
  206. //下一步配置为下一步节点配置
  207. nextStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode);
  208. }
  209. //需求:按角色选择办理人可以不选,表示该角色下所有人都可以办理,同时依据配置:是否本部门人办理显示待选办理人。角色下只要一人办理即可(即:角色下不发起会签)
  210. if (nextStepDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any())
  211. throw new UserFriendlyException("未指定节点处理者");
  212. if (dto.IsStartCountersign)
  213. {
  214. if (!currentStepDefine.CanStartCountersign)
  215. throw new UserFriendlyException("当前节点不支持发起会签");
  216. //if (currentStepDefine.HandlerType is EHandlerType.Role)
  217. // throw new UserFriendlyException("当前节点不支持发起会签");
  218. //即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签
  219. if (nextStepDefine.StepType is EStepType.Summary or EStepType.End)
  220. throw new UserFriendlyException("下一节点不允许发起会签");
  221. //下一节点是会签汇总节点也不允许发起会签
  222. if (dto.BackToCountersignEnd)
  223. throw new UserFriendlyException("下一节点不允许发起会签");
  224. }
  225. var flowAssignInfo =
  226. await GetNextStepFlowAssignInfoAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, cancellationToken);
  227. var nextStepHandlers = await GetNextStepHandlersAsync(workflow, nextStepDefine, dto, cancellationToken);
  228. await _workflowDomainService.NextAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic,
  229. flowAssignInfo, expiredTime, nextStepHandlers, current, cancellationToken);
  230. return workflow;
  231. }
  232. /// <summary>
  233. /// 退回(返回前一节点)
  234. /// </summary>
  235. public async Task PreviousAsync(PreviousWorkflowDto dto, CancellationToken cancellationToken)
  236. {
  237. var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withSteps: true,
  238. withTraces: true, withCountersigns: true, cancellationToken: cancellationToken);
  239. var user = await _userRepository.Queryable()
  240. .Includes(x => x.Organization)
  241. .FirstAsync(x => x.Id == _sessionContext.RequiredUserId, cancellationToken);
  242. await _workflowDomainService.PreviousAsync(workflow, dto, user, _sessionContext, cancellationToken);
  243. }
  244. /// <summary>
  245. /// 工单退回(返回前一节点)
  246. /// </summary>
  247. public async Task PreviousAsync(PreviousWorkflowDto dto, string userId, CancellationToken cancellationToken)
  248. {
  249. var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withSteps: true,
  250. withTraces: true, withCountersigns: true, cancellationToken: cancellationToken);
  251. var user = await _userRepository.Queryable()
  252. .Includes(x => x.Organization)
  253. .FirstAsync(x => x.Id == userId, cancellationToken);
  254. await _workflowDomainService.PreviousAsync(workflow, dto, user, _sessionContext, cancellationToken);
  255. }
  256. /// <summary>
  257. /// 撤回至任意节点
  258. /// </summary>
  259. public async Task RecallAsync(RecallDto dto, DateTime? expiredTime, CancellationToken cancellationToken)
  260. {
  261. var validator = new RecallDtoValidator();
  262. var validationResult = await validator.ValidateAsync(dto, cancellationToken);
  263. if (!validationResult.IsValid)
  264. throw new UserFriendlyException(string.Join(',', validationResult.Errors));
  265. var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true,
  266. withTraces: true, cancellationToken: cancellationToken);
  267. //await _orderDomainService.ReadyToRecallAsync(workflow.ExternalId, cancellationToken);
  268. var targetStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode);
  269. if (targetStepDefine.StepType is EStepType.End)
  270. throw UserFriendlyException.SameMessage("结束节点不支持撤回");
  271. //var isStartCountersign = targetStepDefine.CouldPrevStartCountersign(dto.NextHandlers.Count);
  272. var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.IsStartCountersign,
  273. dto.NextHandlers, cancellationToken);
  274. var stepHandlers = await GetNextStepHandlersAsync(workflow, targetStepDefine, dto, cancellationToken);
  275. await _workflowDomainService.RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, stepHandlers, expiredTime, _sessionContext, cancellationToken);
  276. }
  277. /// <summary>
  278. /// 无视流程模板配置直接将当前节点办理至结束节点
  279. /// </summary>
  280. public async Task HandleToEndAsync(ISessionContext current, string workflowId, string opinion, List<FileDto> files,
  281. EReviewResult? reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default)
  282. {
  283. var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true,
  284. cancellationToken: cancellationToken);
  285. var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
  286. if (endStepDefine is null)
  287. throw new UserFriendlyException("未正确配置结束节点");
  288. var dto = new NextWorkflowDto
  289. {
  290. WorkflowId = workflowId,
  291. NextStepCode = endStepDefine.Code,
  292. NextStepName = endStepDefine.Name,
  293. FlowDirection = EFlowDirection.OrgToFile,
  294. BusinessType = endStepDefine.BusinessType,
  295. Opinion = opinion,
  296. Files = files
  297. };
  298. await NextAsync(dto, current, cancellationToken: cancellationToken);
  299. //var currentStep = workflow.GetActualStep();
  300. //if (currentStep is null)
  301. // throw new UserFriendlyException("未找到实际办理节点");
  302. //await _workflowDomainService.EndAsync(workflow,
  303. // new BasicWorkflowDto
  304. // {
  305. // Opinion = opinion,
  306. // Files = files
  307. // },
  308. // endStepDefine, currentStep, reviewResult, isProvince, cancellationToken: cancellationToken);
  309. }
  310. /// <summary>
  311. /// 查询开始流程的下一步待选节点
  312. /// </summary>
  313. public async Task<NextStepsDto<NextStepOption>> GetStartStepsAsync(string moduleCode, CancellationToken cancellationToken)
  314. {
  315. var wfModule = await GetWorkflowModuleAsync(moduleCode, cancellationToken);
  316. var definition = wfModule.Definition;
  317. if (definition == null)
  318. throw new UserFriendlyException("无效模板编码");
  319. if (definition.Status is not EDefinitionStatus.Enable)
  320. throw new UserFriendlyException("该模板不可用");
  321. var startStepDefine = definition.FindStartStepDefine();
  322. if (startStepDefine.InstanceMode is EInstanceMode.Dynamic &&
  323. !_workflowDomainService.DynamicShouldTerminal(startStepDefine, _sessionContext.OrgLevel))
  324. {
  325. var nextStepOption = await GetDynamicStepAsync(startStepDefine.InstancePolicy.Value,
  326. startStepDefine.StepType, startStepDefine.BusinessType, cancellationToken);
  327. return new NextStepsDto<NextStepOption>
  328. {
  329. Steps = new List<NextStepOption> { nextStepOption }
  330. };
  331. }
  332. var firstStepDefines = definition.FindStepDefines(startStepDefine.NextSteps.Select(d => d.Code));
  333. if (!firstStepDefines.Any())
  334. throw new UserFriendlyException("未正确配置首个办理节点");
  335. return new NextStepsDto<NextStepOption>
  336. {
  337. Steps = await GetConfigStepsAsync(definition.FlowType, startStepDefine.StepType, startStepDefine.BusinessType,
  338. firstStepDefines, cancellationToken)
  339. };
  340. }
  341. /// <summary>
  342. /// 查询办理流程的下一步待选节点
  343. /// </summary>
  344. public async Task<NextStepsWithOpinionDto<NextStepOption>> GetNextStepsAsync(string workflowId, CancellationToken cancellationToken)
  345. {
  346. var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true,
  347. cancellationToken: cancellationToken);
  348. var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow,
  349. _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId);
  350. if (currentStep.StepType is EStepType.End)
  351. throw new UserFriendlyException("结束节点无需办理");
  352. var dto = new NextStepsWithOpinionDto<NextStepOption>
  353. {
  354. CanReject = workflow.IsReviewType() && currentStep.CanReject,
  355. //ExpiredTime = workflow.ExpiredTime,
  356. CanStartCountersign = currentStep.CanStartCountersign,
  357. CurrentStepBusinessType = currentStep.BusinessType,
  358. TimeTypeOptions = EnumExts.GetDescriptions<ETimeType>().ToList(),
  359. IsMainHandlerShow = workflow.WorkflowDefinition.IsMainHandlerShow,
  360. };
  361. var currentStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, currentStep.Code);
  362. if (currentStep.InstanceMode is EInstanceMode.Dynamic &&
  363. !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, _sessionContext.OrgLevel))
  364. {
  365. //动态生成下一步
  366. var nextStepOption = await GetDynamicStepAsync(currentStep.InstancePolicy.Value, currentStep.StepType,
  367. currentStep.BusinessType, cancellationToken);
  368. dto.Steps = new List<NextStepOption> { nextStepOption };
  369. return dto;
  370. }
  371. if (currentStep.IsInCountersign())
  372. {
  373. if (currentStep.IsCountersignEndStep)
  374. {
  375. // 宜宾需求:会签汇总节点展示会签办理节点办理意见
  376. var countersignHandleSteps = workflow.Steps.Where(d =>
  377. d.CountersignId == currentStep.CountersignId &&
  378. d.CountersignPosition is ECountersignPosition.Multi or ECountersignPosition.Single).ToList();
  379. var sb = new StringBuilder();
  380. foreach (var countersignHandleStep in countersignHandleSteps)
  381. {
  382. sb.AppendLine($"{countersignHandleStep.GetActualHandler()?.GetHandler().Value} : {countersignHandleStep.Opinion}");
  383. }
  384. dto.Opinion = sb.ToString();
  385. //当前待办节点为会签汇总节点时:检查是否为顶级会签汇总节点,t:按配置往下走,f:继续往上汇总,不需要重复往下指派
  386. if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
  387. {
  388. var startCountersignStep = GetCsLoopStartStep(workflow, currentStep);
  389. ////查找当前节点对应会签开始节点的上级作为下一个cs汇总节点的汇总对象
  390. //var startCountersignStep =
  391. // workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
  392. //if (startCountersignStep is null)
  393. // throw new UserFriendlyException(
  394. // $"未查询到会签开始节点,workflowId: {workflow.Id}, startStepId: {currentStep.CountersignStartStepId}",
  395. // "未查询到会签开始节点,数据异常");
  396. var countersignEndOption = GetCsEndStepByTargetPrev(workflow.Steps, startCountersignStep);
  397. //按会签策略
  398. var nextStepOption = await GetDynamicStepAsync(currentStep.CountersignPolicy.Value,
  399. EStepType.Normal, currentStep.BusinessType, cancellationToken);
  400. dto.Steps = new List<NextStepOption> { nextStepOption, countersignEndOption };
  401. return dto;
  402. }
  403. }
  404. else
  405. {
  406. //汇总节点
  407. var countersignEndOption = GetCsEndStepByTargetPrev(workflow.Steps, currentStep);
  408. //按会签策略
  409. var nextStepOption =
  410. await GetDynamicStepAsync(currentStep.CountersignPolicy.Value,
  411. EStepType.Normal, currentStep.BusinessType, cancellationToken);
  412. dto.Steps = new List<NextStepOption> { nextStepOption, countersignEndOption };
  413. return dto;
  414. }
  415. }
  416. var nextDefines = workflow.WorkflowDefinition.FindStepDefines(currentStep.NextSteps.Select(d => d.Code));
  417. if (!nextDefines.Any())
  418. throw new UserFriendlyException("未正确配置下一节点");
  419. dto.Steps = await GetConfigStepsAsync(workflow, currentStep, nextDefines, cancellationToken);
  420. // 宜宾需求:汇总节点展示前一级节点办理意见
  421. if (currentStep.StepType is EStepType.Summary &&
  422. !currentStep.IsCountersignEndStep &&
  423. string.IsNullOrEmpty(dto.Opinion))
  424. {
  425. var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId);
  426. dto.Opinion = $"{prevStep?.GetActualHandler()?.GetHandler().Value} : {prevStep?.Opinion}";
  427. }
  428. return dto;
  429. }
  430. private WorkflowStep GetCsLoopStartStep(Workflow workflow, WorkflowStep currentStep)
  431. {
  432. var startCountersignStep =
  433. workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
  434. if (startCountersignStep is null)
  435. throw new UserFriendlyException(
  436. $"未查询到会签开始节点,workflowId: {workflow.Id}, startStepId: {currentStep.CountersignStartStepId}",
  437. "未查询到会签开始节点,数据异常");
  438. if (!startCountersignStep.IsCountersignEndStep)
  439. return startCountersignStep;
  440. return GetCsLoopStartStep(workflow, startCountersignStep);
  441. }
  442. /// <summary>
  443. /// 查询撤回可选节点及办理对象
  444. /// </summary>
  445. public async Task<NextStepsDto<RecallStepOption>> GetRecallStepsAsync(string workflowId, CancellationToken cancellationToken)
  446. {
  447. var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true,
  448. cancellationToken: cancellationToken);
  449. var isEnd = workflow.Steps.Any(x => x.StepType == EStepType.End);
  450. var currentStep = workflow.Steps.FirstOrDefault(d => d.Id == workflow.ActualHandleStepId);
  451. if (currentStep is null)
  452. throw new UserFriendlyException("无效当前节点编号");
  453. var quer = workflow.Steps.Where(d =>
  454. d.StepType != EStepType.End &&
  455. d.IsOrigin);
  456. if (!isEnd)
  457. {
  458. quer = quer.Where(d => d.Id != currentStep.Id);
  459. }
  460. var originSteps = quer.ToList();
  461. var stepCodes = originSteps.Select(d => d.Code).ToList();
  462. var stepDefines = workflow.WorkflowDefinition.FindStepDefines(stepCodes);
  463. var dto = new NextStepsDto<RecallStepOption>
  464. {
  465. TimeTypeOptions = EnumExts.GetDescriptions<ETimeType>().ToList()
  466. };
  467. var steps = await GetRecallConfigStepsAsync(originSteps, stepDefines, currentStep,
  468. workflow.FlowType,
  469. workflow.Status is EWorkflowStatus.Completed,
  470. cancellationToken);
  471. dto.Steps = steps;
  472. return dto;
  473. }
  474. private async Task<List<RecallStepOption>> GetRecallConfigStepsAsync(
  475. List<WorkflowStep> originSteps, List<StepDefine> stepDefines,
  476. WorkflowStep currentStep, EFlowType flowType, bool isWorkflowFiled, CancellationToken cancellationToken)
  477. {
  478. var steps = new List<RecallStepOption>();
  479. foreach (var originStep in originSteps)
  480. {
  481. var stepDefine = stepDefines.First(d => d.Code == originStep.Code);
  482. var nextStepOption = await GetConfigStepAsync(flowType, stepDefine, cancellationToken);
  483. var stepOption = _mapper.Map<RecallStepOption>(nextStepOption);
  484. if (stepDefine.StepType is EStepType.End)
  485. {
  486. steps.Add(stepOption);
  487. continue;
  488. }
  489. stepOption.InputRealHandler = false;
  490. ////已归档工单,撤回至中心看作otc,撤回至部门看作cto
  491. //stepOption.FlowDirection = isWorkflowFiled
  492. // ? stepDefine.BusinessType is EBusinessType.Center or EBusinessType.Send
  493. // ? EFlowDirection.OrgToCenter
  494. // : stepDefine.BusinessType is EBusinessType.Department
  495. // ? EFlowDirection.CenterToOrg
  496. // : null
  497. // : CheckFlowDirection(currentStep.BusinessType, stepDefine.BusinessType);
  498. //需求已调整为特提必重算期满时间
  499. //需求:撤回选择办理对象时,默认选中该节点原办理对象
  500. if (originStep.Handlers.Any())
  501. stepOption.Handler =
  502. originStep.Handlers.FirstOrDefault(d => d.Key == originStep.HandlerId || d.Key == originStep.HandlerOrgId);
  503. //if (originStep.StepHandlers.Any())
  504. // stepOption.Handler = originStep.GetActualHandler()?.GetHandler() ?? new();
  505. steps.Add(stepOption);
  506. }
  507. return steps;
  508. }
  509. /// <summary>
  510. /// 否决
  511. /// </summary>
  512. public async Task RejectAsync(RejectDto dto, CancellationToken cancellationToken)
  513. {
  514. var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withDefine: true,
  515. cancellationToken: cancellationToken);
  516. //var basicDto = _mapper.Map<BasicWorkflowDto>(dto);
  517. //basicDto.NextStepCode = string.Empty;
  518. //basicDto.IsStartCountersign = false;
  519. //await _workflowDomainService.RejectAsync(workflow, basicDto, cancellationToken);
  520. var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
  521. var nextDto = _mapper.Map<NextWorkflowDto>(dto);
  522. nextDto.ReviewResult = EReviewResult.Failed;
  523. nextDto.NextStepCode = endStepDefine.Code;
  524. nextDto.NextStepName = endStepDefine.Name;
  525. nextDto.FlowDirection = _sessionContext.OrgIsCenter
  526. ? EFlowDirection.CenterToFile
  527. : EFlowDirection.OrgToFile;
  528. await NextAsync(nextDto, _sessionContext, cancellationToken: cancellationToken);
  529. }
  530. //供开启流程调用
  531. private async Task<List<NextStepOption>> GetConfigStepsAsync(
  532. EFlowType flowType,
  533. EStepType currentStepType,
  534. EBusinessType currentBusinessType,
  535. List<StepDefine> stepDefines,
  536. CancellationToken cancellationToken)
  537. {
  538. var stepOptions = new List<NextStepOption>();
  539. foreach (var stepDefine in stepDefines)
  540. {
  541. var nextStepOption = await GetConfigStepAsync(flowType, stepDefine, cancellationToken);
  542. nextStepOption.InputRealHandler = currentStepType == EStepType.Normal &&
  543. stepDefine.StepType is EStepType.Summary or EStepType.End;
  544. stepOptions.Add(nextStepOption);
  545. if (stepDefine.StepType is EStepType.End) continue;
  546. nextStepOption.FlowDirection = CheckFlowDirection(currentBusinessType, stepDefine.BusinessType);
  547. //stepOptions.Add(nextStepOption);
  548. }
  549. return stepOptions;
  550. }
  551. /// <summary>
  552. /// 办理流程调用(根据办理过程过滤汇总节点待选办理对象)
  553. /// </summary>
  554. private async Task<List<NextStepOption>> GetConfigStepsAsync(
  555. Workflow workflow,
  556. WorkflowStep currentStep,
  557. //EStepType currentStepType,
  558. //EBusinessType currentBusinessType,
  559. List<StepDefine> stepDefines,
  560. CancellationToken cancellationToken)
  561. {
  562. var stepOptions = new List<NextStepOption>();
  563. foreach (var stepDefine in stepDefines)
  564. {
  565. NextStepOption nextStepOption;
  566. //汇总节点只能选择对应节点办理对象
  567. if (workflow.FlowType is EFlowType.Handle && stepDefine.StepType is EStepType.Summary)
  568. {
  569. //根据汇总对象id找到被汇总节点
  570. var summaryTargetStep = workflow.Steps.FirstOrDefault(d =>
  571. d.Status == EWorkflowStepStatus.Handled &&
  572. d.StepType == EStepType.Normal && d.Code == stepDefine.SummaryTargetCode);
  573. if (summaryTargetStep is null)
  574. throw UserFriendlyException.SameMessage("未查询到汇总对象节点");
  575. var handlers = summaryTargetStep.Handlers
  576. .Where(d => d.Key == summaryTargetStep.HandlerId || d.Key == summaryTargetStep.HandlerOrgId).ToList();
  577. nextStepOption = new NextStepOption
  578. {
  579. Key = stepDefine.Code,
  580. Value = stepDefine.Name,
  581. StepType = stepDefine.StepType,
  582. BusinessType = stepDefine.BusinessType,
  583. HandlerType = stepDefine.HandlerType,
  584. Items = handlers
  585. };
  586. }
  587. else
  588. {
  589. nextStepOption = await GetConfigStepAsync(workflow.FlowType, stepDefine, cancellationToken);
  590. }
  591. nextStepOption.InputRealHandler = currentStep.StepType == EStepType.Normal &&
  592. stepDefine.StepType is EStepType.Summary or EStepType.End;
  593. stepOptions.Add(nextStepOption);
  594. if (stepDefine.StepType is EStepType.End) continue;
  595. nextStepOption.FlowDirection = CheckFlowDirection(currentStep.BusinessType, stepDefine.BusinessType);
  596. }
  597. return stepOptions;
  598. }
  599. public async Task<NextStepOption> GetConfigStepAsync(EFlowType flowType, StepDefine stepDefine,
  600. CancellationToken cancellationToken)
  601. {
  602. var handlers = new List<Kv>();
  603. if (stepDefine.StepType is EStepType.End)
  604. {
  605. return new NextStepOption
  606. {
  607. Key = stepDefine.Code,
  608. Value = stepDefine.Name,
  609. StepType = stepDefine.StepType,
  610. BusinessType = stepDefine.BusinessType,
  611. HandlerType = stepDefine.HandlerType,
  612. Items = handlers
  613. };
  614. }
  615. var orgId = _sessionContext.RequiredOrgId;
  616. var levelOneOrgId = orgId.GetHigherOrgId();
  617. var isCenter = levelOneOrgId.IsCenter();
  618. switch (stepDefine.HandlerType)
  619. {
  620. case EHandlerType.AssignedUser:
  621. case EHandlerType.AssignedOrg:
  622. handlers = stepDefine.HandlerTypeItems;
  623. break;
  624. case EHandlerType.Role:
  625. //当前操作人所属部门的下级部门并且属于配置包含角色
  626. var roles = await _roleRepository.Queryable()
  627. .Includes(
  628. d => d.Accounts.Where(x =>
  629. !x.IsDeleted && x.Status == EAccountStatus.Normal && x.AccountType == EAccountType.Personal).ToList(),
  630. x => x.User)
  631. .Where(d => stepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Name))
  632. .ToListAsync(cancellationToken);
  633. _logger.LogInformation($"角色: {string.Join(",", roles.Select(d => d.Name))}");
  634. var users1 = roles.SelectMany(d => d.Accounts).Select(d => d.User);
  635. //工单办理:除一级部门选择中心汇总(中心会签流程,返回topCountersignStep场景),其余只能选下级部门
  636. if (flowType is EFlowType.Handle
  637. && (stepDefine.StepType != EStepType.Summary && stepDefine.BusinessType != EBusinessType.Center))
  638. users1 = users1.Where(d => d.OrgId.StartsWith(levelOneOrgId));
  639. handlers = users1.Where(d => d != null && !string.IsNullOrEmpty(d.Id)).Select(d => new Kv(d.Id, d.Name)).ToList();
  640. break;
  641. case EHandlerType.OrgLevel:
  642. //当前操作人所属部门的垂直部门并且属于配置orgLevel的部门
  643. var levels = stepDefine.HandlerTypeItems.Select(d => int.Parse(d.Key)).ToList();
  644. // var orgs1 = await _organizeRepository.Queryable()
  645. // .Where(d => d.IsEnable && levels.Contains(d.Level))
  646. // .WhereIF(!isCenter, d => d.Id.StartsWith(levelOneOrgId))
  647. // .ToListAsync(cancellationToken);
  648. _logger.LogInformation($"部门等级: {string.Join(",", levels)}");
  649. var query = _organizeRepository.Queryable()
  650. .Where(d => d.IsEnable);
  651. List<SystemOrganize> orgs1;
  652. if (isCenter)
  653. {
  654. orgs1 = await query
  655. .Where(d => levels.Contains(d.Level) && !d.IsCenter)
  656. .ToListAsync(cancellationToken);
  657. }
  658. else
  659. {
  660. var upLevels = levels.Where(d => d <= _sessionContext.OrgLevel).ToList();
  661. var lowLevels = levels.Where(d => d > _sessionContext.OrgLevel).ToList();
  662. orgs1 = await query
  663. .Where(d => (upLevels.Contains(d.Level) && d.Id.StartsWith(levelOneOrgId)) ||
  664. (lowLevels.Contains(d.Level) && d.Id.Contains(orgId)))
  665. .ToListAsync(cancellationToken);
  666. }
  667. handlers = orgs1.Select(d => new Kv(d.Id, d.Name)).ToList();
  668. break;
  669. case EHandlerType.OrgType:
  670. var types = stepDefine.HandlerTypeItems.Select(d => d.Key)
  671. .Select(d => Enum.Parse<EOrgType>(d));
  672. var orgs2 = await _organizeRepository.Queryable()
  673. .Where(d => d.IsEnable && types.Contains(d.OrgType))
  674. .WhereIF(!isCenter, d => d.Id.StartsWith(levelOneOrgId))
  675. .ToListAsync(cancellationToken);
  676. handlers = orgs2.Select(d => new Kv(d.Id, d.Name)).ToList();
  677. break;
  678. default:
  679. throw new ArgumentOutOfRangeException();
  680. }
  681. return new NextStepOption
  682. {
  683. Key = stepDefine.Code,
  684. Value = stepDefine.Name,
  685. StepType = stepDefine.StepType,
  686. BusinessType = stepDefine.BusinessType,
  687. HandlerType = stepDefine.HandlerType,
  688. Items = handlers
  689. };
  690. }
  691. private EFlowDirection? CheckFlowDirection(EBusinessType sourceStepBusinessType, EBusinessType directionStepBusinessType)
  692. {
  693. switch (sourceStepBusinessType)
  694. {
  695. case EBusinessType.Center:
  696. case EBusinessType.Send:
  697. return directionStepBusinessType switch
  698. {
  699. EBusinessType.Center => EFlowDirection.CenterToCenter,
  700. EBusinessType.Send => EFlowDirection.CenterToCenter,
  701. EBusinessType.Department => EFlowDirection.CenterToOrg,
  702. EBusinessType.File => EFlowDirection.CenterToFile,
  703. _ => throw new ArgumentOutOfRangeException(nameof(directionStepBusinessType),
  704. directionStepBusinessType, null)
  705. };
  706. case EBusinessType.Department:
  707. return directionStepBusinessType switch
  708. {
  709. EBusinessType.Center => EFlowDirection.OrgToCenter,
  710. EBusinessType.Send => EFlowDirection.OrgToCenter,
  711. EBusinessType.Department => EFlowDirection.OrgToOrg,
  712. EBusinessType.File => EFlowDirection.OrgToFile,
  713. _ => throw new ArgumentOutOfRangeException(nameof(directionStepBusinessType),
  714. directionStepBusinessType, null)
  715. };
  716. case EBusinessType.File:
  717. return null;
  718. default:
  719. throw new ArgumentOutOfRangeException(nameof(sourceStepBusinessType), sourceStepBusinessType, null);
  720. }
  721. }
  722. private NextStepOption GetCsEndStepByTargetPrev(List<WorkflowStep> steps, WorkflowStep step)
  723. {
  724. var prevStep = steps.FirstOrDefault(d => d.Id == step.PrevStepId);
  725. if (prevStep is null)
  726. throw new UserFriendlyException("未查找到会签上级节点");
  727. var text = prevStep.HandlerOrgIsCenter.Value
  728. ? "热线中心会签汇总"
  729. : $"{prevStep.HandlerOrgId.CalcOrgLevel().ToChinese()}级部门会签汇总";
  730. var handlers = prevStep.Handlers
  731. .Where(d => d.Key == prevStep.HandlerId || d.Key == prevStep.HandlerOrgId).ToList();
  732. //var handler = prevStep.GetActualHandler()?.GetHandler();
  733. return new NextStepOption
  734. {
  735. Key = prevStep.Code,
  736. Value = text,
  737. BackToCountersignEnd = true,
  738. StepType = EStepType.Summary,
  739. BusinessType = prevStep.BusinessType,
  740. HandlerType = prevStep.HandlerType,
  741. Items = handlers //new List<Kv> { new(prevStep.HandlerId, prevStep.HandlerName) },
  742. };
  743. }
  744. /// <summary>
  745. /// 依据当前操作人及动态策略生成下一步节点
  746. /// </summary>
  747. private NextStepOption CreateDynamicStep(EDynamicPolicy policy)
  748. {
  749. int orgLevel;
  750. switch (policy)
  751. {
  752. case EDynamicPolicy.OrgUpCenterTop:
  753. orgLevel = _sessionContext.OrgLevel - 1;
  754. if (orgLevel < 0) orgLevel = 0;
  755. break;
  756. case EDynamicPolicy.OrgUp:
  757. orgLevel = _sessionContext.OrgLevel - 1;
  758. if (orgLevel <= 0) orgLevel = 1;
  759. break;
  760. case EDynamicPolicy.OrgDownCenterTop:
  761. orgLevel = _sessionContext.OrgIsCenter
  762. ? 1
  763. : _sessionContext.OrgLevel + 1;
  764. break;
  765. case EDynamicPolicy.OrgDown:
  766. orgLevel = _sessionContext.OrgLevel + 1;
  767. break;
  768. default:
  769. throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
  770. }
  771. return new NextStepOption
  772. {
  773. Key = orgLevel.ToString(),
  774. Value = orgLevel == 0 ? "热线中心" : $"{orgLevel.ToChinese()}级部门",
  775. };
  776. }
  777. private async Task<NextStepOption> GetDynamicStepAsync(
  778. EDynamicPolicy policy, EStepType stepType,
  779. EBusinessType currentBusinessType, CancellationToken cancellationToken)
  780. {
  781. int orgLevel;
  782. List<Kv> items;
  783. string upperOrgId;
  784. EBusinessType businessType;
  785. EFlowDirection? flowDirection = null;
  786. switch (policy)
  787. {
  788. case EDynamicPolicy.OrgUpCenterTop:
  789. orgLevel = _sessionContext.OrgLevel - 1;
  790. if (orgLevel < 0) orgLevel = 0;
  791. if (orgLevel == 0)
  792. {
  793. businessType = EBusinessType.Send;
  794. if (currentBusinessType == EBusinessType.Department)
  795. flowDirection = EFlowDirection.OrgToCenter;
  796. items = await _organizeRepository.Queryable()
  797. .Where(d => d.IsCenter)
  798. .Select(d => new Kv { Key = d.Id, Value = d.Name })
  799. .ToListAsync(cancellationToken);
  800. }
  801. else
  802. {
  803. businessType = EBusinessType.Department;
  804. //上级部门Id
  805. upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
  806. items = await _organizeRepository.Queryable()
  807. .Where(d => d.Id == upperOrgId)
  808. .Select(d => new Kv { Key = d.Id, Value = d.Name })
  809. .ToListAsync(cancellationToken);
  810. }
  811. break;
  812. case EDynamicPolicy.OrgUp:
  813. businessType = _sessionContext.OrgIsCenter
  814. ? EBusinessType.Send
  815. : _sessionContext.RequiredOrgId.CalcOrgLevel() == 1
  816. ? EBusinessType.Send
  817. : EBusinessType.Department;
  818. orgLevel = _sessionContext.OrgLevel - 1;
  819. if (orgLevel <= 0) orgLevel = 1;
  820. //上级部门Id
  821. upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
  822. items = await _organizeRepository.Queryable()
  823. .Where(d => d.Id == upperOrgId)
  824. .Select(d => new Kv { Key = d.Id, Value = d.Name })
  825. .ToListAsync(cancellationToken);
  826. break;
  827. case EDynamicPolicy.OrgDownCenterTop:
  828. businessType = EBusinessType.Department;
  829. if (_sessionContext.OrgIsCenter)
  830. {
  831. orgLevel = 1;
  832. items = await _organizeRepository.Queryable()
  833. .Where(d => !d.IsCenter && d.Level == orgLevel)
  834. .Select(d => new Kv { Key = d.Id, Value = d.Name })
  835. .ToListAsync(cancellationToken);
  836. }
  837. else
  838. {
  839. orgLevel = _sessionContext.OrgLevel + 1;
  840. items = await _organizeRepository.Queryable()
  841. .Where(d => !d.IsCenter && d.Level == orgLevel &&
  842. d.Id.StartsWith(_sessionContext.RequiredOrgId))
  843. .Select(d => new Kv { Key = d.Id, Value = d.Name })
  844. .ToListAsync(cancellationToken);
  845. }
  846. break;
  847. case EDynamicPolicy.OrgDown:
  848. businessType = EBusinessType.Department;
  849. orgLevel = _sessionContext.OrgLevel + 1;
  850. items = await _organizeRepository.Queryable()
  851. .Where(d => d.Level == orgLevel && d.Id.StartsWith(_sessionContext.RequiredOrgId))
  852. .Select(d => new Kv { Key = d.Id, Value = d.Name })
  853. .ToListAsync(cancellationToken);
  854. break;
  855. default:
  856. throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
  857. }
  858. return new NextStepOption
  859. {
  860. Key = orgLevel.ToString(),
  861. Value = orgLevel == 0 ? "热线中心" : $"{orgLevel.ToChinese()}级部门",
  862. FlowDirection = flowDirection,
  863. StepType = stepType,
  864. BusinessType = businessType,
  865. HandlerType = EHandlerType.OrgLevel, //目前所有动态策略均属于部门等级
  866. Items = items
  867. };
  868. }
  869. /// <summary>
  870. /// 查询下一节点办理对象类型(user or org)及实际办理对象
  871. /// </summary>
  872. public async Task<FlowAssignInfo> GetNextStepFlowAssignInfoAsync(Workflow workflow, WorkflowStep currentStep,
  873. BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, CancellationToken cancellationToken)
  874. {
  875. if (nextStepDefine.StepType is EStepType.End) return new();
  876. var isStartCountersign = dto.IsStartCountersign;
  877. var handlers = dto.NextHandlers;
  878. if (isStartCountersign)
  879. {
  880. var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType);
  881. //按会签策略判断,目前所有策略为org
  882. return FlowAssignInfo.Create(assignType, handlers, isStartCountersign);
  883. }
  884. //if (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
  885. // return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
  886. if (currentStep.IsInCountersign())
  887. {
  888. if (currentStep.IsCountersignEndStep)
  889. {
  890. if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
  891. {
  892. //汇总节点(非顶级)
  893. //var csStartStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
  894. //if (csStartStep is null)
  895. // throw new UserFriendlyException("未查询到会签开始节点");
  896. var csStartStep = GetCsLoopStartStep(workflow, currentStep);
  897. var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == csStartStep.PrevStepId);
  898. if (prevStep is null)
  899. throw new UserFriendlyException("未查询到目标节点的前一节点");
  900. return FlowAssignInfo.Create(prevStep.FlowAssignType.Value, prevStep.Handlers, isStartCountersign);
  901. }
  902. }
  903. else
  904. {
  905. if (dto.BackToCountersignEnd)
  906. {
  907. var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId);
  908. if (prevStep is null)
  909. throw new UserFriendlyException($"未查询到当前节点的上级节点");
  910. return FlowAssignInfo.Create(prevStep.FlowAssignType.Value, prevStep.Handlers, isStartCountersign);
  911. }
  912. else
  913. {
  914. var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType);
  915. //按会签策略判断,目前所有策略为org
  916. return FlowAssignInfo.Create(assignType, handlers, isStartCountersign);
  917. }
  918. }
  919. }
  920. if (isNextDynamic)
  921. return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
  922. return await GetNextStepFlowAssignInfoByDefineAsync(nextStepDefine, isStartCountersign, handlers,
  923. cancellationToken);
  924. }
  925. private async ValueTask<List<WorkflowStepHandler>> GetNextStepHandlersAsync(Workflow workflow,
  926. StepDefine nextStepDefine, BasicWorkflowDto dto, CancellationToken cancellationToken)
  927. {
  928. var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType);
  929. //var assignType = AssignInfo.GetAssignType(nextStepDefine.HandlerType, dto.NextHandlers.Any());
  930. switch (assignType)
  931. {
  932. case EFlowAssignType.Org:
  933. return dto.NextHandlers.Select(d => WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
  934. assignType, orgId: d.Key, orgName: d.Value)).ToList();
  935. case EFlowAssignType.User:
  936. var userIds = dto.NextHandlers.Select(d => d.Key).ToList();
  937. var users = await _userRepository.Queryable()
  938. .Includes(d => d.Organization)
  939. .Where(d => userIds.Contains(d.Id))
  940. .ToListAsync(cancellationToken);
  941. return users.Select(d => WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
  942. assignType, d.Id, d.Name, d.OrgId, d.Organization.Name))
  943. .ToList();
  944. //case EFlowAssignType.Role:
  945. // handlers = dto.NextHandlers.Select(d => WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
  946. // assignType, roleId: d.Key, roleName: d.Value)).ToList();
  947. // break;
  948. default:
  949. throw new ArgumentOutOfRangeException();
  950. }
  951. }
  952. /// <summary>
  953. /// 按流程模板配置创建下一步办理对象
  954. /// </summary>
  955. private async Task<FlowAssignInfo> GetNextStepFlowAssignInfoByDefineAsync(StepDefine nextStepDefine,
  956. bool isStartCountersign,
  957. List<Kv> handlers, CancellationToken cancellationToken)
  958. {
  959. switch (nextStepDefine.HandlerType)
  960. {
  961. case EHandlerType.Role:
  962. if (!handlers.Any())
  963. {
  964. var roles = await _roleRepository.Queryable()
  965. .Includes(d => d.Accounts, x => x.User)
  966. .Where(d => nextStepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Name))
  967. .ToListAsync(cancellationToken);
  968. handlers = roles.SelectMany(d => d.Accounts).Distinct()
  969. .Select(d => new Kv(d.Id, d.User.Name))
  970. .ToList();
  971. }
  972. return FlowAssignInfo.Create(EFlowAssignType.User, handlers, isStartCountersign);
  973. case EHandlerType.OrgLevel:
  974. case EHandlerType.OrgType:
  975. case EHandlerType.AssignedOrg:
  976. return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
  977. case EHandlerType.AssignedUser:
  978. return FlowAssignInfo.Create(EFlowAssignType.User, handlers, isStartCountersign);
  979. default:
  980. throw new ArgumentOutOfRangeException();
  981. }
  982. }
  983. #region private
  984. /// <summary>
  985. /// 查询流程业务模块
  986. /// </summary>
  987. /// <param name="code"></param>
  988. /// <param name="cancellationToken"></param>
  989. /// <returns></returns>
  990. /// <exception cref="UserFriendlyException"></exception>
  991. public async Task<WorkflowModule> GetWorkflowModuleAsync(string code, CancellationToken cancellationToken)
  992. {
  993. var wfModule = await _wfModuleCacheManager.GetWorkflowModuleAsync(code, cancellationToken);
  994. if (wfModule == null)
  995. throw UserFriendlyException.SameMessage("无效流程模块编码");
  996. if (wfModule.Definition is null)
  997. throw new UserFriendlyException($"{code} 未配置流程模板", "未配置流程模板");
  998. return wfModule;
  999. }
  1000. /// <summary>
  1001. /// 检查退回节点信息
  1002. /// </summary>
  1003. public async Task<(WorkflowStep currentStep, WorkflowStep prevStep, bool isOrgToCenter, bool isSecondToFirstOrgLevel)>
  1004. GetPreviousInformationAsync(string workflowId, string operatorId, string operatorOrgId,
  1005. CancellationToken cancellationToken)
  1006. {
  1007. var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withSteps: true,
  1008. withCountersigns: true, cancellationToken: cancellationToken);
  1009. var (currentStep, prevStep, _) = _workflowDomainService.GetPreviousStep(workflow, operatorId, operatorOrgId);
  1010. var isOrgToCenter = currentStep.BusinessType is EBusinessType.Department &&
  1011. prevStep.BusinessType is EBusinessType.Center or EBusinessType.Send;
  1012. var isSecondToFirstOrgLevel = currentStep.HandlerType is EHandlerType.OrgLevel &&
  1013. currentStep.Handlers.First().Key.CheckIfOrgLevelIs(2) &&
  1014. prevStep.HandlerType is EHandlerType.OrgLevel &&
  1015. prevStep.Handlers.First().Key.CheckIfOrgLevelIs(1) &&
  1016. !prevStep.Handlers.First().Key.IsCenter();
  1017. return (currentStep, prevStep, isOrgToCenter, isSecondToFirstOrgLevel);
  1018. }
  1019. /// <summary>
  1020. /// 开启流程直接归档
  1021. /// </summary>
  1022. public async Task StartToEndAsync(StartWorkflowDto dto, ISessionContext current, string externalId, DateTime? expiredTime = null,
  1023. CancellationToken cancellationToken = default)
  1024. {
  1025. var wfModule = await GetWorkflowModuleAsync(dto.DefinitionModuleCode, cancellationToken);
  1026. var definition = wfModule.Definition;
  1027. if (definition == null)
  1028. throw new UserFriendlyException("无效模板编码");
  1029. if (definition.Status is not EDefinitionStatus.Enable)
  1030. throw new UserFriendlyException("该模板不可用");
  1031. var endStepDefine = definition.FindEndStepDefine();
  1032. dto.NextStepCode = endStepDefine.Code;
  1033. dto.NextStepName = endStepDefine.Name;
  1034. dto.FlowDirection = EFlowDirection.CenterToFile;
  1035. await StartWorkflowAsync(dto, current, externalId, expiredTime, cancellationToken);
  1036. }
  1037. #endregion
  1038. }