WorkflowApplication.cs 54 KB

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