WorkflowDomainService.cs 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068
  1. using Hotline.FlowEngine.Definitions;
  2. using Hotline.FlowEngine.Notifications;
  3. using Hotline.SeedData;
  4. using Hotline.Share.Dtos.FlowEngine;
  5. using Hotline.Share.Enums.FlowEngine;
  6. using Hotline.Users;
  7. using MapsterMapper;
  8. using MediatR;
  9. using Microsoft.Extensions.Logging;
  10. using SqlSugar;
  11. using XF.Domain.Authentications;
  12. using XF.Domain.Dependency;
  13. using XF.Domain.Entities;
  14. using XF.Domain.Exceptions;
  15. using XF.Utility.SequentialId;
  16. namespace Hotline.FlowEngine.Workflows
  17. {
  18. public class WorkflowDomainService : IWorkflowDomainService, IScopeDependency
  19. {
  20. private readonly IWorkflowRepository _workflowRepository;
  21. private readonly IWorkflowStepRepository _workflowStepRepository;
  22. private readonly IWorkflowTraceRepository _workflowTraceRepository;
  23. private readonly IWorkflowSupplementRepository _workflowSupplementRepository;
  24. private readonly IWorkflowAssignRepository _workflowAssignRepository;
  25. private readonly IWorkflowCountersignRepository _workflowCountersignRepository;
  26. private readonly ISessionContext _sessionContext;
  27. private readonly IMapper _mapper;
  28. private readonly IMediator _mediator;
  29. private readonly ILogger<WorkflowDomainService> _logger;
  30. public WorkflowDomainService(
  31. IWorkflowRepository workflowRepository,
  32. IWorkflowStepRepository workflowStepRepository,
  33. IWorkflowTraceRepository workflowTraceRepository,
  34. IWorkflowSupplementRepository workflowSupplementRepository,
  35. IWorkflowAssignRepository workflowAssignRepository,
  36. IWorkflowCountersignRepository workflowCountersignRepository,
  37. ISessionContext sessionContext,
  38. IMapper mapper,
  39. IMediator mediator,
  40. ILogger<WorkflowDomainService> logger)
  41. {
  42. _workflowRepository = workflowRepository;
  43. _workflowStepRepository = workflowStepRepository;
  44. _workflowTraceRepository = workflowTraceRepository;
  45. _workflowSupplementRepository = workflowSupplementRepository;
  46. _workflowAssignRepository = workflowAssignRepository;
  47. _workflowCountersignRepository = workflowCountersignRepository;
  48. _sessionContext = sessionContext;
  49. _mapper = mapper;
  50. _mediator = mediator;
  51. _logger = logger;
  52. }
  53. public async Task<Workflow> CreateWorkflowAsync(Definition definition, string title, string externalId, CancellationToken cancellationToken)
  54. {
  55. var workflow = new Workflow
  56. {
  57. Title = title,
  58. ModuleId = definition.ModuleId,
  59. ModuleName = definition.ModuleName,
  60. ModuleCode = definition.ModuleCode,
  61. DefinitionId = definition.Id,
  62. Status = EWorkflowStatus.Runnable,
  63. TimeLimit = GetTimeLimit(definition.Code),
  64. ExpiredTime = GenerateExpiredTime(definition.Code),
  65. StepBoxes = new(),
  66. Traces = new(),
  67. Definition = definition,
  68. AssignTime = DateTime.Now,
  69. ExternalId = externalId,
  70. };
  71. await _workflowRepository.AddAsync(workflow, cancellationToken);
  72. return workflow;
  73. }
  74. /// <summary>
  75. /// 流程开始
  76. /// </summary>
  77. /// <param name="workflow"></param>
  78. /// <param name="dto"></param>
  79. /// <param name="cancellationToken"></param>
  80. /// <returns></returns>
  81. public async Task StartAsync(Workflow workflow, BasicWorkflowDto dto, StepDefine nextStepBoxDefine, FlowAssignMode flowAssignMode, CancellationToken cancellationToken)
  82. {
  83. //var nextStepBoxDefine = GetStepBoxDefine(workflow.Definition, dto.NextStepCode);
  84. //1. 如果不是按角色指派,handlers必填 2. 如果按角色指派,handlers可以不选
  85. if (nextStepBoxDefine.HandlerType is not EHandlerType.Role && !dto.NextHandlers.Any())
  86. throw UserFriendlyException.SameMessage("未指派办理人");
  87. //开始节点
  88. var (startStepBox, startStep) = await CreateStartStepAsync(workflow, dto, cancellationToken);
  89. var isStartCountersign = startStep.ShouldStartCountersign(dto.NextHandlers.Count);
  90. //检查是否支持会签
  91. if (isStartCountersign && startStep.CountersignMode == ECountersignMode.UnSupport)
  92. throw new UserFriendlyException($"当前节点不支持发起会签, stepId: {startStep.Id}", "当前节点不支持发起会签");
  93. if (isStartCountersign)
  94. {
  95. //创建会签数据
  96. var countersign = await CreateCountersignAsync(workflow.Id, startStep.Id, startStep.Code, dto.NextHandlers.Count, startStep.CountersignId, cancellationToken);
  97. startStep.StartCountersignId = countersign.Id;
  98. await _workflowStepRepository.UpdateAsync(startStep, cancellationToken);
  99. }
  100. //开始节点trace
  101. await AcceptTraceAsync(workflow, startStepBox, startStep, cancellationToken);
  102. await NextTraceAsync(workflow, dto, startStep, cancellationToken);
  103. //第二节点(创建即为 已指派/待接办 状态)
  104. var nextStepBox = await CreateStepAsync(isStartCountersign, workflow, nextStepBoxDefine, dto, EWorkflowStepStatus.Assigned,
  105. startStepBox, startStep, cancellationToken);
  106. //更新当前节点名称、时间、会签节点code 等字段
  107. workflow.SetWorkflowCurrentStepInfo(isStartCountersign, nextStepBox);
  108. workflow.UpdateHandlers(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgCode,
  109. flowAssignMode.FlowAssignType, flowAssignMode.HandlerObjects);
  110. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  111. //publish
  112. await _mediator.Publish(new StartWorkflowNotify(workflow, dto, isStartCountersign, flowAssignMode), cancellationToken);
  113. }
  114. public async Task<Workflow> GetWorkflowAsync(string workflowId,
  115. bool withDefine = false, bool withSteps = false,
  116. bool withTraces = false, bool withSupplements = false,
  117. bool withAssigns = false, bool withCountersigns = false,
  118. CancellationToken cancellationToken = default)
  119. {
  120. var query = _workflowRepository.Queryable().Where(d => d.Id == workflowId);
  121. if (withDefine)
  122. query = query.Includes(d => d.Definition);
  123. if (withSupplements)
  124. query = query.Includes(d => d.Supplements, d => d.Creator);
  125. if (withAssigns)
  126. query = query.Includes(d => d.Assigns);
  127. if (withCountersigns)
  128. query = query.Includes(d => d.Countersigns);
  129. var workflow = await query.FirstAsync();
  130. if (workflow is null)
  131. throw new UserFriendlyException("无效workflowId");
  132. if (withSteps)
  133. {
  134. var steps = await _workflowStepRepository.Queryable()
  135. .Where(d => d.WorkflowId == workflow.Id)
  136. .OrderBy(d => d.CreationTime)
  137. .ToTreeAsync(d => d.Steps, d => d.ParentId, null);
  138. workflow.StepBoxes = steps;
  139. }
  140. if (withTraces)
  141. {
  142. var traces = await _workflowTraceRepository.Queryable()
  143. .Where(d => d.WorkflowId == workflow.Id)
  144. .OrderBy(d => d.CreationTime)
  145. .ToTreeAsync(d => d.Traces, d => d.ParentId, null);
  146. workflow.Traces = traces;
  147. }
  148. return workflow;
  149. }
  150. /// <summary>
  151. /// 受理(接办)
  152. /// </summary>
  153. public async Task AcceptAsync(Workflow workflow, string userId, string userName, string orgCode, string orgName, CancellationToken cancellationToken)
  154. {
  155. if (!workflow.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgCode)) return;
  156. //工单完成以后查看的场景
  157. if (workflow.Status is not EWorkflowStatus.Runnable) return;
  158. var (currentStepBox, currentStep) = GetUnCompleteStepOrDefault(workflow.StepBoxes, orgCode, userId);
  159. if (currentStep is null) return;
  160. if (currentStep.Status is EWorkflowStepStatus.Accepted) return;
  161. if (currentStep.HandlerType is EHandlerType.AssignUser or EHandlerType.Role)
  162. {
  163. //userId
  164. if (!currentStep.HandlerIds.Contains(userId)) return;
  165. }
  166. else
  167. {
  168. //orgId
  169. if (!currentStep.HandlerIds.Contains(orgCode)) return;
  170. }
  171. if (currentStep.StepType is EStepType.End)
  172. throw new UserFriendlyException("当前流程已流转到最终步骤");
  173. var changedSteps = new List<WorkflowStep> { currentStep };
  174. if (currentStepBox.Status is EWorkflowStepStatus.Assigned)
  175. {
  176. currentStepBox.Status = EWorkflowStepStatus.Accepted;
  177. changedSteps.Add(currentStepBox);
  178. }
  179. currentStep.Accept(userId, userName, _sessionContext.RequiredOrgCode, _sessionContext.OrgName);
  180. //接办时非会签并且有多个接办部门时需更新接办部门
  181. if (!workflow.IsInCountersign())
  182. {
  183. var assigns = await _workflowAssignRepository.QueryAsync(d => d.WorkflowId == workflow.Id);
  184. if (assigns.Count > 1)
  185. {
  186. await _workflowAssignRepository.RemoveRangeAsync(assigns, cancellationToken);
  187. var assign = WorkflowAssign.Create(workflow.Id, orgCode, orgName);
  188. await _workflowAssignRepository.AddAsync(assign, cancellationToken);
  189. }
  190. }
  191. await _workflowStepRepository.UpdateRangeAsync(changedSteps, cancellationToken);
  192. await AcceptTraceAsync(workflow, currentStepBox, currentStep, cancellationToken);
  193. await _mediator.Publish(new AcceptWorkflowNotify(workflow), cancellationToken);
  194. }
  195. /// <summary>
  196. /// 办理(流转至下一节点)
  197. /// </summary>
  198. public async Task NextAsync(Workflow workflow, BasicWorkflowDto dto, StepDefine nextStepBoxDefine,
  199. bool isOutOfCallCenter, FlowAssignMode flowAssignMode, CancellationToken cancellationToken)
  200. {
  201. ValidatePermission(workflow);
  202. CheckWhetherRunnable(workflow.Status);
  203. #region 办理当前节点
  204. var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgCode, _sessionContext.RequiredUserId);
  205. if (currentStep.Status is EWorkflowStepStatus.Completed or EWorkflowStepStatus.Created)
  206. throw UserFriendlyException.SameMessage("当前节点状态无法办理");
  207. var isStartCountersign = currentStep.ShouldStartCountersign(dto.NextHandlers.Count);
  208. //检查是否支持发起会签
  209. if (isStartCountersign && currentStep.CountersignMode == ECountersignMode.UnSupport)
  210. throw new UserFriendlyException($"当前节点不支持发起会签, stepId: {currentStep.Id}", "当前节点不支持发起会签");
  211. if (isStartCountersign && nextStepBoxDefine.StepType is EStepType.CountersignEnd)
  212. throw new UserFriendlyException($"汇总节点不支持办理会签, stepId: {currentStep.Id}", "汇总节点不支持办理会签");
  213. if (currentStep.Status is EWorkflowStepStatus.Assigned)
  214. await AcceptAsync(workflow,
  215. _sessionContext.RequiredUserId,
  216. _sessionContext.UserName,
  217. _sessionContext.RequiredOrgCode,
  218. _sessionContext.OrgName,
  219. cancellationToken);
  220. if (currentStep.StepType is EStepType.End)
  221. throw new UserFriendlyException("当前流程已流转到最终步骤");
  222. if (isStartCountersign)
  223. {
  224. //创建会签数据
  225. var countersign = await CreateCountersignAsync(workflow.Id, currentStep.Id, currentStep.Code,
  226. dto.NextHandlers.Count, currentStep.CountersignId, cancellationToken);
  227. currentStep.StartCountersign(countersign.Id);
  228. }
  229. _mapper.Map(dto, currentStep);
  230. //step办理状态
  231. currentStep.Complete(
  232. _sessionContext.RequiredUserId, _sessionContext.UserName,
  233. _sessionContext.RequiredOrgCode, _sessionContext.OrgName,
  234. dto.NextStepCode);
  235. //stepBox办理状态
  236. currentStepBox.CheckStepBoxStatusAndUpdate();
  237. var updateSteps = new List<WorkflowStep> { currentStepBox, currentStep };
  238. //结束当前会签流程
  239. if (currentStep.StepType is EStepType.CountersignEnd && currentStep.IsInCountersign)
  240. {
  241. var currentCountersign = workflow.Countersigns.FirstOrDefault(d => d.Id == currentStep.CountersignId);
  242. if (currentCountersign is null)
  243. throw new UserFriendlyException(
  244. $"未查询到对应会签信息,workflowId:{workflow.Id}, countersignId:{currentStep.CountersignId}", "无效会签编号");
  245. //如果!=,说明未发起会签而是继承的外层会签
  246. if (currentStep.CountersignStartStepCode == currentCountersign.StartStepCode)
  247. {
  248. //结束step会签信息
  249. var countersignStartStepBox =
  250. workflow.StepBoxes.FirstOrDefault(d => d.Code == currentCountersign.StartStepCode);
  251. if (countersignStartStepBox is null)
  252. throw new UserFriendlyException(
  253. $"未查询到会签开始stepBox, workflowId: {workflow.Id}, startStepCode: {currentCountersign.StartStepCode}", "未查询到会签开始节点");
  254. var countersignStartStep =
  255. countersignStartStepBox.Steps.FirstOrDefault(d => d.HasStartedCountersign && d.Id == currentCountersign.StartStepId);
  256. if (countersignStartStep is null)
  257. throw new UserFriendlyException(
  258. $"未查询到会签开始step, workflowId: {workflow.Id}, startStepId: {currentCountersign.StartStepId}", "未查询到会签开始节点");
  259. countersignStartStep.CountersignComplete();
  260. updateSteps.Add(countersignStartStep);
  261. //结束会签
  262. currentCountersign.Complete(currentStep.Id, currentStep.Code);
  263. await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken);
  264. }
  265. }
  266. await _workflowStepRepository.UpdateRangeAsync(updateSteps, cancellationToken);
  267. #endregion
  268. #region 处理流程
  269. //检查会签是否结束,并更新当前会签节点字段
  270. var isCountersignOver = false;
  271. if (currentStep.StepType is EStepType.CountersignEnd && workflow.IsInCountersign())
  272. {
  273. isCountersignOver = workflow.CheckIfCountersignOver();
  274. if (isCountersignOver)
  275. workflow.EndCountersign();
  276. }
  277. //检查是否流转到流程终点
  278. if (nextStepBoxDefine.StepType is EStepType.End && !workflow.IsInCountersign())
  279. {
  280. //create endStep
  281. var (_, endStep) = await CreateEndStepAsync(workflow, nextStepBoxDefine, currentStepBox, currentStep, cancellationToken);
  282. //create endTrace
  283. await EndTraceAsync(workflow, dto, endStep, cancellationToken);
  284. workflow.Complete();
  285. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  286. await _mediator.Publish(new EndWorkflowNotify(workflow), cancellationToken);
  287. return;
  288. }
  289. //是否从中心流转出去,重新计算expiredTime
  290. if (isOutOfCallCenter)
  291. {
  292. workflow.IsStraight = false;
  293. workflow.ExpiredTime = GenerateExpiredTime(workflow.Definition.Code);
  294. workflow.AssignTime = DateTime.Now;
  295. }
  296. //最终办理意见与时间处理
  297. if (currentStep.StepType is EStepType.Normal && nextStepBoxDefine.StepType is EStepType.CountersignEnd)
  298. {
  299. //最终办理
  300. workflow.Opinion = dto.Opinion;
  301. await _mediator.Publish(new OrderFinalManageNotify(workflow), cancellationToken);
  302. }
  303. else if (currentStep.StepType is EStepType.CountersignEnd && nextStepBoxDefine.StepType is EStepType.Normal)
  304. {
  305. //汇总以后又重新指派到非汇总节点办理
  306. workflow.ResetOption();
  307. await _mediator.Publish(new OrderRecallFinalManageNotify(workflow), cancellationToken);
  308. }
  309. //创建下一节点(会签汇总节点不重复创建)
  310. var nextStepBox = await CreateStepAsync(isStartCountersign, workflow, nextStepBoxDefine, dto, EWorkflowStepStatus.Created,
  311. currentStepBox, currentStep, cancellationToken);
  312. //下一节点为汇总节点时,检查下一节点是否可办理
  313. var nextStepCanHandle = true;
  314. if (nextStepBox.StepType is EStepType.CountersignEnd)
  315. {
  316. if (currentStep.IsInCountersign)
  317. {
  318. //同一会签Id,非汇总节点
  319. var steps = await _workflowStepRepository.QueryAsync(d =>
  320. d.WorkflowId == workflow.Id
  321. && d.CountersignId == currentStep.CountersignId
  322. && d.StepType != EStepType.End);
  323. //(当前办理节点所处同一会签内的所有step全都办理完成并且如果开启了会签的step,必须会签结束)
  324. var unComplete = steps.Any(d =>
  325. d.Status != EWorkflowStepStatus.Completed ||
  326. (d.HasStartedCountersign && !(d.IsStartedCountersignComplete ?? false)));
  327. nextStepCanHandle = !unComplete;
  328. }
  329. }
  330. if (nextStepCanHandle)
  331. {
  332. //将下一节点处理为已指派/可接办
  333. await SetNextCountersignEndAssignedAsync(nextStepBox, currentStep, cancellationToken);
  334. _mediator.Publish(new CountersignEndAssigned(workflow), cancellationToken);
  335. }
  336. //更新workflow当前节点名称、时间、会签节点code 等字段
  337. workflow.SetWorkflowCurrentStepInfo(isStartCountersign, nextStepBox);
  338. workflow.UpdateHandlers(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgCode,
  339. flowAssignMode.FlowAssignType, flowAssignMode.HandlerObjects);
  340. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  341. #endregion
  342. #region 流转记录
  343. await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
  344. #endregion
  345. await _mediator.Publish(new NextStepNotify(workflow, dto, isStartCountersign, isCountersignOver, flowAssignMode), cancellationToken);
  346. }
  347. /// <summary>
  348. /// 退回(返回前一节点)
  349. /// </summary>
  350. /// <returns></returns>
  351. public async Task PreviousAsync(Workflow workflow, PreviousWorkflowDto dto, CancellationToken cancellationToken)
  352. {
  353. ValidatePermission(workflow);
  354. CheckWhetherRunnable(workflow.Status);
  355. var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgCode, _sessionContext.RequiredUserId);
  356. if (currentStepBox.StepType is EStepType.Start)
  357. throw UserFriendlyException.SameMessage("当前流程已退回到开始节点");
  358. if (currentStepBox.Steps.Count > 1)
  359. throw UserFriendlyException.SameMessage("会签流程不支持退回");
  360. //update trace
  361. await PreviousTraceAsync(workflow.Id, dto, currentStep, cancellationToken);
  362. //remove workflow.steps
  363. await _workflowStepRepository.RemoveRangeAsync(new List<WorkflowStep> { currentStepBox, currentStep },
  364. cancellationToken);
  365. //todo publish
  366. }
  367. /// <summary>
  368. /// 撤回(返回到之前任意节点)
  369. /// </summary>
  370. public async Task RecallAsync(Workflow workflow, RecallDto dto, CancellationToken cancellationToken)
  371. {
  372. ValidatePermission(workflow);
  373. CheckWhetherRunnable(workflow.Status);
  374. var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgCode, _sessionContext.RequiredUserId);
  375. if (currentStepBox.StepType is EStepType.Start)
  376. throw UserFriendlyException.SameMessage("当前流程已退回到开始节点");
  377. var isStartCountersign = currentStep.ShouldStartCountersign(dto.NextHandlers.Count);
  378. //检查是否支持会签
  379. if (isStartCountersign && currentStep.CountersignMode == ECountersignMode.UnSupport)
  380. throw new UserFriendlyException($"当前节点不支持发起会签, stepId: {currentStep.Id}", "当前节点不支持发起会签");
  381. var targetStepBox = workflow.StepBoxes.FirstOrDefault(d => d.Code == dto.TargetStepCode);
  382. if (targetStepBox is null)
  383. throw UserFriendlyException.SameMessage("该流程尚未流转至该节点");
  384. await RecallAsync(workflow, dto, targetStepBox, isStartCountersign, cancellationToken);
  385. //todo publish
  386. }
  387. /// <summary>
  388. /// 跳转(直接将流程跳转至任意节点)
  389. /// </summary>
  390. public async Task JumpAsync(Workflow workflow, RecallDto dto, CancellationToken cancellationToken)
  391. {
  392. CheckWhetherRunnable(workflow.Status);
  393. var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgCode, _sessionContext.RequiredUserId);
  394. if (currentStepBox.StepType is EStepType.Start)
  395. throw UserFriendlyException.SameMessage("当前流程已退回到开始节点");
  396. if (currentStepBox.StepType is EStepType.End)
  397. throw UserFriendlyException.SameMessage("当前流程已流转到结束节点");
  398. var isStartCountersign = currentStep.ShouldStartCountersign(dto.NextHandlers.Count);
  399. //检查是否支持会签
  400. if (isStartCountersign && currentStep.CountersignMode == ECountersignMode.UnSupport)
  401. throw new UserFriendlyException($"当前节点不支持发起会签, stepId: {currentStep.Id}", "当前节点不支持发起会签");
  402. var targetStepBox = workflow.StepBoxes.FirstOrDefault(d => d.Code == dto.TargetStepCode);
  403. if (targetStepBox == null)
  404. {
  405. //向后跳转
  406. var nextStepBoxDefine = GetStepBoxDefine(workflow.Definition, dto.NextStepCode);
  407. //var isStartCountersign = nextStepBoxDefine.IsStartCountersign(dto.NextHandlers.Count);
  408. var nextStepBox = await CreateStepAsync(isStartCountersign, workflow, nextStepBoxDefine, dto,
  409. EWorkflowStepStatus.Assigned, currentStepBox, currentStep, cancellationToken);
  410. await ResetWorkflowCurrentStepInfo(workflow, dto, nextStepBox, cancellationToken);
  411. #region 补充中间节点处理方案
  412. //var completeStepCodes = workflow.StepBoxes.Select(d => d.Code);
  413. //var uncompleteStepDefines = workflow.Definition.Steps.Where(d => !completeStepCodes.Contains(d.Code));
  414. //创建当前节点与目标节点中间节点
  415. //var jumpDto = new BasicWorkflowDto
  416. //{
  417. // Opinion = "跳转补充"
  418. //};
  419. //foreach (var stepDefine in uncompleteStepDefines)
  420. //{
  421. // var previousStepId = lastStepBox.Steps.Count > 1 ? lastStepBox.Id : lastStepBox.Steps.First().Id;
  422. // if (dto.TargetStepCode == stepDefine.Code)
  423. // {
  424. // await CreateStepAsync(workflow, stepDefine, dto, lastStepBox.Id, previousStepId, cancellationToken);
  425. // break;
  426. // }
  427. // //jump业务下,如果当前节点为会签节点,第一个补充节点的subStep.PreviousId无法确定从哪个子节点跳转过来,统一处理为当前节点的stepBox.Id
  428. // lastStepBox = await CreateStepAsync(workflow, stepDefine, dto, lastStepBox.Id, previousStepId, cancellationToken);
  429. //}
  430. #endregion
  431. }
  432. else
  433. {
  434. //返回之前节点
  435. await RecallAsync(workflow, dto, targetStepBox, isStartCountersign, cancellationToken);
  436. }
  437. //update uncompleted traces
  438. await JumpTraceAsync(workflow.Id, dto, cancellationToken);
  439. //todo publish
  440. }
  441. /// <summary>
  442. /// 补充
  443. /// </summary>
  444. /// <returns></returns>
  445. public async Task SupplementAsync(Workflow workflow, EndWorkflowDto dto, CancellationToken cancellationToken)
  446. {
  447. CheckWhetherRunnable(workflow.Status);
  448. //todo 检查当前办理人是否为该流程中的办理人
  449. var supplement = _mapper.Map<WorkflowSupplement>(dto);
  450. await _workflowSupplementRepository.AddAsync(supplement, cancellationToken);
  451. }
  452. /// <summary>
  453. /// 终止流程
  454. /// </summary>
  455. public async Task TerminateAsync(string id, CancellationToken cancellationToken)
  456. {
  457. var workflow = await _workflowRepository.GetAsync(id, cancellationToken);
  458. if (workflow == null)
  459. throw UserFriendlyException.SameMessage("无效的流程编号");
  460. workflow.Terminate();
  461. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  462. //todo publish
  463. _mediator.Publish(new TerminalWorkflowNotify(workflow));
  464. }
  465. /// <summary>
  466. /// 根据stepCode查询流程配置中对应的节点
  467. /// </summary>
  468. public StepDefine GetStepBoxDefine(Definition definition, string stepCode)
  469. {
  470. if (definition == null) throw new ArgumentNullException(nameof(definition));
  471. if (string.IsNullOrEmpty(stepCode)) throw new ArgumentNullException(nameof(stepCode));
  472. var stepDefine = definition.FindStep(stepCode);
  473. if (stepDefine == null)
  474. throw new UserFriendlyException($"未找到流程中对应的节点,DefineCode: {definition.Code}, stepCode: {stepCode}",
  475. "未查询到对应节点");
  476. return stepDefine;
  477. }
  478. /// <summary>
  479. /// 查询当前待办节点的下一级节点配置(办理参数)
  480. /// </summary>
  481. public IReadOnlyList<StepDefine> GetNextStepOptions(Workflow workflow, CancellationToken cancellationToken)
  482. {
  483. var (currentStepBox, _) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgCode, _sessionContext.RequiredUserId);
  484. return workflow.Definition.FindSteps(currentStepBox.NextSteps);
  485. }
  486. #region private
  487. private async Task<WorkflowCountersign> CreateCountersignAsync(string workflowId, string startStepId, string startStepCode, int count, string? parentId = null, CancellationToken cancellationToken = default)
  488. {
  489. var countersign = new WorkflowCountersign
  490. {
  491. WorkflowId = workflowId,
  492. StartStepId = startStepId,
  493. StartStepCode = startStepCode,
  494. Members = count,
  495. ParentId = parentId,
  496. };
  497. await _workflowCountersignRepository.AddAsync(countersign, cancellationToken);
  498. return countersign;
  499. }
  500. /// <summary>
  501. /// 更新下级汇总节点可办理状态
  502. /// </summary>
  503. /// <param name="nextStepBox"></param>
  504. /// <param name="currentStep"></param>
  505. /// <param name="cancellationToken"></param>
  506. /// <returns></returns>
  507. private async Task SetNextCountersignEndAssignedAsync(WorkflowStep nextStepBox, WorkflowStep currentStep, CancellationToken cancellationToken)
  508. {
  509. var nextSteps = currentStep.StepCountersignStatus is EStepCountersignStatus.InCountersign
  510. ? nextStepBox.Steps.Where(d => d.CountersignId == currentStep.CountersignId).ToList()
  511. : nextStepBox.Steps.Where(d => d.PreviousId == currentStep.Id).ToList();
  512. if (!nextSteps.Any())
  513. throw new UserFriendlyException($"未查询到下一节点, currentStepId: {currentStep.Id}");
  514. foreach (var nextStep in nextSteps)
  515. {
  516. nextStep.SetAssigned();
  517. }
  518. await _workflowStepRepository.UpdateRangeAsync(nextSteps, cancellationToken);
  519. }
  520. /// <summary>
  521. /// 在stepCode对应的stepBox中找到开启会签流程的节点
  522. /// </summary>
  523. private static WorkflowStep FindCountersignStartStep(Workflow workflow, string startCountersignStepCode, string startCountersignId)
  524. {
  525. var countersignStartStepBox = workflow.StepBoxes.First(d => d.Code == startCountersignStepCode);
  526. var countersignStartStep =
  527. countersignStartStepBox.Steps.First(d => d.StartCountersignId == startCountersignId);
  528. return countersignStartStep;
  529. }
  530. private async Task JumpTraceAsync(string workflowId, RecallDto dto, CancellationToken cancellationToken)
  531. {
  532. //未办理的traces
  533. var uncompleteTraces =
  534. await _workflowTraceRepository.QueryAsync(d =>
  535. d.WorkflowId == workflowId && string.IsNullOrEmpty(d.UserId));
  536. foreach (var trace in uncompleteTraces)
  537. {
  538. trace.Jump(
  539. _sessionContext.RequiredUserId,
  540. _sessionContext.UserName,
  541. _sessionContext.RequiredOrgCode,
  542. _sessionContext.OrgName);
  543. }
  544. await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken);
  545. }
  546. private async Task RecallTraceAsync(string workflowId, RecallDto dto, CancellationToken cancellationToken)
  547. {
  548. //未办理的traces
  549. var uncompleteTraces =
  550. await _workflowTraceRepository.QueryAsync(d =>
  551. d.WorkflowId == workflowId && string.IsNullOrEmpty(d.UserId));
  552. foreach (var trace in uncompleteTraces)
  553. {
  554. trace.Recall(
  555. _sessionContext.RequiredUserId,
  556. _sessionContext.UserName,
  557. _sessionContext.RequiredOrgCode,
  558. _sessionContext.OrgName);
  559. }
  560. await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken);
  561. }
  562. private async Task PreviousTraceAsync(string workflowId, PreviousWorkflowDto dto, WorkflowStep step, CancellationToken cancellationToken)
  563. {
  564. var trace = await GetWorkflowTraceAsync(workflowId, step.Id, cancellationToken);
  565. _mapper.Map(dto, trace);
  566. trace.Previous(
  567. _sessionContext.RequiredUserId,
  568. _sessionContext.UserName,
  569. _sessionContext.RequiredOrgCode,
  570. _sessionContext.OrgName);
  571. await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
  572. }
  573. private async Task EndTraceAsync(Workflow workflow, BasicWorkflowDto dto, WorkflowStep step, CancellationToken cancellationToken)
  574. {
  575. var trace = _mapper.Map<WorkflowTrace>(step);
  576. trace.Status = EWorkflowTraceStatus.Normal;
  577. trace.ExpiredTime = workflow.ExpiredTime;
  578. trace.TimeLimit = workflow.TimeLimit;
  579. await _workflowTraceRepository.AddAsync(trace, cancellationToken);
  580. }
  581. private async Task NextTraceAsync(Workflow workflow, BasicWorkflowDto dto, WorkflowStep step, CancellationToken cancellationToken)
  582. {
  583. var trace = await GetWorkflowTraceAsync(workflow.Id, step.Id, cancellationToken);
  584. _mapper.Map(dto, trace);
  585. _mapper.Map(step, trace);
  586. await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
  587. }
  588. private async Task AcceptTraceAsync(Workflow workflow, WorkflowStep currentStepBox, WorkflowStep currentStep, CancellationToken cancellationToken)
  589. {
  590. var trace = _mapper.Map<WorkflowTrace>(currentStep);
  591. trace.Status = EWorkflowTraceStatus.Normal;
  592. trace.ExpiredTime = workflow.ExpiredTime;
  593. trace.TimeLimit = workflow.TimeLimit;
  594. //处于会签中的节点,其对应的trace.parentId赋值上级trace.Id
  595. if (currentStep.StepCountersignStatus is EStepCountersignStatus.InCountersign)
  596. {
  597. var parentTrace = await GetWorkflowTraceAsync(workflow.Id, currentStep.PreviousId, cancellationToken);
  598. trace.ParentId = parentTrace.Id;
  599. }
  600. await _workflowTraceRepository.AddAsync(trace, cancellationToken);
  601. }
  602. private async Task<WorkflowTrace> GetWorkflowTraceAsync(string workflowId, string stepId, CancellationToken cancellationToken)
  603. {
  604. var parentTrace = await _workflowTraceRepository.GetAsync(d =>
  605. d.WorkflowId == workflowId && d.StepId == stepId, cancellationToken);
  606. if (parentTrace == null)
  607. throw new UserFriendlyException($"未找到对应trace, workflowId: {workflowId}, stepId: {stepId}");
  608. return parentTrace;
  609. }
  610. private async Task RecallAsync(Workflow workflow, RecallDto dto, WorkflowStep targetStepBox, bool isStartCountersign, CancellationToken cancellationToken)
  611. {
  612. //update uncompleted traces
  613. await RecallTraceAsync(workflow.Id, dto, cancellationToken);
  614. //remove completedSteps include target self
  615. var completeStepBoxes = workflow.StepBoxes.Where(d =>
  616. d.Code == dto.TargetStepCode || d.CreationTime > targetStepBox.CreationTime);
  617. var removeSteps = new List<WorkflowStep>();
  618. foreach (var stepBox in completeStepBoxes)
  619. {
  620. removeSteps.Add(stepBox);
  621. removeSteps.AddRange(stepBox.Steps);
  622. }
  623. await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken);
  624. //recreate targetStep
  625. var nextStepBoxDefine = GetStepBoxDefine(workflow.Definition, dto.NextStepCode);
  626. //var isStartCountersign = nextStepBoxDefine.IsStartCountersign(dto.NextHandlers.Count);
  627. await CreateStepAsync(isStartCountersign, workflow, nextStepBoxDefine, dto, EWorkflowStepStatus.Assigned,
  628. targetStepBox, targetStepBox.Steps.First(), cancellationToken);
  629. //flow manage
  630. if (workflow.IsInCountersign())
  631. {
  632. var currentCountersignStepBox =
  633. workflow.StepBoxes.First(d => d.Code == workflow.TopCountersignStepCode);
  634. //目标节点在初始会签节点之前或正好
  635. if (targetStepBox.Code == workflow.TopCountersignStepCode || targetStepBox.CreationTime < currentCountersignStepBox.CreationTime)
  636. await ResetWorkflowCurrentStepInfo(workflow, dto, targetStepBox, cancellationToken);
  637. }
  638. }
  639. /// <summary>
  640. /// 重置currentStep信息
  641. /// </summary>
  642. private async Task ResetWorkflowCurrentStepInfo(Workflow workflow, RecallDto dto, WorkflowStep stepBox, CancellationToken cancellationToken)
  643. {
  644. //更新当前节点名称、时间、会签节点code
  645. workflow.CloseCountersignStatus();
  646. var isCountersign = dto.NextHandlers.Count > 1;
  647. workflow.SetWorkflowCurrentStepInfo(isCountersign, stepBox);
  648. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  649. }
  650. private static void CheckWhetherRunnable(EWorkflowStatus status)
  651. {
  652. if (status is not EWorkflowStatus.Runnable)
  653. throw new UserFriendlyException("当前流程状态不可继续流转");
  654. }
  655. private void ValidatePermission(Workflow workflow)
  656. {
  657. if (!workflow.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgCode))
  658. throw new UserFriendlyException("无办理权限");
  659. }
  660. /// <summary>
  661. /// 创建开始节点(保存开始流程的办理意见,对应definition的start节点)
  662. /// </summary>
  663. private async Task<(WorkflowStep stepBox, WorkflowStep step)> CreateStartStepAsync(Workflow workflow, BasicWorkflowDto dto, CancellationToken cancellationToken)
  664. {
  665. if (workflow.StepBoxes.Any())
  666. throw UserFriendlyException.SameMessage("无法重复创建开始节点");
  667. var startStepDefinition = workflow.Definition.Steps.FirstOrDefault(d => d.StepType == EStepType.Start);
  668. if (startStepDefinition == null)
  669. throw new UserFriendlyException($"模板未配置开始节点, defineCode: {workflow.Definition.Code}", "模板未配置开始节点");
  670. var stepBox = CreateStepBox(workflow.Id, startStepDefinition, string.Empty);
  671. await _workflowStepRepository.AddAsync(stepBox, cancellationToken);
  672. //start节点的办理人分类默认为用户,即为当前发起流程的操作员
  673. var handler = new IdName { Id = _sessionContext.RequiredUserId, Name = _sessionContext.UserName };
  674. var step = await CreateStartSubStepAsync(handler, dto, stepBox, cancellationToken);
  675. return (stepBox, step);
  676. }
  677. private async Task<(WorkflowStep stepBox, WorkflowStep step)> CreateEndStepAsync(
  678. Workflow workflow,
  679. StepDefine endStepDefine,
  680. WorkflowStep prevStepBox,
  681. WorkflowStep preveStep,
  682. CancellationToken cancellationToken)
  683. {
  684. if (workflow.StepBoxes.Any(d => d.StepType == EStepType.End))
  685. throw UserFriendlyException.SameMessage("无法重复创建结束节点");
  686. var stepBox = CreateStepBox(workflow.Id, endStepDefine, prevStepBox.Id);
  687. await _workflowStepRepository.AddAsync(stepBox, cancellationToken);
  688. var handler = new IdName { Id = _sessionContext.RequiredUserId, Name = _sessionContext.UserName };
  689. var step = await CreateEndSubStepAsync(handler, stepBox, preveStep, cancellationToken);
  690. return (stepBox, step);
  691. }
  692. /// <summary>
  693. /// 创建节点(不含开始、结束节点)
  694. /// </summary>
  695. private async Task<WorkflowStep> CreateStepAsync(
  696. bool isPrevStartCountersign,
  697. Workflow workflow, StepDefine stepBoxDefine,
  698. BasicWorkflowDto dto, EWorkflowStepStatus status,
  699. WorkflowStep prevStepBox,
  700. WorkflowStep prevStep,
  701. CancellationToken cancellationToken = default)
  702. {
  703. if (stepBoxDefine.StepType is EStepType.Start or EStepType.End)
  704. throw new UserFriendlyException("该方法不支持创建开始或结束节点");
  705. var stepBox = workflow.StepBoxes.FirstOrDefault(d => d.Code == stepBoxDefine.Code);
  706. if (stepBox == null)
  707. {
  708. stepBox = CreateStepBox(workflow.Id, stepBoxDefine, prevStepBox.Id);
  709. await _workflowStepRepository.AddAsync(stepBox, cancellationToken);
  710. }
  711. else if (stepBox.Status != EWorkflowStepStatus.Created)
  712. {
  713. stepBox.Status = EWorkflowStepStatus.Created;
  714. await _workflowStepRepository.UpdateAsync(stepBox, cancellationToken);
  715. }
  716. //下一节点为汇总节点时,同一会签只需要创建一次汇总节点
  717. if (stepBoxDefine.StepType is EStepType.CountersignEnd && prevStep.StepCountersignStatus == EStepCountersignStatus.InCountersign)
  718. {
  719. var step = stepBox.Steps.FirstOrDefault(d =>
  720. d.IsInCountersign && d.CountersignId == prevStep.CountersignId);
  721. if (step != null)
  722. return stepBox;
  723. }
  724. await CreateSubStepsAsync(isPrevStartCountersign, stepBoxDefine, dto, stepBox, status, prevStep, cancellationToken);
  725. return stepBox;
  726. }
  727. private async Task<WorkflowStep> CreateStartSubStepAsync(
  728. IdName handler,
  729. BasicWorkflowDto dto,
  730. WorkflowStep stepBox,
  731. CancellationToken cancellationToken)
  732. {
  733. //开始节点既不发起会签,也不处于会签中
  734. var subStep = CreateSubStep(stepBox, new List<IdName> { handler }, dto.NextStepCode, dto.NextMainHandler,
  735. null, null, EWorkflowStepStatus.Completed, EStepCountersignStatus.None);
  736. subStep.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName,
  737. _sessionContext.RequiredOrgCode, _sessionContext.OrgName);
  738. //step办理状态
  739. subStep.Complete(
  740. _sessionContext.RequiredUserId, _sessionContext.UserName,
  741. _sessionContext.RequiredOrgCode, _sessionContext.OrgName,
  742. dto.NextStepCode);
  743. _mapper.Map(dto, subStep);
  744. stepBox.Steps.Add(subStep);
  745. await _workflowStepRepository.AddAsync(subStep, cancellationToken);
  746. return subStep;
  747. }
  748. private async Task<WorkflowStep> CreateEndSubStepAsync(
  749. IdName handler,
  750. WorkflowStep currentStepBox,
  751. WorkflowStep prevStep,
  752. CancellationToken cancellationToken)
  753. {
  754. var subStep = CreateSubStep(currentStepBox, new List<IdName> { handler }, null, null, prevStep.Id,
  755. null, EWorkflowStepStatus.Completed, EStepCountersignStatus.None);
  756. subStep.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgCode,
  757. _sessionContext.OrgName);
  758. subStep.Complete(_sessionContext.RequiredUserId, _sessionContext.UserName,
  759. _sessionContext.RequiredOrgCode, _sessionContext.OrgName, null);
  760. currentStepBox.Steps.Add(subStep);
  761. await _workflowStepRepository.AddAsync(subStep, cancellationToken);
  762. return subStep;
  763. }
  764. private async Task CreateSubStepsAsync(
  765. bool isPrevStartCountersign,
  766. StepDefine stepBoxDefine,
  767. BasicWorkflowDto dto,
  768. WorkflowStep stepBox,
  769. EWorkflowStepStatus stepStatus,
  770. WorkflowStep prevStep,
  771. CancellationToken cancellationToken = default)
  772. {
  773. var countersignStatus = stepBoxDefine.StepType is EStepType.CountersignEnd
  774. ? prevStep.IsInCountersign
  775. ? EStepCountersignStatus.InCountersign
  776. : EStepCountersignStatus.None
  777. : prevStep.GetNextStepCountersignStatus();
  778. List<WorkflowStep> subSteps;
  779. if (stepBoxDefine.HandlerType is EHandlerType.AssignUser or EHandlerType.AssignOrg)
  780. {
  781. subSteps = CreateSubSteps(isPrevStartCountersign, stepBox, stepBox.HandlerClassifies, dto.NextStepCode, dto.NextMainHandler,
  782. prevStep?.Id, prevStep?.StartCountersignId, stepStatus, countersignStatus);
  783. }
  784. else
  785. {
  786. if (stepBoxDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any())
  787. throw new UserFriendlyException("未指定节点处理者");
  788. subSteps = CreateSubSteps(isPrevStartCountersign, stepBox, dto.NextHandlers, dto.NextStepCode, dto.NextMainHandler,
  789. prevStep?.Id, prevStep?.StartCountersignId, stepStatus, countersignStatus);
  790. }
  791. stepBox.Steps.AddRange(subSteps);
  792. await _workflowStepRepository.AddRangeAsync(subSteps, cancellationToken);
  793. }
  794. /// <summary>
  795. /// 查询未完成节点
  796. /// </summary>
  797. /// <param name="stepBoxes"></param>
  798. /// <param name="orgCode"></param>
  799. /// <param name="userId"></param>
  800. /// <returns></returns>
  801. private (WorkflowStep, WorkflowStep) GetUnCompleteStep(List<WorkflowStep> stepBoxes, string orgCode, string userId)
  802. {
  803. var (stepBox, step) = GetStep(stepBoxes, orgCode, userId, d => d != EWorkflowStepStatus.Completed);
  804. if (step == null)
  805. throw new UserFriendlyException(
  806. $"未找到对应节点, workflowId: {stepBoxes.FirstOrDefault()?.WorkflowId} orgCode:{orgCode}, userId: {userId}",
  807. "未找到对应节点");
  808. return (stepBox, step);
  809. }
  810. private (WorkflowStep, WorkflowStep) GetUnCompleteStepOrDefault(List<WorkflowStep> stepBoxes, string orgCode, string userId) =>
  811. GetStep(stepBoxes, orgCode, userId, d => d != EWorkflowStepStatus.Completed);
  812. private (WorkflowStep, WorkflowStep) GetStep(List<WorkflowStep> stepBoxes, string orgCode, string userId, Func<EWorkflowStepStatus, bool> predicate)
  813. {
  814. if (!stepBoxes.Any()) throw new UserFriendlyException("该流程中暂无节点");
  815. foreach (var stepBox in stepBoxes)
  816. {
  817. foreach (var step in stepBox.Steps)
  818. {
  819. if (predicate(step.Status) && (step.HandlerIds.Contains(orgCode) || step.HandlerIds.Contains(userId)))
  820. return (stepBox, step);
  821. }
  822. }
  823. return new();
  824. }
  825. private WorkflowStep CreateStepBox(string workflowId, StepDefine stepDefine, string prevStepBoxId)
  826. {
  827. var stepBox = _mapper.Map<WorkflowStep>(stepDefine);
  828. stepBox.WorkflowId = workflowId;
  829. stepBox.PreviousId = prevStepBoxId;
  830. stepBox.NextStepCode = string.Empty;
  831. stepBox.Opinion = string.Empty;
  832. return stepBox;
  833. }
  834. private List<WorkflowStep> CreateSubSteps(
  835. bool isPrevStartCountersign,
  836. WorkflowStep stepBox,
  837. List<IdName> handlers,
  838. string nextStepCode,
  839. string? nextMainHandler,
  840. string? prevStepId,
  841. string? countersignId,
  842. EWorkflowStepStatus stepStatus,
  843. EStepCountersignStatus countersignStatus)
  844. {
  845. if (countersignStatus is EStepCountersignStatus.None && !string.IsNullOrEmpty(countersignId))
  846. throw UserFriendlyException.SameMessage("非法参数");
  847. if (countersignStatus is not EStepCountersignStatus.None && string.IsNullOrEmpty(countersignId))
  848. throw UserFriendlyException.SameMessage("非法参数");
  849. //依据是否发起会签创建step,发起会签表示一个handler创建一个step,未发起会签表示多人处理同一个节点,只创建一个step
  850. var steps = new List<WorkflowStep>();
  851. if (isPrevStartCountersign)
  852. {
  853. foreach (var handler in handlers)
  854. {
  855. var step = CreateSubStep(stepBox, new List<IdName> { handler }, nextMainHandler, nextStepCode,
  856. prevStepId, countersignId, stepStatus, countersignStatus);
  857. steps.Add(step);
  858. }
  859. }
  860. else
  861. {
  862. var step = CreateSubStep(stepBox, handlers, nextMainHandler, nextStepCode,
  863. prevStepId, countersignId, stepStatus, countersignStatus);
  864. steps.Add(step);
  865. }
  866. return steps;
  867. }
  868. private WorkflowStep CreateSubStep(
  869. WorkflowStep stepBox,
  870. List<IdName> handlers,
  871. string nextStepCode,
  872. string? nextMainHandler,
  873. string? prevStepId,
  874. string? countersignId,
  875. EWorkflowStepStatus stepStatus,
  876. EStepCountersignStatus countersignStatus)
  877. {
  878. if (!handlers.Any())
  879. throw new UserFriendlyException("非法参数");
  880. var step = _mapper.Map<WorkflowStep>(stepBox);
  881. var handlerIds = handlers.Select(d => d.Id).ToList();
  882. var isMain = handlers.Count > 1 || handlerIds.First() == nextMainHandler;
  883. step.ParentId = stepBox.Id;
  884. step.HandlerIds = handlerIds;
  885. step.NextStepCode = step.StepType is EStepType.End ? string.Empty : nextStepCode;
  886. step.IsMain = isMain;
  887. step.PreviousId = prevStepId;
  888. step.CountersignId = countersignId;
  889. step.Status = stepStatus;
  890. step.StepCountersignStatus = countersignStatus;
  891. return step;
  892. }
  893. /// <summary>
  894. /// 依据配置生成过期时间
  895. /// </summary>
  896. /// <returns></returns>
  897. private DateTime GenerateExpiredTime(string defineCode)
  898. {
  899. //GetConfig(string defineCode).Time
  900. return DateTime.Now.AddDays(7); //todo 依据配置生成, Think about 工作日
  901. }
  902. private string GetTimeLimit(string defineCode)
  903. {
  904. //return GetConfig(string defineCode).Description;
  905. return "7个工作日";
  906. }
  907. //private ConfigInCludeDescriptionAndTime GetConfig(string defineCode)
  908. //{
  909. // throw new NotImplementedException();
  910. //}
  911. #endregion
  912. }
  913. }