WorkflowApplication.cs 61 KB

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