WorkflowApplication.cs 63 KB

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