WorkflowDomainService.cs 72 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553
  1. using Hotline.FlowEngine.Definitions;
  2. using Hotline.FlowEngine.Notifications;
  3. using Hotline.FlowEngine.WfModules;
  4. using Hotline.Settings;
  5. using Hotline.Share.Dtos;
  6. using Hotline.Share.Dtos.FlowEngine;
  7. using Hotline.Share.Enums.FlowEngine;
  8. using MapsterMapper;
  9. using MediatR;
  10. using Microsoft.Extensions.Logging;
  11. using XF.Domain.Authentications;
  12. using XF.Domain.Dependency;
  13. using XF.Domain.Exceptions;
  14. namespace Hotline.FlowEngine.Workflows
  15. {
  16. public class WorkflowDomainService : IWorkflowDomainService, IScopeDependency
  17. {
  18. private readonly IWorkflowRepository _workflowRepository;
  19. private readonly IWorkflowStepRepository _workflowStepRepository;
  20. private readonly IWorkflowTraceRepository _workflowTraceRepository;
  21. private readonly IWorkflowSupplementRepository _workflowSupplementRepository;
  22. private readonly IWorkflowAssignRepository _workflowAssignRepository;
  23. private readonly IWorkflowCountersignRepository _workflowCountersignRepository;
  24. private readonly ISessionContext _sessionContext;
  25. private readonly IMapper _mapper;
  26. private readonly IMediator _mediator;
  27. private readonly ILogger<WorkflowDomainService> _logger;
  28. public WorkflowDomainService(
  29. IWorkflowRepository workflowRepository,
  30. IWorkflowStepRepository workflowStepRepository,
  31. IWorkflowTraceRepository workflowTraceRepository,
  32. IWorkflowAssignRepository workflowAssignRepository,
  33. IWorkflowSupplementRepository workflowSupplementRepository,
  34. IWorkflowCountersignRepository workflowCountersignRepository,
  35. ISessionContext sessionContext,
  36. IMapper mapper,
  37. IMediator mediator,
  38. ILogger<WorkflowDomainService> logger)
  39. {
  40. _workflowRepository = workflowRepository;
  41. _workflowStepRepository = workflowStepRepository;
  42. _workflowTraceRepository = workflowTraceRepository;
  43. _workflowAssignRepository = workflowAssignRepository;
  44. _workflowSupplementRepository = workflowSupplementRepository;
  45. _workflowCountersignRepository = workflowCountersignRepository;
  46. _sessionContext = sessionContext;
  47. _mapper = mapper;
  48. _mediator = mediator;
  49. _logger = logger;
  50. }
  51. public async Task<Workflow> CreateWorkflowAsync(WorkflowModule wfModule, string title, string userId, string userCode,
  52. string? externalId = null, CancellationToken cancellationToken = default)
  53. {
  54. var definition = wfModule.Definition;
  55. if (definition is null)
  56. throw new UserFriendlyException("无效流程模板");
  57. var workflow = new Workflow
  58. {
  59. Title = title,
  60. ModuleId = wfModule.Id,
  61. ModuleName = wfModule.Name,
  62. ModuleCode = wfModule.Code,
  63. DefinitionId = definition.Id,
  64. Status = EWorkflowStatus.Runnable,
  65. TimeLimit = GetTimeLimit(definition.Code),//todo 过期时间
  66. ExpiredTime = CalculateExpiredTime(definition.Code),//todo 过期时间
  67. StepBoxes = new(),
  68. Traces = new(),
  69. Definition = definition,
  70. CenterToOrgTime = DateTime.Now,
  71. ExternalId = externalId ?? string.Empty,
  72. AssignOrgCodes = new List<string> { userCode },
  73. AssignUserIds = new List<string> { userId },
  74. };
  75. await _workflowRepository.AddAsync(workflow, cancellationToken);
  76. return workflow;
  77. }
  78. /// <summary>
  79. /// 流程开始
  80. /// </summary>
  81. /// <param name="workflow"></param>
  82. /// <param name="dto"></param>
  83. /// <param name="cancellationToken"></param>
  84. /// <returns></returns>
  85. public async Task StartAsync(Workflow workflow, BasicWorkflowDto dto, StepDefine nextStepBoxDefine,
  86. FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
  87. {
  88. //创建开始节点
  89. var (startStepBox, startStep, firstStepBox) = await CreateStartAndFirstStepAsync(workflow, dto, cancellationToken);
  90. //1.创建开始节点trace 2.创建firstStep(开始节点的下一个节点),办理firstStep 3.创建sec节点
  91. //办理firstStep
  92. var firstStep = firstStepBox.Steps.First();
  93. var counterSignType = GetCounterSignType(firstStep.BusinessProperty);
  94. await HandleStepAsync(workflow, dto, firstStepBox, firstStep, counterSignType, cancellationToken);
  95. await _workflowStepRepository.UpdateRangeAsync(new List<WorkflowStep> { firstStepBox, firstStep }, cancellationToken);
  96. //firstStep trace
  97. await NextTraceAsync(workflow, dto, firstStep, cancellationToken);
  98. //secondStep
  99. var secondStepDefine = workflow.Definition.FindStepDefine(dto.NextStepCode);
  100. var secondStepBox = await CreateStepAsync(dto.IsStartCountersign, workflow, secondStepDefine, dto,
  101. EWorkflowStepStatus.Created, firstStepBox, firstStep, EWorkflowTraceStatus.Normal,
  102. workflow.ExpiredTime, cancellationToken);
  103. //更新实际办理节点信息
  104. workflow.UpdateWorkflowActualHandleInfo(firstStepBox, firstStep,
  105. _sessionContext.RequiredUserId, _sessionContext.UserName,
  106. _sessionContext.RequiredOrgCode, _sessionContext.OrgName,
  107. _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName);
  108. //更新当前办理节点信息
  109. workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, firstStep, secondStepBox.Steps.First());
  110. //发起会签时记录顶层会签节点(必须在update currentStep之后)
  111. if (dto.IsStartCountersign && !workflow.IsInCountersign())
  112. workflow.StartCountersign(firstStepBox.Code, counterSignType);
  113. //更新受理人信息
  114. workflow.UpdateAcceptor(
  115. _sessionContext.RequiredUserId,
  116. _sessionContext.UserName,
  117. _sessionContext.StaffNo,
  118. _sessionContext.RequiredOrgCode,
  119. _sessionContext.OrgName);
  120. workflow.UpdateHandlers(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgCode,
  121. flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
  122. //更新指派信息
  123. workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlers());
  124. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  125. //publish
  126. await _mediator.Publish(new StartWorkflowNotify(workflow, dto, flowAssignInfo), cancellationToken);
  127. }
  128. public async Task<Workflow> GetWorkflowAsync(string workflowId,
  129. bool withDefine = false, bool withSteps = false,
  130. bool withTraces = false, bool withSupplements = false,
  131. bool withAssigns = false, bool withCountersigns = false,
  132. CancellationToken cancellationToken = default)
  133. {
  134. var query = _workflowRepository.Queryable().Where(d => d.Id == workflowId);
  135. if (withDefine)
  136. query = query.Includes(d => d.Definition);
  137. if (withSupplements)
  138. query = query.Includes(d => d.Supplements, d => d.Creator);
  139. //if (withAssigns)
  140. // query = query.Includes(d => d.Assigns);
  141. if (withCountersigns)
  142. query = query.Includes(d => d.Countersigns);
  143. var workflow = await query.FirstAsync();
  144. if (workflow is null)
  145. throw new UserFriendlyException("无效workflowId");
  146. if (withSteps)
  147. {
  148. var steps = await _workflowStepRepository.Queryable()
  149. .Where(d => d.WorkflowId == workflow.Id)
  150. .OrderBy(d => d.CreationTime)
  151. .ToTreeAsync(d => d.Steps, d => d.ParentId, null);
  152. workflow.StepBoxes = steps;
  153. }
  154. if (withTraces)
  155. {
  156. var traces = await _workflowTraceRepository.Queryable()
  157. .Where(d => d.WorkflowId == workflow.Id)
  158. .OrderBy(d => d.CreationTime)
  159. .ToTreeAsync(d => d.Traces, d => d.ParentId, null);
  160. workflow.Traces = traces;
  161. }
  162. return workflow;
  163. }
  164. /// <summary>
  165. /// 查询工作流包含当前用户办理权限(是否可办理)
  166. /// </summary>
  167. public async Task<(Workflow, bool)> GetWorkflowHandlePermissionAsync(string workflowId, string userId, string orgCode, bool withDefine = false,
  168. bool withSteps = false, bool withTraces = false, bool withSupplements = false, bool withAssigns = false,
  169. bool withCountersigns = false, CancellationToken cancellationToken = default)
  170. {
  171. var workflow = await GetWorkflowAsync(workflowId, withDefine, withSteps, withTraces, withSupplements, withAssigns, withCountersigns, cancellationToken);
  172. var canHandle = workflow.CanHandle(userId, orgCode);
  173. return (workflow, canHandle);
  174. }
  175. /// <summary>
  176. /// 受理(接办)
  177. /// </summary>
  178. public async Task AcceptAsync(Workflow workflow, string userId, string userName, string orgCode, string orgName, CancellationToken cancellationToken)
  179. {
  180. if (!workflow.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgCode)) return;
  181. //工单完成以后查看的场景
  182. if (workflow.Status != EWorkflowStatus.Runnable) return;
  183. var (currentStepBox, currentStep) = GetUnCompleteStepOrDefault(workflow.StepBoxes, orgCode, userId);
  184. if (currentStep is null) return;
  185. if (currentStep.Status is EWorkflowStepStatus.Accepted) return;
  186. if (currentStep.HandlerType is EHandlerType.AssignUser or EHandlerType.Role)
  187. {
  188. //userId
  189. if (currentStep.Handlers.All(d => d.Id != userId)) return;
  190. }
  191. else
  192. {
  193. //orgId
  194. if (currentStep.Handlers.All(d => d.Id != orgCode)) return;
  195. }
  196. if (currentStep.StepType is EStepType.End)
  197. throw new UserFriendlyException("当前流程已流转到最终步骤");
  198. var changedSteps = new List<WorkflowStep> { currentStep };
  199. if (currentStepBox.Status is EWorkflowStepStatus.Assigned)
  200. {
  201. currentStepBox.Status = EWorkflowStepStatus.Accepted;
  202. changedSteps.Add(currentStepBox);
  203. }
  204. currentStep.Accept(userId, userName, _sessionContext.RequiredOrgCode, _sessionContext.OrgName);
  205. //接办时非会签并且有多个接办部门时需更新接办部门
  206. if (!workflow.IsInCountersign())
  207. {
  208. var assigns = await _workflowAssignRepository.QueryAsync(d => d.WorkflowId == workflow.Id);
  209. if (assigns.Count > 1)
  210. {
  211. await _workflowAssignRepository.RemoveRangeAsync(assigns, cancellationToken);
  212. var assign = WorkflowAssign.Create(workflow.Id, orgCode, orgName);
  213. await _workflowAssignRepository.AddAsync(assign, cancellationToken);
  214. }
  215. }
  216. await _workflowStepRepository.UpdateRangeAsync(changedSteps, cancellationToken);
  217. await AcceptTraceAsync(workflow, currentStep, cancellationToken);
  218. await _mediator.Publish(new AcceptWorkflowNotify(workflow), cancellationToken);
  219. }
  220. /// <summary>
  221. /// 办理(流转至下一节点)
  222. /// </summary>
  223. public async Task NextAsync(Workflow workflow, NextWorkflowDto dto, StepDefine nextStepBoxDefine,
  224. bool isStartCountersign, FlowAssignInfo flowAssignInfo, DateTime expiredTime, CancellationToken cancellationToken)
  225. {
  226. ValidatePermission(workflow);
  227. CheckWhetherRunnable(workflow.Status);
  228. #region 办理当前节点
  229. var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgCode, _sessionContext.RequiredUserId);
  230. var counterSignType = GetCounterSignType(currentStep.BusinessProperty);
  231. await HandleStepAsync(workflow, dto, currentStepBox, currentStep, counterSignType, cancellationToken);
  232. //update realhandle info
  233. _mapper.Map(dto, workflow);
  234. var updateSteps = new List<WorkflowStep> { currentStepBox, currentStep };
  235. //结束当前会签流程
  236. if (currentStep.StepType is EStepType.CountersignEnd && currentStep.IsInCountersign)
  237. {
  238. var currentCountersign = workflow.Countersigns.FirstOrDefault(d => d.Id == currentStep.CountersignId);
  239. if (currentCountersign is null)
  240. throw new UserFriendlyException(
  241. $"未查询到对应会签信息,workflowId:{workflow.Id}, countersignId:{currentStep.CountersignId}", "无效会签编号");
  242. //1.根据当前节点配置查找结束节点对应开始节点 2.如该开始节点与当前会签开始节点吻合说明可以结束
  243. //如果!=,说明未发起会签而是继承的外层会签
  244. if (currentStepBox.CountersignStartStepCode == currentCountersign.StartStepCode)
  245. {
  246. //结束step会签信息
  247. var countersignStartStepBox =
  248. workflow.StepBoxes.FirstOrDefault(d => d.Code == currentCountersign.StartStepCode);
  249. if (countersignStartStepBox is null)
  250. throw new UserFriendlyException(
  251. $"未查询到会签开始stepBox, workflowId: {workflow.Id}, startStepCode: {currentCountersign.StartStepCode}", "未查询到会签开始节点");
  252. var countersignStartStep =
  253. countersignStartStepBox.Steps.FirstOrDefault(d => d.HasStartedCountersign && d.Id == currentCountersign.StartStepId);
  254. if (countersignStartStep is null)
  255. throw new UserFriendlyException(
  256. $"未查询到会签开始step, workflowId: {workflow.Id}, startStepId: {currentCountersign.StartStepId}", "未查询到会签开始节点");
  257. countersignStartStep.CountersignComplete();
  258. updateSteps.Add(countersignStartStep);
  259. //结束会签
  260. currentCountersign.Complete(currentStep.Id, currentStep.Code, currentStep.BusinessProperty);
  261. await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken);
  262. }
  263. }
  264. await _workflowStepRepository.UpdateRangeAsync(updateSteps, cancellationToken);
  265. await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
  266. #endregion
  267. #region 处理流程
  268. //检查会签是否结束,并更新当前会签节点字段
  269. var isCountersignOver = false;
  270. if (workflow.IsInCountersign() && currentStep.StepType is EStepType.CountersignEnd)
  271. {
  272. isCountersignOver = workflow.CheckIfCountersignOver();
  273. if (isCountersignOver)
  274. workflow.EndCountersign();
  275. }
  276. //检查是否流转到流程终点
  277. if (nextStepBoxDefine.StepType is EStepType.End && !workflow.IsInCountersign())
  278. {
  279. var endTrace = await WorkflowEnd(workflow, dto, nextStepBoxDefine, currentStepBox, currentStep, cancellationToken);
  280. return;
  281. }
  282. //是否从中心流转出去,重新计算expiredTime
  283. var isCenterToOrg = CheckIfFlowFromCenterToOrg(currentStepBox, nextStepBoxDefine);
  284. if (isCenterToOrg)
  285. workflow.CenterToOrg(CalculateExpiredTime(workflow.Definition.Code));//todo 过期时间
  286. ////最终办理意见与时间处理(解决工单业务需求,如果流程配置普通节点后未配置汇总节点则不会运行到此处)
  287. //var normalSteps = workflow.StepBoxes
  288. // .Where(d => d.StepType is EStepType.Normal)
  289. // .SelectMany(d => d.Steps);
  290. //var isAllCompleted = normalSteps.All(d => d.Status is EWorkflowStepStatus.Completed);
  291. //if (isAllCompleted)
  292. //{
  293. // //最终办理
  294. // workflow.ActualOpinion = dto.Opinion;
  295. // workflow.Status = EWorkflowStatus.Marked;
  296. // await _mediator.Publish(new OrderFinalManageNotify(workflow), cancellationToken);
  297. //}
  298. //else if (workflow.Status is EWorkflowStatus.Marked)
  299. //{
  300. // //汇总以后又重新指派到非汇总节点办理
  301. // workflow.ResetOption();
  302. // workflow.Status = EWorkflowStatus.Runnable;
  303. // await _mediator.Publish(new OrderRecallFinalManageNotify(workflow), cancellationToken);
  304. //}
  305. //创建下一节点(会签汇总节点不重复创建)
  306. var nextStepBox = await CreateStepAsync(isStartCountersign, workflow, nextStepBoxDefine, dto, EWorkflowStepStatus.Created,
  307. currentStepBox, currentStep, EWorkflowTraceStatus.Normal, expiredTime, cancellationToken);
  308. //下一节点为汇总节点时,检查下一节点是否可办理
  309. var nextStepCanHandle = true;
  310. if (nextStepBox.StepType is EStepType.CountersignEnd)
  311. {
  312. if (currentStep.IsInCountersign)
  313. {
  314. //同一会签Id,非汇总节点
  315. var steps = await _workflowStepRepository.QueryAsync(d =>
  316. d.WorkflowId == workflow.Id
  317. && d.CountersignId == currentStep.CountersignId
  318. && d.StepType != EStepType.End);
  319. //(当前办理节点所处同一会签内的所有step全都办理完成并且如果开启了会签的step,必须会签结束)
  320. var unComplete = steps.Any(d =>
  321. d.Status != EWorkflowStepStatus.Completed ||
  322. (d.HasStartedCountersign && !(d.IsStartedCountersignComplete ?? false)));
  323. nextStepCanHandle = !unComplete;
  324. }
  325. }
  326. if (nextStepCanHandle)
  327. {
  328. //将下一节点处理为已指派/可接办
  329. await SetNextCountersignEndAssignedAsync(nextStepBox, currentStep, cancellationToken);
  330. workflow.UpdateHandlers(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgCode,
  331. flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
  332. await _mediator.Publish(new CountersignEndAssigned(workflow), cancellationToken);
  333. }
  334. //更新实际办理节点名称、时间
  335. workflow.UpdateWorkflowActualHandleInfo(currentStepBox, currentStep,
  336. _sessionContext.RequiredUserId, _sessionContext.UserName,
  337. _sessionContext.RequiredOrgCode, _sessionContext.OrgName,
  338. _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName);
  339. //更新当前办理节点信息
  340. workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, currentStep, nextStepBox.Steps.First());
  341. //发起会签时记录顶层会签节点
  342. if (isStartCountersign && !workflow.IsInCountersign())
  343. workflow.StartCountersign(currentStepBox.Code, counterSignType);
  344. //更新指派信息
  345. workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlers());
  346. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  347. #endregion
  348. #region 流转记录
  349. var trace = await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
  350. #endregion
  351. #region 处理额外参数(短信通知、办理时限、省延期)
  352. //需统一处理的放在这里,与业务关联的放在业务中去处理,如:省延期
  353. //todo
  354. #endregion
  355. await _mediator.Publish(new NextStepNotify(workflow, dto, trace,
  356. isCenterToOrg, isStartCountersign, isCountersignOver,
  357. _sessionContext.RequiredOrgCode, flowAssignInfo), cancellationToken);
  358. }
  359. /// <summary>
  360. /// 退回(返回前一节点)
  361. /// </summary>
  362. /// <returns></returns>
  363. public async Task PreviousAsync(Workflow workflow, PreviousWorkflowDto dto, CancellationToken cancellationToken)
  364. {
  365. ValidatePermission(workflow);
  366. CheckWhetherRunnable(workflow.Status);
  367. if (workflow.IsInCountersign())
  368. throw UserFriendlyException.SameMessage("会签流程不支持退回");
  369. var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgCode, _sessionContext.RequiredUserId);
  370. if (currentStepBox.StepType is EStepType.Start)
  371. throw UserFriendlyException.SameMessage("当前流程已退回到开始节点");
  372. //find prevStep, update handler
  373. var prevStepBox = workflow.StepBoxes.FirstOrDefault(d => d.Id == currentStepBox.PreviousId);
  374. if (prevStepBox == null)
  375. throw UserFriendlyException.SameMessage("未查询到上级节点");
  376. var prevStep = prevStepBox.Steps.FirstOrDefault(d => d.Id == currentStep.PreviousId);
  377. if (prevStep == null)
  378. throw UserFriendlyException.SameMessage("未查询到前一节点");
  379. if (prevStep.StepType is EStepType.Start)
  380. throw UserFriendlyException.SameMessage("当前流程已退回到第一节点");
  381. //update trace
  382. await PreviousTraceAsync(workflow.Id, dto, currentStep, cancellationToken);
  383. //检查并重置上级stepbox状态为待接办
  384. await ResetStepBoxStatusAsync(prevStepBox, cancellationToken);
  385. //复制上一个节点为待接办
  386. var newPrevStep = await CreatePrevStepAsync(workflow, prevStep, cancellationToken);
  387. //remove workflow.steps
  388. await _workflowStepRepository.RemoveRangeAsync(new List<WorkflowStep> { currentStepBox, currentStep, prevStep }, cancellationToken);
  389. //更新当前办理节点信息
  390. workflow.UpdateWorkflowCurrentStepInfo(false, nextStep: newPrevStep);
  391. //更新流程可办理对象
  392. workflow.UpdatePreviousHandlers(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgCode, newPrevStep);
  393. //orgToCenter会触发重新计算期满时间,1.无需审核按当前时间进行计算 2.需审核按审核通过时间计算
  394. var isOrgToCenter = CheckIfFlowFromOrgToCenter(currentStepBox, prevStepBox);
  395. if (isOrgToCenter)
  396. workflow.OrgToCenter(CalculateExpiredTime(""));//todo 过期时间
  397. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  398. await _mediator.Publish(new PreviousNotify(workflow, dto, isOrgToCenter), cancellationToken);
  399. }
  400. /// <summary>
  401. /// 撤回(返回到之前任意节点)
  402. /// </summary>
  403. public async Task RecallAsync(Workflow workflow, RecallDto dto, StepDefine targetStepDefine, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
  404. {
  405. if (targetStepDefine.StepType is EStepType.Start or EStepType.End)
  406. throw UserFriendlyException.SameMessage("开始/结束节点不支持撤回");
  407. var targetStepBox = workflow.StepBoxes.FirstOrDefault(d => d.Code == dto.NextStepCode);
  408. if (targetStepBox is null)
  409. throw UserFriendlyException.SameMessage("该流程尚未流转至该节点");
  410. //update uncompleted traces
  411. await RecallTraceAsync(workflow.Id, cancellationToken);
  412. var isOrgToCenter = await RecallAsync(workflow, dto, targetStepDefine, targetStepBox, EWorkflowTraceStatus.Recall, cancellationToken);
  413. workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
  414. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  415. await _mediator.Publish(new RecallNotify(workflow, dto, isOrgToCenter), cancellationToken);
  416. }
  417. /// <summary>
  418. /// 跳转(直接将流程跳转至任意节点)
  419. /// </summary>
  420. public async Task JumpAsync(Workflow workflow, RecallDto dto, StepDefine targetStepDefine,
  421. bool isStartCountersign, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
  422. {
  423. if (targetStepDefine.StepType is EStepType.Start or EStepType.End)
  424. throw UserFriendlyException.SameMessage("开始/结束节点不支持跳转");
  425. //update uncompleted traces
  426. await JumpTraceAsync(workflow.Id, dto, cancellationToken);
  427. bool isOrgToCenter = false, isCenterToOrg = false;
  428. var targetStepBox = workflow.StepBoxes.FirstOrDefault(d => d.Code == dto.NextStepCode);
  429. if (targetStepBox == null)
  430. {
  431. //向后跳转
  432. //此场景并非按配置流转,默认最靠后的节点做为targetStep的prevStep
  433. var lastStepBox = workflow.StepBoxes.MaxBy(d => d.CreationTime);
  434. if (lastStepBox is null || lastStepBox.StepType is EStepType.End)
  435. throw new UserFriendlyException($"流程流转数据异常,未结束流程出现endStep, flowId: {workflow.Id}", "流程流转数据异常");
  436. targetStepBox = await CreateStepAsync(isStartCountersign, workflow, targetStepDefine, dto,
  437. EWorkflowStepStatus.Assigned, lastStepBox, lastStepBox.Steps.First(),
  438. EWorkflowTraceStatus.Jump, workflow.ExpiredTime, cancellationToken);
  439. workflow.EndCountersign();
  440. workflow.ResetOption();
  441. //更新当前办理节点信息
  442. workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, nextStep: targetStepBox.Steps.First());
  443. //calc workflow expired time
  444. isCenterToOrg = CheckIfFlowFromCenterToOrg(workflow, targetStepBox);
  445. if (isCenterToOrg)
  446. workflow.ExpiredTime = CalculateExpiredTime("");
  447. #region 补充中间节点处理方案(暂不需要)
  448. //var completeStepCodes = workflow.StepBoxes.Select(d => d.Code);
  449. //var uncompleteStepDefines = workflow.Definition.Steps.Where(d => !completeStepCodes.Contains(d.Code));
  450. //创建当前节点与目标节点中间节点
  451. //var jumpDto = new BasicWorkflowDto
  452. //{
  453. // Opinion = "跳转补充"
  454. //};
  455. //foreach (var stepDefine in uncompleteStepDefines)
  456. //{
  457. // var previousStepId = lastStepBox.Steps.Count > 1 ? lastStepBox.Id : lastStepBox.Steps.First().Id;
  458. // if (dto.TargetStepCode == stepDefine.Code)
  459. // {
  460. // await CreateStepAsync(workflow, stepDefine, dto, lastStepBox.Id, previousStepId, cancellationToken);
  461. // break;
  462. // }
  463. // //jump业务下,如果当前节点为会签节点,第一个补充节点的subStep.PreviousId无法确定从哪个子节点跳转过来,统一处理为当前节点的stepBox.Id
  464. // lastStepBox = await CreateStepAsync(workflow, stepDefine, dto, lastStepBox.Id, previousStepId, cancellationToken);
  465. //}
  466. #endregion
  467. }
  468. else
  469. {
  470. //返回之前节点
  471. isOrgToCenter = await RecallAsync(workflow, dto, targetStepDefine, targetStepBox, EWorkflowTraceStatus.Jump, cancellationToken);
  472. }
  473. workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
  474. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  475. await _mediator.Publish(new JumpNotify(workflow, dto, flowAssignInfo, isCenterToOrg, isOrgToCenter), cancellationToken);
  476. }
  477. /// <summary>
  478. /// 重办
  479. /// </summary>
  480. public async Task RedoAsync(Workflow workflow, RecallDto dto, StepDefine targetStepDefine,
  481. FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
  482. {
  483. if (targetStepDefine.StepType is EStepType.Start or EStepType.End)
  484. throw UserFriendlyException.SameMessage("开始/结束节点不支持重办");
  485. var targetStepBox = workflow.StepBoxes.FirstOrDefault(d => d.Code == dto.NextStepCode);
  486. if (targetStepBox is null)
  487. throw UserFriendlyException.SameMessage("未找到该节点配置");
  488. var isOrgToCenter = await RecallAsync(workflow, dto, targetStepDefine, targetStepBox, EWorkflowTraceStatus.Redo, cancellationToken);
  489. workflow.Redo();
  490. workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
  491. //todo calc expiredTime
  492. //dto.Extension.TimeLimitCount
  493. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  494. await _mediator.Publish(new RedoNotify(workflow, dto, isOrgToCenter), cancellationToken);
  495. }
  496. /// <summary>
  497. /// 否决(审批流程不通过)
  498. /// </summary>
  499. /// <returns></returns>
  500. public async Task RejectAsync(Workflow workflow, BasicWorkflowDto dto, CancellationToken cancellationToken)
  501. {
  502. var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgCode, _sessionContext.RequiredUserId);
  503. var endStepDefine = workflow.Definition.FindEndStepDefine();
  504. var endTrace = await WorkflowEnd(workflow, dto, endStepDefine, currentStepBox, currentStep, cancellationToken);
  505. await _mediator.Publish(new RejectNotify(workflow, dto), cancellationToken);
  506. }
  507. /// <summary>
  508. /// 补充
  509. /// </summary>
  510. /// <returns></returns>
  511. public async Task SupplementAsync(Workflow workflow, EndWorkflowDto dto, CancellationToken cancellationToken)
  512. {
  513. CheckWhetherRunnable(workflow.Status);
  514. //todo 检查当前办理人是否为该流程中的办理人
  515. var supplement = _mapper.Map<WorkflowSupplement>(dto);
  516. await _workflowSupplementRepository.AddAsync(supplement, cancellationToken);
  517. }
  518. /// <summary>
  519. /// 终止流程
  520. /// </summary>
  521. public async Task TerminateAsync(TerminateDto dto, CancellationToken cancellationToken)
  522. {
  523. var workflow = await _workflowRepository.GetAsync(dto.WorkflowId, cancellationToken);
  524. if (workflow == null)
  525. throw UserFriendlyException.SameMessage("无效的流程编号");
  526. //workflow.Terminate(dto.Opinion);
  527. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  528. await _mediator.Publish(new TerminalWorkflowNotify(workflow), cancellationToken);
  529. }
  530. /// <summary>
  531. /// 根据stepCode查询流程配置中对应的节点
  532. /// </summary>
  533. public StepDefine GetStepBoxDefine(Definition definition, string stepCode)
  534. {
  535. if (definition == null) throw new ArgumentNullException(nameof(definition));
  536. if (string.IsNullOrEmpty(stepCode)) throw new ArgumentNullException(nameof(stepCode));
  537. var stepDefine = definition.FindStepDefine(stepCode);
  538. if (stepDefine == null)
  539. throw new UserFriendlyException($"未找到流程中对应的节点,DefineCode: {definition.Code}, stepCode: {stepCode}",
  540. "未查询到对应节点");
  541. return stepDefine;
  542. }
  543. /// <summary>
  544. /// 查询当前待办节点的下一级节点配置(办理参数)
  545. /// </summary>
  546. public IReadOnlyList<StepDefine> GetNextStepDefines(Workflow workflow)
  547. {
  548. var (currentStepBox, _) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgCode, _sessionContext.RequiredUserId);
  549. return workflow.Definition.FindStepDefines(currentStepBox.NextSteps.Select(d => d.Code));
  550. }
  551. /// <summary>
  552. /// 查找当前办理节点
  553. /// </summary>
  554. public (WorkflowStep StepBox, WorkflowStep Step) FindCurrentStep(Workflow workflow) =>
  555. GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgCode, _sessionContext.RequiredUserId);
  556. /// <summary>
  557. /// 查询待回访部门
  558. /// </summary>
  559. /// <returns></returns>
  560. public async Task<(IdName, IReadOnlyList<IdName>)> GetUnvisitOrgsAsync(string workflowId, CancellationToken cancellationToken)
  561. {
  562. var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellationToken);
  563. if (workflow.CounterSignType is not ECounterSignType.Center)
  564. return new(new IdName(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), new List<IdName>());
  565. var steps = workflow.StepBoxes
  566. .Where(d => d.StepType is EStepType.Normal && d.BusinessProperty is EBusinessProperty.Department)
  567. .SelectMany(d => d.Steps)
  568. .ToList();
  569. var items = steps.Select(d => new IdName(d.OrgCode, d.OrgName)).ToList();
  570. return (new IdName(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), items);
  571. }
  572. /// <summary>
  573. /// 更新一级部门信息
  574. /// </summary>
  575. public async Task UpdateOrgLevelOneAsync(Workflow workflow, string orgCode, string orgName, CancellationToken cancellationToken)
  576. {
  577. workflow.UpdateOrgLevelOne(orgCode, orgName);
  578. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  579. }
  580. /// <summary>
  581. /// 依据配置过滤下一节点
  582. /// </summary>
  583. public List<StepDefine> NextStepDefineFilter(EPathPolicy pathPolicy, List<StepDefine> nextStepDefines)
  584. {
  585. switch (pathPolicy)
  586. {
  587. case EPathPolicy.DirectUpper:
  588. break;
  589. case EPathPolicy.DirectUpperCenterIsTop:
  590. var currentOrgLevel = _sessionContext.RequiredOrgCode.CalcOrgLevel();
  591. if (currentOrgLevel == 1)
  592. {
  593. nextStepDefines = nextStepDefines.Where(d => d.IsCenter()).ToList();
  594. }
  595. else
  596. {
  597. var upperLevel = (--currentOrgLevel).ToString();
  598. nextStepDefines = nextStepDefines
  599. .Where(d => d.HandlerType is EHandlerType.OrgLevel &&
  600. d.HandlerClassifies.Any(x => x.Id == upperLevel))
  601. .ToList();
  602. }
  603. break;
  604. default:
  605. throw new ArgumentOutOfRangeException();
  606. }
  607. return nextStepDefines;
  608. }
  609. /// <summary>
  610. /// 撤销流程
  611. /// </summary>
  612. public async Task CancelAsync(CancelDto dto, CancellationToken cancellationToken)
  613. {
  614. var workflow = await GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true, cancellationToken: cancellationToken);
  615. var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.StepBoxes, _sessionContext.RequiredOrgCode, _sessionContext.RequiredUserId);
  616. var endStepDefine = workflow.Definition.FindEndStepDefine();
  617. var basicDto = _mapper.Map<BasicWorkflowDto>(dto);
  618. var endTrace = await WorkflowEnd(workflow, basicDto, endStepDefine, currentStepBox, currentStep, cancellationToken);
  619. await _mediator.Publish(new CancelWorkflowNotify(workflow), cancellationToken);
  620. }
  621. #region private method
  622. /// <summary>
  623. /// 流程结束
  624. /// </summary>
  625. private async Task<WorkflowTrace> WorkflowEnd(Workflow workflow, BasicWorkflowDto dto, StepDefine nextStepBoxDefine,
  626. WorkflowStep currentStepBox, WorkflowStep currentStep, CancellationToken cancellationToken)
  627. {
  628. //create endStep
  629. var (_, endStep) = await CreateEndStepAsync(workflow, nextStepBoxDefine, currentStepBox, currentStep, cancellationToken);
  630. //update endTrace
  631. var endTrace = await NextTraceAsync(workflow, dto, endStep, cancellationToken);
  632. workflow.Complete();
  633. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  634. await _mediator.Publish(new EndWorkflowNotify(workflow, endTrace), cancellationToken);
  635. return endTrace;
  636. }
  637. /// <summary>
  638. /// 判断会签类型(中心会签或部门会签)
  639. /// </summary>
  640. /// <param name="businessProperty"></param>
  641. /// <returns></returns>
  642. /// <exception cref="ArgumentOutOfRangeException"></exception>
  643. private ECounterSignType GetCounterSignType(EBusinessProperty businessProperty) =>
  644. businessProperty switch
  645. {
  646. EBusinessProperty.Center => ECounterSignType.Center,
  647. EBusinessProperty.Send => ECounterSignType.Center,
  648. EBusinessProperty.Department => ECounterSignType.Department,
  649. _ => throw new ArgumentOutOfRangeException(nameof(businessProperty), businessProperty, null)
  650. };
  651. /// <summary>
  652. /// 办理节点
  653. /// </summary>
  654. private async Task HandleStepAsync(Workflow workflow, BasicWorkflowDto dto, WorkflowStep currentStepBox, WorkflowStep currentStep,
  655. ECounterSignType counterSignType, CancellationToken cancellationToken)
  656. {
  657. if (currentStep.Status is EWorkflowStepStatus.Completed or EWorkflowStepStatus.Created)
  658. throw UserFriendlyException.SameMessage("当前节点状态无法办理");
  659. if (currentStep.Status is EWorkflowStepStatus.Assigned)
  660. await AcceptAsync(workflow,
  661. _sessionContext.RequiredUserId,
  662. _sessionContext.UserName,
  663. _sessionContext.RequiredOrgCode,
  664. _sessionContext.OrgName,
  665. cancellationToken);
  666. if (currentStep.StepType is EStepType.End)
  667. throw new UserFriendlyException("当前流程已流转到最终步骤");
  668. //创建会签数据
  669. if (dto.IsStartCountersign)
  670. await StartCountersignAsync(workflow, dto, currentStepBox, currentStep, counterSignType, cancellationToken);
  671. _mapper.Map(dto, currentStep);
  672. //step办理状态
  673. currentStep.Complete(
  674. _sessionContext.RequiredUserId, _sessionContext.UserName,
  675. _sessionContext.RequiredOrgCode, _sessionContext.OrgName,
  676. _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName,
  677. dto.NextStepCode);
  678. //stepBox办理状态
  679. currentStepBox.CheckStepBoxStatusAndUpdate();
  680. }
  681. /// <summary>
  682. /// 开始会签(创建会签数据,更新currentStep会签数据)
  683. /// </summary>
  684. private async Task StartCountersignAsync(Workflow workflow, BasicWorkflowDto dto,
  685. WorkflowStep currentStepBox, WorkflowStep currentStep, ECounterSignType counterSignType,
  686. CancellationToken cancellationToken)
  687. {
  688. var countersign = await CreateCountersignAsync(workflow.Id, currentStep, currentStepBox.CountersignEndStepCode,
  689. dto.NextHandlers.Count, counterSignType, currentStep.CountersignId, cancellationToken);
  690. currentStep.StartCountersign(countersign.Id);
  691. }
  692. /// <summary>
  693. /// 检查是否从中心流转至部门
  694. /// </summary>
  695. private bool CheckIfFlowFromCenterToOrg(WorkflowStep sourceStepBox, StepDefine targetStepBoxDefine)
  696. {
  697. var isFromCenter = sourceStepBox.IsCenter();
  698. if (!isFromCenter) return false;
  699. var isToOrg = targetStepBoxDefine.IsOrg();
  700. return isFromCenter && isToOrg;
  701. }
  702. /// <summary>
  703. /// 检查是否从中心流转至部门
  704. /// </summary>
  705. private bool CheckIfFlowFromCenterToOrg(Workflow workflow, WorkflowStep targetStepBox)
  706. {
  707. var isToOrg = targetStepBox.IsOrg();
  708. if (!isToOrg) return false;
  709. var isFromCenter = workflow.StepBoxes.All(d => d.BusinessProperty is not EBusinessProperty.Department);
  710. return isFromCenter && isToOrg;
  711. }
  712. /// <summary>
  713. /// 检查是否从部门流转至中心
  714. /// </summary>
  715. private bool CheckIfFlowFromOrgToCenter(WorkflowStep sourceStepBox, StepDefine targetStepBoxDefine)
  716. {
  717. var isFromOrg = sourceStepBox.IsOrg();
  718. if (!isFromOrg) return false;
  719. var isToCenter = targetStepBoxDefine.IsCenter();
  720. return isFromOrg && isToCenter;
  721. }
  722. /// <summary>
  723. /// 检查是否从部门流转至中心
  724. /// </summary>
  725. private bool CheckIfFlowFromOrgToCenter(WorkflowStep sourceStepBox, WorkflowStep targetStepBox)
  726. {
  727. var isFromOrg = sourceStepBox.IsOrg();
  728. if (!isFromOrg) return false;
  729. var isToCenter = targetStepBox.IsCenter();
  730. return isFromOrg && isToCenter;
  731. }
  732. /// <summary>
  733. /// 检查是否从部门流转至中心
  734. /// </summary>
  735. private bool CheckIfFlowFromOrgToCenter(Workflow workflow, WorkflowStep targetStepBox)
  736. {
  737. var isToCenter = targetStepBox.IsCenter();
  738. if (!isToCenter) return false;
  739. var isFromOrg = workflow.StepBoxes.Any(d => d.BusinessProperty is EBusinessProperty.Department);
  740. return isFromOrg && isToCenter;
  741. }
  742. /// <summary>
  743. /// 复制一个节点为待接办
  744. /// </summary>
  745. private async Task<WorkflowStep> CreatePrevStepAsync(Workflow workflow, WorkflowStep step, CancellationToken cancellationToken)
  746. {
  747. step.Reset();
  748. var newStep = _mapper.Map<WorkflowStep>(step);
  749. newStep.Status = EWorkflowStepStatus.Assigned;
  750. newStep.PreviousId = step.PreviousId;
  751. newStep.IsMain = step.IsMain;
  752. newStep.ParentId = step.ParentId;
  753. newStep.Handlers = step.Handlers;
  754. newStep.StartCountersignId = step.StartCountersignId;
  755. newStep.CountersignId = step.CountersignId;
  756. newStep.IsStartedCountersignComplete = step.IsStartedCountersignComplete;
  757. await _workflowStepRepository.AddAsync(newStep, cancellationToken);
  758. await CreateTraceAsync(workflow, newStep, EWorkflowTraceStatus.Previous, cancellationToken);
  759. return newStep;
  760. }
  761. /// <summary>
  762. /// 检查并重置目标stepbox状态为待接办
  763. /// </summary>
  764. private async Task ResetStepBoxStatusAsync(WorkflowStep stepBox, CancellationToken cancellationToken)
  765. {
  766. if (stepBox.Status is EWorkflowStepStatus.Completed)
  767. {
  768. stepBox.Status = EWorkflowStepStatus.Assigned;
  769. await _workflowStepRepository.UpdateAsync(stepBox, cancellationToken);
  770. }
  771. }
  772. private async Task<WorkflowCountersign> CreateCountersignAsync(
  773. string workflowId, WorkflowStep startStep, string endStepCode, int count,
  774. ECounterSignType counterSignType, string? parentId = null, CancellationToken cancellationToken = default)
  775. {
  776. var countersign = new WorkflowCountersign
  777. {
  778. WorkflowId = workflowId,
  779. StartStepId = startStep.Id,
  780. StartStepCode = startStep.Code,
  781. StartStepBusiProperty = startStep.BusinessProperty,
  782. EndStepCode = endStepCode,
  783. Members = count,
  784. ParentId = parentId,
  785. CounterSignType = counterSignType,
  786. };
  787. await _workflowCountersignRepository.AddAsync(countersign, cancellationToken);
  788. return countersign;
  789. }
  790. /// <summary>
  791. /// 更新下级汇总节点可办理状态
  792. /// </summary>
  793. /// <param name="nextStepBox"></param>
  794. /// <param name="currentStep"></param>
  795. /// <param name="cancellationToken"></param>
  796. /// <returns></returns>
  797. private async Task SetNextCountersignEndAssignedAsync(WorkflowStep nextStepBox, WorkflowStep currentStep, CancellationToken cancellationToken)
  798. {
  799. var nextSteps = currentStep.StepCountersignStatus is EStepCountersignStatus.InCountersign
  800. ? nextStepBox.Steps.Where(d => d.CountersignId == currentStep.CountersignId).ToList()
  801. : nextStepBox.Steps.Where(d => d.PreviousId == currentStep.Id).ToList();
  802. if (!nextSteps.Any())
  803. throw new UserFriendlyException($"未查询到下一节点, currentStepId: {currentStep.Id}");
  804. foreach (var nextStep in nextSteps)
  805. {
  806. nextStep.SetAssigned();
  807. }
  808. await _workflowStepRepository.UpdateRangeAsync(nextSteps, cancellationToken);
  809. }
  810. private async Task JumpTraceAsync(string workflowId, RecallDto dto, CancellationToken cancellationToken)
  811. {
  812. //未办理的traces
  813. var uncompleteTraces =
  814. await _workflowTraceRepository.QueryAsync(d =>
  815. d.WorkflowId == workflowId && string.IsNullOrEmpty(d.UserId));
  816. foreach (var trace in uncompleteTraces)
  817. {
  818. trace.Jump(
  819. _sessionContext.RequiredUserId,
  820. _sessionContext.UserName,
  821. _sessionContext.RequiredOrgCode,
  822. _sessionContext.OrgName,
  823. dto.Opinion);
  824. }
  825. await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken);
  826. }
  827. private async Task RecallTraceAsync(string workflowId, CancellationToken cancellationToken)
  828. {
  829. //未办理的traces
  830. var uncompleteTraces =
  831. await _workflowTraceRepository.QueryAsync(d =>
  832. d.WorkflowId == workflowId && string.IsNullOrEmpty(d.UserId));
  833. if (uncompleteTraces.Any())
  834. {
  835. foreach (var trace in uncompleteTraces)
  836. {
  837. trace.Complete(
  838. _sessionContext.RequiredUserId,
  839. _sessionContext.UserName,
  840. _sessionContext.RequiredOrgCode,
  841. _sessionContext.OrgName);
  842. }
  843. await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken);
  844. }
  845. }
  846. private async Task PreviousTraceAsync(string workflowId, PreviousWorkflowDto dto, WorkflowStep step, CancellationToken cancellationToken)
  847. {
  848. var trace = await GetWorkflowTraceAsync(workflowId, step.Id, cancellationToken);
  849. _mapper.Map(dto, trace);
  850. trace.Complete(
  851. _sessionContext.RequiredUserId,
  852. _sessionContext.UserName,
  853. _sessionContext.RequiredOrgCode,
  854. _sessionContext.OrgName);
  855. await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
  856. }
  857. //private async Task EndTraceAsync(Workflow workflow, BasicWorkflowDto dto, WorkflowStep step, CancellationToken cancellationToken)
  858. //{
  859. // var trace = _mapper.Map<WorkflowTrace>(step);
  860. // trace.Status = EWorkflowTraceStatus.Normal;
  861. // trace.ExpiredTime = workflow.ExpiredTime;
  862. // trace.TimeLimit = workflow.TimeLimit;
  863. // await _workflowTraceRepository.AddAsync(trace, cancellationToken);
  864. //}
  865. private async Task<WorkflowTrace> NextTraceAsync(Workflow workflow, BasicWorkflowDto dto, WorkflowStep step, CancellationToken cancellationToken)
  866. {
  867. var trace = await GetWorkflowTraceAsync(workflow.Id, step.Id, cancellationToken);
  868. _mapper.Map(dto, trace);
  869. _mapper.Map(step, trace);
  870. await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
  871. return trace;
  872. }
  873. private async Task AcceptTraceAsync(Workflow workflow, WorkflowStep step, CancellationToken cancellationToken)
  874. {
  875. var trace = await GetWorkflowTraceAsync(workflow.Id, step.Id, cancellationToken);
  876. _mapper.Map(step, trace);
  877. await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
  878. }
  879. private async Task CreateTraceAsync(Workflow workflow, WorkflowStep currentStep,
  880. EWorkflowTraceStatus traceStatus = EWorkflowTraceStatus.Normal, CancellationToken cancellationToken = default)
  881. {
  882. var trace = _mapper.Map<WorkflowTrace>(currentStep);
  883. trace.Status = traceStatus;
  884. //1.如果是汇总节点,trace.parentId=会签开始节点对应的trace.parentId(即与会签开始节点trace同级)
  885. //2.普通节点:2.1: in 判断上级节点是否发起会签,有则赋值parentId为上级trace.Id, 2.2: outer 与上级节点trace保持同级,取值上级节点对应trace.parentId
  886. if (currentStep.StepType is EStepType.CountersignEnd)
  887. {
  888. if (currentStep.IsInCountersign)
  889. {
  890. var countersign =
  891. await _workflowCountersignRepository.GetAsync(currentStep.CountersignId!, cancellationToken);
  892. if (countersign == null)
  893. throw new UserFriendlyException(
  894. $"汇总节点处于会签中,未查询到对应会签,countersignId: {currentStep.CountersignId}");
  895. var startTrace = await GetWorkflowTraceAsync(workflow.Id, countersign.StartStepId, cancellationToken);
  896. trace.ParentId = startTrace.ParentId;
  897. }
  898. }
  899. else if (currentStep.StepType is EStepType.Normal)
  900. {
  901. if (currentStep.StepCountersignStatus is EStepCountersignStatus.InCountersign)
  902. {
  903. var prevTrace = await GetWorkflowTraceAsync(workflow.Id, currentStep.PreviousId, cancellationToken);
  904. trace.ParentId = prevTrace.Id;
  905. }
  906. else if (currentStep.StepCountersignStatus is EStepCountersignStatus.OuterCountersign)
  907. {
  908. var prevTrace = await GetWorkflowTraceAsync(workflow.Id, currentStep.PreviousId, cancellationToken);
  909. trace.ParentId = prevTrace.ParentId;
  910. }
  911. }
  912. ////处于会签中的节点,其对应的trace.parentId赋值上级trace.Id
  913. //if (currentStep.StepCountersignStatus is EStepCountersignStatus.InCountersign)
  914. //{
  915. // var parentTrace = await GetWorkflowTraceAsync(workflow.Id, currentStep.PreviousId, cancellationToken);
  916. // trace.ParentId = parentTrace.Id;
  917. //}
  918. await _workflowTraceRepository.AddAsync(trace, cancellationToken);
  919. }
  920. private async Task<WorkflowTrace> GetWorkflowTraceAsync(string workflowId, string stepId, CancellationToken cancellationToken)
  921. {
  922. var parentTrace = await _workflowTraceRepository.GetAsync(d =>
  923. d.WorkflowId == workflowId && d.StepId == stepId, cancellationToken);
  924. if (parentTrace == null)
  925. throw new UserFriendlyException($"未找到对应trace, workflowId: {workflowId}, stepId: {stepId}");
  926. return parentTrace;
  927. }
  928. private async Task<bool> RecallAsync(Workflow workflow, BasicWorkflowDto dto, StepDefine targetStepDefine, WorkflowStep targetStepBox, EWorkflowTraceStatus traceStatus, CancellationToken cancellationToken)
  929. {
  930. //get targetStep's previous
  931. var targetPrevStepBox = workflow.StepBoxes.FirstOrDefault(d => d.Id == targetStepBox.PreviousId);
  932. if (targetPrevStepBox == null)
  933. throw new UserFriendlyException($"{nameof(RecallAsync)}, 未找到目标节点的前一节点, flowId: {workflow.Id}, targetStepBoxPrevId: {targetPrevStepBox?.PreviousId}");
  934. //真实的前一节点并不存在(非正常流转造成的),所以取前一stepbox任意一个step替代
  935. var targetPrevStep = targetPrevStepBox.Steps.FirstOrDefault();
  936. if (targetPrevStep == null)
  937. throw new UserFriendlyException($"{nameof(RecallAsync)}, 未找到目标节点的前一节点, flowId: {workflow.Id}");
  938. //查询所有目标节点之后的节点,然后删掉(包括目标节点)
  939. var removeSteps = GetStepsBehindTargetStepBox(workflow.StepBoxes, targetStepBox);
  940. if (removeSteps.Any())
  941. {
  942. await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken);
  943. workflow.StepBoxes.RemoveAll(d => removeSteps.Contains(d));
  944. }
  945. workflow.EndCountersign();
  946. workflow.ResetOption();
  947. //recreate targetStep
  948. var targetStepBoxNew = await CreateStepAsync(false, workflow, targetStepDefine, dto, EWorkflowStepStatus.Assigned,
  949. targetPrevStepBox, targetPrevStep, traceStatus, workflow.ExpiredTime, cancellationToken);
  950. //更新当前办理节点信息
  951. workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, nextStep: targetStepBoxNew.Steps.First());
  952. //calc workflow expired time
  953. var isOrgToCenter = CheckIfFlowFromOrgToCenter(workflow, targetStepBox);
  954. if (isOrgToCenter)
  955. workflow.ExpiredTime = CalculateExpiredTime("");
  956. return isOrgToCenter;
  957. }
  958. private ICollection<WorkflowStep> GetStepsBehindTargetStepBox(List<WorkflowStep> stepBoxes, WorkflowStep targetStepBox)
  959. {
  960. var steps = GetStepsIncludeStepBox(targetStepBox);
  961. var nextStepBoxes = stepBoxes.Where(d => d.PreviousId == targetStepBox.Id).ToList();
  962. if (!nextStepBoxes.Any())
  963. return steps;
  964. foreach (var nextStepBox in nextStepBoxes)
  965. {
  966. steps.AddRange(GetStepsBehindTargetStepBox(stepBoxes, nextStepBox));
  967. }
  968. return steps;
  969. }
  970. private List<WorkflowStep> GetStepsIncludeStepBox(WorkflowStep stepBox)
  971. {
  972. var steps = new List<WorkflowStep> { stepBox };
  973. steps.AddRange(stepBox.Steps);
  974. return steps;
  975. }
  976. private static void CheckWhetherRunnable(EWorkflowStatus status)
  977. {
  978. if (status != EWorkflowStatus.Runnable)
  979. throw UserFriendlyException.SameMessage("当前流程状态不可继续流转");
  980. }
  981. private void ValidatePermission(Workflow workflow)
  982. {
  983. if (!workflow.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgCode))
  984. throw UserFriendlyException.SameMessage("无办理权限");
  985. }
  986. private async Task<(WorkflowStep startStepBox, WorkflowStep startStep, WorkflowStep firstStepBox)> CreateStartAndFirstStepAsync(
  987. Workflow workflow, BasicWorkflowDto dto, CancellationToken cancellationToken)
  988. {
  989. if (workflow.StepBoxes.Any())
  990. throw UserFriendlyException.SameMessage("无法重复创建开始节点");
  991. var startStepDefinition = workflow.Definition.Steps.FirstOrDefault(d => d.StepType == EStepType.Start);
  992. if (startStepDefinition == null)
  993. throw new UserFriendlyException($"模板未配置开始节点, defineCode: {workflow.Definition.Code}", "模板未配置开始节点");
  994. var startStepBox = CreateStepBox(workflow.Id, startStepDefinition, string.Empty);
  995. await _workflowStepRepository.AddAsync(startStepBox, cancellationToken);
  996. //start节点的办理人分类默认为用户,即为当前发起流程的操作员
  997. var handler = new IdName { Id = _sessionContext.RequiredUserId, Name = _sessionContext.UserName };
  998. //开始节点的下一个节点(工单业务:话务员节点)
  999. var firstStepCode = workflow.Definition.FindStartStepDefine().NextSteps.First().Code;
  1000. var startStep = await CreateStartSubStepAsync(handler, firstStepCode, startStepBox, dto, cancellationToken);
  1001. //开始节点trace
  1002. await CreateTraceAsync(workflow, startStep, cancellationToken: cancellationToken);
  1003. //创建firstStep
  1004. var firsStepDefine = workflow.Definition.FindStepDefine(firstStepCode);
  1005. var firstStepBox = await CreateStepAsync(dto.IsStartCountersign, workflow, firsStepDefine, dto, EWorkflowStepStatus.Accepted,
  1006. startStepBox, startStep, EWorkflowTraceStatus.Normal, workflow.ExpiredTime, cancellationToken);
  1007. return (startStepBox, startStep, firstStepBox);
  1008. }
  1009. private async Task<(WorkflowStep stepBox, WorkflowStep step)> CreateEndStepAsync(
  1010. Workflow workflow,
  1011. StepDefine endStepDefine,
  1012. WorkflowStep prevStepBox,
  1013. WorkflowStep prevStep,
  1014. CancellationToken cancellationToken)
  1015. {
  1016. if (workflow.StepBoxes.Any(d => d.StepType == EStepType.End))
  1017. throw UserFriendlyException.SameMessage("无法重复创建结束节点");
  1018. var stepBox = CreateStepBox(workflow.Id, endStepDefine, prevStepBox.Id);
  1019. await _workflowStepRepository.AddAsync(stepBox, cancellationToken);
  1020. var handler = new IdName { Id = _sessionContext.RequiredUserId, Name = _sessionContext.UserName };
  1021. var step = await CreateEndSubStepAsync(handler, stepBox, prevStep, cancellationToken);
  1022. //end trace
  1023. await CreateTraceAsync(workflow, step, cancellationToken: cancellationToken);
  1024. return (stepBox, step);
  1025. }
  1026. /// <summary>
  1027. /// 创建节点(不含开始、结束节点)
  1028. /// </summary>
  1029. private async Task<WorkflowStep> CreateStepAsync(
  1030. bool isPrevStartCountersign,
  1031. Workflow workflow,
  1032. StepDefine stepBoxDefine,
  1033. BasicWorkflowDto dto,
  1034. EWorkflowStepStatus status,
  1035. WorkflowStep prevStepBox,
  1036. WorkflowStep prevStep,
  1037. EWorkflowTraceStatus traceStatus,
  1038. DateTime expiredTime,
  1039. CancellationToken cancellationToken)
  1040. {
  1041. if (stepBoxDefine.StepType is EStepType.Start or EStepType.End)
  1042. throw new UserFriendlyException("该方法不支持创建开始或结束节点");
  1043. var stepBox = workflow.StepBoxes.FirstOrDefault(d => d.Code == stepBoxDefine.Code);
  1044. if (stepBox == null)
  1045. {
  1046. stepBox = CreateStepBox(workflow.Id, stepBoxDefine, prevStepBox.Id);
  1047. await _workflowStepRepository.AddAsync(stepBox, cancellationToken);
  1048. workflow.StepBoxes.Add(stepBox);
  1049. }
  1050. else if (stepBox.Status != EWorkflowStepStatus.Created)
  1051. {
  1052. stepBox.Status = EWorkflowStepStatus.Created;
  1053. await _workflowStepRepository.UpdateAsync(stepBox, cancellationToken);
  1054. }
  1055. //下一节点为汇总节点时,同一会签只需要创建一次汇总节点
  1056. if (stepBoxDefine.StepType is EStepType.CountersignEnd && prevStep.StepCountersignStatus == EStepCountersignStatus.InCountersign)
  1057. {
  1058. var step = stepBox.Steps.FirstOrDefault(d =>
  1059. d.IsInCountersign && d.CountersignId == prevStep.CountersignId);
  1060. if (step != null)
  1061. return stepBox;
  1062. }
  1063. await CreateSubStepsAsync(workflow, isPrevStartCountersign, stepBoxDefine, dto, stepBox, status, prevStep, traceStatus, expiredTime, cancellationToken);
  1064. return stepBox;
  1065. }
  1066. //todo obsolete
  1067. private async Task<WorkflowStep> CreateStartSubStepAsync(
  1068. IdName handler,
  1069. BasicWorkflowDto dto,
  1070. WorkflowStep stepBox,
  1071. CancellationToken cancellationToken)
  1072. {
  1073. //开始节点既不发起会签,也不处于会签中
  1074. var subStep = CreateSubStep(stepBox, new List<IdName> { handler }, dto.NextStepCode, dto.NextMainHandler,
  1075. null, null, EWorkflowStepStatus.Completed, EStepCountersignStatus.None, DateTime.Now,
  1076. _mapper.Map<StepExtension>(dto.Extension));
  1077. subStep.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName,
  1078. _sessionContext.RequiredOrgCode, _sessionContext.OrgName);
  1079. //step办理状态
  1080. subStep.Complete(
  1081. _sessionContext.RequiredUserId, _sessionContext.UserName,
  1082. _sessionContext.RequiredOrgCode, _sessionContext.OrgName,
  1083. _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName,
  1084. dto.NextStepCode);
  1085. _mapper.Map(dto, subStep);
  1086. stepBox.Steps.Add(subStep);
  1087. await _workflowStepRepository.AddAsync(subStep, cancellationToken);
  1088. return subStep;
  1089. }
  1090. private async Task<WorkflowStep> CreateStartSubStepAsync(
  1091. IdName handler,
  1092. string nextStepCode,
  1093. WorkflowStep stepBox,
  1094. BasicWorkflowDto dto,
  1095. CancellationToken cancellationToken)
  1096. {
  1097. //开始节点既不发起会签,也不处于会签中
  1098. var subStep = CreateSubStep(stepBox, new List<IdName> { handler }, nextStepCode, null,
  1099. null, null, EWorkflowStepStatus.Completed, EStepCountersignStatus.None, DateTime.Today,
  1100. _mapper.Map<StepExtension>(dto.Extension));
  1101. subStep.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName,
  1102. _sessionContext.RequiredOrgCode, _sessionContext.OrgName);
  1103. //step办理状态
  1104. subStep.Complete(_sessionContext.RequiredUserId, _sessionContext.UserName,
  1105. _sessionContext.RequiredOrgCode, _sessionContext.OrgName,
  1106. _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName,
  1107. nextStepCode);
  1108. subStep.Opinion = "流程开启";
  1109. stepBox.Steps.Add(subStep);
  1110. await _workflowStepRepository.AddAsync(subStep, cancellationToken);
  1111. return subStep;
  1112. }
  1113. private async Task<WorkflowStep> CreateEndSubStepAsync(
  1114. IdName handler,
  1115. WorkflowStep currentStepBox,
  1116. WorkflowStep prevStep,
  1117. CancellationToken cancellationToken)
  1118. {
  1119. var subStep = CreateSubStep(currentStepBox, new List<IdName> { handler }, null, null, prevStep.Id,
  1120. null, EWorkflowStepStatus.Completed, EStepCountersignStatus.None, DateTime.Today, new());
  1121. subStep.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgCode,
  1122. _sessionContext.OrgName);
  1123. subStep.Complete(_sessionContext.RequiredUserId, _sessionContext.UserName,
  1124. _sessionContext.RequiredOrgCode, _sessionContext.OrgName,
  1125. _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName,
  1126. string.Empty);
  1127. currentStepBox.Steps.Add(subStep);
  1128. await _workflowStepRepository.AddAsync(subStep, cancellationToken);
  1129. return subStep;
  1130. }
  1131. private async Task CreateSubStepsAsync(
  1132. Workflow workflow,
  1133. bool isPrevStartCountersign,
  1134. StepDefine stepBoxDefine,
  1135. BasicWorkflowDto dto,
  1136. WorkflowStep stepBox,
  1137. EWorkflowStepStatus stepStatus,
  1138. WorkflowStep prevStep,
  1139. EWorkflowTraceStatus traceStatus,
  1140. DateTime expiredTime,
  1141. CancellationToken cancellationToken = default)
  1142. {
  1143. var countersignStatus = stepBoxDefine.StepType is EStepType.CountersignEnd
  1144. ? prevStep.IsInCountersign
  1145. ? EStepCountersignStatus.InCountersign
  1146. : EStepCountersignStatus.None
  1147. : prevStep.GetNextStepCountersignStatus();
  1148. var countersignId = isPrevStartCountersign ? prevStep.StartCountersignId : prevStep.CountersignId;
  1149. List<WorkflowStep> subSteps;
  1150. var stepExtension = _mapper.Map<StepExtension>(dto.Extension);
  1151. if (stepBoxDefine.HandlerType is EHandlerType.AssignUser or EHandlerType.AssignOrg)
  1152. {
  1153. subSteps = CreateSubSteps(isPrevStartCountersign, stepBox, stepBox.HandlerClassifies, dto.NextStepCode, dto.NextMainHandler,
  1154. prevStep?.Id, countersignId, stepStatus, countersignStatus, expiredTime, stepExtension);
  1155. }
  1156. else
  1157. {
  1158. if (stepBoxDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any())
  1159. throw new UserFriendlyException("未指定节点处理者");
  1160. subSteps = CreateSubSteps(isPrevStartCountersign, stepBox, dto.NextHandlers, dto.NextStepCode, dto.NextMainHandler,
  1161. prevStep?.Id, countersignId, stepStatus, countersignStatus, expiredTime, stepExtension);
  1162. }
  1163. stepBox.Steps.AddRange(subSteps);
  1164. await _workflowStepRepository.AddRangeAsync(subSteps, cancellationToken);
  1165. //create traces
  1166. foreach (var step in subSteps)
  1167. {
  1168. await CreateTraceAsync(workflow, step, traceStatus, cancellationToken);
  1169. }
  1170. }
  1171. /// <summary>
  1172. /// 查询未完成节点
  1173. /// </summary>
  1174. /// <param name="stepBoxes"></param>
  1175. /// <param name="orgCode"></param>
  1176. /// <param name="userId"></param>
  1177. /// <returns></returns>
  1178. private (WorkflowStep, WorkflowStep) GetUnCompleteStep(List<WorkflowStep> stepBoxes, string orgCode, string userId)
  1179. {
  1180. var (stepBox, step) = GetStep(stepBoxes, orgCode, userId, d => d != EWorkflowStepStatus.Completed);
  1181. if (step == null)
  1182. throw new UserFriendlyException(
  1183. $"未找到对应节点, workflowId: {stepBoxes.FirstOrDefault()?.WorkflowId} orgCode:{orgCode}, userId: {userId}",
  1184. "未找到对应节点");
  1185. return (stepBox, step);
  1186. }
  1187. private (WorkflowStep, WorkflowStep) GetUnCompleteStepOrDefault(List<WorkflowStep> stepBoxes, string orgCode, string userId) =>
  1188. GetStep(stepBoxes, orgCode, userId, d => d != EWorkflowStepStatus.Completed);
  1189. private (WorkflowStep, WorkflowStep) GetStep(List<WorkflowStep> stepBoxes, string orgCode, string userId, Func<EWorkflowStepStatus, bool> predicate)
  1190. {
  1191. if (!stepBoxes.Any()) throw new UserFriendlyException("该流程中暂无节点");
  1192. foreach (var stepBox in stepBoxes)
  1193. {
  1194. foreach (var step in stepBox.Steps)
  1195. {
  1196. if (predicate(step.Status) && (step.Handlers.Any(d => d.Id == orgCode) || step.Handlers.Any(d => d.Id == userId)))
  1197. return (stepBox, step);
  1198. }
  1199. }
  1200. return new();
  1201. }
  1202. private WorkflowStep CreateStepBox(string workflowId, StepDefine stepDefine, string prevStepBoxId)
  1203. {
  1204. var stepBox = _mapper.Map<WorkflowStep>(stepDefine);
  1205. stepBox.WorkflowId = workflowId;
  1206. stepBox.PreviousId = prevStepBoxId;
  1207. stepBox.NextStepCode = string.Empty;
  1208. stepBox.Opinion = string.Empty;
  1209. stepBox.CountersignStartStepCode = stepDefine.CountersignStartStepCode;
  1210. stepBox.CountersignEndStepCode = stepDefine.CountersignEndStepCode;
  1211. return stepBox;
  1212. }
  1213. private List<WorkflowStep> CreateSubSteps(
  1214. bool isPrevStartCountersign,
  1215. WorkflowStep stepBox,
  1216. List<IdName> handlers,
  1217. string nextStepCode,
  1218. string? nextMainHandler,
  1219. string? prevStepId,
  1220. string? countersignId,
  1221. EWorkflowStepStatus stepStatus,
  1222. EStepCountersignStatus countersignStatus,
  1223. DateTime expiredTime,
  1224. StepExtension extension)
  1225. {
  1226. if (countersignStatus is EStepCountersignStatus.None && !string.IsNullOrEmpty(countersignId))
  1227. throw UserFriendlyException.SameMessage("非法参数");
  1228. if (countersignStatus is not EStepCountersignStatus.None && string.IsNullOrEmpty(countersignId))
  1229. throw UserFriendlyException.SameMessage("非法参数");
  1230. //依据是否发起会签创建step,发起会签表示一个handler创建一个step,未发起会签表示多人处理同一个节点,只创建一个step
  1231. var steps = new List<WorkflowStep>();
  1232. if (isPrevStartCountersign)
  1233. {
  1234. foreach (var handler in handlers)
  1235. {
  1236. var step = CreateSubStep(stepBox, new List<IdName> { handler }, nextStepCode, nextMainHandler,
  1237. prevStepId, countersignId, stepStatus, countersignStatus, expiredTime, extension);
  1238. steps.Add(step);
  1239. }
  1240. }
  1241. else
  1242. {
  1243. var step = CreateSubStep(stepBox, handlers, nextStepCode, nextMainHandler,
  1244. prevStepId, countersignId, stepStatus, countersignStatus, expiredTime, extension);
  1245. steps.Add(step);
  1246. }
  1247. return steps;
  1248. }
  1249. private WorkflowStep CreateSubStep(
  1250. WorkflowStep stepBox,
  1251. List<IdName> handlers,
  1252. string nextStepCode,
  1253. string? nextMainHandler,
  1254. string? prevStepId,
  1255. string? countersignId,
  1256. EWorkflowStepStatus stepStatus,
  1257. EStepCountersignStatus countersignStatus,
  1258. DateTime expiredTime,
  1259. StepExtension extension)
  1260. {
  1261. if (!handlers.Any())
  1262. throw new UserFriendlyException("非法参数");
  1263. var step = _mapper.Map<WorkflowStep>(stepBox);
  1264. var handlerIds = handlers.Select(d => d.Id).ToList();
  1265. var isMain = handlers.Count == 1 || (handlers.Count > 1 || handlerIds.First() == nextMainHandler);
  1266. step.ParentId = stepBox.Id;
  1267. step.Handlers = handlers;
  1268. step.NextStepCode = step.StepType is EStepType.End ? string.Empty : nextStepCode;
  1269. step.IsMain = isMain;
  1270. step.PreviousId = prevStepId;
  1271. step.CountersignId = countersignId;
  1272. step.Status = stepStatus;
  1273. step.StepCountersignStatus = countersignStatus;
  1274. step.ExpiredTime = expiredTime;
  1275. step.TimeLimit = GetTimeLimit("");//todo 过期时间
  1276. step.Extension = extension;
  1277. return step;
  1278. }
  1279. /// <summary>
  1280. /// 依据配置生成过期时间
  1281. /// </summary>
  1282. /// <returns></returns>
  1283. private DateTime CalculateExpiredTime(string defineCode, DateTime? time = null)
  1284. {
  1285. time ??= DateTime.Now;
  1286. var config = GetConfig(defineCode);
  1287. return time.Value.AddDays(config.Days);
  1288. }
  1289. private string GetTimeLimit(string defineCode)
  1290. {
  1291. return GetConfig(defineCode).Description;
  1292. }
  1293. private ConfigIncludeDescriptionAndTime GetConfig(string defineCode)
  1294. {
  1295. return new ConfigIncludeDescriptionAndTime
  1296. {
  1297. Days = 7,
  1298. Description = "7个工作日"//todo 依据配置生成, Think about 工作日
  1299. };
  1300. }
  1301. #endregion
  1302. }
  1303. public class ConfigIncludeDescriptionAndTime
  1304. {
  1305. public int Days { get; set; }
  1306. public string Description { get; set; }
  1307. }
  1308. }