WorkflowApplication.cs 55 KB

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