WorkflowDomainService.cs 66 KB

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