WorkflowDomainService.cs 130 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828
  1. using Dm;
  2. using Hotline.File;
  3. using Hotline.FlowEngine.Definitions;
  4. using Hotline.FlowEngine.Notifications;
  5. using Hotline.FlowEngine.WorkflowModules;
  6. using Hotline.SeedData;
  7. using Hotline.Settings;
  8. using Hotline.Share.Dtos;
  9. using Hotline.Share.Dtos.FlowEngine;
  10. using Hotline.Share.Dtos.FlowEngine.Definition;
  11. using Hotline.Share.Enums.FlowEngine;
  12. using Hotline.Users;
  13. using MapsterMapper;
  14. using MediatR;
  15. using Microsoft.Extensions.Logging;
  16. using SqlSugar;
  17. using System.Diagnostics;
  18. using Hotline.EventBus;
  19. using XF.Domain.Authentications;
  20. using XF.Domain.Dependency;
  21. using XF.Domain.Entities;
  22. using XF.Domain.Exceptions;
  23. using XF.Domain.Repository;
  24. using System.Reflection.Metadata;
  25. namespace Hotline.FlowEngine.Workflows
  26. {
  27. public class WorkflowDomainService : IWorkflowDomainService, IScopeDependency
  28. {
  29. private readonly IWorkflowRepository _workflowRepository;
  30. private readonly IRepository<WorkflowStep> _workflowStepRepository;
  31. private readonly IRepository<WorkflowTrace> _workflowTraceRepository;
  32. private readonly IRepository<WorkflowSupplement> _workflowSupplementRepository;
  33. private readonly IRepository<WorkflowCountersign> _workflowCountersignRepository;
  34. //private readonly IRepository<WorkflowStepHandler> _workflowStepHandlerRepository;
  35. private readonly ISessionContext _sessionContext;
  36. private readonly IMapper _mapper;
  37. private readonly Publisher _publisher;
  38. private readonly ILogger<WorkflowDomainService> _logger;
  39. private readonly IFileRepository _fileRepository;
  40. private readonly IRepository<User> _userRepository;
  41. public WorkflowDomainService(
  42. IWorkflowRepository workflowRepository,
  43. IRepository<WorkflowStep> workflowStepRepository,
  44. IRepository<WorkflowTrace> workflowTraceRepository,
  45. IRepository<WorkflowSupplement> workflowSupplementRepository,
  46. IRepository<WorkflowCountersign> workflowCountersignRepository,
  47. //IRepository<WorkflowStepHandler> workflowStepHandlerRepository,
  48. ISessionContext sessionContext,
  49. IMapper mapper,
  50. Publisher publisher,
  51. ILogger<WorkflowDomainService> logger,
  52. IFileRepository fileRepository)
  53. {
  54. _workflowRepository = workflowRepository;
  55. _workflowStepRepository = workflowStepRepository;
  56. _workflowTraceRepository = workflowTraceRepository;
  57. _workflowSupplementRepository = workflowSupplementRepository;
  58. _workflowCountersignRepository = workflowCountersignRepository;
  59. //_workflowStepHandlerRepository = workflowStepHandlerRepository;
  60. _sessionContext = sessionContext;
  61. _mapper = mapper;
  62. _publisher = publisher;
  63. _logger = logger;
  64. _fileRepository = fileRepository;
  65. }
  66. public async Task<Workflow> CreateWorkflowAsync(WorkflowModule wfModule, string title, string userId,
  67. string orgId, string? externalId = null, CancellationToken cancellationToken = default)
  68. {
  69. var definition = wfModule.Definition;
  70. if (definition is null)
  71. throw new UserFriendlyException("无效流程模板");
  72. var workflow = new Workflow
  73. {
  74. Title = title,
  75. ModuleId = wfModule.Id,
  76. ModuleName = wfModule.Name,
  77. ModuleCode = wfModule.Code,
  78. DefinitionId = definition.Id,
  79. Status = EWorkflowStatus.Runnable,
  80. Steps = new(),
  81. Traces = new(),
  82. WorkflowDefinition = definition,
  83. ExternalId = externalId ?? string.Empty,
  84. //FlowedOrgIds = new List<string> { orgId },
  85. FlowedUserIds = new List<string> { userId },
  86. FlowType = definition.FlowType,
  87. };
  88. await _workflowRepository.AddAsync(workflow, cancellationToken);
  89. return workflow;
  90. }
  91. /// <summary>
  92. /// 流程开始
  93. /// </summary>
  94. public async Task StartAsync(Workflow workflow, WorkflowStep startStep, BasicWorkflowDto dto,
  95. StepDefine firstStepDefine, bool isNextDynamic, FlowAssignInfo flowAssignInfo,
  96. ECounterSignType? counterSignType, DateTime? expiredTime,
  97. ISessionContext current, CancellationToken cancellationToken)
  98. {
  99. if (firstStepDefine.StepType is EStepType.End)
  100. {
  101. await _publisher.PublishAsync(
  102. new StartWorkflowNotify(workflow, dto, flowAssignInfo, startStep.WorkflowTrace),
  103. PublishStrategy.ParallelWhenAll, cancellationToken);
  104. //firstStep是否为end,t: 实际办理节点为startStep, 并且handlerId赋值 f: 实际办理节点为firstStep, handlerId未赋值
  105. workflow.UpdateActualStepWhenHandle(startStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel);
  106. workflow.UpdateCurrentStepWhenHandle(startStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel);
  107. var endTrace = await EndAsync(workflow, dto, firstStepDefine, startStep, current, expiredTime, cancellationToken);
  108. return;
  109. }
  110. //firststeps
  111. var firstSteps = await CreateNextStepsAsync(workflow, startStep, dto, firstStepDefine,
  112. isNextDynamic, flowAssignInfo, expiredTime, dto.IsStartCountersign, cancellationToken);
  113. //if (firstSteps.Any())
  114. // workflow.Steps.AddRange(firstSteps);
  115. //var counterSignType = GetCounterSignType(startStep.BusinessType);
  116. ////办理开始节点
  117. //await HandleStepAsync(startStep, workflow, dto, flowAssignInfo.FlowAssignType, counterSignType,
  118. // cancellationToken);
  119. ////赋值当前节点的下级办理节点
  120. //if (dto.IsStartCountersign)
  121. // startStep.CreateCountersignSteps(firstSteps);
  122. await _workflowStepRepository.UpdateAsync(startStep, cancellationToken);
  123. //handle trace
  124. var trace = await NextTraceAsync(workflow, dto, startStep, cancellationToken);
  125. //todo 计算办理工作时长
  126. ////更新当前办理节点信息
  127. //workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign,
  128. // current.RequiredUserId, current.UserName,
  129. // current.RequiredOrgId, current.OrgName,
  130. // current.OrgAreaCode, current.OrgAreaName,
  131. // startStep, firstSteps.First());
  132. //指派实际办理节点
  133. UpdateActualStep(workflow, dto, firstStepDefine, firstSteps);
  134. //更新实际办理节点
  135. UpdateCurrentStep(workflow, dto, firstStepDefine, firstSteps);
  136. //发起会签时记录顶层会签节点(必须在update currentStep之后)
  137. if (dto.IsStartCountersign && !workflow.IsInCountersign)
  138. workflow.StartCountersign(startStep.Id, counterSignType);
  139. ////更新实际办理节点信息
  140. //workflow.UpdateWorkflowActualHandleInfo(startStep,
  141. // current.RequiredUserId, current.UserName,
  142. // current.RequiredOrgId, current.OrgName,
  143. // current.OrgAreaCode, current.OrgAreaName,
  144. // current.OrgLevel);
  145. workflow.UpdateHandlers(current.RequiredUserId, current.RequiredOrgId,
  146. flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects, true);
  147. //更新指派信息
  148. workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds());
  149. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  150. //publish
  151. await _publisher.PublishAsync(new StartWorkflowNotify(workflow, dto, flowAssignInfo, trace),
  152. PublishStrategy.ParallelWhenAll, cancellationToken);
  153. }
  154. public async Task<Workflow> GetWorkflowAsync(string workflowId,
  155. bool withDefine = false, bool withSteps = false,
  156. bool withTraces = false, bool withTracesTree = false,
  157. bool withSupplements = false, bool withCountersigns = false,
  158. CancellationToken cancellationToken = default)
  159. {
  160. if (withTraces && withTracesTree)
  161. throw new UserFriendlyException("traces只能在集合与树形集合结构中二选一");
  162. var query = _workflowRepository.Queryable().Where(d => d.Id == workflowId);
  163. if (withDefine)
  164. query = query.Includes(d => d.WorkflowDefinition);
  165. if (withSupplements)
  166. query = query.Includes(d => d.Supplements);
  167. //if (withAssigns)
  168. // query = query.Includes(d => d.Assigns);
  169. if (withCountersigns)
  170. query = query.Includes(d => d.Countersigns, x => x.Members);
  171. if (withSteps)
  172. query = query.Includes(d => d.Steps);
  173. //if (withTraces)
  174. // query = query.Includes(d => d.Traces);
  175. var workflow = await query.FirstAsync(cancellationToken);
  176. if (workflow is null)
  177. throw new UserFriendlyException("无效workflowId");
  178. //if (withSteps)
  179. //{
  180. // var steps = await _workflowStepRepository.Queryable()
  181. // .Where(d => d.WorkflowId == workflow.Id)
  182. // .OrderBy(d => d.CreationTime)
  183. // .ToTreeAsync(d => d.Steps, d => d.ParentId, null);
  184. // workflow.Steps = steps;
  185. //}
  186. if (withTracesTree)
  187. {
  188. workflow.Traces = await _workflowTraceRepository.Queryable()
  189. .Where(d => d.WorkflowId == workflow.Id)
  190. .OrderBy(d => d.CreationTime)
  191. .ToTreeAsync(d => d.Traces, d => d.ParentId, null);
  192. }
  193. if (withTraces)
  194. {
  195. workflow.Traces = await _workflowTraceRepository.Queryable()
  196. .Where(d => d.WorkflowId == workflow.Id)
  197. .OrderBy(d => d.CreationTime)
  198. .ToListAsync(cancellationToken);
  199. }
  200. return workflow;
  201. }
  202. /// <summary>
  203. /// 查询工作流包含当前用户结束会签权限(是否可结束)
  204. /// </summary>
  205. public async Task<(Workflow Workflow, string? CountersignId, bool CanHandle, bool CanPrevious, WorkflowTrace?
  206. Trace)>
  207. GetWorkflowHandlePermissionAsync(
  208. string workflowId, string userId, string orgId, string[] roleIds,
  209. CancellationToken cancellationToken = default)
  210. {
  211. var workflow = await GetWorkflowAsync(workflowId,
  212. withSteps: true, withTraces: true, withCountersigns: true,
  213. cancellationToken: cancellationToken);
  214. var canHandle = workflow.IsCanHandle(userId, orgId, roleIds);
  215. var canPrevious = false;
  216. if (canHandle)
  217. {
  218. var currentStep = FindCurrentStepWaitForHandle(workflow, userId, orgId, roleIds);
  219. if (currentStep.Status is not EWorkflowStepStatus.Handled)
  220. {
  221. canPrevious = !(currentStep.IsInCountersign() &&
  222. !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId));
  223. }
  224. }
  225. var unhandlePreviousTrace = workflow.Traces.FirstOrDefault(d =>
  226. d.Status is not EWorkflowStepStatus.Handled
  227. //&& d.TraceType is EWorkflowTraceType.Previous
  228. );
  229. //var previousOpinion = unhandlePreviousTrace?.Opinion ?? null;
  230. var unCompletedCountersign = workflow.Countersigns
  231. .FirstOrDefault(d => !d.IsCompleted() && d.StarterOrgId == orgId);
  232. if (unCompletedCountersign is null)
  233. return (workflow, null, canHandle, canPrevious, unhandlePreviousTrace);
  234. //var existCountersignEndStep = workflow.Steps.Exists(d =>
  235. // d.IsCountersignEndStep && d.CountersignStartStepId == unCompletedCountersign.StartStepId);
  236. //return (workflow, existCountersignEndStep ? null : unCompletedCountersign.Id, canPrevious);
  237. return (workflow, unCompletedCountersign.Id, canHandle, canPrevious, unhandlePreviousTrace);
  238. }
  239. /// <summary>
  240. /// 受理(接办)
  241. /// </summary>
  242. public async Task AcceptAsync(Workflow workflow,
  243. string userId, string? userName,
  244. string orgId, string? orgName,
  245. string? orgAreaCode, string? orgAreaName,
  246. CancellationToken cancellationToken)
  247. {
  248. if (!workflow.IsCanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId,
  249. _sessionContext.Roles)) return;
  250. //工单完成以后查看的场景
  251. if (workflow.Status != EWorkflowStatus.Runnable) return;
  252. var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId,
  253. _sessionContext.RequiredUserId, _sessionContext.Roles);
  254. if (currentStep.Status is not EWorkflowStepStatus.WaitForAccept) return;
  255. if (currentStep.Handlers.All(d => d.Key != orgId && d.Key != userId)) return;
  256. if (currentStep.StepType is EStepType.End)
  257. throw new UserFriendlyException("当前流程已流转到最终步骤");
  258. currentStep.Accept(userId, userName,
  259. orgId, orgName,
  260. orgAreaCode, orgAreaName);
  261. var trace = workflow.Traces.First(d => d.Id == currentStep.Id);
  262. _mapper.Map(currentStep, trace);
  263. currentStep.WorkflowTrace = trace;
  264. //await _workflowStepRepository.UpdateAsync(currentStep, cancellationToken);
  265. await _workflowStepRepository.UpdateNav(currentStep)
  266. .Include(d => d.Workflow)
  267. .Include(d => d.WorkflowTrace)
  268. .ExecuteCommandAsync();
  269. //await AcceptTraceAsync(workflow, currentStep, cancellationToken);
  270. //await _mediator.Publish(new AcceptWorkflowNotify(workflow), cancellationToken);
  271. }
  272. /// <summary>
  273. /// 办理(流转至下一节点)
  274. /// </summary>
  275. public async Task NextAsync(Workflow workflow, WorkflowStep currentStep, NextWorkflowDto dto,
  276. StepDefine nextStepDefine, bool isNextDynamic, FlowAssignInfo flowAssignInfo,
  277. DateTime? expiredTime, ISessionContext current, CancellationToken cancellationToken)
  278. {
  279. //ValidatePermission(workflow, current.RequiredOrgId, current.RequiredUserId, current.Roles);
  280. CheckWhetherRunnable(workflow.Status);
  281. #region 办理当前节点
  282. if (dto.Files != null && dto.Files.Any())
  283. currentStep.FileJson = await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId,
  284. currentStep.Id, cancellationToken);
  285. //(currentStep.IsInCountersign() && !dto.BackToCountersignEnd) || dto.IsStartCountersign;
  286. var isStartCountersign = currentStep.CountersignPosition switch
  287. {
  288. ECountersignPosition.None => dto.IsStartCountersign,
  289. ECountersignPosition.Multi => !dto.BackToCountersignEnd,
  290. ECountersignPosition.Single => !dto.BackToCountersignEnd,
  291. ECountersignPosition.End => dto.IsStartCountersign,
  292. _ => throw new ArgumentOutOfRangeException()
  293. };
  294. var counterSignType = GetCounterSignType(dto.IsStartCountersign);
  295. var updateSteps = new List<WorkflowStep> { currentStep };
  296. //结束当前会签流程
  297. if (currentStep.IsCountersignEndStep)
  298. {
  299. var countersignStartStep =
  300. workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
  301. if (countersignStartStep is null)
  302. throw new UserFriendlyException(
  303. $"未查询到会签开始step, workflowId: {workflow.Id}, currentStepId: {currentStep.Id}",
  304. "未查询到会签开始节点");
  305. if (countersignStartStep.IsStartCountersign)
  306. {
  307. var currentCountersign =
  308. workflow.Countersigns.FirstOrDefault(d => d.Id == countersignStartStep.StartCountersignId);
  309. if (currentCountersign is null)
  310. throw new UserFriendlyException(
  311. $"未查询到对应会签信息,workflowId:{workflow.Id}, countersignId:{currentStep.CountersignId}",
  312. "无效会签编号");
  313. //结束step会签信息
  314. countersignStartStep.CountersignEnd();
  315. updateSteps.Add(countersignStartStep);
  316. //结束会签
  317. currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType,
  318. current.RequiredUserId, current.UserName,
  319. current.RequiredOrgId, current.OrgName,
  320. current.OrgAreaCode, current.OrgAreaName);
  321. await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken);
  322. }
  323. }
  324. await HandleStepAsync(current, currentStep, workflow, dto, flowAssignInfo.FlowAssignType,
  325. counterSignType, expiredTime, cancellationToken);
  326. //创建会签数据
  327. if (isStartCountersign)
  328. {
  329. var exists = workflow.Countersigns.Any(d =>
  330. !d.IsCompleted() && d.StarterId == current.RequiredUserId);
  331. if (exists)
  332. throw new UserFriendlyException("该用户在当前流程存在未结束会签");
  333. await StartCountersignAsync(current, workflow, currentStep, dto, flowAssignInfo.FlowAssignType,
  334. counterSignType, expiredTime, cancellationToken);
  335. }
  336. currentStep.IsActualHandled = CheckIsActualHandle(workflow, currentStep, nextStepDefine, dto);
  337. _mapper.Map(dto, workflow);
  338. ////操作为回到会签汇总时,更新开始会签节点的会签办理状态
  339. //if (currentStep.IsInCountersign() && dto.BackToCountersignEnd)
  340. //{
  341. // if (currentStep.IsCountersignEndStep)
  342. // {
  343. // if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
  344. // {
  345. // //汇总节点(非顶级)
  346. // var csStartStep =
  347. // workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
  348. // if (csStartStep is null)
  349. // throw new UserFriendlyException("未查询到会签开始节点");
  350. // PrevStepCsHandled(workflow, csStartStep, ref updateSteps);
  351. // }
  352. // }
  353. // else if (currentStep.CountersignPosition is ECountersignPosition.Inner)
  354. // {
  355. // PrevStepCsHandled(workflow, currentStep, ref updateSteps);
  356. // }
  357. //}
  358. //会签办理节点办理时更新会签members字段
  359. if (currentStep.CountersignPosition is ECountersignPosition.Multi or ECountersignPosition.Single)
  360. {
  361. if (!string.IsNullOrEmpty(currentStep.CountersignId))
  362. {
  363. //会签中正常办理节点,更新会签members办理状态
  364. var countersign =
  365. workflow.Countersigns.FirstOrDefault(d =>
  366. !d.IsCompleted() && d.Id == currentStep.CountersignId);
  367. if (countersign is not null)
  368. {
  369. //throw new UserFriendlyException(
  370. // $"会签数据异常, workflowId: {currentStep.WorkflowId}, countersignId: {currentStep.CountersignId}",
  371. // "会签数据异常");
  372. countersign.MemberHandled(current.RequiredUserId, current.RequiredOrgId);
  373. //update cs
  374. await _workflowCountersignRepository.UpdateNav(countersign)
  375. .Include(d => d.Members)
  376. .ExecuteCommandAsync();
  377. }
  378. }
  379. }
  380. await _workflowStepRepository.UpdateRangeAsync(updateSteps, cancellationToken);
  381. //await _workflowStepRepository.UpdateNav(updateSteps)
  382. // .Include(d => d.StepHandlers)
  383. // .ExecuteCommandAsync();
  384. //更新traces
  385. var updateTraces = new List<WorkflowTrace>();
  386. foreach (var updateStep in updateSteps)
  387. {
  388. var updateTrace = workflow.Traces.First(d => d.Id == updateStep.Id);
  389. _mapper.Map(updateStep, updateTrace);
  390. updateTraces.Add(updateTrace);
  391. }
  392. await _workflowTraceRepository.UpdateRangeAsync(updateTraces, cancellationToken);
  393. //var trace = await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
  394. #endregion
  395. #region 处理流程
  396. //检查会签是否结束,并更新当前会签节点字段
  397. var isCountersignOver = false;
  398. if (workflow.IsInCountersign && currentStep.IsCountersignEndStep)
  399. {
  400. isCountersignOver = workflow.CheckIfCountersignOver();
  401. if (isCountersignOver)
  402. workflow.EndCountersign();
  403. }
  404. if (workflow.ActualHandleStepId == currentStep.Id)
  405. {
  406. //更新实际办理节点信息
  407. workflow.UpdateActualStepWhenHandle(currentStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel);
  408. }
  409. if (workflow.CurrentStepId == currentStep.Id)
  410. {
  411. workflow.UpdateCurrentStepWhenHandle(currentStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel);
  412. }
  413. //检查是否流转到流程终点
  414. if (nextStepDefine.StepType is EStepType.End)
  415. {
  416. var endTrace = await EndAsync(workflow, dto, nextStepDefine, currentStep, current, expiredTime,
  417. cancellationToken);
  418. return;
  419. }
  420. //创建下一/N个节点(会签汇总节点:会签未全部办理时不创建,最后一个会签办理节点创建会签汇总节点)
  421. var nextSteps = await CreateNextStepsAsync(workflow, currentStep, dto,
  422. nextStepDefine, isNextDynamic, flowAssignInfo, expiredTime, isStartCountersign,
  423. cancellationToken);
  424. ////赋值当前节点的下级办理节点
  425. //if (dto.IsStartCountersign
  426. // //|| (currentStep.IsInCountersign() &&
  427. // // !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
  428. // )
  429. //{
  430. // currentStep.CreateCountersignSteps(nextSteps);
  431. // await _workflowStepRepository.UpdateAsync(currentStep, cancellationToken);
  432. //}
  433. //更新办理对象(nextSteps无元素表示当前节点为会签办理节点且当前会签没有全部办理完成)
  434. workflow.UpdateHandlers(current.RequiredUserId, current.RequiredOrgId,
  435. flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects, nextSteps.Any());
  436. //todo 计算办理工作时长
  437. //指派实际办理节点
  438. UpdateActualStep(workflow, dto, nextStepDefine, nextSteps);
  439. //更新实际办理节点
  440. UpdateCurrentStep(workflow, dto, nextStepDefine, nextSteps);
  441. //发起会签时记录顶层会签节点
  442. if (dto.IsStartCountersign && !workflow.IsInCountersign)
  443. workflow.StartCountersign(currentStep.Id, counterSignType);
  444. //更新指派信息
  445. workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds());
  446. //更新会签实际办理对象信息
  447. if (currentStep.IsActualHandled)
  448. workflow.AddCsActualHandler(current.RequiredUserId, current.RequiredOrgId);
  449. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  450. #endregion
  451. #region 流转记录
  452. //var trace = await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
  453. #endregion
  454. var currentTrace = workflow.Traces.First(d => d.Id == currentStep.Id);
  455. await _publisher.PublishAsync(
  456. new NextStepNotify(workflow, dto, flowAssignInfo, currentTrace, nextStepDefine,
  457. current.RequiredOrgId, expiredTime.HasValue), PublishStrategy.ParallelWhenAll,
  458. cancellationToken);
  459. }
  460. /// <summary>
  461. /// 退回(返回前一节点)
  462. /// </summary>
  463. /// <returns></returns>
  464. public async Task<EFlowDirection> PreviousAsync(Workflow workflow, PreviousWorkflowDto dto,
  465. string applicantId, string applicantName,
  466. string applicantOrgId, string applicantOrgName,
  467. string applicantOrgAreaCode, string applicantOrgAreaName,
  468. bool applicantIsCenter,
  469. string[] applicantRoleIds, CancellationToken cancellationToken)
  470. {
  471. //ValidatePermission(workflow, operater.OrgId, operater.Id);
  472. var (currentStep, prevStep, countersignStartStep) =
  473. GetPreviousStep(workflow, applicantId, applicantOrgId, applicantRoleIds);
  474. //保存附件
  475. if (dto.Files.Any())
  476. currentStep.FileJson = await _fileRepository.AddFileAsync(
  477. dto.Files, workflow.ExternalId, currentStep.Id, cancellationToken);
  478. // add prev current to remove list
  479. var removeSteps = new List<WorkflowStep> { currentStep, prevStep };
  480. if (countersignStartStep is not null)
  481. {
  482. //add cs steps to remove list
  483. SearchCountersignSteps(countersignStartStep, workflow.Steps, ref removeSteps);
  484. //end cs
  485. var currentCountersign =
  486. workflow.Countersigns.FirstOrDefault(d => d.Id == countersignStartStep.StartCountersignId);
  487. if (currentCountersign is null)
  488. throw new UserFriendlyException(
  489. $"未查询到对应会签信息,workflowId:{workflow.Id}, countersignId:{currentStep.CountersignId}",
  490. "无效会签编号");
  491. //结束step会签信息
  492. countersignStartStep.CountersignEnd();
  493. await _workflowStepRepository.UpdateAsync(countersignStartStep, cancellationToken);
  494. //updateSteps.Add(countersignStartStep);
  495. //结束会签
  496. //currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType,
  497. // current.RequiredUserId, current.UserName,
  498. // current.RequiredOrgId, current.OrgName,
  499. // current.OrgAreaCode, current.OrgAreaName);
  500. currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType,
  501. applicantId, applicantName,
  502. applicantOrgId, applicantOrgName,
  503. applicantOrgAreaCode, applicantOrgAreaName);
  504. await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken);
  505. //update workflow cs status
  506. if (workflow.CheckIfCountersignOver())
  507. workflow.EndCountersign();
  508. }
  509. //update trace
  510. //var trace = await PreviousTraceAsync(workflow.Id, dto, currentStep,
  511. // applicantId, applicantName,
  512. // applicantOrgId, applicantOrgName,
  513. // applicantOrgAreaCode, applicantOrgAreaName,
  514. // applicantIsCenter, cancellationToken);
  515. var trace = workflow.Traces.First(t => t.StepId == currentStep.Id);
  516. _mapper.Map(dto, trace);
  517. trace.FileJson = currentStep.FileJson;
  518. //HandleTrace(trace, dto.Opinion, current);
  519. trace.Handle(applicantId, applicantName,
  520. applicantOrgId, applicantOrgName,
  521. applicantOrgAreaCode, applicantOrgAreaName,
  522. applicantIsCenter, dto.Opinion);
  523. //await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
  524. //如果有传入期满时间 新节点为传入的期满时间
  525. if (dto.ExpiredTime.HasValue)
  526. prevStep.StepExpiredTime = dto.ExpiredTime;
  527. //复制上一个节点为待接办
  528. var newPrevStep =
  529. await DuplicateStepWithTraceAsync(workflow, prevStep, EWorkflowTraceType.Previous, cancellationToken);
  530. //退给派单组节点,需按照平均分配原则派给一个派单员 禅道299 TODO
  531. if (dto.Handler != null)//todo 改为按策略判断
  532. {
  533. var handle = dto.Handler;
  534. newPrevStep.Assign(handle.UserId, handle.Username, handle.OrgId, handle.OrgName, handle.RoleId, handle.RoleName);
  535. }
  536. //remove workflow.steps
  537. await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken);
  538. //await _workflowStepRepository.RemoveNav(removeSteps)
  539. // .Include(d => d.StepHandlers)
  540. // .ExecuteCommandAsync();
  541. var stepIds = removeSteps.Select(d => d.Id).ToList();
  542. var updateTraces = workflow.Traces.Where(d => stepIds.Contains(d.StepId)).ToList();
  543. await UpdateTracesStateAsync(updateTraces, EWorkflowTraceState.StepRemoveByPrevious, cancellationToken);
  544. if (workflow.Status is EWorkflowStatus.Completed)
  545. workflow.SetStatusRunnable();
  546. //更新实际办理节点信息
  547. workflow.UpdateActualStepWhenAssign(newPrevStep, new FlowStepHandler
  548. {
  549. UserId = prevStep.HandlerId,
  550. Username = prevStep.HandlerName,
  551. OrgId = prevStep.HandlerOrgId,
  552. OrgName = prevStep.HandlerOrgName,
  553. });
  554. workflow.UpdateCurrentStepWhenAssign(newPrevStep, new FlowStepHandler
  555. {
  556. UserId = prevStep.HandlerId,
  557. Username = prevStep.HandlerName,
  558. OrgId = prevStep.HandlerOrgId,
  559. OrgName = prevStep.HandlerOrgName,
  560. });
  561. //更新流程可办理对象
  562. workflow.UpdatePreviousHandlers(applicantId, applicantOrgId, prevStep);
  563. //orgToCenter会触发重新计算期满时间,1.无需审核按当前时间进行计算 2.需审核按审核通过时间计算
  564. var isOrgToCenter = prevStep.BusinessType is EBusinessType.Send && prevStep.IsOrigin;
  565. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  566. await _publisher.PublishAsync(new PreviousNotify(workflow, newPrevStep, dto, isOrgToCenter),
  567. PublishStrategy.ParallelWhenAll, cancellationToken);
  568. return GetFlowDirection(currentStep.BusinessType, prevStep.BusinessType);
  569. }
  570. private async Task UpdateTracesStateAsync(List<WorkflowTrace> traces, EWorkflowTraceState traceState,
  571. CancellationToken cancellationToken)
  572. {
  573. foreach (var trace in traces)
  574. {
  575. trace.TraceState = traceState;
  576. }
  577. await _workflowTraceRepository.UpdateRangeAsync(traces, cancellationToken);
  578. }
  579. /// <summary>
  580. /// 查询退回节点信息
  581. /// </summary>
  582. public (WorkflowStep currentStep, WorkflowStep prevStep, WorkflowStep? countersignStartStep) GetPreviousStep(
  583. Workflow workflow, string operaterId, string operaterOrgId, string[] roleIds)
  584. {
  585. var currentStep = GetUnHandleStep(workflow.Steps, operaterOrgId, operaterId, roleIds);
  586. var isCurrentTopCountersignEndStep = workflow.IsInCountersign &&
  587. currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId);
  588. if (currentStep.IsInCountersign() && !isCurrentTopCountersignEndStep)
  589. throw UserFriendlyException.SameMessage("会签节点不支持退回");
  590. if (workflow.FlowType is EFlowType.Review && currentStep.StepType is EStepType.Start &&
  591. currentStep.IsOrigin)
  592. throw UserFriendlyException.SameMessage("当前流程已退回到开始节点");
  593. //当退回操作遇到会签时,删除所有会签节点直达topCsStep
  594. //find prevStep, update handler
  595. WorkflowStep? prevStep, countersignStartStep = null;
  596. if (isCurrentTopCountersignEndStep)
  597. {
  598. //prev is topstart's prev
  599. countersignStartStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
  600. if (countersignStartStep is null)
  601. throw new UserFriendlyException("未查询到对应会签开始节点");
  602. prevStep = workflow.Steps.FirstOrDefault(d => d.Id == countersignStartStep.PrevStepId);
  603. }
  604. else
  605. {
  606. prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId);
  607. }
  608. if (prevStep == null)
  609. throw UserFriendlyException.SameMessage("未查询到前一节点");
  610. if (prevStep.IsCountersignEndStep)
  611. {
  612. countersignStartStep = workflow.Steps.FirstOrDefault(d => d.Id == prevStep.CountersignStartStepId);
  613. prevStep = countersignStartStep ?? throw new UserFriendlyException("未查询到对应会签开始节点");
  614. if (prevStep == null)
  615. throw UserFriendlyException.SameMessage("未查询到前一节点");
  616. }
  617. return (currentStep, prevStep, countersignStartStep);
  618. }
  619. /// <summary>
  620. /// 查询派单池中流程节点id
  621. /// </summary>
  622. public async Task<IReadOnlyList<string>> GetUnhandleStepIdsFromSendPoolAsync(string sendPoolId,
  623. CancellationToken cancellationToken)
  624. {
  625. return await _workflowStepRepository.Queryable()
  626. .Where(d => SqlFunc.JsonListObjectAny(d.Handlers, "Key", sendPoolId))
  627. .Select(d => d.Id)
  628. .ToListAsync(cancellationToken);
  629. }
  630. /// <summary>
  631. /// 查询归属某用户的所有流程节点
  632. /// </summary>
  633. public async Task<List<WorkflowStep>> GetStepsBelongsToAsync(string userId, CancellationToken cancellationToken)
  634. {
  635. return await _workflowStepRepository.Queryable()
  636. .Includes(d => d.WorkflowTrace)
  637. .Where(d => d.HandlerId == userId)
  638. .OrderBy(d => d.CreationTime)
  639. .ToListAsync(cancellationToken);
  640. }
  641. ///// <summary>
  642. ///// 批量改变办理对象
  643. ///// </summary>
  644. //public async Task<ICollection<string>> ChangeHandlerRangeAsync(string sendPoolId,
  645. // IReadOnlyList<(string userId, string username, string orgId, string orgName, IReadOnlyList<string> stepIds)> handlers,
  646. // CancellationToken cancellationToken)
  647. //{
  648. // var stepsIds = handlers.SelectMany(d => d.stepIds).ToList();
  649. // var steps = await _workflowStepRepository.Queryable()
  650. // .Includes(d => d.Workflow)
  651. // .Includes(d => d.WorkflowTrace)
  652. // //.Includes(d => d.StepHandlers)
  653. // .Where(d => stepsIds.Contains(d.Id))
  654. // .ToListAsync(cancellationToken);
  655. // foreach (var handler in handlers)
  656. // {
  657. // var thisHandlers = new List<Kv> { new(handler.userId, handler.username) };
  658. // var thisHandlerGroup = new HandlerGroupItem
  659. // {
  660. // GroupId = Guid.NewGuid().ToString(),
  661. // Key = handler.userId,
  662. // Value = handler.username,
  663. // };
  664. // var thisSteps = steps.Where(d => handler.stepIds.Contains(d.Id)).ToList();
  665. // foreach (var thisStep in thisSteps)
  666. // {
  667. // //var stepHandler = WorkflowStepHandler.Create(thisStep.Workflow.Id, thisStep.Workflow.ExternalId,
  668. // // thisStep.FlowAssignType ?? EFlowAssignType.User, handler.userId, handler.username, handler.orgId, handler.orgName);
  669. // //thisStep.StepHandlers.Clear();
  670. // //thisStep.StepHandlers.Add(stepHandler);
  671. // thisStep.Handlers = thisHandlers;
  672. // //update trace
  673. // thisStep.WorkflowTrace.Handlers = thisStep.Handlers;
  674. // // update workflow
  675. // thisStep.Workflow.FlowedUserIds.Remove(sendPoolId);
  676. // thisStep.Workflow.FlowedUserIds.Add(handler.userId);
  677. // thisStep.Workflow.UpdateHandlers(sendPoolId, null, EFlowAssignType.User,
  678. // new List<HandlerGroupItem> { thisHandlerGroup }, true);
  679. // var handlerUser = thisStep.Workflow.HandlerUsers.FirstOrDefault(d => d.Key == sendPoolId);
  680. // if (handlerUser == null) continue;
  681. // handlerUser.Key = handler.userId;
  682. // handlerUser.Value = handler.username;
  683. // }
  684. // }
  685. // await _workflowStepRepository.UpdateNav(steps)
  686. // .Include(d => d.WorkflowTrace)
  687. // .Include(d => d.Workflow)
  688. // //.Include(d => d.StepHandlers)
  689. // .ExecuteCommandAsync();
  690. // return steps.Select(d => d.WorkflowId).ToList();
  691. //}
  692. /// <summary>
  693. /// 批量修改工单办理对象
  694. /// </summary>
  695. public async Task ChangeHandlerBatchAsync(
  696. IReadOnlyList<(string userId, string username, string orgId, string orgName, string? roleId, string? roleName, ICollection<WorkflowStep>
  697. steps
  698. )> handlers,
  699. CancellationToken cancellationToken)
  700. {
  701. foreach (var handler in handlers)
  702. {
  703. foreach (var step in handler.steps)
  704. {
  705. step.FlowAssignType = EFlowAssignType.User;
  706. step.Assign(handler.userId, handler.username,
  707. handler.orgId, handler.orgName, handler.roleId, handler.roleName);
  708. if (step.WorkflowTrace is null)
  709. throw new UserFriendlyException("未查询节点对应快照信息");
  710. step.WorkflowTrace.FlowAssignType = EFlowAssignType.User;
  711. step.WorkflowTrace.Assign(handler.userId, handler.username,
  712. handler.orgId, handler.orgName, handler.roleId, handler.roleName);
  713. }
  714. }
  715. var steps = handlers.SelectMany(d => d.steps).ToList();
  716. //await _workflowStepRepository.UpdateRangeAsync(steps, cancellationToken);
  717. await _workflowStepRepository.UpdateNav(steps)
  718. .Include(d => d.WorkflowTrace)
  719. .ExecuteCommandAsync();
  720. }
  721. /// <summary>
  722. /// 查询工单办理中的一级部门
  723. /// </summary>
  724. public async Task<ICollection<Kv>> GetLevelOneOrgsAsync(string workflowId, CancellationToken cancellation)
  725. {
  726. var traces = await _workflowTraceRepository.Queryable()
  727. .LeftJoin<SystemOrganize>((t, o) => t.HandlerOrgId == o.Id)
  728. .Where((t, o) => t.WorkflowId == workflowId &&
  729. !string.IsNullOrEmpty(t.HandlerOrgId) &&
  730. o.Level == 1)
  731. .ToListAsync(cancellation);
  732. //var handlers = await _workflowStepHandlerRepository.Queryable()
  733. // .InnerJoin<WorkflowTrace>((wsh, wt) => wsh.WorkflowStepId == wt.StepId)
  734. // .LeftJoin<SystemOrganize>((wsh, wt, o) => wsh.OrgId == o.Id)
  735. // .Where((wsh, wt, o) => wsh.WorkflowId == workflowId &&
  736. // //wt.BusinessType == EBusinessType.Department &&
  737. // //wt.HandlerType == EHandlerType.OrgLevel &&
  738. // !string.IsNullOrEmpty(wsh.OrgId) &&
  739. // o.Level == 1)
  740. // .ToListAsync(cancellation);
  741. //var orgs = handlers.Select(d => new Kv(d.OrgId, d.OrgName))
  742. // .DistinctBy(d => d.Key)
  743. // .ToList();
  744. var orgs = traces
  745. .DistinctBy(d => d.HandlerOrgId)
  746. .Select(d => new Kv(d.HandlerOrgId, d.HandlerOrgName))
  747. .ToList();
  748. return orgs;
  749. //var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellation);
  750. //var list = workflow.Steps.Distinct().Where(d => d.BusinessType == EBusinessType.Department &&
  751. // d.HandlerType == EHandlerType.OrgLevel &&
  752. // d.StepHandlers.Any(d =>
  753. // !string.IsNullOrEmpty(d.OrgId) && d.OrgId.CheckIfOrgLevelIs(1)))
  754. // .Select(d => new Kv(d.StepHandlers.First().OrgId, d.StepHandlers.First().OrgName))
  755. // .ToList();
  756. //return list.Where((x, i) => list.FindIndex(z => z.Key == x.Key) == i).ToList();
  757. }
  758. /// <summary>
  759. /// 更新未办理节点的期满时间
  760. /// </summary>
  761. public async Task UpdateUnhandleExpiredTimeAsync(string workflowId, DateTime expiredTime,
  762. CancellationToken cancellation)
  763. {
  764. var steps = await _workflowStepRepository.Queryable()
  765. .Includes(d => d.WorkflowTrace)
  766. .Where(d => d.WorkflowId == workflowId &&
  767. d.Status < EWorkflowStepStatus.Handled)
  768. .ToListAsync(cancellation);
  769. foreach (var step in steps)
  770. {
  771. step.StepExpiredTime = expiredTime;
  772. step.WorkflowTrace.StepExpiredTime = expiredTime;
  773. }
  774. await _workflowStepRepository.UpdateNav(steps)
  775. .Include(d => d.WorkflowTrace)
  776. .ExecuteCommandAsync();
  777. }
  778. /// <summary>
  779. /// 查询该部门最后办理节点
  780. /// </summary>
  781. /// <returns></returns>
  782. public async Task<WorkflowStep> FindLastHandleStepAsync(string workflowId, string orgId,
  783. CancellationToken cancellation)
  784. {
  785. return await _workflowStepRepository.Queryable()
  786. .Where(d => d.WorkflowId == workflowId && d.HandlerOrgId == orgId && d.StepType != EStepType.End && d.StepType != EStepType.Summary)
  787. //.Where(d => d.StepHandlers.Any(sh => sh.OrgId == orgId) && d.WorkflowId == workflowId)
  788. .OrderByDescending(d => d.HandleTime)
  789. .FirstAsync(cancellation);
  790. }
  791. /// <summary>
  792. /// 部门会签工单获取流程最顶级办理节点
  793. /// </summary>
  794. /// <returns></returns>
  795. public async Task<WorkflowStep> FindTopHandleStepAsync(string workflowId, CancellationToken cancellation)
  796. {
  797. var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellation);
  798. return workflow.Steps.FirstOrDefault(x => x.Id == workflow.TopCountersignStepId);
  799. }
  800. /// <summary>
  801. /// 查询流转方向
  802. /// </summary>
  803. public EFlowDirection GetFlowDirection(EBusinessType sourceStepBusinessType,
  804. EBusinessType directionStepBusinessType)
  805. {
  806. switch (sourceStepBusinessType)
  807. {
  808. case EBusinessType.Seat:
  809. case EBusinessType.Send:
  810. return directionStepBusinessType switch
  811. {
  812. EBusinessType.Seat => EFlowDirection.CenterToCenter,
  813. EBusinessType.Send => EFlowDirection.CenterToCenter,
  814. EBusinessType.Department => EFlowDirection.CenterToOrg,
  815. EBusinessType.File => EFlowDirection.CenterToFile,
  816. _ => throw new ArgumentOutOfRangeException(nameof(directionStepBusinessType),
  817. directionStepBusinessType, null)
  818. };
  819. case EBusinessType.Department:
  820. case EBusinessType.File:
  821. return directionStepBusinessType switch
  822. {
  823. EBusinessType.Seat => EFlowDirection.OrgToCenter,
  824. EBusinessType.Send => EFlowDirection.OrgToCenter,
  825. EBusinessType.Department => EFlowDirection.OrgToOrg,
  826. EBusinessType.File => EFlowDirection.OrgToFile,
  827. _ => throw new ArgumentOutOfRangeException(nameof(directionStepBusinessType),
  828. directionStepBusinessType, null)
  829. };
  830. default:
  831. throw new ArgumentOutOfRangeException(nameof(sourceStepBusinessType), sourceStepBusinessType, null);
  832. }
  833. }
  834. /// <summary>
  835. /// 流程被签收至某个用户(更新流转对象,办理对象,节点办理对象以及stepHandlers)
  836. /// </summary>
  837. public async Task<Workflow> SignToSomebodyAsync(string workflowId, string userId, string username, string orgId,
  838. string orgName, CancellationToken cancellationToken)
  839. {
  840. var workflow = await GetWorkflowAsync(workflowId, withSteps: true, withTraces: true,
  841. cancellationToken: cancellationToken);
  842. workflow.Assign(EFlowAssignType.User, _sessionContext.RequiredUserId);
  843. workflow.HandlerOrgs = new();
  844. workflow.HandlerUsers = new List<HandlerGroupItem>
  845. {
  846. new()
  847. {
  848. GroupId = Guid.NewGuid().ToString(),
  849. Key = userId,
  850. Value = username
  851. }
  852. };
  853. var startStep = workflow.Steps.First(d => d.StepType == EStepType.Start && d.IsOrigin);
  854. startStep.Handlers = new List<Kv> { new(userId, username) };
  855. startStep.AcceptorId = userId;
  856. startStep.AcceptorName = username;
  857. startStep.AcceptTime = DateTime.Now;
  858. startStep.AcceptorOrgId = orgId;
  859. startStep.AcceptorOrgName = orgName;
  860. startStep.FlowAssignType = EFlowAssignType.User;
  861. startStep.Assign(userId, username, orgId, orgName);
  862. //var stepHandler = startStep.StepHandlers.First();
  863. //startStep.StepHandlers.RemoveAll(d => d.Id != stepHandler.Id);
  864. //stepHandler.UserId = userId;
  865. //stepHandler.Username = username;
  866. //stepHandler.OrgId = orgId;
  867. //stepHandler.OrgName = orgName;
  868. startStep.WorkflowTrace = workflow.Traces.First(d => d.Id == startStep.Id);
  869. _mapper.Map(startStep, startStep.WorkflowTrace);
  870. await _workflowStepRepository.UpdateNav(startStep)
  871. //.Include(d => d.StepHandlers)
  872. .Include(d => d.WorkflowTrace)
  873. .ExecuteCommandAsync();
  874. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  875. return workflow;
  876. }
  877. /// <summary>
  878. /// 非节点办理人员查询待办节点
  879. /// </summary>
  880. /// <returns></returns>
  881. public async Task<ICollection<WorkflowStep>> GetUnhandleStepsByOthersAsync(string workflowId, CancellationToken cancellationToken)
  882. {
  883. return await _workflowStepRepository.Queryable()
  884. .Where(d => d.WorkflowId == workflowId && d.Status != EWorkflowStepStatus.Handled)
  885. .ToListAsync(cancellationToken);
  886. }
  887. /// <summary>
  888. /// 查找当前会签内所有节点(含start,end)
  889. /// </summary>
  890. private void SearchCountersignSteps(WorkflowStep startStep, List<WorkflowStep> steps,
  891. ref List<WorkflowStep> csSteps)
  892. {
  893. if (startStep.IsStartCountersign)
  894. {
  895. var countersignSteps = steps.Where(d => d.CountersignId == startStep.StartCountersignId).ToList();
  896. if (countersignSteps.Any())
  897. {
  898. foreach (var countersignStep in countersignSteps)
  899. {
  900. SearchCountersignSteps(countersignStep, steps, ref csSteps);
  901. }
  902. }
  903. }
  904. csSteps.Add(startStep);
  905. }
  906. /// <summary>
  907. /// 撤回(返回到之前任意节点)
  908. /// </summary>
  909. public async Task RecallAsync(Workflow workflow, RecallDto dto, StepDefine targetStepDefine,
  910. FlowAssignInfo flowAssignInfo, EWorkflowTraceType traceType, DateTime? expiredTime, bool isOrderFiled,
  911. CancellationToken cancellationToken)
  912. {
  913. var targetStep = workflow.Steps.FirstOrDefault(d => d.Code == dto.NextStepCode && d.IsOrigin);
  914. if (targetStep is null)
  915. throw UserFriendlyException.SameMessage("该流程尚未流转至该节点");
  916. //update uncompleted traces
  917. //await RecallTraceAsync(workflow.Traces, dto.Opinion, _sessionContext, cancellationToken);
  918. var isOrgToCenter = await RecallAsync(workflow, dto, flowAssignInfo, targetStepDefine, targetStep,
  919. traceType, expiredTime, isOrderFiled, cancellationToken);
  920. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  921. await _publisher.PublishAsync(new RecallNotify(workflow, targetStep, dto, isOrgToCenter),
  922. PublishStrategy.ParallelWhenAll, cancellationToken);
  923. }
  924. /// <summary>
  925. /// 撤回至开始节点
  926. /// </summary>
  927. public async Task RecallToStartStepAsync(string workflowId, string opinion, ISessionContext current,
  928. CancellationToken cancellationToken)
  929. {
  930. //todo 1.当前待办节点删掉 2.当前待办trace更新(status, opinion) 3.复制startStep为待办 4.更新workflow(status, csStatus, handlers) 5.publish event
  931. var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true,
  932. cancellationToken: cancellationToken);
  933. var startStep = workflow.Steps.First(d => d.StepType == EStepType.Start);
  934. if (startStep is null)
  935. throw new UserFriendlyException($"数据异常, workflowId: {workflowId}", "该流程无开始节点");
  936. await RecallToTargetStepAsync(workflow, startStep, opinion, current, cancellationToken);
  937. }
  938. /// <summary>
  939. /// 撤回至派单节点
  940. /// </summary>
  941. public async Task RecallToSendStepAsync(string workflowId, string opinion, ISessionContext current,
  942. CancellationToken cancellationToken)
  943. {
  944. var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true,
  945. cancellationToken: cancellationToken);
  946. var sendStep = workflow.Steps.FirstOrDefault(d => d.BusinessType == EBusinessType.Send);
  947. if (sendStep is null)
  948. throw new UserFriendlyException($"未找到派单节点, workflowId: {workflowId}", "该流程无派单节点");
  949. await RecallToTargetStepAsync(workflow, sendStep, opinion, current, cancellationToken);
  950. }
  951. /// <summary>
  952. /// 特提至中心(优先派单组其次坐席)
  953. /// </summary>
  954. /// <returns></returns>
  955. public async Task RecallToCenterFirstToSendAsync(string workflowId, string opinion, ISessionContext current,
  956. CancellationToken cancellationToken)
  957. {
  958. var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true,
  959. cancellationToken: cancellationToken);
  960. var sendStep = workflow.Steps.FirstOrDefault(d => d.BusinessType == EBusinessType.Send);
  961. if (sendStep is not null)
  962. {
  963. await RecallToTargetStepAsync(workflow, sendStep, opinion, current, cancellationToken);
  964. }
  965. else
  966. {
  967. var startStep = workflow.Steps.First(d => d.StepType == EStepType.Start);
  968. if (startStep is null)
  969. throw new UserFriendlyException($"数据异常, workflowId: {workflowId}", "该流程无开始节点");
  970. await RecallToTargetStepAsync(workflow, startStep, opinion, current, cancellationToken);
  971. }
  972. }
  973. private async Task RecallToTargetStepAsync(Workflow workflow, WorkflowStep targetStep, string opinion, ISessionContext current,
  974. CancellationToken cancellationToken)
  975. {
  976. //update uncompleted traces
  977. await RecallTraceAsync(workflow.Traces, opinion, current, cancellationToken);
  978. await _workflowStepRepository.RemoveRangeAsync(workflow.Steps, cancellationToken);
  979. workflow.Steps.RemoveAll(_ => true);
  980. workflow.EndCountersign();
  981. workflow.ResetOption();
  982. if (workflow.Status is EWorkflowStatus.Completed)
  983. workflow.SetStatusRunnable();
  984. var newStartStep =
  985. await DuplicateStepWithTraceAsync(workflow, targetStep, EWorkflowTraceType.Recall, cancellationToken);
  986. workflow.UpdateActualStepWhenAssign(targetStep, new FlowStepHandler
  987. {
  988. UserId = targetStep.HandlerId,
  989. Username = targetStep.HandlerName,
  990. OrgId = targetStep.HandlerOrgId,
  991. OrgName = targetStep.HandlerOrgName,
  992. });
  993. workflow.UpdateCurrentStepWhenAssign(targetStep, new FlowStepHandler
  994. {
  995. UserId = targetStep.HandlerId,
  996. Username = targetStep.HandlerName,
  997. OrgId = targetStep.HandlerOrgId,
  998. OrgName = targetStep.HandlerOrgName,
  999. });
  1000. var isOrgToCenter = CheckIfFlowFromOrgToCenter(workflow, targetStep);
  1001. var flowAssignInfo = FlowAssignInfo.Create(targetStep.FlowAssignType.Value, targetStep.Handlers);
  1002. workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
  1003. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  1004. var dto = _mapper.Map<RecallDto>(targetStep);
  1005. dto.WorkflowId = workflow.Id;
  1006. await _publisher.PublishAsync(new RecallNotify(workflow, targetStep, dto, isOrgToCenter),
  1007. PublishStrategy.ParallelWhenAll, cancellationToken);
  1008. }
  1009. ///// <summary>
  1010. ///// 跳转(直接将流程跳转至任意节点)
  1011. ///// </summary>
  1012. //public async Task JumpAsync(Workflow workflow, RecallDto dto, StepDefine targetStepDefine,
  1013. // FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
  1014. //{
  1015. // //todo 跳转至结束节点,(自动办理)
  1016. // //if (targetStepDefine.StepType is EStepType.Start or EStepType.End)
  1017. // // throw UserFriendlyException.SameMessage("开始/结束节点不支持跳转");
  1018. // //update uncompleted traces
  1019. // await JumpTraceAsync(workflow.Id, dto, cancellationToken);
  1020. // bool isOrgToCenter = false, isCenterToOrg = false;
  1021. // var targetStep = workflow.Steps.FirstOrDefault(d => d.Code == dto.NextStepCode && d.IsOrigin);
  1022. // if (targetStep == null)
  1023. // {
  1024. // //向后跳转
  1025. // //此场景并非按配置流转,默认最靠后的节点做为targetStep的prevStep
  1026. // var lastStep = workflow.Steps.Where(d => d.IsOrigin).MaxBy(d => d.CreationTime);
  1027. // if (lastStep is null || lastStep.StepType is EStepType.End)
  1028. // throw new UserFriendlyException($"流程流转数据异常,未结束流程出现endStep, flowId: {workflow.Id}", "流程流转数据异常");
  1029. // var targetSteps = await CreateConfigStepsAsync(workflow, targetStepDefine, lastStep, dto,
  1030. // flowAssignInfo, EWorkflowTraceStatus.Jump, cancellationToken);
  1031. // targetStep = targetSteps.First();
  1032. // workflow.EndCountersign();
  1033. // workflow.ResetOption();
  1034. // ////更新当前办理节点信息
  1035. // //workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign,
  1036. // // _sessionContext.RequiredUserId, _sessionContext.UserName,
  1037. // // _sessionContext.RequiredOrgId, _sessionContext.OrgName,
  1038. // // _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName,
  1039. // // nextStep: targetStep);
  1040. // //calc workflow expired time
  1041. // isCenterToOrg = CheckIfFlowFromCenterToOrg(workflow, targetStep);
  1042. // //if (isCenterToOrg)
  1043. // // workflow.ExpiredTime = CalculateExpiredTime("");//todo calc expiredTime
  1044. // #region 补充中间节点处理方案(暂不需要)
  1045. // //var completeStepCodes = workflow.StepBoxes.Select(d => d.Code);
  1046. // //var uncompleteStepDefines = workflow.Definition.Steps.Where(d => !completeStepCodes.Contains(d.Code));
  1047. // //创建当前节点与目标节点中间节点
  1048. // //var jumpDto = new BasicWorkflowDto
  1049. // //{
  1050. // // Opinion = "跳转补充"
  1051. // //};
  1052. // //foreach (var stepDefine in uncompleteStepDefines)
  1053. // //{
  1054. // // var previousStepId = lastStepBox.Steps.Count > 1 ? lastStepBox.Id : lastStepBox.Steps.First().Id;
  1055. // // if (dto.TargetStepCode == stepDefine.Code)
  1056. // // {
  1057. // // await CreateStepAsync(workflow, stepDefine, dto, lastStepBox.Id, previousStepId, cancellationToken);
  1058. // // break;
  1059. // // }
  1060. // // //jump业务下,如果当前节点为会签节点,第一个补充节点的subStep.PreviousId无法确定从哪个子节点跳转过来,统一处理为当前节点的stepBox.Id
  1061. // // lastStepBox = await CreateStepAsync(workflow, stepDefine, dto, lastStepBox.Id, previousStepId, cancellationToken);
  1062. // //}
  1063. // #endregion
  1064. // }
  1065. // else
  1066. // {
  1067. // //返回之前节点
  1068. // isOrgToCenter = await RecallAsync(workflow, dto, flowAssignInfo, targetStepDefine, targetStep,
  1069. // EWorkflowTraceStatus.Jump, cancellationToken);
  1070. // }
  1071. // workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
  1072. // await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  1073. // await _mediator.Publish(
  1074. // new JumpNotify(workflow, targetStep, dto, flowAssignInfo, isCenterToOrg, isOrgToCenter),
  1075. // cancellationToken);
  1076. //}
  1077. ///// <summary>
  1078. ///// 重办
  1079. ///// </summary>
  1080. //public async Task RedoAsync(Workflow workflow, RecallDto dto, StepDefine targetStepDefine,
  1081. // FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
  1082. //{
  1083. // if (targetStepDefine.StepType is EStepType.Start or EStepType.End)
  1084. // throw UserFriendlyException.SameMessage("开始/结束节点不支持重办");
  1085. // var targetStepBox = workflow.Steps.FirstOrDefault(d => d.Code == dto.NextStepCode);
  1086. // if (targetStepBox is null)
  1087. // throw UserFriendlyException.SameMessage("未找到该节点配置");
  1088. // var isOrgToCenter = await RecallAsync(workflow, dto, flowAssignInfo, targetStepDefine, targetStepBox,
  1089. // EWorkflowTraceStatus.Redo, cancellationToken);
  1090. // workflow.Redo();
  1091. // workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
  1092. // //todo calc expiredTime
  1093. // //dto.Extension.TimeLimitCount
  1094. // await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  1095. // await _mediator.Publish(new RedoNotify(workflow, dto, isOrgToCenter), cancellationToken);
  1096. //}
  1097. ///// <summary>
  1098. ///// 否决(审批流程不通过)
  1099. ///// </summary>
  1100. ///// <returns></returns>
  1101. //public async Task RejectAsync(Workflow workflow, BasicWorkflowDto dto, CancellationToken cancellationToken)
  1102. //{
  1103. // var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId,
  1104. // _sessionContext.RequiredUserId);
  1105. // await HandleStepAsync(currentStep, workflow, dto, null,
  1106. // null, null, cancellationToken);
  1107. // await _workflowStepRepository.UpdateAsync(currentStep, cancellationToken);
  1108. // workflow.UpdateActualStepWhenHandle(currentStep,
  1109. // _sessionContext.RequiredUserId, _sessionContext.UserName,
  1110. // _sessionContext.RequiredOrgId, _sessionContext.OrgName,
  1111. // _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName,
  1112. // _sessionContext.OrgLevel);
  1113. // var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
  1114. // var endTrace = await EndAsync(workflow, dto, endStepDefine, currentStep, cancellationToken);
  1115. // //await _mediator.Publish(new RejectNotify(workflow, dto), cancellationToken);
  1116. //}
  1117. /// <summary>
  1118. /// 补充
  1119. /// </summary>
  1120. /// <returns></returns>
  1121. public async Task SupplementAsync(Workflow workflow, EndWorkflowDto dto, CancellationToken cancellationToken)
  1122. {
  1123. CheckWhetherRunnable(workflow.Status);
  1124. //todo 检查当前办理人是否为该流程中的办理人
  1125. var supplement = _mapper.Map<WorkflowSupplement>(dto);
  1126. await _workflowSupplementRepository.AddAsync(supplement, cancellationToken);
  1127. }
  1128. /// <summary>
  1129. /// 终止流程
  1130. /// </summary>
  1131. public async Task TerminateAsync(TerminateDto dto, CancellationToken cancellationToken)
  1132. {
  1133. var workflow = await _workflowRepository.GetAsync(dto.WorkflowId, cancellationToken);
  1134. if (workflow == null)
  1135. throw UserFriendlyException.SameMessage("无效的流程编号");
  1136. workflow.Terminate(dto.Opinion);
  1137. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  1138. await _publisher.PublishAsync(new TerminalWorkflowNotify(workflow), PublishStrategy.ParallelWhenAll,
  1139. cancellationToken);
  1140. }
  1141. /// <summary>
  1142. /// 根据stepCode查询流程配置中对应的节点
  1143. /// </summary>
  1144. public StepDefine GetStepDefine(WorkflowDefinition workflowDefinition, string stepCode)
  1145. {
  1146. if (workflowDefinition == null) throw new ArgumentNullException(nameof(workflowDefinition));
  1147. if (string.IsNullOrEmpty(stepCode)) throw new ArgumentNullException(nameof(stepCode));
  1148. var stepDefine = workflowDefinition.FindStepDefine(stepCode);
  1149. if (stepDefine == null)
  1150. throw new UserFriendlyException(
  1151. $"未找到流程中对应的节点,DefineCode: {workflowDefinition.Code}, stepCode: {stepCode}",
  1152. "未查询到对应节点");
  1153. return stepDefine;
  1154. }
  1155. /// <summary>
  1156. /// 查询当前待办理节点
  1157. /// </summary>
  1158. public WorkflowStep FindCurrentStepWaitForHandle(Workflow workflow, string userId, string orgId,
  1159. string[] roleIds) =>
  1160. GetUnHandleStep(workflow.Steps, orgId, userId, roleIds);
  1161. /// <summary>
  1162. /// 查询当前节点中最后一个节点
  1163. /// </summary>
  1164. public async Task<WorkflowStep?> FindLastStepAsync(string workflowId, CancellationToken cancellationToken)
  1165. {
  1166. var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellationToken);
  1167. return workflow.Steps.MaxBy(d => d.CreationTime);
  1168. }
  1169. /// <summary>
  1170. /// 查询所有办理部门及实际办理部门
  1171. /// </summary>
  1172. /// <returns></returns>
  1173. public async Task<(Kv, IReadOnlyList<Kv>)> GetHandleOrgsAsync(string workflowId,
  1174. CancellationToken cancellationToken)
  1175. {
  1176. var workflow = await GetWorkflowAsync(workflowId, withTraces: true, cancellationToken: cancellationToken);
  1177. var steps = workflow.Traces
  1178. .Where(d => d.StepType is EStepType.Normal)
  1179. .ToList();
  1180. var items = steps.Where(d => d.TraceType == EWorkflowTraceType.Normal || d.TraceType == EWorkflowTraceType.Jump)
  1181. .Select(d => new Kv(d.HandlerOrgId, d.HandlerOrgName))
  1182. .DistinctBy(d => d.Key).ToList();
  1183. return (new Kv(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), items);
  1184. }
  1185. /// <summary>
  1186. /// 依据配置过滤下一节点
  1187. /// </summary>
  1188. public List<StepDefine> NextStepDefineFilter(EPathPolicy pathPolicy, List<StepDefine> nextStepDefines)
  1189. {
  1190. switch (pathPolicy)
  1191. {
  1192. case EPathPolicy.DirectUpper:
  1193. break;
  1194. case EPathPolicy.DirectUpperCenterIsTop:
  1195. var currentOrgLevel = _sessionContext.RequiredOrgId.CalcOrgLevel();
  1196. if (currentOrgLevel == 1)
  1197. {
  1198. nextStepDefines = nextStepDefines.Where(d => d.IsCenter()).ToList();
  1199. }
  1200. else
  1201. {
  1202. var upperLevel = (--currentOrgLevel).ToString();
  1203. nextStepDefines = nextStepDefines
  1204. .Where(d => d.HandlerType is EHandlerType.OrgLevel &&
  1205. d.HandlerTypeItems.Any(x => x.Key == upperLevel))
  1206. .ToList();
  1207. }
  1208. break;
  1209. default:
  1210. throw new ArgumentOutOfRangeException();
  1211. }
  1212. return nextStepDefines;
  1213. }
  1214. /// <summary>
  1215. /// 撤销流程
  1216. /// </summary>
  1217. public async Task CancelAsync(CancelDto dto, DateTime? expiredTime, ISessionContext current,
  1218. CancellationToken cancellationToken)
  1219. {
  1220. var workflow = await GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true,
  1221. cancellationToken: cancellationToken);
  1222. var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId,
  1223. _sessionContext.RequiredUserId, _sessionContext.Roles);
  1224. //var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
  1225. var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
  1226. var basicDto = _mapper.Map<BasicWorkflowDto>(dto);
  1227. var endTrace = await EndAsync(workflow, basicDto, endStepDefine, currentStep, current,
  1228. expiredTime, cancellationToken: cancellationToken);
  1229. await _publisher.PublishAsync(new CancelWorkflowNotify(workflow), PublishStrategy.ParallelWhenAll,
  1230. cancellationToken);
  1231. }
  1232. /// <summary>
  1233. /// 新增流程流转记录
  1234. /// </summary>
  1235. public async Task AddTracesAsync(string workflowId, List<WorkflowTrace> traces,
  1236. CancellationToken cancellationToken)
  1237. {
  1238. var workflow = await GetWorkflowAsync(workflowId, cancellationToken: cancellationToken);
  1239. if (workflow is null)
  1240. throw new UserFriendlyException("找不到该流程");
  1241. await _workflowTraceRepository.AddRangeAsync(traces, cancellationToken);
  1242. }
  1243. /// <summary>
  1244. /// 创建开始节点
  1245. /// </summary>
  1246. public WorkflowStep CreateStartStep(Workflow workflow, StepDefine startStepDefine,
  1247. BasicWorkflowDto dto, FlowStepHandler handler, DateTime? expiredTime,
  1248. EFlowAssignType? flowAssignType = EFlowAssignType.User)
  1249. {
  1250. //startstep
  1251. var nextSteps = _mapper.Map<List<StepSimple>>(startStepDefine.NextSteps);
  1252. if (startStepDefine.InstanceMode is EInstanceMode.Config)
  1253. {
  1254. var selectedStep = nextSteps.FirstOrDefault(d => d.Code == dto.NextStepCode);
  1255. if (selectedStep is not null)
  1256. selectedStep.Selected = true;
  1257. }
  1258. var startStep = _mapper.Map<WorkflowStep>(startStepDefine);
  1259. _mapper.Map(workflow, startStep);
  1260. startStep.FlowAssignType = flowAssignType;
  1261. startStep.Handlers = new List<Kv> { new(handler.Key, handler.Value) };
  1262. //startStep.StepHandlers = stepHandlers;
  1263. startStep.NextSteps = nextSteps;
  1264. startStep.IsMain = true;
  1265. startStep.IsOrigin = true;
  1266. startStep.Status = EWorkflowStepStatus.WaitForAccept;
  1267. startStep.PrevChosenStepCode = null;
  1268. startStep.StepExpiredTime = expiredTime;
  1269. startStep.Assign(handler.UserId, handler.Username,
  1270. handler.OrgId, handler.OrgName,
  1271. handler.RoleId, handler.RoleName);
  1272. startStep.InitId();
  1273. return startStep;
  1274. }
  1275. /// <summary>
  1276. /// 流程结束
  1277. /// </summary>
  1278. public async Task<WorkflowTrace> EndAsync(Workflow workflow, BasicWorkflowDto dto,
  1279. StepDefine endStepDefine, WorkflowStep currentStep, ISessionContext current,
  1280. DateTime? expiredTime, CancellationToken cancellationToken)
  1281. {
  1282. //var endStepHandles = new List<WorkflowStepHandler>
  1283. //{
  1284. // WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId, EFlowAssignType.User,
  1285. // current.RequiredUserId, current.UserName, current.RequiredOrgId, current.OrgName)
  1286. //};
  1287. //create endStep
  1288. var endStep = await CreateEndStepAsync(current, workflow, endStepDefine, currentStep, expiredTime,
  1289. cancellationToken);
  1290. //workflow.Steps.Add(endStep);
  1291. //update endTrace
  1292. var endTrace = await NextTraceAsync(workflow, dto, endStep, cancellationToken);
  1293. workflow.Complete(endStep, dto.ReviewResult);
  1294. //需求调整:归档时当前节点显示为归档节点
  1295. workflow.UpdateCurrentStepWhenHandle(endStep,
  1296. current.OrgAreaCode, current.OrgAreaName, current.OrgLevel);
  1297. workflow.UpdateCurrentStepAcceptTime(endStep.AcceptTime.Value);
  1298. //workflow.UpdateActualStepWhenHandle(endStep, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel);
  1299. //workflow.UpdateActualStepAcceptTime(endStep.AcceptTime.Value);
  1300. if (string.IsNullOrEmpty(workflow.OrgLevelOneCode))
  1301. workflow.UpdateLevelOneOrg(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName);
  1302. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  1303. await _publisher.PublishAsync(new EndWorkflowNotify(workflow, endTrace, dto),
  1304. PublishStrategy.ParallelWhenAll, cancellationToken);
  1305. return endTrace;
  1306. }
  1307. ///// <summary>
  1308. ///// 判断会签类型(中心会签或部门会签)
  1309. ///// </summary>
  1310. ///// <param name="businessType"></param>
  1311. ///// <returns></returns>
  1312. ///// <exception cref="ArgumentOutOfRangeException"></exception>
  1313. //public ECounterSignType? GetCounterSignType(EBusinessType businessType) =>
  1314. // businessType switch
  1315. // {
  1316. // EBusinessType.Seat => ECounterSignType.Seat,
  1317. // EBusinessType.Send => ECounterSignType.Seat,
  1318. // EBusinessType.Department => ECounterSignType.Department,
  1319. // EBusinessType.File => null,
  1320. // _ => throw new ArgumentOutOfRangeException(nameof(businessType), businessType, null)
  1321. // };
  1322. public ECounterSignType? GetCounterSignType(bool isStartCountersign)
  1323. {
  1324. if (!isStartCountersign) return null;
  1325. return _sessionContext.OrgIsCenter ? ECounterSignType.Center : ECounterSignType.Department;
  1326. }
  1327. /// <summary>
  1328. /// 办理节点
  1329. /// </summary>
  1330. public async Task HandleStepAsync(ISessionContext current, WorkflowStep step, Workflow workflow,
  1331. BasicWorkflowDto dto, EFlowAssignType? flowAssignType, ECounterSignType? counterSignType,
  1332. DateTime? expiredTime, CancellationToken cancellationToken)
  1333. {
  1334. if (step.Status is EWorkflowStepStatus.Handled)
  1335. throw UserFriendlyException.SameMessage("当前节点状态已办理");
  1336. if (step.StepType is EStepType.End)
  1337. throw new UserFriendlyException("当前流程已流转到最终步骤");
  1338. if (dto.IsStartCountersign && !counterSignType.HasValue)
  1339. throw new UserFriendlyException("缺少会签类型参数");
  1340. //办理参数
  1341. //_mapper.Map(dto, step);
  1342. step.NextHandlers = dto.NextHandlers;
  1343. step.NextMainHandler = dto.NextMainHandler;
  1344. step.NextStepCode = dto.NextStepCode;
  1345. step.IsSms = dto.IsSms;
  1346. step.Opinion = dto.Opinion;
  1347. step.Remark = dto.Remark;
  1348. //step办理状态
  1349. HandleStep(current, step, dto.Opinion, dto.NextStepCode);
  1350. }
  1351. #region private method
  1352. private static void UpdateCurrentStep(Workflow workflow, BasicWorkflowDto dto,
  1353. StepDefine nextStepDefine, List<WorkflowStep> nextSteps)
  1354. {
  1355. if (dto.IsStartCountersign) return;
  1356. if (workflow.IsInCountersign) return;
  1357. if (nextStepDefine.BusinessType is EBusinessType.Seat or EBusinessType.Send)
  1358. {
  1359. //坐席->派单不选办理对象时
  1360. workflow.UpdateCurrentStepWhenAssign(nextSteps.First(),
  1361. new FlowStepHandler
  1362. {
  1363. OrgId = OrgSeedData.CenterId,
  1364. OrgName = OrgSeedData.CenterName
  1365. });
  1366. }
  1367. else
  1368. {
  1369. var nextHandler = dto.NextHandlers.First();
  1370. workflow.UpdateCurrentStepWhenAssign(nextSteps.First(), nextHandler);
  1371. }
  1372. }
  1373. private static void UpdateActualStep(Workflow workflow, BasicWorkflowDto dto,
  1374. StepDefine nextStepDefine, List<WorkflowStep> nextSteps)
  1375. {
  1376. if (dto.IsStartCountersign) return;
  1377. if (workflow.IsInCountersign) return;
  1378. if (nextStepDefine.StepType is EStepType.Summary or EStepType.End) return;
  1379. if (nextStepDefine.BusinessType is EBusinessType.Seat or EBusinessType.Send)
  1380. {
  1381. //坐席->派单不选办理对象时
  1382. workflow.UpdateActualStepWhenAssign(nextSteps.First(),
  1383. new FlowStepHandler
  1384. {
  1385. OrgId = OrgSeedData.CenterId,
  1386. OrgName = OrgSeedData.CenterName
  1387. });
  1388. }
  1389. else
  1390. {
  1391. var nextHandler = dto.NextHandlers.First();
  1392. workflow.UpdateActualStepWhenAssign(nextSteps.First(), nextHandler);
  1393. }
  1394. //if ( /*workflow.FlowType is EFlowType.Handle &&*/
  1395. // !workflow.IsInCountersign &&
  1396. // nextStepDefine.StepType != EStepType.Summary &&
  1397. // nextStepDefine.StepType != EStepType.End)
  1398. //{
  1399. // if (nextStepDefine.BusinessType is EBusinessType.Seat or EBusinessType.Send)
  1400. // {
  1401. // workflow.UpdateActualStepWhenAssign(firstSteps.First(), actualHandleOrgCode: OrgSeedData.CenterId,
  1402. // actualHandleOrgName: "市民热线服务中心");
  1403. // }
  1404. // else
  1405. // {
  1406. // var nextHandler = dto.NextHandlers.First();
  1407. // workflow.UpdateActualStepWhenAssign(firstSteps.First(),
  1408. // actualHandleOrgCode: nextHandler.Key, actualHandleOrgName: nextHandler.Value);
  1409. // }
  1410. //}
  1411. }
  1412. private async Task<WorkflowStep> CreateStartStepAsync(Workflow workflow, StepDefine startStepDefine,
  1413. BasicWorkflowDto dto, FlowStepHandler handler, EWorkflowTraceType traceType,
  1414. DateTime? expiredTime, EFlowAssignType? flowAssignType, CancellationToken cancellationToken)
  1415. {
  1416. var startStep = CreateStartStep(workflow, startStepDefine, dto, handler, expiredTime, flowAssignType);
  1417. await _workflowStepRepository.AddAsync(startStep, cancellationToken);
  1418. //await _workflowStepRepository.AddNav(startStep)
  1419. // .Include(d => d.StepHandlers)
  1420. // .ExecuteCommandAsync();
  1421. await CreateTraceAsync(workflow, startStep, traceType, cancellationToken);
  1422. return startStep;
  1423. }
  1424. ////更新目标节点前一节点的会签办理完成状态
  1425. //private void PrevStepCsHandled(Workflow workflow, WorkflowStep targetStep, ref List<WorkflowStep> updateSteps)
  1426. //{
  1427. // var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == targetStep.PrevStepId);
  1428. // if (prevStep is null)
  1429. // throw new UserFriendlyException("未查询到目标节点的前一节点");
  1430. // var csStep = prevStep.CountersignSteps.FirstOrDefault(d => d.StepId == targetStep.Id);
  1431. // if (csStep is null)
  1432. // throw new UserFriendlyException("未查询到当前待办节点");
  1433. // csStep.Completed = true;
  1434. // updateSteps.Add(prevStep);
  1435. //}
  1436. /// <summary>
  1437. /// 创建下1/N个节点
  1438. /// </summary>
  1439. private async Task<List<WorkflowStep>> CreateNextStepsAsync(Workflow workflow, WorkflowStep currentStep,
  1440. BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, FlowAssignInfo flowAssignInfo,
  1441. DateTime? expiredTime, bool isStartCountersign,
  1442. CancellationToken cancellationToken)
  1443. {
  1444. List<WorkflowStep> nextSteps = new();
  1445. if (currentStep.IsInCountersign())
  1446. {
  1447. if (currentStep.IsCountersignEndStep)
  1448. {
  1449. // check if current is topend f: csStartStep.prev
  1450. // t: check if dto.StartCs t: csconfig f: config
  1451. if (currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
  1452. {
  1453. if (isStartCountersign)
  1454. {
  1455. //依据会签策略创建会签下一级节点
  1456. nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
  1457. flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken);
  1458. }
  1459. else
  1460. {
  1461. //创建普通节点(根据配置)
  1462. nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto,
  1463. flowAssignInfo, EWorkflowTraceType.Normal, expiredTime, cancellationToken);
  1464. }
  1465. }
  1466. else
  1467. {
  1468. if (dto.BackToCountersignEnd)
  1469. {
  1470. // csStartStep.prev
  1471. var csStartStep = GetRealCsStartHandleStep(workflow.Steps, currentStep.CountersignStartStepId);
  1472. if (csStartStep is null)
  1473. throw new UserFriendlyException("未查询到会签节点");
  1474. nextSteps = await CreateCsEndStepsByTargetPrevAsync(workflow, csStartStep, dto, expiredTime,
  1475. cancellationToken);
  1476. }
  1477. else
  1478. {
  1479. //依据会签策略创建会签下一级节点
  1480. nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
  1481. flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken);
  1482. }
  1483. }
  1484. }
  1485. else
  1486. {
  1487. if (dto.BackToCountersignEnd)
  1488. {
  1489. // check if cs all complete, create next
  1490. nextSteps = await CreateCsEndStepsByTargetPrevAsync(workflow, currentStep, dto,
  1491. expiredTime, cancellationToken);
  1492. }
  1493. else
  1494. {
  1495. //依据会签策略创建会签下一级节点
  1496. nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
  1497. flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken);
  1498. }
  1499. }
  1500. }
  1501. else if (isStartCountersign) //top
  1502. {
  1503. //依据会签策略创建会签下一级节点
  1504. nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
  1505. flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken);
  1506. }
  1507. else if (isNextDynamic)
  1508. {
  1509. //创建动态下一级节点
  1510. nextSteps = await CreateDynamicStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo,
  1511. expiredTime, cancellationToken);
  1512. }
  1513. else
  1514. {
  1515. //创建普通节点(根据配置)
  1516. nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo,
  1517. EWorkflowTraceType.Normal, expiredTime, cancellationToken);
  1518. }
  1519. return nextSteps;
  1520. }
  1521. /// <summary>
  1522. /// 查询当前会签开始的第一级办理节点(会签汇总再次发起会签得场景)
  1523. /// </summary>
  1524. private static WorkflowStep? GetRealCsStartHandleStep(List<WorkflowStep> steps, string countersignStartStepId)
  1525. {
  1526. if (string.IsNullOrEmpty(countersignStartStepId))
  1527. throw new UserFriendlyException("会签汇总节点未正确赋值会签开始节点编号");
  1528. var csStartStep = steps.FirstOrDefault(d => d.Id == countersignStartStepId);
  1529. if (csStartStep.IsCountersignEndStep)
  1530. csStartStep = GetRealCsStartHandleStep(steps, csStartStep.CountersignStartStepId);
  1531. return csStartStep;
  1532. }
  1533. private async Task<List<WorkflowStep>> CreateDynamicStepsAsync(
  1534. Workflow workflow,
  1535. StepDefine nextStepDefine,
  1536. WorkflowStep prevStep,
  1537. BasicWorkflowDto dto,
  1538. FlowAssignInfo flowAssignInfo,
  1539. DateTime? expiredTime,
  1540. CancellationToken cancellationToken)
  1541. {
  1542. var handlerType = nextStepDefine.InstancePolicy switch
  1543. {
  1544. EDynamicPolicy.OrgUpCenterTop => EHandlerType.OrgLevel,
  1545. EDynamicPolicy.OrgUp => EHandlerType.OrgLevel,
  1546. EDynamicPolicy.OrgDownCenterTop => EHandlerType.OrgLevel,
  1547. EDynamicPolicy.OrgDown => EHandlerType.OrgLevel,
  1548. null => throw new ArgumentOutOfRangeException(),
  1549. _ => throw new ArgumentOutOfRangeException()
  1550. };
  1551. return await CreateStepsAsync(workflow, nextStepDefine, prevStep, dto,
  1552. flowAssignInfo.FlowAssignType, dto.NextHandlers, null, EWorkflowStepStatus.WaitForAccept,
  1553. ECountersignPosition.None, false, EWorkflowTraceType.Normal, handlerType, expiredTime,
  1554. cancellationToken: cancellationToken);
  1555. }
  1556. private Task<List<WorkflowStep>> CreateCountersignStepsAsync(
  1557. Workflow workflow,
  1558. StepDefine stepDefine,
  1559. WorkflowStep prevStep,
  1560. BasicWorkflowDto dto,
  1561. EFlowAssignType flowAssignType,
  1562. DateTime? expiredTime,
  1563. bool isStartCountersign,
  1564. CancellationToken cancellationToken = default
  1565. )
  1566. {
  1567. //var countersignId = dto.IsStartCountersign ? prevStep.StartCountersignId : prevStep.CountersignId;
  1568. var countersignId = prevStep.StartCountersignId;
  1569. var handlerType = stepDefine.CountersignPolicy switch
  1570. {
  1571. EDynamicPolicyCountersign.OrgUpCenterTop => EHandlerType.OrgLevel,
  1572. EDynamicPolicyCountersign.OrgUp => EHandlerType.OrgLevel,
  1573. EDynamicPolicyCountersign.OrgDownCenterTop => EHandlerType.OrgLevel,
  1574. EDynamicPolicyCountersign.OrgDown => EHandlerType.OrgLevel,
  1575. null => throw new ArgumentOutOfRangeException(),
  1576. _ => throw new ArgumentOutOfRangeException()
  1577. };
  1578. var nextStepCountersignPosition = dto.NextHandlers.Count > 1
  1579. ? ECountersignPosition.Multi
  1580. : ECountersignPosition.Single;
  1581. return CreateStepsAsync(workflow, stepDefine, prevStep, dto, flowAssignType, dto.NextHandlers,
  1582. countersignId, EWorkflowStepStatus.WaitForAccept, nextStepCountersignPosition,
  1583. false, EWorkflowTraceType.Normal, handlerType, expiredTime, cancellationToken: cancellationToken);
  1584. }
  1585. /// <summary>
  1586. /// 根据传入节点的上一节点创建会签汇总节点(汇总传入节点的前一节点)
  1587. /// </summary>
  1588. private async Task<List<WorkflowStep>> CreateCsEndStepsByTargetPrevAsync(Workflow workflow, WorkflowStep step,
  1589. BasicWorkflowDto dto, DateTime? expiredTime, CancellationToken cancellationToken)
  1590. {
  1591. var countersignStartStep = workflow.Steps.FirstOrDefault(d => d.Id == step.PrevStepId);
  1592. if (countersignStartStep is null)
  1593. throw new UserFriendlyException("未查询到当前节点上级节点");
  1594. var nextSteps = new List<WorkflowStep>();
  1595. //会签未全部办理则不创建汇总节点
  1596. var csInnerSteps = workflow.Steps.Where(d => d.PrevStepId == countersignStartStep.Id).ToList();
  1597. if (csInnerSteps.Any(d =>
  1598. d.Status != EWorkflowStepStatus.Handled || (d.IsStartCountersign && !d.IsStartedCountersignEnd)))
  1599. return nextSteps;
  1600. //if (csInnerSteps.All(d => d.Status == EWorkflowStepStatus.Handled))
  1601. //{
  1602. // 创建会签汇总节点
  1603. var countersignEndStep =
  1604. await CreateCountersignEndStepAsync(workflow, countersignStartStep, dto, expiredTime, cancellationToken);
  1605. nextSteps = new List<WorkflowStep> { countersignEndStep };
  1606. //create trace
  1607. await CreateTraceAsync(workflow, countersignEndStep, EWorkflowTraceType.Normal, cancellationToken);
  1608. await _publisher.PublishAsync(new CountersignEndAssigned(workflow), PublishStrategy.ParallelWhenAll,
  1609. cancellationToken);
  1610. //}
  1611. return nextSteps;
  1612. }
  1613. private async Task<WorkflowStep> CreateCountersignEndStepAsync(
  1614. Workflow workflow, WorkflowStep countersignStartStep,
  1615. BasicWorkflowDto dto, DateTime? expiredTime,
  1616. CancellationToken cancellationToken = default)
  1617. {
  1618. var csEndStep = _mapper.Map<WorkflowStep>(countersignStartStep);
  1619. csEndStep.Status = EWorkflowStepStatus.WaitForAccept;
  1620. csEndStep.PrevStepId = null;
  1621. csEndStep.PrevStepCode = null;
  1622. csEndStep.IsOrigin = false;
  1623. csEndStep.CountersignId = countersignStartStep.StartCountersignId;
  1624. csEndStep.CountersignPosition = ECountersignPosition.End;
  1625. //csEndStep.CountersignSteps = new();
  1626. csEndStep.IsCountersignEndStep = true;
  1627. csEndStep.CountersignStartStepId = countersignStartStep.Id;
  1628. csEndStep.Name = dto.NextStepName;
  1629. //csEndStep.TimeLimit = GetTimeLimit("");
  1630. csEndStep.StepExpiredTime = expiredTime;
  1631. csEndStep.BusinessType = dto.BusinessType;
  1632. csEndStep.Handlers = countersignStartStep.Handlers
  1633. .Where(d => d.Key == countersignStartStep.HandlerId || d.Key == countersignStartStep.HandlerOrgId)
  1634. .ToList();
  1635. //需求调整:汇总节点指派给发起人部门办理
  1636. csEndStep.FlowAssignType = EFlowAssignType.Org;
  1637. //csEndStep.StepHandlers = stepHandlers;
  1638. csEndStep.Reset();
  1639. csEndStep.ResetParameters();
  1640. await _workflowStepRepository.AddAsync(csEndStep, cancellationToken);
  1641. workflow.Steps.Add(csEndStep);
  1642. //await _workflowStepRepository.AddNav(csEndStep)
  1643. // .Include(d => d.StepHandlers)
  1644. // .ExecuteCommandAsync();
  1645. return csEndStep;
  1646. }
  1647. private bool CheckIsActualHandle(Workflow workflow, WorkflowStep step, StepDefine nextStepDefine,
  1648. BasicWorkflowDto dto)
  1649. {
  1650. //1. workflow是否为办理类型 2. 非会签:当前是否为普通节点and下一节点是否为汇总 or endStep 3. 会签:当前操作为汇总还是继续往下办理?thk: 汇总以后但未回到top又往下办理的场景,前面实际办理部门也算作办理部门
  1651. if (workflow.FlowType is not EFlowType.Handle) return false;
  1652. if (workflow.IsInCountersign)
  1653. {
  1654. return !step.IsCountersignEndStep && dto.BackToCountersignEnd;
  1655. }
  1656. else
  1657. {
  1658. return step.StepType is EStepType.Normal &&
  1659. nextStepDefine.StepType is EStepType.Summary or EStepType.End;
  1660. }
  1661. }
  1662. /// <summary>
  1663. /// 办理节点(赋值节点的办理对象信息)
  1664. /// </summary>
  1665. private void HandleStep(ISessionContext current, WorkflowStep step, string opinion, string nextStepCode)
  1666. {
  1667. step.Handle(current.RequiredUserId, current.UserName,
  1668. current.RequiredOrgId, current.OrgName,
  1669. current.OrgAreaCode, current.OrgAreaName,
  1670. current.OrgIsCenter, opinion, nextStepCode);
  1671. //var handler = step.FindActualHandler(current.Roles, current.RequiredUserId, current.RequiredOrgId);
  1672. //if (handler is not null)
  1673. // handler.IsActualHandler = true;
  1674. }
  1675. /// <summary>
  1676. /// 开始会签(创建会签数据,更新currentStep会签数据)
  1677. /// </summary>
  1678. private async Task StartCountersignAsync(ISessionContext current, Workflow workflow, WorkflowStep startStep,
  1679. BasicWorkflowDto dto,
  1680. EFlowAssignType? flowAssignType, ECounterSignType? counterSignType, DateTime? expiredTime,
  1681. CancellationToken cancellationToken)
  1682. {
  1683. var countersign = await CreateCountersignAsync(current, workflow, startStep,
  1684. dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), flowAssignType,
  1685. counterSignType, expiredTime, startStep.CountersignId, cancellationToken);
  1686. startStep.StartCountersign(countersign.Id);
  1687. }
  1688. /// <summary>
  1689. /// 检查是否从中心流转至部门
  1690. /// </summary>
  1691. private bool CheckIfFlowFromCenterToOrg(WorkflowStep sourceStep, StepDefine targetStepBoxDefine)
  1692. {
  1693. var isFromCenter = sourceStep.IsCenter();
  1694. if (!isFromCenter) return false;
  1695. var isToOrg = targetStepBoxDefine.IsOrg();
  1696. return isFromCenter && isToOrg;
  1697. }
  1698. /// <summary>
  1699. /// 检查是否从中心流转至部门
  1700. /// </summary>
  1701. private bool CheckIfFlowFromCenterToOrg(Workflow workflow, WorkflowStep targetStepBox)
  1702. {
  1703. var isToOrg = targetStepBox.IsOrg();
  1704. if (!isToOrg) return false;
  1705. var isFromCenter = workflow.Steps.All(d => d.BusinessType is not EBusinessType.Department);
  1706. return isFromCenter && isToOrg;
  1707. }
  1708. /// <summary>
  1709. /// 检查是否从部门流转至中心
  1710. /// </summary>
  1711. private bool CheckIfFlowFromOrgToCenter(WorkflowStep sourceStepBox, StepDefine targetStepBoxDefine)
  1712. {
  1713. var isFromOrg = sourceStepBox.IsOrg();
  1714. if (!isFromOrg) return false;
  1715. var isToCenter = targetStepBoxDefine.IsCenter();
  1716. return isFromOrg && isToCenter;
  1717. }
  1718. /// <summary>
  1719. /// 检查是否从部门流转至中心
  1720. /// </summary>
  1721. private bool CheckIfFlowFromOrgToCenter(WorkflowStep sourceStepBox, WorkflowStep targetStep)
  1722. {
  1723. var isFromOrg = sourceStepBox.IsOrg();
  1724. if (!isFromOrg) return false;
  1725. var isToCenter = targetStep.IsCenter();
  1726. return isFromOrg && isToCenter;
  1727. }
  1728. /// <summary>
  1729. /// 检查是否从部门流转至中心
  1730. /// </summary>
  1731. private bool CheckIfFlowFromOrgToCenter(Workflow workflow, WorkflowStep targetStep)
  1732. {
  1733. var isToCenter = targetStep.IsCenter();
  1734. if (!isToCenter) return false;
  1735. var isFromOrg = workflow.Steps.Any(d => d.BusinessType is EBusinessType.Department);
  1736. return isFromOrg && isToCenter;
  1737. }
  1738. /// <summary>
  1739. /// 复制一个节点为待接办
  1740. /// </summary>
  1741. private async Task<WorkflowStep> DuplicateStepWithTraceAsync(Workflow workflow, WorkflowStep step,
  1742. EWorkflowTraceType traceType, CancellationToken cancellationToken)
  1743. {
  1744. var newStep = _mapper.Map<WorkflowStep>(step);
  1745. newStep.Reset();
  1746. newStep.Status = EWorkflowStepStatus.WaitForAccept;
  1747. newStep.PrevStepId = step.PrevStepId;
  1748. newStep.IsMain = step.IsMain;
  1749. newStep.IsOrigin = step.IsOrigin;
  1750. //newStep.ParentId = step.ParentId;
  1751. newStep.Handlers = step.Handlers;
  1752. //newStep.StepHandlers = _mapper.Map<List<WorkflowStepHandler>>(step.StepHandlers);
  1753. newStep.StartCountersignId = step.StartCountersignId;
  1754. newStep.CountersignId = step.CountersignId;
  1755. newStep.IsStartedCountersignEnd = step.IsStartedCountersignEnd;
  1756. //退回场景:指派给原办理人,其余场景:按照原节点原始指派方式复制
  1757. if (traceType is EWorkflowTraceType.Previous)
  1758. {
  1759. //newStep.FlowAssignType = EFlowAssignType.User;
  1760. // 是否中心 临时紧急修改 后续在流程模版定义是否原办理人退回类型 来实现流程 禅道200
  1761. newStep.FlowAssignType = step.HandlerOrgIsCenter!.Value
  1762. ? step.BusinessType is EBusinessType.Send ? EFlowAssignType.User : EFlowAssignType.Role
  1763. : EFlowAssignType.Org;
  1764. //if (newStep is { FlowAssignType: EFlowAssignType.Role, BusinessType: EBusinessType.Send })
  1765. // newStep.FlowAssignType = EFlowAssignType.User;
  1766. newStep.Assign(step.HandlerId, step.HandlerName, step.HandlerOrgId, step.HandlerOrgName, step.RoleId, step.RoleName);
  1767. }
  1768. await _workflowStepRepository.AddAsync(newStep, cancellationToken);
  1769. //await _workflowStepRepository.AddNav(newStep)
  1770. // .Include(d => d.StepHandlers)
  1771. // .ExecuteCommandAsync();
  1772. await CreateTraceAsync(workflow, newStep, traceType, cancellationToken);
  1773. return newStep;
  1774. }
  1775. private async Task<WorkflowCountersign> CreateCountersignAsync(
  1776. ISessionContext current, Workflow workflow, WorkflowStep startStep, List<Kv> handlers,
  1777. EFlowAssignType? flowAssignType, ECounterSignType? counterSignType, DateTime? expiredTime,
  1778. string? parentId = null, CancellationToken cancellationToken = default)
  1779. {
  1780. var members = handlers.Select(d => new WorkflowCountersignMember
  1781. {
  1782. Key = d.Key,
  1783. Value = d.Value,
  1784. FlowAssignType = flowAssignType
  1785. }).ToList();
  1786. var countersign = new WorkflowCountersign
  1787. {
  1788. WorkflowId = workflow.Id,
  1789. StartStepId = startStep.Id,
  1790. StartStepCode = startStep.Code,
  1791. StartStepBusiType = startStep.BusinessType,
  1792. StarterId = current.RequiredUserId,
  1793. StarterName = current.UserName ?? string.Empty,
  1794. StarterOrgId = current.RequiredOrgId,
  1795. StarterOrgName = current.OrgName,
  1796. StarterOrgAreaCode = current.OrgAreaCode ?? string.Empty,
  1797. StarterOrgAreaName = current.OrgAreaName ?? string.Empty,
  1798. ParentId = parentId,
  1799. Members = members,
  1800. FlowAssignType = flowAssignType,
  1801. CounterSignType = counterSignType,
  1802. ExpiredTime = expiredTime,
  1803. //ExternalId = workflow.ExternalId,
  1804. };
  1805. //await _workflowCountersignRepository.AddAsync(countersign, cancellationToken);
  1806. await _workflowCountersignRepository.AddNav(countersign)
  1807. .Include(d => d.Members)
  1808. .ExecuteCommandAsync();
  1809. return countersign;
  1810. }
  1811. //private async Task JumpTraceAsync(string workflowId, RecallDto dto, CancellationToken cancellationToken)
  1812. //{
  1813. // //未办理的traces
  1814. // var uncompleteTraces =
  1815. // await _workflowTraceRepository.QueryAsync(d =>
  1816. // d.WorkflowId == workflowId && string.IsNullOrEmpty(d.HandlerId));
  1817. // foreach (var trace in uncompleteTraces)
  1818. // {
  1819. // HandleTrace(trace, dto.Opinion);
  1820. // }
  1821. // await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken);
  1822. //}
  1823. private async Task RecallTraceAsync(List<WorkflowTrace> traces, string opinion, ISessionContext current,
  1824. CancellationToken cancellationToken)
  1825. {
  1826. //未办理的traces
  1827. //var uncompleteTraces =
  1828. // await _workflowTraceRepository.QueryAsync(d =>
  1829. // d.WorkflowId == workflowId && string.IsNullOrEmpty(d.HandlerId));
  1830. var uncompleteTraces = traces.Where(d => d.Status != EWorkflowStepStatus.Handled).ToList();
  1831. if (uncompleteTraces.Any())
  1832. {
  1833. foreach (var trace in uncompleteTraces)
  1834. {
  1835. trace.Handle(
  1836. current.RequiredUserId, current.UserName,
  1837. current.RequiredOrgId, current.OrgName,
  1838. current.OrgAreaCode, current.OrgAreaName,
  1839. current.OrgIsCenter, opinion);
  1840. }
  1841. await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken);
  1842. }
  1843. }
  1844. private async Task<WorkflowTrace> PreviousTraceAsync(string workflowId,
  1845. PreviousWorkflowDto dto, WorkflowStep step,
  1846. string applicantId, string applicantName,
  1847. string applicantOrgId, string applicantOrgName,
  1848. string applicantOrgAreaCode, string applicantOrgAreaName,
  1849. bool applicantIsCenter, CancellationToken cancellationToken)
  1850. {
  1851. var trace = await GetWorkflowTraceAsync(workflowId, step.Id, cancellationToken);
  1852. _mapper.Map(dto, trace);
  1853. //HandleTrace(trace, dto.Opinion, current);
  1854. trace.Handle(applicantId, applicantName,
  1855. applicantOrgId, applicantOrgName,
  1856. applicantOrgAreaCode, applicantOrgAreaName,
  1857. applicantIsCenter, dto.Opinion);
  1858. await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
  1859. return trace;
  1860. }
  1861. //private async Task EndTraceAsync(Workflow workflow, BasicWorkflowDto dto, WorkflowStep step, CancellationToken cancellationToken)
  1862. //{
  1863. // var trace = _mapper.Map<WorkflowTrace>(step);
  1864. // trace.Status = EWorkflowTraceStatus.Normal;
  1865. // trace.ExpiredTime = workflow.ExpiredTime;
  1866. // trace.TimeLimit = workflow.TimeLimit;
  1867. // await _workflowTraceRepository.AddAsync(trace, cancellationToken);
  1868. //}
  1869. private async Task<WorkflowTrace> NextTraceAsync(Workflow workflow, BasicWorkflowDto dto, WorkflowStep step,
  1870. CancellationToken cancellationToken)
  1871. {
  1872. // var trace = await GetWorkflowTraceAsync(workflow.Id, step.Id, cancellationToken);
  1873. var trace = workflow.Traces.FirstOrDefault(d => d.Id == step.Id);
  1874. if (trace == null)
  1875. throw new UserFriendlyException($"未找到对应trace, workflowId: {workflow.Id}, stepId: {step.Id}");
  1876. _mapper.Map(dto, trace);
  1877. _mapper.Map(step, trace);
  1878. await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
  1879. return trace;
  1880. }
  1881. private async Task AcceptTraceAsync(Workflow workflow, WorkflowStep step, CancellationToken cancellationToken)
  1882. {
  1883. var trace = await GetWorkflowTraceAsync(workflow.Id, step.Id, cancellationToken);
  1884. _mapper.Map(step, trace);
  1885. await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
  1886. }
  1887. private async Task CreateTraceAsync(Workflow workflow, WorkflowStep step,
  1888. EWorkflowTraceType traceType = EWorkflowTraceType.Normal,
  1889. CancellationToken cancellationToken = default)
  1890. {
  1891. var sendHandleTimes = 0;
  1892. if (step.BusinessType == EBusinessType.Send)
  1893. {
  1894. var sendHandleCount = workflow.Traces.Count(d => d.StepType == EStepType.Normal &&
  1895. d.BusinessType == EBusinessType.Send);
  1896. sendHandleTimes = sendHandleCount + 1;
  1897. }
  1898. var trace = _mapper.Map<WorkflowTrace>(step);
  1899. trace.TraceType = traceType;
  1900. trace.SendHandleTimes = sendHandleTimes;
  1901. if (step.IsInCountersign())
  1902. {
  1903. if (step.IsCountersignEndStep)
  1904. {
  1905. var startTrace =
  1906. await GetWorkflowTraceAsync(workflow.Id, step.CountersignStartStepId, cancellationToken);
  1907. trace.ParentId = startTrace.ParentId;
  1908. }
  1909. else
  1910. {
  1911. //if (step.CountersignPosition is ECountersignPosition.Multi)
  1912. //{
  1913. // var prevTrace = await GetWorkflowTraceAsync(workflow.Id, step.PrevStepId, cancellationToken);
  1914. // trace.ParentId = prevTrace.Id;
  1915. //}
  1916. //else if (step.CountersignPosition is ECountersignPosition.Single)
  1917. //{
  1918. // var prevTrace = await GetWorkflowTraceAsync(workflow.Id, step.PrevStepId, cancellationToken);
  1919. // trace.ParentId = prevTrace.ParentId;
  1920. //}
  1921. var prevTrace = await GetWorkflowTraceAsync(workflow.Id, step.PrevStepId, cancellationToken);
  1922. trace.ParentId = prevTrace.Id;
  1923. }
  1924. }
  1925. await _workflowTraceRepository.AddAsync(trace, cancellationToken);
  1926. workflow.Traces.Add(trace);
  1927. }
  1928. private async Task<WorkflowTrace> GetWorkflowTraceAsync(string workflowId, string stepId,
  1929. CancellationToken cancellationToken)
  1930. {
  1931. var parentTrace = await _workflowTraceRepository.GetAsync(d =>
  1932. d.WorkflowId == workflowId && d.StepId == stepId, cancellationToken);
  1933. if (parentTrace == null)
  1934. throw new UserFriendlyException($"未找到对应trace, workflowId: {workflowId}, stepId: {stepId}");
  1935. return parentTrace;
  1936. }
  1937. private async Task<bool> RecallAsync(Workflow workflow, BasicWorkflowDto dto, FlowAssignInfo flowAssignInfo,
  1938. StepDefine targetStepDefine, WorkflowStep targetStep, EWorkflowTraceType traceType,
  1939. DateTime? expiredTime, bool isOrderFiled, CancellationToken cancellationToken)
  1940. {
  1941. var targetIsStartStep = targetStepDefine.StepType is EStepType.Start;
  1942. var updateTraces = new List<WorkflowTrace>();
  1943. //update uncomplete traces
  1944. var uncompleteTraces = workflow.Traces.Where(d => d.Status != EWorkflowStepStatus.Handled).ToList();
  1945. if (uncompleteTraces.Any())
  1946. {
  1947. foreach (var trace in uncompleteTraces)
  1948. {
  1949. trace.Handle(
  1950. _sessionContext.RequiredUserId, _sessionContext.UserName,
  1951. _sessionContext.RequiredOrgId, _sessionContext.OrgName,
  1952. _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName,
  1953. _sessionContext.OrgIsCenter, dto.Opinion);
  1954. }
  1955. //await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken);
  1956. updateTraces.AddRange(uncompleteTraces);
  1957. }
  1958. else
  1959. {
  1960. var endTrace = workflow.Traces.Where(d => d.StepType == EStepType.End).MaxBy(d => d.CreationTime);
  1961. if (endTrace is not null)
  1962. {
  1963. endTrace.Opinion += ("\r\n" + dto.Opinion);
  1964. updateTraces.Add(endTrace);
  1965. }
  1966. }
  1967. //get targetStep's previous
  1968. WorkflowStep? targetPrevStep = null;
  1969. if (!targetIsStartStep)
  1970. {
  1971. targetPrevStep = workflow.Steps.FirstOrDefault(d => d.Id == targetStep.PrevStepId);
  1972. if (targetPrevStep == null)
  1973. throw new UserFriendlyException($"{nameof(RecallAsync)}, 未找到目标节点的前一节点, flowId: {workflow.Id}");
  1974. }
  1975. //查询所有目标节点之后的节点,然后删掉(包括目标节点)
  1976. var removeSteps = GetStepsBehindTargetStep(workflow.Steps, targetStep);
  1977. if (removeSteps.Any())
  1978. {
  1979. await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken);
  1980. //await _workflowStepRepository.RemoveNav(removeSteps)
  1981. // .Include(d => d.StepHandlers)
  1982. // .ExecuteCommandAsync();
  1983. workflow.Steps.RemoveAll(d => removeSteps.Contains(d));
  1984. //更新快照对应节点状态
  1985. var stepIds = removeSteps.Select(d => d.Id).ToList();
  1986. var traces = workflow.Traces.Where(d => stepIds.Contains(d.StepId)).ToList();
  1987. //await UpdateTracesStateAsync(updateTraces, EWorkflowTraceState.StepRemoveByRecall, cancellationToken);
  1988. foreach (var trace in traces)
  1989. {
  1990. trace.TraceState = isOrderFiled
  1991. ? EWorkflowTraceState.StepRemoveByRecallWhenFiled
  1992. : EWorkflowTraceState.StepRemoveByRecall;
  1993. }
  1994. updateTraces.AddRange(traces);
  1995. }
  1996. await _workflowTraceRepository.UpdateRangeAsync(updateTraces, cancellationToken);
  1997. workflow.EndCountersign();
  1998. workflow.ResetOption();
  1999. if (workflow.Status is EWorkflowStatus.Completed)
  2000. workflow.SetStatusRunnable();
  2001. var targetStepNew = targetIsStartStep
  2002. ? await CreateStartStepAsync(workflow, targetStepDefine, dto,
  2003. dto.NextHandlers.First(), traceType, expiredTime, EFlowAssignType.Org, cancellationToken)
  2004. : (await CreateStepsAsync(workflow, targetStepDefine, targetPrevStep, dto,
  2005. flowAssignInfo.FlowAssignType, dto.NextHandlers,
  2006. null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceType,
  2007. null, expiredTime, cancellationToken: cancellationToken)).First();
  2008. //更新实际办理节点信息
  2009. workflow.UpdateActualStepWhenAssign(targetStepNew, new FlowStepHandler
  2010. {
  2011. UserId = targetStep.HandlerId,
  2012. Username = targetStep.HandlerName,
  2013. OrgId = targetStep.HandlerOrgId,
  2014. OrgName = targetStep.HandlerOrgName
  2015. });
  2016. workflow.UpdateCurrentStepWhenAssign(targetStepNew, new FlowStepHandler
  2017. {
  2018. UserId = targetStep.HandlerId,
  2019. Username = targetStep.HandlerName,
  2020. OrgId = targetStep.HandlerOrgId,
  2021. OrgName = targetStep.HandlerOrgName
  2022. });
  2023. workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds());
  2024. workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
  2025. //calc workflow expired time
  2026. var isOrgToCenter = CheckIfFlowFromOrgToCenter(workflow, targetStep);
  2027. return isOrgToCenter;
  2028. }
  2029. private List<WorkflowStep> GetStepsBehindTargetStep(List<WorkflowStep> steps, WorkflowStep targetStep)
  2030. {
  2031. var behindSteps = new List<WorkflowStep> { targetStep };
  2032. if (!steps.Any()) return behindSteps;
  2033. var nextSteps = targetStep.IsStartCountersign
  2034. ? steps.Where(d => d.CountersignId == targetStep.StartCountersignId).ToList()
  2035. : steps.Where(d => d.PrevStepId == targetStep.Id).ToList();
  2036. //var nextSteps = steps.Where(d => d.PrevStepId == targetStep.Id).ToList();
  2037. if (!nextSteps.Any())
  2038. return behindSteps;
  2039. foreach (var nextStep in nextSteps)
  2040. {
  2041. var leftSteps = steps.Except(behindSteps).ToList();
  2042. behindSteps.AddRange(GetStepsBehindTargetStep(leftSteps, nextStep));
  2043. }
  2044. return behindSteps;
  2045. }
  2046. private static void CheckWhetherRunnable(EWorkflowStatus status)
  2047. {
  2048. if (status != EWorkflowStatus.Runnable)
  2049. throw UserFriendlyException.SameMessage("当前流程状态不可继续流转");
  2050. }
  2051. private void ValidatePermission(Workflow workflow, string OrgId, string UserId, string[] roleIds)
  2052. {
  2053. if (!workflow.IsCanHandle(UserId, OrgId, roleIds))
  2054. throw UserFriendlyException.SameMessage("无办理权限");
  2055. }
  2056. private async Task<WorkflowStep> CreateEndStepAsync(
  2057. ISessionContext current,
  2058. Workflow workflow,
  2059. StepDefine endStepDefine,
  2060. WorkflowStep prevStep,
  2061. DateTime? expiredTime,
  2062. CancellationToken cancellationToken)
  2063. {
  2064. if (workflow.Steps.Any(d => d.StepType == EStepType.End))
  2065. throw UserFriendlyException.SameMessage("无法重复创建结束节点");
  2066. var handler = new FlowStepHandler
  2067. {
  2068. Key = current.UserId,
  2069. Value = current.UserName,
  2070. UserId = current.RequiredUserId,
  2071. Username = current.UserName,
  2072. OrgId = current.OrgId,
  2073. OrgName = current.OrgName,
  2074. };
  2075. var step = CreateStep(workflow, endStepDefine, prevStep, EFlowAssignType.User, handler,
  2076. null, null, EWorkflowStepStatus.WaitForAccept,
  2077. ECountersignPosition.None, expiredTime, endStepDefine.Name, true);
  2078. //step.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName,
  2079. // _sessionContext.RequiredOrgId, _sessionContext.OrgName,
  2080. // _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName);
  2081. HandleStep(current, step, "流程归档", string.Empty);
  2082. await _workflowStepRepository.AddAsync(step, cancellationToken);
  2083. workflow.Steps.Add(step);
  2084. //end trace
  2085. await CreateTraceAsync(workflow, step, cancellationToken: cancellationToken);
  2086. return step;
  2087. }
  2088. public async Task<List<WorkflowStep>> CreateConfigStepsAsync(
  2089. Workflow workflow,
  2090. StepDefine stepDefine,
  2091. WorkflowStep prevStep,
  2092. BasicWorkflowDto dto,
  2093. FlowAssignInfo flowAssignInfo,
  2094. EWorkflowTraceType traceType,
  2095. DateTime? expiredTime,
  2096. CancellationToken cancellationToken)
  2097. {
  2098. List<FlowStepHandler> handlers;
  2099. if (stepDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any())
  2100. throw new UserFriendlyException("未指定节点处理者");
  2101. if (stepDefine.HandlerType == EHandlerType.Role && !dto.NextHandlers.Any())
  2102. {
  2103. var handler = stepDefine.HandlerTypeItems.First();
  2104. handlers = new List<FlowStepHandler>
  2105. {
  2106. new() { Key = handler.Key, Value = handler.Value, RoleId = handler.Key, RoleName = handler.Value }
  2107. };
  2108. }
  2109. else
  2110. {
  2111. handlers = dto.NextHandlers;
  2112. }
  2113. return await CreateStepsAsync(workflow, stepDefine, prevStep, dto, /*dto.IsStartCountersign,*/
  2114. flowAssignInfo.FlowAssignType, handlers, null,
  2115. EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None,
  2116. true, traceType, null, expiredTime, cancellationToken);
  2117. }
  2118. private async Task<List<WorkflowStep>> CreateStepsAsync(
  2119. Workflow workflow,
  2120. StepDefine stepDefine,
  2121. WorkflowStep prevStep,
  2122. BasicWorkflowDto dto,
  2123. //bool isStartCountersign,
  2124. EFlowAssignType? flowAssignType,
  2125. List<FlowStepHandler> handlers,
  2126. string? countersignId,
  2127. EWorkflowStepStatus stepStatus,
  2128. ECountersignPosition csPosition,
  2129. bool isOrigin,
  2130. EWorkflowTraceType traceType,
  2131. EHandlerType? handlerType = null,
  2132. DateTime? expiredTime = null,
  2133. CancellationToken cancellationToken = default
  2134. )
  2135. {
  2136. List<WorkflowStep> steps = new();
  2137. foreach (var handler in handlers)
  2138. {
  2139. var isMain = handlers.Count == 1 || (handlers.Count > 1 && handler.Key == dto.NextMainHandler);
  2140. var step = CreateStep(workflow, stepDefine, prevStep, flowAssignType,
  2141. handler, dto.NextStepCode, countersignId, stepStatus, csPosition, expiredTime,
  2142. dto.NextStepName, isOrigin, isMain, handlerType, dto.BusinessType);
  2143. //var stepHandler = stepHandlers.First(d => d.GetHandler().Key == handler.Key);
  2144. //step.StepHandlers = new List<WorkflowStepHandler> { stepHandler };
  2145. steps.Add(step);
  2146. }
  2147. await _workflowStepRepository.AddRangeAsync(steps, cancellationToken);
  2148. workflow.Steps.AddRange(steps);
  2149. //await _workflowStepRepository.AddNav(steps)
  2150. // .Include(d => d.StepHandlers)
  2151. // .ExecuteCommandAsync();
  2152. //create traces todo add range traces
  2153. foreach (var step in steps)
  2154. {
  2155. await CreateTraceAsync(workflow, step, traceType, cancellationToken);
  2156. }
  2157. return steps;
  2158. }
  2159. /// <summary>
  2160. /// 查询未完成节点
  2161. /// </summary>
  2162. public WorkflowStep GetUnHandleStep(List<WorkflowStep> steps, string orgId, string userId, string[] roleIds)
  2163. {
  2164. //var step = GetStep(steps, orgCode, userId, d => d != EWorkflowStepStatus.Handled);
  2165. var step = steps.FirstOrDefault(d => d.IsCanHandle(userId, orgId, roleIds));
  2166. if (step == null)
  2167. throw new UserFriendlyException(
  2168. $"未找到对应节点, workflowId: {steps.FirstOrDefault()?.WorkflowId} orgCode:{orgId}, userId: {userId}, roleIds: {string.Join(',', roleIds)}",
  2169. "未找到对应节点");
  2170. return step;
  2171. }
  2172. /// <summary>
  2173. /// 检查当前办理节点是否为开始节点
  2174. /// </summary>
  2175. /// <param name="workflowId"></param>
  2176. /// <param name="userId">当前办理人Id</param>
  2177. /// <param name="orgId">当前办理人orgId</param>
  2178. /// <param name="cancellationToken"></param>
  2179. /// <returns></returns>
  2180. public async Task<bool> CheckCurrentIsStartStepAsync(string workflowId, string userId, string orgId,
  2181. CancellationToken cancellationToken)
  2182. {
  2183. var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellationToken);
  2184. var currentStep = GetStep(workflow.Steps, orgId, userId, d => d != EWorkflowStepStatus.Handled);
  2185. if (currentStep is null) return false;
  2186. return workflow.Steps.Count == 1 && currentStep.StepType is EStepType.Start && currentStep.IsOrigin;
  2187. }
  2188. /// <summary>
  2189. /// 检查动态节点是否该终止
  2190. /// </summary>
  2191. public bool DynamicShouldTerminal(StepDefine currentStepDefine, int currentOrgLevel)
  2192. {
  2193. if (currentStepDefine.InstanceMode is not EInstanceMode.Dynamic)
  2194. throw new UserFriendlyException("非动态节点");
  2195. switch (currentStepDefine.InstancePolicy)
  2196. {
  2197. case EDynamicPolicy.OrgUpCenterTop:
  2198. case EDynamicPolicy.OrgUp:
  2199. if (!int.TryParse(currentStepDefine.TerminalDynamicMark, out var tMark))
  2200. throw new UserFriendlyException(
  2201. $"TerminalDynamicMark parse to int failed, tMark: {currentStepDefine.TerminalDynamicMark}");
  2202. return currentOrgLevel <= tMark;
  2203. case EDynamicPolicy.OrgDownCenterTop:
  2204. case EDynamicPolicy.OrgDown:
  2205. if (!int.TryParse(currentStepDefine.TerminalDynamicMark, out var tMark1))
  2206. throw new UserFriendlyException(
  2207. $"TerminalDynamicMark parse to int failed, tMark: {currentStepDefine.TerminalDynamicMark}");
  2208. return currentOrgLevel >= tMark1;
  2209. default:
  2210. throw new ArgumentOutOfRangeException();
  2211. }
  2212. }
  2213. /// <summary>
  2214. /// 终止会签
  2215. /// </summary>
  2216. /// <param name="countersignId"></param>
  2217. /// <param name="cancellationToken"></param>
  2218. /// <returns></returns>
  2219. public async Task<Workflow> TerminalCountersignAsync(string countersignId, CancellationToken cancellationToken)
  2220. {
  2221. var countersign = await _workflowCountersignRepository.GetAsync(countersignId, cancellationToken);
  2222. if (countersign is null)
  2223. throw new UserFriendlyException("无效会签编号");
  2224. //1. 检查会签是否已结束 t: return 2.检查是否有嵌套会签 t: 一起结束 3.结束会签 4.trace 5.检查workflow会签状态,如果会签全结束需更新状态 6.cp会签发起节点变为待办节点
  2225. if (countersign.IsCompleted())
  2226. throw new UserFriendlyException("该会签已结束");
  2227. var workflow = await GetWorkflowAsync(countersign.WorkflowId, withSteps: true, withTraces: true,
  2228. withCountersigns: true, cancellationToken: cancellationToken);
  2229. if (!workflow.IsInCountersign)
  2230. throw new UserFriendlyException("该流程未处于会签中");
  2231. countersign = workflow.Countersigns.First(d => d.Id == countersignId);
  2232. var startCountersignStep = workflow.Steps.Find(d => d.StartCountersignId == countersignId);
  2233. if (startCountersignStep is null)
  2234. throw new UserFriendlyException("未查询到发起会签节点");
  2235. if (startCountersignStep.IsStartedCountersignEnd)
  2236. throw new UserFriendlyException("该会签已汇总");
  2237. var updateCountersigns = new List<WorkflowCountersign>();
  2238. EndCountersignWithCascade(countersign, workflow.Countersigns, startCountersignStep.BusinessType,
  2239. ref updateCountersigns);
  2240. if (updateCountersigns.Any())
  2241. {
  2242. var updateSteps = new List<WorkflowStep>();
  2243. var updateTraces = new List<WorkflowTrace>();
  2244. HandleStepsByTerminalCs(startCountersignStep, workflow.Steps, workflow.Traces, ref updateSteps,
  2245. ref updateTraces);
  2246. if (updateSteps.Any())
  2247. await _workflowStepRepository.RemoveRangeAsync(updateSteps, cancellationToken);
  2248. //await _workflowStepRepository.RemoveNav(updateSteps)
  2249. // .Include(d => d.StepHandlers)
  2250. // .ExecuteCommandAsync();
  2251. if (updateTraces.Any())
  2252. await _workflowTraceRepository.UpdateRangeAsync(updateTraces, cancellationToken);
  2253. await _workflowCountersignRepository.UpdateRangeAsync(updateCountersigns, cancellationToken);
  2254. //cp会签发起节点变为待办节点
  2255. //1. create terminal trace 2. 撤回至startStep
  2256. var newStep = await DuplicateStepWithTraceAsync(workflow, startCountersignStep,
  2257. EWorkflowTraceType.Normal,
  2258. cancellationToken);
  2259. //当topcsStep结束cs时,实际办理节点应该更新为newStep
  2260. if (startCountersignStep.Id == workflow.TopCountersignStepId)
  2261. {
  2262. workflow.UpdateActualStepWhenAssign(newStep,
  2263. new FlowStepHandler
  2264. {
  2265. UserId = startCountersignStep.HandlerId,
  2266. Username = startCountersignStep.HandlerName,
  2267. OrgId = startCountersignStep.HandlerOrgId,
  2268. OrgName = startCountersignStep.HandlerOrgName
  2269. });
  2270. workflow.UpdateCurrentStepWhenAssign(newStep,
  2271. new FlowStepHandler
  2272. {
  2273. UserId = startCountersignStep.HandlerId,
  2274. Username = startCountersignStep.HandlerName,
  2275. OrgId = startCountersignStep.HandlerOrgId,
  2276. OrgName = startCountersignStep.HandlerOrgName
  2277. });
  2278. }
  2279. //csEndStep又开启了cs,在结束会签时,如果该节点是topcs的end节点, workflow.topcsStep应该更新为前一cs开启stepId
  2280. if (startCountersignStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
  2281. workflow.TopCountersignStepId = startCountersignStep.CountersignStartStepId;
  2282. if (workflow.CheckIfCountersignOver())
  2283. workflow.EndCountersign();
  2284. var removeHandlers = updateSteps.SelectMany(d => d.Handlers).Select(d => d.Key).ToList();
  2285. var handlerObjs = newStep.Handlers.Select(d => new HandlerGroupItem
  2286. {
  2287. GroupId = Guid.NewGuid().ToString(),
  2288. Key = d.Key,
  2289. Value = d.Value
  2290. }).ToList();
  2291. workflow.UpdateHandlers(removeHandlers, newStep.FlowAssignType.Value, handlerObjs);
  2292. await _workflowRepository.UpdateAsync(workflow, cancellationToken);
  2293. }
  2294. return workflow;
  2295. }
  2296. private void HandleStepsByTerminalCs(WorkflowStep step, List<WorkflowStep> steps, List<WorkflowTrace> traces,
  2297. ref List<WorkflowStep> updateSteps, ref List<WorkflowTrace> updateTraces)
  2298. {
  2299. if (step.IsStartCountersign)
  2300. {
  2301. var countersignSteps = steps.Where(d => d.CountersignId == step.StartCountersignId).ToList();
  2302. if (countersignSteps.Any())
  2303. {
  2304. foreach (var countersignStep in countersignSteps)
  2305. {
  2306. HandleStepsByTerminalCs(countersignStep, steps, traces, ref updateSteps, ref updateTraces);
  2307. }
  2308. }
  2309. }
  2310. EndStepByTerminalCs(step, traces, ref updateSteps, ref updateTraces);
  2311. }
  2312. private void EndStepByTerminalCs(WorkflowStep step, List<WorkflowTrace> traces,
  2313. ref List<WorkflowStep> updateSteps, ref List<WorkflowTrace> updateTraces)
  2314. {
  2315. var isHandled = step.Status is EWorkflowStepStatus.Handled;
  2316. var opinion = $"会签未办理完成,由 {_sessionContext.OrgName} 的 {_sessionContext.UserName} 终止办理";
  2317. if (step.IsStartCountersign)
  2318. step.CountersignEnd();
  2319. if (step.Status is not EWorkflowStepStatus.Handled)
  2320. {
  2321. step.Handle(_sessionContext.RequiredUserId, _sessionContext.UserName,
  2322. _sessionContext.RequiredOrgId, _sessionContext.OrgName,
  2323. _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName,
  2324. _sessionContext.OrgIsCenter, opinion);
  2325. }
  2326. updateSteps.Add(step);
  2327. if (isHandled) return;
  2328. var trace = traces.FirstOrDefault(d => d.StepId == step.Id);
  2329. if (trace != null)
  2330. {
  2331. _mapper.Map(step, trace);
  2332. updateTraces.Add(trace);
  2333. }
  2334. }
  2335. /// <summary>
  2336. /// 结束会签(包含子项)
  2337. /// </summary>
  2338. /// <param name="countersign"></param>
  2339. /// <param name="updateCountersigns"></param>
  2340. private void EndCountersignWithCascade(WorkflowCountersign countersign, List<WorkflowCountersign> countersigns,
  2341. EBusinessType businessType, ref List<WorkflowCountersign> updateCountersigns)
  2342. {
  2343. if (countersign is null) return;
  2344. var childCountersigns = countersigns.Where(d => d.ParentId == countersign.Id).ToList();
  2345. if (childCountersigns.Any())
  2346. {
  2347. foreach (var childCountersign in childCountersigns)
  2348. {
  2349. EndCountersignWithCascade(childCountersign, countersigns, businessType, ref updateCountersigns);
  2350. }
  2351. }
  2352. EndCountersign(countersign, countersigns, businessType, ref updateCountersigns);
  2353. }
  2354. private void EndCountersign(WorkflowCountersign countersign, List<WorkflowCountersign> countersigns,
  2355. EBusinessType businessType, ref List<WorkflowCountersign> updateCountersigns)
  2356. {
  2357. //todo 1. trace? 先确定展现形式 2. end cs
  2358. countersign.End(null, null, businessType,
  2359. _sessionContext.RequiredUserId, _sessionContext.UserName,
  2360. _sessionContext.RequiredOrgId, _sessionContext.OrgName,
  2361. _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName);
  2362. /*
  2363. * //结束step会签信息
  2364. countersignStartStep.CountersignEnd();
  2365. updateSteps.Add(countersignStartStep);
  2366. //结束会签
  2367. currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType,
  2368. _sessionContext.RequiredUserId, _sessionContext.UserName,
  2369. _sessionContext.RequiredOrgId, _sessionContext.OrgName,
  2370. _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName);
  2371. await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken);
  2372. */
  2373. updateCountersigns.Add(countersign);
  2374. }
  2375. private WorkflowStep? GetStep(List<WorkflowStep> steps, string orgCode, string userId,
  2376. Func<EWorkflowStepStatus, bool> predicate) =>
  2377. steps.FirstOrDefault(d =>
  2378. predicate(d.Status) && d.Handlers.Any(x => x.Key == orgCode || x.Key == userId));
  2379. private WorkflowStep CreateStep(
  2380. Workflow workflow,
  2381. StepDefine stepDefine,
  2382. WorkflowStep prevStep,
  2383. EFlowAssignType? flowAssignType,
  2384. FlowStepHandler handler,
  2385. string nextStepCode,
  2386. string? countersignId,
  2387. EWorkflowStepStatus stepStatus,
  2388. ECountersignPosition countersignPosition,
  2389. DateTime? expiredTime,
  2390. string stepName,
  2391. bool isOrigin,
  2392. bool isMainHandler = false,
  2393. EHandlerType? handlerType = null, //动态节点依据动态策略判断
  2394. EBusinessType? businessType = null
  2395. )
  2396. {
  2397. //if (!handlers.Any())
  2398. // throw new UserFriendlyException($"非法参数, handlers为空, method: {nameof(CreateStep)}");
  2399. var step = _mapper.Map<WorkflowStep>(stepDefine);
  2400. _mapper.Map(workflow, step);
  2401. step.FlowAssignType = flowAssignType;
  2402. step.Handlers = new List<Kv> { new(handler.Key, handler.Value) };
  2403. //step.StepHandlers = stepHandlers;
  2404. step.NextStepCode = step.StepType is EStepType.End ? string.Empty : nextStepCode;
  2405. step.IsMain = isMainHandler;
  2406. step.PrevStepId = prevStep.Id;
  2407. step.PrevStepCode = prevStep.Code;
  2408. step.CountersignId = countersignId;
  2409. step.Status = stepStatus;
  2410. step.CountersignPosition = countersignPosition;
  2411. step.StepExpiredTime = expiredTime;
  2412. //step.TimeLimit = GetTimeLimit("");
  2413. step.IsOrigin = isOrigin;
  2414. step.Name = stepName;
  2415. //新增需求: 部门汇总节点由部门办理//todo 待确认中心由部门处理还是由之前办理人办理
  2416. if (step.StepType == EStepType.Summary && step.BusinessType == EBusinessType.Department)
  2417. step.FlowAssignType = EFlowAssignType.Org;
  2418. step.Assign(handler.UserId, handler.Username,
  2419. handler.OrgId, handler.OrgName,
  2420. handler.RoleId, handler.RoleName);
  2421. if (handlerType.HasValue)
  2422. step.HandlerType = handlerType.Value;
  2423. if (businessType.HasValue)
  2424. step.BusinessType = businessType.Value;
  2425. return step;
  2426. }
  2427. #endregion
  2428. }
  2429. }